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.
This commit is contained 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"
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
98
java/org/gnu/emacs/EmacsPreferencesActivity.java
Normal file
98
java/org/gnu/emacs/EmacsPreferencesActivity.java
Normal file
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
/* This is the callback used on Android 8 to 25. */
|
||||
|
||||
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, "surfaceCreated: " + view);
|
||||
created = true;
|
||||
}
|
||||
|
||||
/* Drop the lock when doing this, or a deadlock can
|
||||
result. */
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
surfaceDestroyed (SurfaceHolder holder)
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
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 ());
|
||||
|
||||
surfaceChangeLock = new Object ();
|
||||
this.surfaceChangeLock = new Object ();
|
||||
this.view = view;
|
||||
|
||||
getHolder ().addCallback (new SurfaceHolder.Callback () {
|
||||
@Override
|
||||
public void
|
||||
surfaceChanged (SurfaceHolder holder, int format,
|
||||
int width, int height)
|
||||
{
|
||||
Log.d (TAG, "surfaceChanged: " + view);
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@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 ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
surfaceDestroyed (SurfaceHolder holder)
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceDestroyed: " + view);
|
||||
created = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
getHolder ().addCallback (new Callback ());
|
||||
else
|
||||
getHolder ().addCallback (new Callback2 ());
|
||||
}
|
||||
|
||||
public boolean
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user