emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android d44b60c2f00 2/6: Update Android port


From: Po Lu
Subject: feature/android d44b60c2f00 2/6: Update Android port
Date: Fri, 20 Jan 2023 09:18:21 -0500 (EST)

branch: feature/android
commit d44b60c2f001d57b010f0e9b82f798fbad9a23d6
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Update Android port
    
    * .gitignore: Don't ignore verbose.mk.android.
    * doc/emacs/Makefile.in (EMACSSOURCES): Add android.texi and
    input.texi.
    * doc/emacs/android.texi (Android): Document support for the
    on-screen keyboard.
    (Android Startup): Document how to start Emacs with -Q on
    Android.
    (Android Environment): Document how Emacs works around the
    system ``task killer''.  Document changes to frame deletion
    behavior.
    * doc/emacs/emacs.texi (Top):
    * doc/emacs/input.texi (Other Input Devices, On-Screen
    Keyboards): Document how to use Emacs with virtual keyboards.
    * doc/lispref/commands.texi (Touchscreen Events): Document
    changes to `touch-screen-track-drag'.
    * doc/lispref/frames.texi (Frames, On-Screen Keyboards): New
    node.
    * java/AndroidManifest.xml.in: Add settings activity and
    appropriate OSK adjustment mode.
    * java/org/gnu/emacs/EmacsActivity.java (onCreate): Allow
    creating Emacs with -Q.
    (onDestroy): Don't remove if killed by the system.
    * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems):
    Fix context menus again.
    * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Make all
    event sending functions return long.
    * java/org/gnu/emacs/EmacsPreferencesActivity.java
    (EmacsPreferencesActivity): New class.
    * java/org/gnu/emacs/EmacsService.java (EmacsService)
    (onStartCommand, onCreate, startEmacsService): Start as a
    foreground service if necessary to bypass system restrictions.
    * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
    * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run):
    * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout)
    (onDetachedFromWindow):
    * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, viewLayout):
    Implement frame resize synchronization..
    * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
    (EmacsWindowAttachmentManager, removeWindowConsumer): Adjust
    accordingly for changes to frame deletion behavior.
    * lisp/frame.el (android-toggle-on-screen-keyboard)
    (frame-toggle-on-screen-keyboard): New function.
    * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard)
    (minibuffer-exit-on-screen-keyboard): New functions.
    (minibuffer-setup-hook, minibuffer-exit-hook): Add new functions
    to hooks.
    
    * lisp/touch-screen.el (touch-screen-relative-xy): Accept new
    value of window `frame'.  Return frame coordinates in that case.
    (touch-screen-set-point-commands): New variable.
    (touch-screen-handle-point-up): Respect that variable.
    (touch-screen-track-drag): Return `no-drag' where appropriate.
    (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line):
    Refactor to use `no-drag'.
    
    * src/android.c (struct android_emacs_window): New methods.
    Make all event sending functions return the event serial.
    (android_toggle_on_screen_keyboard, android_window_updated): New
    functions.
    * src/android.h: Update prototypes.
    * src/androidfns.c (Fandroid_toggle_on_screen_keyboard)
    (syms_of_androidfns): New function.
    * src/androidgui.h (struct android_any_event)
    (struct android_key_event, struct android_configure_event)
    (struct android_focus_event, struct android_window_action_event)
    (struct android_crossing_event, struct android_motion_event)
    (struct android_button_event, struct android_touch_event)
    (struct android_wheel_event, struct android_iconify_event)
    (struct android_menu_event): Add `serial' fields.
    
    * src/androidterm.c (handle_one_android_event)
    (android_frame_up_to_date):
    * src/androidterm.h (struct android_output): Implement frame
    resize synchronization.
---
 .gitignore                                         |   1 +
 doc/emacs/Makefile.in                              |   2 +
 doc/emacs/android.texi                             |  70 +++++--
 doc/emacs/emacs.texi                               |   1 +
 doc/emacs/input.texi                               |  36 ++++
 doc/lispref/commands.texi                          |   8 +-
 doc/lispref/frames.texi                            |  21 +++
 java/AndroidManifest.xml.in                        |  12 ++
 java/org/gnu/emacs/EmacsActivity.java              |  20 +-
 java/org/gnu/emacs/EmacsContextMenu.java           |   1 +
 java/org/gnu/emacs/EmacsNative.java                |  39 ++--
 java/org/gnu/emacs/EmacsPreferencesActivity.java   |  98 ++++++++++
 java/org/gnu/emacs/EmacsService.java               |  62 ++++++-
 java/org/gnu/emacs/EmacsSurfaceView.java           | 127 +++++++++----
 java/org/gnu/emacs/EmacsThread.java                |  12 +-
 java/org/gnu/emacs/EmacsView.java                  |  55 +++++-
 java/org/gnu/emacs/EmacsWindow.java                |  58 +++++-
 .../gnu/emacs/EmacsWindowAttachmentManager.java    |   4 +-
 lisp/frame.el                                      |  17 ++
 lisp/minibuffer.el                                 |  23 +++
 lisp/touch-screen.el                               | 203 +++++++++++++--------
 src/android.c                                      | 147 ++++++++++++---
 src/android.h                                      |   2 +
 src/androidfns.c                                   |  25 +++
 src/androidgui.h                                   |  20 ++
 src/androidterm.c                                  |  24 +++
 src/androidterm.h                                  |   5 +
 27 files changed, 894 insertions(+), 199 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3bc40c67489..6494e4e8f39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,7 @@ src/emacs-module.h
 # Built by recursive call to `configure'.
 *.android
 !INSTALL.android
+!verbose.mk.android
 
 # Built by `java'.
 java/install_temp/*
diff --git a/doc/emacs/Makefile.in b/doc/emacs/Makefile.in
index 161bdcb1c59..c7415312753 100644
--- a/doc/emacs/Makefile.in
+++ b/doc/emacs/Makefile.in
@@ -146,6 +146,8 @@ EMACSSOURCES= \
        ${srcdir}/glossary.texi \
        ${srcdir}/ack.texi \
        ${srcdir}/kmacro.texi \
+       ${srcdir}/android.texi \
+       ${srcdir}/input.texi \
        $(EMACS_XTRA)
 
 ## Disable implicit rules.
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index 01a2e68a97e..ba5d2ca3e09 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -10,8 +10,8 @@ Alliance.  This section describes the peculiarities of using 
Emacs on
 an Android device running Android 2.2 or later.
 
   Android devices commonly rely on user input through a touch screen
-or digitizer device.  For more information about using them with
-Emacs, @pxref{other Input Devices}.
+or digitizer device and on-screen keyboard.  For more information
+about using such devices with Emacs, @pxref{Other Input Devices}.
 
 @menu
 * What is Android?::   Preamble.
@@ -82,6 +82,16 @@ command on that other system:
 $ adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)"
 @end example
 
+@cindex emacs -Q, android
+Since Android has no command line, there is normally no way to specify
+command-line arguments.  However, Emacs can be started with the
+equivalent of the @code{--quick} option (@pxref{Initial Options})
+through a special preferences screen, which can be accessed through
+the Emacs ``app info'' page in the system settings application.
+
+Consult the manufacturer of your device for more details, as how to do
+this varies by device.
+
 @node Android File System
 @section What files Emacs can access under Android
 @cindex /assets directory, android
@@ -149,7 +159,7 @@ which is the app data directory (@pxref{Android File 
System}.)
 directories, and the app data directories of other applications.  In
 recent versions of Android, the system also prohibits, for security
 reasons, even Emacs itself from running executables inside the app
-data directory!
+data directory.
 
   Emacs comes with several binaries.  While being executable files,
 they are packaged as libraries in the library directory, because
@@ -173,9 +183,28 @@ within itself.
   Application processes are treated as disposable entities by the
 system.  When all Emacs frames move to the background, Emacs is liable
 to be killed by the system at any time, for the purpose of saving
-resources.  There is currently no easy way to bypass these
-restrictions, aside from keeping Emacs constantly running in the
-foreground.
+system resources.
+
+  On Android 7.1 and earlier, Emacs tells the system to treat it as a
+``background service''.  The system will try to avoid killing Emacs
+unless the device is under memory stress.
+
+  Android 8.0 removed the ability for background services to receive
+such special treatment.  However, Emacs applies a workaround: the
+system considers applications that create a permanent notification to
+be performing active work, and will avoid killing such applications.
+Thus, on those systems, Emacs displays a permanant notification for as
+long as it is running.  Once the notification is displayed, it can be
+safely hidden through the system settings without resulting in Emacs
+being killed.
+
+  However, it is not guaranteed that the system will not kill Emacs,
+even if the notification is being displayed.  While the Open Handset
+Alliance's sample implementation of Android behaves correctly, many
+manufacturers place additional restrictions on program execution in
+the background in their proprietary versions of Android.  There is a
+list of such troublesome manufacturers and sometimes workarounds, at
+@url{https://dontkillmyapp.com/}.
 
 @section Android permissions
 @cindex external storage, android
@@ -272,14 +301,14 @@ maximized or full-screen, and only one window can be 
displayed at a
 time.  On larger devices, the system allows up to four windows to be
 tiled on the screen at any time.
 
-Windows on Android do not continue to exist indefinitely after they
+  Windows on Android do not continue to exist indefinitely after they
 are created.  Instead, the system may choose to terminate windows that
 are not on screen in order to save memory, with the assumption that
 the program will save its contents to disk and restore them later,
 when the user asks to open it again.  As this is obvious not possible
 with Emacs, Emacs separates a frame from a system window.
 
-Each system window created (including the initial window created
+  Each system window created (including the initial window created
 during Emacs startup) is appended to a list of windows that do not
 have associated frames.  When a frame is created, Emacs looks up any
 window within that list, and displays the contents of the frame
@@ -287,11 +316,28 @@ within; if there is no window at all, then one is 
created.  Likewise,
 when a new window is created by the system, Emacs places the contents
 of any frame that is not already displayed within a window inside.
 When a frame is closed, the corresponding system window is also
-closed.
+closed.  Upon startup, the system creates a window itself (within
+which Emacs displays the first window system frame shortly
+thereafter.)  Emacs differentiates between that window and windows
+created on behalf of other frames to determine what to do when the
+system window associated with a frame is closed:
+
+@itemize @bullet
+@item
+When the system closes the window created during application startup
+in order to save memory, Emacs retains the frame for when that window
+is created later.
 
-This strategy works as long as one window is in the foreground.
-Otherwise, Emacs can only run in the background for a limited amount
-of time before the process is killed completely.
+@item
+When the user closes the window created during application startup,
+and the window was not previously closed by the system in order to
+save resources, Emacs deletes any frame displayed within that window.
+
+@item
+When the user or the system closes any window created by Emacs on
+behalf of a specific frame, Emacs deletes the frame displayed within
+that window.
+@end itemize
 
 @cindex windowing limitations, android
 @cindex frame parameters, android
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 9fd169cd5cc..0cb454e5294 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -1269,6 +1269,7 @@ Emacs and Android
 Emacs and unconventional input devices
 
 * Touchscreens::       Using Emacs on touchscreens.
+* On-Screen Keyboards::        Using Emacs with virtual keyboards.
 
 Emacs and Microsoft Windows/MS-DOS
 
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
index 0e029c401e3..1a58d1ca0ac 100644
--- a/doc/emacs/input.texi
+++ b/doc/emacs/input.texi
@@ -16,6 +16,7 @@ input devices, which is detailed here.
 
 @menu
 * Touchscreens::               Using Emacs on touchscreens.
+* On-Screen Keyboards::                Using Emacs with virtual keyboards.
 @end menu
 
 @node Touchscreens
@@ -58,3 +59,38 @@ were to be held down.  @xref{Mouse Commands}.
   By default, Emacs considers a tool as having been left on the
 display for a while after 0.7 seconds, but this can be changed by
 customizing the variable @code{touch-screen-delay}.
+
+@node On-Screen Keyboards
+@section Using Emacs with virtual keyboards
+@cindex virtual keyboards
+@cindex on-screen keyboards
+
+  When there is no physical keyboard attached to a system, the
+windowing system typically provides an on-screen keyboard, more often
+known as a ``virtual keyboard'', containing rows of clickable buttons
+that send keyboard input to the application, much like a real keyboard
+would.  This virtual keyboard is hidden by default, as it uses up
+valuable on-screen real estate, and must be opened once the program
+being used is ready to accept keyboard input.
+
+  Under the X Window System, the client that provides the on-screen
+keyboard typically detects when the application is ready to accept
+keyboard input through a set of complex heuristics, and automatically
+displays the keyboard when necessary.
+
+  On other systems such as Android, Emacs must tell the system when it
+is ready to accept keyboard input.  Typically, this is done in
+response to a touchscreen ``tap'' gesture (@pxref{Touchscreens}), or
+once to the minibuffer becomes in use (@pxref{Minibuffer}.)
+
+@vindex touch-screen-set-point-commands
+  When a ``tap'' gesture results in a command being executed, Emacs
+checks to see whether or not the command is supposed to set the point
+by looking for it in the list @code{touch-screen-set-point-commands}.
+If it is, then Emacs looks up whether or not the text under the point
+is read-only; if not, it activates the on-screen keyboard, assuming
+that the user is about to enter text in to the current buffer.
+
+  Emacs also provides a set of functions to show or hide the on-screen
+keyboard.  For more details, @pxref{On-Screen Keyboards,,, elisp, The
+Emacs Lisp Reference Manual}.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 59367a2cecc..484c7dc2a06 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2055,9 +2055,11 @@ The caller should not perform any action in that case.
 
 @defun touch-screen-track-drag event update &optional data
 This function is used to track a single ``drag'' gesture originating
-from the @code{touchscreen-begin} event @code{event}.  Currently, it
-behaves identically to @code{touch-screen-track-tap}, but differences
-are anticipated in the future.
+from the @code{touchscreen-begin} event @code{event}.
+
+It behaves like @code{touch-screen-track-tap}, except that it returns
+@code{no-drag} if the touchpoint in @code{event} did not move far
+enough to qualify as an actual drag.
 @end defun
 
 @node Focus Events
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 2ceafab7a6b..497715bdb19 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -104,6 +104,7 @@ window of another Emacs frame.  @xref{Child Frames}.
 * Mouse Tracking::              Getting events that say when the mouse moves.
 * Mouse Position::              Asking where the mouse is, or moving it.
 * Pop-Up Menus::                Displaying a menu for the user to select from.
+* On-Screen Keyboards::                Displaying the virtual keyboard.
 * Dialog Boxes::                Displaying a box to ask yes or no.
 * Pointer Shape::               Specifying the shape of the mouse pointer.
 * Window System Selections::    Transferring text to and from other X clients.
@@ -3812,6 +3813,26 @@ keymap.  It won't be called if @code{x-popup-menu} 
returns for some
 other reason without displaying a pop-up menu.
 @end defvar
 
+@node On-Screen Keyboards
+@section On-Screen Keyboards
+
+  An on-screen keyboard is a special kind of pop up provided by the
+system, with rows of clickable buttons that act as a real keyboard.
+
+  On certain systems (@pxref{On-Screen Keyboards,,,emacs, The Emacs
+Manual}), Emacs is supposed to display and hide the on screen keyboard
+depending on whether or not the user is about to type something.
+
+@defun frame-toggle-on-screen-keyboard frame hide
+This function displays or hides the on-screen keyboard on behalf of
+the frame @var{frame}.  If @var{hide} is non-@code{nil}, then the
+on-screen keyboard is hidden; otherwise, it is displayed.
+
+This has no effect if the system automatically detects when to display
+the on-screen keyboard, or when it does not provide any on-screen
+keyboard.
+@end defun
+
 @node Dialog Boxes
 @section Dialog Boxes
 @cindex dialog boxes
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
index b680137a9d0..74f69d2a8e5 100644
--- a/java/AndroidManifest.xml.in
+++ b/java/AndroidManifest.xml.in
@@ -62,8 +62,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
               android:theme="@android:style/Theme"
               android:debuggable="true"
               android:extractNativeLibs="true">
+
     <activity android:name="org.gnu.emacs.EmacsActivity"
              android:launchMode="singleTop"
+             android:windowSoftInputMode="adjustResize"
              
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
       <intent-filter>
         <action android:name="android.intent.action.MAIN" />
@@ -73,8 +75,18 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
     </activity>
 
     <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
+             android:windowSoftInputMode="adjustResize"
              
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
 
+    <activity android:autoRemoveFromRecents="true"
+              android:label="Emacs options"
+              android:name=".EmacsPreferencesActivity">
+      <intent-filter>
+        <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
+        <category android:name="android.intent.category.DEFAULT" />
+      </intent-filter>
+    </activity>
+
     <service android:name="org.gnu.emacs.EmacsService"
             android:directBootAware="false"
             android:enabled="true"
diff --git a/java/org/gnu/emacs/EmacsActivity.java 
b/java/org/gnu/emacs/EmacsActivity.java
index 79c0991a5d3..d377cf982ef 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -161,6 +161,13 @@ public class EmacsActivity extends Activity
   onCreate (Bundle savedInstanceState)
   {
     FrameLayout.LayoutParams params;
+    Intent intent;
+
+    /* See if Emacs should be started with -Q.  */
+    intent = getIntent ();
+    EmacsService.needDashQ
+      = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
+                               false);
 
     /* Set the theme to one without a title bar.  */
 
@@ -179,9 +186,8 @@ public class EmacsActivity extends Activity
     /* Set it as the content view.  */
     setContentView (layout);
 
-    if (EmacsService.SERVICE == null)
-      /* Start the Emacs service now.  */
-      startService (new Intent (this, EmacsService.class));
+    /* Maybe start the Emacs service if necessary.  */
+    EmacsService.startEmacsService (this);
 
     /* Add this activity to the list of available activities.  */
     EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
@@ -193,10 +199,16 @@ public class EmacsActivity extends Activity
   public void
   onDestroy ()
   {
+    EmacsWindowAttachmentManager manager;
+    boolean isMultitask;
+
+    manager = EmacsWindowAttachmentManager.MANAGER;
+
     /* The activity will die shortly hereafter.  If there is a window
        attached, close it now.  */
     Log.d (TAG, "onDestroy " + this);
-    EmacsWindowAttachmentManager.MANAGER.removeWindowConsumer (this);
+    isMultitask = this instanceof EmacsMultitaskActivity;
+    manager.removeWindowConsumer (this, isMultitask || isFinishing ());
     focusedActivities.remove (this);
     invalidateFocus ();
     super.onDestroy ();
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java 
b/java/org/gnu/emacs/EmacsContextMenu.java
index ac67ebe4aa0..056d8fb692c 100644
--- a/java/org/gnu/emacs/EmacsContextMenu.java
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -174,6 +174,7 @@ public class EmacsContextMenu
                   support doing so, create the submenu and add the
                   contents of the menu to it.  */
                submenu = menu.addSubMenu (item.itemName);
+               item.subMenu.inflateMenuItems (submenu);
              }
            catch (UnsupportedOperationException exception)
              {
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index 2f3a732ea7c..3efdc0cff9a 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -61,75 +61,76 @@ public class EmacsNative
   /* Abort and generate a native core dump.  */
   public static native void emacsAbort ();
 
-  /* Send an ANDROID_CONFIGURE_NOTIFY event.  */
-  public static native void sendConfigureNotify (short window, long time,
+  /* Send an ANDROID_CONFIGURE_NOTIFY event.  The values of all the
+     functions below are the serials of the events sent.  */
+  public static native long sendConfigureNotify (short window, long time,
                                                 int x, int y, int width,
                                                 int height);
 
   /* Send an ANDROID_KEY_PRESS event.  */
-  public static native void sendKeyPress (short window, long time, int state,
+  public static native long sendKeyPress (short window, long time, int state,
                                          int keyCode, int unicodeChar);
 
   /* Send an ANDROID_KEY_RELEASE event.  */
-  public static native void sendKeyRelease (short window, long time, int state,
+  public static native long sendKeyRelease (short window, long time, int state,
                                            int keyCode, int unicodeChar);
 
   /* Send an ANDROID_FOCUS_IN event.  */
-  public static native void sendFocusIn (short window, long time);
+  public static native long sendFocusIn (short window, long time);
 
   /* Send an ANDROID_FOCUS_OUT event.  */
-  public static native void sendFocusOut (short window, long time);
+  public static native long sendFocusOut (short window, long time);
 
   /* Send an ANDROID_WINDOW_ACTION event.  */
-  public static native void sendWindowAction (short window, int action);
+  public static native long sendWindowAction (short window, int action);
 
   /* Send an ANDROID_ENTER_NOTIFY event.  */
-  public static native void sendEnterNotify (short window, int x, int y,
+  public static native long sendEnterNotify (short window, int x, int y,
                                             long time);
 
   /* Send an ANDROID_LEAVE_NOTIFY event.  */
-  public static native void sendLeaveNotify (short window, int x, int y,
+  public static native long sendLeaveNotify (short window, int x, int y,
                                             long time);
 
   /* Send an ANDROID_MOTION_NOTIFY event.  */
-  public static native void sendMotionNotify (short window, int x, int y,
+  public static native long sendMotionNotify (short window, int x, int y,
                                              long time);
 
   /* Send an ANDROID_BUTTON_PRESS event.  */
-  public static native void sendButtonPress (short window, int x, int y,
+  public static native long sendButtonPress (short window, int x, int y,
                                             long time, int state,
                                             int button);
 
   /* Send an ANDROID_BUTTON_RELEASE event.  */
-  public static native void sendButtonRelease (short window, int x, int y,
+  public static native long sendButtonRelease (short window, int x, int y,
                                               long time, int state,
                                               int button);
 
   /* Send an ANDROID_TOUCH_DOWN event.  */
-  public static native void sendTouchDown (short window, int x, int y,
+  public static native long sendTouchDown (short window, int x, int y,
                                           long time, int pointerID);
 
   /* Send an ANDROID_TOUCH_UP event.  */
-  public static native void sendTouchUp (short window, int x, int y,
+  public static native long sendTouchUp (short window, int x, int y,
                                         long time, int pointerID);
 
   /* Send an ANDROID_TOUCH_MOVE event.  */
-  public static native void sendTouchMove (short window, int x, int y,
+  public static native long sendTouchMove (short window, int x, int y,
                                           long time, int pointerID);
 
   /* Send an ANDROID_WHEEL event.  */
-  public static native void sendWheel (short window, int x, int y,
+  public static native long sendWheel (short window, int x, int y,
                                       long time, int state,
                                       float xDelta, float yDelta);
 
   /* Send an ANDROID_ICONIFIED event.  */
-  public static native void sendIconified (short window);
+  public static native long sendIconified (short window);
 
   /* Send an ANDROID_DEICONIFIED event.  */
-  public static native void sendDeiconified (short window);
+  public static native long sendDeiconified (short window);
 
   /* Send an ANDROID_CONTEXT_MENU event.  */
-  public static native void sendContextMenu (short window, int menuEventID);
+  public static native long sendContextMenu (short window, int menuEventID);
 
   static
   {
diff --git a/java/org/gnu/emacs/EmacsPreferencesActivity.java 
b/java/org/gnu/emacs/EmacsPreferencesActivity.java
new file mode 100644
index 00000000000..0db983984fd
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPreferencesActivity.java
@@ -0,0 +1,98 @@
+/* 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 <https://www.gnu.org/licenses/>.  */
+
+package org.gnu.emacs;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import android.R;
+
+/* This module provides a ``preferences'' display for Emacs.  It is
+   supposed to be launched from inside the Settings application to
+   perform various actions, such as starting Emacs with the ``-Q''
+   option, which would not be possible otherwise, as there is no
+   command line on Android.  */
+
+public class EmacsPreferencesActivity extends Activity
+{
+  /* The linear layout associated with the activity.  */
+  private LinearLayout layout;
+
+  /* Restart Emacs with -Q.  Call EmacsThread.exit to kill Emacs now, and
+     tell the system to EmacsActivity with some parameters later.  */
+
+  private void
+  startEmacsQ ()
+  {
+    Intent intent;
+
+    intent = new Intent (this, EmacsActivity.class);
+    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    intent.putExtra ("org.gnu.emacs.START_DASH_Q", true);
+    startActivity (intent);
+    System.exit (0);
+  }
+
+  @Override
+  public void
+  onCreate (Bundle savedInstanceState)
+  {
+    LinearLayout layout;
+    TextView textView;
+    LinearLayout.LayoutParams params;
+    int resid;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+      setTheme (R.style.Theme_DeviceDefault_Settings);
+    else if (Build.VERSION.SDK_INT
+            >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+      setTheme (R.style.Theme_DeviceDefault);
+
+    layout = new LinearLayout (this);
+    layout.setOrientation (LinearLayout.VERTICAL);
+    setContentView (layout);
+
+    textView = new TextView (this);
+    textView.setPadding (8, 20, 20, 8);
+
+    params = new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT,
+                                           LayoutParams.WRAP_CONTENT);
+    textView.setLayoutParams (params);
+    textView.setText ("(Re)start Emacs with -Q");
+    textView.setOnClickListener (new View.OnClickListener () {
+       @Override
+       public void
+       onClick (View view)
+       {
+         startEmacsQ ();
+       }
+      });
+    layout.addView (textView);
+
+    super.onCreate (savedInstanceState);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index bcf8d9ff6e8..95f21b211a3 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -32,7 +32,13 @@ import android.view.InputDevice;
 import android.view.KeyEvent;
 
 import android.annotation.TargetApi;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
 import android.app.Service;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.AssetManager;
@@ -63,6 +69,7 @@ public class EmacsService extends Service
   public static final String TAG = "EmacsService";
   public static final int MAX_PENDING_REQUESTS = 256;
   public static volatile EmacsService SERVICE;
+  public static boolean needDashQ;
 
   private EmacsThread thread;
   private Handler handler;
@@ -74,6 +81,31 @@ public class EmacsService extends Service
   public int
   onStartCommand (Intent intent, int flags, int startId)
   {
+    Notification notification;
+    NotificationManager manager;
+    NotificationChannel channel;
+    String infoBlurb;
+    Object tem;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      {
+       tem = getSystemService (Context.NOTIFICATION_SERVICE);
+       manager = (NotificationManager) tem;
+       infoBlurb = ("See (emacs)Android Environment for more"
+                    + " details about this notification.");
+       channel
+         = new NotificationChannel ("emacs", "Emacs persistent notification",
+                                    NotificationManager.IMPORTANCE_DEFAULT);
+       manager.createNotificationChannel (channel);
+       notification = (new Notification.Builder (this, "emacs")
+                       .setContentTitle ("Emacs")
+                       .setContentText (infoBlurb)
+                       .setSmallIcon (android.R.drawable.sym_def_app_icon)
+                       .build ());
+       manager.notify (1, notification);
+       startForeground (1, notification);
+      }
+
     return START_NOT_STICKY;
   }
 
@@ -137,7 +169,7 @@ public class EmacsService extends Service
                                    this);
 
        /* Start the thread that runs Emacs.  */
-       thread = new EmacsThread (this);
+       thread = new EmacsThread (this, needDashQ);
        thread.start ();
       }
     catch (IOException exception)
@@ -444,4 +476,32 @@ public class EmacsService extends Service
          }
       }
   }
+
+  
+
+  /* Start the Emacs service if necessary.  On Android 26 and up,
+     start Emacs as a foreground service with a notification, to avoid
+     it being killed by the system.
+
+     On older systems, simply start it as a normal background
+     service.  */
+
+  public static void
+  startEmacsService (Context context)
+  {
+    PendingIntent intent;
+
+    if (EmacsService.SERVICE == null)
+      {
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+         /* Start the Emacs service now.  */
+         context.startService (new Intent (context,
+                                           EmacsService.class));
+       else
+         /* Display the permanant notification and start Emacs as a
+            foreground service.  */
+         context.startForegroundService (new Intent (context,
+                                                     EmacsService.class));
+      }
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
index f713818d4bc..2fe9e103b2b 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -32,53 +32,106 @@ import android.util.Log;
 public class EmacsSurfaceView extends SurfaceView
 {
   private static final String TAG = "EmacsSurfaceView";
-
   public Object surfaceChangeLock;
   private boolean created;
+  private EmacsView view;
 
-  public
-  EmacsSurfaceView (final EmacsView view)
-  {
-    super (view.getContext ());
-
-    surfaceChangeLock = new Object ();
+  /* This is the callback used on Android 8 to 25.  */
 
-    getHolder ().addCallback (new SurfaceHolder.Callback () {
-       @Override
-       public void
-       surfaceChanged (SurfaceHolder holder, int format,
-                       int width, int height)
+  private class Callback implements SurfaceHolder.Callback
+  {
+    @Override
+    public void
+    surfaceChanged (SurfaceHolder holder, int format,
+                   int width, int height)
+    {
+      Log.d (TAG, "surfaceChanged: " + view + ", " + view.pendingConfigure);
+
+      /* Make sure not to swap buffers if there is pending
+        configuration, because otherwise the redraw callback will not
+        run correctly.  */
+
+      if (view.pendingConfigure == 0)
+       view.swapBuffers ();
+    }
+
+    @Override
+    public void
+    surfaceCreated (SurfaceHolder holder)
+    {
+      synchronized (surfaceChangeLock)
        {
-         Log.d (TAG, "surfaceChanged: " + view);
-         view.swapBuffers ();
+         Log.d (TAG, "surfaceCreated: " + view);
+         created = true;
        }
 
-       @Override
-       public void
-       surfaceCreated (SurfaceHolder holder)
-       {
-         synchronized (surfaceChangeLock)
-           {
-             Log.d (TAG, "surfaceCreated: " + view);
-             created = true;
-           }
-
-         /* Drop the lock when doing this, or a deadlock can
-            result.  */
-         view.swapBuffers ();
-       }
+      /* Drop the lock when doing this, or a deadlock can
+        result.  */
+      view.swapBuffers ();
+    }
 
-       @Override
-       public void
-       surfaceDestroyed (SurfaceHolder holder)
+    @Override
+    public void
+    surfaceDestroyed (SurfaceHolder holder)
+    {
+      synchronized (surfaceChangeLock)
        {
-         synchronized (surfaceChangeLock)
-           {
-             Log.d (TAG, "surfaceDestroyed: " + view);
-             created = false;
-           }
+         Log.d (TAG, "surfaceDestroyed: " + view);
+         created = false;
        }
-      });
+    }
+  }
+
+  /* And this is the callback used on Android 26 and later.  It is
+     used because it can tell the system when drawing completes.  */
+
+  private class Callback2 extends Callback implements SurfaceHolder.Callback2
+  {
+    @Override
+    public void
+    surfaceRedrawNeeded (SurfaceHolder holder)
+    {
+      /* This version is not supported.  */
+      return;
+    }
+
+    @Override
+    public void
+    surfaceRedrawNeededAsync (SurfaceHolder holder,
+                             Runnable drawingFinished)
+    {
+      Runnable old;
+
+      Log.d (TAG, "surfaceRedrawNeededAsync: " + view.pendingConfigure);
+
+      /* The system calls this function when it wants to know whether
+        or not Emacs is still configuring itself in response to a
+        resize.
+
+         If the view did not send an outstanding ConfigureNotify
+         event, then call drawingFinish immediately.  Else, give it to
+         the view to execute after drawing completes.  */
+
+      if (view.pendingConfigure == 0)
+       drawingFinished.run ();
+      else
+       /* And set this runnable to run once drawing completes.  */
+       view.drawingFinished = drawingFinished;
+    }
+  }
+
+  public
+  EmacsSurfaceView (final EmacsView view)
+  {
+    super (view.getContext ());
+
+    this.surfaceChangeLock = new Object ();
+    this.view = view;
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+      getHolder ().addCallback (new Callback ());
+    else
+      getHolder ().addCallback (new Callback2 ());
   }
 
   public boolean
diff --git a/java/org/gnu/emacs/EmacsThread.java 
b/java/org/gnu/emacs/EmacsThread.java
index 0882753747f..f9bc132f354 100644
--- a/java/org/gnu/emacs/EmacsThread.java
+++ b/java/org/gnu/emacs/EmacsThread.java
@@ -23,12 +23,13 @@ import java.lang.Thread;
 
 public class EmacsThread extends Thread
 {
-  EmacsService context;
+  /* Whether or not Emacs should be started -Q.  */
+  private boolean startDashQ;
 
   public
-  EmacsThread (EmacsService service)
+  EmacsThread (EmacsService service, boolean startDashQ)
   {
-    context = service;
+    this.startDashQ = startDashQ;
   }
 
   public void
@@ -36,7 +37,10 @@ public class EmacsThread extends Thread
   {
     String args[];
 
-    args = new String[] { "libandroid-emacs.so", };
+    if (!startDashQ)
+      args = new String[] { "libandroid-emacs.so", };
+    else
+      args = new String[] { "libandroid-emacs.so", "-Q", };
 
     /* Run the native code now.  */
     EmacsNative.initEmacs (args);
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 82f44acaebe..74bbb7b3ecc 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -19,6 +19,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 package org.gnu.emacs;
 
+import android.content.Context;
 import android.content.res.ColorStateList;
 
 import android.view.ContextMenu;
@@ -27,6 +28,8 @@ import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewGroup;
 
+import android.view.inputmethod.InputMethodManager;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -86,11 +89,23 @@ public class EmacsView extends ViewGroup
   /* The serial of the last clip rectangle change.  */
   private long lastClipSerial;
 
+  /* The InputMethodManager for this view's context.  */
+  private InputMethodManager imManager;
+
+  /* Runnable that will run once drawing completes.  */
+  public Runnable drawingFinished;
+
+  /* Serial of the last ConfigureNotify event sent that Emacs has not
+     yet responded to.  0 if there is no such outstanding event.  */
+  public long pendingConfigure;
+
   public
   EmacsView (EmacsWindow window)
   {
     super (EmacsService.SERVICE);
 
+    Object tem;
+
     this.window = window;
     this.damageRegion = new Region ();
     this.paint = new Paint ();
@@ -111,6 +126,10 @@ public class EmacsView extends ViewGroup
     /* Get rid of the default focus highlight.  */
     if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
       setDefaultFocusHighlightEnabled (false);
+
+    /* Obtain the input method manager.  */
+    tem = getContext ().getSystemService (Context.INPUT_METHOD_SERVICE);
+    imManager = (InputMethodManager) tem;
   }
 
   private void
@@ -259,7 +278,8 @@ public class EmacsView extends ViewGroup
     if (changed || mustReportLayout)
       {
        mustReportLayout = false;
-       window.viewLayout (left, top, right, bottom);
+       pendingConfigure
+         = window.viewLayout (left, top, right, bottom);
       }
 
     measuredWidth = right - left;
@@ -538,4 +558,37 @@ public class EmacsView extends ViewGroup
        Runtime.getRuntime ().gc ();
       }
   }
+
+  public void
+  showOnScreenKeyboard ()
+  {
+    /* Specifying no flags at all tells the system the user asked for
+       the input method to be displayed.  */
+    imManager.showSoftInput (this, 0);
+  }
+
+  public void
+  hideOnScreenKeyboard ()
+  {
+    imManager.hideSoftInputFromWindow (this.getWindowToken (),
+                                      0);
+  }
+
+  public void
+  windowUpdated (long serial)
+  {
+    Log.d (TAG, "windowUpdated: serial is " + serial);
+
+    if (pendingConfigure <= serial
+       /* Detect wraparound.  */
+       || pendingConfigure - serial >= 0x7fffffff)
+      {
+       pendingConfigure = 0;
+
+       if (drawingFinished != null)
+         drawingFinished.run ();
+
+       drawingFinished = null;
+      }
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index c5b1522086c..8511af9193e 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -233,7 +233,7 @@ public class EmacsWindow extends EmacsHandleObject
     return attached;
   }
 
-  public void
+  public long
   viewLayout (int left, int top, int right, int bottom)
   {
     int rectWidth, rectHeight;
@@ -249,10 +249,10 @@ public class EmacsWindow extends EmacsHandleObject
     rectWidth = right - left;
     rectHeight = bottom - top;
 
-    EmacsNative.sendConfigureNotify (this.handle,
-                                    System.currentTimeMillis (),
-                                    left, top, rectWidth,
-                                    rectHeight);
+    return EmacsNative.sendConfigureNotify (this.handle,
+                                           System.currentTimeMillis (),
+                                           left, top, rectWidth,
+                                           rectHeight);
   }
 
   public void
@@ -589,11 +589,20 @@ public class EmacsWindow extends EmacsHandleObject
     EmacsActivity.invalidateFocus ();
   }
 
+  /* Notice that the activity has been detached or destroyed.
+
+     ISFINISHING is set if the activity is not the main activity, or
+     if the activity was not destroyed in response to explicit user
+     action.  */
+
   public void
-  onActivityDetached ()
+  onActivityDetached (boolean isFinishing)
   {
-    /* Destroy the associated frame when the activity is detached.  */
-    EmacsNative.sendWindowAction (this.handle, 0);
+    /* Destroy the associated frame when the activity is detached in
+       response to explicit user action.  */
+
+    if (isFinishing)
+      EmacsNative.sendWindowAction (this.handle, 0);
   }
 
   /* Look through the button state to determine what button EVENT was
@@ -1064,4 +1073,37 @@ public class EmacsWindow extends EmacsHandleObject
     /* Return the resulting coordinates.  */
     return array;
   }
+
+  public void
+  toggleOnScreenKeyboard (final boolean on)
+  {
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         if (on)
+           view.showOnScreenKeyboard ();
+         else
+           view.hideOnScreenKeyboard ();
+       }
+      });
+  }
+
+  /* Notice that outstanding configure events have been processed.
+     SERIAL is checked in the UI thread to verify that no new
+     configure events have been generated in the mean time.  */
+
+  public void
+  windowUpdated (final long serial)
+  {
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.windowUpdated (serial);
+       }
+      });
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java 
b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
index 15eb3bb65c2..510300571b8 100644
--- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -134,7 +134,7 @@ public class EmacsWindowAttachmentManager
   }
 
   public void
-  removeWindowConsumer (WindowConsumer consumer)
+  removeWindowConsumer (WindowConsumer consumer, boolean isFinishing)
   {
     EmacsWindow window;
 
@@ -147,7 +147,7 @@ public class EmacsWindowAttachmentManager
        Log.d (TAG, "removeWindowConsumer: detaching " + window);
 
        consumer.detachWindow ();
-       window.onActivityDetached ();
+       window.onActivityDetached (isFinishing);
       }
 
     Log.d (TAG, "removeWindowConsumer: removing " + consumer);
diff --git a/lisp/frame.el b/lisp/frame.el
index f21c0c369bd..f8ef17325ec 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -2554,6 +2554,23 @@ symbols."
                'core-keyboard))))))
 
 
+;;;; On-screen keyboard management.
+
+(declare-function android-toggle-on-screen-keyboard "androidfns.c")
+
+(defun frame-toggle-on-screen-keyboard (frame hide)
+  "Display or hide the on-screen keyboard.
+On systems with an on-screen keyboard, display the on screen
+keyboard on behalf of the frame FRAME if HIDE is nil.  Else, hide
+the on screen keyboard.
+
+FRAME must already have the input focus for this to work
+ reliably."
+  (let ((frame-type (framep-on-display frame)))
+    (cond ((eq frame-type 'android)
+           (android-toggle-on-screen-keyboard frame hide)))))
+
+
 ;;;; Frame geometry values
 
 (defun frame-geom-value-cons (type value &optional frame)
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 21d4607e7cf..3c42f29cd1f 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4576,6 +4576,29 @@ is included in the return value."
                   default)))
    ": "))
 
+
+;;; On screen keyboard support.
+;; Try to display the on screen keyboard whenever entering the
+;; mini-buffer, and hide it whenever leaving.
+
+(defun minibuffer-setup-on-screen-keyboard ()
+  "Maybe display the on-screen keyboard in the current frame.
+Display the on-screen keyboard in the current frame if the
+last device to have sent an input event is not a keyboard.
+This is run upon minibuffer setup."
+  (when (not (memq (device-class last-event-frame
+                               last-event-device)
+                   '(keyboard core-keyboard)))
+    (frame-toggle-on-screen-keyboard (selected-frame) nil)))
+
+(defun minibuffer-exit-on-screen-keyboard ()
+  "Hide the on-screen keyboard if it was displayed.
+This is run upon minibuffer exit."
+  (frame-toggle-on-screen-keyboard (selected-frame) t))
+
+(add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard)
+(add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard)
+
 (provide 'minibuffer)
 
 ;;; minibuffer.el ends here
diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el
index bcc4f5e9be3..a1c9e0b4afd 100644
--- a/lisp/touch-screen.el
+++ b/lisp/touch-screen.el
@@ -38,6 +38,12 @@ touch point, and the initial position of the touchpoint.  See
 `touch-screen-handle-point-update' for the meanings of the fourth
 element.")
 
+(defvar touch-screen-set-point-commands '(mouse-set-point)
+  "List of commands known to set the point.
+This is used to determine whether or not to display the on-screen
+keyboard after a mouse command is executed in response to a
+`touchscreen-end' event.")
+
 (defvar touch-screen-current-timer nil
   "Timer used to track long-presses.
 This is always cleared upon any significant state change.")
@@ -54,20 +60,28 @@ However, return the coordinates relative to WINDOW.
 
 If (posn-window posn) is the same as window, simply return the
 coordinates in POSN.  Otherwise, convert them to the frame, and
-then back again."
-  (if (eq (posn-window posn) window)
+then back again.
+
+If WINDOW is the symbol `frame', simply convert the coordinates
+to the frame that they belong in."
+  (if (or (eq (posn-window posn) window)
+          (and (eq window 'frame)
+               (framep (posn-window posn))))
       (posn-x-y posn)
     (let ((xy (posn-x-y posn))
-          (edges (window-inside-pixel-edges window)))
+          (edges (and (windowp window)
+                      (window-inside-pixel-edges window))))
       ;; Make the X and Y positions frame relative.
       (when (windowp (posn-window posn))
         (let ((edges (window-inside-pixel-edges
                       (posn-window posn))))
           (setq xy (cons (+ (car xy) (car edges))
                          (+ (cdr xy) (cadr edges))))))
-      ;; Make the X and Y positions window relative again.
-      (cons (- (car xy) (car edges))
-            (- (cdr xy) (cadr edges))))))
+      (if (eq window 'frame)
+          xy
+        ;; Make the X and Y positions window relative again.
+        (cons (- (car xy) (car edges))
+              (- (cdr xy) (cadr edges)))))))
 
 (defun touch-screen-handle-scroll (dx dy)
   "Scroll the display assuming that a touch point has moved by DX and DY."
@@ -252,7 +266,12 @@ If the fourth argument of `touch-screen-current-tool' is 
nil,
 move point to the position of POINT, selecting the window under
 POINT as well, and deactivate the mark; if there is a button or
 link at POINT, call the command bound to `mouse-2' there.
-Otherwise, call the command bound to `mouse-1'."
+Otherwise, call the command bound to `mouse-1'.
+
+If the command being executed is listed in
+`touch-screen-set-point-commands' also display the on-screen
+keyboard if the current buffer and the character at the new point
+is not read-only."
   (let ((what (nth 3 touch-screen-current-tool)))
     (cond ((null what)
            (when (windowp (posn-window (cdr point)))
@@ -283,9 +302,17 @@ Otherwise, call the command bound to `mouse-1'."
                (deactivate-mark)
                ;; This is necessary for following links.
                (goto-char (posn-point (cdr point)))
+               ;; Figure out if the on screen keyboard needs to be
+               ;; displayed.
                (when command
                  (call-interactively command nil
-                                     (vector event)))))))))
+                                     (vector event))
+                 (when (memq command touch-screen-set-point-commands)
+                   (if (not (or buffer-read-only
+                                (get-text-property (point) 'read-only)))
+                       (frame-toggle-on-screen-keyboard (selected-frame) nil)
+                     ;; Otherwise, hide the on screen keyboard now.
+                     (frame-toggle-on-screen-keyboard (selected-frame) 
t))))))))))
 
 (defun touch-screen-handle-touch (event)
   "Handle a single touch EVENT, and perform associated actions.
@@ -379,22 +406,34 @@ touch point with the same ID as in EVENT, call UPDATE 
with the
 touch point in event and DATA.
 
 Return nil immediately if any other kind of event is received;
-otherwise, return t once the `touchscreen-end' event arrives."
-  (catch 'finish
-    (while t
-      (let ((new-event (read-event)))
-        (cond
-         ((eq (car-safe new-event) 'touchscreen-update)
-          (let ((tool (assq (caadr event) (nth 1 new-event))))
-            (when (and update tool)
-              (funcall update new-event data))))
-         ((eq (car-safe new-event) 'touchscreen-end)
-          (throw 'finish
-                 ;; Now determine whether or not the `touchscreen-end'
-                 ;; event has the same ID as EVENT.  If it doesn't,
-                 ;; then this is another touch, so return nil.
-                 (eq (caadr event) (caadr new-event))))
-         (t (throw 'finish nil)))))))
+otherwise, return either t or `no-drag' once the
+`touchscreen-end' event arrives; return `no-drag' returned if the
+touch point in EVENT did not move significantly, and t otherwise."
+  (let ((return-value 'no-drag)
+        (start-xy (touch-screen-relative-xy (cdadr event)
+                                            'frame)))
+    (catch 'finish
+      (while t
+        (let ((new-event (read-event)))
+          (cond
+           ((eq (car-safe new-event) 'touchscreen-update)
+            (when-let* ((tool (assq (caadr event) (nth 1 new-event)))
+                        (xy (touch-screen-relative-xy (cdr tool) 'frame)))
+              (when (or (> (- (car xy) (car start-xy)) 5)
+                        (< (- (car xy) (car start-xy)) -5)
+                        (> (- (cdr xy) (cdr start-xy)) 5)
+                        (< (- (cdr xy) (cdr start-xy)) -5))
+                (setq return-value t))
+              (when (and update tool)
+                (funcall update new-event data))))
+           ((eq (car-safe new-event) 'touchscreen-end)
+            (throw 'finish
+                   ;; Now determine whether or not the `touchscreen-end'
+                   ;; event has the same ID as EVENT.  If it doesn't,
+                   ;; then this is another touch, so return nil.
+                   (and (eq (caadr event) (caadr new-event))
+                        return-value)))
+           (t (throw 'finish nil))))))))
 
 
 
@@ -402,45 +441,8 @@ otherwise, return t once the `touchscreen-end' event 
arrives."
 
 (defun touch-screen-drag-mode-line-1 (event)
   "Internal helper for `touch-screen-drag-mode-line'.
-This is called when that function determines it need not execute
-any keymaps on the mode line at that particular spot."
-  ;; Find the window that should be dragged and the starting position.
-  (let* ((window (posn-window (cdadr event)))
-         (relative-xy (touch-screen-relative-xy
-                       (cdadr event) window))
-         (last-position (cdr relative-xy)))
-    (when (window-resizable window 0)
-      (touch-screen-track-drag
-       event (lambda (new-event &optional _data)
-               ;; Find the position of the touchpoint in NEW-EVENT.
-               (let* ((touchpoint (assq (caadr event) (cadr new-event)))
-                      (new-relative-xy
-                       (touch-screen-relative-xy (cdr touchpoint)
-                                                 window))
-                      (position (cdr new-relative-xy))
-                      growth)
-                 ;; Now set the new height of the window.
-                 ;; If new-relative-y is above relative-xy, then
-                 ;; make the window that much shorter.  Otherwise,
-                 ;; make it bigger.
-                 (unless (or (zerop (setq growth (- position last-position)))
-                             (and (> growth 0)
-                                  (< position (+ (window-pixel-top window)
-                                                 (window-pixel-height 
window))))
-                             (and (< growth 0)
-                                  (> position (+ (window-pixel-top window)
-                                                 (window-pixel-height 
window)))))
-                   (adjust-window-trailing-edge window growth nil t))
-                 (setq last-position position)))))))
-
-(defun touch-screen-drag-mode-line (event)
-  "Begin dragging the mode line in response to a touch EVENT.
-If EVENT lies on top of text with a mouse command bound, run
-that command instead.
-
-Change the height of the window based on where the touch point
-in EVENT moves."
-  (interactive "e")
+This is called when that function determines that no drag really
+happened.  EVENT is the same as in `touch-screen-drag-mode-line'."
   ;; If there is an object at EVENT, then look either a keymap bound
   ;; to [down-mouse-1] or a command bound to [mouse-1].  Then, if a
   ;; keymap was found, pop it up as a menu.  Otherwise, wait for a tap
@@ -457,21 +459,66 @@ in EVENT moves."
          (keymap (lookup-key object-keymap [mode-line down-mouse-1]))
          (command (or (lookup-key object-keymap [mode-line mouse-1])
                       keymap)))
-    (if (or (keymapp keymap) command)
-        (if (keymapp keymap)
-            (when (touch-screen-track-tap event)
-              (when-let* ((command (x-popup-menu event keymap))
-                          (tem (lookup-key keymap
-                                           (if (consp command)
-                                               (apply #'vector command)
-                                             (vector command))
-                                           t)))
-                (call-interactively tem)))
-          (when (and (commandp command)
-                     (touch-screen-track-tap event))
-            (call-interactively command nil
-                                (vector (list 'mouse-1 (cdadr event))))))
-      (touch-screen-drag-mode-line-1 event))))
+    (when (or (keymapp keymap) command)
+      (if (keymapp keymap)
+          (when-let* ((command (x-popup-menu event keymap))
+                      (tem (lookup-key keymap
+                                       (if (consp command)
+                                           (apply #'vector command)
+                                         (vector command))
+                                       t)))
+            (call-interactively tem))
+        (when (commandp command)
+          (call-interactively command nil
+                              (vector (list 'mouse-1 (cdadr event)))))))))
+
+(defun touch-screen-drag-mode-line (event)
+  "Begin dragging the mode line in response to a touch EVENT.
+Change the height of the window based on where the touch point in
+EVENT moves.
+
+If it does not actually move anywhere and the touch point is
+removed, and EVENT lies on top of text with a mouse command
+bound, run that command instead."
+  (interactive "e")
+  ;; Find the window that should be dragged and the starting position.
+  (let* ((window (posn-window (cdadr event)))
+         (relative-xy (touch-screen-relative-xy
+                       (cdadr event) window))
+         (last-position (cdr relative-xy)))
+    (when (window-resizable window 0)
+      (when (eq
+             (touch-screen-track-drag
+              event (lambda (new-event &optional _data)
+                      ;; Find the position of the touchpoint in
+                      ;; NEW-EVENT.
+                      (let* ((touchpoint (assq (caadr event)
+                                               (cadr new-event)))
+                             (new-relative-xy
+                              (touch-screen-relative-xy (cdr touchpoint)
+                                                        window))
+                             (position (cdr new-relative-xy))
+                             growth)
+                        ;; Now set the new height of the window.  If
+                        ;; new-relative-y is above relative-xy, then
+                        ;; make the window that much shorter.
+                        ;; Otherwise, make it bigger.
+                        (unless (or (zerop (setq growth
+                                                 (- position last-position)))
+                                    (and (> growth 0)
+                                         (< position
+                                            (+ (window-pixel-top window)
+                                               (window-pixel-height window))))
+                                    (and (< growth 0)
+                                         (> position
+                                            (+ (window-pixel-top window)
+                                               (window-pixel-height window)))))
+                          (adjust-window-trailing-edge window growth nil t))
+                        (setq last-position position))))
+             'no-drag)
+        ;; Dragging did not actually happen, so try to run any command
+        ;; necessary.
+        (touch-screen-drag-mode-line-1 event)))))
 
 (global-set-key [mode-line touchscreen-begin]
                 #'touch-screen-drag-mode-line)
diff --git a/src/android.c b/src/android.c
index eb9c404f1a3..43ac3b3f754 100644
--- a/src/android.c
+++ b/src/android.c
@@ -121,6 +121,8 @@ struct android_emacs_window
 {
   jclass class;
   jmethodID swap_buffers;
+  jmethodID toggle_on_screen_keyboard;
+  jmethodID window_updated;
 };
 
 /* The asset manager being used.  */
@@ -193,6 +195,10 @@ static struct android_emacs_drawable drawable_class;
 /* Various methods associated with the EmacsWindow class.  */
 static struct android_emacs_window window_class;
 
+/* The last event serial used.  This is a 32 bit value, but it is
+   stored in unsigned long to be consistent with X.  */
+static unsigned int event_serial;
+
 
 
 /* Event handling functions.  Events are stored on a (circular) queue
@@ -1435,6 +1441,9 @@ android_init_emacs_window (void)
   assert (window_class.c_name);
 
   FIND_METHOD (swap_buffers, "swapBuffers", "()V");
+  FIND_METHOD (toggle_on_screen_keyboard,
+              "toggleOnScreenKeyboard", "(Z)V");
+  FIND_METHOD (window_updated, "windowUpdated", "(J)V");
 #undef FIND_METHOD
 }
 
@@ -1497,7 +1506,7 @@ NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
   emacs_abort ();
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
                                   jshort window, jlong time,
                                   jint x, jint y, jint width,
@@ -1506,6 +1515,7 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject 
object,
   union android_event event;
 
   event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
+  event.xconfigure.serial = ++event_serial;
   event.xconfigure.window = window;
   event.xconfigure.time = time;
   event.xconfigure.x = x;
@@ -1514,9 +1524,10 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject 
object,
   event.xconfigure.height = height;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
                            jshort window, jlong time,
                            jint state, jint keycode,
@@ -1525,6 +1536,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
   union android_event event;
 
   event.xkey.type = ANDROID_KEY_PRESS;
+  event.xkey.serial = ++event_serial;
   event.xkey.window = window;
   event.xkey.time = time;
   event.xkey.state = state;
@@ -1532,9 +1544,10 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
   event.xkey.unicode_char = unicode_char;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
                              jshort window, jlong time,
                              jint state, jint keycode,
@@ -1543,6 +1556,7 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
   union android_event event;
 
   event.xkey.type = ANDROID_KEY_RELEASE;
+  event.xkey.serial = ++event_serial;
   event.xkey.window = window;
   event.xkey.time = time;
   event.xkey.state = state;
@@ -1550,48 +1564,55 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject 
object,
   event.xkey.unicode_char = unicode_char;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
                           jshort window, jlong time)
 {
   union android_event event;
 
-  event.xkey.type = ANDROID_FOCUS_IN;
-  event.xkey.window = window;
-  event.xkey.time = time;
+  event.xfocus.type = ANDROID_FOCUS_IN;
+  event.xfocus.serial = ++event_serial;
+  event.xfocus.window = window;
+  event.xfocus.time = time;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
                            jshort window, jlong time)
 {
   union android_event event;
 
-  event.xkey.type = ANDROID_FOCUS_OUT;
-  event.xkey.window = window;
-  event.xkey.time = time;
+  event.xfocus.type = ANDROID_FOCUS_OUT;
+  event.xfocus.serial = ++event_serial;
+  event.xfocus.window = window;
+  event.xfocus.time = time;
 
   android_write_event (&event);
+  return ++event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
                                jshort window, jint action)
 {
   union android_event event;
 
   event.xaction.type = ANDROID_WINDOW_ACTION;
+  event.xaction.serial = ++event_serial;
   event.xaction.window = window;
   event.xaction.action = action;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object,
                               jshort window, jint x, jint y,
                               jlong time)
@@ -1599,15 +1620,17 @@ NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject 
object,
   union android_event event;
 
   event.xcrossing.type = ANDROID_ENTER_NOTIFY;
+  event.xcrossing.serial = ++event_serial;
   event.xcrossing.window = window;
   event.xcrossing.x = x;
   event.xcrossing.y = y;
   event.xcrossing.time = time;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object,
                               jshort window, jint x, jint y,
                               jlong time)
@@ -1615,15 +1638,17 @@ NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject 
object,
   union android_event event;
 
   event.xcrossing.type = ANDROID_LEAVE_NOTIFY;
+  event.xcrossing.serial = ++event_serial;
   event.xcrossing.window = window;
   event.xcrossing.x = x;
   event.xcrossing.y = y;
   event.xcrossing.time = time;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object,
                                jshort window, jint x, jint y,
                                jlong time)
@@ -1631,15 +1656,17 @@ NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject 
object,
   union android_event event;
 
   event.xmotion.type = ANDROID_MOTION_NOTIFY;
+  event.xmotion.serial = ++event_serial;
   event.xmotion.window = window;
   event.xmotion.x = x;
   event.xmotion.y = y;
   event.xmotion.time = time;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
                               jshort window, jint x, jint y,
                               jlong time, jint state,
@@ -1648,6 +1675,7 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject 
object,
   union android_event event;
 
   event.xbutton.type = ANDROID_BUTTON_PRESS;
+  event.xbutton.serial = ++event_serial;
   event.xbutton.window = window;
   event.xbutton.x = x;
   event.xbutton.y = y;
@@ -1656,9 +1684,10 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject 
object,
   event.xbutton.button = button;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
                                 jshort window, jint x, jint y,
                                 jlong time, jint state,
@@ -1667,6 +1696,7 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject 
object,
   union android_event event;
 
   event.xbutton.type = ANDROID_BUTTON_RELEASE;
+  event.xbutton.serial = ++event_serial;
   event.xbutton.window = window;
   event.xbutton.x = x;
   event.xbutton.y = y;
@@ -1675,9 +1705,10 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject 
object,
   event.xbutton.button = button;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
                             jshort window, jint x, jint y,
                             jlong time, jint pointer_id)
@@ -1685,6 +1716,7 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
   union android_event event;
 
   event.touch.type = ANDROID_TOUCH_DOWN;
+  event.touch.serial = ++event_serial;
   event.touch.window = window;
   event.touch.x = x;
   event.touch.y = y;
@@ -1692,9 +1724,10 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
   event.touch.pointer_id = pointer_id;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
                           jshort window, jint x, jint y,
                           jlong time, jint pointer_id)
@@ -1702,6 +1735,7 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
   union android_event event;
 
   event.touch.type = ANDROID_TOUCH_UP;
+  event.touch.serial = ++event_serial;
   event.touch.window = window;
   event.touch.x = x;
   event.touch.y = y;
@@ -1709,9 +1743,10 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
   event.touch.pointer_id = pointer_id;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
                             jshort window, jint x, jint y,
                             jlong time, jint pointer_id)
@@ -1719,6 +1754,7 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
   union android_event event;
 
   event.touch.type = ANDROID_TOUCH_MOVE;
+  event.touch.serial = ++event_serial;
   event.touch.window = window;
   event.touch.x = x;
   event.touch.y = y;
@@ -1726,9 +1762,10 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
   event.touch.pointer_id = pointer_id;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
                         jshort window, jint x, jint y,
                         jlong time, jint state,
@@ -1737,6 +1774,7 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
   union android_event event;
 
   event.wheel.type = ANDROID_WHEEL;
+  event.wheel.serial = ++event_serial;
   event.wheel.window = window;
   event.wheel.x = x;
   event.wheel.y = y;
@@ -1746,43 +1784,50 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
   event.wheel.y_delta = y_delta;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
                             jshort window)
 {
   union android_event event;
 
   event.iconified.type = ANDROID_ICONIFIED;
+  event.iconified.serial = ++event_serial;
   event.iconified.window = window;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
                               jshort window)
 {
   union android_event event;
 
   event.iconified.type = ANDROID_DEICONIFIED;
+  event.iconified.serial = ++event_serial;
   event.iconified.window = window;
 
   android_write_event (&event);
+  return event_serial;
 }
 
-extern JNIEXPORT void JNICALL
+extern JNIEXPORT jlong JNICALL
 NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object,
                               jshort window, jint menu_event_id)
 {
   union android_event event;
 
   event.menu.type = ANDROID_CONTEXT_MENU;
+  event.menu.serial = ++event_serial;
   event.menu.window = window;
   event.menu.menu_event_id = menu_event_id;
 
   android_write_event (&event);
+  return event_serial;
 }
 
 #ifdef __clang__
@@ -3599,6 +3644,15 @@ android_translate_coordinates (android_window src, int x,
   ANDROID_DELETE_LOCAL_REF (coordinates);
 }
 
+void
+android_sync (void)
+{
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      service_class.sync);
+  android_exception_check ();
+}
+
 
 
 /* Low level drawing primitives.  */
@@ -3795,12 +3849,45 @@ android_get_keysym_name (int keysym, char *name_return, 
size_t size)
   ANDROID_DELETE_LOCAL_REF (string);
 }
 
+/* Display the on screen keyboard on window WINDOW, or hide it if SHOW
+   is false.  Ask the system to bring up or hide the on-screen
+   keyboard on behalf of WINDOW.  The request may be rejected by the
+   system, especially when the window does not have the input
+   focus.  */
+
 void
-android_sync (void)
+android_toggle_on_screen_keyboard (android_window window, bool show)
 {
-  (*android_java_env)->CallVoidMethod (android_java_env,
-                                      emacs_service,
-                                      service_class.sync);
+  jobject object;
+  jmethodID method;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  method = window_class.toggle_on_screen_keyboard;
+
+  /* Now display the on screen keyboard.  */
+  (*android_java_env)->CallVoidMethod (android_java_env, object,
+                                      method, (jboolean) show);
+
+  /* Check for out of memory errors.  */
+  android_exception_check ();
+}
+
+/* Tell the window system that all configure events sent to WINDOW
+   have been fully processed, and that it is now okay to display its
+   new contents.  SERIAL is the serial of the last configure event
+   processed.  */
+
+void
+android_window_updated (android_window window, unsigned long serial)
+{
+  jobject object;
+  jmethodID method;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  method = window_class.window_updated;
+
+  (*android_java_env)->CallVoidMethod (android_java_env, object,
+                                      method, (jlong) serial);
   android_exception_check ();
 }
 
@@ -3811,7 +3898,7 @@ android_sync (void)
 #undef faccessat
 
 /* Replace the system faccessat with one which understands AT_EACCESS.
-   Android's faccessat simply fails upon using AT_EACCESS, so repalce
+   Android's faccessat simply fails upon using AT_EACCESS, so replace
    it with zero here.  This isn't caught during configuration.
 
    This replacement is only done when building for Android 17 or
@@ -3829,7 +3916,7 @@ faccessat (int dirfd, const char *pathname, int mode, int 
flags)
   return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
 }
 
-#endif /* __ANDROID_API__ < 16 */
+#endif /* __ANDROID_API__ >= 17 */
 
 
 
diff --git a/src/android.h b/src/android.h
index 240bc90d831..97818ab4911 100644
--- a/src/android.h
+++ b/src/android.h
@@ -91,6 +91,8 @@ extern void android_exception_check (void);
 
 extern void android_get_keysym_name (int, char *, size_t);
 extern void android_wait_event (void);
+extern void android_toggle_on_screen_keyboard (android_window, bool);
+extern void android_window_updated (android_window, unsigned long);
 
 
 
diff --git a/src/androidfns.c b/src/androidfns.c
index bb37c415069..77ee2e8de44 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -2332,6 +2332,30 @@ there is no mouse.  */)
 #endif
 }
 
+DEFUN ("android-toggle-on-screen-keyboard",
+       Fandroid_toggle_on_screen_keyboard,
+       Sandroid_toggle_on_screen_keyboard, 2, 2, 0,
+       doc: /* Display or hide the on-screen keyboard.
+If HIDE is non-nil, hide the on screen keyboard if it is currently
+being displayed.  Else, request that the system display it on behalf
+of FRAME.  This request may be rejected if FRAME does not have the
+input focus.  */)
+  (Lisp_Object frame, Lisp_Object hide)
+{
+#ifndef ANDROID_STUBIFY
+  struct frame *f;
+
+  f = decode_window_system_frame (frame);
+
+  block_input ();
+  android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f),
+                                    NILP (hide));
+  unblock_input ();
+#endif
+
+  return Qnil;
+}
+
 
 
 #ifndef ANDROID_STUBIFY
@@ -2781,6 +2805,7 @@ syms_of_androidfns (void)
   defsubr (&Sx_show_tip);
   defsubr (&Sx_hide_tip);
   defsubr (&Sandroid_detect_mouse);
+  defsubr (&Sandroid_toggle_on_screen_keyboard);
 
 #ifndef ANDROID_STUBIFY
   tip_timer = Qnil;
diff --git a/src/androidgui.h b/src/androidgui.h
index 1f28c18ff34..3b9a74dc0b0 100644
--- a/src/androidgui.h
+++ b/src/androidgui.h
@@ -239,6 +239,7 @@ enum android_event_type
 struct android_any_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
 };
 
@@ -252,6 +253,7 @@ enum android_modifier_mask
 struct android_key_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
   android_time time;
   unsigned int state;
@@ -271,6 +273,7 @@ struct android_key_event
 struct android_configure_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
   android_time time;
   int x, y;
@@ -280,6 +283,7 @@ struct android_configure_event
 struct android_focus_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
   android_time time;
 };
@@ -287,6 +291,7 @@ struct android_focus_event
 struct android_window_action_event
 {
   enum android_event_type type;
+  unsigned long serial;
 
   /* The window handle.  This can be ANDROID_NONE.  */
   android_window window;
@@ -301,6 +306,7 @@ struct android_window_action_event
 struct android_crossing_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
   int x, y;
   unsigned long time;
@@ -309,6 +315,7 @@ struct android_crossing_event
 struct android_motion_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
   int x, y;
   unsigned long time;
@@ -317,6 +324,7 @@ struct android_motion_event
 struct android_button_event
 {
   enum android_event_type type;
+  unsigned long serial;
   android_window window;
   int x, y;
   unsigned long time;
@@ -329,6 +337,9 @@ struct android_touch_event
   /* Type of the event.  */
   enum android_event_type type;
 
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
   /* Window associated with the event.  */
   android_window window;
 
@@ -347,6 +358,9 @@ struct android_wheel_event
   /* Type of the event.  */
   enum android_event_type type;
 
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
   /* Window associated with the event.  */
   android_window window;
 
@@ -368,6 +382,9 @@ struct android_iconify_event
   /* Type of the event.  */
   enum android_event_type type;
 
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
   /* Window associated with the event.  */
   android_window window;
 };
@@ -377,6 +394,9 @@ struct android_menu_event
   /* Type of the event.  */
   enum android_event_type type;
 
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
   /* Window associated with the event.  Always None.  */
   android_window window;
 
diff --git a/src/androidterm.c b/src/androidterm.c
index 3c16b542d91..3024890d789 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -591,7 +591,17 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
          android_clear_under_internal_border (f);
          SET_FRAME_GARBAGED (f);
          cancel_mouse_face (f);
+
+         /* Now stash the serial of this configure event somewhere,
+            and call android_window_updated with it once the redraw
+            completes.  */
+         FRAME_OUTPUT_DATA (f)->last_configure_serial
+           = configureEvent.xconfigure.serial;
        }
+      else
+       /* Reply to this ConfigureNotify event immediately.  */
+       android_window_updated (FRAME_ANDROID_WINDOW (f),
+                               configureEvent.xconfigure.serial);
 
       goto OTHER;
 
@@ -1194,6 +1204,9 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
       /* Iconification.  This is vastly simpler than on X.  */
     case ANDROID_ICONIFIED:
 
+      if (!any)
+       goto OTHER;
+
       if (FRAME_ICONIFIED_P (any))
        goto OTHER;
 
@@ -1206,6 +1219,9 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
 
     case ANDROID_DEICONIFIED:
 
+      if (!any)
+       goto OTHER;
+
       if (!FRAME_ICONIFIED_P (any))
        goto OTHER;
 
@@ -1311,6 +1327,14 @@ android_frame_up_to_date (struct frame *f)
   /* The frame is now complete, as its contents have been drawn.  */
   FRAME_ANDROID_COMPLETE_P (f) = true;
 
+  /* If there was an outstanding configure event, then tell system
+     that the update has finished and the new contents can now be
+     displayed.  */
+  if (FRAME_OUTPUT_DATA (f)->last_configure_serial)
+    android_window_updated (FRAME_ANDROID_WINDOW (f),
+                           FRAME_OUTPUT_DATA (f)->last_configure_serial);
+  FRAME_OUTPUT_DATA (f)->last_configure_serial = 0;
+
   /* Shrink the scanline buffer used by the font backend.  */
   sfntfont_android_shrink_scanline_buffer ();
   unblock_input ();
diff --git a/src/androidterm.h b/src/androidterm.h
index c0f862e35fb..11b24d40b73 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -241,6 +241,11 @@ struct android_output
   /* List of all tools (either styluses or fingers) pressed onto the
      frame.  */
   struct android_touch_point *touch_points;
+
+  /* Event serial of the last ConfigureNotify event received that has
+     not yet been drawn.  This is used to synchronize resize with the
+     window system.  0 if no such outstanding event exists.  */
+  unsigned long last_configure_serial;
 };
 
 enum



reply via email to

[Prev in Thread] Current Thread [Next in Thread]