diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 329a054828156..6a63871e9ccd6 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -1642,61 +1642,31 @@ class FrameTiming { /// 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 932ad2c5ada57..a28176b0f799b 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivity.java +++ b/shell/platform/android/io/flutter/app/FlutterActivity.java @@ -157,12 +157,6 @@ public void onUserLeaveHint() { eventDelegate.onUserLeaveHint(); } - @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 bcc54b1e9786f..9883d6efdb88f 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -260,11 +260,6 @@ public void onUserLeaveHint() { flutterView.getPluginRegistry().onUserLeaveHint(); } - @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 diff --git a/shell/platform/android/io/flutter/app/FlutterActivityEvents.java b/shell/platform/android/io/flutter/app/FlutterActivityEvents.java index abbdec4fe6afa..9340adb405394 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityEvents.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityEvents.java @@ -64,10 +64,4 @@ public interface FlutterActivityEvents /** @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 c31a5a20a43a8..bf8935e8bb161 100644 --- a/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java @@ -155,12 +155,6 @@ public void onUserLeaveHint() { eventDelegate.onUserLeaveHint(); } - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - eventDelegate.onWindowFocusChanged(hasFocus); - } - @Override public void onTrimMemory(int level) { super.onTrimMemory(level); diff --git a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java index 887a9118979cd..023e723e93c8e 100644 --- a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java +++ b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java @@ -28,7 +28,6 @@ public class FlutterPluginRegistry PluginRegistry.RequestPermissionsResultListener, PluginRegistry.ActivityResultListener, PluginRegistry.NewIntentListener, - PluginRegistry.WindowFocusChangedListener, PluginRegistry.UserLeaveHintListener, PluginRegistry.ViewDestroyListener { private static final String TAG = "FlutterPluginRegistry"; @@ -45,7 +44,6 @@ public class FlutterPluginRegistry private final List mActivityResultListeners = new ArrayList<>(0); private final List mNewIntentListeners = new ArrayList<>(0); private final List mUserLeaveHintListeners = new ArrayList<>(0); - private final List mWindowFocusChangedListeners = new ArrayList<>(0); private final List mViewDestroyListeners = new ArrayList<>(0); public FlutterPluginRegistry(FlutterNativeView nativeView, Context context) { @@ -184,12 +182,6 @@ public Registrar addUserLeaveHintListener(UserLeaveHintListener listener) { return this; } - @Override - public Registrar addWindowFocusChangedListener(WindowFocusChangedListener listener) { - mWindowFocusChangedListeners.add(listener); - return this; - } - @Override public Registrar addViewDestroyListener(ViewDestroyListener listener) { mViewDestroyListeners.add(listener); @@ -235,13 +227,6 @@ public void onUserLeaveHint() { } } - @Override - public void onWindowFocusChanged(boolean hasFocus) { - for (WindowFocusChangedListener listener : mWindowFocusChangedListeners) { - listener.onWindowFocusChanged(hasFocus); - } - } - @Override public boolean onViewDestroy(FlutterNativeView view) { boolean handled = false; diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 53344a1a3ad79..ef8b87dbc92b9 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -949,14 +949,6 @@ public void onUserLeaveHint() { } } - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (stillAttachedForEvent("onWindowFocusChanged")) { - delegate.onWindowFocusChanged(hasFocus); - } - } - @Override public void onTrimMemory(int level) { super.onTrimMemory(level); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 359eca9514125..9a9f4d9a6a19c 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 @@ public boolean onPreDraw() { void onResume() { Log.v(TAG, "onResume()"); ensureAlive(); - if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) { + if (host.shouldDispatchAppLifecycleState()) { flutterEngine.getLifecycleChannel().appIsResumed(); } } @@ -629,7 +629,7 @@ void updateSystemUiOverlays() { void onPause() { Log.v(TAG, "onPause()"); ensureAlive(); - if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) { + if (host.shouldDispatchAppLifecycleState()) { flutterEngine.getLifecycleChannel().appIsInactive(); } } @@ -652,7 +652,7 @@ void onStop() { Log.v(TAG, "onStop()"); ensureAlive(); - if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) { + if (host.shouldDispatchAppLifecycleState()) { flutterEngine.getLifecycleChannel().appIsPaused(); } @@ -763,7 +763,7 @@ void onDetach() { platformPlugin = null; } - if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) { + if (host.shouldDispatchAppLifecycleState()) { flutterEngine.getLifecycleChannel().appIsDetached(); } @@ -898,27 +898,6 @@ void onUserLeaveHint() { } } - /** - * Invoke this from {@code Activity#onWindowFocusChanged()}. - * - *

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)}. * diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 85ce3ade38fb0..0ef5d872358d9 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 @@ public class FlutterFragment extends Fragment 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. * @@ -1125,23 +1109,9 @@ public void onStop() { } } - @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 e1ae7440f02e6..e2bc8b54d9ee4 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java @@ -732,10 +732,6 @@ private static class FlutterEngineActivityPluginBinding implements ActivityPlugi private final Set onUserLeaveHintListeners = new HashSet<>(); - @NonNull - private final Set - onWindowFocusChangedListeners = new HashSet<>(); - @NonNull private final Set onSaveInstanceStateListeners = new HashSet<>(); @@ -851,25 +847,6 @@ public void removeOnUserLeaveHintListener( onUserLeaveHintListeners.remove(listener); } - @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 8c5f84b453c8a..fc2deca721061 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 @@ -91,19 +91,6 @@ void removeRequestPermissionsResultListener( */ 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 d27da01dae04b..9c40d0a016f21 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 @@ class ShimRegistrar implements PluginRegistry.Registrar, FlutterPlugin, Activity new HashSet<>(); private final Set newIntentListeners = new HashSet<>(); private final Set userLeaveHintListeners = new HashSet<>(); - private final Set WindowFocusChangedListeners = - new HashSet<>(); private FlutterPlugin.FlutterPluginBinding pluginBinding; private ActivityPluginBinding activityPluginBinding; @@ -148,18 +146,6 @@ public PluginRegistry.Registrar addUserLeaveHintListener( return this; } - @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( @@ -227,8 +213,5 @@ private void addExistingListenersToActivityPluginBinding() { 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 f34f774536018..9e8c22bc909be 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. - * - *

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 channel; + @NonNull public final BasicMessageChannel channel; public LifecycleChannel(@NonNull DartExecutor dartExecutor) { - this(new BasicMessageChannel(dartExecutor, CHANNEL_NAME, StringCodec.INSTANCE)); - } - - @VisibleForTesting - public LifecycleChannel(@NonNull BasicMessageChannel 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; + this.channel = + new BasicMessageChannel<>(dartExecutor, "flutter/lifecycle", StringCodec.INSTANCE); } - // 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 appIsInactive() { + Log.v(TAG, "Sending AppLifecycleState.inactive message."); + channel.send("AppLifecycleState.inactive"); } public void appIsResumed() { - sendState(RESUMED, lastFocus); - } - - public void appIsInactive() { - sendState(INACTIVE, lastFocus); + 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 4859617d93b29..649bce820dbec 100644 --- a/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java +++ b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java @@ -309,23 +309,6 @@ Registrar addRequestPermissionsResultListener( @NonNull Registrar addUserLeaveHintListener(@NonNull UserLeaveHintListener listener); - /** - * Adds a callback allowing the plugin to take part in handling incoming calls to {@link - * Activity#onWindowFocusChanged(boolean)}. - * - *

This registrar is for Flutter's v1 embedding. To listen for leave hints in the v2 - * embedding, use {@link - * ActivityPluginBinding#addOnWindowFocusChangedListener(PluginRegistry.WindowFocusChangedListener)}. - * - *

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()}. @@ -405,14 +388,6 @@ interface UserLeaveHintListener { void onUserLeaveHint(); } - /** - * 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 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 d5daa43f269a2..8c1f545853087 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 @@ public void itSendsLifecycleEventsToFlutter() { // 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 @@ public void itSendsLifecycleEventsToFlutter() { // 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 @@ public void itSendsLifecycleEventsToFlutter() { // 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 @@ public void itSendsLifecycleEventsToFlutter() { // 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 @@ public void itDoesNotSendsLifecycleEventsToFlutter() { 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 e9211d5f3b411..0000000000000 --- 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 mockChannel; - - @Before - public void setUp() { - mockChannel = mock(BasicMessageChannel.class); - lifecycleChannel = new LifecycleChannel(mockChannel); - } - - @Test - public void lifecycleChannel_handlesResumed() { - lifecycleChannel.appIsResumed(); - ArgumentCaptor 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 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 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 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()); - } -}