emacs-diffs
[Top][All Lists]
Advanced

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

feature/android a32963e11f 1/2: Update Android port


From: Po Lu
Subject: feature/android a32963e11f 1/2: Update Android port
Date: Mon, 2 Jan 2023 08:39:25 -0500 (EST)

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

    Update Android port
    
    * Makefile.in (java): Depend on info.
    (MAKEFILE_NAME):
    (config.status): Remove unneeded changes.
    * configure.ac (BUILD_DETAILS, ANDROID_STUBIFY): Don't require a
    C++ compiler on Android.
    * java/AndroidManifest.xml: <EmacsActivity>: Set launchMode
    appropriately.  <EmacsMultitaskActivity>: New activity.
    * java/Makefile.in (CROSS_BINS): Add EmacsClient.
    * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity)
    (onCreate): Use the window attachment manager.
    * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea)
    (paintTo): Implement clip masks correctly.
    * java/org/gnu/emacs/EmacsDrawRectangle.java (getRect, paintTo):
    Fix damage tracking rectangles.
    * java/org/gnu/emacs/EmacsFontDriver.java (FontSpec, toString):
    New function.
    (FontMetrics, EmacsFontDriver): Fix signature of textExtents.
    * java/org/gnu/emacs/EmacsMultitaskActivity.java
    (EmacsMultitaskActivity): New file.
    * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
    functions sendFocusIn, sendFocusOut, sendWindowAction.
    * java/org/gnu/emacs/EmacsPaintQueue.java (run): Fix clipping
    handling.
    * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Add
    constructor for mutable pixmaps.
    * java/org/gnu/emacs/EmacsSdk23FontDriver.java
    (EmacsSdk23FontDriver): New file.
    * java/org/gnu/emacs/EmacsSdk7FontDriver.java
    (EmacsSdk7FontDriver, Sdk7Typeface, Sdk7FontEntity, Sdk7FontObject)
    (checkMatch, hasChar, encodeChar): Implement text display and
    fix font metrics semantics.
    
    * java/org/gnu/emacs/EmacsService.java (EmacsService): Remove
    availableChildren.
    (getLibraryDirectory, onCreate): Pass pixel density to Emacs.
    (clearArea): Fix arguments.  Switch to using the window
    attachment manager.
    * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged)
    (surfaceCreated): Flip buffers on surface attachment.
    * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers):
    New argument FORCE.  Always swap if it is true.
    (onKeyMultiple, onFocusChanged): New functions.
    
    * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, destroyHandle)
    (run): Switch to using the window attachment manager.
    * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
    (EmacsWindowAttachmentManager): New file.
    
    * lisp/cus-edit.el (custom-button, custom-button-mouse)
    (custom-button-pressed):
    * lisp/faces.el (tool-bar): Define faces correctly on Android.
    * src/android.c (struct android_emacs_pixmap): Add mutable
    constructor.
    (struct android_emacs_drawable): New structure.
    (android_write_event): Check if event queue hasn't yet been
    initialized.
    (android_select): Set errno to EINTR if pselect fails.
    (android_close): Remove unused debugging code.
    (android_get_home_directory): New function.
    (Java_org_gnu_emacs_EmacsNative_setEmacsParams): Set pixel
    density and compute game path.
    (android_init_emacs_drawable): New function.
    (Java_org_gnu_emacs_EmacsNative_sendKeyPress): New argument
    `unicode_char'.  Pass it in events.
    (Java_org_gnu_emacs_EmacsNative_sendKeyRelease): Likewise.
    (Java_org_gnu_emacs_EmacsNative_sendFocusIn)
    (Java_org_gnu_emacs_EmacsNative_sendFocusOut)
    (Java_org_gnu_emacs_EmacsNative_sendWindowAction): New
    functions.
    (android_resolve_handle): Export function.
    (android_change_gc): Clear clip rects under the right
    circumstances.  Set right clip mask field.
    (android_create_pixmap_from_bitmap_data): Use correct alpha
    channels.
    (android_create_pixmap): Create mutable pixmap and avoid
    redundant color array allocation.
    (android_create_bitmap_from_data, android_create_image)
    (android_destroy_image, android_put_pixel, android_get_pixel)
    (android_get_image, android_put_image, faccessat): New
    functions.
    
    * src/android.h: Update prototypes.
    
    * src/androidfns.c (android_default_font_parameter): Prefer
    monospace to Droid Sans Mono.
    * src/androidfont.c (struct android_emacs_font_driver): New
    method `draw'.
    (struct android_emacs_font_spec): New field `dpi'.
    (struct androidfont_info): Add font metrics cache.
    (android_init_font_driver, android_init_font_spec): Adjust
    accordingly.
    (androidfont_from_lisp, androidfont_from_java): Handle new
    fields.
    (androidfont_draw): Implement function.
    (androidfont_open_font): Set pixel size correctly.
    (androidfont_close_font): Free metrics cache.
    (androidfont_cache_text_extents)
    (androidfont_check_cached_extents): New functions.
    (androidfont_text_extents): Cache glyph metrics somewhere for
    future use.
    (androidfont_list_family): Implement function.
    
    * src/androidgui.h (enum android_event_type): New focus and
    window action events.
    (enum android_modifier_mask): New masks.
    (struct android_key_event): New field `unicode_char'.
    (ANDROID_IS_MODIFIER_KEY): Newmacro.
    (struct android_focus_event, struct
    android_window_action_event): New structs.
    (union android_event): Add new fields.
    (enum android_image_format, struct android_image): New enums and
    structs.
    
    * src/androidterm.c (android_android_to_emacs_modifiers)
    (android_emacs_to_android_modifiers, android_lower_frame)
    (android_raise_frame, android_new_focus_frame)
    (android_focus_changed, android_detect_focus_change): New
    functions.
    (handle_one_android_event): Implement focus and key event
    handling.
    (android_frame_rehighlight): New function.
    (android_frame_raise_lower): Implement accordingly.
    (android_make_frame_invisible): Clear highlight_frame if
    required.
    (android_free_frame_resources): Clear x_focus_event_frame if
    required.
    (android_draw_fringe_bitmap, android_draw_image_foreground)
    (android_draw_image_foreground_1)
    (android_draw_image_glyph_string): Remove unnecessary code.
    (android_create_terminal, android_term_init): Set the baud rate
    to something sensible.
    * src/androidterm.h (struct android_bitmap_record): Make
    structure the same as on X.
    (struct android_display_info): New focus tracking fields.
    (struct android_output): Likewise.
    * src/dispextern.h (struct image): Add ximg and mask_img on
    Android.
    
    * src/emacs.c (android_emacs_init): Fix argc sorting iteration.
    
    * src/fileio.c (user_homedir):
    (get_homedir): Implement correctly on Android.
    
    * src/font.h (PT_PER_INCH): Define correctly on Android.
    
    * src/fringe.c (X, swap_nibble, init_fringe_bitmap): Swap fringe
    bitmaps correctly on Android.
    
    * src/image.c (GET_PIXEL, image_create_bitmap_from_data)
    (image_create_bitmap_from_file, free_bitmap_record)
    (image_unget_x_image_or_dc, struct image_type)
    (prepare_image_for_display, image_clear_image_1)
    (image_size_in_bytes, x_check_image_size)
    (x_create_x_image_and_pixmap, x_destroy_x_image)
    (image_check_image_size, image_create_x_image_and_pixmap_1)
    (image_destroy_x_image, gui_put_x_image, image_put_x_image)
    (image_get_x_image, image_unget_x_image)
    (Create_Pixmap_From_Bitmap_Data, image_pixmap_draw_cross)
    (MaskForeground, image_types, syms_of_image): Implement all of
    the above on Android in terms of an API very similar to X.
    
    * src/keyboard.c (FUNCTION_KEY_OFFSET, lispy_function_keys):
    Define on Android to something sensible.
    
    * src/lread.c (build_load_history): Fix problem.
---
 Makefile.in                                        |  17 +-
 configure.ac                                       |  20 +-
 java/AndroidManifest.xml                           |   9 +-
 java/Makefile.in                                   |   3 +-
 java/org/gnu/emacs/EmacsActivity.java              | 153 +++--
 java/org/gnu/emacs/EmacsCopyArea.java              | 135 ++++-
 java/org/gnu/emacs/EmacsDrawRectangle.java         |   8 +-
 java/org/gnu/emacs/EmacsFontDriver.java            |  26 +-
 java/org/gnu/emacs/EmacsMultitaskActivity.java     |  25 +
 java/org/gnu/emacs/EmacsNative.java                |  18 +-
 java/org/gnu/emacs/EmacsPaintQueue.java            |  24 +-
 java/org/gnu/emacs/EmacsPixmap.java                |  29 +
 java/org/gnu/emacs/EmacsSdk23FontDriver.java       |  43 ++
 java/org/gnu/emacs/EmacsSdk7FontDriver.java        | 188 +++++-
 java/org/gnu/emacs/EmacsService.java               |  83 +--
 java/org/gnu/emacs/EmacsSurfaceView.java           |  37 +-
 java/org/gnu/emacs/EmacsView.java                  |  45 +-
 java/org/gnu/emacs/EmacsWindow.java                | 143 +++--
 .../gnu/emacs/EmacsWindowAttachmentManager.java    | 166 ++++++
 lisp/cus-edit.el                                   |   6 +-
 lisp/faces.el                                      |   2 +-
 src/android.c                                      | 627 +++++++++++++++++++--
 src/android.h                                      |  15 +
 src/androidfns.c                                   |   5 +-
 src/androidfont.c                                  | 270 ++++++---
 src/androidgui.h                                   |  81 +++
 src/androidterm.c                                  | 544 ++++++++++--------
 src/androidterm.h                                  |  30 +-
 src/dispextern.h                                   |  11 +-
 src/emacs.c                                        |   5 +-
 src/fileio.c                                       |   9 +
 src/font.h                                         |   6 +
 src/fringe.c                                       |  23 +-
 src/image.c                                        | 249 +++++---
 src/keyboard.c                                     |  53 +-
 src/lread.c                                        |  20 +-
 36 files changed, 2435 insertions(+), 693 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index d8c5ba555d..6ca1b1dc38 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -536,7 +536,7 @@ lisp: src
 lib lib-src lisp nt: Makefile
        $(MAKE) -C $@ all
 
-java: lisp
+java: lisp info
        $(MAKE) -C $@ all
 
 xcompile: src
@@ -571,20 +571,10 @@ blessmail: Makefile src
 # then attempts to build that file.  This forces 'Makefile', 'lib/Makefile',
 # etc. to be built without running into similar recursion problems.
 MAKEFILE_NAME = Makefile
-ifeq ($(ANDROID),)
 $(MAKEFILE_NAME): config.status $(srcdir)/configure \
         $(srcdir)/lib/gnulib.mk.in \
          $(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
        MAKE='$(MAKE)' ./config.status
-else
-# Note that calling config.status is insufficient on Android due to
-# the recursive calls to configure.
-
-$(MAKEFILE_NAME): $(srcdir)/configure \
-        $(srcdir)/lib/gnulib.mk.in \
-         $(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
-       $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS);
-endif
 
 # Don't erase these files if make is interrupted while refreshing them.
 .PRECIOUS: Makefile config.status
@@ -592,17 +582,12 @@ endif
 # Note that calling config.status --recheck is insufficient on Android
 # due to the recursive calls to configure.
 
-ifneq ($(ANDROID),)
-config.status: ${srcdir}/configure
-       $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS)
-else
 config.status: ${srcdir}/configure
        if [ -x ./config.status ]; then         \
            $(CFG) ./config.status --recheck;   \
        else                                    \
            $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
        fi
-endif
 
 $(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/m4/*.m4
        cd $(srcdir) && ./autogen.sh autoconf
diff --git a/configure.ac b/configure.ac
index da8d753afc..70dbfa10b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,9 +31,8 @@ if test "$XCONFIGURE" = "android"; then
   # Android!
   AC_MSG_NOTICE([called to recursively configure Emacs \
 for Android.])
-  # Set CC to ANDROID_CC, and CXX to ANDROID_CXX.
+  # Set CC to ANDROID_CC.
   CC=$ANDROID_CC
-  CXX=$ANDROID_CXX
 fi
 
 dnl Set emacs_config_options to the options of 'configure', quoted for the 
shell,
@@ -147,7 +146,7 @@ if test "$XCONFIGURE" = "android"; then
     ;;
     *) AC_MSG_ERROR([The cross compiler does not compile for Android.
 Please verify that you specified the correct compiler in the ANDROID_CC
-and ANDROID_CXX variables when you ran configure.])
+variable when you ran configure.])
     ;;
   esac
   AC_MSG_RESULT([$host_alias])
@@ -759,8 +758,7 @@ tools such as aapt, dx, and aidl):
 
 The cross-compiler should then be specified:
 
-              ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang
-              ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++])
+              ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang])
 elif test "$with_android" = "no" || test "$with_android" = ""; then
   ANDROID=no
 else
@@ -833,12 +831,11 @@ Please verify that the path to the SDK build tools you 
specified is correct]);
   dnl Now configure Emacs to generate binaries for Android.  After the
   dnl configuration completes, move the generated Makefiles.
 
-  if test "$ANDROID_CC" = "" || test "$ANDROID_CXX" = ""; then
+  if test "$ANDROID_CC" = ""; then
     AC_MSG_ERROR([Please specify the path to the Android cross-compiler
 for your machine.  For example:
 
   ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang    \\
-  ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++ \\
     ./configure --with-android])
   fi
 
@@ -863,7 +860,7 @@ for your machine.  For example:
     *) AC_MSG_ERROR([configure could not determine the type of Android \
 binary Emacs is being configured for.  Please port this configure script \
 to your Android system, or verify that you specified the correct compiler \
-in the ANDROID_CC and ANDROID_CXX variables when you ran configure.])
+in the ANDROID_CC variable when you ran configure.])
       ;;
   esac
   AC_MSG_RESULT([$android_abi])
@@ -874,9 +871,8 @@ in the ANDROID_CC and ANDROID_CXX variables when you ran 
configure.])
   mv -f confdefs.h _confdefs.h
   mv -f config.log _config.log
 
-  AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
-  ANDROID_CXX="$ANDROID_CXX" $0], [], [AC_MSG_ERROR([Failed to cross-\
-configure Emacs for android.])])
+  AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" $0], [],
+    [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])])
 
   # Now set ANDROID to yes.
   ANDROID=yes
@@ -2254,7 +2250,7 @@ for Android, but all API calls need to be stubbed out])
     ANDROID_CFLAGS="-fPIC -fvisibility=hidden"
 
     # Link with libraries required for Android support.
-    ANDROID_LIBS="-landroid -llog"
+    ANDROID_LIBS="-landroid -llog -ljnigraphics"
   fi
 fi
 
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 75aa5bdf40..06417017ae 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -31,13 +31,15 @@
            android:targetSdkVersion="28"/>
 
   <application android:name="org.gnu.emacs.EmacsApplication"
-              android:label="GNU Emacs"
+              android:label="Emacs"
               android:hardwareAccelerated="true"
               android:supportsRtl="true"
               android:theme="@android:style/Theme"
               android:debuggable="true"
               android:extractNativeLibs="true">
-    <activity android:name="org.gnu.emacs.EmacsActivity">
+    <activity android:name="org.gnu.emacs.EmacsActivity"
+             android:launchMode="singleTop"
+             
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
       <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.DEFAULT" />
@@ -45,6 +47,9 @@
       </intent-filter>
     </activity>
 
+    <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
+             
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
+
     <service android:name="org.gnu.emacs.EmacsService"
             android:directBootAware="false"
             android:enabled="true"
diff --git a/java/Makefile.in b/java/Makefile.in
index e9fcc625cb..31bd22b5a2 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -60,7 +60,7 @@ all: emacs.apk
 # Binaries to cross-compile.
 CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags   \
             ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail      \
-            ../xcompile/lib-src/ebrowse
+            ../xcompile/lib-src/ebrowse ../xcompile/lib-src/emacsclient
 
 # Libraries to cross-compile.
 CROSS_LIBS = ../xcompile/src/libemacs.so
@@ -84,6 +84,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
 # Install architecture independents to assets/etc and assets/lisp
        cp -r $(top_builddir)/lisp install_temp/assets
        cp -r $(top_builddir)/etc install_temp/assets
+       cp -r $(top_builddir)/info install_temp/assets
 # Remove undesirable files from those directories.
        for subdir in `find install_temp -type d -print`; do    \
          chmod a+rx $${subdir} ;                               \
diff --git a/java/org/gnu/emacs/EmacsActivity.java 
b/java/org/gnu/emacs/EmacsActivity.java
index cacd5f13e6..67f411a38c 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -32,65 +32,114 @@ import android.widget.FrameLayout;
 import android.widget.FrameLayout.LayoutParams;
 
 public class EmacsActivity extends Activity
+  implements EmacsWindowAttachmentManager.WindowConsumer
 {
   public static final String TAG = "EmacsActivity";
 
-  /* List of all activities that do not have an associated
-     EmacsWindow.  */
-  public static List<EmacsActivity> availableActivities;
-
   /* The currently attached EmacsWindow, or null if none.  */
   private EmacsWindow window;
 
   /* The frame layout associated with the activity.  */
   private FrameLayout layout;
 
+  /* List of activities with focus.  */
+  private static List<EmacsActivity> focusedActivities;
+
+  /* The currently focused window.  */
+  public static EmacsWindow focusedWindow;
+
   static
   {
-    /* Set up the list of available activities.  */
-    availableActivities = new ArrayList<EmacsActivity> ();
+    focusedActivities = new ArrayList<EmacsActivity> ();
   };
 
+  public static void
+  invalidateFocus1 (EmacsWindow window)
+  {
+    if (window.view.isFocused ())
+      focusedWindow = window;
+
+    for (EmacsWindow child : window.children)
+      invalidateFocus1 (window);
+  }
+
+  public static void
+  invalidateFocus ()
+  {
+    EmacsWindow oldFocus;
+
+    /* Walk through each focused activity and assign the window focus
+       to the bottom-most focused window within.  Record the old focus
+       as well.  */
+    oldFocus = focusedWindow;
+    focusedWindow = null;
+
+    for (EmacsActivity activity : focusedActivities)
+      {
+       if (activity.window != null)
+         invalidateFocus1 (activity.window);
+      }
+
+    /* Send focus in- and out- events to the previous and current
+       focus.  */
+
+    if (oldFocus != null)
+      EmacsNative.sendFocusOut (oldFocus.handle,
+                               System.currentTimeMillis ());
+
+    if (focusedWindow != null)
+      EmacsNative.sendFocusIn (focusedWindow.handle,
+                              System.currentTimeMillis ());
+  }
+
+  @Override
   public void
-  attachChild (EmacsWindow child)
+  detachWindow ()
+  {
+    if (window == null)
+      Log.w (TAG, "detachWindow called, but there is no window");
+    else
+      {
+       /* Clear the window's pointer to this activity and remove the
+          window's view.  */
+       window.setConsumer (null);
+       layout.removeView (window.view);
+       window = null;
+
+       invalidateFocus ();
+      }
+  }
+
+  @Override
+  public void
+  attachWindow (EmacsWindow child)
   {
     if (window != null)
       throw new IllegalStateException ("trying to attach window when one"
                                       + " already exists");
 
     /* Record and attach the view.  */
+
     window = child;
     layout.addView (window.view);
+    child.setConsumer (this);
 
-    /* Remove the objects from the lists of what is available.  */
-    EmacsService.availableChildren.remove (child);
-    availableActivities.remove (this);
-
-    /* Now set child->activity.  */
-    child.setActivity (this);
+    /* Invalidate the focus.  */
+    invalidateFocus ();
   }
 
-  /* Make this activity available for future windows to attach
-     again.  */
-
+  @Override
   public void
-  makeAvailable ()
+  destroy ()
   {
-    window = null;
-
-    for (EmacsWindow iterWindow
-          : EmacsService.availableChildren)
-      {
-       synchronized (iterWindow)
-         {
-           if (!iterWindow.isDestroyed ())
-             attachChild (iterWindow);
-
-           return;
-         }
-      }
+    finish ();
+  }
 
-    availableActivities.add (this);
+  @Override
+  public EmacsWindow
+  getAttachedWindow ()
+  {
+    return window;
   }
 
   @Override
@@ -109,38 +158,38 @@ public class EmacsActivity extends Activity
     /* Set it as the content view.  */
     setContentView (layout);
 
-    /* Make the activity available before starting the
-       service.  */
-    makeAvailable ();
-
     if (EmacsService.SERVICE == null)
       /* Start the Emacs service now.  */
       startService (new Intent (this, EmacsService.class));
 
+    /* Add this activity to the list of available activities.  */
+    EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
+
     super.onCreate (savedInstanceState);
   }
 
   @Override
   public void
-  onStop ()
+  onDestroy ()
   {
-    /* The activity is no longer visible.  If there is a window
-       attached, detach it.  */
-
-    if (window != null)
-      {
-       layout.removeView (window.view);
+    /* The activity will die shortly hereafter.  If there is a window
+       attached, close it now.  */
+    Log.d (TAG, "onDestroy " + this);
+    EmacsWindowAttachmentManager.MANAGER.removeWindowConsumer (this);
+    focusedActivities.remove (this);
+    invalidateFocus ();
+    super.onDestroy ();
+  }
 
-       /* Notice that the window is already available too.  But do
-          not call noticeAvailableChild; that might assign it to some
-          other activity, which behaves badly.  */
-       EmacsService.availableChildren.add (window);
-       window = null;
-      }
+  @Override
+  public void
+  onWindowFocusChanged (boolean isFocused)
+  {
+    if (isFocused && !focusedActivities.contains (this))
+      focusedActivities.add (this);
+    else
+      focusedActivities.remove (this);
 
-    /* Finally, remove this activity from the list of available
-       activities.  */
-    availableActivities.remove (this);
-    super.onStop ();
+    invalidateFocus ();
   }
 };
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java 
b/java/org/gnu/emacs/EmacsCopyArea.java
index f34d1ecde0..0dd5b2c1fb 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -32,19 +32,22 @@ public class EmacsCopyArea implements EmacsPaintReq
   private int src_x, src_y, dest_x, dest_y, width, height;
   private EmacsDrawable destination, source;
   private EmacsGC immutableGC;
-  private static Xfermode xorAlu, srcInAlu;
+  private static Xfermode xorAlu, srcInAlu, overAlu;
 
   static
   {
+    overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
     xorAlu = new PorterDuffXfermode (Mode.XOR);
     srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
   };
 
   public
-  EmacsCopyArea (EmacsDrawable destination, EmacsDrawable source,
+  EmacsCopyArea (EmacsDrawable source, EmacsDrawable destination,
                 int src_x, int src_y, int width, int height,
                 int dest_x, int dest_y, EmacsGC immutableGC)
   {
+    Bitmap bitmap;
+
     this.destination = destination;
     this.source = source;
     this.src_x = src_x;
@@ -71,6 +74,16 @@ public class EmacsCopyArea implements EmacsPaintReq
     return destination;
   }
 
+  private void
+  insetRectBy (Rect rect, int left, int top, int right,
+              int bottom)
+  {
+    rect.left += left;
+    rect.top += top;
+    rect.right -= right;
+    rect.bottom -= bottom;
+  }
+
   @Override
   public EmacsGC
   getGC ()
@@ -86,16 +99,45 @@ public class EmacsCopyArea implements EmacsPaintReq
     Bitmap bitmap;
     Paint maskPaint;
     Canvas maskCanvas;
-    Bitmap maskBitmap;
-    Rect rect, srcRect;
+    Bitmap srcBitmap, maskBitmap, clipBitmap;
+    Rect rect, maskRect, srcRect, dstRect, maskDestRect;
+    boolean needFill;
 
     /* TODO implement stippling.  */
     if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
       return;
 
     alu = immutableGC.function;
+
+    /* A copy must be created or drawBitmap could end up overwriting
+       itself.  */
+    srcBitmap = source.getBitmap ();
+
+    /* If srcBitmap is out of bounds, then adjust the source rectangle
+       to be within bounds.  Note that tiling on windows with
+       backgrounds is unimplemented.  */
+
+    if (src_x < 0)
+      {
+       width += src_x;
+       dest_x -= src_x;
+       src_x = 0;
+      }
+
+    if (src_y < 0)
+      {
+       height += src_y;
+       dest_y -= src_y;
+       src_y = 0;
+      }
+
+    if (src_x + width > srcBitmap.getWidth ())
+      width = srcBitmap.getWidth () - src_x;
+
+    if (src_y + height > srcBitmap.getHeight ())
+      height = srcBitmap.getHeight () - src_y;
+
     rect = getRect ();
-    bitmap = source.getBitmap ();
 
     if (alu == EmacsGC.GC_COPY)
       paint.setXfermode (null);
@@ -103,29 +145,76 @@ public class EmacsCopyArea implements EmacsPaintReq
       paint.setXfermode (xorAlu);
 
     if (immutableGC.clip_mask == null)
-      canvas.drawBitmap (bitmap, new Rect (src_x, src_y,
-                                          src_x + width,
-                                          src_y + height),
-                        rect, paint);
+      {
+       bitmap = Bitmap.createBitmap (srcBitmap,
+                                     src_x, src_y, width,
+                                     height);
+       canvas.drawBitmap (bitmap, null, rect, paint);
+      }
     else
       {
-       maskPaint = new Paint ();
-       srcRect = new Rect (0, 0, rect.width (),
-                           rect.height ());
-       maskBitmap
-         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
-                                              true);
-
-       if (maskBitmap == null)
+       /* Drawing with a clip mask involves calculating the
+          intersection of the clip mask with the dst rect, and
+          extrapolating the corresponding part of the src rect.  */
+       clipBitmap = immutableGC.clip_mask.bitmap;
+       dstRect = new Rect (dest_x, dest_y,
+                           dest_x + width,
+                           dest_y + height);
+       maskRect = new Rect (immutableGC.clip_x_origin,
+                            immutableGC.clip_y_origin,
+                            (immutableGC.clip_x_origin
+                             + clipBitmap.getWidth ()),
+                            (immutableGC.clip_y_origin
+                             + clipBitmap.getHeight ()));
+       clipBitmap = immutableGC.clip_mask.bitmap;
+
+       if (!maskRect.setIntersect (dstRect, maskRect))
+         /* There is no intersection between the clip mask and the
+            dest rect.  */
          return;
 
-       maskPaint.setXfermode (srcInAlu);
+       /* Now figure out which part of the source corresponds to
+          maskRect and return it relative to srcBitmap.  */
+       srcRect = new Rect (src_x, src_y, src_x + width,
+                           src_y + height);
+       insetRectBy (srcRect, maskRect.left - dstRect.left,
+                    maskRect.top - dstRect.top,
+                    maskRect.right - dstRect.right,
+                    maskRect.bottom - dstRect.bottom);
+
+       /* Finally, create a temporary bitmap that is the size of
+          maskRect.  */
+
+       maskBitmap
+         = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+                                Bitmap.Config.ARGB_8888);
+
+       /* Draw the mask onto the maskBitmap.  */
        maskCanvas = new Canvas (maskBitmap);
-       maskCanvas.drawBitmap (bitmap, new Rect (src_x, src_y,
-                                                src_x + width,
-                                                src_y + height),
-                              srcRect, maskPaint);
-       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+       maskRect.offset (-immutableGC.clip_x_origin,
+                        -immutableGC.clip_y_origin);
+       maskCanvas.drawBitmap (immutableGC.clip_mask.bitmap,
+                              maskRect, new Rect (0, 0,
+                                                  maskRect.width (),
+                                                  maskRect.height ()),
+                              paint);
+       maskRect.offset (immutableGC.clip_x_origin,
+                        immutableGC.clip_y_origin);
+
+       /* Set the transfer mode to SRC_IN to preserve only the parts
+          of the source that overlap with the mask.  */
+       maskPaint = new Paint ();
+       maskPaint.setXfermode (srcInAlu);
+
+       /* Draw the source.  */
+       maskDestRect = new Rect (0, 0, srcRect.width (),
+                                srcRect.height ());
+       maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect,
+                              maskPaint);
+
+       /* Finally, draw the mask bitmap to the destination.  */
+       paint.setXfermode (overAlu);
+       canvas.drawBitmap (maskBitmap, null, maskRect, paint);
       }
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java 
b/java/org/gnu/emacs/EmacsDrawRectangle.java
index 462bf7c85b..e3f2822714 100644
--- a/java/org/gnu/emacs/EmacsDrawRectangle.java
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -57,7 +57,10 @@ public class EmacsDrawRectangle implements EmacsPaintReq
   public Rect
   getRect ()
   {
-    return new Rect (x, y, x + width, y + height);
+    /* Canvas.drawRect actually behaves exactly like PolyRectangle wrt
+       to where the lines are placed, so extend the width and height
+       by 1 in the damage rectangle.  */
+    return new Rect (x, y, x + width + 1, y + height + 1);
   }
 
   @Override
@@ -89,9 +92,10 @@ public class EmacsDrawRectangle implements EmacsPaintReq
       return;
 
     alu = immutableGC.function;
-    rect = getRect ();
+    rect = new Rect (x, y, x + width, y + height);
 
     paint.setStyle (Paint.Style.STROKE);
+    paint.setStrokeWidth (1);
 
     if (alu == EmacsGC.GC_COPY)
       paint.setXfermode (null);
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java 
b/java/org/gnu/emacs/EmacsFontDriver.java
index f419e71059..9f40aa04c4 100644
--- a/java/org/gnu/emacs/EmacsFontDriver.java
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -21,6 +21,8 @@ package org.gnu.emacs;
 
 import java.util.List;
 
+import android.os.Build;
+
 public abstract class EmacsFontDriver
 {
   /* Font weights.  */
@@ -75,6 +77,7 @@ public abstract class EmacsFontDriver
     public Integer size;
     public Integer spacing;
     public Integer avgwidth;
+    public Integer dpi;
 
     @Override
     public String
@@ -88,7 +91,8 @@ public abstract class EmacsFontDriver
              + " weight: " + weight
              + " slant: " + slant
              + " spacing: " + spacing
-             + " avgwidth: " + avgwidth);
+             + " avgwidth: " + avgwidth
+             + " dpi: " + dpi);
     }
   };
 
@@ -99,6 +103,17 @@ public abstract class EmacsFontDriver
     public short width;
     public short ascent;
     public short descent;
+
+    @Override
+    public String
+    toString ()
+    {
+      return ("lbearing " + lbearing
+             + " rbearing " + rbearing
+             + " width " + width
+             + " ascent " + ascent
+             + " descent " + descent);
+    }
   }
 
   public class FontEntity extends FontSpec
@@ -139,12 +154,19 @@ public abstract class EmacsFontDriver
   public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
   public abstract int hasChar (FontSpec font, char charCode);
   public abstract void textExtents (FontObject font, int code[],
-                                   FontMetrics fontMetrics[]);
+                                   FontMetrics fontMetrics);
   public abstract int encodeChar (FontObject fontObject, char charCode);
+  public abstract int draw (FontObject fontObject, EmacsGC gc,
+                           EmacsDrawable drawable, int[] chars,
+                           int x, int y, int backgroundWidth,
+                           boolean withBackground);
 
   public static EmacsFontDriver
   createFontDriver ()
   {
+    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
+      return new EmacsSdk23FontDriver ();
+
     return new EmacsSdk7FontDriver ();
   }
 };
diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java 
b/java/org/gnu/emacs/EmacsMultitaskActivity.java
new file mode 100644
index 0000000000..dbdc99a355
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java
@@ -0,0 +1,25 @@
+/* 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;
+
+public class EmacsMultitaskActivity extends EmacsActivity
+{
+
+}
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index 6550e6fa2a..c80339031a 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -38,10 +38,15 @@ public class EmacsNative
      libDir must be the package's data storage location for native
      libraries.  It is used as PATH.
 
+     pixelDensityX and pixelDensityY are the DPI values that will be
+     used by Emacs.
+
      emacsService must be the emacsService singleton.  */
   public static native void setEmacsParams (AssetManager assetManager,
                                            String filesDir,
                                            String libDir,
+                                           float pixelDensityX,
+                                           float pixelDensityY,
                                            EmacsService emacsService);
 
   /* Initialize Emacs with the argument array ARGV.  Each argument
@@ -59,11 +64,20 @@ public class EmacsNative
 
   /* Send an ANDROID_KEY_PRESS event.  */
   public static native void sendKeyPress (short window, long time, int state,
-                                         int keyCode);
+                                         int keyCode, int unicodeChar);
 
   /* Send an ANDROID_KEY_RELEASE event.  */
   public static native void sendKeyRelease (short window, long time, int state,
-                                           int keyRelease);
+                                           int keyCode, int unicodeChar);
+
+  /* Send an ANDROID_FOCUS_IN event.  */
+  public static native void sendFocusIn (short window, long time);
+
+  /* Send an ANDROID_FOCUS_OUT event.  */
+  public static native void sendFocusOut (short window, long time);
+
+  /* Send an ANDROID_WINDOW_ACTION event.  */
+  public static native void sendWindowAction (short window, int action);
 
   static
   {
diff --git a/java/org/gnu/emacs/EmacsPaintQueue.java 
b/java/org/gnu/emacs/EmacsPaintQueue.java
index 5af5868d3b..f4840dbf5a 100644
--- a/java/org/gnu/emacs/EmacsPaintQueue.java
+++ b/java/org/gnu/emacs/EmacsPaintQueue.java
@@ -47,7 +47,7 @@ public class EmacsPaintQueue
   {
     EmacsDrawable drawable, last;
     Canvas canvas;
-    EmacsGC gc, lastGC;
+    EmacsGC gc;
     int i;
     Paint paint;
     Rect rect, offsetRect, copyRect;
@@ -60,45 +60,34 @@ public class EmacsPaintQueue
     for (EmacsPaintReq req : paintOperations)
       {
        drawable = req.getDrawable ();
-
-       synchronized (req)
-         {
-           /* Ignore graphics requests for drawables that have been
-              destroyed.  */
-           if (drawable.isDestroyed ())
-             continue;
-         }
-
        canvas = drawable.lockCanvas ();
 
        if (canvas == null)
          /* No canvas is currently available.  */
          continue;
 
-       lastGC = gc;
        gc = req.getGC ();
        rect = req.getRect ();
 
+       drawable.damageRect (rect);
+
        if (gc.clip_rects == null)
          {
            /* No clipping is applied.  Just draw and continue.  */
-           canvas.save ();
            req.paintTo (canvas, paint, gc);
-           canvas.restore ();
-           drawable.damageRect (rect);
            continue;
          }
 
        if (gc.clip_rects != null && gc.clip_rects.length > 0)
          {
-           canvas.save ();
-
            if (gc.clip_rects.length == 1)
              {
                /* There is only a single clip rect, which is simple
                   enough.  */
+               canvas.save ();
                canvas.clipRect (gc.clip_rects[0]);
                req.paintTo (canvas, paint, gc);
+               canvas.restore ();
              }
            else
              {
@@ -122,9 +111,6 @@ public class EmacsPaintQueue
                      }
                  }
              }
-
-           drawable.damageRect (rect);
-           canvas.restore ();
          }
       }
   }
diff --git a/java/org/gnu/emacs/EmacsPixmap.java 
b/java/org/gnu/emacs/EmacsPixmap.java
index ccd5f1e004..897902c5b5 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -69,6 +69,35 @@ public class EmacsPixmap extends EmacsHandleObject
     this.depth = depth;
   }
 
+  public
+  EmacsPixmap (short handle, int width, int height, int depth)
+  {
+    super (handle);
+
+    if (depth != 1 && depth != 24)
+      throw new IllegalArgumentException ("Invalid depth specified"
+                                         + " for pixmap: " + depth);
+
+    switch (depth)
+      {
+      case 1:
+       bitmap = Bitmap.createBitmap (width, height,
+                                     Bitmap.Config.ALPHA_8,
+                                     false);
+       break;
+
+      case 24:
+       bitmap = Bitmap.createBitmap (width, height,
+                                     Bitmap.Config.ARGB_8888,
+                                     false);
+       break;
+      }
+
+    this.width = width;
+    this.height = height;
+    this.depth = depth;
+  }
+
   @Override
   public Canvas
   lockCanvas ()
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
new file mode 100644
index 0000000000..34e2b1803a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -0,0 +1,43 @@
+/* Font backend 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.graphics.Paint;
+
+public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
+{
+  @Override
+  public int
+  hasChar (FontSpec font, char charCode)
+  {
+    Sdk7FontObject fontObject;
+    Paint paint;
+
+    if (font instanceof Sdk7FontObject)
+      {
+       fontObject = (Sdk7FontObject) font;
+       paint = fontObject.typeface.typefacePaint;
+      }
+    else
+      paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
+
+    return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 5a8cdbfc75..437f38e62d 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -28,16 +28,19 @@ import java.util.List;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.Canvas;
 
 import android.util.Log;
 
+import android.os.Build;
+
 public class EmacsSdk7FontDriver extends EmacsFontDriver
 {
   private static final String TOFU_STRING = "\uDB3F\uDFFD";
   private static final String EM_STRING   = "m";
   private static final String TAG        = "EmacsSdk7FontDriver";
 
-  private class Sdk7Typeface
+  protected class Sdk7Typeface
   {
     /* The typeface and paint.  */
     public Typeface typeface;
@@ -57,7 +60,10 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
       width = UNSPECIFIED;
       spacing = PROPORTIONAL;
 
+      this.typeface = typeface;
+
       typefacePaint = new Paint ();
+      typefacePaint.setAntiAlias (true);
       typefacePaint.setTypeface (typeface);
 
       /* For the calls to measureText below.  */
@@ -160,7 +166,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     }
   };
 
-  private class Sdk7FontEntity extends FontEntity
+  protected class Sdk7FontEntity extends FontEntity
   {
     /* The typeface.  */
     public Sdk7Typeface typeface;
@@ -177,19 +183,17 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
       slant = typeface.slant;
       spacing = typeface.spacing;
       width = typeface.width;
+      dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
 
       this.typeface = typeface;
     }
   };
 
-  private class Sdk7FontObject extends FontObject
+  protected class Sdk7FontObject extends FontObject
   {
     /* The typeface.  */
     public Sdk7Typeface typeface;
 
-    /* The text size.  */
-    public int pixelSize;
-
     public
     Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
     {
@@ -205,6 +209,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
       slant = typeface.slant;
       spacing = typeface.spacing;
       width = typeface.width;
+      dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
 
       /* Compute the ascent and descent.  */
       typeface.typefacePaint.setTextSize (pixelSize);
@@ -238,6 +243,93 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     }
   };
 
+  private class Sdk7DrawString implements EmacsPaintReq
+  {
+    private boolean drawBackground;
+    private Sdk7FontObject fontObject;
+    private char[] chars;
+    private EmacsGC immutableGC;
+    private EmacsDrawable drawable;
+    private Rect rect;
+    private int originX, originY;
+
+    public
+    Sdk7DrawString (Sdk7FontObject fontObject, char[] chars,
+                   EmacsGC immutableGC, EmacsDrawable drawable,
+                   boolean drawBackground, Rect rect,
+                   int originX, int originY)
+    {
+      this.fontObject = fontObject;
+      this.chars = chars;
+      this.immutableGC = immutableGC;
+      this.drawable = drawable;
+      this.drawBackground = drawBackground;
+      this.rect = rect;
+      this.originX = originX;
+      this.originY = originY;
+    }
+
+    @Override
+    public EmacsDrawable
+    getDrawable ()
+    {
+      return drawable;
+    }
+
+    @Override
+    public EmacsGC
+    getGC ()
+    {
+      return immutableGC;
+    }
+
+    @Override
+    public void
+    paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+    {
+      int scratch;
+
+      paint.setStyle (Paint.Style.FILL);
+
+      if (drawBackground)
+       {
+         paint.setColor (immutableGC.background | 0xff000000);
+         canvas.drawRect (rect, paint);
+       }
+
+      paint.setTextSize (fontObject.pixelSize);
+      paint.setColor (immutableGC.foreground | 0xff000000);
+      paint.setTypeface (fontObject.typeface.typeface);
+      paint.setAntiAlias (true);
+
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+       /* Disable hinting as that leads to displayed text not
+          matching the computed metrics.  */
+       paint.setHinting (Paint.HINTING_OFF);
+
+      canvas.drawText (chars, 0, chars.length, originX, originY, paint);
+      paint.setAntiAlias (false);
+    }
+
+    @Override
+    public Rect
+    getRect ()
+    {
+      Rect rect;
+
+      rect = new Rect ();
+
+      fontObject.typeface.typefacePaint.setTextSize (fontObject.pixelSize);
+      fontObject.typeface.typefacePaint.getTextBounds (chars, 0, chars.length,
+                                                      rect);
+
+      /* Add the background rect to the damage as well.  */
+      rect.union (this.rect);
+
+      return rect;
+    }
+  };
+
   private String[] fontFamilyList;
   private Sdk7Typeface[] typefaceList;
   private Sdk7Typeface fallbackTypeface;
@@ -252,7 +344,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     systemFontsDirectory = new File ("/system/fonts");
 
     fontFamilyList = systemFontsDirectory.list ();
-    typefaceList = new Sdk7Typeface[fontFamilyList.length];
+    typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
 
     /* It would be nice to avoid opening each and every font upon
        startup.  But that doesn't seem to be possible on
@@ -267,8 +359,18 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
                                            typeface);
       }
 
+    /* Initialize the default monospace and serif typefaces.  */
     fallbackTypeface = new Sdk7Typeface ("monospace",
                                         Typeface.MONOSPACE);
+    typefaceList[fontFamilyList.length] = fallbackTypeface;
+
+    fallbackTypeface = new Sdk7Typeface ("Monospace",
+                                        Typeface.MONOSPACE);
+    typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
+
+    fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+                                        Typeface.DEFAULT);
+    typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
   }
 
   private boolean
@@ -278,11 +380,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
        && !fontSpec.family.equals (typeface.familyName))
       return false;
 
-
-    if (fontSpec.adstyle != null
-       && !fontSpec.adstyle.isEmpty ())
-      /* return false; */;
-
     if (fontSpec.slant != null
        && !fontSpec.weight.equals (typeface.weight))
       return false;
@@ -393,7 +490,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
                         rect1);
     paint.getTextBounds ("" + charCode, 0, 1, rect2);
-    return rect1.equals (rect2) ? 1 : 0;
+    return rect1.equals (rect2) ? 0 : 1;
   }
 
   private void
@@ -434,21 +531,47 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
 
   @Override
   public void
-  textExtents (FontObject font, int code[], FontMetrics fontMetrics[])
+  textExtents (FontObject font, int code[], FontMetrics fontMetrics)
   {
     int i;
     Paint paintCache;
     Rect boundsCache;
     Sdk7FontObject fontObject;
+    char[] text;
+    float width;
 
     fontObject = (Sdk7FontObject) font;
     paintCache = fontObject.typeface.typefacePaint;
     paintCache.setTextSize (fontObject.pixelSize);
     boundsCache = new Rect ();
 
-    for (i = 0; i < code.length; ++i)
-      textExtents1 ((Sdk7FontObject) font, code[i], fontMetrics[i],
+    if (code.length == 0)
+      {
+       fontMetrics.lbearing = 0;
+       fontMetrics.rbearing = 0;
+       fontMetrics.ascent = 0;
+       fontMetrics.descent = 0;
+       fontMetrics.width = 0;
+      }
+    else if (code.length == 1)
+      textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
                    paintCache, boundsCache);
+    else
+      {
+       text = new char[code.length];
+
+       for (i = 0; i < code.length; ++i)
+         text[i] = (char) code[i];
+
+       paintCache.getTextBounds (text, 0, 1, boundsCache);
+       width = paintCache.measureText (text, 0, code.length);
+
+       fontMetrics.lbearing = (short) boundsCache.left;
+       fontMetrics.rbearing = (short) boundsCache.right;
+       fontMetrics.ascent = (short) -boundsCache.top;
+       fontMetrics.descent = (short) boundsCache.bottom;
+       fontMetrics.width = (short) Math.round (width);
+      }
   }
 
   @Override
@@ -457,4 +580,37 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
   {
     return charCode;
   }
+
+  @Override
+  public int
+  draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
+       int[] chars, int x, int y, int backgroundWidth,
+       boolean withBackground)
+  {
+    Rect backgroundRect;
+    Sdk7FontObject sdk7FontObject;
+    Sdk7DrawString op;
+    char[] charsArray;
+    int i;
+
+    sdk7FontObject = (Sdk7FontObject) fontObject;
+    charsArray = new char[chars.length];
+
+    for (i = 0; i < chars.length; ++i)
+      charsArray[i] = (char) chars[i];
+
+    backgroundRect = new Rect ();
+    backgroundRect.top = y - sdk7FontObject.ascent;
+    backgroundRect.left = x;
+    backgroundRect.right = x + backgroundWidth;
+    backgroundRect.bottom = y + sdk7FontObject.descent;
+
+    op = new Sdk7DrawString (sdk7FontObject, charsArray,
+                            gc.immutableGC (), drawable,
+                            withBackground,
+                            backgroundRect, x, y);
+
+    EmacsService.SERVICE.appendPaintOperation (op);
+    return 1;
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 311226e6f7..41a45b0bd8 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -28,6 +28,8 @@ import android.graphics.Canvas;
 import android.graphics.Bitmap;
 import android.graphics.Point;
 
+import android.view.View;
+
 import android.annotation.TargetApi;
 import android.app.Service;
 import android.content.Context;
@@ -37,7 +39,9 @@ import android.os.Build;
 import android.os.Looper;
 import android.os.IBinder;
 import android.os.Handler;
+
 import android.util.Log;
+import android.util.DisplayMetrics;
 
 class Holder<T>
 {
@@ -57,14 +61,8 @@ public class EmacsService extends Service
   private Handler handler;
   private EmacsPaintQueue paintQueue;
 
-  /* List of all EmacsWindows that are available to attach to an
-     activity.  */
-  public static List<EmacsWindow> availableChildren;
-
-  static
-  {
-    availableChildren = new ArrayList<EmacsWindow> ();
-  };
+  /* Display metrics used by font backends.  */
+  public DisplayMetrics metrics;
 
   @Override
   public int
@@ -88,7 +86,7 @@ public class EmacsService extends Service
     Context context;
 
     context = getApplicationContext ();
-    apiLevel = android.os.Build.VERSION.SDK_INT;
+    apiLevel = Build.VERSION.SDK_INT;
 
     if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
       return context.getApplicationInfo().nativeLibraryDir;
@@ -105,11 +103,16 @@ public class EmacsService extends Service
     AssetManager manager;
     Context app_context;
     String filesDir, libDir;
+    double pixelDensityX;
+    double pixelDensityY;
 
     SERVICE = this;
     handler = new Handler (Looper.getMainLooper ());
     manager = getAssets ();
     app_context = getApplicationContext ();
+    metrics = getResources ().getDisplayMetrics ();
+    pixelDensityX = metrics.xdpi;
+    pixelDensityY = metrics.ydpi;
 
     try
       {
@@ -122,6 +125,8 @@ public class EmacsService extends Service
               + " and libDir = " + libDir);
 
        EmacsNative.setEmacsParams (manager, filesDir, libDir,
+                                   (float) pixelDensityX,
+                                   (float) pixelDensityY,
                                    this);
 
        /* Start the thread that runs Emacs.  */
@@ -147,7 +152,8 @@ public class EmacsService extends Service
   }
 
   EmacsView
-  getEmacsView (final EmacsWindow window)
+  getEmacsView (final EmacsWindow window, final int visibility,
+               final boolean isFocusedByDefault)
   {
     Runnable runnable;
     final Holder<EmacsView> view;
@@ -161,6 +167,8 @@ public class EmacsService extends Service
          synchronized (this)
            {
              view.thing = new EmacsView (window);
+             view.thing.setVisibility (visibility);
+             view.thing.setFocusedByDefault (isFocusedByDefault);
              notify ();
            }
        }
@@ -183,48 +191,6 @@ public class EmacsService extends Service
     return view.thing;
   }
 
-  /* Notice that a child of the root window named WINDOW is now
-     available for attachment to a specific activity.  */
-
-  public void
-  noticeAvailableChild (final EmacsWindow window)
-  {
-    Log.d (TAG, "A new child is available: " + window);
-
-    handler.post (new Runnable () {
-       public void
-       run ()
-       {
-         for (EmacsActivity activity
-                : EmacsActivity.availableActivities)
-           {
-             /* TODO: check if the activity matches.  */
-             activity.attachChild (window);
-             break;
-           }
-
-         /* Nope, wait for an activity to become available.  */
-         availableChildren.add (window);
-       }
-      });
-  }
-
-  /* Notice that a child of the root window named WINDOW has been
-     destroyed.  */
-
-  public void
-  noticeChildDestroyed (final EmacsWindow child)
-  {
-    handler.post (new Runnable () {
-       @Override
-       public void
-       run ()
-       {
-         availableChildren.remove (child);
-       }
-      });
-  }
-
   /* X drawing operations.  These are quite primitive operations.  The
      drawing queue is kept on the Emacs thread, but is periodically
      flushed to the application thread, upon buffers swaps and once it
@@ -311,11 +277,6 @@ public class EmacsService extends Service
 
     ensurePaintQueue ();
 
-    if (gc.clip_rects != null && gc.clip_rects.length >= 1)
-      android.util.Log.d ("drawRectangle",
-                         gc.clip_rects[0].toString ()
-                         + " " + gc.toString ());
-
     req = new EmacsDrawRectangle (drawable, x, y,
                                  width, height,
                                  gc.immutableGC ());
@@ -381,4 +342,12 @@ public class EmacsService extends Service
   {
     window.clearArea (x, y, width, height);
   }
+
+  public void
+  appendPaintOperation (EmacsPaintReq op)
+  {
+    ensurePaintQueue ();
+    paintQueue.appendPaintOperation (op);
+    checkFlush ();
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
index 194f6ad37a..b8b828e482 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -22,6 +22,8 @@ package org.gnu.emacs;
 import android.view.SurfaceView;
 import android.view.SurfaceHolder;
 
+import android.os.Build;
+
 import android.graphics.Canvas;
 import android.graphics.Rect;
 
@@ -40,7 +42,9 @@ public class EmacsSurfaceView extends SurfaceView
        surfaceChanged (SurfaceHolder holder, int format,
                        int width, int height)
        {
-
+         /* Force a buffer swap now to get the contents of the Emacs
+            view on screen.  */
+         view.swapBuffers (true);
        }
 
        @Override
@@ -51,7 +55,7 @@ public class EmacsSurfaceView extends SurfaceView
 
          /* Force a buffer swap now to get the contents of the Emacs
             view on screen.  */
-         view.swapBuffers ();
+         view.swapBuffers (true);
        }
 
        @Override
@@ -72,12 +76,37 @@ public class EmacsSurfaceView extends SurfaceView
   public Canvas
   lockCanvas (Rect damage)
   {
-    return getHolder ().lockCanvas (damage);
+    SurfaceHolder holder;
+
+    holder = getHolder ();
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      {
+       damage.setEmpty ();
+       return holder.lockHardwareCanvas ();
+      }
+
+    return holder.lockCanvas (damage);
+  }
+
+  /* This method is only used during debugging when it seems damage
+     isn't working correctly.  */
+
+  public Canvas
+  lockCanvas ()
+  {
+    SurfaceHolder holder;
+
+    holder = getHolder ();
+    return holder.lockCanvas ();
   }
 
   public void
   unlockCanvasAndPost (Canvas canvas)
   {
-    getHolder ().unlockCanvasAndPost (canvas);
+    SurfaceHolder holder;
+
+    holder = getHolder ();
+    holder.unlockCanvasAndPost (canvas);
   }
 };
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 237946d636..7b48eaf0aa 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -19,17 +19,20 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 package org.gnu.emacs;
 
+import android.content.res.ColorStateList;
+
 import android.view.View;
 import android.view.KeyEvent;
 import android.view.ViewGroup;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Paint;
-import android.util.Log;
 
 import android.os.Build;
+import android.util.Log;
 
 /* This is an Android view which has a back and front buffer.  When
    swapBuffers is called, the back buffer is swapped to the front
@@ -70,10 +73,11 @@ public class EmacsView extends ViewGroup
     this.damageRegion = new Region ();
     this.paint = new Paint ();
 
+    setFocusable (true);
+    setFocusableInTouchMode (true);
+
     /* Create the surface view.  */
     this.surfaceView = new EmacsSurfaceView (this);
-
-    setFocusable (FOCUSABLE);
     addView (this.surfaceView);
   }
 
@@ -162,7 +166,7 @@ public class EmacsView extends ViewGroup
   }
 
   public void
-  swapBuffers ()
+  swapBuffers (boolean force)
   {
     Bitmap back;
     Canvas canvas;
@@ -185,14 +189,25 @@ public class EmacsView extends ViewGroup
     if (canvas == null)
       return;
 
-    /* Copy from the back buffer to the canvas.  */
-    canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+    /* Copy from the back buffer to the canvas.  If damageRect was
+       made empty, then draw the entire back buffer.  */
+
+    if (damageRect.isEmpty ())
+      canvas.drawBitmap (bitmap, 0f, 0f, paint);
+    else
+      canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
 
     /* Unlock the canvas and clear the damage.  */
     surfaceView.unlockCanvasAndPost (canvas);
     damageRegion.setEmpty ();
   }
 
+  public void
+  swapBuffers ()
+  {
+    swapBuffers (false);
+  }
+
   @Override
   public boolean
   onKeyDown (int keyCode, KeyEvent event)
@@ -201,6 +216,14 @@ public class EmacsView extends ViewGroup
     return true;
   }
 
+  @Override
+  public boolean
+  onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
+  {
+    window.onKeyDown (keyCode, event);
+    return true;
+  }
+
   @Override
   public boolean
   onKeyUp (int keyCode, KeyEvent event)
@@ -208,4 +231,14 @@ public class EmacsView extends ViewGroup
     window.onKeyUp (keyCode, event);
     return true;
   }
+
+  @Override
+  public void
+  onFocusChanged (boolean gainFocus, int direction,
+                 Rect previouslyFocusedRect)
+  {
+    window.onFocusChanged (gainFocus);
+    super.onFocusChanged (gainFocus, direction,
+                         previouslyFocusedRect);
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index 28db04a261..26e788a20a 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -32,6 +32,8 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.view.KeyEvent;
 
+import android.content.Intent;
+
 /* This defines a window, which is a handle.  Windows represent a
    rectangular subset of the screen with their own contents.
 
@@ -57,10 +59,10 @@ public class EmacsWindow extends EmacsHandleObject
 
   /* List of all children in stacking order.  This must be kept
      consistent!  */
-  private ArrayList<EmacsWindow> children;
+  public ArrayList<EmacsWindow> children;
 
-  /* The EmacsActivity currently attached, if it exists.  */
-  private EmacsActivity attached;
+  /* The window consumer currently attached, if it exists.  */
+  private EmacsWindowAttachmentManager.WindowConsumer attached;
 
   /* The window background scratch GC.  foreground is always the
      window background.  */
@@ -74,35 +76,44 @@ public class EmacsWindow extends EmacsHandleObject
 
     rect = new Rect (x, y, x + width, y + height);
 
-    /* Create the view from the context's UI thread.  */
-    view = EmacsService.SERVICE.getEmacsView (this);
+    /* Create the view from the context's UI thread.  The window is
+       unmapped, so the view is GONE.  */
+    view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
+                                             parent == null);
     this.parent = parent;
-    children = new ArrayList<EmacsWindow> ();
 
-    /* The window is unmapped by default.  */
-    view.setVisibility (View.GONE);
+    /* Create the list of children.  */
+    children = new ArrayList<EmacsWindow> ();
 
-    /* If parent is the root window, notice that there are new
-       children available for interested activites to pick up.  */
-    if (parent == null)
-      EmacsService.SERVICE.noticeAvailableChild (this);
-    else
+    if (parent != null)
       {
-       /* Otherwise, directly add this window as a child of that
-          window's view.  */
-       synchronized (parent)
+       parent.children.add (this);
+       parent.view.post (new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             parent.view.addView (view);
+           }
+         });
+      }
+    else
+      EmacsService.SERVICE.runOnUiThread (new Runnable () {
+         @Override
+         public void
+         run ()
          {
-           parent.children.add (this);
-           parent.view.post (new Runnable () {
-               @Override
-               public void
-               run ()
-               {
-                 parent.view.addView (view);
-               }
-             });
+           EmacsWindowAttachmentManager manager;
+
+           manager = EmacsWindowAttachmentManager.MANAGER;
+
+           /* If parent is the root window, notice that there are new
+              children available for interested activites to pick
+              up.  */
+
+           manager.registerWindow (EmacsWindow.this);
          }
-      }
+       });
 
     scratchGC = new EmacsGC ((short) 0);
   }
@@ -129,28 +140,35 @@ public class EmacsWindow extends EmacsHandleObject
   public void
   destroyHandle () throws IllegalStateException
   {
-    synchronized (this)
-      {
-       if (!children.isEmpty ())
-         throw new IllegalStateException ("Trying to destroy window with "
-                                          + "children!");
-      }
+    if (parent != null)
+      parent.children.remove (this);
+
+    EmacsActivity.invalidateFocus ();
 
-    /* Notice that the child has been destroyed.  */
-    EmacsService.SERVICE.noticeChildDestroyed (this);
+    if (!children.isEmpty ())
+      throw new IllegalStateException ("Trying to destroy window with "
+                                      + "children!");
 
     /* Remove the view from its parent and make it invisible.  */
     view.post (new Runnable () {
        public void
        run ()
        {
+         View parent;
+         EmacsWindowAttachmentManager manager;
+
+         if (EmacsActivity.focusedWindow == EmacsWindow.this)
+           EmacsActivity.focusedWindow = null;
+
+         manager = EmacsWindowAttachmentManager.MANAGER;
          view.setVisibility (View.GONE);
 
-         if (view.getParent () != null)
-           ((ViewGroup) view.getParent ()).removeView (view);
+         parent = (View) view.getParent ();
 
-         if (attached != null)
-           attached.makeAvailable ();
+         if (parent != null && attached == null)
+           ((ViewGroup) parent).removeView (view);
+
+         manager.detachWindow (EmacsWindow.this);
        }
       });
 
@@ -158,12 +176,15 @@ public class EmacsWindow extends EmacsHandleObject
   }
 
   public void
-  setActivity (EmacsActivity activity)
+  setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
   {
-    synchronized (this)
-      {
-       activity = activity;
-      }
+    attached = consumer;
+  }
+
+  public EmacsWindowAttachmentManager.WindowConsumer
+  getAttachedConsumer ()
+  {
+    return attached;
   }
 
   public void
@@ -233,7 +254,10 @@ public class EmacsWindow extends EmacsHandleObject
        public void
        run ()
        {
+
          view.setVisibility (View.VISIBLE);
+         /* Eventually this should check no-focus-on-map.  */
+         view.requestFocus ();
        }
       });
   }
@@ -319,18 +343,47 @@ public class EmacsWindow extends EmacsHandleObject
   public void
   onKeyDown (int keyCode, KeyEvent event)
   {
+    int state;
+
+    state = event.getModifiers ();
+    state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
+
     EmacsNative.sendKeyPress (this.handle,
                              event.getEventTime (),
                              event.getModifiers (),
-                             keyCode);
+                             keyCode,
+                             /* Ignore meta-state understood by Emacs
+                                for now, or Ctrl+C will not be
+                                recognized as an ASCII key press
+                                event.  */
+                             event.getUnicodeChar (state));
   }
 
   public void
   onKeyUp (int keyCode, KeyEvent event)
   {
+    int state;
+
+    state = event.getModifiers ();
+    state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
+
     EmacsNative.sendKeyRelease (this.handle,
                                event.getEventTime (),
                                event.getModifiers (),
-                               keyCode);
+                               keyCode,
+                               event.getUnicodeChar (state));
+  }
+
+  public void
+  onFocusChanged (boolean gainFocus)
+  {
+    EmacsActivity.invalidateFocus ();
+  }
+
+  public void
+  onActivityDetached ()
+  {
+    /* Destroy the associated frame when the activity is detached.  */
+    EmacsNative.sendWindowAction (this.handle, 0);
   }
 };
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java 
b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
new file mode 100644
index 0000000000..34be2ab878
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -0,0 +1,166 @@
+/* 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 java.util.LinkedList;
+import java.util.List;
+
+import android.content.Intent;
+import android.util.Log;
+
+/* Code to paper over the differences in lifecycles between
+   "activities" and windows.  There are four interfaces to an instance
+   of this class:
+
+     registerWindowConsumer (WindowConsumer)
+     registerWindow (EmacsWindow)
+     removeWindowConsumer (WindowConsumer)
+     removeWindow (EmacsWindow)
+
+   A WindowConsumer is expected to allow an EmacsWindow to be attached
+   to it, and be created or destroyed.
+
+   Every time a window is created, registerWindow checks the list of
+   window consumers.  If a consumer exists and does not currently have
+   a window of its own attached, it gets the new window.  Otherwise,
+   the window attachment manager starts a new consumer.
+
+   Every time a consumer is registered, registerWindowConsumer checks
+   the list of available windows.  If a window exists and is not
+   currently attached to a consumer, then the consumer gets it.
+
+   Finally, every time a window is removed, the consumer is
+   destroyed.  */
+
+public class EmacsWindowAttachmentManager
+{
+  public static EmacsWindowAttachmentManager MANAGER;
+  private final static String TAG = "EmacsWindowAttachmentManager";
+
+  static
+  {
+    MANAGER = new EmacsWindowAttachmentManager ();
+  };
+
+  public interface WindowConsumer
+  {
+    public void attachWindow (EmacsWindow window);
+    public EmacsWindow getAttachedWindow ();
+    public void detachWindow ();
+    public void destroy ();
+  };
+
+  private List<WindowConsumer> consumers;
+  private List<EmacsWindow> windows;
+
+  public
+  EmacsWindowAttachmentManager ()
+  {
+    consumers = new LinkedList<WindowConsumer> ();
+    windows = new LinkedList<EmacsWindow> ();
+  }
+
+  public void
+  registerWindowConsumer (WindowConsumer consumer)
+  {
+    Log.d (TAG, "registerWindowConsumer " + consumer);
+
+    consumers.add (consumer);
+
+    for (EmacsWindow window : windows)
+      {
+       if (window.getAttachedConsumer () == null)
+         {
+           Log.d (TAG, "registerWindowConsumer: attaching " + window);
+           consumer.attachWindow (window);
+           return;
+         }
+      }
+
+    Log.d (TAG, "registerWindowConsumer: sendWindowAction 0, 0");
+    EmacsNative.sendWindowAction ((short) 0, 0);
+  }
+
+  public void
+  registerWindow (EmacsWindow window)
+  {
+    Intent intent;
+
+    Log.d (TAG, "registerWindow " + window);
+    windows.add (window);
+
+    for (WindowConsumer consumer : consumers)
+      {
+       if (consumer.getAttachedWindow () == null)
+         {
+           Log.d (TAG, "registerWindow: attaching " + consumer);
+           consumer.attachWindow (window);
+           return;
+         }
+      }
+
+    intent = new Intent (EmacsService.SERVICE,
+                        EmacsMultitaskActivity.class);
+    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+                    | Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+    EmacsService.SERVICE.startActivity (intent);
+    Log.d (TAG, "registerWindow: startActivity");
+  }
+
+  public void
+  removeWindowConsumer (WindowConsumer consumer)
+  {
+    EmacsWindow window;
+
+    Log.d (TAG, "removeWindowConsumer " + consumer);
+
+    window = consumer.getAttachedWindow ();
+
+    if (window != null)
+      {
+       Log.d (TAG, "removeWindowConsumer: detaching " + window);
+
+       consumer.detachWindow ();
+       window.onActivityDetached ();
+      }
+
+    Log.d (TAG, "removeWindowConsumer: removing " + consumer);
+    consumers.remove (consumer);
+  }
+
+  public void
+  detachWindow (EmacsWindow window)
+  {
+    WindowConsumer consumer;
+
+    Log.d (TAG, "detachWindow " + window);
+
+    if (window.getAttachedConsumer () != null)
+      {
+       consumer = window.getAttachedConsumer ();
+
+       Log.d (TAG, "detachWindow: removing" + consumer);
+
+       consumers.remove (consumer);
+       consumer.destroy ();
+      }
+  }
+};
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 65eb066a55..6e71bfa6db 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2209,7 +2209,7 @@ and `face'."
 ;;; The `custom' Widget.
 
 (defface custom-button
-  '((((type x w32 ns haiku pgtk) (class color))        ; Like default mode line
+  '((((type x w32 ns haiku pgtk android) (class color))        ; Like default 
mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2217,7 +2217,7 @@ and `face'."
   :group 'custom-faces)
 
 (defface custom-button-mouse
-  '((((type x w32 ns haiku pgtk) (class color))
+  '((((type x w32 ns haiku pgtk android) (class color))
      :box (:line-width 2 :style released-button)
      :background "grey90" :foreground "black")
     (t
@@ -2242,7 +2242,7 @@ and `face'."
       (if custom-raised-buttons 'custom-button-mouse 'highlight))
 
 (defface custom-button-pressed
-  '((((type x w32 ns haiku pgtk) (class color))
+  '((((type x w32 ns haiku pgtk android) (class color))
      :box (:line-width 2 :style pressed-button)
      :background "lightgrey" :foreground "black")
     (t :inverse-video t))
diff --git a/lisp/faces.el b/lisp/faces.el
index 0dd89be473..2779bfc295 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2911,7 +2911,7 @@ Note: Other faces cannot inherit from the cursor face."
     (((type haiku))
      :foreground "B_MENU_ITEM_TEXT_COLOR"
      :background "B_MENU_BACKGROUND_COLOR")
-    (((type x w32 ns pgtk) (class color))
+    (((type x w32 ns pgtk android) (class color))
      :background "grey75")
     (((type x) (class mono))
      :background "grey"))
diff --git a/src/android.c b/src/android.c
index dd841cf383..db12e24427 100644
--- a/src/android.c
+++ b/src/android.c
@@ -24,6 +24,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <limits.h>
 #include <signal.h>
 #include <semaphore.h>
+#include <dlfcn.h>
 
 #include <sys/stat.h>
 #include <sys/mman.h>
@@ -46,6 +47,7 @@ bool android_init_gui;
 
 #include <android/asset_manager.h>
 #include <android/asset_manager_jni.h>
+#include <android/bitmap.h>
 #include <android/log.h>
 
 #include <linux/ashmem.h>
@@ -86,6 +88,7 @@ struct android_emacs_pixmap
 {
   jclass class;
   jmethodID constructor;
+  jmethodID constructor_mutable;
 };
 
 struct android_graphics_point
@@ -94,6 +97,12 @@ struct android_graphics_point
   jmethodID constructor;
 };
 
+struct android_emacs_drawable
+{
+  jclass class;
+  jmethodID get_bitmap;
+};
+
 /* The asset manager being used.  */
 static AAssetManager *asset_manager;
 
@@ -106,6 +115,12 @@ char *android_site_load_path;
 /* The path used to store native libraries.  */
 char *android_lib_dir;
 
+/* The path used to store game files.  */
+char *android_game_path;
+
+/* The display's pixel densities.  */
+double android_pixel_density_x, android_pixel_density_y;
+
 /* The Android application data directory.  */
 static char *android_files_dir;
 
@@ -149,6 +164,9 @@ static struct android_emacs_pixmap pixmap_class;
 /* Various methods associated with the Point class.  */
 static struct android_graphics_point point_class;
 
+/* Various methods associated with the EmacsDrawable class.  */
+static struct android_emacs_drawable drawable_class;
+
 
 
 /* Event handling functions.  Events are stored on a (circular) queue
@@ -383,6 +401,10 @@ android_write_event (union android_event *event)
   if (!container)
     return;
 
+  /* If the event queue hasn't been initialized yet, return false.  */
+  if (!event_queue.events.next)
+    return;
+
   pthread_mutex_lock (&event_queue.mutex);
 
   /* The event queue is full, wait for events to be read.  */
@@ -451,6 +473,10 @@ android_select (int nfds, fd_set *readfds, fd_set 
*writefds,
   /* Unlock the event queue mutex.  */
   pthread_mutex_unlock (&event_queue.mutex);
 
+  /* This is to shut up process.c when pselect gets EINTR.  */
+  if (nfds_return < 0)
+    errno = EINTR;
+
   return nfds_return;
 }
 
@@ -793,16 +819,20 @@ android_close (int fd)
 {
   if (fd < ANDROID_MAX_ASSET_FD
       && (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID))
-    {
-      __android_log_print (ANDROID_LOG_INFO, __func__,
-                          "closing android file descriptor %d",
-                          fd);
-      android_table[fd].flags = 0;
-    }
+    android_table[fd].flags = 0;
 
   return close (fd);
 }
 
+/* Return the current user's ``home'' directory, which is actually the
+   app data directory on Android.  */
+
+const char *
+android_get_home_directory (void)
+{
+  return android_files_dir;
+}
+
 
 
 /* JNI functions called by Java.  */
@@ -814,6 +844,8 @@ JNIEXPORT void JNICALL
 NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
                              jobject local_asset_manager,
                              jobject files_dir, jobject libs_dir,
+                             jfloat pixel_density_x,
+                             jfloat pixel_density_y,
                              jobject emacs_service_object)
 {
   int pipefd[2];
@@ -829,6 +861,9 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
       return;
     }
 
+  android_pixel_density_x = pixel_density_x;
+  android_pixel_density_y = pixel_density_y;
+
   __android_log_print (ANDROID_LOG_INFO, __func__,
                       "Initializing "PACKAGE_STRING"...\nPlease report bugs to 
"
                       PACKAGE_BUGREPORT".  Thanks.\n");
@@ -891,15 +926,23 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
   if (!android_site_load_path)
     emacs_abort ();
 
+  android_game_path = malloc (PATH_MAX + 1);
+
+  if (!android_game_path)
+    emacs_abort ();
+
   snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
            android_files_dir);
+  snprintf (android_game_path, PATH_MAX, "%s/scores", android_files_dir);
+
   __android_log_print (ANDROID_LOG_INFO, __func__,
                       "Site-lisp directory: %s\n"
                       "Files directory: %s\n"
-                      "Native code directory: %s",
+                      "Native code directory: %s\n"
+                      "Game score path: %s\n",
                       android_site_load_path,
                       android_files_dir,
-                      android_lib_dir);
+                      android_lib_dir, android_game_path);
 
   /* Make a reference to the Emacs service.  */
   emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
@@ -997,6 +1040,7 @@ android_init_emacs_pixmap (void)
   assert (pixmap_class.c_name);
 
   FIND_METHOD (constructor, "<init>", "(S[IIII)V");
+  FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
 
 #undef FIND_METHOD
 }
@@ -1031,6 +1075,36 @@ android_init_graphics_point (void)
 #undef FIND_METHOD
 }
 
+static void
+android_init_emacs_drawable (void)
+{
+  jclass old;
+
+  drawable_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsDrawable");
+  eassert (drawable_class.class);
+
+  old = drawable_class.class;
+  drawable_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!drawable_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  drawable_class.c_name                                                \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       drawable_class.class,   \
+                                       name, signature);       \
+  assert (drawable_class.c_name);
+
+  FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;");
+#undef FIND_METHOD
+}
+
 extern JNIEXPORT void JNICALL
 NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv)
 {
@@ -1063,6 +1137,15 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, 
jarray argv)
   android_init_emacs_service ();
   android_init_emacs_pixmap ();
   android_init_graphics_point ();
+  android_init_emacs_drawable ();
+
+  /* Set HOME to the app data directory.  */
+  setenv ("HOME", android_files_dir, 1);
+
+  /* Set the cwd to that directory as well.  */
+  if (chdir (android_files_dir))
+    __android_log_print (ANDROID_LOG_WARN, __func__,
+                        "chdir: %s", strerror (errno));
 
   /* Initialize the Android GUI.  */
   android_init_gui = true;
@@ -1099,7 +1182,8 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject 
object,
 extern JNIEXPORT void JNICALL
 NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
                            jshort window, jlong time,
-                           jint state, jint keycode)
+                           jint state, jint keycode,
+                           jint unicode_char)
 {
   union android_event event;
 
@@ -1108,6 +1192,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
   event.xkey.time = time;
   event.xkey.state = state;
   event.xkey.keycode = keycode;
+  event.xkey.unicode_char = unicode_char;
 
   android_write_event (&event);
 }
@@ -1115,7 +1200,8 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
 extern JNIEXPORT void JNICALL
 NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
                              jshort window, jlong time,
-                             jint state, jint keycode)
+                             jint state, jint keycode,
+                             jint unicode_char)
 {
   union android_event event;
 
@@ -1124,6 +1210,46 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject 
object,
   event.xkey.time = time;
   event.xkey.state = state;
   event.xkey.keycode = keycode;
+  event.xkey.unicode_char = unicode_char;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
+                          jshort window, jlong time)
+{
+  union android_event event;
+
+  event.xkey.type = ANDROID_FOCUS_IN;
+  event.xkey.window = window;
+  event.xkey.time = time;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
+                           jshort window, jlong time)
+{
+  union android_event event;
+
+  event.xkey.type = ANDROID_FOCUS_OUT;
+  event.xkey.window = window;
+  event.xkey.time = time;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
+                               jshort window, jint action)
+{
+  union android_event event;
+
+  event.xaction.type = ANDROID_WINDOW_ACTION;
+  event.xaction.window = window;
+  event.xaction.action = action;
 
   android_write_event (&event);
 }
@@ -1142,13 +1268,6 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject 
object,
 
 #define MAX_HANDLE 65535
 
-enum android_handle_type
-  {
-    ANDROID_HANDLE_WINDOW,
-    ANDROID_HANDLE_GCONTEXT,
-    ANDROID_HANDLE_PIXMAP,
-  };
-
 struct android_handle_entry
 {
   /* The type.  */
@@ -1245,7 +1364,7 @@ android_destroy_handle (android_handle handle)
   android_handles[handle].handle = NULL;
 }
 
-static jobject
+jobject
 android_resolve_handle (android_handle handle,
                        enum android_handle_type type)
 {
@@ -1625,14 +1744,15 @@ android_change_gc (struct android_gc *gc,
                                     ANDROID_HANDLE_PIXMAP);
       (*android_java_env)->SetObjectField (android_java_env,
                                           gcontext,
-                                          emacs_gc_stipple,
+                                          emacs_gc_clip_mask,
                                           what);
 
       /* Clearing GCClipMask also clears the clip rectangles.  */
-      (*android_java_env)->SetObjectField (android_java_env,
-                                          gcontext,
-                                          emacs_gc_clip_rects,
-                                          NULL);
+      if (!what)
+       (*android_java_env)->SetObjectField (android_java_env,
+                                            gcontext,
+                                            emacs_gc_clip_rects,
+                                            NULL);
     }
 
   if (mask & ANDROID_GC_STIPPLE)
@@ -2008,10 +2128,23 @@ android_create_pixmap_from_bitmap_data (char *data, 
unsigned int width,
     {
       for (x = 0; x < width; ++x)
        {
-         if (data[y / 8] & (1 << (x % 8)))
-           region[x] = foreground;
+         if (depth == 24)
+           {
+             /* The alpha channels must be set, or otherwise, the
+                pixmap will be created entirely transparent.  */
+
+             if (data[y * (width + 7) / 8 + x / 8] & (1 << (x % 8)))
+               region[x] = foreground | 0xff000000;
+             else
+               region[x] = background | 0xff000000;
+           }
          else
-           region[x] = background;
+           {
+             if (data[y * (width + 7) / 8 + x / 8] & (1 << (x % 8)))
+               region[x] = foreground;
+             else
+               region[x] = background;
+           }
        }
 
       (*android_java_env)->SetIntArrayRegion (android_java_env,
@@ -2236,36 +2369,21 @@ android_create_pixmap (unsigned int width, unsigned int 
height,
 {
   android_handle prev_max_handle;
   jobject object;
-  jintArray colors;
   android_pixmap pixmap;
 
-  /* Create the color array holding the data.  */
-  colors = (*android_java_env)->NewIntArray (android_java_env,
-                                            width * height);
-
-  if (!colors)
-    {
-      (*android_java_env)->ExceptionClear (android_java_env);
-      memory_full (0);
-    }
-
   /* First, allocate the pixmap handle.  */
   prev_max_handle = max_handle;
   pixmap = android_alloc_id ();
 
   if (!pixmap)
-    {
-      ANDROID_DELETE_LOCAL_REF ((jobject) colors);
-      error ("Out of pixmap handles!");
-    }
+    error ("Out of pixmap handles!");
 
   object = (*android_java_env)->NewObject (android_java_env,
                                           pixmap_class.class,
-                                          pixmap_class.constructor,
-                                          (jshort) pixmap, colors,
+                                          pixmap_class.constructor_mutable,
+                                          (jshort) pixmap,
                                           (jint) width, (jint) height,
                                           (jint) depth);
-  ANDROID_DELETE_LOCAL_REF ((jobject) colors);
 
   if (!object)
     {
@@ -2313,6 +2431,387 @@ android_clear_area (android_window handle, int x, int y,
                                       (jint) width, (jint) height);
 }
 
+android_pixmap
+android_create_bitmap_from_data (char *bits, unsigned int width,
+                                unsigned int height)
+{
+  return android_create_pixmap_from_bitmap_data (bits, 1, 0,
+                                                width, height, 1);
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+                     char *data, unsigned int width, unsigned int height)
+{
+  struct android_image *image;
+
+  image = xmalloc (sizeof *image);
+
+  /* Fill in the fields required by image.c.  N.B. that
+     android_destroy_image ostensibly will free data, but image.c
+     mostly sets and frees data itself.  */
+  image->width = width;
+  image->height = height;
+  image->data = data;
+  image->depth = depth;
+  image->format = format;
+
+  /* Now fill in the image dimensions.  There are only two depths
+     supported by this function.  */
+
+  if (depth == 1)
+    {
+      image->bytes_per_line = (width + 7) / 8;
+      image->bits_per_pixel = 1;
+    }
+  else if (depth == 24)
+    {
+      image->bytes_per_line = width * 4;
+      image->bits_per_pixel = 32;
+    }
+  else
+    emacs_abort ();
+
+  return image;
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+  /* If XIMG->data is NULL, then it has already been freed by
+     image.c.  */
+
+  if (ximg->data)
+    xfree (ximg->data);
+  xfree (ximg);
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+                  unsigned long pixel)
+{
+  char *byte, *word;
+  unsigned int r, g, b;
+
+  /* Ignore out-of-bounds accesses.  */
+
+  if (x >= ximg->width || y >= ximg->height || x < 0 || y < 0)
+    return;
+
+  switch (ximg->depth)
+    {
+    case 1:
+      byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+
+      if (pixel)
+       *byte |= (1 << x % 8);
+      else
+       *byte &= ~(1 << x % 8);
+      break;
+
+    case 24:
+      /* Unaligned accesses are problematic on Android devices.  */
+      word = ximg->data + y * ximg->bytes_per_line + x * 4;
+
+      /* Swizzle the pixel into ABGR format.  Android uses Skia's
+        ``native color type'', which is ABGR.  This is despite the
+        format being named ``ARGB'', and more confusingly
+        `ANDROID_BITMAP_FORMAT_RGBA_8888' in bitmap.h.  */
+      r = pixel & 0x00ff0000;
+      g = pixel & 0x0000ff00;
+      b = pixel & 0x000000ff;
+      pixel = (r >> 16) | g | (b << 16) | 0xff000000;
+
+      memcpy (word, &pixel, sizeof pixel);
+      break;
+    }
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+  char *byte, *word;
+  unsigned int pixel, r, g, b;
+
+  if (x >= ximg->width || y >= ximg->height
+      || x < 0 || y < 0)
+    return 0;
+
+  switch (ximg->depth)
+    {
+    case 1:
+      byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+      return (*byte & (1 << x % 8)) ? 1 : 0;
+
+    case 24:
+      word = ximg->data + y * ximg->bytes_per_line + x * 4;
+      memcpy (&pixel, word, sizeof pixel);
+
+      /* Convert the pixel back to RGB.  */
+      b = pixel & 0x00ff0000;
+      g = pixel & 0x0000ff00;
+      r = pixel & 0x000000ff;
+      pixel = ((r << 16) | g | (b >> 16)) & ~0xff000000;
+
+      return pixel;
+    }
+
+  emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable handle,
+                  enum android_image_format format)
+{
+  jobject drawable, bitmap;
+  AndroidBitmapInfo bitmap_info;
+  size_t byte_size;
+  void *data;
+  struct android_image *image;
+  unsigned char *data1, *data2;
+  int i, x;
+
+  /* N.B. that supporting windows requires some more work to make
+     EmacsDrawable.getBitmap thread safe.  */
+  drawable = android_resolve_handle2 (handle, ANDROID_HANDLE_WINDOW,
+                                     ANDROID_HANDLE_PIXMAP);
+
+  /* Look up the drawable and get the bitmap corresponding to it.
+     Then, lock the bitmap's bits.  */
+  bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 drawable,
+                                                 drawable_class.get_bitmap);
+  if (!bitmap)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  memset (&bitmap_info, 0, sizeof bitmap_info);
+
+  /* The NDK doc seems to imply this function can fail but doesn't say
+     what value it gives when it does! */
+  AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+  if (!bitmap_info.stride)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  /* Compute how big the image data will be.  Fail if it would be too
+     big.  */
+
+  if (bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8)
+    {
+      if (INT_MULTIPLY_WRAPV ((size_t) bitmap_info.stride,
+                             (size_t) bitmap_info.height,
+                             &byte_size))
+       {
+         ANDROID_DELETE_LOCAL_REF (bitmap);
+         memory_full (0);
+       }
+    }
+  else
+    /* This A8 image will be packed into A1 later on.  */
+    byte_size = (bitmap_info.width + 7) / 8;
+
+  /* Lock the image data.  Once again, the NDK documentation says the
+     call can fail, but does not say how to determine whether or not
+     it has failed, nor how the address is aligned.  */
+  data = NULL;
+  AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+  if (!data)
+    {
+      /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+        failed.  */
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  /* Copy the data into a new struct android_image.  */
+  image = xmalloc (sizeof *image);
+  image->width = bitmap_info.width;
+  image->height = bitmap_info.height;
+  image->data = malloc (byte_size);
+
+  if (!image->data)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      xfree (image);
+      memory_full (byte_size);
+    }
+
+  /* Use the format of the bitmap to determine the image depth.  */
+  switch (bitmap_info.format)
+    {
+    case ANDROID_BITMAP_FORMAT_RGBA_8888:
+      image->depth = 24;
+      image->bits_per_pixel = 32;
+      break;
+
+      /* A8 images are used by Emacs to represent bitmaps.  They have
+        to be packed manually.  */
+    case ANDROID_BITMAP_FORMAT_A_8:
+      image->depth = 1;
+      image->bits_per_pixel = 1;
+      break;
+
+      /* Other formats are currently not supported.  */
+    default:
+      emacs_abort ();
+    }
+
+  image->format = format;
+
+  if (image->depth == 24)
+    {
+      image->bytes_per_line = bitmap_info.stride;
+
+      /* Copy the bitmap data over.  */
+      memcpy (image->data, data, byte_size);
+    }
+  else
+    {
+      /* Pack the A8 image data into bits manually.  */
+      image->bytes_per_line = (image->width + 7) / 8;
+
+      data1 = (unsigned char *) image->data;
+      data2 = data;
+
+      for (i = 0; i < image->height; ++i)
+       {
+         for (x = 0; x < image->width; ++x)
+           /* Some bits in data1 might be initialized at this point,
+              but they will all be set properly later.  */
+           data1[x / 8] = (data2[x]
+                           ? (data1[x / 8] | (1 << (x % 8)))
+                           : (data1[x / 8] & ~(1 << (x % 8))));
+
+         data1 += image->bytes_per_line;
+         data2 += bitmap_info.stride;
+       }
+    }
+
+  /* Unlock the bitmap pixels.  */
+  AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+  /* Delete the bitmap reference.  */
+  ANDROID_DELETE_LOCAL_REF (bitmap);
+  return image;
+}
+
+void
+android_put_image (android_pixmap handle, struct android_image *image)
+{
+  jobject drawable, bitmap;
+  AndroidBitmapInfo bitmap_info;
+  void *data;
+  unsigned char *data_1, *data_2;
+  int i, x;
+
+  drawable = android_resolve_handle (handle, ANDROID_HANDLE_PIXMAP);
+
+  /* Look up the drawable and get the bitmap corresponding to it.
+     Then, lock the bitmap's bits.  */
+  bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 drawable,
+                                                 drawable_class.get_bitmap);
+  if (!bitmap)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  memset (&bitmap_info, 0, sizeof bitmap_info);
+
+  /* The NDK doc seems to imply this function can fail but doesn't say
+     what value it gives when it does! */
+  AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+  if (!bitmap_info.stride)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  if (bitmap_info.width != image->width
+      || bitmap_info.height != image->height)
+    /* This is not yet supported.  */
+    emacs_abort ();
+
+  /* Make sure the bitmap formats are compatible with each other.  */
+
+  if ((image->depth == 24
+       && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
+      || (image->depth == 1
+         && bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8))
+    emacs_abort ();
+
+  /* Lock the image data.  Once again, the NDK documentation says the
+     call can fail, but does not say how to determine whether or not
+     it has failed, nor how the address is aligned.  */
+  data = NULL;
+  AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+  if (!data)
+    {
+      /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+        failed.  */
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  data_1 = data;
+  data_2 = (unsigned char *) image->data;
+
+  /* Copy the bitmap data over scanline-by-scanline.  */
+  for (i = 0; i < image->height; ++i)
+    {
+      if (image->depth != 1)
+       memcpy (data_1, data_2,
+               image->width * (image->bits_per_pixel / 8));
+      else
+       {
+         /* Android internally uses a 1 byte-per-pixel format for
+            ALPHA_8 images.  Expand the image from the 1
+            bit-per-pixel X format correctly.  */
+
+         for (x = 0; x < image->width; ++x)
+           data_1[x] = (data_2[x / 8] & (1 << x % 8)) ? 0xff : 0;
+       }
+
+      data_1 += bitmap_info.stride;
+      data_2 += image->bytes_per_line;
+    }
+
+  /* Unlock the bitmap pixels.  */
+  AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+  /* Delete the bitmap reference.  */
+  ANDROID_DELETE_LOCAL_REF (bitmap);
+}
+
+
+
+#undef faccessat
+
+/* Replace the system faccessat with one which understands AT_EACCESS.
+   Android's faccessat simply fails upon using AT_EACCESS, so repalce
+   it with zero here.  This isn't caught during configuration.  */
+
+int
+faccessat (int dirfd, const char *pathname, int mode, int flags)
+{
+  static int (*real_faccessat) (int, const char *, int, int);
+
+  if (!real_faccessat)
+    real_faccessat = dlsym (RTLD_NEXT, "faccessat");
+
+  return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
+}
+
 #else /* ANDROID_STUBIFY */
 
 /* X emulation functions for Android.  */
@@ -2332,4 +2831,44 @@ android_free_gc (struct android_gc *gc)
   emacs_abort ();
 }
 
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+                     char *data, unsigned int width, unsigned int height)
+{
+  emacs_abort ();
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+  emacs_abort ();
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+                  unsigned long pixel)
+{
+  emacs_abort ();
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+  emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable drawable,
+                  enum android_image_format format)
+{
+  emacs_abort ();
+}
+
+void
+android_put_image (android_pixmap pixmap,
+                  struct android_image *image)
+{
+  emacs_abort ();
+}
+
 #endif
diff --git a/src/android.h b/src/android.h
index 6bdcd38ed6..4d702fe207 100644
--- a/src/android.h
+++ b/src/android.h
@@ -28,6 +28,8 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <jni.h>
 #include <pwd.h>
 #include <sys/stat.h>
+
+#include "androidgui.h"
 #endif
 
 /* This must be used in every symbol declaration to export it to the
@@ -49,6 +51,19 @@ extern int android_fstat (int, struct stat *);
 extern int android_fstatat (int, const char *restrict,
                            struct stat *restrict, int);
 extern int android_close (int);
+extern const char *android_get_home_directory (void);
+
+extern double android_pixel_density_x, android_pixel_density_y;
+
+enum android_handle_type
+  {
+    ANDROID_HANDLE_WINDOW,
+    ANDROID_HANDLE_GCONTEXT,
+    ANDROID_HANDLE_PIXMAP,
+  };
+
+extern jobject android_resolve_handle (android_handle,
+                                      enum android_handle_type);
 
 #endif
 
diff --git a/src/androidfns.c b/src/androidfns.c
index e9e1ae3d52..a78b35fc8f 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -531,9 +531,10 @@ android_default_font_parameter (struct frame *f, 
Lisp_Object parms)
   if (! FONTP (font) && ! STRINGP (font))
     {
       const char *names[] = {
-       /* This will find the normal font.  */
-       "DroidSansMono",
+       /* This will find the normal font.  The default font size on
+          Android is 8.  */
        "monospace",
+       "DroidSansMono",
        NULL
       };
       int i;
diff --git a/src/androidfont.c b/src/androidfont.c
index e312e55c54..b2c83201b0 100644
--- a/src/androidfont.c
+++ b/src/androidfont.c
@@ -49,6 +49,7 @@ struct android_emacs_font_driver
   jmethodID has_char;
   jmethodID text_extents;
   jmethodID encode_char;
+  jmethodID draw;
 
   /* Static methods.  */
   jmethodID create_font_driver;
@@ -67,6 +68,7 @@ struct android_emacs_font_spec
   jfieldID size;
   jfieldID spacing;
   jfieldID avgwidth;
+  jfieldID dpi;
 };
 
 struct android_emacs_font_metrics
@@ -113,6 +115,9 @@ struct androidfont_info
 
   /* The Java-side font.  */
   jobject object;
+
+  /* Cached glyph metrics arranged in a two dimensional array.  */
+  struct font_metrics **metrics;
 };
 
 struct androidfont_entity
@@ -197,9 +202,11 @@ android_init_font_driver (void)
   FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
               "Spec;C)I");
   FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
-              "$FontObject;[I[Lorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
+              "$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
   FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
               "$FontObject;C)I");
+  FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
+              "Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
 
   font_driver_class.create_font_driver
     = (*android_java_env)->GetStaticMethodID (android_java_env,
@@ -252,6 +259,7 @@ android_init_font_spec (void)
   FIND_FIELD (size, "size", "Ljava/lang/Integer;");
   FIND_FIELD (spacing, "spacing", "Ljava/lang/Integer;");
   FIND_FIELD (avgwidth, "avgwidth", "Ljava/lang/Integer;");
+  FIND_FIELD (dpi, "dpi", "Ljava/lang/Integer;");
 #undef FIND_FIELD
 }
 
@@ -449,6 +457,9 @@ androidfont_from_lisp (Lisp_Object font)
   DO_CARDINAL_FIELD (avgwidth, (FIXNUMP (AREF (font, FONT_AVGWIDTH_INDEX))
                                ? XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX))
                                : -1));
+  DO_CARDINAL_FIELD (dpi, (FIXNUMP (AREF (font, FONT_DPI_INDEX))
+                          ? XFIXNUM (AREF (font, FONT_DPI_INDEX))
+                          : -1));
 
 #undef DO_CARDINAL_FIELD
 
@@ -507,6 +518,8 @@ androidfont_from_java (jobject spec, Lisp_Object entity)
   DO_CARDINAL_FIELD (size, FONT_SIZE_INDEX, false);
   DO_CARDINAL_FIELD (spacing, FONT_SPACING_INDEX, false);
   DO_CARDINAL_FIELD (avgwidth, FONT_AVGWIDTH_INDEX, false);
+  DO_CARDINAL_FIELD (dpi, FONT_DPI_INDEX, false);
+
 #undef DO_CARDINAL_FIELD
 }
 
@@ -613,49 +626,98 @@ static int
 androidfont_draw (struct glyph_string *s, int from, int to,
                  int x, int y, bool with_background)
 {
-  return 0;
+  struct androidfont_info *info;
+  jarray chars;
+  int rc;
+  jobject gcontext, drawable;
+
+  verify (sizeof (unsigned int) == sizeof (jint));
+  info = (struct androidfont_info *) s->font;
+
+  gcontext = android_resolve_handle (s->gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+  drawable = android_resolve_handle (FRAME_ANDROID_WINDOW (s->f),
+                                    ANDROID_HANDLE_WINDOW);
+  chars = (*android_java_env)->NewIntArray (android_java_env,
+                                           to - from);
+
+  if (!chars)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  (*android_java_env)->SetIntArrayRegion (android_java_env, chars,
+                                         0, to - from,
+                                         (jint *) s->char2b + from);
+
+  info = (struct androidfont_info *) s->font;
+  prepare_face_for_display (s->f, s->face);
+
+  rc = (*android_java_env)->CallIntMethod (android_java_env,
+                                          font_driver,
+                                          font_driver_class.draw,
+                                          info->object,
+                                          gcontext, drawable,
+                                          chars, (jint) x, (jint) y,
+                                          (jint) s->width,
+                                          (jboolean) with_background);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (chars);
+
+  return rc;
 }
 
 static Lisp_Object
-androidfont_open_font (struct frame *f, Lisp_Object font_entity, int x)
+androidfont_open_font (struct frame *f, Lisp_Object font_entity,
+                      int pixel_size)
 {
   struct androidfont_info *font_info;
   struct androidfont_entity *entity;
   struct font *font;
-  Lisp_Object font_object, tem;
+  Lisp_Object font_object;
   jobject old;
   jint value;
 
-  if (x <= 0)
+  if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+    pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+  else if (pixel_size == 0)
     {
-      /* Get pixel size from frame instead.  */
-      tem = get_frame_param (f, Qfontsize);
-      x = NILP (tem) ? 0 : XFIXNAT (tem);
+      /* This bit was copied from xfont.c.  The values might need
+        adjustment.  */
+
+      if (FRAME_FONT (f))
+       pixel_size = FRAME_FONT (f)->pixel_size;
+      else
+       pixel_size = 12;
     }
 
   __android_log_print (ANDROID_LOG_DEBUG, __func__,
                       "opening font entity %"pI"x:%d",
-                      (EMACS_INT) font_entity, x);
+                      (EMACS_INT) font_entity, pixel_size);
 
   entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
 
   block_input ();
   font_object = font_make_object (VECSIZE (struct androidfont_info),
-                                 font_entity, x);
+                                 font_entity, pixel_size);
   ASET (font_object, FONT_TYPE_INDEX, Qandroid);
   font_info = (struct androidfont_info *) XFONT_OBJECT (font_object);
   font = &font_info->font;
   font->driver = &androidfont_driver;
 
-  /* Clear font_info->object early in case GC happens later on! */
+  /* Clear font_info->object and font_info->metrics early in case GC
+     happens later on! */
   font_info->object = NULL;
+  font_info->metrics = NULL;
   unblock_input ();
 
   font_info->object
     = (*android_java_env)->CallObjectMethod (android_java_env,
                                             font_driver,
                                             font_driver_class.open_font,
-                                            entity->object, (jint) x);
+                                            entity->object,
+                                            (jint) pixel_size);
   if (!font_info->object)
     {
       (*android_java_env)->ExceptionClear (android_java_env);
@@ -710,9 +772,19 @@ static void
 androidfont_close_font (struct font *font)
 {
   struct androidfont_info *info;
+  int i;
 
   info = (struct androidfont_info *) font;
 
+  /* Free the font metrics cache if it exists.  */
+
+  if (info->metrics)
+    {
+      for (i = 0; i < 256; ++i)
+       xfree (info->metrics[i]);
+      xfree (info->metrics);
+    }
+
   /* If info->object is NULL, then FONT was unsuccessfully created,
      and there is no global reference that has to be deleted.  */
 
@@ -762,18 +834,65 @@ androidfont_encode_char (struct font *font, int c)
                                             info->object, (jchar) c);
 }
 
+static void
+androidfont_cache_text_extents (struct androidfont_info *info,
+                               unsigned int glyph,
+                               struct font_metrics *metrics)
+{
+  int i;
+
+  /* Glyphs larger than 65535 can't be cached.  */
+  if (glyph >= 256 * 256)
+    return;
+
+  if (!info->metrics)
+    info->metrics = xzalloc (256 * sizeof *info->metrics);
+
+  if (!info->metrics[glyph / 256])
+    {
+      info->metrics[glyph / 256]
+       = xnmalloc (256, sizeof **info->metrics);
+
+      /* Now, all the metrics in that array as invalid by setting
+        lbearing to SHRT_MAX.  */
+      for (i = 0; i < 256; ++i)
+       info->metrics[glyph / 256][i].lbearing = SHRT_MAX;
+    }
+
+  /* Finally, cache the glyph.  */
+  info->metrics[glyph / 256][glyph % 256] = *metrics;
+}
+
+static bool
+androidfont_check_cached_extents (struct androidfont_info *info,
+                                 unsigned int glyph,
+                                 struct font_metrics *metrics)
+{
+  if (info->metrics && info->metrics[glyph / 256]
+      && info->metrics[glyph / 256][glyph % 256].lbearing != SHRT_MAX)
+    {
+      *metrics = info->metrics[glyph / 256][glyph % 256];
+      return true;
+    }
+
+  return false;
+}
+
 static void
 androidfont_text_extents (struct font *font, const unsigned int *code,
                          int nglyphs, struct font_metrics *metrics)
 {
   struct androidfont_info *info;
-  jarray codepoint_array, metrics_array;
+  jarray codepoint_array;
   jobject metrics_object;
-  int i;
   short value;
 
   info = (struct androidfont_info *) font;
 
+  if (nglyphs == 1
+      && androidfont_check_cached_extents (info, *code, metrics))
+    return;
+
   /* Allocate the arrays of code points and font metrics.  */
   codepoint_array
     = (*android_java_env)->NewIntArray (android_java_env,
@@ -784,92 +903,103 @@ androidfont_text_extents (struct font *font, const 
unsigned int *code,
       memory_full (0);
     }
 
-  metrics_array
-    = (*android_java_env)->NewObjectArray (android_java_env,
-                                          nglyphs,
-                                          font_metrics_class.class,
-                                          NULL);
-  if (!metrics_array)
-    {
-      (*android_java_env)->ExceptionClear (android_java_env);
-      ANDROID_DELETE_LOCAL_REF (metrics_array);
-      memory_full (0);
-    }
-
-  if (sizeof (unsigned int) == sizeof (jint))
-    /* Always true on every Android device.  */
-    (*android_java_env)->SetIntArrayRegion (android_java_env,
-                                           codepoint_array,
-                                           0, nglyphs,
-                                           (jint *) code);
-  else
-    emacs_abort ();
-
-  for (i = 0; i < nglyphs; ++i)
-    {
-      metrics_object
-       = (*android_java_env)->AllocObject (android_java_env,
-                                           font_metrics_class.class);
+  verify (sizeof (unsigned int) == sizeof (jint));
 
-      if (!metrics_object)
-       {
-         (*android_java_env)->ExceptionClear (android_java_env);
-         ANDROID_DELETE_LOCAL_REF (metrics_array);
-         ANDROID_DELETE_LOCAL_REF (codepoint_array);
-         memory_full (0);
-       }
+  /* Always true on every Android device.  */
+  (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                         codepoint_array,
+                                         0, nglyphs,
+                                         (jint *) code);
 
-      (*android_java_env)->SetObjectArrayElement (android_java_env,
-                                                 metrics_array, i,
-                                                 metrics_object);
-      ANDROID_DELETE_LOCAL_REF (metrics_object);
-    }
+  metrics_object
+    = (*android_java_env)->AllocObject (android_java_env,
+                                       font_metrics_class.class);
 
   (*android_java_env)->CallVoidMethod (android_java_env,
                                       font_driver,
                                       font_driver_class.text_extents,
                                       info->object, codepoint_array,
-                                      metrics_array);
+                                      metrics_object);
 
   if ((*android_java_env)->ExceptionCheck (android_java_env))
     {
       (*android_java_env)->ExceptionClear (android_java_env);
-      ANDROID_DELETE_LOCAL_REF (metrics_array);
+      ANDROID_DELETE_LOCAL_REF (metrics_object);
       ANDROID_DELETE_LOCAL_REF (codepoint_array);
       memory_full (0);
     }
 
-  for (i = 0; i < nglyphs; ++i)
-    {
-      metrics_object
-       = (*android_java_env)->GetObjectArrayElement (android_java_env,
-                                                     metrics_array, i);
 #define DO_CARDINAL_FIELD(field)                                       \
   value                                                                        
\
     = (*android_java_env)->GetShortField (android_java_env,            \
                                          metrics_object,               \
                                          font_metrics_class.field);    \
-  metrics[i].field = value;
+  metrics->field = value;
 
-      DO_CARDINAL_FIELD (lbearing);
-      DO_CARDINAL_FIELD (rbearing);
-      DO_CARDINAL_FIELD (width);
-      DO_CARDINAL_FIELD (ascent);
-      DO_CARDINAL_FIELD (descent);
+  DO_CARDINAL_FIELD (lbearing);
+  DO_CARDINAL_FIELD (rbearing);
+  DO_CARDINAL_FIELD (width);
+  DO_CARDINAL_FIELD (ascent);
+  DO_CARDINAL_FIELD (descent);
 
 #undef DO_CARDINAL_FIELD
 
-      ANDROID_DELETE_LOCAL_REF (metrics_object);
-    }
-
-  ANDROID_DELETE_LOCAL_REF (metrics_array);
+  ANDROID_DELETE_LOCAL_REF (metrics_object);
   ANDROID_DELETE_LOCAL_REF (codepoint_array);
+
+  /* Emacs spends a lot of time in androidfont_text_extents, which
+     makes calling JNI too slow.  Cache the metrics for this single
+     glyph.  */
+
+  if (nglyphs == 1)
+    androidfont_cache_text_extents (info, *code, metrics);
 }
 
 static Lisp_Object
 androidfont_list_family (struct frame *f)
 {
-  return Qnil;
+  Lisp_Object families;
+  jarray family_array;
+  jobject string;
+  jsize i, length;
+  const char *family;
+
+  family_array
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            font_driver,
+                                            font_driver_class.list_families);
+  if (!family_array)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  length = (*android_java_env)->GetArrayLength (android_java_env,
+                                               family_array);
+  families = Qnil;
+
+  for (i = 0; i < length; ++i)
+    {
+      string = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                          family_array, i);
+      family = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                      (jstring) string, NULL);
+
+      if (!family)
+       {
+         ANDROID_DELETE_LOCAL_REF (string);
+         ANDROID_DELETE_LOCAL_REF (family_array);
+       }
+
+      families = Fcons (build_string_from_utf8 (string), families);
+      (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                                 (jstring) string,
+                                                 family);
+      ANDROID_DELETE_LOCAL_REF (string);
+    }
+
+  ANDROID_DELETE_LOCAL_REF (family_array);
+  return Fnreverse (families);
 }
 
 struct font_driver androidfont_driver =
diff --git a/src/androidgui.h b/src/androidgui.h
index 43ccc86e5c..b0ea820cfd 100644
--- a/src/androidgui.h
+++ b/src/androidgui.h
@@ -201,6 +201,9 @@ enum android_event_type
     ANDROID_KEY_PRESS,
     ANDROID_KEY_RELEASE,
     ANDROID_CONFIGURE_NOTIFY,
+    ANDROID_FOCUS_IN,
+    ANDROID_FOCUS_OUT,
+    ANDROID_WINDOW_ACTION,
   };
 
 struct android_any_event
@@ -209,6 +212,13 @@ struct android_any_event
   android_window window;
 };
 
+enum android_modifier_mask
+  {
+    ANDROID_SHIFT_MASK  = 193,
+    ANDROID_CONTROL_MASK = 4096,
+    ANDROID_ALT_MASK    = 2,
+  };
+
 struct android_key_event
 {
   enum android_event_type type;
@@ -216,8 +226,18 @@ struct android_key_event
   android_time time;
   unsigned int state;
   unsigned int keycode;
+  unsigned int unicode_char;
 };
 
+/* These hard coded values are Android modifier keycodes derived
+   through experimentation.  */
+
+#define ANDROID_IS_MODIFIER_KEY(key)                                   \
+  ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114          \
+   || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78      \
+   || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95         \
+   || (key) == 63)
+
 struct android_configure_event
 {
   enum android_event_type type;
@@ -227,12 +247,35 @@ struct android_configure_event
   int width, height;
 };
 
+struct android_focus_event
+{
+  enum android_event_type type;
+  android_window window;
+  android_time time;
+};
+
+struct android_window_action_event
+{
+  enum android_event_type type;
+
+  /* The window handle.  This can be ANDROID_NONE.  */
+  android_window window;
+
+  /* Numerical identifier for this action.  If 0 and WINDOW is set,
+     then it means the frame associated with that window has been
+     destroyed.  Otherwise, it means Emacs should create a new
+     frame.  */
+  unsigned int action;
+};
+
 union android_event
 {
   enum android_event_type type;
   struct android_any_event xany;
   struct android_key_event xkey;
   struct android_configure_event xconfigure;
+  struct android_focus_event xfocus;
+  struct android_window_action_event xaction;
 };
 
 extern int android_pending (void);
@@ -303,9 +346,47 @@ extern android_pixmap android_create_pixmap (unsigned int, 
unsigned int,
 extern void android_set_ts_origin (struct android_gc *, int, int);
 extern void android_clear_area (android_window, int, int, unsigned int,
                                unsigned int);
+extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
+                                                      unsigned int);
 
 #endif
 
+
+
+/* Image support.  Keep the API as similar to XImage as possible.  To
+   avoid leaving a huge mess of "#ifndef ANDROID_STUBIFY" in image.c,
+   stubs should be defined for all functions.  */
+
+enum android_image_format
+  {
+    ANDROID_Z_PIXMAP,
+  };
+
+struct android_image
+{
+  int width, height;
+  enum android_image_format format;
+  char *data;
+  int depth;
+  int bytes_per_line;
+  int bits_per_pixel;
+};
+
+extern struct android_image *android_create_image (unsigned int,
+                                                  enum android_image_format,
+                                                  char *, unsigned int,
+                                                  unsigned int);
+extern void android_destroy_image (struct android_image *);
+
+extern void android_put_pixel (struct android_image *, int, int,
+                              unsigned long);
+extern unsigned long android_get_pixel (struct android_image *, int, int);
+extern struct android_image *android_get_image (android_drawable,
+                                               enum android_image_format);
+extern void android_put_image (android_pixmap, struct android_image *);
+
+
+
 /* X emulation stuff also needed while building stubs.  */
 
 extern struct android_gc *android_create_gc (enum android_gc_value_mask,
diff --git a/src/androidterm.c b/src/androidterm.c
index bb3c2a2973..0279da4f58 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -39,6 +39,8 @@ struct android_display_info *x_display_list;
 
 #ifndef ANDROID_STUBIFY
 
+#include <android/log.h>
+
 enum
   {
     ANDROID_EVENT_NORMAL,
@@ -141,6 +143,124 @@ android_flush_dirty_back_buffer_on (struct frame *f)
   show_back_buffer (f);
 }
 
+/* Convert between the modifier bits Android uses and the modifier
+   bits Emacs uses.  */
+
+static int
+android_android_to_emacs_modifiers (struct android_display_info *dpyinfo,
+                                   int state)
+{
+  return ((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0
+         | (state & ANDROID_SHIFT_MASK) ? shift_modifier : 0
+         | (state & ANDROID_ALT_MASK) ? meta_modifier : 0);
+}
+
+static int
+android_emacs_to_android_modifiers (struct android_display_info *dpyinfo,
+                                   intmax_t state)
+{
+  return ((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0
+         | (state & shift_modifier) ? ANDROID_SHIFT_MASK : 0
+         | (state & meta_modifier) ? ANDROID_ALT_MASK : 0);
+}
+
+static void android_frame_rehighlight (struct android_display_info *);
+
+static void
+android_lower_frame (struct frame *f)
+{
+  /* TODO.  */
+}
+
+static void
+android_raise_frame (struct frame *f)
+{
+  /* TODO.  */
+}
+
+static void
+android_new_focus_frame (struct android_display_info *dpyinfo,
+                        struct frame *frame)
+{
+  struct frame *old_focus;
+
+  old_focus = dpyinfo->focus_frame;
+
+  if (frame != dpyinfo->focus_frame)
+    {
+      /* Set this before calling other routines, so that they see
+        the correct value of x_focus_frame.  */
+      dpyinfo->focus_frame = frame;
+
+      if (old_focus && old_focus->auto_lower)
+       android_lower_frame (old_focus);
+
+      if (dpyinfo->focus_frame && dpyinfo->focus_frame->auto_raise)
+       dpyinfo->pending_autoraise_frame = dpyinfo->focus_frame;
+      else
+       dpyinfo->pending_autoraise_frame = NULL;
+    }
+
+  android_frame_rehighlight (dpyinfo);
+}
+
+static void
+android_focus_changed (int type, int state,
+                      struct android_display_info *dpyinfo,
+                      struct frame *frame, struct input_event *bufp)
+{
+  if (type == ANDROID_FOCUS_IN)
+    {
+      if (dpyinfo->x_focus_event_frame != frame)
+        {
+          android_new_focus_frame (dpyinfo, frame);
+          dpyinfo->x_focus_event_frame = frame;
+          bufp->kind = FOCUS_IN_EVENT;
+          XSETFRAME (bufp->frame_or_window, frame);
+        }
+
+      frame->output_data.android->focus_state |= state;
+    }
+  else if (type == ANDROID_FOCUS_OUT)
+    {
+      frame->output_data.android->focus_state &= ~state;
+
+      if (dpyinfo->x_focus_event_frame == frame)
+        {
+          dpyinfo->x_focus_event_frame = 0;
+          android_new_focus_frame (dpyinfo, 0);
+
+          bufp->kind = FOCUS_OUT_EVENT;
+          XSETFRAME (bufp->frame_or_window, frame);
+        }
+
+      if (frame->pointer_invisible)
+        android_toggle_invisible_pointer (frame, false);
+    }
+}
+
+static void
+android_detect_focus_change (struct android_display_info *dpyinfo,
+                            struct frame *frame,
+                            union android_event *event,
+                            struct input_event *bufp)
+{
+  if (!frame)
+    return;
+
+  switch (event->type)
+    {
+    case ANDROID_FOCUS_IN:
+    case ANDROID_FOCUS_OUT:
+      android_focus_changed (event->type, FOCUS_EXPLICIT,
+                            dpyinfo, frame, bufp);
+      break;
+
+    default:
+      break;
+    }
+}
+
 static int
 handle_one_android_event (struct android_display_info *dpyinfo,
                          union android_event *event, int *finish,
@@ -149,11 +269,20 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
   union android_event configureEvent;
   struct frame *f, *any, *mouse_frame;
   Mouse_HLInfo *hlinfo;
+  union buffered_input_event inev;
+  int modifiers, count;
+
+  /* It is okay for this to not resemble handle_one_xevent so much.
+     Differences in event handling code are much less nasty than
+     stuble differences in the graphics code.  */
 
+  count = 0;
   hlinfo = &dpyinfo->mouse_highlight;
   *finish = ANDROID_EVENT_NORMAL;
   any = android_window_to_frame (dpyinfo, event->xany.window);
 
+  EVENT_INIT (inev.ie);
+
   switch (event->type)
     {
     case ANDROID_CONFIGURE_NOTIFY:
@@ -192,12 +321,15 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
 
     case ANDROID_KEY_PRESS:
 
+      /* Set f to any.  There are no ``outer windows'' on Android.  */
+      f = any;
+
       /* If mouse-highlight is an integer, input clears out
         mouse highlighting.  */
       if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
          && (any == 0
-             || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
-             || !EQ (f->tab_bar_window, hlinfo->mouse_face_window)))
+             || !EQ (any->tool_bar_window, hlinfo->mouse_face_window)
+             || !EQ (any->tab_bar_window, hlinfo->mouse_face_window)))
         {
          mouse_frame = hlinfo->mouse_face_mouse_frame;
 
@@ -208,13 +340,90 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
            android_flush_dirty_back_buffer_on (mouse_frame);
        }
 
+      event->xkey.state
+       |= android_emacs_to_android_modifiers (dpyinfo,
+                                              extra_keyboard_modifiers);
+      modifiers = event->xkey.state;
+
+      /* Common for all keysym input events.  */
+      XSETFRAME (inev.ie.frame_or_window, any);
+      inev.ie.modifiers
+       = android_android_to_emacs_modifiers (dpyinfo, modifiers);
+      inev.ie.timestamp = event->xkey.time;
+
+      /* First deal with keysyms which have defined translations to
+        characters.  */
+
+      if (event->xkey.unicode_char >= 32
+         && event->xkey.unicode_char < 128)
+       {
+         inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+         inev.ie.code = event->xkey.unicode_char;
+       }
+      else if (event->xkey.unicode_char < 32)
+       {
+         /* If the key is a modifier key, just return.  */
+         if (ANDROID_IS_MODIFIER_KEY (event->xkey.keycode))
+           goto done_keysym;
+
+         /* Next, deal with special ``characters'' by giving the
+            keycode to keyboard.c.  */
+         inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+         inev.ie.code = event->xkey.keycode;
+       }
+      else
+       {
+         /* Finally, deal with Unicode characters.  */
+         inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+         inev.ie.code = event->xkey.unicode_char;
+       }
+
+      goto done_keysym;
+
+    done_keysym:
+      goto OTHER;
+
+    case ANDROID_FOCUS_IN:
+    case ANDROID_FOCUS_OUT:
+      android_detect_focus_change (dpyinfo, any, event, &inev.ie);
+      goto OTHER;
+
+    case ANDROID_WINDOW_ACTION:
+
+      f = any;
+
+      if (event->xaction.action == 0)
+       {
+         /* Action 0 either means to destroy a frame or to create a
+            new frame, depending on whether or not
+            event->xaction.window exists.  */
+
+         if (event->xaction.window)
+           {
+             if (!f)
+               goto OTHER;
+
+             inev.ie.kind = DELETE_WINDOW_EVENT;
+             XSETFRAME (inev.ie.frame_or_window, f);
+           }
+         else
+           /* A new frame must be created.  */;
+       }
+
+      goto OTHER;
+
     default:
       goto OTHER;
     }
 
  OTHER:
+  if (inev.ie.kind != NO_EVENT)
+    {
+      kbd_buffer_store_buffered_event (&inev, hold_quit);
+      count++;
+    }
 
-  return 0;
+  return count;
 }
 
 static int
@@ -378,15 +587,52 @@ android_focus_frame (struct frame *f, bool noactivate)
 }
 
 static void
-android_frame_rehighlight (struct frame *f)
+android_frame_rehighlight (struct android_display_info *dpyinfo)
 {
-  /* TODO */
+  struct frame *old_highlight;
+
+  old_highlight = dpyinfo->highlight_frame;
+
+  if (dpyinfo->focus_frame)
+    {
+      dpyinfo->highlight_frame
+       = ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->focus_frame)))
+          ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->focus_frame))
+          : dpyinfo->focus_frame);
+      if (!FRAME_LIVE_P (dpyinfo->highlight_frame))
+       {
+         fset_focus_frame (dpyinfo->focus_frame, Qnil);
+         dpyinfo->highlight_frame = dpyinfo->focus_frame;
+       }
+    }
+  else
+    dpyinfo->highlight_frame = 0;
+
+  if (dpyinfo->highlight_frame != old_highlight)
+    {
+      /* This is not yet required on Android.  */
+#if 0
+      if (old_highlight)
+       android_frame_unhighlight (old_highlight);
+      if (dpyinfo->highlight_frame)
+       android_frame_highlight (dpyinfo->highlight_frame);
+#endif
+    }
+}
+
+static void
+android_frame_rehighlight_hook (struct frame *f)
+{
+  android_frame_rehighlight (FRAME_DISPLAY_INFO (f));
 }
 
 static void
 android_frame_raise_lower (struct frame *f, bool raise_flag)
 {
-  /* TODO */
+  if (raise_flag)
+    android_raise_frame (f);
+  else
+    android_lower_frame (f);
 }
 
 void
@@ -401,6 +647,10 @@ android_make_frame_visible (struct frame *f)
 void
 android_make_frame_invisible (struct frame *f)
 {
+  /* Don't keep the highlight on an invisible frame.  */
+  if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
+    FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+
   android_unmap_window (FRAME_ANDROID_WINDOW (f));
 
   SET_FRAME_VISIBLE (f, false);
@@ -584,6 +834,8 @@ android_free_frame_resources (struct frame *f)
 
   if (f == dpyinfo->focus_frame)
     dpyinfo->focus_frame = 0;
+  if (f == dpyinfo->x_focus_event_frame)
+    dpyinfo->x_focus_event_frame = 0;
   if (f == dpyinfo->highlight_frame)
     dpyinfo->highlight_frame = 0;
   if (f == hlinfo->mouse_face_mouse_frame)
@@ -783,7 +1035,7 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
       if (p->overlay_p)
        {
          clipmask = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
-                                                            1, 0, 0);
+                                                            1, 0, 1);
 
          gcv.clip_mask = clipmask;
          gcv.clip_x_origin = p->x;
@@ -1606,158 +1858,47 @@ android_draw_image_foreground (struct glyph_string *s)
 
   if (s->img->pixmap)
     {
-      if (s->img->mask)
-       {
-         unsigned long mask = (ANDROID_GC_CLIP_MASK
-                               | ANDROID_GC_CLIP_X_ORIGIN
-                               | ANDROID_GC_CLIP_Y_ORIGIN
-                               | ANDROID_GC_FUNCTION);
-         struct android_gc_values xgcv;
-         struct android_rectangle clip_rect, image_rect, r;
-
-         xgcv.clip_mask = s->img->mask;
-         xgcv.clip_x_origin = x - s->slice.x;
-         xgcv.clip_y_origin = y - s->slice.y;
-         xgcv.function = ANDROID_GC_COPY;
-         android_change_gc (s->gc, mask, &xgcv);
-
-         get_glyph_string_clip_rect (s, &clip_rect);
-         image_rect.x = x;
-         image_rect.y = y;
-         image_rect.width = s->slice.width;
-         image_rect.height = s->slice.height;
-
-         if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
-            android_copy_area (s->img->pixmap, FRAME_ANDROID_WINDOW (s->f),
-                              s->gc, s->slice.x + r.x - x,
-                              s->slice.y + r.y - y,
-                               r.x, r.y, r.width, r.height);
-       }
-      else
+      unsigned long mask = (ANDROID_GC_CLIP_MASK
+                           | ANDROID_GC_CLIP_X_ORIGIN
+                           | ANDROID_GC_CLIP_Y_ORIGIN
+                           | ANDROID_GC_FUNCTION);
+      struct android_gc_values xgcv;
+      struct android_rectangle clip_rect, image_rect, r;
+
+      xgcv.clip_mask = s->img->mask;
+      xgcv.clip_x_origin = x - s->slice.x;
+      xgcv.clip_y_origin = y - s->slice.y;
+      xgcv.function = ANDROID_GC_COPY;
+      android_change_gc (s->gc, mask, &xgcv);
+
+      get_glyph_string_clip_rect (s, &clip_rect);
+      image_rect.x = x;
+      image_rect.y = y;
+      image_rect.width = s->slice.width;
+      image_rect.height = s->slice.height;
+
+      if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
+       android_copy_area (s->img->pixmap,
+                          FRAME_ANDROID_WINDOW (s->f),
+                          s->gc, s->slice.x + r.x - x,
+                          s->slice.y + r.y - y,
+                          r.width, r.height, r.x, r.y);
+
+      /* When the image has a mask, we can expect that at least part
+        of a mouse highlight or a block cursor will be visible.  If
+        the image doesn't have a mask, make a block cursor visible by
+        drawing a rectangle around the image.  I believe it's looking
+        better if we do nothing here for mouse-face.  */
+      if (s->hl == DRAW_CURSOR && !s->img->mask)
        {
-         unsigned long mask = (ANDROID_GC_CLIP_MASK
-                               | ANDROID_GC_CLIP_X_ORIGIN
-                               | ANDROID_GC_CLIP_Y_ORIGIN
-                               | ANDROID_GC_FUNCTION);
-         struct android_gc_values xgcv;
-         struct android_rectangle clip_rect, image_rect, r;
-
-         xgcv.clip_mask = s->img->mask;
-         xgcv.clip_x_origin = x - s->slice.x;
-         xgcv.clip_y_origin = y - s->slice.y;
-         xgcv.function = ANDROID_GC_COPY;
-         android_change_gc (s->gc, mask, &xgcv);
-
-         get_glyph_string_clip_rect (s, &clip_rect);
-         image_rect.x = x;
-         image_rect.y = y;
-         image_rect.width = s->slice.width;
-         image_rect.height = s->slice.height;
-
-         if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
-            android_copy_area (s->img->pixmap,
-                              FRAME_ANDROID_WINDOW (s->f),
-                              s->gc, s->slice.x + r.x - x,
-                              s->slice.y + r.y - y,
-                               r.x, r.y, r.width, r.height);
-
-         /* When the image has a mask, we can expect that at
-            least part of a mouse highlight or a block cursor will
-            be visible.  If the image doesn't have a mask, make
-            a block cursor visible by drawing a rectangle around
-            the image.  I believe it's looking better if we do
-            nothing here for mouse-face.  */
-         if (s->hl == DRAW_CURSOR)
-           {
-             int relief = eabs (s->img->relief);
-             android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
-                                     x - relief, y - relief,
-                                     s->slice.width + relief*2 - 1,
-                                     s->slice.height + relief*2 - 1);
-           }
+         int relief = eabs (s->img->relief);
+         android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+                                 x - relief, y - relief,
+                                 s->slice.width + relief*2 - 1,
+                                 s->slice.height + relief*2 - 1);
        }
-    }
-  else
-    /* Draw a rectangle if image could not be loaded.  */
-    android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x, y,
-                           s->slice.width - 1, s->slice.height - 1);
-}
-
-/* Draw the foreground of image glyph string S to PIXMAP.  */
-
-static void
-android_draw_image_foreground_1 (struct glyph_string *s,
-                                android_pixmap pixmap)
-{
-  int x = 0;
-  int y = s->ybase - s->y - image_ascent (s->img, s->face, &s->slice);
-
-  /* If first glyph of S has a left box line, start drawing it to the
-     right of that line.  */
-  if (s->face->box != FACE_NO_BOX
-      && s->first_glyph->left_box_line_p
-      && s->slice.x == 0)
-    x += max (s->face->box_vertical_line_width, 0);
-
-  /* If there is a margin around the image, adjust x- and y-position
-     by that margin.  */
-  if (s->slice.x == 0)
-    x += s->img->hmargin;
-  if (s->slice.y == 0)
-    y += s->img->vmargin;
 
-  if (s->img->pixmap)
-    {
-      if (s->img->mask)
-       {
-         unsigned long mask = (ANDROID_GC_CLIP_MASK
-                               | ANDROID_GC_CLIP_X_ORIGIN
-                               | ANDROID_GC_CLIP_Y_ORIGIN
-                               | ANDROID_GC_FUNCTION);
-         struct android_gc_values xgcv;
-         struct android_rectangle clip_rect, image_rect, r;
-
-         xgcv.clip_mask = s->img->mask;
-         xgcv.clip_x_origin = x - s->slice.x;
-         xgcv.clip_y_origin = y - s->slice.y;
-         xgcv.function = ANDROID_GC_COPY;
-         android_change_gc (s->gc, mask, &xgcv);
-
-         get_glyph_string_clip_rect (s, &clip_rect);
-         image_rect.x = x;
-         image_rect.y = y;
-         image_rect.width = s->slice.width;
-         image_rect.height = s->slice.height;
-
-         if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
-           android_copy_area (s->img->pixmap, pixmap, s->gc,
-                              s->slice.x + r.x - x,
-                              s->slice.y + r.y - y,
-                               r.x, r.y, r.width, r.height);
-
-         android_set_clip_mask (s->gc, ANDROID_NONE);
-       }
-      else
-       {
-         android_copy_area (s->img->pixmap, pixmap, s->gc,
-                            s->slice.x, s->slice.y,
-                            s->slice.width, s->slice.height, x, y);
-
-         /* When the image has a mask, we can expect that at
-            least part of a mouse highlight or a block cursor will
-            be visible.  If the image doesn't have a mask, make
-            a block cursor visible by drawing a rectangle around
-            the image.  I believe it's looking better if we do
-            nothing here for mouse-face.  */
-         if (s->hl == DRAW_CURSOR)
-           {
-             int r = eabs (s->img->relief);
-             android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
-                                     s->gc, x - r, y - r,
-                                     s->slice.width + r*2 - 1,
-                                     s->slice.height + r*2 - 1);
-           }
-       }
+      android_set_clip_mask (s->gc, ANDROID_NONE);
     }
   else
     /* Draw a rectangle if image could not be loaded.  */
@@ -1771,7 +1912,6 @@ android_draw_image_glyph_string (struct glyph_string *s)
   int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
   int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
   int height;
-  android_pixmap pixmap = ANDROID_NONE;
 
   height = s->height;
   if (s->slice.y == 0)
@@ -1793,79 +1933,27 @@ android_draw_image_glyph_string (struct glyph_string *s)
       if (s->stippled_p)
        s->row->stipple_p = true;
 
-      if (s->img->mask)
-       {
-         /* Create a pixmap as large as the glyph string.  Fill it
-            with the background color.  Copy the image to it, using
-            its mask.  Copy the temporary pixmap to the display.  */
-         int depth = FRAME_DISPLAY_INFO (s->f)->n_planes;
-
-         /* Create a pixmap as large as the glyph string.  */
-          pixmap = android_create_pixmap (s->background_width,
-                                         s->height, depth);
-
-         /* Don't clip in the following because we're working on the
-            pixmap.  */
-         android_set_clip_mask (s->gc, ANDROID_NONE);
-
-         /* Fill the pixmap with the background color/stipple.  */
-         if (s->stippled_p)
-           {
-             /* Fill background with a stipple pattern.  */
-             android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
-             android_set_ts_origin (s->gc, - s->x, - s->y);
-             android_fill_rectangle (pixmap, s->gc,
-                                     0, 0, s->background_width, s->height);
-             android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
-             android_set_ts_origin (s->gc, 0, 0);
-           }
-         else
-           {
-             struct android_gc_values xgcv;
+      int x = s->x;
+      int y = s->y;
+      int width = s->background_width;
 
-             android_get_gc_values (s->gc, (ANDROID_GC_FOREGROUND
-                                            | ANDROID_GC_BACKGROUND),
-                                    &xgcv);
-             android_set_foreground (s->gc, xgcv.background);
-             android_fill_rectangle (pixmap, s->gc,
-                                     0, 0, s->background_width, s->height);
-             android_set_background (s->gc, xgcv.foreground);
-           }
-       }
-      else
+      if (s->first_glyph->left_box_line_p
+         && s->slice.x == 0)
        {
-         int x = s->x;
-         int y = s->y;
-         int width = s->background_width;
+         x += box_line_hwidth;
+         width -= box_line_hwidth;
+       }
 
-         if (s->first_glyph->left_box_line_p
-             && s->slice.x == 0)
-           {
-             x += box_line_hwidth;
-             width -= box_line_hwidth;
-           }
+      if (s->slice.y == 0)
+       y += box_line_vwidth;
 
-         if (s->slice.y == 0)
-           y += box_line_vwidth;
-
-         android_draw_glyph_string_bg_rect (s, x, y, width, height);
-       }
+      android_draw_glyph_string_bg_rect (s, x, y, width, height);
 
       s->background_filled_p = true;
     }
 
   /* Draw the foreground.  */
-  if (pixmap != ANDROID_NONE)
-    {
-      android_draw_image_foreground_1 (s, pixmap);
-      android_set_glyph_string_clipping (s);
-      android_copy_area (pixmap, FRAME_ANDROID_WINDOW (s->f), s->gc,
-                        0, 0, s->background_width, s->height, s->x,
-                        s->y);
-      android_free_pixmap (pixmap);
-    }
-  else
-    android_draw_image_foreground (s);
+  android_draw_image_foreground (s);
 
   /* If we must draw a relief around the image, do it.  */
   if (s->img->relief
@@ -3047,7 +3135,7 @@ android_create_terminal (struct android_display_info 
*dpyinfo)
   terminal->mouse_position_hook = android_mouse_position;
   terminal->get_focus_frame = android_get_focus_frame;
   terminal->focus_frame_hook = android_focus_frame;
-  terminal->frame_rehighlight_hook = android_frame_rehighlight;
+  terminal->frame_rehighlight_hook = android_frame_rehighlight_hook;
   terminal->frame_raise_lower_hook = android_frame_raise_lower;
   terminal->frame_visible_invisible_hook
     = android_make_frame_visible_invisible;
@@ -3117,9 +3205,12 @@ android_term_init (void)
 
   dpyinfo->color_map = color_map;
 
-  /* TODO! */
-  dpyinfo->resx = 96.0;
-  dpyinfo->resy = 96.0;
+#ifndef ANDROID_STUBIFY
+
+  dpyinfo->resx = android_pixel_density_x;
+  dpyinfo->resy = android_pixel_density_y;
+
+#endif
 
   /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html  */
   dpyinfo->smallest_font_height = 1;
@@ -3130,6 +3221,9 @@ android_term_init (void)
   /* The display "connection" is now set up, and it must never go
      away.  */
   terminal->reference_count = 30000;
+
+  /* Set the baud rate to the same value it gets set to on X.  */
+  baud_rate = 19200;
 }
 
 
diff --git a/src/androidterm.h b/src/androidterm.h
index 3a0c9f6055..c834ffb70e 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -28,8 +28,8 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 struct android_bitmap_record
 {
-  /* The image backing the bitmap.  */
-  Emacs_Pixmap img;
+  /* The image backing the bitmap and its mask.  */
+  android_pixmap pixmap, mask;
 
   /* The file from which it comes.  */
   char *file;
@@ -37,8 +37,11 @@ struct android_bitmap_record
   /* The number of references to it.  */
   int refcount;
 
-  /* The height and width.  */
-  int height, width;
+  /* The height and width and the depth.  */
+  int height, width, depth;
+
+  /* Whether or not there is a mask.  */
+  bool have_mask;
 };
 
 struct android_display_info
@@ -95,6 +98,9 @@ struct android_display_info
   /* The frame currently with the input focus.  */
   struct frame *focus_frame;
 
+  /* The last frame mentioned in a focus event.  */
+  struct frame *x_focus_event_frame;
+
   /* The frame which currently has the visual highlight, and should
      get keyboard input.  It points to the focus frame's selected
      window's frame, but can differ.  */
@@ -206,8 +212,24 @@ struct android_output
   /* The background for which the above relief GCs were set up.
      They are changed only when a different background is involved.  */
   unsigned long relief_background;
+
+  /* Focus state.  Only present for consistency with X; it is actually
+     a boolean.  */
+  int focus_state;
 };
 
+enum
+  {
+    /* Values for focus_state, used as bit mask.  EXPLICIT means we
+       received a FocusIn for the frame and know it has the focus.
+       IMPLICIT means we received an EnterNotify and the frame may
+       have the focus if no window manager is running.  FocusOut and
+       LeaveNotify clears EXPLICIT/IMPLICIT. */
+    FOCUS_NONE     = 0,
+    FOCUS_IMPLICIT = 1,
+    FOCUS_EXPLICIT = 2
+  };
+
 /* Return the Android output data for frame F.  */
 #define FRAME_ANDROID_OUTPUT(f)        ((f)->output_data.android)
 #define FRAME_OUTPUT_DATA(f)   ((f)->output_data.android)
diff --git a/src/dispextern.h b/src/dispextern.h
index e521dffc37..c9b3495934 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -160,8 +160,8 @@ typedef Emacs_Pixmap Emacs_Pix_Context;
 #ifdef HAVE_ANDROID
 #include "androidgui.h"
 typedef struct android_display_info Display_Info;
-typedef Emacs_Pixmap Emacs_Pix_Container;
-typedef Emacs_Pixmap Emacs_Pix_Context;
+typedef struct android_image *Emacs_Pix_Container;
+typedef struct android_image *Emacs_Pix_Context;
 #endif
 
 #ifdef HAVE_WINDOW_SYSTEM
@@ -3119,6 +3119,13 @@ struct image
   int original_width, original_height;
 # endif
 #endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+  /* Android images of the image, corresponding to the above Pixmaps.
+     Non-NULL means it and its Pixmap counterpart may be out of sync
+     and the latter is outdated.  NULL means the X image has been
+     synchronized to Pixmap.  */
+  struct android_image *ximg, *mask_img;
+#endif /* HAVE_ANDROID */
 #ifdef HAVE_NTGUI
   XFORM xform;
 #endif
diff --git a/src/emacs.c b/src/emacs.c
index 9ef58ca412..d3c53dd805 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1440,9 +1440,8 @@ main (int argc, char **argv)
   bool only_version = false;
   sort_args (argc, argv);
   old_argc = argc, argc = 0;
-  while (argv[argc]
-        /* Don't allow going past argv.  */
-        && argc < old_argc) argc++;
+  /* Don't allow going past argv.  */
+  while (argc < old_argc && argv[argc]) argc++;
 
   skip_args = 0;
   if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
diff --git a/src/fileio.c b/src/fileio.c
index 1d22c51eba..0f205283d7 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -898,6 +898,10 @@ user_homedir (char const *name)
   p[length] = 0;
   struct passwd *pw = getpwnam (p);
   SAFE_FREE ();
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  if (pw && !pw->pw_dir && pw->pw_uid == getuid ())
+    return (char *) android_get_home_directory ();
+#endif
   if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir)))
     return NULL;
   return pw->pw_dir;
@@ -1888,6 +1892,11 @@ get_homedir (void)
        pw = getpwuid (getuid ());
       if (pw)
        home = pw->pw_dir;
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+      if (!home && pw && pw->pw_uid == getuid ())
+       return android_get_home_directory ();
+#endif
       if (!home)
        return "";
     }
diff --git a/src/font.h b/src/font.h
index c1ab26b87c..1db94a9f3f 100644
--- a/src/font.h
+++ b/src/font.h
@@ -547,8 +547,14 @@ CHECK_FONT_GET_OBJECT (Lisp_Object x)
   return XFONT_OBJECT (x);
 }
 
+#ifndef HAVE_ANDROID
 /* Number of pt per inch (from the TeXbook).  */
 #define PT_PER_INCH 72.27
+#else
+/* Android uses this value instead to compensate for different device
+   dimensions.  */
+#define PT_PER_INCH 160.00
+#endif
 
 /* Return a pixel size (integer) corresponding to POINT size (double)
    on resolution DPI.  */
diff --git a/src/fringe.c b/src/fringe.c
index 5d7c8dca99..f10bc42423 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -1422,25 +1422,30 @@ If BITMAP overrides a standard fringe bitmap, the 
original bitmap is restored.
    On X, we bit-swap the built-in bitmaps and reduce bitmap
    from short to char array if width is <= 8 bits.
 
+   The Android port tries to follow X as closely as possible, so do
+   that there too.
+
    On MAC with big-endian CPU, we need to byte-swap each short.
 
    On W32 and MAC (little endian), there's no need to do this.
 */
 
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK)
-static const unsigned char swap_nibble[16] = {
-  0x0, 0x8, 0x4, 0xc,           /* 0000 1000 0100 1100 */
-  0x2, 0xa, 0x6, 0xe,           /* 0010 1010 0110 1110 */
-  0x1, 0x9, 0x5, 0xd,           /* 0001 1001 0101 1101 */
-  0x3, 0xb, 0x7, 0xf};          /* 0011 1011 0111 1111 */
-#endif                          /* HAVE_X_WINDOWS */
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK) || defined (HAVE_ANDROID)
+static const unsigned char swap_nibble[16] =
+  {
+    0x0, 0x8, 0x4, 0xc,           /* 0000 1000 0100 1100 */
+    0x2, 0xa, 0x6, 0xe,           /* 0010 1010 0110 1110 */
+    0x1, 0x9, 0x5, 0xd,           /* 0001 1001 0101 1101 */
+    0x3, 0xb, 0x7, 0xf,           /* 0011 1011 0111 1111 */
+  };
+#endif
 
 static void
 init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
 {
   if (once_p || fb->dynamic)
     {
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
       unsigned short *bits = fb->bits;
       int j;
 
@@ -1488,7 +1493,7 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, 
int once_p)
            }
        }
 #endif /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
 
 #if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
       unsigned short *bits = fb->bits;
diff --git a/src/image.c b/src/image.c
index 986dd7aada..08d092e2c6 100644
--- a/src/image.c
+++ b/src/image.c
@@ -177,11 +177,14 @@ typedef struct haiku_bitmap_record Bitmap_Record;
 
 #ifdef HAVE_ANDROID
 #include "androidterm.h"
+
 typedef struct android_bitmap_record Bitmap_Record;
 
-/* TODO: implement images on Android.  */
-#define GET_PIXEL(ximg, x, y)          0
-#define PUT_PIXEL(ximg, x, y, pixel)   ((void) (pixel))
+typedef struct android_image XImage;
+typedef android_pixmap Pixmap;
+
+#define GET_PIXEL(ximg, x, y)          android_get_pixel (ximg, x, y)
+#define PUT_PIXEL(ximg, x, y, pixel)    android_put_pixel (ximg, x, y, pixel)
 #define NO_PIXMAP                      0
 
 #define PIX_MASK_RETAIN        0
@@ -507,6 +510,18 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
     return -1;
 #endif /* HAVE_X_WINDOWS */
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  android_pixmap bitmap;
+
+  bitmap = android_create_bitmap_from_data (bits, width, height);
+
+  if (!bitmap)
+    return -1;
+#else
+  ((void) dpyinfo);
+  emacs_abort ();
+#endif
+
 #ifdef HAVE_NTGUI
   Lisp_Object frame UNINIT;    /* The value is not used.  */
   Emacs_Pixmap bitmap;
@@ -619,14 +634,14 @@ image_create_bitmap_from_data (struct frame *f, char 
*bits,
   dpyinfo->bitmaps[id - 1].width = width;
   dpyinfo->bitmaps[id - 1].refcount = 1;
 
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS && defined HAVE_ANDROID
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
   dpyinfo->bitmaps[id - 1].have_mask = false;
   dpyinfo->bitmaps[id - 1].depth = 1;
 #ifdef USE_CAIRO
   dpyinfo->bitmaps[id - 1].stipple = NULL;
 #endif /* USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
 
 #ifdef HAVE_NTGUI
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
@@ -637,7 +652,7 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
   return id;
 }
 
-#if defined HAVE_HAIKU || defined HAVE_NS
+#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_ANDROID
 static char *slurp_file (int, ptrdiff_t *);
 static Lisp_Object image_find_image_fd (Lisp_Object, int *);
 static bool xbm_read_bitmap_data (struct frame *, char *, char *,
@@ -861,8 +876,62 @@ image_create_bitmap_from_file (struct frame *f, 
Lisp_Object file)
   /* This function should never be called when building stubs.  */
   emacs_abort ();
 #else
-  /* you lose, not yet implemented TODO */
-  return 0;
+  ptrdiff_t id, size;
+  int fd, width, height, rc;
+  char *contents, *data;
+  Lisp_Object found;
+  android_pixmap bitmap;
+
+  /* Look for an existing bitmap with the same name.  */
+  for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+    {
+      if (dpyinfo->bitmaps[id].refcount
+         && dpyinfo->bitmaps[id].file
+         && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file)))
+       {
+         ++dpyinfo->bitmaps[id].refcount;
+         return id + 1;
+       }
+    }
+
+  /* Search bitmap-file-path for the file, if appropriate.  */
+  if (openp (Vx_bitmap_file_path, file, Qnil, &found,
+            make_fixnum (R_OK), false, false)
+      < 0)
+    return -1;
+
+  if (!STRINGP (image_find_image_fd (file, &fd))
+      && !STRINGP (image_find_image_fd (found, &fd)))
+    return -1;
+
+  contents = slurp_file (fd, &size);
+
+  if (!contents)
+    return -1;
+
+  rc = xbm_read_bitmap_data (f, contents, contents + size,
+                            &width, &height, &data, 0);
+
+  if (!rc)
+    {
+      xfree (contents);
+      return -1;
+    }
+
+  xfree (contents);
+  bitmap = android_create_bitmap_from_data (data, width, height);
+  xfree (data);
+
+  id = image_allocate_bitmap_record (f);
+  dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].have_mask = false;
+  dpyinfo->bitmaps[id - 1].refcount = 1;
+  dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+  dpyinfo->bitmaps[id - 1].depth = 1;
+  dpyinfo->bitmaps[id - 1].height = height;
+  dpyinfo->bitmaps[id - 1].width = width;
+
+  return id;
 #endif
 #endif
 }
@@ -882,6 +951,13 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record 
*bm)
 #endif /* USE_CAIRO */
 #endif /* HAVE_X_WINDOWS */
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  android_free_pixmap (bm->pixmap);
+
+  if (bm->have_mask)
+    android_free_pixmap (bm->pixmap);
+#endif
+
 #ifdef HAVE_NTGUI
   DeleteObject (bm->pixmap);
 #endif /* HAVE_NTGUI */
@@ -969,7 +1045,7 @@ static void image_unget_x_image (struct image *, bool, 
Emacs_Pix_Container);
   image_unget_x_image (img, mask_p, ximg)
 #endif
 
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
 
 #ifndef USE_CAIRO
 static void image_sync_to_pixmaps (struct frame *, struct image *);
@@ -983,6 +1059,8 @@ static bool x_create_x_image_and_pixmap (struct frame *, 
int, int, int,
                                         XImage **, Pixmap *);
 static void x_destroy_x_image (XImage *);
 
+#if defined HAVE_X_WINDOWS
+
 /* Create a mask of a bitmap. Note is this not a perfect mask.
    It's nicer with some borders in this context */
 
@@ -1079,7 +1157,9 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
   x_destroy_x_image (mask_img);
 }
 
-#endif /* HAVE_X_WINDOWS */
+#endif
+
+#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/
 
 /***********************************************************************
                            Image types
@@ -1113,7 +1193,7 @@ struct image_type
 #if defined HAVE_RSVG || defined HAVE_PNG || defined HAVE_GIF || \
   defined HAVE_TIFF || defined HAVE_JPEG || defined HAVE_XPM || \
   defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK || \
-  defined HAVE_WEBP
+  defined HAVE_WEBP || defined HAVE_ANDROID
 # ifdef WINDOWSNT
 #  define IMAGE_TYPE_INIT(f) f
 # else
@@ -1615,7 +1695,7 @@ prepare_image_for_display (struct frame *f, struct image 
*img)
        }
       unblock_input ();
     }
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   if (!img->load_failed_p)
     {
       block_input ();
@@ -1822,7 +1902,7 @@ image_clear_image_1 (struct frame *f, struct image *img, 
int flags)
          /* NOTE (HAVE_NS): background color is NOT an indexed color! */
          img->background_valid = 0;
        }
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
       if (img->ximg)
        {
          image_destroy_x_image (img->ximg);
@@ -1840,7 +1920,7 @@ image_clear_image_1 (struct frame *f, struct image *img, 
int flags)
          img->mask = NO_PIXMAP;
          img->background_transparent_valid = 0;
        }
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
       if (img->mask_img)
        {
          image_destroy_x_image (img->mask_img);
@@ -2212,11 +2292,11 @@ image_size_in_bytes (struct image *img)
   if (msk)
     size += msk->height * msk->bytes_per_line;
 
-#elif defined HAVE_X_WINDOWS
-  /* Use a nominal depth of 24 bpp for pixmap and 1 bpp for mask,
-     to avoid having to query the server. */
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+  /* Use a nominal depth of 24 and a bpp of 32 for pixmap and 1 bpp
+     for mask, to avoid having to query the server. */
   if (img->pixmap != NO_PIXMAP)
-    size += img->width * img->height * 3;
+    size += img->width * img->height * 4;
   if (img->mask != NO_PIXMAP)
     size += img->width * img->height / 8;
 
@@ -3195,9 +3275,12 @@ mark_image_cache (struct image_cache *c)
 
 /***********************************************************************
                          X / NS / W32 support code
+             Most of this code is shared with Android to make
+             it easier to maintain.
  ***********************************************************************/
 
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+
 static bool
 x_check_image_size (XImage *ximg, int width, int height)
 {
@@ -3213,7 +3296,11 @@ x_check_image_size (XImage *ximg, int width, int height)
   int bitmap_pad, depth, bytes_per_line;
   if (ximg)
     {
+#ifndef HAVE_ANDROID
       bitmap_pad = ximg->bitmap_pad;
+#else
+      bitmap_pad = (ximg->depth == 1 ? 8 : 32);
+#endif
       depth = ximg->depth;
       bytes_per_line = ximg->bytes_per_line;
     }
@@ -3231,16 +3318,23 @@ static bool
 x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
                             XImage **ximg, Pixmap *pixmap)
 {
+#ifndef HAVE_ANDROID
   Display *display = FRAME_X_DISPLAY (f);
   Drawable drawable = FRAME_X_DRAWABLE (f);
+#endif
 
   eassert (input_blocked_p ());
 
   if (depth <= 0)
     depth = FRAME_DISPLAY_INFO (f)->n_planes;
+#ifndef HAVE_ANDROID
   *ximg = XCreateImage (display, FRAME_X_VISUAL (f),
                        depth, ZPixmap, 0, NULL, width, height,
                        depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
+#else
+  *ximg = android_create_image (depth, ANDROID_Z_PIXMAP, NULL, width,
+                               height);
+#endif
   if (*ximg == NULL)
     {
       image_error ("Unable to allocate X image");
@@ -3260,7 +3354,15 @@ x_create_x_image_and_pixmap (struct frame *f, int width, 
int height, int depth,
   (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);
 
   /* Allocate a pixmap of the same size.  */
+#ifndef HAVE_ANDROID
   *pixmap = XCreatePixmap (display, drawable, width, height, depth);
+#else
+#ifndef ANDROID_STUBIFY
+  *pixmap = android_create_pixmap (width, height, depth);
+#else
+  emacs_abort ();
+#endif
+#endif
   if (*pixmap == NO_PIXMAP)
     {
       x_destroy_x_image (*ximg);
@@ -3281,7 +3383,11 @@ x_destroy_x_image (XImage *ximg)
       ximg->data = NULL;
     }
 
+#ifndef HAVE_ANDROID
   XDestroyImage (ximg);
+#else
+  android_destroy_image (ximg);
+#endif
 }
 
 # if !defined USE_CAIRO && defined HAVE_XRENDER
@@ -3348,7 +3454,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap 
pixmap, int depth)
 static bool
 image_check_image_size (Emacs_Pix_Container ximg, int width, int height)
 {
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
   return x_check_image_size (ximg, width, height);
 #else
   /* FIXME: Implement this check for the HAVE_NS and HAVE_NTGUI cases.
@@ -3372,16 +3478,6 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int 
width, int height, int d
                                    Emacs_Pix_Container *pimg,
                                    Emacs_Pixmap *pixmap, Picture *picture)
 {
-#ifdef HAVE_ANDROID
-#ifdef ANDROID_STUBIFY
-  /* This function should never be called when building stubs.  */
-  emacs_abort ();
-#else
-  /* you lose, not yet implemented TODO */
-  return false;
-#endif
-#endif
-
 #ifdef USE_CAIRO
   eassert (input_blocked_p ());
 
@@ -3396,7 +3492,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int 
width, int height, int d
 
   *pimg = *pixmap;
   return 1;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap))
     return 0;
 # ifdef HAVE_XRENDER
@@ -3542,7 +3638,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int 
width, int height, int d
 static void
 image_destroy_x_image (Emacs_Pix_Container pimg)
 {
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
   x_destroy_x_image (pimg);
 #else
   eassert (input_blocked_p ());
@@ -3581,7 +3677,9 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container 
pimg,
   XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, pimg, 0, 0, 0, 0,
              pimg->width, pimg->height);
   XFreeGC (FRAME_X_DISPLAY (f), gc);
-#endif /* HAVE_X_WINDOWS */
+#elif defined HAVE_ANDROID
+  android_put_image (pixmap, pimg);
+#endif
 
 #ifdef HAVE_NS
   eassert (pimg == pixmap);
@@ -3618,7 +3716,7 @@ static void
 image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container 
ximg,
                   bool mask_p)
 {
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
   if (!mask_p)
     {
       eassert (img->ximg == NULL);
@@ -3636,7 +3734,7 @@ image_put_x_image (struct frame *f, struct image *img, 
Emacs_Pix_Container ximg,
 #endif
 }
 
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
 /* Put the X images recorded in IMG on frame F into pixmaps, then free
    the X images and their buffers.  */
 
@@ -3690,19 +3788,9 @@ image_unget_x_image_or_dc (struct image *img, bool 
mask_p,
 static Emacs_Pix_Container
 image_get_x_image (struct frame *f, struct image *img, bool mask_p)
 {
-#ifdef HAVE_ANDROID
-#ifdef ANDROID_STUBIFY
-  /* This function should never be called when building stubs.  */
-  emacs_abort ();
-#else
-  /* you lose, not yet implemented TODO */
-  return 0;
-#endif
-#endif
-
 #if defined USE_CAIRO || defined (HAVE_HAIKU)
   return !mask_p ? img->pixmap : img->mask;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
 
   if (ximg_in_img)
@@ -3712,9 +3800,15 @@ image_get_x_image (struct frame *f, struct image *img, 
bool mask_p)
     return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
                      0, 0, img->original_width, img->original_height, ~0, 
ZPixmap);
 #endif
+#ifndef HAVE_ANDROID
   else
     return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
                      0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+  else
+    return android_get_image (!mask_p ? img->pixmap : img->mask,
+                             ANDROID_Z_PIXMAP);
+#endif
 #elif defined (HAVE_NS)
   Emacs_Pix_Container pixmap = !mask_p ? img->pixmap : img->mask;
 
@@ -3727,13 +3821,18 @@ static void
 image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
 {
 #ifdef USE_CAIRO
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
 
   if (ximg_in_img)
     eassert (ximg == ximg_in_img);
+#ifdef HAVE_ANDROID
+  else
+    android_destroy_image (ximg);
+#else
   else
     XDestroyImage (ximg);
+#endif
 #elif defined (HAVE_NS)
   ns_release_object (ximg);
 #endif
@@ -4256,6 +4355,15 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct 
image *img, char *data,
     img->picture = x_create_xrender_picture (f, img->pixmap, 0);
 # endif
 
+#elif defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+  img->pixmap
+    = android_create_pixmap_from_bitmap_data (data, img->width, img->height,
+                                             fg, bg,
+                                             FRAME_DISPLAY_INFO (f)->n_planes);
+#else
+  emacs_abort ();
+#endif
 #elif defined HAVE_NTGUI
   img->pixmap
     = w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
@@ -4686,7 +4794,8 @@ xbm_load (struct frame *f, struct image *img)
                              XPM images
  ***********************************************************************/
 
-#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) \
+  || defined (HAVE_ANDROID)
 
 static bool xpm_image_p (Lisp_Object object);
 static bool xpm_load (struct frame *f, struct image *img);
@@ -4716,7 +4825,8 @@ static bool xpm_load (struct frame *f, struct image *img);
 #endif /* not HAVE_NTGUI */
 #endif /* HAVE_XPM */
 
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined 
HAVE_HAIKU
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS   \
+  || defined HAVE_HAIKU || defined HAVE_ANDROID
 
 /* Indices of image specification fields in xpm_format, below.  */
 
@@ -4736,7 +4846,8 @@ enum xpm_keyword_index
   XPM_LAST
 };
 
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined 
HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU  \
+  || defined HAVE_PGTK || defined HAVE_ANDROID
 /* Vector of image_keyword structures describing the format
    of valid XPM image specifications.  */
 
@@ -4978,7 +5089,8 @@ init_xpm_functions (void)
 
 #endif /* WINDOWSNT */
 
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined 
HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU  \
+  || defined HAVE_PGTK || defined HAVE_ANDROID
 /* Value is true if COLOR_SYMBOLS is a valid color symbols list
    for XPM images.  Such a list must consist of conses whose car and
    cdr are strings.  */
@@ -5014,9 +5126,9 @@ xpm_image_p (Lisp_Object object)
          && (! fmt[XPM_COLOR_SYMBOLS].count
              || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
 }
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK || HAVE_ANDROID */
 
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
 
 #if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
 ptrdiff_t
@@ -5389,10 +5501,12 @@ xpm_load (struct frame *f, struct image *img)
 #if (defined USE_CAIRO && defined HAVE_XPM)    \
   || (defined HAVE_NS && !defined HAVE_XPM)    \
   || (defined HAVE_HAIKU && !defined HAVE_XPM)  \
-  || (defined HAVE_PGTK && !defined HAVE_XPM)
+  || (defined HAVE_PGTK && !defined HAVE_XPM)  \
+  || (defined HAVE_ANDROID && !defined HAVE_XPM)
 
-/* XPM support functions for NS and Haiku where libxpm is not available, and 
for
-   Cairo.  Only XPM version 3 (without any extensions) is supported.  */
+/* XPM support functions for NS, Haiku and Android where libxpm is not
+   available, and for Cairo.  Only XPM version 3 (without any
+   extensions) is supported.  */
 
 static void xpm_put_color_table_v (Lisp_Object, const char *,
                                    int, Lisp_Object);
@@ -6492,11 +6606,16 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap 
pixmap,
 #elif HAVE_HAIKU
   be_draw_cross_on_pixmap (pixmap, x, y, width, height, color);
 #elif HAVE_ANDROID
-#ifdef ANDROID_STUBIFY
-  /* This function should never be called when building stubs.  */
-  emacs_abort ();
+#ifndef ANDROID_STUBIFY
+  struct android_gc *gc;
+
+  gc = android_create_gc (0, NULL);
+  android_set_foreground (gc, color);
+  android_draw_line (pixmap, gc, x, y, x + width - 1, y + height - 1);
+  android_draw_line (pixmap, gc, x, y + height - 1, x + width - 1, y);
+  android_free_gc (gc);
 #else
-  /* you lose, not yet implemented TODO */
+  emacs_abort ();
 #endif
 #endif
 }
@@ -6552,7 +6671,7 @@ image_disable_image (struct frame *f, struct image *img)
 #define MaskForeground(f)  PIX_MASK_DRAW
 #endif /* USE_CAIRO || HAVE_HAIKU */
 
-#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
+#if !defined USE_CAIRO && !defined HAVE_HAIKU
       image_sync_to_pixmaps (f, img);
 #endif /* !USE_CAIRO && !HAVE_HAIKU */
       image_pixmap_draw_cross (f, img->pixmap, 0, 0, img->width, img->height,
@@ -12079,7 +12198,8 @@ static struct image_type const image_types[] =
  { SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
    IMAGE_TYPE_INIT (init_jpeg_functions) },
 #endif
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined 
HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU  \
+  || defined HAVE_PGTK || defined HAVE_ANDROID
  { SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
    IMAGE_TYPE_INIT (init_xpm_functions) },
 #endif
@@ -12138,7 +12258,7 @@ syms_of_image (void)
   DEFVAR_LISP ("image-types", Vimage_types,
     doc: /* List of potentially supported image types.
 Each element of the list is a symbol for an image type, like `jpeg' or `png'.
-To check whether it is really supported, use `image-type-available-p'.  */);
+ check whether it is really supported, use `image-type-available-p'.  */);
   Vimage_types = Qnil;
 
   DEFVAR_LISP ("max-image-size", Vmax_image_size,
@@ -12241,7 +12361,8 @@ non-numeric, there is no explicit limit on the size of 
images.  */);
   add_image_type (Qxbm);
 
 #if defined (HAVE_XPM) || defined (HAVE_NS) \
-  || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+  || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+  || defined (HAVE_ANDROID)
   DEFSYM (Qxpm, "xpm");
   add_image_type (Qxpm);
 #endif
diff --git a/src/keyboard.c b/src/keyboard.c
index 7bf89ac7d4..8eec833a9c 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4909,7 +4909,58 @@ static const char *const lispy_accent_keys[] =
   "dead-horn",
 };
 
-#ifdef HAVE_NTGUI
+#ifdef HAVE_ANDROID
+#define FUNCTION_KEY_OFFSET 0
+
+const char *const lispy_function_keys[] =
+  {
+    /* All elements in this array default to 0, except for the few
+       function keys that Emacs recognizes.  */
+    [111] = "escape",
+    [121] = "break",
+    [122] = "home",
+    [123] = "end",
+    [124] = "insert",
+    [131] = "f1",
+    [132] = "f2",
+    [133] = "f3",
+    [134] = "f4",
+    [135] = "f5",
+    [136] = "f6",
+    [137] = "f7",
+    [138] = "f8",
+    [139] = "f9",
+    [140] = "f10",
+    [141] = "f11",
+    [142] = "f12",
+    [160] = "kp-ret",
+    [19]  = "up",
+    [20]  = "down",
+    [213] = "muhenkan",
+    [214] = "henkan",
+    [215] = "hiragana-katakana",
+    [218] = "kana",
+    [21]  = "left",
+    [22]  = "right",
+    [259] = "help",
+    [268] = "kp-up-left",
+    [269] = "kp-down-left",
+    [270] = "kp-up-right",
+    [271] = "kp-down-right",
+    [277] = "cut",
+    [278] = "copy",
+    [279] = "paste",
+    [28]  = "clear",
+    [4]          = "back",
+    [61]  = "tab",
+    [66]  = "return",
+    [67]  = "backspace",
+    [82]  = "menu",
+    [92]  = "page-up",
+    [93]  = "page-down",
+  };
+
+#elif defined HAVE_NTGUI
 #define FUNCTION_KEY_OFFSET 0x0
 
 const char *const lispy_function_keys[] =
diff --git a/src/lread.c b/src/lread.c
index eb029f0162..15273ad34f 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -2073,7 +2073,7 @@ static void
 build_load_history (Lisp_Object filename, bool entire)
 {
   Lisp_Object tail, prev, newelt;
-  Lisp_Object tem, tem2;
+  Lisp_Object tem, tem2, association;
   bool foundit = 0;
 
   tail = Vload_history;
@@ -2088,7 +2088,7 @@ build_load_history (Lisp_Object filename, bool entire)
        {
          foundit = 1;
 
-         /*  If we're loading the entire file, remove old data.  */
+         /* If we're loading the entire file, remove old data.  */
          if (entire)
            {
              if (NILP (prev))
@@ -2096,8 +2096,8 @@ build_load_history (Lisp_Object filename, bool entire)
              else
                Fsetcdr (prev, XCDR (tail));
            }
-
-         /*  Otherwise, cons on new symbols that are not already members.  */
+         /* Otherwise, cons on new symbols that are not already
+            members.  */
          else
            {
              tem2 = Vcurrent_load_list;
@@ -2122,8 +2122,16 @@ build_load_history (Lisp_Object filename, bool entire)
      front of load-history, the most-recently-loaded position.  Also
      do this if we didn't find an existing member for the file.  */
   if (entire || !foundit)
-    Vload_history = Fcons (Fnreverse (Vcurrent_load_list),
-                          Vload_history);
+    {
+      association = Fnreverse (Vcurrent_load_list);
+
+      if (!NILP (association) && STRINGP (XCAR (association)))
+       /* readevalloop can be called with SOURCENAME set to some
+          nonsense value, meaning the car of ASSOCIATION will be nil
+          (or worse something else), leading to an invalid
+          Vload_history.  Ignore such invalid entries.  */
+       Vload_history = Fcons (association, Vload_history);
+    }
 }
 
 static void



reply via email to

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