From df5a9a78b51f2f42d2dbb010e811a239fc014732 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Mon, 25 Sep 2023 13:01:44 +0800
Subject: [PATCH] Update Android port

* doc/lispref/os.texi (Desktop Notifications): Revise
documentation for android-notifications-notify to reflect
changes.

* java/org/gnu/emacs/EmacsDesktopNotification.java (display1):
Convert notification importance to a legacy priority between
Android 7.1 and 4.1.

* java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Remove
immutable bitmap constructor, as the underlying Android API
functions are erroneously implemented.

* src/android.c (android_init_emacs_pixmap): Cease searching for
deleted constructor.
(android_create_pixmap_from_bitmap_data): Create a pixmap, then
fill it with the contents of the bitmap, in lieu of employing
the aforementioned constructor.

* src/androidselect.c (Fandroid_notifications_notify): Revise
doc string.
---
 doc/lispref/os.texi                           |  18 ++-
 .../gnu/emacs/EmacsDesktopNotification.java   |  37 ++++-
 java/org/gnu/emacs/EmacsPixmap.java           |  33 -----
 src/android.c                                 | 129 +++++++++---------
 src/androidselect.c                           |  18 ++-
 5 files changed, 124 insertions(+), 111 deletions(-)

diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 3c704970cda..5400d492f0a 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -3220,15 +3220,25 @@ These have the same meaning as they do when used in calls to
 @code{notifications-notify}.
 
 @item :urgency @var{urgency}
+The set of values for @var{urgency} is the same as with
+@code{notifications-notify}, but the urgency applies to all
+notifications displayed with the defined @var{group}, except under
+Android 7.1 and earlier.
+
 @item :group @var{group}
-These two parameters are ignored under Android 7.1 and earlier
-versions of the system.  The set of values for @var{urgency} is the
-same as with @code{notifications-notify}, but the urgency applies to
-all notifications displayed with the defined @var{group}.
+@var{group} is a string that designates a category to which the
+notification sent will belong.  This category is reproduced within the
+system's notification settings menus, but is ignored under Android 7.1
+and earlier.
 
 If @var{group} is nil or not present within @var{params}, it is
 replaced by the string @samp{"Desktop Notifications"}.
 
+Callers should provide one stable combination of @var{urgency} and
+@var{group} for each kind of notification they send, given that the
+system may elect to disregard @var{urgency} if it does not match that
+of any notification previously delivered to @var{group}.
+
 @item :icon @var{icon}
 This parameter controls the symbolic icon the notification will be
 displayed with.  Its value is a string designating an icon within the
diff --git a/java/org/gnu/emacs/EmacsDesktopNotification.java b/java/org/gnu/emacs/EmacsDesktopNotification.java
index 121d8f481a2..56ed984d497 100644
--- a/java/org/gnu/emacs/EmacsDesktopNotification.java
+++ b/java/org/gnu/emacs/EmacsDesktopNotification.java
@@ -96,6 +96,7 @@ public final class EmacsDesktopNotification
     RemoteViews contentView;
     Intent intent;
     PendingIntent pending;
+    int priority;
 
     tem = context.getSystemService (Context.NOTIFICATION_SERVICE);
     manager = (NotificationManager) tem;
@@ -116,11 +117,37 @@ public final class EmacsDesktopNotification
 			.build ());
       }
     else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
-      notification = (new Notification.Builder (context)
-		      .setContentTitle (title)
-		      .setContentText (content)
-		      .setSmallIcon (icon)
-		      .build ());
+      {
+	/* Android 7.1 and earlier don't segregate notifications into
+	   distinct categories, but permit an importance to be
+	   assigned to each individual notification.  */
+
+	switch (importance)
+	  {
+	  case 2: /* IMPORTANCE_LOW */
+	  default:
+	    priority = Notification.PRIORITY_LOW;
+	    break;
+
+	  case 3: /* IMPORTANCE_DEFAULT */
+	    priority = Notification.PRIORITY_DEFAULT;
+	    break;
+
+	  case 4: /* IMPORTANCE_HIGH */
+	    priority = Notification.PRIORITY_HIGH;
+	    break;
+	  }
+	
+	notification = (new Notification.Builder (context)
+			.setContentTitle (title)
+			.setContentText (content)
+			.setSmallIcon (icon)
+			.setPriority (priority)
+			.build ());
+
+	if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN)
+	  notification.priority = priority;
+      }
     else
       {
 	notification = new Notification ();
diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java
index e02699ecba7..fa6e61c15a5 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -50,39 +50,6 @@ public final class EmacsPixmap extends EmacsHandleObject
      changed.  */
   private long gcClipRectID;
 
-  public
-  EmacsPixmap (short handle, int colors[], 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 (colors, width, height,
-				      Bitmap.Config.ALPHA_8);
-	break;
-
-      case 24:
-	bitmap = Bitmap.createBitmap (colors, width, height,
-				      Bitmap.Config.ARGB_8888);
-	bitmap.setHasAlpha (false);
-	break;
-      }
-
-    this.width = width;
-    this.height = height;
-    this.depth = depth;
-
-    /* The immutable bitmap constructor is only leveraged to create
-       small fringe bitmaps.  */
-    this.needCollect = false;
-  }
-
   public
   EmacsPixmap (short handle, int width, int height, int depth)
   {
diff --git a/src/android.c b/src/android.c
index 0996a84823d..4df9d71b1b1 100644
--- a/src/android.c
+++ b/src/android.c
@@ -73,7 +73,6 @@ bool android_init_gui;
 struct android_emacs_pixmap
 {
   jclass class;
-  jmethodID constructor;
   jmethodID constructor_mutable;
 };
 
@@ -1649,7 +1648,6 @@ android_init_emacs_pixmap (void)
 					name, signature);	\
   assert (pixmap_class.c_name);
 
-  FIND_METHOD (constructor, "<init>", "(S[IIII)V");
   FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
 
 #undef FIND_METHOD
@@ -3404,86 +3402,91 @@ android_create_pixmap_from_bitmap_data (char *data, unsigned int width,
 					unsigned long background,
 					unsigned int depth)
 {
-  android_handle prev_max_handle;
-  jobject object;
-  jintArray colors;
   android_pixmap pixmap;
+  jobject object;
+  AndroidBitmapInfo info;
+  unsigned int *depth_24;
+  unsigned char *depth_8;
+  void *bitmap_data;
   unsigned int x, y;
-  jint *region;
+  unsigned int r, g, b;
 
-  USE_SAFE_ALLOCA;
+  /* Create a pixmap with the right dimensions and depth.  */
+  pixmap = android_create_pixmap (width, height, depth);
 
-  /* Create the color array holding the data.  */
-  colors = (*android_java_env)->NewIntArray (android_java_env,
-					     width * height);
-  android_exception_check ();
+  /* Lock the bitmap data.  */
+  bitmap_data = android_lock_bitmap (pixmap, &info, &object);
+
+  /* Merely return if locking the bitmap fails.  */
+  if (!bitmap_data)
+    return pixmap;
 
-  SAFE_NALLOCA (region, sizeof *region, width);
+  eassert (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888
+	   || info.format == ANDROID_BITMAP_FORMAT_A_8);
 
-  for (y = 0; y < height; ++y)
+  /* Begin copying each line.  */
+
+  switch (info.format)
     {
-      for (x = 0; x < width; ++x)
+    case ANDROID_BITMAP_FORMAT_RGBA_8888:
+
+      /* Swizzle the pixels 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 = background & 0x00ff0000;
+      g = background & 0x0000ff00;
+      b = background & 0x000000ff;
+      background = (r >> 16) | g | (b << 16) | 0xff000000;
+      r = foreground & 0x00ff0000;
+      g = foreground & 0x0000ff00;
+      b = foreground & 0x000000ff;
+      foreground = (r >> 16) | g | (b << 16) | 0xff000000;
+
+      for (y = 0; y < height; ++y)
 	{
-	  if (depth == 24)
-	    {
-	      /* The alpha channels must be set, or otherwise, the
-		 pixmap will be created entirely transparent.  */
+	  depth_24 = (void *) ((char *) bitmap_data + y * info.stride);
 
-	      if (data[x / 8] & (1 << (x % 8)))
-		region[x] = foreground | 0xff000000;
-	      else
-		region[x] = background | 0xff000000;
-	    }
-	  else
-	    {
-	      if (data[x / 8] & (1 << (x % 8)))
-		region[x] = foreground;
-	      else
-		region[x] = background;
-	    }
+	  for (x = 0; x < width; ++x)
+	    depth_24[x] = ((data[x / 8] & (1 << (x % 8)))
+			   ? foreground : background);
+
+	  data += (width + 7) / 8;
 	}
 
-      (*android_java_env)->SetIntArrayRegion (android_java_env,
-					      colors,
-					      width * y, width,
-					      region);
-      data += width / 8;
-    }
+      break;
 
-  /* First, allocate the pixmap handle.  */
-  prev_max_handle = max_handle;
-  pixmap = android_alloc_id ();
+    case ANDROID_BITMAP_FORMAT_A_8:
 
-  if (!pixmap)
-    {
-      ANDROID_DELETE_LOCAL_REF ((jobject) colors);
-      error ("Out of pixmap handles!");
-    }
+      /* 8-bit pixmaps are created, but in spite of that they are
+	 employed only to represent bitmaps.  */
 
-  object = (*android_java_env)->NewObject (android_java_env,
-					   pixmap_class.class,
-					   pixmap_class.constructor,
-					   (jshort) pixmap, colors,
-					   (jint) width, (jint) height,
-					   (jint) depth);
-  (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+      foreground = (foreground ? 255 : 0);
+      background = (background ? 255 : 0);
 
-  if (!object)
-    {
-      max_handle = prev_max_handle;
-      memory_full (0);
+      for (y = 0; y < height; ++y)
+	{
+	  depth_8 = (void *) ((char *) bitmap_data + y * info.stride);
+
+	  for (x = 0; x < width; ++x)
+	    depth_8[x] = ((data[x / 8] & (1 << (x % 8)))
+			  ? foreground : background);
+
+	  data += (width + 7) / 8;
+	}
+
+      break;
+
+    default:
+      emacs_abort ();
     }
 
-  android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
-  android_handles[pixmap].handle
-    = (*android_java_env)->NewGlobalRef (android_java_env, object);
+  /* Unlock the bitmap itself.  */
+  AndroidBitmap_unlockPixels (android_java_env, object);
   ANDROID_DELETE_LOCAL_REF (object);
 
-  if (!android_handles[pixmap].handle)
-    memory_full (0);
-
-  SAFE_FREE ();
+  /* Return the pixmap.  */
   return pixmap;
 }
 
diff --git a/src/androidselect.c b/src/androidselect.c
index 5735eda2dd5..cf2265d4cf4 100644
--- a/src/androidselect.c
+++ b/src/androidselect.c
@@ -660,12 +660,18 @@ keywords is understood:
   :icon         The name of a drawable resource to display as the
                 notification's icon.
 
-The notification group and urgency are ignored on Android 7.1 and
-earlier versions of Android.  Outside such older systems, it
-identifies a category that will be displayed in the system Settings
-menu.  The urgency provided always extends to affect all notifications
-displayed within that category.  If the group is not provided, it
-defaults to the string "Desktop Notifications".
+The notification group is ignored on Android 7.1 and earlier versions
+of Android.  Outside such older systems, it identifies a category that
+will be displayed in the system Settings menu, and the urgency
+provided always extends to affect all notifications displayed within
+that category.  If the group is not provided, it defaults to the
+string "Desktop Notifications".
+
+Each caller should strive to provide one unchanging combination of
+notification group and urgency for each kind of notification it sends,
+inasmuch as the system may, subject to user configuration, disregard
+the urgency specified within a notification, should it not be the
+first notification sent to its notification group.
 
 The provided icon should be the name of a "drawable resource" present
 within the "android.R.drawable" class designating an icon with a
-- 
2.39.5