]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement `fullscreen' on Android 4.0 and later
authorPo Lu <luangruo@yahoo.com>
Sun, 19 Feb 2023 05:17:43 +0000 (13:17 +0800)
committerPo Lu <luangruo@yahoo.com>
Sun, 19 Feb 2023 05:17:43 +0000 (13:17 +0800)
* doc/emacs/android.texi (Android Windowing): Document what new
frame parameters are now supported.
* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
field `isFullscreen'.
(detachWindow, attachWindow): Sync fullscreen state.
(onWindowFocusChanged): Add more logging.
(onResume): Restore previous fullscreen state.
(syncFullscreen): New function.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow)
(setFullscreen): New function.
* src/android.c (struct android_emacs_window): Add new method.
(android_init_emacs_window): Look up new method.
(android_set_fullscreen): New function.
* src/androidgui.h:
* src/androidterm.c (android_fullscreen_hook): Implement
accordingly.

doc/emacs/android.texi
java/org/gnu/emacs/EmacsActivity.java
java/org/gnu/emacs/EmacsWindow.java
src/android.c
src/androidgui.h
src/androidterm.c

index 35f0ba55cf44b88df0ea9957e6d532f208cee217..4e5402f5f40a950cac0e13d80d9bf18b15a7b33f 100644 (file)
@@ -355,8 +355,8 @@ tiled on the screen at any time.
 are created.  Instead, the system may choose to terminate windows that
 are not on screen in order to save memory, with the assumption that
 the program will save its contents to disk and restore them later,
-when the user asks to open it again.  As this is obvious not possible
-with Emacs, Emacs separates a frame from a system window.
+when the user asks to open it again.  As this is obviously not
+possible with Emacs, Emacs separates a frame from a system window.
 
   Each system window created (including the initial window created
 during Emacs startup) is appended to a list of windows that do not
@@ -402,15 +402,15 @@ devices.
 
 @item
 The @code{alpha}, @code{alpha-background}, @code{z-group},
-@code{override-redirect}, @code{mouse-color}, @code{cursor-color},
-@code{cursor-type}, @code{title}, @code{wait-for-wm}, @code{sticky},
-@code{undecorated} and @code{tool-bar-position} frame parameters
-(@pxref{Frame Parameters,,, elisp, the Emacs Lisp Reference Manual})
-are unsupported.
+@code{override-redirect}, @code{mouse-color}, @code{title},
+@code{wait-for-wm}, @code{sticky}, @code{undecorated} and
+@code{tool-bar-position} frame parameters (@pxref{Frame Parameters,,,
+elisp, the Emacs Lisp Reference Manual}) are unsupported.
 
 @item
-The @code{fullscreen} frame parameter is always @code{maximized} for
-top-level frames.
+On Android 4.0 and earlier, the @code{fullscreen} frame parameter is
+always @code{maximized} for top-level frames; on later versions of
+Android, it can also be @code{fullscreen}.
 @end itemize
 
 @cindex selections, android
index 3156a144a0f355ac65d5246b7cbbfbec68d676ad..7e09e60898404d95d0e171af0caacb1a89f1309f 100644 (file)
@@ -26,12 +26,16 @@ import java.util.ArrayList;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Bundle;
 import android.os.Build;
+import android.os.Bundle;
 import android.util.Log;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
 import android.view.Menu;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.FrameLayout;
 
 public class EmacsActivity extends Activity
   implements EmacsWindowAttachmentManager.WindowConsumer
@@ -56,6 +60,9 @@ public class EmacsActivity extends Activity
   /* Whether or not this activity is paused.  */
   private boolean isPaused;
 
+  /* Whether or not this activity is fullscreen.  */
+  private boolean isFullscreen;
+
   static
   {
     focusedActivities = new ArrayList<EmacsActivity> ();
@@ -104,6 +111,8 @@ public class EmacsActivity extends Activity
   public void
   detachWindow ()
   {
+    syncFullscreenWith (null);
+
     if (window == null)
       Log.w (TAG, "detachWindow called, but there is no window");
     else
@@ -131,6 +140,8 @@ public class EmacsActivity extends Activity
       throw new IllegalStateException ("trying to attach window when one"
                                       + " already exists");
 
+    syncFullscreenWith (child);
+
     /* Record and attach the view.  */
 
     window = child;
@@ -230,6 +241,9 @@ public class EmacsActivity extends Activity
   public void
   onWindowFocusChanged (boolean isFocused)
   {
+    Log.d (TAG, ("onWindowFocusChanged: "
+                + (isFocused ? "YES" : "NO")));
+
     if (isFocused && !focusedActivities.contains (this))
       {
        focusedActivities.add (this);
@@ -257,6 +271,9 @@ public class EmacsActivity extends Activity
   {
     isPaused = false;
 
+    /* Update the window insets.  */
+    syncFullscreenWith (window);
+
     EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
     super.onResume ();
   }
@@ -279,4 +296,88 @@ public class EmacsActivity extends Activity
 
     super.onContextMenuClosed (menu);
   }
+
+  @SuppressWarnings ("deprecation")
+  public void
+  syncFullscreenWith (EmacsWindow emacsWindow)
+  {
+    WindowInsetsController controller;
+    Window window;
+    int behavior, flags;
+    View view;
+
+    if (emacsWindow != null)
+      isFullscreen = emacsWindow.fullscreen;
+    else
+      isFullscreen = false;
+
+    /* On Android 11 or later, use the window insets controller to
+       control whether or not the view is fullscreen.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+      {
+       window = getWindow ();
+
+       /* If there is no attached window, return immediately.  */
+       if (window == null)
+         return;
+
+       behavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+       controller = window.getInsetsController ();
+       controller.setSystemBarsBehavior (behavior);
+
+       if (isFullscreen)
+         controller.hide (WindowInsets.Type.statusBars ()
+                          | WindowInsets.Type.navigationBars ());
+       else
+         controller.show (WindowInsets.Type.statusBars ()
+                          | WindowInsets.Type.navigationBars ());
+
+       return;
+      }
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+      {
+       /* On Android 4.1 or later, use `setSystemUiVisibility'.  */
+
+       window = getWindow ();
+
+       if (window == null)
+         return;
+
+       view = window.getDecorView ();
+
+       if (isFullscreen)
+         {
+           if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+             /* This flag means that Emacs will be full screen, but
+                the system will cancel the full screen state upon
+                switching to another program.  */
+             view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_FULLSCREEN);
+           else
+             {
+               /* These flags means that Emacs will be full screen as
+                  long as the state flag is set.  */
+               flags = 0;
+               flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
+               flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+               flags |= View.SYSTEM_UI_FLAG_IMMERSIVE;
+               flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+               view.setSystemUiVisibility (flags);
+             }
+         }
+       else
+         view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_VISIBLE);
+      }
+  }
+
+  @Override
+  public void
+  onAttachedToWindow ()
+  {
+    super.onAttachedToWindow ();
+
+    /* Update the window insets.  */
+    syncFullscreenWith (window);
+  }
 };
index 0eca35cec6191e04e830d61852cf90da3fb231a8..90fc4c441989b232ff587586c73676e707e28803 100644 (file)
@@ -128,6 +128,9 @@ public class EmacsWindow extends EmacsHandleObject
      events.  */
   public LinkedHashMap<Integer, String> eventStrings;
 
+  /* Whether or not this window is fullscreen.  */
+  public boolean fullscreen;
+
   public
   EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
               int width, int height, boolean overrideRedirect)
@@ -1179,4 +1182,27 @@ public class EmacsWindow extends EmacsHandleObject
 
     return any;
   }
+
+  public void
+  setFullscreen (final boolean isFullscreen)
+  {
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         EmacsActivity activity;
+         Object tem;
+
+         fullscreen = isFullscreen;
+         tem = getAttachedConsumer ();
+
+         if (tem != null)
+           {
+             activity = (EmacsActivity) getAttachedConsumer ();
+             activity.syncFullscreenWith (EmacsWindow.this);
+           }
+       }
+      });
+  }
 };
index d21aaabc1ac9e54e09048c6306c6a20315960a98..fc5e6d278ed63f61c187da4de938e49d52b0d809 100644 (file)
@@ -136,6 +136,7 @@ struct android_emacs_window
   jmethodID swap_buffers;
   jmethodID toggle_on_screen_keyboard;
   jmethodID lookup_string;
+  jmethodID set_fullscreen;
 };
 
 /* The API level of the current device.  */
@@ -1920,6 +1921,7 @@ android_init_emacs_window (void)
   FIND_METHOD (toggle_on_screen_keyboard,
               "toggleOnScreenKeyboard", "(Z)V");
   FIND_METHOD (lookup_string, "lookupString", "(I)Ljava/lang/String;");
+  FIND_METHOD (set_fullscreen, "setFullscreen", "(Z)V");
 #undef FIND_METHOD
 }
 
@@ -5475,6 +5477,36 @@ android_reset_ic (android_window window, enum android_ic_mode mode)
 
 \f
 
+/* Window decoration management functions.  */
+
+/* Make the specified WINDOW fullscreen, i.e. obscure all of the
+   system navigation and status bars.  If not FULLSCREEN, make it
+   maximized instead.
+
+   Value is 1 if the system does not support this, else 0.  */
+
+int
+android_set_fullscreen (android_window window, bool fullscreen)
+{
+  jobject object;
+
+  /* Android 4.0 and earlier don't support fullscreen windows.  */
+
+  if (android_api_level < 16)
+    return 1;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      object,
+                                      window_class.set_fullscreen,
+                                      (jboolean) fullscreen);
+  android_exception_check ();
+  return 0;
+}
+
+\f
+
 #else /* ANDROID_STUBIFY */
 
 /* X emulation functions for Android.  */
index 25dc6754fff69bde76836c365171b7c93e5d22ff..84419457a8a1aa825f2f39487f7f3bf50be3d1ec 100644 (file)
@@ -614,6 +614,7 @@ extern int android_wc_lookup_string (android_key_pressed_event *,
 extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t,
                               ptrdiff_t, ptrdiff_t);
 extern void android_reset_ic (android_window, enum android_ic_mode);
+extern int android_set_fullscreen (android_window, bool);
 
 #endif
 
index 0fbee1e9867bd8fc656bc8c7a1cba2959ac5c40a..62a8d5d12b3285c749146e0dfa739e42e60ca0ca 100644 (file)
@@ -1792,12 +1792,30 @@ android_make_frame_visible_invisible (struct frame *f, bool visible)
 static void
 android_fullscreen_hook (struct frame *f)
 {
-  /* Explicitly setting fullscreen is not supported on Android.  */
+  Lisp_Object wanted;
 
   if (!FRAME_PARENT_FRAME (f))
-    store_frame_param (f, Qfullscreen, Qmaximized);
+    {
+      /* Explicitly setting fullscreen is not supported on older
+        Android versions.  */
+
+      wanted = (f->want_fullscreen == FULLSCREEN_BOTH
+               ? Qfullscreen : Qmaximized);
+
+      if (android_set_fullscreen (FRAME_ANDROID_WINDOW (f),
+                                 EQ (wanted, Qfullscreen)))
+       store_frame_param (f, Qfullscreen, Qmaximized);
+      else
+        store_frame_param (f, Qfullscreen, wanted);
+    }
   else
-    store_frame_param (f, Qfullscreen, Qnil);
+    {
+      store_frame_param (f, Qfullscreen, Qnil);
+
+      /* If this is a child frame, don't keep it fullscreen
+        anymore.  */
+      android_set_fullscreen (FRAME_ANDROID_WINDOW (f), false);
+    }
 }
 
 void