summaryrefslogtreecommitdiff
path: root/java/org/gnu/emacs/EmacsActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/org/gnu/emacs/EmacsActivity.java')
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java129
1 files changed, 117 insertions, 12 deletions
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java
index 3237f650240..e380b7bfc2a 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -20,9 +20,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
package org.gnu.emacs;
import java.lang.IllegalStateException;
+
import java.util.List;
import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
import android.app.Activity;
import android.content.ContentResolver;
@@ -31,6 +34,7 @@ import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemClock;
import android.util.Log;
@@ -78,13 +82,16 @@ public class EmacsActivity extends Activity
/* The last context menu to be closed. */
private static Menu lastClosedMenu;
+ /* The time of the most recent call to onStop. */
+ private static long timeOfLastInteraction;
+
static
{
focusedActivities = new ArrayList<EmacsActivity> ();
};
public static void
- invalidateFocus1 (EmacsWindow window)
+ invalidateFocus1 (EmacsWindow window, boolean resetWhenChildless)
{
if (window.view.isFocused ())
focusedWindow = window;
@@ -92,12 +99,23 @@ public class EmacsActivity extends Activity
synchronized (window.children)
{
for (EmacsWindow child : window.children)
- invalidateFocus1 (child);
+ invalidateFocus1 (child, false);
+
+ /* If no focused window was previously detected among WINDOW's
+ children and RESETWHENCHILDLESS is set (implying it is a
+ toplevel window), request that it be focused, to avoid
+ creating a situation where no windows exist focused or can be
+ transferred the input focus by user action. */
+ if (focusedWindow == null && resetWhenChildless)
+ {
+ window.view.requestFocus ();
+ focusedWindow = window;
+ }
}
}
public static void
- invalidateFocus ()
+ invalidateFocus (int whence)
{
EmacsWindow oldFocus;
@@ -110,7 +128,7 @@ public class EmacsActivity extends Activity
for (EmacsActivity activity : focusedActivities)
{
if (activity.window != null)
- invalidateFocus1 (activity.window);
+ invalidateFocus1 (activity.window, focusedWindow == null);
}
/* Send focus in- and out- events to the previous and current
@@ -144,7 +162,7 @@ public class EmacsActivity extends Activity
layout.removeView (window.view);
window = null;
- invalidateFocus ();
+ invalidateFocus (0);
}
}
@@ -172,8 +190,17 @@ public class EmacsActivity extends Activity
if (isPaused)
window.noticeIconified ();
- /* Invalidate the focus. */
- invalidateFocus ();
+ /* Invalidate the focus. Since attachWindow may be called from
+ either the main or the UI thread, post this to the UI thread. */
+
+ runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ invalidateFocus (1);
+ }
+ });
}
@Override
@@ -238,6 +265,10 @@ public class EmacsActivity extends Activity
}
super.onCreate (savedInstanceState);
+
+ /* Call `onWindowFocusChanged' to read the focus state, which fails
+ to be called after an activity is recreated. */
+ onWindowFocusChanged (false);
}
@Override
@@ -249,6 +280,50 @@ public class EmacsActivity extends Activity
@Override
public final void
+ onStop ()
+ {
+ timeOfLastInteraction = SystemClock.elapsedRealtime ();
+
+ super.onStop ();
+ }
+
+ /* Return whether the task is being finished in response to explicit
+ user action. That is to say, Activity.isFinished, but as
+ documented. */
+
+ public final boolean
+ isReallyFinishing ()
+ {
+ long atime, dtime;
+ int hours;
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+ return isFinishing ();
+
+ /* When the number of tasks retained in the recents list exceeds a
+ threshold, Android 7 and later so destroy activities in trimming
+ them from recents on the expiry of a timeout that isFinishing
+ returns true, in direct contradiction to the documentation. This
+ timeout is generally 6 hours, but admits of customization by
+ individual system distributors, so to err on the side of the
+ caution, the timeout Emacs applies is a more conservative figure
+ of 4 hours. */
+
+ if (timeOfLastInteraction == 0)
+ return isFinishing ();
+
+ atime = timeOfLastInteraction;
+
+ /* Compare atime with the current system time. */
+ dtime = SystemClock.elapsedRealtime () - atime;
+ if (dtime + 1000000 < TimeUnit.HOURS.toMillis (4))
+ return isFinishing ();
+
+ return false;
+ }
+
+ @Override
+ public final void
onDestroy ()
{
EmacsWindowAttachmentManager manager;
@@ -259,9 +334,10 @@ public class EmacsActivity extends Activity
/* The activity will die shortly hereafter. If there is a window
attached, close it now. */
isMultitask = this instanceof EmacsMultitaskActivity;
- manager.removeWindowConsumer (this, isMultitask || isFinishing ());
+ manager.removeWindowConsumer (this, (isMultitask
+ || isReallyFinishing ()));
focusedActivities.remove (this);
- invalidateFocus ();
+ invalidateFocus (2);
/* Remove this activity from the static field, lest it leak. */
if (lastFocusedActivity == this)
@@ -274,9 +350,16 @@ public class EmacsActivity extends Activity
public final void
onWindowFocusChanged (boolean isFocused)
{
- if (isFocused && !focusedActivities.contains (this))
+ /* At times and on certain versions of Android ISFOCUSED does not
+ reflect whether the window actually holds focus, so replace it
+ with the value of `hasWindowFocus'. */
+ isFocused = hasWindowFocus ();
+
+ if (isFocused)
{
- focusedActivities.add (this);
+ if (!focusedActivities.contains (this))
+ focusedActivities.add (this);
+
lastFocusedActivity = this;
/* Update the window insets as the focus change may have
@@ -291,7 +374,7 @@ public class EmacsActivity extends Activity
else
focusedActivities.remove (this);
- invalidateFocus ();
+ invalidateFocus (3);
}
@Override
@@ -309,6 +392,7 @@ public class EmacsActivity extends Activity
onResume ()
{
isPaused = false;
+ timeOfLastInteraction = 0;
EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
super.onResume ();
@@ -433,6 +517,27 @@ public class EmacsActivity extends Activity
syncFullscreenWith (window);
}
+ @Override
+ public final void
+ onNewIntent (Intent intent)
+ {
+ String tag, action;
+
+ /* This function is called when EmacsActivity is relaunched from a
+ notification. */
+
+ if (intent == null || EmacsService.SERVICE == null)
+ return;
+
+ tag = intent.getStringExtra (EmacsDesktopNotification.NOTIFICATION_TAG);
+ action
+ = intent.getStringExtra (EmacsDesktopNotification.NOTIFICATION_ACTION);
+
+ if (tag == null || action == null)
+ return;
+
+ EmacsNative.sendNotificationAction (tag, action);
+ }
@Override