aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2023-01-20 19:06:32 +0800
committerPo Lu2023-01-20 19:06:32 +0800
commitd44b60c2f001d57b010f0e9b82f798fbad9a23d6 (patch)
tree1ff208bfccd74a3b2ad4488cd66c89762d0acd7e /java
parente07b58dc35a66b5e611f437ec007292d82dc8f13 (diff)
downloademacs-d44b60c2f001d57b010f0e9b82f798fbad9a23d6.tar.gz
emacs-d44b60c2f001d57b010f0e9b82f798fbad9a23d6.zip
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.
Diffstat (limited to 'java')
-rw-r--r--java/AndroidManifest.xml.in12
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java20
-rw-r--r--java/org/gnu/emacs/EmacsContextMenu.java1
-rw-r--r--java/org/gnu/emacs/EmacsNative.java39
-rw-r--r--java/org/gnu/emacs/EmacsPreferencesActivity.java98
-rw-r--r--java/org/gnu/emacs/EmacsService.java62
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java127
-rw-r--r--java/org/gnu/emacs/EmacsThread.java12
-rw-r--r--java/org/gnu/emacs/EmacsView.java55
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java58
-rw-r--r--java/org/gnu/emacs/EmacsWindowAttachmentManager.java4
11 files changed, 412 insertions, 76 deletions
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/>. -->
62 android:theme="@android:style/Theme" 62 android:theme="@android:style/Theme"
63 android:debuggable="true" 63 android:debuggable="true"
64 android:extractNativeLibs="true"> 64 android:extractNativeLibs="true">
65
65 <activity android:name="org.gnu.emacs.EmacsActivity" 66 <activity android:name="org.gnu.emacs.EmacsActivity"
66 android:launchMode="singleTop" 67 android:launchMode="singleTop"
68 android:windowSoftInputMode="adjustResize"
67 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"> 69 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
68 <intent-filter> 70 <intent-filter>
69 <action android:name="android.intent.action.MAIN" /> 71 <action android:name="android.intent.action.MAIN" />
@@ -73,8 +75,18 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. -->
73 </activity> 75 </activity>
74 76
75 <activity android:name="org.gnu.emacs.EmacsMultitaskActivity" 77 <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
78 android:windowSoftInputMode="adjustResize"
76 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/> 79 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
77 80
81 <activity android:autoRemoveFromRecents="true"
82 android:label="Emacs options"
83 android:name=".EmacsPreferencesActivity">
84 <intent-filter>
85 <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
86 <category android:name="android.intent.category.DEFAULT" />
87 </intent-filter>
88 </activity>
89
78 <service android:name="org.gnu.emacs.EmacsService" 90 <service android:name="org.gnu.emacs.EmacsService"
79 android:directBootAware="false" 91 android:directBootAware="false"
80 android:enabled="true" 92 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
161 onCreate (Bundle savedInstanceState) 161 onCreate (Bundle savedInstanceState)
162 { 162 {
163 FrameLayout.LayoutParams params; 163 FrameLayout.LayoutParams params;
164 Intent intent;
165
166 /* See if Emacs should be started with -Q. */
167 intent = getIntent ();
168 EmacsService.needDashQ
169 = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
170 false);
164 171
165 /* Set the theme to one without a title bar. */ 172 /* Set the theme to one without a title bar. */
166 173
@@ -179,9 +186,8 @@ public class EmacsActivity extends Activity
179 /* Set it as the content view. */ 186 /* Set it as the content view. */
180 setContentView (layout); 187 setContentView (layout);
181 188
182 if (EmacsService.SERVICE == null) 189 /* Maybe start the Emacs service if necessary. */
183 /* Start the Emacs service now. */ 190 EmacsService.startEmacsService (this);
184 startService (new Intent (this, EmacsService.class));
185 191
186 /* Add this activity to the list of available activities. */ 192 /* Add this activity to the list of available activities. */
187 EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this); 193 EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
@@ -193,10 +199,16 @@ public class EmacsActivity extends Activity
193 public void 199 public void
194 onDestroy () 200 onDestroy ()
195 { 201 {
202 EmacsWindowAttachmentManager manager;
203 boolean isMultitask;
204
205 manager = EmacsWindowAttachmentManager.MANAGER;
206
196 /* The activity will die shortly hereafter. If there is a window 207 /* The activity will die shortly hereafter. If there is a window
197 attached, close it now. */ 208 attached, close it now. */
198 Log.d (TAG, "onDestroy " + this); 209 Log.d (TAG, "onDestroy " + this);
199 EmacsWindowAttachmentManager.MANAGER.removeWindowConsumer (this); 210 isMultitask = this instanceof EmacsMultitaskActivity;
211 manager.removeWindowConsumer (this, isMultitask || isFinishing ());
200 focusedActivities.remove (this); 212 focusedActivities.remove (this);
201 invalidateFocus (); 213 invalidateFocus ();
202 super.onDestroy (); 214 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
174 support doing so, create the submenu and add the 174 support doing so, create the submenu and add the
175 contents of the menu to it. */ 175 contents of the menu to it. */
176 submenu = menu.addSubMenu (item.itemName); 176 submenu = menu.addSubMenu (item.itemName);
177 item.subMenu.inflateMenuItems (submenu);
177 } 178 }
178 catch (UnsupportedOperationException exception) 179 catch (UnsupportedOperationException exception)
179 { 180 {
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
61 /* Abort and generate a native core dump. */ 61 /* Abort and generate a native core dump. */
62 public static native void emacsAbort (); 62 public static native void emacsAbort ();
63 63
64 /* Send an ANDROID_CONFIGURE_NOTIFY event. */ 64 /* Send an ANDROID_CONFIGURE_NOTIFY event. The values of all the
65 public static native void sendConfigureNotify (short window, long time, 65 functions below are the serials of the events sent. */
66 public static native long sendConfigureNotify (short window, long time,
66 int x, int y, int width, 67 int x, int y, int width,
67 int height); 68 int height);
68 69
69 /* Send an ANDROID_KEY_PRESS event. */ 70 /* Send an ANDROID_KEY_PRESS event. */
70 public static native void sendKeyPress (short window, long time, int state, 71 public static native long sendKeyPress (short window, long time, int state,
71 int keyCode, int unicodeChar); 72 int keyCode, int unicodeChar);
72 73
73 /* Send an ANDROID_KEY_RELEASE event. */ 74 /* Send an ANDROID_KEY_RELEASE event. */
74 public static native void sendKeyRelease (short window, long time, int state, 75 public static native long sendKeyRelease (short window, long time, int state,
75 int keyCode, int unicodeChar); 76 int keyCode, int unicodeChar);
76 77
77 /* Send an ANDROID_FOCUS_IN event. */ 78 /* Send an ANDROID_FOCUS_IN event. */
78 public static native void sendFocusIn (short window, long time); 79 public static native long sendFocusIn (short window, long time);
79 80
80 /* Send an ANDROID_FOCUS_OUT event. */ 81 /* Send an ANDROID_FOCUS_OUT event. */
81 public static native void sendFocusOut (short window, long time); 82 public static native long sendFocusOut (short window, long time);
82 83
83 /* Send an ANDROID_WINDOW_ACTION event. */ 84 /* Send an ANDROID_WINDOW_ACTION event. */
84 public static native void sendWindowAction (short window, int action); 85 public static native long sendWindowAction (short window, int action);
85 86
86 /* Send an ANDROID_ENTER_NOTIFY event. */ 87 /* Send an ANDROID_ENTER_NOTIFY event. */
87 public static native void sendEnterNotify (short window, int x, int y, 88 public static native long sendEnterNotify (short window, int x, int y,
88 long time); 89 long time);
89 90
90 /* Send an ANDROID_LEAVE_NOTIFY event. */ 91 /* Send an ANDROID_LEAVE_NOTIFY event. */
91 public static native void sendLeaveNotify (short window, int x, int y, 92 public static native long sendLeaveNotify (short window, int x, int y,
92 long time); 93 long time);
93 94
94 /* Send an ANDROID_MOTION_NOTIFY event. */ 95 /* Send an ANDROID_MOTION_NOTIFY event. */
95 public static native void sendMotionNotify (short window, int x, int y, 96 public static native long sendMotionNotify (short window, int x, int y,
96 long time); 97 long time);
97 98
98 /* Send an ANDROID_BUTTON_PRESS event. */ 99 /* Send an ANDROID_BUTTON_PRESS event. */
99 public static native void sendButtonPress (short window, int x, int y, 100 public static native long sendButtonPress (short window, int x, int y,
100 long time, int state, 101 long time, int state,
101 int button); 102 int button);
102 103
103 /* Send an ANDROID_BUTTON_RELEASE event. */ 104 /* Send an ANDROID_BUTTON_RELEASE event. */
104 public static native void sendButtonRelease (short window, int x, int y, 105 public static native long sendButtonRelease (short window, int x, int y,
105 long time, int state, 106 long time, int state,
106 int button); 107 int button);
107 108
108 /* Send an ANDROID_TOUCH_DOWN event. */ 109 /* Send an ANDROID_TOUCH_DOWN event. */
109 public static native void sendTouchDown (short window, int x, int y, 110 public static native long sendTouchDown (short window, int x, int y,
110 long time, int pointerID); 111 long time, int pointerID);
111 112
112 /* Send an ANDROID_TOUCH_UP event. */ 113 /* Send an ANDROID_TOUCH_UP event. */
113 public static native void sendTouchUp (short window, int x, int y, 114 public static native long sendTouchUp (short window, int x, int y,
114 long time, int pointerID); 115 long time, int pointerID);
115 116
116 /* Send an ANDROID_TOUCH_MOVE event. */ 117 /* Send an ANDROID_TOUCH_MOVE event. */
117 public static native void sendTouchMove (short window, int x, int y, 118 public static native long sendTouchMove (short window, int x, int y,
118 long time, int pointerID); 119 long time, int pointerID);
119 120
120 /* Send an ANDROID_WHEEL event. */ 121 /* Send an ANDROID_WHEEL event. */
121 public static native void sendWheel (short window, int x, int y, 122 public static native long sendWheel (short window, int x, int y,
122 long time, int state, 123 long time, int state,
123 float xDelta, float yDelta); 124 float xDelta, float yDelta);
124 125
125 /* Send an ANDROID_ICONIFIED event. */ 126 /* Send an ANDROID_ICONIFIED event. */
126 public static native void sendIconified (short window); 127 public static native long sendIconified (short window);
127 128
128 /* Send an ANDROID_DEICONIFIED event. */ 129 /* Send an ANDROID_DEICONIFIED event. */
129 public static native void sendDeiconified (short window); 130 public static native long sendDeiconified (short window);
130 131
131 /* Send an ANDROID_CONTEXT_MENU event. */ 132 /* Send an ANDROID_CONTEXT_MENU event. */
132 public static native void sendContextMenu (short window, int menuEventID); 133 public static native long sendContextMenu (short window, int menuEventID);
133 134
134 static 135 static
135 { 136 {
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 @@
1/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22import android.app.Activity;
23import android.content.Intent;
24import android.os.Bundle;
25import android.os.Build;
26import android.view.View;
27import android.view.ViewGroup.LayoutParams;
28import android.widget.LinearLayout;
29import android.widget.TextView;
30
31import android.R;
32
33/* This module provides a ``preferences'' display for Emacs. It is
34 supposed to be launched from inside the Settings application to
35 perform various actions, such as starting Emacs with the ``-Q''
36 option, which would not be possible otherwise, as there is no
37 command line on Android. */
38
39public class EmacsPreferencesActivity extends Activity
40{
41 /* The linear layout associated with the activity. */
42 private LinearLayout layout;
43
44 /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and
45 tell the system to EmacsActivity with some parameters later. */
46
47 private void
48 startEmacsQ ()
49 {
50 Intent intent;
51
52 intent = new Intent (this, EmacsActivity.class);
53 intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
54 | Intent.FLAG_ACTIVITY_CLEAR_TASK);
55 intent.putExtra ("org.gnu.emacs.START_DASH_Q", true);
56 startActivity (intent);
57 System.exit (0);
58 }
59
60 @Override
61 public void
62 onCreate (Bundle savedInstanceState)
63 {
64 LinearLayout layout;
65 TextView textView;
66 LinearLayout.LayoutParams params;
67 int resid;
68
69 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
70 setTheme (R.style.Theme_DeviceDefault_Settings);
71 else if (Build.VERSION.SDK_INT
72 >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
73 setTheme (R.style.Theme_DeviceDefault);
74
75 layout = new LinearLayout (this);
76 layout.setOrientation (LinearLayout.VERTICAL);
77 setContentView (layout);
78
79 textView = new TextView (this);
80 textView.setPadding (8, 20, 20, 8);
81
82 params = new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT,
83 LayoutParams.WRAP_CONTENT);
84 textView.setLayoutParams (params);
85 textView.setText ("(Re)start Emacs with -Q");
86 textView.setOnClickListener (new View.OnClickListener () {
87 @Override
88 public void
89 onClick (View view)
90 {
91 startEmacsQ ();
92 }
93 });
94 layout.addView (textView);
95
96 super.onCreate (savedInstanceState);
97 }
98};
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;
32import android.view.KeyEvent; 32import android.view.KeyEvent;
33 33
34import android.annotation.TargetApi; 34import android.annotation.TargetApi;
35
36import android.app.Notification;
37import android.app.NotificationManager;
38import android.app.NotificationChannel;
39import android.app.PendingIntent;
35import android.app.Service; 40import android.app.Service;
41
36import android.content.Context; 42import android.content.Context;
37import android.content.Intent; 43import android.content.Intent;
38import android.content.res.AssetManager; 44import android.content.res.AssetManager;
@@ -63,6 +69,7 @@ public class EmacsService extends Service
63 public static final String TAG = "EmacsService"; 69 public static final String TAG = "EmacsService";
64 public static final int MAX_PENDING_REQUESTS = 256; 70 public static final int MAX_PENDING_REQUESTS = 256;
65 public static volatile EmacsService SERVICE; 71 public static volatile EmacsService SERVICE;
72 public static boolean needDashQ;
66 73
67 private EmacsThread thread; 74 private EmacsThread thread;
68 private Handler handler; 75 private Handler handler;
@@ -74,6 +81,31 @@ public class EmacsService extends Service
74 public int 81 public int
75 onStartCommand (Intent intent, int flags, int startId) 82 onStartCommand (Intent intent, int flags, int startId)
76 { 83 {
84 Notification notification;
85 NotificationManager manager;
86 NotificationChannel channel;
87 String infoBlurb;
88 Object tem;
89
90 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
91 {
92 tem = getSystemService (Context.NOTIFICATION_SERVICE);
93 manager = (NotificationManager) tem;
94 infoBlurb = ("See (emacs)Android Environment for more"
95 + " details about this notification.");
96 channel
97 = new NotificationChannel ("emacs", "Emacs persistent notification",
98 NotificationManager.IMPORTANCE_DEFAULT);
99 manager.createNotificationChannel (channel);
100 notification = (new Notification.Builder (this, "emacs")
101 .setContentTitle ("Emacs")
102 .setContentText (infoBlurb)
103 .setSmallIcon (android.R.drawable.sym_def_app_icon)
104 .build ());
105 manager.notify (1, notification);
106 startForeground (1, notification);
107 }
108
77 return START_NOT_STICKY; 109 return START_NOT_STICKY;
78 } 110 }
79 111
@@ -137,7 +169,7 @@ public class EmacsService extends Service
137 this); 169 this);
138 170
139 /* Start the thread that runs Emacs. */ 171 /* Start the thread that runs Emacs. */
140 thread = new EmacsThread (this); 172 thread = new EmacsThread (this, needDashQ);
141 thread.start (); 173 thread.start ();
142 } 174 }
143 catch (IOException exception) 175 catch (IOException exception)
@@ -444,4 +476,32 @@ public class EmacsService extends Service
444 } 476 }
445 } 477 }
446 } 478 }
479
480
481
482 /* Start the Emacs service if necessary. On Android 26 and up,
483 start Emacs as a foreground service with a notification, to avoid
484 it being killed by the system.
485
486 On older systems, simply start it as a normal background
487 service. */
488
489 public static void
490 startEmacsService (Context context)
491 {
492 PendingIntent intent;
493
494 if (EmacsService.SERVICE == null)
495 {
496 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
497 /* Start the Emacs service now. */
498 context.startService (new Intent (context,
499 EmacsService.class));
500 else
501 /* Display the permanant notification and start Emacs as a
502 foreground service. */
503 context.startForegroundService (new Intent (context,
504 EmacsService.class));
505 }
506 }
447}; 507};
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;
32public class EmacsSurfaceView extends SurfaceView 32public class EmacsSurfaceView extends SurfaceView
33{ 33{
34 private static final String TAG = "EmacsSurfaceView"; 34 private static final String TAG = "EmacsSurfaceView";
35
36 public Object surfaceChangeLock; 35 public Object surfaceChangeLock;
37 private boolean created; 36 private boolean created;
37 private EmacsView view;
38 38
39 public 39 /* This is the callback used on Android 8 to 25. */
40 EmacsSurfaceView (final EmacsView view)
41 {
42 super (view.getContext ());
43
44 surfaceChangeLock = new Object ();
45 40
46 getHolder ().addCallback (new SurfaceHolder.Callback () { 41 private class Callback implements SurfaceHolder.Callback
47 @Override 42 {
48 public void 43 @Override
49 surfaceChanged (SurfaceHolder holder, int format, 44 public void
50 int width, int height) 45 surfaceChanged (SurfaceHolder holder, int format,
46 int width, int height)
47 {
48 Log.d (TAG, "surfaceChanged: " + view + ", " + view.pendingConfigure);
49
50 /* Make sure not to swap buffers if there is pending
51 configuration, because otherwise the redraw callback will not
52 run correctly. */
53
54 if (view.pendingConfigure == 0)
55 view.swapBuffers ();
56 }
57
58 @Override
59 public void
60 surfaceCreated (SurfaceHolder holder)
61 {
62 synchronized (surfaceChangeLock)
51 { 63 {
52 Log.d (TAG, "surfaceChanged: " + view); 64 Log.d (TAG, "surfaceCreated: " + view);
53 view.swapBuffers (); 65 created = true;
54 } 66 }
55 67
56 @Override 68 /* Drop the lock when doing this, or a deadlock can
57 public void 69 result. */
58 surfaceCreated (SurfaceHolder holder) 70 view.swapBuffers ();
59 { 71 }
60 synchronized (surfaceChangeLock)
61 {
62 Log.d (TAG, "surfaceCreated: " + view);
63 created = true;
64 }
65
66 /* Drop the lock when doing this, or a deadlock can
67 result. */
68 view.swapBuffers ();
69 }
70 72
71 @Override 73 @Override
72 public void 74 public void
73 surfaceDestroyed (SurfaceHolder holder) 75 surfaceDestroyed (SurfaceHolder holder)
76 {
77 synchronized (surfaceChangeLock)
74 { 78 {
75 synchronized (surfaceChangeLock) 79 Log.d (TAG, "surfaceDestroyed: " + view);
76 { 80 created = false;
77 Log.d (TAG, "surfaceDestroyed: " + view);
78 created = false;
79 }
80 } 81 }
81 }); 82 }
83 }
84
85 /* And this is the callback used on Android 26 and later. It is
86 used because it can tell the system when drawing completes. */
87
88 private class Callback2 extends Callback implements SurfaceHolder.Callback2
89 {
90 @Override
91 public void
92 surfaceRedrawNeeded (SurfaceHolder holder)
93 {
94 /* This version is not supported. */
95 return;
96 }
97
98 @Override
99 public void
100 surfaceRedrawNeededAsync (SurfaceHolder holder,
101 Runnable drawingFinished)
102 {
103 Runnable old;
104
105 Log.d (TAG, "surfaceRedrawNeededAsync: " + view.pendingConfigure);
106
107 /* The system calls this function when it wants to know whether
108 or not Emacs is still configuring itself in response to a
109 resize.
110
111 If the view did not send an outstanding ConfigureNotify
112 event, then call drawingFinish immediately. Else, give it to
113 the view to execute after drawing completes. */
114
115 if (view.pendingConfigure == 0)
116 drawingFinished.run ();
117 else
118 /* And set this runnable to run once drawing completes. */
119 view.drawingFinished = drawingFinished;
120 }
121 }
122
123 public
124 EmacsSurfaceView (final EmacsView view)
125 {
126 super (view.getContext ());
127
128 this.surfaceChangeLock = new Object ();
129 this.view = view;
130
131 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
132 getHolder ().addCallback (new Callback ());
133 else
134 getHolder ().addCallback (new Callback2 ());
82 } 135 }
83 136
84 public boolean 137 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;
23 23
24public class EmacsThread extends Thread 24public class EmacsThread extends Thread
25{ 25{
26 EmacsService context; 26 /* Whether or not Emacs should be started -Q. */
27 private boolean startDashQ;
27 28
28 public 29 public
29 EmacsThread (EmacsService service) 30 EmacsThread (EmacsService service, boolean startDashQ)
30 { 31 {
31 context = service; 32 this.startDashQ = startDashQ;
32 } 33 }
33 34
34 public void 35 public void
@@ -36,7 +37,10 @@ public class EmacsThread extends Thread
36 { 37 {
37 String args[]; 38 String args[];
38 39
39 args = new String[] { "libandroid-emacs.so", }; 40 if (!startDashQ)
41 args = new String[] { "libandroid-emacs.so", };
42 else
43 args = new String[] { "libandroid-emacs.so", "-Q", };
40 44
41 /* Run the native code now. */ 45 /* Run the native code now. */
42 EmacsNative.initEmacs (args); 46 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/>. */
19 19
20package org.gnu.emacs; 20package org.gnu.emacs;
21 21
22import android.content.Context;
22import android.content.res.ColorStateList; 23import android.content.res.ColorStateList;
23 24
24import android.view.ContextMenu; 25import android.view.ContextMenu;
@@ -27,6 +28,8 @@ import android.view.KeyEvent;
27import android.view.MotionEvent; 28import android.view.MotionEvent;
28import android.view.ViewGroup; 29import android.view.ViewGroup;
29 30
31import android.view.inputmethod.InputMethodManager;
32
30import android.graphics.Bitmap; 33import android.graphics.Bitmap;
31import android.graphics.Canvas; 34import android.graphics.Canvas;
32import android.graphics.Rect; 35import android.graphics.Rect;
@@ -86,11 +89,23 @@ public class EmacsView extends ViewGroup
86 /* The serial of the last clip rectangle change. */ 89 /* The serial of the last clip rectangle change. */
87 private long lastClipSerial; 90 private long lastClipSerial;
88 91
92 /* The InputMethodManager for this view's context. */
93 private InputMethodManager imManager;
94
95 /* Runnable that will run once drawing completes. */
96 public Runnable drawingFinished;
97
98 /* Serial of the last ConfigureNotify event sent that Emacs has not
99 yet responded to. 0 if there is no such outstanding event. */
100 public long pendingConfigure;
101
89 public 102 public
90 EmacsView (EmacsWindow window) 103 EmacsView (EmacsWindow window)
91 { 104 {
92 super (EmacsService.SERVICE); 105 super (EmacsService.SERVICE);
93 106
107 Object tem;
108
94 this.window = window; 109 this.window = window;
95 this.damageRegion = new Region (); 110 this.damageRegion = new Region ();
96 this.paint = new Paint (); 111 this.paint = new Paint ();
@@ -111,6 +126,10 @@ public class EmacsView extends ViewGroup
111 /* Get rid of the default focus highlight. */ 126 /* Get rid of the default focus highlight. */
112 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) 127 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
113 setDefaultFocusHighlightEnabled (false); 128 setDefaultFocusHighlightEnabled (false);
129
130 /* Obtain the input method manager. */
131 tem = getContext ().getSystemService (Context.INPUT_METHOD_SERVICE);
132 imManager = (InputMethodManager) tem;
114 } 133 }
115 134
116 private void 135 private void
@@ -259,7 +278,8 @@ public class EmacsView extends ViewGroup
259 if (changed || mustReportLayout) 278 if (changed || mustReportLayout)
260 { 279 {
261 mustReportLayout = false; 280 mustReportLayout = false;
262 window.viewLayout (left, top, right, bottom); 281 pendingConfigure
282 = window.viewLayout (left, top, right, bottom);
263 } 283 }
264 284
265 measuredWidth = right - left; 285 measuredWidth = right - left;
@@ -538,4 +558,37 @@ public class EmacsView extends ViewGroup
538 Runtime.getRuntime ().gc (); 558 Runtime.getRuntime ().gc ();
539 } 559 }
540 } 560 }
561
562 public void
563 showOnScreenKeyboard ()
564 {
565 /* Specifying no flags at all tells the system the user asked for
566 the input method to be displayed. */
567 imManager.showSoftInput (this, 0);
568 }
569
570 public void
571 hideOnScreenKeyboard ()
572 {
573 imManager.hideSoftInputFromWindow (this.getWindowToken (),
574 0);
575 }
576
577 public void
578 windowUpdated (long serial)
579 {
580 Log.d (TAG, "windowUpdated: serial is " + serial);
581
582 if (pendingConfigure <= serial
583 /* Detect wraparound. */
584 || pendingConfigure - serial >= 0x7fffffff)
585 {
586 pendingConfigure = 0;
587
588 if (drawingFinished != null)
589 drawingFinished.run ();
590
591 drawingFinished = null;
592 }
593 }
541}; 594};
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
233 return attached; 233 return attached;
234 } 234 }
235 235
236 public void 236 public long
237 viewLayout (int left, int top, int right, int bottom) 237 viewLayout (int left, int top, int right, int bottom)
238 { 238 {
239 int rectWidth, rectHeight; 239 int rectWidth, rectHeight;
@@ -249,10 +249,10 @@ public class EmacsWindow extends EmacsHandleObject
249 rectWidth = right - left; 249 rectWidth = right - left;
250 rectHeight = bottom - top; 250 rectHeight = bottom - top;
251 251
252 EmacsNative.sendConfigureNotify (this.handle, 252 return EmacsNative.sendConfigureNotify (this.handle,
253 System.currentTimeMillis (), 253 System.currentTimeMillis (),
254 left, top, rectWidth, 254 left, top, rectWidth,
255 rectHeight); 255 rectHeight);
256 } 256 }
257 257
258 public void 258 public void
@@ -589,11 +589,20 @@ public class EmacsWindow extends EmacsHandleObject
589 EmacsActivity.invalidateFocus (); 589 EmacsActivity.invalidateFocus ();
590 } 590 }
591 591
592 /* Notice that the activity has been detached or destroyed.
593
594 ISFINISHING is set if the activity is not the main activity, or
595 if the activity was not destroyed in response to explicit user
596 action. */
597
592 public void 598 public void
593 onActivityDetached () 599 onActivityDetached (boolean isFinishing)
594 { 600 {
595 /* Destroy the associated frame when the activity is detached. */ 601 /* Destroy the associated frame when the activity is detached in
596 EmacsNative.sendWindowAction (this.handle, 0); 602 response to explicit user action. */
603
604 if (isFinishing)
605 EmacsNative.sendWindowAction (this.handle, 0);
597 } 606 }
598 607
599 /* Look through the button state to determine what button EVENT was 608 /* Look through the button state to determine what button EVENT was
@@ -1064,4 +1073,37 @@ public class EmacsWindow extends EmacsHandleObject
1064 /* Return the resulting coordinates. */ 1073 /* Return the resulting coordinates. */
1065 return array; 1074 return array;
1066 } 1075 }
1076
1077 public void
1078 toggleOnScreenKeyboard (final boolean on)
1079 {
1080 EmacsService.SERVICE.runOnUiThread (new Runnable () {
1081 @Override
1082 public void
1083 run ()
1084 {
1085 if (on)
1086 view.showOnScreenKeyboard ();
1087 else
1088 view.hideOnScreenKeyboard ();
1089 }
1090 });
1091 }
1092
1093 /* Notice that outstanding configure events have been processed.
1094 SERIAL is checked in the UI thread to verify that no new
1095 configure events have been generated in the mean time. */
1096
1097 public void
1098 windowUpdated (final long serial)
1099 {
1100 EmacsService.SERVICE.runOnUiThread (new Runnable () {
1101 @Override
1102 public void
1103 run ()
1104 {
1105 view.windowUpdated (serial);
1106 }
1107 });
1108 }
1067}; 1109};
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
134 } 134 }
135 135
136 public void 136 public void
137 removeWindowConsumer (WindowConsumer consumer) 137 removeWindowConsumer (WindowConsumer consumer, boolean isFinishing)
138 { 138 {
139 EmacsWindow window; 139 EmacsWindow window;
140 140
@@ -147,7 +147,7 @@ public class EmacsWindowAttachmentManager
147 Log.d (TAG, "removeWindowConsumer: detaching " + window); 147 Log.d (TAG, "removeWindowConsumer: detaching " + window);
148 148
149 consumer.detachWindow (); 149 consumer.detachWindow ();
150 window.onActivityDetached (); 150 window.onActivityDetached (isFinishing);
151 } 151 }
152 152
153 Log.d (TAG, "removeWindowConsumer: removing " + consumer); 153 Log.d (TAG, "removeWindowConsumer: removing " + consumer);