Revert "Adding app lifecycle notification for macOS and Linux, add hidden state." (flutter/engine#42384)

This commit is contained in:
Zachary Anderson
2023-05-28 00:02:58 -07:00
committed by GitHub
parent 0be6b8d6ef
commit 879917b1e6
28 changed files with 134 additions and 1112 deletions

View File

@@ -2463,7 +2463,6 @@ ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.cc + ../../.
ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.h + ../../../flutter/LICENSE 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.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h + ../../../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/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/byte_buffer_streams.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc + ../../../flutter/LICENSE
@@ -2681,7 +2680,6 @@ 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.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm + ../../../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/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/FlutterEngine.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.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 ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h + ../../../flutter/LICENSE
@@ -2693,8 +2691,6 @@ 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/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.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h + ../../../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.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm + ../../../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 ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h + ../../../flutter/LICENSE
@@ -5134,7 +5130,6 @@ FILE: ../../../flutter/shell/platform/common/accessibility_bridge.cc
FILE: ../../../flutter/shell/platform/common/accessibility_bridge.h 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.cc
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h 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/binary_messenger_impl.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h FILE: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc FILE: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc
@@ -5354,7 +5349,6 @@ 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.h
FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm 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/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/FlutterEngine.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h
@@ -5367,8 +5361,6 @@ 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/AccessibilityBridgeMacTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.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/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.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h

View File

@@ -1679,15 +1679,13 @@ class FrameTiming {
} }
} }
/// States that an application can be in once it is running. /// States that an application can be in.
/// ///
/// States not supported on a platform will be synthesized by the framework when /// The values below describe notifications from the operating system.
/// transitioning between states which are supported, so that all /// Applications should not expect to always receive all possible notifications.
/// implementations share the same state machine. /// For example, if the users pulls out the battery from the device, no
/// /// notification will be sent before the application is suddenly terminated,
/// The initial value for the state is the [detached] state, updated to the /// along with the rest of the operating system.
/// 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 /// 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 /// do not correspond one to one with the state names on all platforms. On
@@ -1698,52 +1696,15 @@ class FrameTiming {
/// Flutter enters the [paused] state. See the individual state's documentation /// Flutter enters the [paused] state. See the individual state's documentation
/// for descriptions of what they mean on each platform. /// 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: /// See also:
/// ///
/// * [AppLifecycleListener], an object used observe the lifecycle state that
/// provides state transition callbacks.
/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state /// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state
/// from the widgets layer. /// from the widgets layer.
/// * iOS's [IOKit activity /// * iOS's [IOKit activity lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc) documentation.
/// lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc) /// * Android's [activity lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle) documentation.
/// documentation. /// * macOS's [AppKit activity lifecycle](https://developer.apple.com/documentation/appkit/nsapplicationdelegate?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 { enum AppLifecycleState {
/// The application is still hosted by a Flutter engine but is detached from /// The application is visible and responsive to user input.
/// 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 /// On Android, this state corresponds to the Flutter host view having focus
/// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean)) /// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean))
@@ -1756,71 +1717,54 @@ enum AppLifecycleState {
/// was called with false), but hasn't had /// was called with false), but hasn't had
/// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause()) /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
/// called on it. /// called on it.
///
/// On iOS and macOS, this corresponds to the app running in the foreground
/// active state.
resumed, resumed,
/// At least one view of the application is visible, but none have input /// The application is in an inactive state and is not receiving user input.
/// focus. The application is otherwise running normally.
/// ///
/// On non-web desktop platforms, this corresponds to an application that is /// On iOS, this state corresponds to an app or the Flutter host view running
/// not in the foreground, but still has visible windows. /// 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 the web, this corresponds to an application that is running in a /// On Android, this corresponds to an app or the Flutter host view running in
/// window or tab that does not have input focus. /// Android's paused state (i.e.
///
/// 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 the Flutter host view running in Android's
/// paused state (i.e.
/// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause()) /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
/// has been called), or in Android's "resumed" state (i.e. /// has been called), or in Android's "resumed" state (i.e.
/// [`Activity.onResume`](https://developer.android.com/reference/android/app/Activity#onResume()) /// [`Activity.onResume`](https://developer.android.com/reference/android/app/Activity#onResume())
/// has been called) but does not have window focus. Examples of when apps /// 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 /// transition to this state include when the app is partially obscured or
/// another activity is focused, a app running in a split screen that isn't /// another activity is focused, such as: a split-screen app, a phone call, a
/// the current app, an app interrupted by a phone call, a picture-in-picture /// picture-in-picture app, a system dialog, another view, when the
/// app, a system dialog, another view. It will also be inactive when the
/// notification window shade is down, or the application switcher is visible. /// notification window shade is down, or the application switcher is visible.
/// ///
/// On Android and iOS, apps in this state should assume that they may be /// Apps in this state should assume that they may be [paused] at any time.
/// [hidden] and [paused] at any time.
inactive, inactive,
/// All views of an application are hidden, either because the application is /// The application is not currently visible to the user, not responding to
/// about to be paused (on iOS and Android), or because it has been minimized /// user input, and running in the background.
/// 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 /// When the application is in this state, the engine will not call the
/// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame]
/// callbacks. /// callbacks.
///
/// This state is only entered on iOS and Android.
paused, 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,
} }
/// The possible responses to a request to exit the application. /// The possible responses to a request to exit the application.
/// ///
/// The request is typically responded to by creating an [AppLifecycleListener] /// The request is typically responded to by a [WidgetsBindingObserver].
/// and supplying an [AppLifecycleListener.onExitRequested] callback, or by // TODO(gspencergoog): Insert doc references here to AppLifecycleListener and to
/// overriding [WidgetsBindingObserver.didRequestAppExit]. // the actual function called on WidgetsBindingObserver once those have landed
// in the framework. https://github.com/flutter/flutter/issues/121721
enum AppExitResponse { enum AppExitResponse {
/// Exiting the application can proceed. /// Exiting the application can proceed.
exit, exit,
@@ -1829,7 +1773,10 @@ enum AppExitResponse {
} }
/// The type of application exit to perform when calling /// The type of application exit to perform when calling
/// [ServicesBinding.exitApplication]. /// `ServicesBinding.exitApplication`.
// TODO(gspencergoog): Insert doc references here to
// ServicesBinding.exitApplication that has landed in the framework.
// https://github.com/flutter/flutter/issues/121721
enum AppExitType { enum AppExitType {
/// Requests that the application start an orderly exit, sending a request /// Requests that the application start an orderly exit, sending a request
/// back to the framework through the [WidgetsBinding]. If that responds /// back to the framework through the [WidgetsBinding]. If that responds

View File

@@ -104,7 +104,7 @@ abstract class PlatformDispatcher {
VoidCallback? get onLocaleChanged; VoidCallback? get onLocaleChanged;
set onLocaleChanged(VoidCallback? callback); set onLocaleChanged(VoidCallback? callback);
String get initialLifecycleState => ''; String get initialLifecycleState => 'AppLifecycleState.resumed';
bool get alwaysUse24HourFormat; bool get alwaysUse24HourFormat;
@@ -247,11 +247,10 @@ class FrameTiming {
} }
enum AppLifecycleState { enum AppLifecycleState {
detached,
resumed, resumed,
inactive, inactive,
hidden,
paused, paused,
detached,
} }
enum AppExitResponse { enum AppExitResponse {

View File

@@ -104,7 +104,6 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
_addFontSizeObserver(); _addFontSizeObserver();
_addLocaleChangedListener(); _addLocaleChangedListener();
registerHotRestartListener(dispose); registerHotRestartListener(dispose);
_setAppLifecycleState(ui.AppLifecycleState.resumed);
} }
/// The [EnginePlatformDispatcher] singleton. /// The [EnginePlatformDispatcher] singleton.
@@ -1006,14 +1005,6 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
_fontSizeObserver = null; _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. /// A callback that is invoked whenever [textScaleFactor] changes value.
/// ///
/// The framework invokes this callback in the same zone in which the /// The framework invokes this callback in the same zone in which the

View File

@@ -22,21 +22,14 @@ public class LifecycleChannel {
private static final String TAG = "LifecycleChannel"; private static final String TAG = "LifecycleChannel";
private static final String CHANNEL_NAME = "flutter/lifecycle"; private static final String CHANNEL_NAME = "flutter/lifecycle";
// This enum should match the Dart enum of the same name. // These should stay in sync with the AppLifecycleState enum in the framework.
// private static final String RESUMED = "AppLifecycleState.resumed";
// HIDDEN isn't used on Android (it's synthesized in the Framework code). It's private static final String INACTIVE = "AppLifecycleState.inactive";
// only listed here so that apicheck_test.dart can make sure that the states here private static final String PAUSED = "AppLifecycleState.paused";
// match the Dart code. private static final String DETACHED = "AppLifecycleState.detached";
private enum AppLifecycleState {
DETACHED,
RESUMED,
INACTIVE,
HIDDEN,
PAUSED,
};
private AppLifecycleState lastAndroidState = null; private String lastAndroidState = "";
private AppLifecycleState lastFlutterState = null; private String lastFlutterState = "";
private boolean lastFocus = true; private boolean lastFocus = true;
@NonNull private final BasicMessageChannel<String> channel; @NonNull private final BasicMessageChannel<String> channel;
@@ -62,39 +55,21 @@ public class LifecycleChannel {
// | Stopped | false | paused | // | Stopped | false | paused |
// | Detached | true | detached | // | Detached | true | detached |
// | Detached | false | detached | // | Detached | false | detached |
// private void sendState(String state, boolean hasFocus) {
// The hidden state isn't used on Android, it's synthesized in the Framework
// code when transitioning between paused and inactive in either direction.
private void sendState(AppLifecycleState state, boolean hasFocus) {
if (lastAndroidState == state && hasFocus == lastFocus) { if (lastAndroidState == state && hasFocus == lastFocus) {
// No inputs changed, so Flutter state could not have changed. // No inputs changed, so Flutter state could not have changed.
return; return;
} }
if (state == null && lastAndroidState == null) { String newState;
// If we're responding to a focus change before the state is set, just if (state == RESUMED) {
// keep the last reported focus state and don't send anything to the // Focus is only taken into account when the Android state is "Resumed".
// framework. This could happen if focus events and lifecycle events are // In all other states, focus is ignored, because we can't know what order
// delivered out of the expected order. // Android lifecycle notifications and window focus notifications events
lastFocus = hasFocus; // will arrive in, and those states don't send input events anyhow.
return; newState = hasFocus ? RESUMED : INACTIVE;
} else {
newState = state;
} }
AppLifecycleState newState = null;
switch (state) {
case 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 ? AppLifecycleState.RESUMED : AppLifecycleState.INACTIVE;
break;
case INACTIVE:
case HIDDEN:
case PAUSED:
case DETACHED:
newState = state;
break;
}
// Keep the last reported values for future updates. // Keep the last reported values for future updates.
lastAndroidState = state; lastAndroidState = state;
lastFocus = hasFocus; lastFocus = hasFocus;
@@ -102,14 +77,12 @@ public class LifecycleChannel {
// No change in the resulting Flutter state, so don't report anything. // No change in the resulting Flutter state, so don't report anything.
return; return;
} }
String message = "AppLifecycleState." + newState.name().toLowerCase(); Log.v(TAG, "Sending " + newState + " message.");
Log.v(TAG, "Sending " + message + " message."); channel.send(newState);
channel.send(message);
lastFlutterState = newState; lastFlutterState = newState;
} }
// Called if at least one window in the app has focus, even if the focused // Called if at least one window in the app has focus.
// window doesn't contain a Flutter view.
public void aWindowIsFocused() { public void aWindowIsFocused() {
sendState(lastAndroidState, true); sendState(lastAndroidState, true);
} }
@@ -120,18 +93,18 @@ public class LifecycleChannel {
} }
public void appIsResumed() { public void appIsResumed() {
sendState(AppLifecycleState.RESUMED, lastFocus); sendState(RESUMED, lastFocus);
} }
public void appIsInactive() { public void appIsInactive() {
sendState(AppLifecycleState.INACTIVE, lastFocus); sendState(INACTIVE, lastFocus);
} }
public void appIsPaused() { public void appIsPaused() {
sendState(AppLifecycleState.PAUSED, lastFocus); sendState(PAUSED, lastFocus);
} }
public void appIsDetached() { public void appIsDetached() {
sendState(AppLifecycleState.DETACHED, lastFocus); sendState(DETACHED, lastFocus);
} }
} }

View File

@@ -58,10 +58,7 @@ source_set("common_cpp_input") {
} }
source_set("common_cpp_enums") { source_set("common_cpp_enums") {
public = [ public = [ "platform_provided_menu.h" ]
"app_lifecycle_state.h",
"platform_provided_menu.h",
]
public_configs = [ public_configs = [
"//flutter:config", "//flutter:config",

View File

@@ -1,89 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_APP_LIFECYCLE_STATE_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_APP_LIFECYCLE_STATE_H_
namespace flutter {
/**
* These constants describe the possible lifecycle states of the application.
* They must be kept up to date with changes in the framework's
* AppLifecycleState enum. They are passed to the embedder's |SetLifecycleState|
* function.
*
* 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.
*
* Here is the state machine:
*
* +-----------+ +-----------+
* | detached |------------------------------>| resumed |
* +-----------+ +-----------+
* ^ ^
* | |
* | v
* +-----------+ +--------------+ +-----------+
* | paused |<------>| hidden |<----->| inactive |
* +-----------+ +--------------+ +-----------+
*/
enum class AppLifecycleState {
/**
* Corresponds to the Framework's AppLifecycleState.detached: The initial
* state of the state machine. On Android and iOS, also the final state of the
* state machine when all views are detached. Other platforms do not enter
* this state again after initially leaving it.
*/
kDetached,
/**
* Corresponds to the Framework's AppLifecycleState.resumed: The nominal
* "running" state of the application. The application is visible, has input
* focus, and is running.
*/
kResumed,
/**
* Corresponds to the Framework's AppLifecycleState.inactive: At least one
* view of the application is visible, but none have input focus. The
* application is otherwise running normally.
*/
kInactive,
/**
* Corresponds to the Framework's AppLifecycleState.hidden: All views of an
* application are hidden, either because the application is being stopped (on
* iOS and Android), or because it is being minimized or on a desktop that is
* no longer visible (on desktop), or on a tab that is no longer visible (on
* web).
*/
kHidden,
/**
* Corresponds to the Framework's AppLifecycleState.paused: The application is
* not running, and can be detached or started again at any time. This state
* is typically only entered into on iOS and Android.
*/
kPaused,
};
constexpr const char* AppLifecycleStateToString(AppLifecycleState state) {
switch (state) {
case AppLifecycleState::kDetached:
return "AppLifecycleState.detached";
case AppLifecycleState::kResumed:
return "AppLifecycleState.resumed";
case AppLifecycleState::kInactive:
return "AppLifecycleState.inactive";
case AppLifecycleState::kHidden:
return "AppLifecycleState.hidden";
case AppLifecycleState::kPaused:
return "AppLifecycleState.paused";
}
}
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_COMMON_APP_LIFECYCLE_STATE_H_

View File

@@ -38,7 +38,6 @@ _framework_binary_subpath = "Versions/A/$_flutter_framework_name"
# the Flutter engine source root. # the Flutter engine source root.
_flutter_framework_headers = [ _flutter_framework_headers = [
"framework/Headers/FlutterAppDelegate.h", "framework/Headers/FlutterAppDelegate.h",
"framework/Headers/FlutterAppLifecycleDelegate.h",
"framework/Headers/FlutterEngine.h", "framework/Headers/FlutterEngine.h",
"framework/Headers/FlutterMacOS.h", "framework/Headers/FlutterMacOS.h",
"framework/Headers/FlutterPlatformViews.h", "framework/Headers/FlutterPlatformViews.h",
@@ -57,7 +56,6 @@ source_set("flutter_framework_source") {
"framework/Source/AccessibilityBridgeMac.h", "framework/Source/AccessibilityBridgeMac.h",
"framework/Source/AccessibilityBridgeMac.mm", "framework/Source/AccessibilityBridgeMac.mm",
"framework/Source/FlutterAppDelegate.mm", "framework/Source/FlutterAppDelegate.mm",
"framework/Source/FlutterAppLifecycleDelegate.mm",
"framework/Source/FlutterBackingStore.h", "framework/Source/FlutterBackingStore.h",
"framework/Source/FlutterBackingStore.mm", "framework/Source/FlutterBackingStore.mm",
"framework/Source/FlutterChannelKeyResponder.h", "framework/Source/FlutterChannelKeyResponder.h",
@@ -169,7 +167,6 @@ executable("flutter_desktop_darwin_unittests") {
sources = [ sources = [
"framework/Source/AccessibilityBridgeMacTest.mm", "framework/Source/AccessibilityBridgeMacTest.mm",
"framework/Source/FlutterAppLifecycleDelegateTest.mm",
"framework/Source/FlutterChannelKeyResponderTest.mm", "framework/Source/FlutterChannelKeyResponderTest.mm",
"framework/Source/FlutterCompositorTest.mm", "framework/Source/FlutterCompositorTest.mm",
"framework/Source/FlutterEmbedderExternalTextureTest.mm", "framework/Source/FlutterEmbedderExternalTextureTest.mm",

View File

@@ -7,11 +7,10 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "FlutterAppLifecycleDelegate.h"
#import "FlutterMacros.h" #import "FlutterMacros.h"
/** /**
* |NSApplicationDelegate| subclass for simple apps that want default behavior. * `NSApplicationDelegate` subclass for simple apps that want default behavior.
* *
* This class implements the following behaviors: * This class implements the following behaviors:
* * Updates the application name of items in the application menu to match the name in * * Updates the application name of items in the application menu to match the name in
@@ -34,23 +33,11 @@ FLUTTER_DARWIN_EXPORT
@property(weak, nonatomic) IBOutlet NSMenu* applicationMenu; @property(weak, nonatomic) IBOutlet NSMenu* applicationMenu;
/** /**
* The primary application window containing a FlutterViewController. This is * The primary application window containing a FlutterViewController. This is primarily intended
* primarily intended for use in single-window applications. * for use in single-window applications.
*/ */
@property(weak, nonatomic) IBOutlet NSWindow* mainFlutterWindow; @property(weak, nonatomic) IBOutlet NSWindow* mainFlutterWindow;
/**
* Adds an object implementing |FlutterAppLifecycleDelegate| to the list of
* delegates to be informed of application lifecycle events.
*/
- (void)addApplicationLifecycleDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate;
/**
* Removes an object implementing |FlutterAppLifecycleDelegate| to the list of
* delegates to be informed of application lifecycle events.
*/
- (void)removeApplicationLifecycleDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate;
@end @end
#endif // FLUTTER_FLUTTERAPPDELEGATE_H_ #endif // FLUTTER_FLUTTERAPPDELEGATE_H_

View File

@@ -1,137 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERAPPLIFECYCLEDELEGATE_H_
#define FLUTTER_FLUTTERAPPLIFECYCLEDELEGATE_H_
#import <Cocoa/Cocoa.h>
#include <Foundation/Foundation.h>
#import "FlutterMacros.h"
#import "FlutterPluginMacOS.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
/**
* Protocol for listener of lifecycle events from the NSApplication, typically a
* FlutterPlugin.
*/
@protocol FlutterAppLifecycleDelegate <NSObject>
@optional
/**
* Called when the |FlutterAppDelegate| gets the applicationWillFinishLaunching
* notification.
*/
- (void)handleWillFinishLaunching:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationDidFinishLaunching
* notification.
*/
- (void)handleDidFinishLaunching:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
* notification.
*/
- (void)handleWillBecomeActive:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationDidBecomeActive
* notification.
*/
- (void)handleDidBecomeActive:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationWillResignActive
* notification.
*/
- (void)handleWillResignActive:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationWillResignActive
* notification.
*/
- (void)handleDidResignActive:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationWillHide
* notification.
*/
- (void)handleWillHide:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationDidHide
* notification.
*/
- (void)handleDidHide:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationWillUnhide
* notification.
*/
- (void)handleWillUnhide:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationDidUnhide
* notification.
*/
- (void)handleDidUnhide:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationDidUnhide
* notification.
*/
- (void)handleDidChangeScreenParameters:(NSNotification*)notification;
/**
* Called when the |FlutterAppDelegate| gets the applicationDidUnhide
* notification.
*/
- (void)handleDidChangeOcclusionState:(NSNotification*)notification API_AVAILABLE(macos(10.9));
/**
* Called when the |FlutterAppDelegate| gets the applicationWillTerminate
* notification.
*
* 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 this notification may be skipped.
*/
- (void)handleWillTerminate:(NSNotification*)notification;
@end
#pragma mark -
/**
* Propagates `NSAppDelegate` callbacks to registered delegates.
*/
FLUTTER_DARWIN_EXPORT
@interface FlutterAppLifecycleRegistrar : NSObject <FlutterAppLifecycleDelegate>
/**
* Registers `delegate` to receive lifecycle callbacks via this
* FlutterAppLifecycleDelegate as long as it is alive.
*
* `delegate` will only be referenced weakly.
*/
- (void)addDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate;
/**
* Unregisters `delegate` so that it will no longer receive life cycle callbacks
* via this FlutterAppLifecycleDelegate.
*
* `delegate` will only be referenced weakly.
*/
- (void)removeDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate;
@end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERAPPLIFECYCLEDELEGATE_H_

View File

@@ -9,7 +9,6 @@
#include <stdint.h> #include <stdint.h>
#import "FlutterAppLifecycleDelegate.h"
#import "FlutterBinaryMessenger.h" #import "FlutterBinaryMessenger.h"
#import "FlutterDartProject.h" #import "FlutterDartProject.h"
#import "FlutterMacros.h" #import "FlutterMacros.h"
@@ -27,8 +26,7 @@
* code. * code.
*/ */
FLUTTER_DARWIN_EXPORT FLUTTER_DARWIN_EXPORT
@interface FlutterEngine @interface FlutterEngine : NSObject <FlutterTextureRegistry, FlutterPluginRegistry>
: NSObject <FlutterTextureRegistry, FlutterPluginRegistry, FlutterAppLifecycleDelegate>
/** /**
* Initializes an engine with the given project. * Initializes an engine with the given project.

View File

@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
#import "FlutterAppDelegate.h" #import "FlutterAppDelegate.h"
#import "FlutterAppLifecycleDelegate.h"
#import "FlutterBinaryMessenger.h" #import "FlutterBinaryMessenger.h"
#import "FlutterChannels.h" #import "FlutterChannels.h"
#import "FlutterCodecs.h" #import "FlutterCodecs.h"

View File

@@ -8,7 +8,8 @@
#import "FlutterCodecs.h" #import "FlutterCodecs.h"
#import "FlutterMacros.h" #import "FlutterMacros.h"
NS_ASSUME_NONNULL_BEGIN // TODO: Merge this file and FlutterPluginRegistrarMacOS.h with the iOS FlutterPlugin.h, sharing
// all but the platform-specific methods.
@protocol FlutterPluginRegistrar; @protocol FlutterPluginRegistrar;
@@ -28,7 +29,7 @@ FLUTTER_DARWIN_EXPORT
* Creates an instance of the plugin to register with |registrar| using the desired * Creates an instance of the plugin to register with |registrar| using the desired
* FlutterPluginRegistrar methods. * FlutterPluginRegistrar methods.
*/ */
+ (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar; + (void)registerWithRegistrar:(nonnull id<FlutterPluginRegistrar>)registrar;
@optional @optional
@@ -43,8 +44,6 @@ FLUTTER_DARWIN_EXPORT
* - Any other value (including nil) to indicate success. The value will * - Any other value (including nil) to indicate success. The value will
* be returned to the Flutter caller, and must be serializable to JSON. * be returned to the Flutter caller, and must be serializable to JSON.
*/ */
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; - (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result;
NS_ASSUME_NONNULL_END
@end @end

View File

@@ -8,7 +8,6 @@
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
#include "flutter/fml/logging.h" #include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"
#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder.h"
@interface FlutterAppDelegate () @interface FlutterAppDelegate ()
@@ -18,15 +17,17 @@
*/ */
- (NSString*)applicationName; - (NSString*)applicationName;
@property(nonatomic) FlutterAppLifecycleRegistrar* lifecycleRegistrar;
@end @end
@implementation FlutterAppDelegate @implementation FlutterAppDelegate
// TODO(gspencergoog): Implement application lifecycle forwarding to plugins here, as is done
// on iOS. Currently macOS plugins don't have access to lifecycle messages.
// https://github.com/flutter/flutter/issues/30735
- (instancetype)init { - (instancetype)init {
if (self = [super init]) { if (self = [super init]) {
_terminationHandler = nil; _terminationHandler = nil;
_lifecycleRegistrar = [[FlutterAppLifecycleRegistrar alloc] init];
} }
return self; return self;
} }
@@ -41,16 +42,6 @@
} }
} }
#pragma mark - Delegate handling
- (void)addApplicationLifecycleDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
[[self lifecycleRegistrar] addDelegate:delegate];
}
- (void)removeApplicationLifecycleDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
[[self lifecycleRegistrar] removeDelegate:delegate];
}
#pragma mark Private Methods #pragma mark Private Methods
- (NSString*)applicationName { - (NSString*)applicationName {

View File

@@ -1,130 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"
#include <AppKit/AppKit.h>
#include <AppKit/NSApplication.h>
#include <Foundation/Foundation.h>
#include <objc/message.h>
#include "flutter/fml/logging.h"
#include "flutter/fml/paths.h"
@interface FlutterAppLifecycleRegistrar ()
@end
@implementation FlutterAppLifecycleRegistrar {
NSMutableArray* _notificationUnsubscribers;
// Weak references to registered plugins.
NSPointerArray* _delegates;
}
- (void)addObserverFor:(NSString*)name selector:(SEL)selector {
[[NSNotificationCenter defaultCenter] addObserver:self selector:selector name:name object:nil];
__block NSObject* blockSelf = self;
dispatch_block_t unsubscribe = ^{
[[NSNotificationCenter defaultCenter] removeObserver:blockSelf name:name object:nil];
};
[_notificationUnsubscribers addObject:[unsubscribe copy]];
}
- (instancetype)init {
if (self = [super init]) {
_notificationUnsubscribers = [[NSMutableArray alloc] init];
// Using a macro to avoid errors where the notification doesn't match the
// selector.
#ifdef OBSERVE_NOTIFICATION
#error OBSERVE_NOTIFICATION ALREADY DEFINED!
#else
#define OBSERVE_NOTIFICATION(SELECTOR) \
[self addObserverFor:NSApplication##SELECTOR##Notification selector:@selector(handle##SELECTOR:)]
#endif
OBSERVE_NOTIFICATION(WillFinishLaunching);
OBSERVE_NOTIFICATION(DidFinishLaunching);
OBSERVE_NOTIFICATION(WillBecomeActive);
OBSERVE_NOTIFICATION(DidBecomeActive);
OBSERVE_NOTIFICATION(WillResignActive);
OBSERVE_NOTIFICATION(DidResignActive);
OBSERVE_NOTIFICATION(WillTerminate);
OBSERVE_NOTIFICATION(WillHide);
OBSERVE_NOTIFICATION(DidHide);
OBSERVE_NOTIFICATION(WillUnhide);
OBSERVE_NOTIFICATION(DidUnhide);
OBSERVE_NOTIFICATION(DidChangeScreenParameters);
OBSERVE_NOTIFICATION(DidChangeOcclusionState);
#undef OBSERVE_NOTIFICATION
_delegates = [NSPointerArray weakObjectsPointerArray];
}
return self;
}
- (void)dealloc {
for (dispatch_block_t unsubscribe in _notificationUnsubscribers) {
unsubscribe();
}
[_notificationUnsubscribers removeAllObjects];
_delegates = nil;
_notificationUnsubscribers = nil;
}
static BOOL IsPowerOfTwo(NSUInteger x) {
return x != 0 && (x & (x - 1)) == 0;
}
- (void)addDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
[_delegates addPointer:(__bridge void*)delegate];
if (IsPowerOfTwo([_delegates count])) {
[_delegates compact];
}
}
- (void)removeDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
NSUInteger index = [[_delegates allObjects] indexOfObject:delegate];
if (index >= 0) {
[_delegates removePointerAtIndex:index];
}
}
// This isn't done via performSelector because that can cause leaks due to the
// selector not being known. Using a macro to avoid mismatch errors between the
// notification and the selector.
#ifdef DISTRIBUTE_NOTIFICATION
#error DISTRIBUTE_NOTIFICATION ALREADY DEFINED!
#else
#define DISTRIBUTE_NOTIFICATION(SELECTOR) \
-(void)handle##SELECTOR : (NSNotification*)notification { \
for (NSObject<FlutterAppLifecycleDelegate> * delegate in _delegates) { \
if (!delegate) { \
continue; \
} \
if ([delegate respondsToSelector:@selector(handle##SELECTOR:)]) { \
[delegate handle##SELECTOR:notification]; \
} \
} \
}
#endif
DISTRIBUTE_NOTIFICATION(WillFinishLaunching)
DISTRIBUTE_NOTIFICATION(DidFinishLaunching)
DISTRIBUTE_NOTIFICATION(WillBecomeActive)
DISTRIBUTE_NOTIFICATION(DidBecomeActive)
DISTRIBUTE_NOTIFICATION(WillResignActive)
DISTRIBUTE_NOTIFICATION(DidResignActive)
DISTRIBUTE_NOTIFICATION(WillTerminate)
DISTRIBUTE_NOTIFICATION(WillHide)
DISTRIBUTE_NOTIFICATION(WillUnhide)
DISTRIBUTE_NOTIFICATION(DidHide)
DISTRIBUTE_NOTIFICATION(DidUnhide)
DISTRIBUTE_NOTIFICATION(DidChangeScreenParameters)
DISTRIBUTE_NOTIFICATION(DidChangeOcclusionState)
#undef DISTRIBUTE_NOTIFICATION
@end

View File

@@ -1,223 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"
#import "flutter/testing/testing.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
@interface TestFlutterAppLifecycleDelegate : NSObject <FlutterAppLifecycleDelegate>
@property(nonatomic, readwrite, nullable) NSNotification* lastNotification;
@end
@implementation TestFlutterAppLifecycleDelegate
- (void)setNotification:(NSNotification*)notification {
self.lastNotification = notification;
}
- (void)handleWillFinishLaunching:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidFinishLaunching:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleWillBecomeActive:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidBecomeActive:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleWillResignActive:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidResignActive:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleWillHide:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidHide:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleWillUnhide:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidUnhide:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidChangeScreenParameters:(NSNotification*)notification {
[self setNotification:notification];
}
- (void)handleDidChangeOcclusionState:(NSNotification*)notification API_AVAILABLE(macos(10.9)) {
[self setNotification:notification];
}
- (void)handleWillTerminate:(NSNotification*)notification {
[self setNotification:notification];
}
@end
namespace flutter::testing {
TEST(FlutterAppLifecycleDelegateTest, RespondsToWillFinishLaunching) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* willFinishLaunching =
[NSNotification notificationWithName:NSApplicationWillFinishLaunchingNotification object:nil];
[registrar handleWillFinishLaunching:willFinishLaunching];
EXPECT_EQ([delegate lastNotification], willFinishLaunching);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidFinishLaunching) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didFinishLaunching =
[NSNotification notificationWithName:NSApplicationDidFinishLaunchingNotification object:nil];
[registrar handleDidFinishLaunching:didFinishLaunching];
EXPECT_EQ([delegate lastNotification], didFinishLaunching);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToWillBecomeActive) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* willBecomeActive =
[NSNotification notificationWithName:NSApplicationWillBecomeActiveNotification object:nil];
[registrar handleWillBecomeActive:willBecomeActive];
EXPECT_EQ([delegate lastNotification], willBecomeActive);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidBecomeActive) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didBecomeActive =
[NSNotification notificationWithName:NSApplicationDidBecomeActiveNotification object:nil];
[registrar handleDidBecomeActive:didBecomeActive];
EXPECT_EQ([delegate lastNotification], didBecomeActive);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToWillResignActive) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* willResignActive =
[NSNotification notificationWithName:NSApplicationWillResignActiveNotification object:nil];
[registrar handleWillResignActive:willResignActive];
EXPECT_EQ([delegate lastNotification], willResignActive);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidResignActive) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didResignActive =
[NSNotification notificationWithName:NSApplicationDidResignActiveNotification object:nil];
[registrar handleDidResignActive:didResignActive];
EXPECT_EQ([delegate lastNotification], didResignActive);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToWillTerminate) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* applicationWillTerminate =
[NSNotification notificationWithName:NSApplicationWillTerminateNotification object:nil];
[registrar handleWillTerminate:applicationWillTerminate];
EXPECT_EQ([delegate lastNotification], applicationWillTerminate);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToWillHide) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* willHide = [NSNotification notificationWithName:NSApplicationWillHideNotification
object:nil];
[registrar handleWillHide:willHide];
EXPECT_EQ([delegate lastNotification], willHide);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToWillUnhide) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* willUnhide =
[NSNotification notificationWithName:NSApplicationWillUnhideNotification object:nil];
[registrar handleWillUnhide:willUnhide];
EXPECT_EQ([delegate lastNotification], willUnhide);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidHide) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didHide = [NSNotification notificationWithName:NSApplicationDidHideNotification
object:nil];
[registrar handleDidHide:didHide];
EXPECT_EQ([delegate lastNotification], didHide);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidUnhide) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didUnhide =
[NSNotification notificationWithName:NSApplicationDidUnhideNotification object:nil];
[registrar handleDidUnhide:didUnhide];
EXPECT_EQ([delegate lastNotification], didUnhide);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidChangeScreenParameters) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didChangeScreenParameters =
[NSNotification notificationWithName:NSApplicationDidChangeScreenParametersNotification
object:nil];
[registrar handleDidChangeScreenParameters:didChangeScreenParameters];
EXPECT_EQ([delegate lastNotification], didChangeScreenParameters);
}
TEST(FlutterAppLifecycleDelegateTest, RespondsToDidChangeOcclusionState) {
FlutterAppLifecycleRegistrar* registrar = [[FlutterAppLifecycleRegistrar alloc] init];
TestFlutterAppLifecycleDelegate* delegate = [[TestFlutterAppLifecycleDelegate alloc] init];
[registrar addDelegate:delegate];
NSNotification* didChangeOcclusionState =
[NSNotification notificationWithName:NSApplicationDidChangeOcclusionStateNotification
object:nil];
if ([registrar respondsToSelector:@selector(handleDidChangeOcclusionState:)]) {
[registrar handleDidChangeOcclusionState:didChangeOcclusionState];
EXPECT_EQ([delegate lastNotification], didChangeOcclusionState);
}
}
} // namespace flutter::testing

View File

@@ -9,7 +9,6 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "flutter/shell/platform/common/app_lifecycle_state.h"
#include "flutter/shell/platform/common/engine_switches.h" #include "flutter/shell/platform/common/engine_switches.h"
#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder.h"
@@ -25,8 +24,6 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h"
NSString* const kFlutterPlatformChannel = @"flutter/platform"; NSString* const kFlutterPlatformChannel = @"flutter/platform";
NSString* const kFlutterSettingsChannel = @"flutter/settings";
NSString* const kFlutterLifecycleChannel = @"flutter/lifecycle";
/** /**
* Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
@@ -176,7 +173,8 @@ constexpr char kTextPlainFormat[] = "text/plain";
_terminator = terminator ? terminator : ^(id sender) { _terminator = terminator ? terminator : ^(id sender) {
// Default to actually terminating the application. The terminator exists to // Default to actually terminating the application. The terminator exists to
// allow tests to override it so that an actual exit doesn't occur. // allow tests to override it so that an actual exit doesn't occur.
[[NSApplication sharedApplication] terminate:sender]; NSApplication* flutterApp = [NSApplication sharedApplication];
[flutterApp terminate:sender];
}; };
FlutterAppDelegate* appDelegate = FlutterAppDelegate* appDelegate =
(FlutterAppDelegate*)[[NSApplication sharedApplication] delegate]; (FlutterAppDelegate*)[[NSApplication sharedApplication] delegate];
@@ -392,14 +390,7 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
FlutterThreadSynchronizer* _threadSynchronizer; FlutterThreadSynchronizer* _threadSynchronizer;
// The next available view ID.
int _nextViewId; int _nextViewId;
// Whether the application is currently the active application.
BOOL _active;
// Whether any portion of the application is currently visible.
BOOL _visible;
} }
- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
@@ -411,8 +402,7 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
allowHeadlessExecution:(BOOL)allowHeadlessExecution { allowHeadlessExecution:(BOOL)allowHeadlessExecution {
self = [super init]; self = [super init];
NSAssert(self, @"Super init cannot be nil"); NSAssert(self, @"Super init cannot be nil");
_active = NO;
_visible = NO;
_project = project ?: [[FlutterDartProject alloc] init]; _project = project ?: [[FlutterDartProject alloc] init];
_messengerHandlers = [[NSMutableDictionary alloc] init]; _messengerHandlers = [[NSMutableDictionary alloc] init];
_currentMessengerConnection = 1; _currentMessengerConnection = 1;
@@ -443,19 +433,11 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
[self setUpPlatformViewChannel]; [self setUpPlatformViewChannel];
[self setUpAccessibilityChannel]; [self setUpAccessibilityChannel];
[self setUpNotificationCenterListeners]; [self setUpNotificationCenterListeners];
FlutterAppDelegate* appDelegate =
reinterpret_cast<FlutterAppDelegate*>([[NSApplication sharedApplication] delegate]);
[appDelegate addApplicationLifecycleDelegate:self];
return self; return self;
} }
- (void)dealloc { - (void)dealloc {
FlutterAppDelegate* appDelegate =
reinterpret_cast<FlutterAppDelegate*>([[NSApplication sharedApplication] delegate]);
if (appDelegate != nil) {
[appDelegate removeApplicationLifecycleDelegate:self];
}
@synchronized(_isResponseValid) { @synchronized(_isResponseValid) {
[_isResponseValid removeAllObjects]; [_isResponseValid removeAllObjects];
[_isResponseValid addObject:@NO]; [_isResponseValid addObject:@NO];
@@ -1015,11 +997,11 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
[FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]]; [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]];
[FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]]; [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
_settingsChannel = _settingsChannel =
[FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel [FlutterBasicMessageChannel messageChannelWithName:@"flutter/settings"
binaryMessenger:self.binaryMessenger binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]]; codec:[FlutterJSONMessageCodec sharedInstance]];
_platformChannel = _platformChannel =
[FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel [FlutterMethodChannel methodChannelWithName:@"flutter/platform"
binaryMessenger:self.binaryMessenger binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]; codec:[FlutterJSONMethodCodec sharedInstance]];
[_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
@@ -1077,7 +1059,7 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
} }
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([call.method isEqualToString:@"SystemNavigator.pop"]) { if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
[[NSApplication sharedApplication] terminate:self]; [NSApp terminate:self];
result(nil); result(nil);
} else if ([call.method isEqualToString:@"SystemSound.play"]) { } else if ([call.method isEqualToString:@"SystemSound.play"]) {
[self playSystemSound:call.arguments]; [self playSystemSound:call.arguments];
@@ -1139,60 +1121,6 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
return _threadSynchronizer; return _threadSynchronizer;
} }
#pragma mark - FlutterAppLifecycleDelegate
- (void)setApplicationState:(flutter::AppLifecycleState)state {
NSString* nextState =
[[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
[self sendOnChannel:kFlutterLifecycleChannel
message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
}
/**
* Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
* notification.
*/
- (void)handleWillBecomeActive:(NSNotification*)notification {
_active = YES;
if (!_visible) {
[self setApplicationState:flutter::AppLifecycleState::kHidden];
} else {
[self setApplicationState:flutter::AppLifecycleState::kResumed];
}
}
/**
* Called when the |FlutterAppDelegate| gets the applicationWillResignActive
* notification.
*/
- (void)handleWillResignActive:(NSNotification*)notification {
_active = NO;
if (!_visible) {
[self setApplicationState:flutter::AppLifecycleState::kHidden];
} else {
[self setApplicationState:flutter::AppLifecycleState::kInactive];
}
}
/**
* Called when the |FlutterAppDelegate| gets the applicationDidUnhide
* notification.
*/
- (void)handleDidChangeOcclusionState:(NSNotification*)notification API_AVAILABLE(macos(10.9)) {
NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
if (occlusionState & NSApplicationOcclusionStateVisible) {
_visible = YES;
if (_active) {
[self setApplicationState:flutter::AppLifecycleState::kResumed];
} else {
[self setApplicationState:flutter::AppLifecycleState::kInactive];
}
} else {
_visible = NO;
[self setApplicationState:flutter::AppLifecycleState::kHidden];
}
}
#pragma mark - FlutterBinaryMessenger #pragma mark - FlutterBinaryMessenger
- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {

View File

@@ -805,75 +805,55 @@ TEST_F(FlutterEngineTest, HandleAccessibilityEvent) {
EXPECT_TRUE(announced); EXPECT_TRUE(announced);
} }
TEST_F(FlutterEngineTest, HandleLifecycleStates) API_AVAILABLE(macos(10.9)) { TEST_F(FlutterEngineTest, RunWithEntrypointUpdatesDisplayConfig) {
__block flutter::AppLifecycleState sentState; BOOL updated = NO;
id engineMock = CreateMockFlutterEngine(nil); FlutterEngine* engine = GetFlutterEngine();
auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
// Have to enumerate all the values because OCMStub can't capture engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
// non-Objective-C object arguments. NotifyDisplayUpdate, ([&updated, &original_update_displays](
OCMStub([engineMock setApplicationState:flutter::AppLifecycleState::kDetached]) auto engine, auto update_type, auto* displays, auto display_count) {
.andDo((^(NSInvocation* invocation) { updated = YES;
sentState = flutter::AppLifecycleState::kDetached; return original_update_displays(engine, update_type, displays, display_count);
}));
OCMStub([engineMock setApplicationState:flutter::AppLifecycleState::kResumed])
.andDo((^(NSInvocation* invocation) {
sentState = flutter::AppLifecycleState::kResumed;
}));
OCMStub([engineMock setApplicationState:flutter::AppLifecycleState::kInactive])
.andDo((^(NSInvocation* invocation) {
sentState = flutter::AppLifecycleState::kInactive;
}));
OCMStub([engineMock setApplicationState:flutter::AppLifecycleState::kHidden])
.andDo((^(NSInvocation* invocation) {
sentState = flutter::AppLifecycleState::kHidden;
}));
OCMStub([engineMock setApplicationState:flutter::AppLifecycleState::kPaused])
.andDo((^(NSInvocation* invocation) {
sentState = flutter::AppLifecycleState::kPaused;
})); }));
__block NSApplicationOcclusionState visibility = NSApplicationOcclusionStateVisible; EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
id mockApplication = OCMPartialMock([NSApplication sharedApplication]); EXPECT_TRUE(updated);
OCMStub((NSApplicationOcclusionState)[mockApplication occlusionState])
.andDo(^(NSInvocation* invocation) {
[invocation setReturnValue:&visibility];
});
NSNotification* willBecomeActive = updated = NO;
[[NSNotification alloc] initWithName:NSApplicationWillBecomeActiveNotification [[NSNotificationCenter defaultCenter]
object:nil postNotificationName:NSApplicationDidChangeScreenParametersNotification
userInfo:nil]; object:nil];
NSNotification* willResignActive = EXPECT_TRUE(updated);
[[NSNotification alloc] initWithName:NSApplicationWillResignActiveNotification }
object:nil
userInfo:nil];
NSNotification* didChangeOcclusionState; TEST_F(FlutterEngineTest, NotificationsUpdateDisplays) {
didChangeOcclusionState = BOOL updated = NO;
[[NSNotification alloc] initWithName:NSApplicationDidChangeOcclusionStateNotification FlutterEngine* engine = GetFlutterEngine();
object:nil auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
userInfo:nil]; engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
SendWindowMetricsEvent,
([&updated, &original_set_viewport_metrics](auto engine, auto* window_metrics) {
updated = YES;
return original_set_viewport_metrics(engine, window_metrics);
}));
[engineMock handleDidChangeOcclusionState:didChangeOcclusionState]; EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
EXPECT_EQ(sentState, flutter::AppLifecycleState::kInactive);
[engineMock handleWillBecomeActive:willBecomeActive]; updated = NO;
EXPECT_EQ(sentState, flutter::AppLifecycleState::kResumed); [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
object:nil];
// No VC.
EXPECT_FALSE(updated);
[engineMock handleWillResignActive:willResignActive]; FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine
EXPECT_EQ(sentState, flutter::AppLifecycleState::kInactive); nibName:nil
bundle:nil];
[viewController loadView];
viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
visibility = 0; [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
[engineMock handleDidChangeOcclusionState:didChangeOcclusionState]; object:nil];
EXPECT_EQ(sentState, flutter::AppLifecycleState::kHidden); EXPECT_TRUE(updated);
[engineMock handleWillBecomeActive:willBecomeActive];
EXPECT_EQ(sentState, flutter::AppLifecycleState::kHidden);
[engineMock handleWillResignActive:willResignActive];
EXPECT_EQ(sentState, flutter::AppLifecycleState::kHidden);
[mockApplication stopMocking];
} }
} // namespace flutter::testing } // namespace flutter::testing

View File

@@ -8,8 +8,6 @@
#include <memory> #include <memory>
#include "flutter/shell/platform/common/app_lifecycle_state.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.h" #import "flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
@@ -174,14 +172,6 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {
- (nonnull FlutterPlatformViewController*)platformViewController; - (nonnull FlutterPlatformViewController*)platformViewController;
/**
* Handles changes to the application state, sending them to the framework.
*
* @param state One of the lifecycle constants in app_lifecycle_state.h,
* corresponding to the Dart enum AppLifecycleState.
*/
- (void)setApplicationState:(flutter::AppLifecycleState)state;
// Accessibility API. // Accessibility API.
/** /**

View File

@@ -158,7 +158,6 @@ source_set("flutter_linux_sources") {
] ]
deps = [ deps = [
"//flutter/shell/platform/common:common_cpp_enums",
"//flutter/shell/platform/common:common_cpp_input", "//flutter/shell/platform/common:common_cpp_input",
"//flutter/shell/platform/common:common_cpp_switches", "//flutter/shell/platform/common:common_cpp_switches",
"//flutter/shell/platform/embedder:embedder_headers", "//flutter/shell/platform/embedder:embedder_headers",
@@ -258,7 +257,6 @@ executable("flutter_linux_unittests") {
":flutter_linux_gschemas", ":flutter_linux_gschemas",
":flutter_linux_sources", ":flutter_linux_sources",
"//flutter/runtime:libdart", "//flutter/runtime:libdart",
"//flutter/shell/platform/common:common_cpp_enums",
"//flutter/shell/platform/embedder:embedder_headers", "//flutter/shell/platform/embedder:embedder_headers",
"//flutter/shell/platform/embedder:embedder_test_utils", "//flutter/shell/platform/embedder:embedder_test_utils",
"//flutter/testing", "//flutter/testing",

View File

@@ -10,7 +10,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "flutter/shell/platform/common/app_lifecycle_state.h"
#include "flutter/shell/platform/common/engine_switches.h" #include "flutter/shell/platform/common/engine_switches.h"
#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
@@ -24,7 +23,6 @@
#include "flutter/shell/platform/linux/fl_texture_gl_private.h" #include "flutter/shell/platform/linux/fl_texture_gl_private.h"
#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" #include "flutter/shell/platform/linux/fl_texture_registrar_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h"
// Unique number associated with platform tasks. // Unique number associated with platform tasks.
static constexpr size_t kPlatformTaskRunnerIdentifier = 1; static constexpr size_t kPlatformTaskRunnerIdentifier = 1;
@@ -34,8 +32,6 @@ static constexpr size_t kPlatformTaskRunnerIdentifier = 1;
static constexpr int32_t kMousePointerDeviceId = 0; static constexpr int32_t kMousePointerDeviceId = 0;
static constexpr int32_t kPointerPanZoomDeviceId = 1; static constexpr int32_t kPointerPanZoomDeviceId = 1;
static constexpr const char* kFlutterLifecycleChannel = "flutter/lifecycle";
struct _FlEngine { struct _FlEngine {
GObject parent_instance; GObject parent_instance;
@@ -126,25 +122,6 @@ static void parse_locale(const gchar* locale,
} }
} }
static void set_app_lifecycle_state(FlEngine* self,
const flutter::AppLifecycleState state) {
FlBinaryMessenger* binary_messenger = fl_engine_get_binary_messenger(self);
g_autoptr(FlValue) value =
fl_value_new_string(flutter::AppLifecycleStateToString(state));
g_autoptr(FlStringCodec) codec = fl_string_codec_new();
g_autoptr(GBytes) message =
fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), value, nullptr);
if (message == nullptr) {
return;
}
fl_binary_messenger_send_on_channel(binary_messenger,
kFlutterLifecycleChannel, message,
nullptr, nullptr, nullptr);
}
// Passes locale information to the Flutter engine. // Passes locale information to the Flutter engine.
static void setup_locales(FlEngine* self) { static void setup_locales(FlEngine* self) {
const gchar* const* languages = g_get_language_names(); const gchar* const* languages = g_get_language_names();
@@ -744,18 +721,6 @@ GBytes* fl_engine_send_platform_message_finish(FlEngine* self,
return static_cast<GBytes*>(g_task_propagate_pointer(G_TASK(result), error)); return static_cast<GBytes*>(g_task_propagate_pointer(G_TASK(result), error));
} }
void fl_engine_send_window_state_event(FlEngine* self,
gboolean visible,
gboolean focused) {
if (visible && focused) {
set_app_lifecycle_state(self, flutter::AppLifecycleState::kResumed);
} else if (visible) {
set_app_lifecycle_state(self, flutter::AppLifecycleState::kInactive);
} else {
set_app_lifecycle_state(self, flutter::AppLifecycleState::kHidden);
}
}
void fl_engine_send_window_metrics_event(FlEngine* self, void fl_engine_send_window_metrics_event(FlEngine* self,
size_t width, size_t width,
size_t height, size_t height,

View File

@@ -167,18 +167,6 @@ void fl_engine_send_window_metrics_event(FlEngine* engine,
size_t height, size_t height,
double pixel_ratio); double pixel_ratio);
/**
* fl_engine_send_window_state_event:
* @engine: an #FlEngine.
* @visible: whether the window is currently visible or not.
* @focused: whether the window is currently focused or not.
*
* Sends a window state event to the engine.
*/
void fl_engine_send_window_state_event(FlEngine* engine,
gboolean visible,
gboolean focused);
/** /**
* fl_engine_send_mouse_pointer_event: * fl_engine_send_mouse_pointer_event:
* @engine: an #FlEngine. * @engine: an #FlEngine.

View File

@@ -5,12 +5,10 @@
// Included first as it collides with the X11 headers. // Included first as it collides with the X11 headers.
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "flutter/shell/platform/common/app_lifecycle_state.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h"
#include "flutter/shell/platform/linux/testing/fl_test.h" #include "flutter/shell/platform/linux/testing/fl_test.h"
// MOCK_ENGINE_PROC is leaky by design // MOCK_ENGINE_PROC is leaky by design
@@ -425,42 +423,6 @@ TEST(FlEngineTest, SwitchesEmpty) {
EXPECT_EQ(switches->len, 0U); EXPECT_EQ(switches->len, 0U);
} }
TEST(FlEngineTest, SendWindowStateEvent) {
g_autoptr(FlEngine) engine = make_mock_engine();
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
bool called = false;
std::string state;
embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
SendPlatformMessage,
([&called, &state](auto engine, const FlutterPlatformMessage* message) {
EXPECT_STREQ(message->channel, "flutter/lifecycle");
called = true;
g_autoptr(FlStringCodec) codec = fl_string_codec_new();
g_autoptr(GBytes) data =
g_bytes_new(message->message, message->message_size);
g_autoptr(GError) error = nullptr;
g_autoptr(FlValue) parsed_state = fl_message_codec_decode_message(
FL_MESSAGE_CODEC(codec), data, &error);
state = fl_value_get_string(parsed_state);
return kSuccess;
}));
fl_engine_send_window_state_event(engine, false, false);
EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
flutter::AppLifecycleState::kHidden));
fl_engine_send_window_state_event(engine, false, true);
EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
flutter::AppLifecycleState::kHidden));
fl_engine_send_window_state_event(engine, true, false);
EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
flutter::AppLifecycleState::kInactive));
fl_engine_send_window_state_event(engine, true, true);
EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
flutter::AppLifecycleState::kResumed));
EXPECT_TRUE(called);
}
#ifndef FLUTTER_RELEASE #ifndef FLUTTER_RELEASE
TEST(FlEngineTest, Switches) { TEST(FlEngineTest, Switches) {
g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlEngine) engine = make_mock_engine();

View File

@@ -42,9 +42,6 @@ struct _FlView {
// Pointer button state recorded for sending status updates. // Pointer button state recorded for sending status updates.
int64_t button_state; int64_t button_state;
// Current state information for the window associated with this view.
GdkWindowState window_state;
// Flutter system channel handlers. // Flutter system channel handlers.
FlAccessibilityPlugin* accessibility_plugin; FlAccessibilityPlugin* accessibility_plugin;
FlKeyboardManager* keyboard_manager; FlKeyboardManager* keyboard_manager;
@@ -62,9 +59,7 @@ struct _FlView {
/* FlKeyboardViewDelegate related properties */ /* FlKeyboardViewDelegate related properties */
KeyboardLayoutNotifier keyboard_layout_notifier; KeyboardLayoutNotifier keyboard_layout_notifier;
GdkKeymap* keymap; GdkKeymap* keymap;
gulong keymap_keys_changed_cb_id; // Signal connection ID for gulong keymap_keys_changed_cb_id; // Signal connection ID.
// keymap-keys-changed
gulong window_state_cb_id; // Signal connection ID for window-state-changed
}; };
enum { kPropFlutterProject = 1, kPropLast }; enum { kPropFlutterProject = 1, kPropLast };
@@ -240,8 +235,6 @@ static void on_pre_engine_restart_cb(FlEngine* engine, gpointer user_data) {
g_clear_object(&self->scrolling_manager); g_clear_object(&self->scrolling_manager);
init_keyboard(self); init_keyboard(self);
init_scrolling(self); init_scrolling(self);
self->window_state =
gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(self)));
} }
// Implements FlPluginRegistry::get_registrar_for_plugin. // Implements FlPluginRegistry::get_registrar_for_plugin.
@@ -487,42 +480,12 @@ static void gesture_zoom_end_cb(GtkGestureZoom* gesture,
fl_scrolling_manager_handle_zoom_end(self->scrolling_manager); fl_scrolling_manager_handle_zoom_end(self->scrolling_manager);
} }
static gboolean window_state_event_cb(GtkWidget* widget,
GdkEvent* event,
gpointer user_data) {
g_return_val_if_fail(FL_IS_VIEW(user_data), FALSE);
g_return_val_if_fail(FL_IS_ENGINE(FL_VIEW(user_data)->engine), FALSE);
FlView* self = FL_VIEW(user_data);
GdkWindowState state = event->window_state.new_window_state;
GdkWindowState previous_state = self->window_state;
self->window_state = state;
bool was_visible = !((previous_state & GDK_WINDOW_STATE_WITHDRAWN) ||
(previous_state & GDK_WINDOW_STATE_ICONIFIED));
bool is_visible = !((state & GDK_WINDOW_STATE_WITHDRAWN) ||
(state & GDK_WINDOW_STATE_ICONIFIED));
bool was_focused = (previous_state & GDK_WINDOW_STATE_FOCUSED);
bool is_focused = (state & GDK_WINDOW_STATE_FOCUSED);
if (was_visible != is_visible || was_focused != is_focused) {
if (self->engine != nullptr) {
fl_engine_send_window_state_event(FL_ENGINE(self->engine), is_visible,
is_focused);
}
}
return FALSE;
}
static void realize_cb(GtkWidget* widget) { static void realize_cb(GtkWidget* widget) {
FlView* self = FL_VIEW(widget); FlView* self = FL_VIEW(widget);
g_autoptr(GError) error = nullptr; g_autoptr(GError) error = nullptr;
// Handle requests by the user to close the application. // Handle requests by the user to close the application.
GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(self)); GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(self));
// Listen to window state changes.
self->window_state_cb_id =
g_signal_connect(toplevel_window, "window-state-event",
G_CALLBACK(window_state_event_cb), self);
g_signal_connect(toplevel_window, "delete-event", g_signal_connect(toplevel_window, "delete-event",
G_CALLBACK(window_delete_event_cb), self); G_CALLBACK(window_delete_event_cb), self);
@@ -661,12 +624,6 @@ static void fl_view_dispose(GObject* object) {
nullptr); nullptr);
} }
if (self->window_state_cb_id != 0) {
GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(self));
g_signal_handler_disconnect(toplevel_window, self->window_state_cb_id);
self->window_state_cb_id = 0;
}
g_clear_object(&self->project); g_clear_object(&self->project);
g_clear_object(&self->renderer); g_clear_object(&self->renderer);
g_clear_object(&self->engine); g_clear_object(&self->engine);
@@ -726,8 +683,6 @@ static void fl_view_class_init(FlViewClass* klass) {
static void fl_view_init(FlView* self) { static void fl_view_init(FlView* self) {
gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE); gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);
self->window_state = gdk_window_get_state(
gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(self))));
} }
G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) { G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) {

View File

@@ -129,7 +129,6 @@ source_set("flutter_windows_source") {
public_deps = [ public_deps = [
"//flutter/fml:string_conversion", "//flutter/fml:string_conversion",
"//flutter/shell/platform/common:common_cpp_accessibility", "//flutter/shell/platform/common:common_cpp_accessibility",
"//flutter/shell/platform/common:common_cpp_enums",
] ]
deps = [ deps = [

View File

@@ -397,7 +397,6 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
displays.data(), displays.size()); displays.data(), displays.size());
SendSystemLocales(); SendSystemLocales();
SetLifecycleState(flutter::AppLifecycleState::kResumed);
settings_plugin_->StartWatching(); settings_plugin_->StartWatching();
settings_plugin_->SendSettings(); settings_plugin_->SendSettings();
@@ -563,13 +562,6 @@ void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
this); this);
} }
void FlutterWindowsEngine::SetLifecycleState(flutter::AppLifecycleState state) {
const char* state_name = flutter::AppLifecycleStateToString(state);
SendPlatformMessage("flutter/lifecycle",
reinterpret_cast<const uint8_t*>(state_name),
strlen(state_name), nullptr, nullptr);
}
void FlutterWindowsEngine::SendSystemLocales() { void FlutterWindowsEngine::SendSystemLocales() {
std::vector<LanguageInfo> languages = std::vector<LanguageInfo> languages =
GetPreferredLanguageInfo(*windows_registry_); GetPreferredLanguageInfo(*windows_registry_);

View File

@@ -16,7 +16,6 @@
#include "flutter/fml/closure.h" #include "flutter/fml/closure.h"
#include "flutter/fml/macros.h" #include "flutter/fml/macros.h"
#include "flutter/shell/platform/common/accessibility_bridge.h" #include "flutter/shell/platform/common/accessibility_bridge.h"
#include "flutter/shell/platform/common/app_lifecycle_state.h"
#include "flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h" #include "flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h"
#include "flutter/shell/platform/common/incoming_message_dispatcher.h" #include "flutter/shell/platform/common/incoming_message_dispatcher.h"
@@ -307,9 +306,6 @@ class FlutterWindowsEngine {
// system changes. // system changes.
void SendSystemLocales(); void SendSystemLocales();
// Sends the current lifecycle state to the framework.
void SetLifecycleState(flutter::AppLifecycleState state);
// Create the keyboard & text input sub-systems. // Create the keyboard & text input sub-systems.
// //
// This requires that a view is attached to the engine. // This requires that a view is attached to the engine.

View File

@@ -43,6 +43,10 @@ void checkApiConsistency(String flutterRoot) {
sourcePath: path.join(flutterRoot, 'lib', 'ui', 'window.dart'), sourcePath: path.join(flutterRoot, 'lib', 'ui', 'window.dart'),
className: 'AccessibilityFeatures', className: 'AccessibilityFeatures',
); );
final List<String> webuiFields = getDartClassFields(
sourcePath: path.join(flutterRoot, 'lib', 'ui', 'window.dart'),
className: 'AccessibilityFeatures',
);
// C values: kFlutterAccessibilityFeatureFooBar = 1 << N, // C values: kFlutterAccessibilityFeatureFooBar = 1 << N,
final List<String> embedderEnumValues = getCppEnumValues( final List<String> embedderEnumValues = getCppEnumValues(
sourcePath: path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), sourcePath: path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'),
@@ -60,6 +64,7 @@ void checkApiConsistency(String flutterRoot) {
enumName: 'AccessibilityFeature', enumName: 'AccessibilityFeature',
).map(allCapsToCamelCase).toList(); ).map(allCapsToCamelCase).toList();
expect(webuiFields, uiFields);
expect(embedderEnumValues, uiFields); expect(embedderEnumValues, uiFields);
expect(internalEnumValues, uiFields); expect(internalEnumValues, uiFields);
expect(javaEnumValues, uiFields); expect(javaEnumValues, uiFields);
@@ -72,7 +77,7 @@ void checkApiConsistency(String flutterRoot) {
className: 'SemanticsAction', className: 'SemanticsAction',
); );
final List<String> webuiFields = getDartClassFields( final List<String> webuiFields = getDartClassFields(
sourcePath: path.join(flutterRoot, 'lib', 'web_ui', 'lib', 'semantics.dart'), sourcePath: path.join(flutterRoot, 'lib', 'ui', 'semantics.dart'),
className: 'SemanticsAction', className: 'SemanticsAction',
); );
// C values: kFlutterSemanticsActionFooBar = 1 << N. // C values: kFlutterSemanticsActionFooBar = 1 << N.
@@ -98,33 +103,6 @@ void checkApiConsistency(String flutterRoot) {
expect(javaEnumValues, uiFields); expect(javaEnumValues, uiFields);
}); });
test('AppLifecycleState enums match', () {
// Dart values: _kFooBarIndex = 1 << N.
final List<String> uiFields = getDartClassFields(
sourcePath: path.join(flutterRoot, 'lib', 'ui', 'platform_dispatcher.dart'),
className: 'AppLifecycleState',
);
final List<String> webuiFields = getDartClassFields(
sourcePath: path.join(flutterRoot, 'lib', 'web_ui', 'lib', 'platform_dispatcher.dart'),
className: 'AppLifecycleState',
);
// C++ values: kFooBar = 1 << N.
final List<String> internalEnumValues = getCppEnumClassValues(
sourcePath: path.join(flutterRoot, 'shell', 'platform', 'common', 'app_lifecycle_state.h'),
enumName: 'AppLifecycleState',
);
// Java values: FOO_BAR(1 << N).
final List<String> javaEnumValues = getJavaEnumValues(
sourcePath: path.join(flutterRoot, 'shell', 'platform', 'android', 'io',
'flutter', 'embedding', 'engine', 'systemchannels', 'LifecycleChannel.java'),
enumName: 'AppLifecycleState',
).map(allCapsToCamelCase).toList();
expect(webuiFields, uiFields);
expect(internalEnumValues, uiFields);
expect(javaEnumValues, uiFields);
});
test('SemanticsFlag enums match', () { test('SemanticsFlag enums match', () {
// Dart values: _kFooBarIndex = 1 << N. // Dart values: _kFooBarIndex = 1 << N.
final List<String> uiFields = getDartClassFields( final List<String> uiFields = getDartClassFields(