From 420533a8f9b345699dad9eeafeb3ccecfed516b2 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 4 Feb 2023 23:32:07 +0800 Subject: Add emacsclient desktop file equivalent on Android * doc/emacs/android.texi (Android File System): * java/AndroidManifest.xml.in: Update with new activity. Remove Android 10 restrictions through a special flag. * java/org/gnu/emacs/EmacsNative.java (getProcName): New function. * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): New file. * java/org/gnu/emacs/EmacsService.java (getLibraryDirection): Remove unused annotation. * lib-src/emacsclient.c (decode_options): Set alt_display on Android. * src/android.c (android_proc_name): New function. (NATIVE_NAME): Export via JNI. --- java/org/gnu/emacs/EmacsOpenActivity.java | 357 ++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 java/org/gnu/emacs/EmacsOpenActivity.java (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java new file mode 100644 index 00000000000..268a9abd7b1 --- /dev/null +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -0,0 +1,357 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +package org.gnu.emacs; + +/* This class makes the Emacs server work reasonably on Android. + + There is no way to make the Unix socket publicly available on + Android. + + Instead, this activity tries to connect to the Emacs server, to + make it open files the system asks Emacs to open, and to emulate + some reasonable behavior when Emacs has not yet started. + + First, Emacs registers itself as an application that can open text + and image files. + + Then, when the user is asked to open a file and selects ``Emacs'' + as the application that will open the file, the system pops up a + window, this activity, and calls the `onCreate' function. + + `onCreate' then tries very to find the file name of the file that + was selected, and give it to emacsclient. + + If emacsclient successfully opens the file, then this activity + starts EmacsActivity (to bring it on to the screen); otherwise, it + displays the output of emacsclient or any error message that occurs + and exits. */ + +import android.app.AlertDialog; +import android.app.Activity; + +import android.content.Context; +import android.content.ContentResolver; +import android.content.DialogInterface; +import android.content.Intent; + +import android.net.Uri; + +import android.os.Build; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +public class EmacsOpenActivity extends Activity + implements DialogInterface.OnClickListener +{ + private class EmacsClientThread extends Thread + { + private ProcessBuilder builder; + + public + EmacsClientThread (ProcessBuilder processBuilder) + { + builder = processBuilder; + } + + @Override + public void + run () + { + Process process; + InputStream error; + String errorText; + + try + { + /* Start emacsclient. */ + process = builder.start (); + process.waitFor (); + + /* Now figure out whether or not starting the process was + successful. */ + if (process.exitValue () == 0) + finishSuccess (); + else + finishFailure ("Error opening file", null); + } + catch (IOException exception) + { + finishFailure ("Internal error", exception.toString ()); + } + catch (InterruptedException exception) + { + finishFailure ("Internal error", exception.toString ()); + } + } + } + + @Override + public void + onClick (DialogInterface dialog, int which) + { + finish (); + } + + public String + readEmacsClientLog () + { + File file, cache; + FileReader reader; + char[] buffer; + int rc; + String what; + + cache = getCacheDir (); + file = new File (cache, "emacsclient.log"); + what = ""; + + try + { + reader = new FileReader (file); + buffer = new char[2048]; + + while ((rc = reader.read (buffer, 0, 2048)) != -1) + what += String.valueOf (buffer, 0, 2048); + + reader.close (); + return what; + } + catch (IOException exception) + { + return ("Couldn't read emacsclient.log: " + + exception.toString ()); + } + } + + private void + displayFailureDialog (String title, String text) + { + AlertDialog.Builder builder; + AlertDialog dialog; + + builder = new AlertDialog.Builder (this); + dialog = builder.create (); + dialog.setTitle (title); + + if (text == null) + /* Read in emacsclient.log instead. */ + text = readEmacsClientLog (); + + dialog.setMessage (text); + dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this); + dialog.show (); + } + + /* Finish this activity in response to emacsclient having + successfully opened a file. + + In the main thread, close this window, and open a window + belonging to an Emacs frame. */ + + public void + finishSuccess () + { + runOnUiThread (new Runnable () { + @Override + public void + run () + { + Intent intent; + + intent = new Intent (EmacsOpenActivity.this, + EmacsActivity.class); + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity (intent); + + EmacsOpenActivity.this.finish (); + } + }); + } + + /* Finish this activity after displaying a dialog associated with + failure to open a file. + + Use TITLE as the title of the dialog. If TEXT is non-NULL, + display that text in the dialog. Otherwise, use the contents of + emacsclient.log in the cache directory instead. */ + + public void + finishFailure (final String title, final String text) + { + runOnUiThread (new Runnable () { + @Override + public void + run () + { + displayFailureDialog (title, text); + } + }); + } + + public String + getLibraryDirectory () + { + int apiLevel; + Context context; + + context = getApplicationContext (); + apiLevel = Build.VERSION.SDK_INT; + + if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) + return context.getApplicationInfo().nativeLibraryDir; + else if (apiLevel >= Build.VERSION_CODES.DONUT) + return context.getApplicationInfo().dataDir + "/lib"; + + return "/data/data/" + context.getPackageName() + "/lib"; + } + + public void + startEmacsClient (String fileName) + { + String libDir; + ProcessBuilder builder; + Process process; + EmacsClientThread thread; + File file; + + file = new File (getCacheDir (), "emacsclient.log"); + + libDir = getLibraryDirectory (); + builder = new ProcessBuilder (libDir + "/libemacsclient.so", + fileName, "--reuse-frame", + "--timeout=10", "--no-wait"); + + /* Redirect standard error to a file so that errors can be + meaningfully reported. */ + + if (file.exists ()) + file.delete (); + + builder.redirectError (file); + + /* Track process output in a new thread, since this is the UI + thread and doing so here can cause deadlocks when EmacsService + decides to wait for something. */ + + thread = new EmacsClientThread (builder); + thread.start (); + } + + @Override + public void + onCreate (Bundle savedInstanceState) + { + String action, fileName; + Intent intent; + Uri uri; + ContentResolver resolver; + ParcelFileDescriptor fd; + byte[] names; + String errorBlurb; + + super.onCreate (savedInstanceState); + + /* Obtain the intent that started Emacs. */ + intent = getIntent (); + action = intent.getAction (); + + if (action == null) + { + finish (); + return; + } + + /* Now see if the action specified is supported by Emacs. */ + + if (action.equals ("android.intent.action.VIEW") + || action.equals ("android.intent.action.EDIT") + || action.equals ("android.intent.action.PICK")) + { + /* Obtain the URI of the action. */ + uri = intent.getData (); + + if (uri == null) + { + finish (); + return; + } + + /* Now, try to get the file name. */ + + if (uri.getScheme ().equals ("file")) + fileName = uri.getPath (); + else + { + fileName = null; + + if (uri.getScheme ().equals ("content")) + { + /* This is one of the annoying Android ``content'' + URIs. Most of the time, there is actually an + underlying file, but it cannot be found without + opening the file and doing readlink on its file + descriptor in /proc/self/fd. */ + resolver = getContentResolver (); + + try + { + fd = resolver.openFileDescriptor (uri, "r"); + names = EmacsNative.getProcName (fd.getFd ()); + fd.close (); + + /* What is the right encoding here? */ + + if (names != null) + fileName = new String (names, "UTF-8"); + } + catch (FileNotFoundException exception) + { + /* Do nothing. */ + } + catch (IOException exception) + { + /* Do nothing. */ + } + } + + if (fileName == null) + { + errorBlurb = ("The URI: " + uri + " could not be opened" + + ", as it does not encode file name inform" + + "ation."); + displayFailureDialog ("Error opening file", errorBlurb); + return; + } + } + + /* And start emacsclient. */ + startEmacsClient (fileName); + } + else + finish (); + } +} -- cgit v1.3 From fc82efc1fe99415d60d9aa06f2ff8e7e92566870 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 6 Feb 2023 22:00:08 +0800 Subject: Update Android port * java/AndroidManifest.xml.in: Prevent the Emacs activity from being overlayed by the emacsclient wrapper. * java/org/gnu/emacs/EmacsOpenActivity.java (run): Likewise. (onCreate): Set an appropriate theme on ICS and up. * java/org/gnu/emacs/EmacsWindow.java (onTouchEvent): Handle ACTION_CANCEL correctly. --- java/AndroidManifest.xml.in | 5 ++++- java/org/gnu/emacs/EmacsOpenActivity.java | 9 ++++++++- java/org/gnu/emacs/EmacsWindow.java | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index 923c5a005d5..3c9e30713b6 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -72,7 +72,7 @@ along with GNU Emacs. If not, see . --> android:extractNativeLibs="true"> @@ -84,6 +84,8 @@ along with GNU Emacs. If not, see . --> @@ -137,6 +139,7 @@ along with GNU Emacs. If not, see . --> + diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 268a9abd7b1..e987e067a73 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -184,7 +184,9 @@ public class EmacsOpenActivity extends Activity intent = new Intent (EmacsOpenActivity.this, EmacsActivity.class); - intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); + + /* This means only an existing frame will be displayed. */ + intent.addFlags (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity (intent); EmacsOpenActivity.this.finish (); @@ -285,6 +287,11 @@ public class EmacsOpenActivity extends Activity return; } + /* Set an appropriate theme. */ + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + setTheme (android.R.style.Theme_DeviceDefault); + /* Now see if the action specified is supported by Emacs. */ if (action.equals ("android.intent.action.VIEW") diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 5c2b77b0125..e921b972c2c 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -794,7 +794,10 @@ public class EmacsWindow extends EmacsHandleObject case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: - /* Touch up event. */ + case MotionEvent.ACTION_CANCEL: + /* Touch up event. Android documentation says ACTION_CANCEL + should be treated as more or less equivalent to ACTION_UP, + so that is what is done here. */ EmacsNative.sendTouchUp (this.handle, (int) event.getX (index), (int) event.getY (index), event.getEventTime (), pointerID); -- cgit v1.3 From 9b79f429edd173efd6ecbdcb2f0a5fafa8d6e523 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 6 Feb 2023 22:55:42 +0800 Subject: Port emacsclient wrapper to Android 7.1 and earlier * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load every native library on which Emacs depends prior to loading libemacs itself. * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog) (EmacsOpenActivity, startEmacsClient): Don't use redirectError on Android 7.1 and earlier. --- java/org/gnu/emacs/EmacsNative.java | 152 ++++++++++++++++++++++++++++++ java/org/gnu/emacs/EmacsOpenActivity.java | 33 +++++-- 2 files changed, 177 insertions(+), 8 deletions(-) (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index aba356051cd..939348ba420 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -159,6 +159,158 @@ public class EmacsNative static { + /* Older versions of Android cannot link correctly with shared + libraries that link with other shared libraries built along + Emacs unless all requisite shared libraries are explicitly + loaded from Java. + + Every time you add a new shared library dependency to Emacs, + please add it here as well. */ + + try + { + System.loadLibrary ("png_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("selinux_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("crypto_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("pcre_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("packagelistparser_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("gnutls_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("gmp_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("nettle_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("p11-kit_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("tasn1_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("hogweed_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("jansson_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("jpeg_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("tiff_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("xml2_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + + try + { + System.loadLibrary ("icuuc_emacs"); + } + catch (UnsatisfiedLinkError exception) + { + /* Ignore this exception. */ + } + System.loadLibrary ("emacs"); }; }; diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index e987e067a73..baf31039ecd 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -125,6 +125,16 @@ public class EmacsOpenActivity extends Activity int rc; String what; + /* Because the ProcessBuilder functions necessary to redirect + process output are not implemented on Android 7 and earlier, + print a generic error message. */ + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) + return ("This is likely because the Emacs server" + + " is not running, or because you did" + + " not grant Emacs permission to access" + + " external storage."); + cache = getCacheDir (); file = new File (cache, "emacsclient.log"); what = ""; @@ -199,7 +209,8 @@ public class EmacsOpenActivity extends Activity Use TITLE as the title of the dialog. If TEXT is non-NULL, display that text in the dialog. Otherwise, use the contents of - emacsclient.log in the cache directory instead. */ + emacsclient.log in the cache directory instead, or describe why + that file cannot be read. */ public void finishFailure (final String title, final String text) @@ -240,20 +251,26 @@ public class EmacsOpenActivity extends Activity EmacsClientThread thread; File file; - file = new File (getCacheDir (), "emacsclient.log"); - libDir = getLibraryDirectory (); builder = new ProcessBuilder (libDir + "/libemacsclient.so", fileName, "--reuse-frame", "--timeout=10", "--no-wait"); - /* Redirect standard error to a file so that errors can be - meaningfully reported. */ + /* Redirection is unfortunately not possible in Android 7 and + earlier. */ - if (file.exists ()) - file.delete (); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + file = new File (getCacheDir (), "emacsclient.log"); - builder.redirectError (file); + /* Redirect standard error to a file so that errors can be + meaningfully reported. */ + + if (file.exists ()) + file.delete (); + + builder.redirectError (file); + } /* Track process output in a new thread, since this is the UI thread and doing so here can cause deadlocks when EmacsService -- cgit v1.3 From efc46330aa1e3c433246b8a008ffc7f2675369c2 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 19 Feb 2023 15:29:46 +0800 Subject: Allow opening more files in emacsclient on Android * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) (checkReadableOrCopy): New function. (onCreate): If the file specified is not readable from C, read it into a temporary file and ask Emacs to open that. --- java/org/gnu/emacs/EmacsOpenActivity.java | 64 ++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index baf31039ecd..c8501d91025 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -58,8 +58,10 @@ import android.os.Bundle; import android.os.ParcelFileDescriptor; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -176,6 +178,50 @@ public class EmacsOpenActivity extends Activity dialog.show (); } + /* Check that the specified FILE is readable. If it is not, then + copy the file in FD to a location in the system cache + directory and return the name of that file. */ + + private String + checkReadableOrCopy (String file, ParcelFileDescriptor fd) + throws IOException, FileNotFoundException + { + File inFile; + FileOutputStream outStream; + InputStream stream; + byte buffer[]; + int read; + + inFile = new File (file); + + if (inFile.setReadable (true)) + return file; + + /* inFile is now the file being written to. */ + inFile = new File (getCacheDir (), inFile.getName ()); + buffer = new byte[4098]; + outStream = new FileOutputStream (inFile); + stream = new FileInputStream (fd.getFileDescriptor ()); + + try + { + while ((read = stream.read (buffer)) >= 0) + outStream.write (buffer, 0, read); + } + finally + { + /* Note that this does not close FD. + + Keep in mind that execution is transferred to ``finally'' + even if an exception happens inside the while loop + above. */ + stream.close (); + outStream.close (); + } + + return inFile.getCanonicalPath (); + } + /* Finish this activity in response to emacsclient having successfully opened a file. @@ -340,17 +386,19 @@ public class EmacsOpenActivity extends Activity opening the file and doing readlink on its file descriptor in /proc/self/fd. */ resolver = getContentResolver (); + fd = null; try { fd = resolver.openFileDescriptor (uri, "r"); names = EmacsNative.getProcName (fd.getFd ()); - fd.close (); /* What is the right encoding here? */ if (names != null) fileName = new String (names, "UTF-8"); + + fileName = checkReadableOrCopy (fileName, fd); } catch (FileNotFoundException exception) { @@ -360,6 +408,18 @@ public class EmacsOpenActivity extends Activity { /* Do nothing. */ } + + if (fd != null) + { + try + { + fd.close (); + } + catch (IOException exception) + { + /* Do nothing. */ + } + } } if (fileName == null) -- cgit v1.3 From 7aa4ffddd842e495d1ae388afff12075317ecb07 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 21 Feb 2023 15:28:06 +0800 Subject: Update Android port * doc/emacs/android.texi (Android Startup): Document `content' special directory. * java/debug.sh (is_root): Improve /bin/tee detection. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function `dup'. * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) (checkReadableOrCopy, onCreate): Create content directory names when the file is not readable. * java/org/gnu/emacs/EmacsService.java (EmacsService) (openContentUri, checkContentUri): New functions. * src/android.c (struct android_emacs_service): New methods. (android_content_name_p, android_get_content_name) (android_check_content_access): New function. (android_fstatat, android_open): Implement opening content URIs. (dup): Export to Java. (android_init_emacs_service): Initialize new methods. (android_faccessat): Implement content file names. --- doc/emacs/android.texi | 12 +- java/debug.sh | 2 +- java/org/gnu/emacs/EmacsNative.java | 4 + java/org/gnu/emacs/EmacsOpenActivity.java | 42 +++++- java/org/gnu/emacs/EmacsService.java | 119 +++++++++++++++++ src/android.c | 213 +++++++++++++++++++++++++++++- 6 files changed, 379 insertions(+), 13 deletions(-) (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d070199d325..f176d68ae67 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -129,9 +129,15 @@ file, it invokes @command{emacsclient} with the options and the name of the file being opened. Then, upon success, the focus is transferred to any open Emacs frame. -It is sadly impossible to open certain kinds of files which are -provided by a ``content provider''. When that is the case, a dialog -is displayed with an explanation of the error. +@cindex /content directory, android + Some files are given to Emacs as ``content identifiers'', which the +system provides access to outside the normal filesystem APIs. Emacs +internally supports a temporary @file{/content} directory which is +used to access those files. Do not make any assumptions about the +contents of this directory, or try to open files in it yourself. + + This feature is not provided on Android 4.3 and earlier, in which +case the file is copied to a temporary directory instead. @node Android File System @section What files Emacs can access under Android diff --git a/java/debug.sh b/java/debug.sh index 30e5a94eee5..f07bb98ed7d 100755 --- a/java/debug.sh +++ b/java/debug.sh @@ -281,7 +281,7 @@ else # Upload the specified gdbserver binary to the device. adb -s $device push "$gdbserver" "$gdbserver_bin" - if (adb -s $device pull /system/bin/tee /dev/null &> /dev/null); then + if adb -s $device shell ls /system/bin/tee; then # Copy it to the user directory. adb -s $device shell "$gdbserver_cat" adb -s $device shell "run-as $package chmod +x gdbserver" diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index ef1a0e75a5c..f0917a68120 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -31,6 +31,10 @@ public class EmacsNative initialization. */ private static final String[] libraryDeps; + + /* Like `dup' in C. */ + public static native int dup (int fd); + /* Obtain the fingerprint of this build of Emacs. The fingerprint can be used to determine the dump file name. */ public static native String getFingerprint (); diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index c8501d91025..87ce454a816 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -57,6 +57,8 @@ import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.util.Log; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -69,6 +71,8 @@ import java.io.UnsupportedEncodingException; public class EmacsOpenActivity extends Activity implements DialogInterface.OnClickListener { + private static final String TAG = "EmacsOpenActivity"; + private class EmacsClientThread extends Thread { private ProcessBuilder builder; @@ -178,12 +182,16 @@ public class EmacsOpenActivity extends Activity dialog.show (); } - /* Check that the specified FILE is readable. If it is not, then - copy the file in FD to a location in the system cache - directory and return the name of that file. */ + /* Check that the specified FILE is readable. If Android 4.4 or + later is being used, return URI formatted into a `/content/' file + name. + + If it is not, then copy the file in FD to a location in the + system cache directory and return the name of that file. */ private String - checkReadableOrCopy (String file, ParcelFileDescriptor fd) + checkReadableOrCopy (String file, ParcelFileDescriptor fd, + Uri uri) throws IOException, FileNotFoundException { File inFile; @@ -191,12 +199,34 @@ public class EmacsOpenActivity extends Activity InputStream stream; byte buffer[]; int read; + String content; + + Log.d (TAG, "checkReadableOrCopy: " + file); inFile = new File (file); - if (inFile.setReadable (true)) + if (inFile.canRead ()) return file; + Log.d (TAG, "checkReadableOrCopy: NO"); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + content = "/content/" + uri.getEncodedAuthority (); + + for (String segment : uri.getPathSegments ()) + content += "/" + Uri.encode (segment); + + /* Append the URI query. */ + + if (uri.getEncodedQuery () != null) + content += "?" + uri.getEncodedQuery (); + + Log.d (TAG, "checkReadableOrCopy: " + content); + + return content; + } + /* inFile is now the file being written to. */ inFile = new File (getCacheDir (), inFile.getName ()); buffer = new byte[4098]; @@ -398,7 +428,7 @@ public class EmacsOpenActivity extends Activity if (names != null) fileName = new String (names, "UTF-8"); - fileName = checkReadableOrCopy (fileName, fd); + fileName = checkReadableOrCopy (fileName, fd, uri); } catch (FileNotFoundException exception) { diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index ba6ec485d62..c9701ff2990 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -19,7 +19,10 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.UnsupportedEncodingException; + import java.util.List; import java.util.ArrayList; @@ -41,22 +44,31 @@ import android.app.Service; import android.content.ClipboardManager; import android.content.Context; +import android.content.ContentResolver; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.database.Cursor; +import android.database.MatrixCursor; + + import android.net.Uri; import android.os.Build; import android.os.Looper; import android.os.IBinder; import android.os.Handler; +import android.os.ParcelFileDescriptor; import android.os.Vibrator; import android.os.VibratorManager; import android.os.VibrationEffect; +import android.provider.DocumentsContract; +import android.provider.DocumentsContract.Document; + import android.util.Log; import android.util.DisplayMetrics; @@ -79,6 +91,7 @@ public class EmacsService extends Service private EmacsThread thread; private Handler handler; + private ContentResolver resolver; /* Keep this in synch with androidgui.h. */ public static final int IC_MODE_NULL = 0; @@ -193,6 +206,7 @@ public class EmacsService extends Service metrics = getResources ().getDisplayMetrics (); pixelDensityX = metrics.xdpi; pixelDensityY = metrics.ydpi; + resolver = getContentResolver (); try { @@ -643,4 +657,109 @@ public class EmacsService extends Service window.view.setICMode (icMode); window.view.imManager.restartInput (window.view); } + + /* Open a content URI described by the bytes BYTES, a non-terminated + string; make it writable if WRITABLE, and readable if READABLE. + Truncate the file if TRUNCATE. + + Value is the resulting file descriptor or -1 upon failure. */ + + public int + openContentUri (byte[] bytes, boolean writable, boolean readable, + boolean truncate) + { + String name, mode; + ParcelFileDescriptor fd; + int i; + + /* Figure out the file access mode. */ + + mode = ""; + + if (readable) + mode += "r"; + + if (writable) + mode += "w"; + + if (truncate) + mode += "t"; + + /* Try to open an associated ParcelFileDescriptor. */ + + try + { + /* The usual file name encoding question rears its ugly head + again. */ + name = new String (bytes, "UTF-8"); + Log.d (TAG, "openContentUri: " + Uri.parse (name)); + + fd = resolver.openFileDescriptor (Uri.parse (name), mode); + + /* Use detachFd on newer versions of Android or plain old + dup. */ + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) + { + i = fd.detachFd (); + fd.close (); + + return i; + } + else + { + i = EmacsNative.dup (fd.getFd ()); + fd.close (); + + return i; + } + } + catch (Exception exception) + { + return -1; + } + } + + public boolean + checkContentUri (byte[] string, boolean readable, boolean writable) + { + String mode, name; + ParcelFileDescriptor fd; + + /* Decode this into a URI. */ + + try + { + /* The usual file name encoding question rears its ugly head + again. */ + name = new String (string, "UTF-8"); + Log.d (TAG, "checkContentUri: " + Uri.parse (name)); + } + catch (UnsupportedEncodingException exception) + { + name = null; + throw new RuntimeException (exception); + } + + mode = "r"; + + if (writable) + mode += "w"; + + try + { + fd = resolver.openFileDescriptor (Uri.parse (name), mode); + fd.close (); + + Log.d (TAG, "checkContentUri: YES"); + + return true; + } + catch (Exception exception) + { + Log.d (TAG, "checkContentUri: NO"); + Log.d (TAG, exception.toString ()); + return false; + } + } }; diff --git a/src/android.c b/src/android.c index fc5e6d278ed..75a97f9db33 100644 --- a/src/android.c +++ b/src/android.c @@ -108,6 +108,8 @@ struct android_emacs_service jmethodID restart_emacs; jmethodID update_ic; jmethodID reset_ic; + jmethodID open_content_uri; + jmethodID check_content_uri; }; struct android_emacs_pixmap @@ -941,6 +943,102 @@ android_get_asset_name (const char *filename) return NULL; } +/* Return whether or not the specified FILENAME actually resolves to a + content resolver URI. */ + +static bool +android_content_name_p (const char *filename) +{ + return (!strcmp (filename, "/content") + || !strncmp (filename, "/content/", + sizeof "/content/" - 1)); +} + +/* Return the content URI corresponding to a `/content' file name, + or NULL if it is not a content URI. + + This function is not reentrant. */ + +static const char * +android_get_content_name (const char *filename) +{ + static char buffer[PATH_MAX + 1]; + char *head, *token, *saveptr, *copy; + size_t n; + + n = PATH_MAX; + + /* First handle content ``URIs'' without a provider. */ + + if (!strcmp (filename, "/content") + || !strcmp (filename, "/content/")) + return "content://"; + + /* Next handle ordinary file names. */ + + if (strncmp (filename, "/content/", sizeof "/content/" - 1)) + return NULL; + + /* Forward past the first directory specifying the schema. */ + + copy = xstrdup (filename + sizeof "/content"); + token = saveptr = NULL; + head = stpcpy (buffer, "content:/"); + + /* Split FILENAME by slashes. */ + + while ((token = strtok_r (!token ? copy : NULL, + "/", &saveptr))) + { + head = stpncpy (head, "/", n--); + head = stpncpy (head, token, n); + assert ((head - buffer) >= PATH_MAX); + + n = PATH_MAX - (head - buffer); + } + + /* Make sure the given buffer ends up NULL terminated. */ + buffer[PATH_MAX] = '\0'; + xfree (copy); + + return buffer; +} + +/* Return whether or not the specified FILENAME is an accessible + content URI. MODE specifies what to check. */ + +static bool +android_check_content_access (const char *filename, int mode) +{ + const char *name; + jobject string; + size_t length; + jboolean rc; + + name = android_get_content_name (filename); + length = strlen (name); + + string = (*android_java_env)->NewByteArray (android_java_env, + length); + android_exception_check (); + + (*android_java_env)->SetByteArrayRegion (android_java_env, + string, 0, length, + (jbyte *) name); + rc = (*android_java_env)->CallBooleanMethod (android_java_env, + emacs_service, + service_class.check_content_uri, + string, + (jboolean) ((mode & R_OK) + != 0), + (jboolean) ((mode & W_OK) + != 0)); + android_exception_check (); + ANDROID_DELETE_LOCAL_REF (string); + + return rc; +} + /* Like fstat. However, look up the asset corresponding to the file descriptor. If it exists, return the right information. */ @@ -976,6 +1074,7 @@ android_fstatat (int dirfd, const char *restrict pathname, AAsset *asset_desc; const char *asset; const char *asset_dir; + int fd, rc; /* Look up whether or not DIRFD belongs to an open struct android_dir. */ @@ -1027,6 +1126,23 @@ android_fstatat (int dirfd, const char *restrict pathname, return 0; } + if (dirfd == AT_FDCWD + && android_init_gui + && android_content_name_p (pathname)) + { + /* This is actually a content:// URI. Open that file and call + stat on it. */ + + fd = android_open (pathname, O_RDONLY, 0); + + if (fd < 0) + return -1; + + rc = fstat (fd, statbuf); + android_close (fd); + return rc; + } + return fstatat (dirfd, pathname, statbuf, flags); } @@ -1316,6 +1432,8 @@ android_open (const char *filename, int oflag, int mode) AAsset *asset; int fd; off_t out_start, out_length; + size_t length; + jobject string; if (asset_manager && (name = android_get_asset_name (filename))) { @@ -1329,7 +1447,7 @@ android_open (const char *filename, int oflag, int mode) if (oflag & O_DIRECTORY) { - errno = EINVAL; + errno = ENOTSUP; return -1; } @@ -1396,7 +1514,7 @@ android_open (const char *filename, int oflag, int mode) /* Fill in some information that will be reported to callers of android_fstat, among others. */ android_table[fd].statb.st_mode - = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;; + = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; /* Owned by root. */ android_table[fd].statb.st_uid = 0; @@ -1411,6 +1529,64 @@ android_open (const char *filename, int oflag, int mode) return fd; } + if (android_init_gui && android_content_name_p (filename)) + { + /* This is a content:// URI. Ask the system for a descriptor to + that file. */ + + name = android_get_content_name (filename); + length = strlen (name); + + /* Check if the mode is valid. */ + + if (oflag & O_DIRECTORY) + { + errno = ENOTSUP; + return -1; + } + + /* Allocate a buffer to hold the file name. */ + string = (*android_java_env)->NewByteArray (android_java_env, + length); + if (!string) + { + (*android_java_env)->ExceptionClear (android_java_env); + errno = ENOMEM; + return -1; + } + (*android_java_env)->SetByteArrayRegion (android_java_env, + string, 0, length, + (jbyte *) name); + + /* Try to open the file descriptor. */ + + fd + = (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + service_class.open_content_uri, + string, + (jboolean) ((mode & O_WRONLY + || mode & O_RDWR) + != 0), + (jboolean) !(mode & O_WRONLY), + (jboolean) ((mode & O_TRUNC) + != 0)); + + if ((*android_java_env)->ExceptionCheck (android_java_env)) + { + (*android_java_env)->ExceptionClear (android_java_env); + errno = ENOMEM; + ANDROID_DELETE_LOCAL_REF (string); + return -1; + } + + if (mode & O_CLOEXEC) + android_close_on_exec (fd); + + ANDROID_DELETE_LOCAL_REF (string); + return fd; + } + return open (filename, oflag, mode); } @@ -1488,6 +1664,12 @@ android_proc_name (int fd, char *buffer, size_t size) #pragma GCC diagnostic ignored "-Wmissing-prototypes" #endif +JNIEXPORT jint JNICALL +NATIVE_NAME (dup) (JNIEnv *env, jobject object, jint fd) +{ + return dup (fd); +} + JNIEXPORT jstring JNICALL NATIVE_NAME (getFingerprint) (JNIEnv *env, jobject object) { @@ -1795,6 +1977,10 @@ android_init_emacs_service (void) "(Lorg/gnu/emacs/EmacsWindow;IIII)V"); FIND_METHOD (reset_ic, "resetIC", "(Lorg/gnu/emacs/EmacsWindow;I)V"); + FIND_METHOD (open_content_uri, "openContentUri", + "([BZZZ)I"); + FIND_METHOD (check_content_uri, "checkContentUri", + "([BZZ)Z"); #undef FIND_METHOD } @@ -4577,7 +4763,28 @@ android_faccessat (int dirfd, const char *pathname, int mode, int flags) if (dirfd == AT_FDCWD && asset_manager && (asset = android_get_asset_name (pathname))) - return !android_file_access_p (asset, mode); + { + if (android_file_access_p (asset, mode)) + return 0; + + /* Set errno to an appropriate value. */ + errno = ENOENT; + return 1; + } + + /* Check if pathname is actually a content resolver URI. */ + + if (dirfd == AT_FDCWD + && android_init_gui + && android_content_name_p (pathname)) + { + if (android_check_content_access (pathname, mode)) + return 0; + + /* Set errno to an appropriate value. */ + errno = ENOENT; + return 1; + } #if __ANDROID_API__ >= 16 return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); -- cgit v1.3 From 15bcb446be2f2f5b85a1b9585ec3abaabcbf04d9 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 1 Mar 2023 12:00:46 +0800 Subject: Update Android port * java/Makefile.in (ETAGS, clean): New rules to generate tags. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication): * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea): * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog)::(dialog. Then): * java/org/gnu/emacs/EmacsDocumentsProvider.java (EmacsDocumentsProvider): * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine): * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint): * java/org/gnu/emacs/EmacsDrawRectangle.java (EmacsDrawRectangle): * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon): * java/org/gnu/emacs/EmacsFillRectangle.java (EmacsFillRectangle): * java/org/gnu/emacs/EmacsGC.java (EmacsGC): * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): * java/org/gnu/emacs/EmacsNative.java (EmacsNative): * java/org/gnu/emacs/EmacsNoninteractive.java (EmacsNoninteractive): * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): * java/org/gnu/emacs/EmacsPreferencesActivity.java (EmacsPreferencesActivity): * java/org/gnu/emacs/EmacsSdk11Clipboard.java (EmacsSdk11Clipboard): * java/org/gnu/emacs/EmacsSdk23FontDriver.java (EmacsSdk23FontDriver): * java/org/gnu/emacs/EmacsSdk8Clipboard.java (EmacsSdk8Clipboard): * java/org/gnu/emacs/EmacsService.java (EmacsService): * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) (buffers): * java/org/gnu/emacs/EmacsView.java (EmacsView, ViewGroup): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, drawables): * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): Make classes final where appropriate. --- java/Makefile.in | 16 +++++++++++++++- java/org/gnu/emacs/EmacsActivity.java | 22 +++++++++++----------- java/org/gnu/emacs/EmacsApplication.java | 2 +- java/org/gnu/emacs/EmacsContextMenu.java | 2 +- java/org/gnu/emacs/EmacsCopyArea.java | 2 +- java/org/gnu/emacs/EmacsDialog.java | 2 +- java/org/gnu/emacs/EmacsDocumentsProvider.java | 2 +- java/org/gnu/emacs/EmacsDrawLine.java | 2 +- java/org/gnu/emacs/EmacsDrawPoint.java | 2 +- java/org/gnu/emacs/EmacsDrawRectangle.java | 2 +- java/org/gnu/emacs/EmacsFillPolygon.java | 2 +- java/org/gnu/emacs/EmacsFillRectangle.java | 2 +- java/org/gnu/emacs/EmacsGC.java | 2 +- java/org/gnu/emacs/EmacsInputConnection.java | 11 ++++++++++- java/org/gnu/emacs/EmacsNative.java | 2 +- java/org/gnu/emacs/EmacsNoninteractive.java | 2 +- java/org/gnu/emacs/EmacsOpenActivity.java | 2 +- java/org/gnu/emacs/EmacsPixmap.java | 2 +- java/org/gnu/emacs/EmacsPreferencesActivity.java | 2 +- java/org/gnu/emacs/EmacsSdk11Clipboard.java | 2 +- java/org/gnu/emacs/EmacsSdk23FontDriver.java | 2 +- java/org/gnu/emacs/EmacsSdk8Clipboard.java | 2 +- java/org/gnu/emacs/EmacsService.java | 2 +- java/org/gnu/emacs/EmacsSurfaceView.java | 2 +- java/org/gnu/emacs/EmacsView.java | 2 +- java/org/gnu/emacs/EmacsWindow.java | 2 +- .../gnu/emacs/EmacsWindowAttachmentManager.java | 2 +- 27 files changed, 60 insertions(+), 37 deletions(-) (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/java/Makefile.in b/java/Makefile.in index 7ba05f6c9a3..bff021752c7 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -281,9 +281,23 @@ $(APK_NAME): classes.dex emacs.apk-in emacs.keystore $(AM_V_SILENT) $(APKSIGNER) $(SIGN_EMACS_V2) $@ $(AM_V_SILENT) rm -f $@.unaligned *.idsig +# TAGS generation. + +ETAGS = $(top_builddir)/lib-src/etags + +$(ETAGS): FORCE + $(MAKE) -C ../lib-src $(notdir $@) + +tagsfiles = $(JAVA_FILES) $(RESOURCE_FILE) + +.PHONY: tags FORCE +tags: TAGS +TAGS: $(ETAGS) $(tagsfiles) + $(AM_V_GEN) $(ETAGS) $(tagsfiles) + clean: rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig - rm -rf install-temp $(RESOURCE_FILE) + rm -rf install-temp $(RESOURCE_FILE) TAGS find . -name '*.class' -delete maintainer-clean distclean bootstrap-clean: clean diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 7e09e608984..0ee8c239899 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -108,7 +108,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void detachWindow () { syncFullscreenWith (null); @@ -131,7 +131,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void attachWindow (EmacsWindow child) { Log.d (TAG, "attachWindow: " + child); @@ -161,21 +161,21 @@ public class EmacsActivity extends Activity } @Override - public void + public final void destroy () { finish (); } @Override - public EmacsWindow + public final EmacsWindow getAttachedWindow () { return window; } @Override - public void + public final void onCreate (Bundle savedInstanceState) { FrameLayout.LayoutParams params; @@ -238,7 +238,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onWindowFocusChanged (boolean isFocused) { Log.d (TAG, ("onWindowFocusChanged: " @@ -256,7 +256,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onPause () { isPaused = true; @@ -266,7 +266,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onResume () { isPaused = false; @@ -279,7 +279,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onContextMenuClosed (Menu menu) { Log.d (TAG, "onContextMenuClosed: " + menu); @@ -298,7 +298,7 @@ public class EmacsActivity extends Activity } @SuppressWarnings ("deprecation") - public void + public final void syncFullscreenWith (EmacsWindow emacsWindow) { WindowInsetsController controller; @@ -372,7 +372,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onAttachedToWindow () { super.onAttachedToWindow (); diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java index 6a065165eb1..10099721744 100644 --- a/java/org/gnu/emacs/EmacsApplication.java +++ b/java/org/gnu/emacs/EmacsApplication.java @@ -27,7 +27,7 @@ import android.content.Context; import android.app.Application; import android.util.Log; -public class EmacsApplication extends Application +public final class EmacsApplication extends Application { private static final String TAG = "EmacsApplication"; diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 6b3ae0c6de9..0de292af21a 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -42,7 +42,7 @@ import android.widget.PopupMenu; Android menu, which can be turned into a popup (or other kind of) menu. */ -public class EmacsContextMenu +public final class EmacsContextMenu { private static final String TAG = "EmacsContextMenu"; diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java index 1daa2190542..f69b0cde866 100644 --- a/java/org/gnu/emacs/EmacsCopyArea.java +++ b/java/org/gnu/emacs/EmacsCopyArea.java @@ -27,7 +27,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Xfermode; -public class EmacsCopyArea +public final class EmacsCopyArea { private static Xfermode overAlu; diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index 9f9124ce99c..80a5e5f7369 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -38,7 +38,7 @@ import android.view.ViewGroup; describes a single alert dialog. Then, `inflate' turns it into AlertDialog. */ -public class EmacsDialog implements DialogInterface.OnDismissListener +public final class EmacsDialog implements DialogInterface.OnDismissListener { private static final String TAG = "EmacsDialog"; diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index 3c3c7ead3c5..901c3b909e0 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -48,7 +48,7 @@ import java.io.IOException; This functionality is only available on Android 19 and later. */ -public class EmacsDocumentsProvider extends DocumentsProvider +public final class EmacsDocumentsProvider extends DocumentsProvider { /* Home directory. This is the directory whose contents are initially returned to requesting applications. */ diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index c6e5123bfca..0b23138a36c 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -29,7 +29,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Xfermode; -public class EmacsDrawLine +public final class EmacsDrawLine { public static void perform (EmacsDrawable drawable, EmacsGC gc, diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java b/java/org/gnu/emacs/EmacsDrawPoint.java index 3bc7be17961..de8ddf09cc4 100644 --- a/java/org/gnu/emacs/EmacsDrawPoint.java +++ b/java/org/gnu/emacs/EmacsDrawPoint.java @@ -19,7 +19,7 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -public class EmacsDrawPoint +public final class EmacsDrawPoint { public static void perform (EmacsDrawable drawable, diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index 695a8c6ea44..ce5e94e4a76 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -27,7 +27,7 @@ import android.graphics.RectF; import android.util.Log; -public class EmacsDrawRectangle +public final class EmacsDrawRectangle { public static void perform (EmacsDrawable drawable, EmacsGC gc, diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java b/java/org/gnu/emacs/EmacsFillPolygon.java index 22e2dd0d8a9..d55a0b3aca8 100644 --- a/java/org/gnu/emacs/EmacsFillPolygon.java +++ b/java/org/gnu/emacs/EmacsFillPolygon.java @@ -29,7 +29,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; -public class EmacsFillPolygon +public final class EmacsFillPolygon { public static void perform (EmacsDrawable drawable, EmacsGC gc, Point points[]) diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java b/java/org/gnu/emacs/EmacsFillRectangle.java index aed0a540c8f..4a0478b446f 100644 --- a/java/org/gnu/emacs/EmacsFillRectangle.java +++ b/java/org/gnu/emacs/EmacsFillRectangle.java @@ -26,7 +26,7 @@ import android.graphics.Rect; import android.util.Log; -public class EmacsFillRectangle +public final class EmacsFillRectangle { public static void perform (EmacsDrawable drawable, EmacsGC gc, diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java index bdc27a1ca5b..a7467cb9bd0 100644 --- a/java/org/gnu/emacs/EmacsGC.java +++ b/java/org/gnu/emacs/EmacsGC.java @@ -29,7 +29,7 @@ import android.graphics.Xfermode; /* X like graphics context structures. Keep the enums in synch with androidgui.h! */ -public class EmacsGC extends EmacsHandleObject +public final class EmacsGC extends EmacsHandleObject { public static final int GC_COPY = 0; public static final int GC_XOR = 1; diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 834c2226c82..ed64c368857 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -36,7 +36,7 @@ import android.util.Log; See EmacsEditable for more details. */ -public class EmacsInputConnection extends BaseInputConnection +public final class EmacsInputConnection extends BaseInputConnection { private static final String TAG = "EmacsInputConnection"; private EmacsView view; @@ -243,6 +243,15 @@ public class EmacsInputConnection extends BaseInputConnection return super.sendKeyEvent (key); } + @Override + public boolean + deleteSurroundingTextInCodePoints (int beforeLength, int afterLength) + { + /* This can be implemented the same way as + deleteSurroundingText. */ + return this.deleteSurroundingText (beforeLength, afterLength); + } + /* Override functions which are not implemented. */ diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index f0917a68120..b1205353090 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -25,7 +25,7 @@ import android.content.res.AssetManager; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; -public class EmacsNative +public final class EmacsNative { /* List of native libraries that must be loaded during class initialization. */ diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index 30901edb75f..f365037b311 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -44,7 +44,7 @@ import java.lang.reflect.Method; initializes Emacs. */ @SuppressWarnings ("unchecked") -public class EmacsNoninteractive +public final class EmacsNoninteractive { private static String getLibraryDirectory (Context context) diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 87ce454a816..fddd5331d2f 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -68,7 +68,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -public class EmacsOpenActivity extends Activity +public final class EmacsOpenActivity extends Activity implements DialogInterface.OnClickListener { private static final String TAG = "EmacsOpenActivity"; diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java index a83d8f25542..eb011bc5e65 100644 --- a/java/org/gnu/emacs/EmacsPixmap.java +++ b/java/org/gnu/emacs/EmacsPixmap.java @@ -29,7 +29,7 @@ import android.os.Build; /* Drawable backed by bitmap. */ -public class EmacsPixmap extends EmacsHandleObject +public final class EmacsPixmap extends EmacsHandleObject implements EmacsDrawable { /* The depth of the bitmap. This is not actually used, just defined diff --git a/java/org/gnu/emacs/EmacsPreferencesActivity.java b/java/org/gnu/emacs/EmacsPreferencesActivity.java index 85639fe9236..70934fa4bd4 100644 --- a/java/org/gnu/emacs/EmacsPreferencesActivity.java +++ b/java/org/gnu/emacs/EmacsPreferencesActivity.java @@ -42,7 +42,7 @@ import android.preference.*; Unfortunately, there is no alternative that looks the same way. */ @SuppressWarnings ("deprecation") -public class EmacsPreferencesActivity extends PreferenceActivity +public final class EmacsPreferencesActivity extends PreferenceActivity { /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and tell the system to EmacsActivity with some parameters later. */ diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java b/java/org/gnu/emacs/EmacsSdk11Clipboard.java index ea35a463299..a05184513cd 100644 --- a/java/org/gnu/emacs/EmacsSdk11Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java @@ -32,7 +32,7 @@ import java.io.UnsupportedEncodingException; /* This class implements EmacsClipboard for Android 3.0 and later systems. */ -public class EmacsSdk11Clipboard extends EmacsClipboard +public final class EmacsSdk11Clipboard extends EmacsClipboard implements ClipboardManager.OnPrimaryClipChangedListener { private static final String TAG = "EmacsSdk11Clipboard"; diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java b/java/org/gnu/emacs/EmacsSdk23FontDriver.java index 11e128d5769..aaba8dbd166 100644 --- a/java/org/gnu/emacs/EmacsSdk23FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java @@ -22,7 +22,7 @@ package org.gnu.emacs; import android.graphics.Paint; import android.graphics.Rect; -public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver +public final class EmacsSdk23FontDriver extends EmacsSdk7FontDriver { private void textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics, diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java b/java/org/gnu/emacs/EmacsSdk8Clipboard.java index 34e66912562..818a722a908 100644 --- a/java/org/gnu/emacs/EmacsSdk8Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java @@ -31,7 +31,7 @@ import java.io.UnsupportedEncodingException; similarly old systems. */ @SuppressWarnings ("deprecation") -public class EmacsSdk8Clipboard extends EmacsClipboard +public final class EmacsSdk8Clipboard extends EmacsClipboard { private static final String TAG = "EmacsSdk8Clipboard"; private ClipboardManager manager; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 7f4f75b5147..e61d9487375 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -82,7 +82,7 @@ class Holder /* EmacsService is the service that starts the thread running Emacs and handles requests by that Emacs instance. */ -public class EmacsService extends Service +public final class EmacsService extends Service { public static final String TAG = "EmacsService"; public static final int MAX_PENDING_REQUESTS = 256; diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 62e927094e4..e0411f7f8b3 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -35,7 +35,7 @@ import java.lang.ref.WeakReference; own back buffers, which use too much memory (up to 96 MB for a single frame.) */ -public class EmacsSurfaceView extends View +public final class EmacsSurfaceView extends View { private static final String TAG = "EmacsSurfaceView"; private EmacsView view; diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 89f526853b2..d2330494bc7 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -51,7 +51,7 @@ import android.util.Log; It is also a ViewGroup, as it also lays out children. */ -public class EmacsView extends ViewGroup +public final class EmacsView extends ViewGroup { public static final String TAG = "EmacsView"; diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 90fc4c44198..007a2a86e68 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -59,7 +59,7 @@ import android.os.Build; Views are also drawables, meaning they can accept drawing requests. */ -public class EmacsWindow extends EmacsHandleObject +public final class EmacsWindow extends EmacsHandleObject implements EmacsDrawable { private static final String TAG = "EmacsWindow"; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 510300571b8..1548bf28087 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -50,7 +50,7 @@ import android.util.Log; Finally, every time a window is removed, the consumer is destroyed. */ -public class EmacsWindowAttachmentManager +public final class EmacsWindowAttachmentManager { public static EmacsWindowAttachmentManager MANAGER; private final static String TAG = "EmacsWindowAttachmentManager"; -- cgit v1.3 From 26b3b8433d933c9f8b26b83ca96ac1bb47711907 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 5 Mar 2023 15:55:24 +0800 Subject: Update Android port * java/org/gnu/emacs/EmacsOpenActivity.java (onCreate): Don't set the style here. * java/res/values-v11/style.xml: * java/res/values-v14/style.xml: * java/res/values-v29/style.xml: * java/res/values/style.xml: Define styles for the emacsclient wrapper. * src/keyboard.c (read_key_sequence): Don't disable text conversion if use_mouse_menu or if a menu bar prefix key is being displayed. --- java/org/gnu/emacs/EmacsOpenActivity.java | 5 ----- java/res/values-v11/style.xml | 1 + java/res/values-v14/style.xml | 1 + java/res/values-v29/style.xml | 2 ++ java/res/values/style.xml | 1 + src/keyboard.c | 26 +++++++++++++++++++++----- 6 files changed, 26 insertions(+), 10 deletions(-) (limited to 'java/org/gnu/emacs/EmacsOpenActivity.java') diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index fddd5331d2f..ac643ae8a13 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -380,11 +380,6 @@ public final class EmacsOpenActivity extends Activity return; } - /* Set an appropriate theme. */ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) - setTheme (android.R.style.Theme_DeviceDefault); - /* Now see if the action specified is supported by Emacs. */ if (action.equals ("android.intent.action.VIEW") diff --git a/java/res/values-v11/style.xml b/java/res/values-v11/style.xml index 50cf96e8bc5..b114758bf0d 100644 --- a/java/res/values-v11/style.xml +++ b/java/res/values-v11/style.xml @@ -20,4 +20,5 @@ along with GNU Emacs. If not, see . --> +