Skip to content

Commit

Permalink
Adding app lifecycle notification for macOS and Linux, add hidden s…
Browse files Browse the repository at this point in the history
…tate. (flutter#40542)

## Description

This adds app lifecycle notification for macOS and Linux, and adds a new `hidden` state to the `AppLifecycleState` enum.

To be functional, this needs a corresponding framework change: flutter/flutter#123274

## Related Issues
 - flutter/flutter#30735

## Tests
 - Added tests for state changes.
  • Loading branch information
gspencergoog authored May 26, 2023
1 parent 7b55ec9 commit 9fd0082
Show file tree
Hide file tree
Showing 28 changed files with 1,113 additions and 135 deletions.
8 changes: 8 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2463,6 +2463,7 @@ ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.cc + ../../.
ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/app_lifecycle_state.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -2680,6 +2681,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm + ../../
ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h + ../../../flutter/LICENSE
Expand All @@ -2691,6 +2693,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibil
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegateTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h + ../../../flutter/LICENSE
Expand Down Expand Up @@ -5130,6 +5134,7 @@ FILE: ../../../flutter/shell/platform/common/accessibility_bridge.cc
FILE: ../../../flutter/shell/platform/common/accessibility_bridge.h
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.cc
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h
FILE: ../../../flutter/shell/platform/common/app_lifecycle_state.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc
Expand Down Expand Up @@ -5349,6 +5354,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm
FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h
FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h
Expand All @@ -5361,6 +5367,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibilit
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegateTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h
Expand Down
135 changes: 94 additions & 41 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1679,13 +1679,15 @@ class FrameTiming {
}
}

/// States that an application can be in.
/// States that an application can be in once it is running.
///
/// 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.
/// States not supported on a platform will be synthesized by the framework when
/// transitioning between states which are supported, so that all
/// implementations share the same state machine.
///
/// The initial value for the state is the [detached] state, updated to the
/// current state (usually [resumed]) as soon as the first lifecycle update is
/// received from the platform.
///
/// 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
Expand All @@ -1696,15 +1698,52 @@ class FrameTiming {
/// Flutter enters the [paused] state. See the individual state's documentation
/// for descriptions of what they mean on each platform.
///
/// The current application state can be obtained from
/// [SchedulerBinding.instance.lifecycleState], and changes to the state can be
/// observed by creating an [AppLifecycleListener], or by using a
/// [WidgetsBindingObserver] by overriding the
/// [WidgetsBindingObserver.didChangeAppLifecycleState] method.
///
/// Applications should not rely on always receiving all possible notifications.
///
/// For example, if the application is killed with a task manager, a kill
/// signal, the user pulls the power from the device, or there is a rapid
/// unscheduled disassembly of the device, no notification will be sent before
/// the application is suddenly terminated, and some states may be skipped.
///
/// See also:
///
/// * [AppLifecycleListener], an object used observe the lifecycle state that
/// provides state transition callbacks.
/// * [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.
/// * 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.
enum AppLifecycleState {
/// The application is visible and responsive to user input.
/// The application is still hosted by a Flutter engine but is detached from
/// any host views.
///
/// The application defaults to this state before it initializes, and can be
/// in this state (on Android and iOS only) after all views have been
/// detached.
///
/// When the application is in this state, the engine is running without a
/// view.
///
/// This state is only entered on iOS and Android, although on all platforms
/// it is the default state before the application begins running.
detached,

/// On all platforms, this state indicates that the application is in the
/// default running mode for a running application that has input focus and is
/// visible.
///
/// On Android, this state corresponds to the Flutter host view having focus
/// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean))
Expand All @@ -1717,54 +1756,71 @@ enum AppLifecycleState {
/// was called with false), but hasn't had
/// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
/// called on it.
///
/// On iOS and macOS, this corresponds to the app running in the foreground
/// active state.
resumed,

/// The application is in an inactive state and is not receiving user input.
/// At least one view of the application is visible, but none have input
/// focus. The application is otherwise running normally.
///
/// On non-web desktop platforms, this corresponds to an application that is
/// not in the foreground, but still has visible windows.
///
/// On the web, this corresponds to an application that is running in a
/// window or tab that does not have input focus.
///
/// 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
/// switcher or the control center, or when the UIViewController hosting the
/// Flutter app is transitioning.
/// On iOS and macOS, this state corresponds to the Flutter host view running in the
/// foreground inactive state. Apps transition to this state when in a phone
/// call, when 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.
/// On Android, this corresponds to 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
/// has been called) but does not have 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
/// another activity is focused, a app running in a split screen that isn't
/// the current app, an app interrupted by a phone call, a picture-in-picture
/// app, a system dialog, another view. It will also be inactive when the
/// notification window shade is down, or the application switcher is visible.
///
/// Apps in this state should assume that they may be [paused] at any time.
/// On Android and iOS, apps in this state should assume that they may be
/// [hidden] and [paused] at any time.
inactive,

/// The application is not currently visible to the user, not responding to
/// user input, and running in the background.
/// All views of an application are hidden, either because the application is
/// about to be paused (on iOS and Android), or because it has been minimized
/// or placed on a desktop that is no longer visible (on non-web desktop), or
/// is running in a window or tab that is no longer visible (on the web).
///
/// On iOS and Android, in order to keep the state machine the same on all
/// platforms, a transition to this state is synthesized before the [paused]
/// state is entered when coming from [inactive], and before the [inactive]
/// state is entered when coming from [paused]. This allows cross-platform
/// implementations that want to know when an app is conceptually "hidden" to
/// only write one handler.
hidden,

/// The application is not currently visible to the user, and not responding
/// to user input.
///
/// When the application is in this state, the engine will not call the
/// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame]
/// callbacks.
paused,

/// The application is still hosted on a flutter engine but is detached from
/// any host views.
///
/// When the application is in this state, the engine is running without
/// a view. It can either be in the progress of attaching a view when engine
/// was first initializes, or after the view being destroyed due to a Navigator
/// pop.
detached,
/// This state is only entered on iOS and Android.
paused,
}

/// The possible responses to a request to exit the application.
///
/// The request is typically responded to by a [WidgetsBindingObserver].
// TODO(gspencergoog): Insert doc references here to AppLifecycleListener and to
// the actual function called on WidgetsBindingObserver once those have landed
// in the framework. https://github.com/flutter/flutter/issues/121721
/// The request is typically responded to by creating an [AppLifecycleListener]
/// and supplying an [AppLifecycleListener.onExitRequested] callback, or by
/// overriding [WidgetsBindingObserver.didRequestAppExit].
enum AppExitResponse {
/// Exiting the application can proceed.
exit,
Expand All @@ -1773,10 +1829,7 @@ enum AppExitResponse {
}

/// The type of application exit to perform when calling
/// `ServicesBinding.exitApplication`.
// TODO(gspencergoog): Insert doc references here to
// ServicesBinding.exitApplication that has landed in the framework.
// https://github.com/flutter/flutter/issues/121721
/// [ServicesBinding.exitApplication].
enum AppExitType {
/// Requests that the application start an orderly exit, sending a request
/// back to the framework through the [WidgetsBinding]. If that responds
Expand Down
5 changes: 3 additions & 2 deletions lib/web_ui/lib/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ abstract class PlatformDispatcher {
VoidCallback? get onLocaleChanged;
set onLocaleChanged(VoidCallback? callback);

String get initialLifecycleState => 'AppLifecycleState.resumed';
String get initialLifecycleState => '';

bool get alwaysUse24HourFormat;

Expand Down Expand Up @@ -247,10 +247,11 @@ class FrameTiming {
}

enum AppLifecycleState {
detached,
resumed,
inactive,
hidden,
paused,
detached,
}

enum AppExitResponse {
Expand Down
9 changes: 9 additions & 0 deletions lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
_addFontSizeObserver();
_addLocaleChangedListener();
registerHotRestartListener(dispose);
_setAppLifecycleState(ui.AppLifecycleState.resumed);
}

/// The [EnginePlatformDispatcher] singleton.
Expand Down Expand Up @@ -1005,6 +1006,14 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
_fontSizeObserver = null;
}

void _setAppLifecycleState(ui.AppLifecycleState state) {
sendPlatformMessage(
'flutter/lifecycle',
Uint8List.fromList(utf8.encode(state.toString())).buffer.asByteData(),
null,
);
}

/// A callback that is invoked whenever [textScaleFactor] changes value.
///
/// The framework invokes this callback in the same zone in which the
Expand Down
Loading

0 comments on commit 9fd0082

Please sign in to comment.