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:
Po Lu
2023-01-20 19:06:32 +08:00
parent e07b58dc35
commit d44b60c2f0
27 changed files with 895 additions and 200 deletions

View File

@@ -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"

View File

@@ -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 ();

View File

@@ -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)
{

View File

@@ -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
{

View 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);
}
};

View File

@@ -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));
}
}
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}
}
};

View File

@@ -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);
}
});
}
};

View File

@@ -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);