[flutter roll] Revert "Determine lifecycle by looking at window focus also" (#41626)
Reverts flutter/engine#41094.
context: updated the java/android timeouts and details in b/280204906
diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart
index 329a054..6a63871 100644
--- a/lib/ui/platform_dispatcher.dart
+++ b/lib/ui/platform_dispatcher.dart
@@ -1642,61 +1642,31 @@
/// States that an application can be in.
///
/// The values below describe notifications from the operating system.
-/// Applications should not expect to always receive all possible notifications.
-/// For example, if the users pulls out the battery from the device, no
-/// notification will be sent before the application is suddenly terminated,
-/// along with the rest of the operating system.
-///
-/// For historical and name collision reasons, Flutter's application state names
-/// do not correspond one to one with the state names on all platforms. On
-/// Android, for instance, when the OS calls
-/// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause()),
-/// Flutter will enter the [inactive] state, but when Android calls
-/// [`Activity.onStop`](https://developer.android.com/reference/android/app/Activity#onStop()),
-/// Flutter enters the [paused] state. See the individual state's documentation
-/// for descriptions of what they mean on each platform.
+/// Applications should not expect to always receive all possible
+/// notifications. For example, if the users pulls out the battery from the
+/// device, no notification will be sent before the application is suddenly
+/// terminated, along with the rest of the operating system.
///
/// See also:
///
-/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state
-/// from the widgets layer.
-/// * iOS's [IOKit activity lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc) documentation.
-/// * Android's [activity lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle) documentation.
-/// * macOS's [AppKit activity lifecycle](https://developer.apple.com/documentation/appkit/nsapplicationdelegate?language=objc) documentation.
+/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state
+/// from the widgets layer.
enum AppLifecycleState {
- /// The application is visible and responsive to user input.
- ///
- /// On Android, this state corresponds to the Flutter host view having focus
- /// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean))
- /// was called with true) while in Android's "resumed" state. It is possible
- /// for the Flutter app to be in the [inactive] state while still being in
- /// Android's
- /// ["onResume"](https://developer.android.com/guide/components/activities/activity-lifecycle)
- /// state if the app has lost focus
- /// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean))
- /// was called with false), but hasn't had
- /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
- /// called on it.
+ /// The application is visible and responding to user input.
resumed,
/// The application is in an inactive state and is not receiving user input.
///
/// On iOS, this state corresponds to an app or the Flutter host view running
- /// in the foreground inactive state. Apps transition to this state when in a
- /// phone call, responding to a TouchID request, when entering the app
+ /// in the foreground inactive state. Apps transition to this state when in
+ /// a phone call, responding to a TouchID request, when entering the app
/// switcher or the control center, or when the UIViewController hosting the
/// Flutter app is transitioning.
///
- /// On Android, this corresponds to an app or the Flutter host view running in
- /// Android's paused state (i.e.
- /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
- /// has been called), or in Android's "resumed" state (i.e.
- /// [`Activity.onResume`](https://developer.android.com/reference/android/app/Activity#onResume())
- /// has been called) but it has lost window focus. Examples of when apps
- /// transition to this state include when the app is partially obscured or
- /// another activity is focused, such as: a split-screen app, a phone call, a
- /// picture-in-picture app, a system dialog, another view, when the
- /// notification window shade is down, or the application switcher is visible.
+ /// On Android, this corresponds to an app or the Flutter host view running
+ /// in the foreground inactive state. Apps transition to this state when
+ /// another activity is focused, such as a split-screen app, a phone call,
+ /// a picture-in-picture app, a system dialog, or another view.
///
/// Apps in this state should assume that they may be [paused] at any time.
inactive,
diff --git a/shell/platform/android/io/flutter/app/FlutterActivity.java b/shell/platform/android/io/flutter/app/FlutterActivity.java
index 932ad2c..a28176b 100644
--- a/shell/platform/android/io/flutter/app/FlutterActivity.java
+++ b/shell/platform/android/io/flutter/app/FlutterActivity.java
@@ -158,12 +158,6 @@
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- eventDelegate.onWindowFocusChanged(hasFocus);
- }
-
- @Override
public void onTrimMemory(int level) {
eventDelegate.onTrimMemory(level);
}
diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
index bcc54b1..9883d6e 100644
--- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
+++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
@@ -261,11 +261,6 @@
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- flutterView.getPluginRegistry().onWindowFocusChanged(hasFocus);
- }
-
- @Override
public void onTrimMemory(int level) {
// Use a trim level delivered while the application is running so the
// framework has a chance to react to the notification.
diff --git a/shell/platform/android/io/flutter/app/FlutterActivityEvents.java b/shell/platform/android/io/flutter/app/FlutterActivityEvents.java
index abbdec4..9340adb 100644
--- a/shell/platform/android/io/flutter/app/FlutterActivityEvents.java
+++ b/shell/platform/android/io/flutter/app/FlutterActivityEvents.java
@@ -64,10 +64,4 @@
/** @see android.app.Activity#onUserLeaveHint() */
void onUserLeaveHint();
-
- /**
- * @param hasFocus True if the current activity window has focus.
- * @see android.app.Activity#onWindowFocusChanged(boolean)
- */
- void onWindowFocusChanged(boolean hasFocus);
}
diff --git a/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java
index c31a5a2..bf8935e 100644
--- a/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java
+++ b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java
@@ -156,12 +156,6 @@
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- eventDelegate.onWindowFocusChanged(hasFocus);
- }
-
- @Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
eventDelegate.onTrimMemory(level);
diff --git a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java
index 887a911..023e723 100644
--- a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java
+++ b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java
@@ -28,7 +28,6 @@
PluginRegistry.RequestPermissionsResultListener,
PluginRegistry.ActivityResultListener,
PluginRegistry.NewIntentListener,
- PluginRegistry.WindowFocusChangedListener,
PluginRegistry.UserLeaveHintListener,
PluginRegistry.ViewDestroyListener {
private static final String TAG = "FlutterPluginRegistry";
@@ -45,7 +44,6 @@
private final List<ActivityResultListener> mActivityResultListeners = new ArrayList<>(0);
private final List<NewIntentListener> mNewIntentListeners = new ArrayList<>(0);
private final List<UserLeaveHintListener> mUserLeaveHintListeners = new ArrayList<>(0);
- private final List<WindowFocusChangedListener> mWindowFocusChangedListeners = new ArrayList<>(0);
private final List<ViewDestroyListener> mViewDestroyListeners = new ArrayList<>(0);
public FlutterPluginRegistry(FlutterNativeView nativeView, Context context) {
@@ -185,12 +183,6 @@
}
@Override
- public Registrar addWindowFocusChangedListener(WindowFocusChangedListener listener) {
- mWindowFocusChangedListeners.add(listener);
- return this;
- }
-
- @Override
public Registrar addViewDestroyListener(ViewDestroyListener listener) {
mViewDestroyListeners.add(listener);
return this;
@@ -236,13 +228,6 @@
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- for (WindowFocusChangedListener listener : mWindowFocusChangedListeners) {
- listener.onWindowFocusChanged(hasFocus);
- }
- }
-
- @Override
public boolean onViewDestroy(FlutterNativeView view) {
boolean handled = false;
for (ViewDestroyListener listener : mViewDestroyListeners) {
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
index 53344a1..ef8b87d 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
@@ -950,14 +950,6 @@
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (stillAttachedForEvent("onWindowFocusChanged")) {
- delegate.onWindowFocusChanged(hasFocus);
- }
- }
-
- @Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (stillAttachedForEvent("onTrimMemory")) {
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
index 359eca9..9a9f4d9 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
@@ -581,7 +581,7 @@
void onResume() {
Log.v(TAG, "onResume()");
ensureAlive();
- if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
+ if (host.shouldDispatchAppLifecycleState()) {
flutterEngine.getLifecycleChannel().appIsResumed();
}
}
@@ -629,7 +629,7 @@
void onPause() {
Log.v(TAG, "onPause()");
ensureAlive();
- if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
+ if (host.shouldDispatchAppLifecycleState()) {
flutterEngine.getLifecycleChannel().appIsInactive();
}
}
@@ -652,7 +652,7 @@
Log.v(TAG, "onStop()");
ensureAlive();
- if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
+ if (host.shouldDispatchAppLifecycleState()) {
flutterEngine.getLifecycleChannel().appIsPaused();
}
@@ -763,7 +763,7 @@
platformPlugin = null;
}
- if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
+ if (host.shouldDispatchAppLifecycleState()) {
flutterEngine.getLifecycleChannel().appIsDetached();
}
@@ -899,27 +899,6 @@
}
/**
- * Invoke this from {@code Activity#onWindowFocusChanged()}.
- *
- * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
- * the {@code Fragment} can then invoke this method.
- */
- void onWindowFocusChanged(boolean hasFocus) {
- ensureAlive();
- Log.v(TAG, "Received onWindowFocusChanged: " + (hasFocus ? "true" : "false"));
- if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
- // TODO(gspencergoog): Once we have support for multiple windows/views,
- // this code will need to consult the list of windows/views to determine if
- // any windows in the app are focused and call the appropriate function.
- if (hasFocus) {
- flutterEngine.getLifecycleChannel().aWindowIsFocused();
- } else {
- flutterEngine.getLifecycleChannel().noWindowsAreFocused();
- }
- }
- }
-
- /**
* Invoke this from {@link android.app.Activity#onTrimMemory(int)}.
*
* <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
index 85ce3ad..0ef5d87 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
@@ -8,16 +8,13 @@
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnWindowFocusChangeListener;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
@@ -170,19 +167,6 @@
protected static final String ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED =
"should_automatically_handle_on_back_pressed";
- @RequiresApi(18)
- private final OnWindowFocusChangeListener onWindowFocusChangeListener =
- Build.VERSION.SDK_INT >= 18
- ? new OnWindowFocusChangeListener() {
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- if (stillAttachedForEvent("onWindowFocusChanged")) {
- delegate.onWindowFocusChanged(hasFocus);
- }
- }
- }
- : null;
-
/**
* Creates a {@code FlutterFragment} with a default configuration.
*
@@ -1126,22 +1110,8 @@
}
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- if (Build.VERSION.SDK_INT >= 18) {
- view.getViewTreeObserver().addOnWindowFocusChangeListener(onWindowFocusChangeListener);
- }
- }
-
- @Override
public void onDestroyView() {
super.onDestroyView();
- if (Build.VERSION.SDK_INT >= 18) {
- // onWindowFocusChangeListener is API 18+ only.
- requireView()
- .getViewTreeObserver()
- .removeOnWindowFocusChangeListener(onWindowFocusChangeListener);
- }
if (stillAttachedForEvent("onDestroyView")) {
delegate.onDestroyView();
}
diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java
index e1ae744..e2bc8b5 100644
--- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java
+++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java
@@ -733,10 +733,6 @@
onUserLeaveHintListeners = new HashSet<>();
@NonNull
- private final Set<io.flutter.plugin.common.PluginRegistry.WindowFocusChangedListener>
- onWindowFocusChangedListeners = new HashSet<>();
-
- @NonNull
private final Set<OnSaveInstanceStateListener> onSaveInstanceStateListeners = new HashSet<>();
public FlutterEngineActivityPluginBinding(
@@ -852,25 +848,6 @@
}
@Override
- public void addOnWindowFocusChangedListener(
- @NonNull io.flutter.plugin.common.PluginRegistry.WindowFocusChangedListener listener) {
- onWindowFocusChangedListeners.add(listener);
- }
-
- @Override
- public void removeOnWindowFocusChangedListener(
- @NonNull io.flutter.plugin.common.PluginRegistry.WindowFocusChangedListener listener) {
- onWindowFocusChangedListeners.remove(listener);
- }
-
- void onWindowFocusChanged(boolean hasFocus) {
- for (io.flutter.plugin.common.PluginRegistry.WindowFocusChangedListener listener :
- onWindowFocusChangedListeners) {
- listener.onWindowFocusChanged(hasFocus);
- }
- }
-
- @Override
public void addOnSaveStateListener(@NonNull OnSaveInstanceStateListener listener) {
onSaveInstanceStateListeners.add(listener);
}
diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java
index 8c5f84b..fc2deca 100644
--- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java
+++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java
@@ -92,19 +92,6 @@
void removeOnUserLeaveHintListener(@NonNull PluginRegistry.UserLeaveHintListener listener);
/**
- * Adds a listener that is invoked whenever the associated {@link android.app.Activity}'s {@code
- * onWindowFocusChanged()} method is invoked.
- */
- void addOnWindowFocusChangedListener(@NonNull PluginRegistry.WindowFocusChangedListener listener);
-
- /**
- * Removes a listener that was added in {@link
- * #addOnWindowFocusChangedListener(PluginRegistry.WindowFocusChangedListener)}.
- */
- void removeOnWindowFocusChangedListener(
- @NonNull PluginRegistry.WindowFocusChangedListener listener);
-
- /**
* Adds a listener that is invoked when the associated {@code Activity} or {@code Fragment} saves
* and restores instance state.
*/
diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
index d27da01..9c40d0a 100644
--- a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
+++ b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
@@ -39,8 +39,6 @@
new HashSet<>();
private final Set<PluginRegistry.NewIntentListener> newIntentListeners = new HashSet<>();
private final Set<PluginRegistry.UserLeaveHintListener> userLeaveHintListeners = new HashSet<>();
- private final Set<PluginRegistry.WindowFocusChangedListener> WindowFocusChangedListeners =
- new HashSet<>();
private FlutterPlugin.FlutterPluginBinding pluginBinding;
private ActivityPluginBinding activityPluginBinding;
@@ -149,18 +147,6 @@
}
@Override
- public PluginRegistry.Registrar addWindowFocusChangedListener(
- PluginRegistry.WindowFocusChangedListener listener) {
- WindowFocusChangedListeners.add(listener);
-
- if (activityPluginBinding != null) {
- activityPluginBinding.addOnWindowFocusChangedListener(listener);
- }
-
- return this;
- }
-
- @Override
@NonNull
public PluginRegistry.Registrar addViewDestroyListener(
@NonNull PluginRegistry.ViewDestroyListener listener) {
@@ -227,8 +213,5 @@
for (PluginRegistry.UserLeaveHintListener listener : userLeaveHintListeners) {
activityPluginBinding.addOnUserLeaveHintListener(listener);
}
- for (PluginRegistry.WindowFocusChangedListener listener : WindowFocusChangedListeners) {
- activityPluginBinding.addOnWindowFocusChangedListener(listener);
- }
}
}
diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
index f34f774..9e8c22b 100644
--- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
+++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
@@ -5,106 +5,39 @@
package io.flutter.embedding.engine.systemchannels;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import io.flutter.Log;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.StringCodec;
-/**
- * A {@link BasicMessageChannel} that communicates lifecycle events to the framework.
- *
- * <p>The activity listens to the Android lifecycle events, in addition to the focus events for
- * windows, and this channel combines that information to decide if the application is the inactive,
- * resumed, paused, or detached state.
- */
+/** TODO(mattcarroll): fill in javadoc for LifecycleChannel. */
public class LifecycleChannel {
private static final String TAG = "LifecycleChannel";
- private static final String CHANNEL_NAME = "flutter/lifecycle";
- // These should stay in sync with the AppLifecycleState enum in the framework.
- private static final String RESUMED = "AppLifecycleState.resumed";
- private static final String INACTIVE = "AppLifecycleState.inactive";
- private static final String PAUSED = "AppLifecycleState.paused";
- private static final String DETACHED = "AppLifecycleState.detached";
-
- private String lastAndroidState = "";
- private String lastFlutterState = "";
- private boolean lastFocus = false;
-
- @NonNull private final BasicMessageChannel<String> channel;
+ @NonNull public final BasicMessageChannel<String> channel;
public LifecycleChannel(@NonNull DartExecutor dartExecutor) {
- this(new BasicMessageChannel<String>(dartExecutor, CHANNEL_NAME, StringCodec.INSTANCE));
- }
-
- @VisibleForTesting
- public LifecycleChannel(@NonNull BasicMessageChannel<String> channel) {
- this.channel = channel;
- }
-
- // Here's the state table this implements:
- //
- // | Android State | Window focused | Flutter state |
- // |---------------|----------------|---------------|
- // | Resumed | true | resumed |
- // | Resumed | false | inactive |
- // | Paused | true | inactive |
- // | Paused | false | inactive |
- // | Stopped | true | paused |
- // | Stopped | false | paused |
- // | Detached | true | detached |
- // | Detached | false | detached |
- private void sendState(String state, boolean hasFocus) {
- if (lastAndroidState == state && hasFocus == lastFocus) {
- // No inputs changed, so Flutter state could not have changed.
- return;
- }
- String newState;
- if (state == RESUMED) {
- // Focus is only taken into account when the Android state is "Resumed".
- // In all other states, focus is ignored, because we can't know what order
- // Android lifecycle notifications and window focus notifications events
- // will arrive in, and those states don't send input events anyhow.
- newState = hasFocus ? RESUMED : INACTIVE;
- } else {
- newState = state;
- }
- // Keep the last reported values for future updates.
- lastAndroidState = state;
- lastFocus = hasFocus;
- if (newState == lastFlutterState) {
- // No change in the resulting Flutter state, so don't report anything.
- return;
- }
- Log.v(TAG, "Sending " + newState + " message.");
- channel.send(newState);
- lastFlutterState = newState;
- }
-
- // Called if at least one window in the app has focus.
- public void aWindowIsFocused() {
- sendState(lastAndroidState, true);
- }
-
- // Called if no windows in the app have focus.
- public void noWindowsAreFocused() {
- sendState(lastAndroidState, false);
- }
-
- public void appIsResumed() {
- sendState(RESUMED, lastFocus);
+ this.channel =
+ new BasicMessageChannel<>(dartExecutor, "flutter/lifecycle", StringCodec.INSTANCE);
}
public void appIsInactive() {
- sendState(INACTIVE, lastFocus);
+ Log.v(TAG, "Sending AppLifecycleState.inactive message.");
+ channel.send("AppLifecycleState.inactive");
+ }
+
+ public void appIsResumed() {
+ Log.v(TAG, "Sending AppLifecycleState.resumed message.");
+ channel.send("AppLifecycleState.resumed");
}
public void appIsPaused() {
- sendState(PAUSED, lastFocus);
+ Log.v(TAG, "Sending AppLifecycleState.paused message.");
+ channel.send("AppLifecycleState.paused");
}
public void appIsDetached() {
- sendState(DETACHED, lastFocus);
+ Log.v(TAG, "Sending AppLifecycleState.detached message.");
+ channel.send("AppLifecycleState.detached");
}
}
diff --git a/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java
index 4859617..649bce8 100644
--- a/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java
+++ b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java
@@ -311,23 +311,6 @@
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@link
- * Activity#onWindowFocusChanged(boolean)}.
- *
- * <p>This registrar is for Flutter's v1 embedding. To listen for leave hints in the v2
- * embedding, use {@link
- * ActivityPluginBinding#addOnWindowFocusChangedListener(PluginRegistry.WindowFocusChangedListener)}.
- *
- * <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
- * http://flutter.dev/go/android-plugin-migration
- *
- * @param listener a {@link WindowFocusChangedListener} callback.
- * @return this {@link Registrar}.
- */
- @NonNull
- Registrar addWindowFocusChangedListener(@NonNull WindowFocusChangedListener listener);
-
- /**
- * Adds a callback allowing the plugin to take part in handling incoming calls to {@link
* Activity#onDestroy()}.
*
* <p>This registrar is for Flutter's v1 embedding. The concept of {@code View} destruction does
@@ -406,14 +389,6 @@
}
/**
- * Delegate interface for handling window focus changes on behalf of the main {@link
- * android.app.Activity}.
- */
- interface WindowFocusChangedListener {
- void onWindowFocusChanged(boolean hasFocus);
- }
-
- /**
* Delegate interface for handling an {@link android.app.Activity}'s onDestroy method being
* called. A plugin that implements this interface can adopt the {@link FlutterNativeView} by
* retaining a reference and returning true.
diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
index d5daa43..8c1f545 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
@@ -107,36 +107,13 @@
// By the time an Activity/Fragment is started, we don't expect any lifecycle messages
// to have been sent to Flutter.
delegate.onStart();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).noWindowsAreFocused();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
+ verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
// When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter.
delegate.onResume();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).noWindowsAreFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
-
- // When the app loses focus because something else has it (e.g. notification
- // windowshade or app switcher), it should go to inactive.
- delegate.onWindowFocusChanged(false);
- verify(mockFlutterEngine.getLifecycleChannel(), never()).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).noWindowsAreFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
-
- // When the app regains focus, it should go to resumed again.
- delegate.onWindowFocusChanged(true);
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).noWindowsAreFocused();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
@@ -144,8 +121,6 @@
// When the Activity/Fragment is paused, an inactive message should have been sent to Flutter.
delegate.onPause();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).noWindowsAreFocused();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
@@ -155,8 +130,6 @@
// Notice that Flutter uses the term "paused" in a different way, and at a different time
// than the Android OS.
delegate.onStop();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).noWindowsAreFocused();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused();
@@ -164,8 +137,6 @@
// When activity detaches, a detached message should have been sent to Flutter.
delegate.onDetach();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), times(1)).noWindowsAreFocused();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused();
@@ -186,14 +157,10 @@
delegate.onCreateView(null, null, null, 0, true);
delegate.onStart();
delegate.onResume();
- delegate.onWindowFocusChanged(false);
- delegate.onWindowFocusChanged(true);
delegate.onPause();
delegate.onStop();
delegate.onDetach();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).aWindowIsFocused();
- verify(mockFlutterEngine.getLifecycleChannel(), never()).noWindowsAreFocused();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
diff --git a/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/LifecycleChannelTest.java b/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/LifecycleChannelTest.java
deleted file mode 100644
index e9211d5..0000000
--- a/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/LifecycleChannelTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-package io.flutter.embedding.engine.systemchannels;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import io.flutter.plugin.common.BasicMessageChannel;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.annotation.Config;
-
-@Config(manifest = Config.NONE)
-@RunWith(AndroidJUnit4.class)
-public class LifecycleChannelTest {
- LifecycleChannel lifecycleChannel;
- BasicMessageChannel<String> mockChannel;
-
- @Before
- public void setUp() {
- mockChannel = mock(BasicMessageChannel.class);
- lifecycleChannel = new LifecycleChannel(mockChannel);
- }
-
- @Test
- public void lifecycleChannel_handlesResumed() {
- lifecycleChannel.appIsResumed();
- ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(1)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.inactive", stringArgumentCaptor.getValue());
-
- lifecycleChannel.aWindowIsFocused();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(2)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.resumed", stringArgumentCaptor.getValue());
-
- lifecycleChannel.noWindowsAreFocused();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(3)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.inactive", stringArgumentCaptor.getValue());
-
- // Stays inactive, so no event is sent.
- lifecycleChannel.appIsInactive();
- verify(mockChannel, times(3)).send(any(String.class));
-
- // Stays inactive, so no event is sent.
- lifecycleChannel.appIsResumed();
- verify(mockChannel, times(3)).send(any(String.class));
-
- lifecycleChannel.aWindowIsFocused();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(4)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.resumed", stringArgumentCaptor.getValue());
- }
-
- @Test
- public void lifecycleChannel_handlesInactive() {
- lifecycleChannel.appIsInactive();
- ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(1)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.inactive", stringArgumentCaptor.getValue());
-
- // Stays inactive, so no event is sent.
- lifecycleChannel.aWindowIsFocused();
- verify(mockChannel, times(1)).send(any(String.class));
-
- // Stays inactive, so no event is sent.
- lifecycleChannel.noWindowsAreFocused();
- verify(mockChannel, times(1)).send(any(String.class));
-
- lifecycleChannel.appIsResumed();
- lifecycleChannel.aWindowIsFocused();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(2)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.resumed", stringArgumentCaptor.getValue());
- }
-
- @Test
- public void lifecycleChannel_handlesPaused() {
- // Stays inactive, so no event is sent.
- lifecycleChannel.appIsPaused();
- ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(1)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.paused", stringArgumentCaptor.getValue());
-
- // Stays paused, so no event is sent.
- lifecycleChannel.aWindowIsFocused();
- verify(mockChannel, times(1)).send(any(String.class));
-
- lifecycleChannel.noWindowsAreFocused();
- verify(mockChannel, times(1)).send(any(String.class));
-
- lifecycleChannel.appIsResumed();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(2)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.inactive", stringArgumentCaptor.getValue());
-
- lifecycleChannel.aWindowIsFocused();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(3)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.resumed", stringArgumentCaptor.getValue());
- }
-
- @Test
- public void lifecycleChannel_handlesDetached() {
- // Stays inactive, so no event is sent.
- lifecycleChannel.appIsDetached();
- ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(1)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.detached", stringArgumentCaptor.getValue());
-
- // Stays paused, so no event is sent.
- lifecycleChannel.aWindowIsFocused();
- verify(mockChannel, times(1)).send(any(String.class));
-
- lifecycleChannel.noWindowsAreFocused();
- verify(mockChannel, times(1)).send(any(String.class));
-
- lifecycleChannel.appIsResumed();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(2)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.inactive", stringArgumentCaptor.getValue());
-
- lifecycleChannel.aWindowIsFocused();
- stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockChannel, times(3)).send(stringArgumentCaptor.capture());
- assertEquals("AppLifecycleState.resumed", stringArgumentCaptor.getValue());
- }
-}