Update Android port

* java/org/gnu/emacs/EmacsView.java (EmacsView)
(prepareForLayout): New function.  Call this prior to mapping
the view.
(onGlobalLayout): New function.  Register as global layout
listener.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow)
(notifyContentRectPosition): New function.  Use specified
xPosition and yPosition when reporting the offsets of children
of the root window.
* java/org/gnu/emacs/EmacsWindowAttachmentManager.java
(registerWindow): Specify activity launch bounds if necessary.
* src/androidterm.c (handle_one_android_event): Send
MOVE_FRAME_EVENT where necessary.
This commit is contained in:
Po Lu 2023-03-18 10:54:26 +08:00
parent 773bdb15ab
commit 634e3fcc20
4 changed files with 160 additions and 63 deletions

View file

@ -28,6 +28,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@ -51,6 +52,7 @@
It is also a ViewGroup, as it also lays out children. */
public final class EmacsView extends ViewGroup
implements ViewTreeObserver.OnGlobalLayoutListener
{
public static final String TAG = "EmacsView";
@ -136,6 +138,9 @@ public final class EmacsView extends ViewGroup
context = getContext ();
tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
imManager = (InputMethodManager) tem;
/* Add this view as its own global layout listener. */
getViewTreeObserver ().addOnGlobalLayoutListener (this);
}
private void
@ -238,6 +243,13 @@ public final class EmacsView extends ViewGroup
return canvas;
}
public void
prepareForLayout (int wantedWidth, int wantedHeight)
{
measuredWidth = wantedWidth;
measuredHeight = wantedWidth;
}
@Override
protected void
onMeasure (int widthMeasureSpec, int heightMeasureSpec)
@ -562,8 +574,7 @@ else if (child.getVisibility () != GONE)
bitmapDirty = true;
/* Now expose the view contents again. */
EmacsNative.sendExpose (this.window.handle, 0, 0,
measuredWidth, measuredHeight);
EmacsNative.sendExpose (this.window.handle, 0, 0, 0, 0);
super.onAttachedToWindow ();
}
@ -678,4 +689,19 @@ else if (child.getVisibility () != GONE)
{
return icMode;
}
@Override
public void
onGlobalLayout ()
{
int[] locations;
/* Get the absolute offset of this view and specify its left and
top position in subsequent ConfigureNotify events. */
locations = new int[2];
getLocationInWindow (locations);
window.notifyContentRectPosition (locations[0],
locations[1]);
}
};

View file

@ -133,6 +133,9 @@ private static class Coordinate
creating new bitmaps. */
public volatile int background;
/* The position of this window relative to the root window. */
public int xPosition, yPosition;
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height, boolean overrideRedirect)
@ -192,18 +195,14 @@ private static class Coordinate
background = pixel;
}
public Rect
public synchronized Rect
getGeometry ()
{
synchronized (this)
{
/* Huh, this is it. */
return rect;
}
return new Rect (rect);
}
@Override
public void
public synchronized void
destroyHandle () throws IllegalStateException
{
if (parent != null)
@ -258,22 +257,28 @@ private static class Coordinate
return attached;
}
public long
public synchronized long
viewLayout (int left, int top, int right, int bottom)
{
int rectWidth, rectHeight;
synchronized (this)
{
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
}
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
rectWidth = right - left;
rectHeight = bottom - top;
/* If parent is null, use xPosition and yPosition instead of the
geometry rectangle positions. */
if (parent == null)
{
left = xPosition;
top = yPosition;
}
return EmacsNative.sendConfigureNotify (this.handle,
System.currentTimeMillis (),
left, top, rectWidth,
@ -283,8 +288,6 @@ private static class Coordinate
public void
requestViewLayout ()
{
/* This is necessary because otherwise subsequent drawing on the
Emacs thread may be lost. */
view.explicitlyDirtyBitmap ();
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@ -302,35 +305,29 @@ private static class Coordinate
});
}
public void
public synchronized void
resizeWindow (int width, int height)
{
synchronized (this)
{
rect.right = rect.left + width;
rect.bottom = rect.top + height;
rect.right = rect.left + width;
rect.bottom = rect.top + height;
requestViewLayout ();
}
requestViewLayout ();
}
public void
public synchronized void
moveWindow (int x, int y)
{
int width, height;
synchronized (this)
{
width = rect.width ();
height = rect.height ();
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
requestViewLayout ();
}
requestViewLayout ();
}
private WindowManager.LayoutParams
@ -366,13 +363,17 @@ private static class Coordinate
return EmacsService.SERVICE;
}
public void
public synchronized void
mapWindow ()
{
final int width, height;
if (isMapped)
return;
isMapped = true;
width = rect.width ();
height = rect.height ();
if (parent == null)
{
@ -424,6 +425,7 @@ private static class Coordinate
/* Attach the view. */
try
{
view.prepareForLayout (width, height);
windowManager.addView (view, params);
/* Record the window manager being used in the
@ -448,10 +450,14 @@ private static class Coordinate
public void
run ()
{
/* Prior to mapping the view, set its measuredWidth and
measuredHeight to some reasonable value, in order to
avoid excessive bitmap dirtying. */
view.prepareForLayout (width, height);
view.setVisibility (View.VISIBLE);
if (!getDontFocusOnMap ())
/* Eventually this should check no-focus-on-map. */
view.requestFocus ();
}
});
@ -512,12 +518,9 @@ private static class Coordinate
public void
clearWindow ()
{
synchronized (this)
{
EmacsService.SERVICE.fillRectangle (this, scratchGC,
0, 0, rect.width (),
rect.height ());
}
EmacsService.SERVICE.fillRectangle (this, scratchGC,
0, 0, rect.width (),
rect.height ());
}
public void
@ -1001,7 +1004,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER)
return false;
}
public void
public synchronized void
reparentTo (final EmacsWindow otherWindow, int x, int y)
{
int width, height;
@ -1017,15 +1020,12 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER)
parent = otherWindow;
/* Move this window to the new location. */
synchronized (this)
{
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
}
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
/* Now do the work necessary on the UI thread to reparent the
window. */
@ -1081,7 +1081,7 @@ else if (EmacsWindow.this.isMapped)
});
}
public void
public synchronized void
raise ()
{
/* This does nothing here. */
@ -1103,7 +1103,7 @@ else if (EmacsWindow.this.isMapped)
});
}
public void
public synchronized void
lower ()
{
/* This does nothing here. */
@ -1125,17 +1125,15 @@ else if (EmacsWindow.this.isMapped)
});
}
public int[]
public synchronized int[]
getWindowGeometry ()
{
int[] array;
Rect rect;
array = new int[4];
rect = getGeometry ();
array[0] = rect.left;
array[1] = rect.top;
array[0] = parent != null ? rect.left : xPosition;
array[1] = parent != null ? rect.top : yPosition;
array[2] = rect.width ();
array[3] = rect.height ();
@ -1272,4 +1270,31 @@ else if (EmacsWindow.this.isMapped)
}
});
}
public synchronized void
notifyContentRectPosition (int xPosition, int yPosition)
{
Rect geometry;
/* Ignore these notifications if not a child of the root
window. */
if (parent != null)
return;
/* xPosition and yPosition are the position of this window
relative to the screen. Set them and request a ConfigureNotify
event. */
if (this.xPosition != xPosition
|| this.yPosition != yPosition)
{
this.xPosition = xPosition;
this.yPosition = yPosition;
EmacsNative.sendConfigureNotify (this.handle,
System.currentTimeMillis (),
xPosition, yPosition,
rect.width (), rect.height ());
}
}
};

View file

@ -22,7 +22,9 @@
import java.util.ArrayList;
import java.util.List;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
/* Code to paper over the differences in lifecycles between
@ -102,6 +104,7 @@ public interface WindowConsumer
registerWindow (EmacsWindow window)
{
Intent intent;
ActivityOptions options;
Log.d (TAG, "registerWindow (maybe): " + window);
@ -128,7 +131,18 @@ public interface WindowConsumer
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
| Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
EmacsService.SERVICE.startActivity (intent);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
EmacsService.SERVICE.startActivity (intent);
else
{
/* Specify the desired window size. */
options = ActivityOptions.makeBasic ();
options.setLaunchBounds (window.getGeometry ());
EmacsService.SERVICE.startActivity (intent,
options.toBundle ());
}
Log.d (TAG, "registerWindow: startActivity");
}

View file

@ -787,6 +787,33 @@ handle_one_android_event (struct android_display_info *dpyinfo,
cancel_mouse_face (f);
}
/* Now change the left and top position of this window. */
{
int old_left = f->left_pos;
int old_top = f->top_pos;
Lisp_Object frame;
XSETFRAME (frame, f);
{
android_window root;
unsigned int dummy_uint;
android_get_geometry (FRAME_ANDROID_WINDOW (f),
&root, &f->left_pos, &f->top_pos,
&dummy_uint, &dummy_uint,
&dummy_uint);
}
if (!FRAME_TOOLTIP_P (f)
&& (old_left != f->left_pos || old_top != f->top_pos))
{
inev.ie.kind = MOVE_FRAME_EVENT;
XSETFRAME (inev.ie.frame_or_window, f);
}
}
goto OTHER;
case ANDROID_KEY_PRESS:
@ -1100,6 +1127,11 @@ handle_one_android_event (struct android_display_info *dpyinfo,
if (!FRAME_GARBAGED_P (f))
{
__android_log_print (ANDROID_LOG_VERBOSE, __func__,
"expose: %d %d %d %d\n",
event->xexpose.x, event->xexpose.y,
event->xexpose.width,
event->xexpose.height);
expose_frame (f, event->xexpose.x, event->xexpose.y,
event->xexpose.width, event->xexpose.height);
show_back_buffer (f);