From 75c74dc463d56e17be10315cfde409010fd8f90b Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 24 May 2017 16:34:34 -0700 Subject: [PATCH] Add inactive, suspending ApplicationLifecycleState values (#3713) **This is a breaking change on iOS** Previously, the `paused` state was entered when the application resigned active status. `inactive` now maps to this status. `paused` now maps to an app that has been backgrounded. `inactive` is currently emitted on iOS only and corresponds to iOS's foreground inactive state. Current state transitions are: `resumed` <--> `inactive` <--> `paused` suspending is currently emitted on Android only and corresponds to the transition to Android's stopped state. Current state transitions are: `resumed` <--> `paused` --> `suspending` --> `resumed` These transitions may change in future. --- lib/ui/window.dart | 37 +++++++++++++++++-- shell/common/engine.cc | 6 ++- .../android/io/flutter/view/FlutterView.java | 4 ++ .../framework/Source/FlutterViewController.mm | 18 +++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index cc594cb58..eceef0df6 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -26,14 +26,43 @@ typedef void PlatformMessageResponseCallback(ByteData data); typedef void PlatformMessageCallback(String name, ByteData data, PlatformMessageResponseCallback callback); /// 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. enum AppLifecycleState { - /// The application is not currently visible to the user. When the - /// application is in this state, the engine will not call the + /// 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 running in the foreground + /// inactive state. Apps transition to this state when in a phone call, + /// responding to a TouchID request, or when entering the app switcher. Apps + /// in this state should assume that they may be [paused] at any time. + /// + /// On Android, this state is currently unused. + inactive, + + /// The application is not currently visible to the user, not responding to + /// user input, and running in the background. + /// + /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// Apps in this state should assume that they may be [suspended] at any + /// time. paused, - /// The application is visible to the user. - resumed, + /// The application will be suspended momentarily. + /// + /// When the application is in this state, the engine will not call the + /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// On iOS, this state is currently unused. + suspending, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 7cd50574c..55a658bfb 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -318,10 +318,12 @@ void Engine::DispatchPlatformMessage( bool Engine::HandleLifecyclePlatformMessage(blink::PlatformMessage* message) { const auto& data = message->data(); std::string state(reinterpret_cast(data.data()), data.size()); - if (state == "AppLifecycleState.paused") { + if (state == "AppLifecycleState.paused" || + state == "AppLifecycleState.suspending") { activity_running_ = false; StopAnimator(); - } else if (state == "AppLifecycleState.resumed") { + } else if (state == "AppLifecycleState.resumed" || + state == "AppLifecycle.inactive") { activity_running_ = true; StartAnimatorIfPossible(); } diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index c55c84d96..e36b75e78 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -213,6 +213,10 @@ public class FlutterView extends SurfaceView mFlutterLifecycleChannel.send("AppLifecycleState.resumed"); } + public void onStop() { + mFlutterLifecycleChannel.send("AppLifecycleState.suspending"); + } + public void onMemoryPressure() { Map message = new HashMap<>(1); message.put("type", "memoryPressure"); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 4c075fd35..3d7404418 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -189,6 +189,16 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { name:UIApplicationWillResignActiveNotification object:nil]; + [center addObserver:self + selector:@selector(applicationDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + + [center addObserver:self + selector:@selector(applicationWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + [center addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification @@ -261,9 +271,17 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { } - (void)applicationWillResignActive:(NSNotification*)notification { + [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; +} + +- (void)applicationDidEnterBackground:(NSNotification*)notification { [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"]; } +- (void)applicationWillEnterForeground:(NSNotification*)notification { + [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; +} + #pragma mark - Touch event handling enum MapperPhase { -- GitLab