-
Notifications
You must be signed in to change notification settings - Fork 6k
Determine lifecycle by looking at window focus also #41094
Determine lifecycle by looking at window focus also #41094
Conversation
e29c0ce to
cd16c9f
Compare
cd16c9f to
cf20679
Compare
reidbaker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code looks great but it will need some tests. Specifically tests to make sure that we are forwarding the onWindowFocusChanged events. I pinged another contributor for a second opinion about the potential risks of having a second item set inactive.
My final thought is about ensuring that android activities forward the onWindowFocusChanged call along to flutter fragments. Do you know if we have the ability to write custom linters to warn the caller if they dont forward events to us?
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Show resolved
Hide resolved
gnprice
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems good to me. Comments below.
- That the
onWindowFocusChangedwill report false, even when one of the windows in the activity is focused (I'm not sure yet what it does when you switch between one window and another in a multi-window app).
Yeah, I'd expect that if you have two activities visible in multi-window mode and you switch focus from one to the other, then the system would call onWindowFocusChanged(false) on the one activity and onWindowFocusChanged(true) on the other.
That should be pretty similar to what happens if you switch between activities in the app switcher, which I believe will call onStop on one activity and onResume et al. on the other. So if that causes confusion in Flutter, then it's probably a confusion that already exists.
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/plugin/common/PluginRegistry.java
Outdated
Show resolved
Hide resolved
0311b10 to
76280f2
Compare
|
I removed the state in the Sending extra state updates in the case where we move from inactive to resumed or vice versa are unfortunate, but I'd rather have one source of truth for the state of the app and just deduplicate them. Off to writing tests now. |
f311b92 to
523bb42
Compare
What should those tests look like? I've added a unit test to verify that we do the right thing when we receive
No, I don' t know the answer to that, maybe @gnprice knows? |
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
Outdated
Show resolved
Hide resolved
lib/ui/platform_dispatcher.dart
Outdated
| /// The application is visible and responding to user input. | ||
| /// The application is visible and responsive to user input. | ||
| /// | ||
| /// On Android, this state corresponds to an app or the Flutter host view |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure the or is apt here. If you consider the first part of the or, 'an app having focus while in Android's "resumed" state' could be true most of the time while this enum isn't resumed in hybrid apps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also worth noting that "focus" here refers to the window, and a single application may have more than one window, thus implying part of the application has focus while other parts don't. I think even something as simple as displaying a native Android dialog (which a plugin could do) would trigger onWindowFocusChanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the possible values it could be set to in the hybrid case where the app has focus and is in the Android resumed state?
I am assuming that if "onResume" has been called by Android on the activity, and the Flutter activity has focus, then the only Flutter state this should be in is "resumed". If the Flutter activity doesn't have focus, then it should be in "inactive".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm picking on the doc wording, not on the implementation. It's not "the app OR the Flutter host view having focus", it's just "the Flutter host view having focus". In hybrid apps where 95% of the app isn't Flutter, you're not going to be AppLifecycleState.resumed if "the app has focused while in Android's resumed state". The Flutter view might not even be attached to the FlutterEngine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm picking on the doc wording, not on the implementation.
Sure, I got that, and a totally fine thing to do! I want to get it right.
In hybrid apps where 95% of the app isn't Flutter, you're not going to be AppLifecycleState.resumed if "the app has focused while in Android's resumed state". The Flutter view might not even be attached to the FlutterEngine.
If the Flutter view isn't attached to the engine, wouldn't it be in Android's "Detached" state? In that state, it doesn't matter what the focus is, the Flutter state will be AppLifecycleState.detached. Only in Android's "Resumed" state am I making any changes to how things work. Take a look at the state table I put in the PR description and tell me if that helps explain it.
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
Outdated
Show resolved
Hide resolved
e0c89cc to
e6bdea3
Compare
|
Okay, I've updated it so that |
shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
Outdated
Show resolved
Hide resolved
6b6afee to
30b86c8
Compare
gnprice
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this state-table-based approach!
shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
Show resolved
Hide resolved
8c5dc9a to
a038358
Compare
|
Okay, I've added tests for the Otherwise, I think this is ready for another look. |
a038358 to
ec6e980
Compare
…to Android lifecycle state.
ec6e980 to
6d40a69
Compare
|
Overall LGTM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great documentation!
flutter/engine@98b6fab...2a84ea5 2023-04-28 zanderso@users.noreply.github.com Revert "Replace deprecated and internal Skia EncodedImage calls with public versions" (flutter/engine#41595) 2023-04-28 maruel@gmail.com Roll buildroot to c96c9d4641714301fab450a5f3b0f3c42712e1e3 (flutter/engine#41589) 2023-04-28 ychris@google.com [platform_view] Only dispose view when it is removed from the composition order (flutter/engine#41521) 2023-04-28 gspencergoog@users.noreply.github.com Determine lifecycle by looking at window focus also (flutter/engine#41094) 2023-04-28 zanderso@users.noreply.github.com Increase timeout for clang-tidy on mac (flutter/engine#41588) 2023-04-28 bdero@google.com [Impeller] Always enable validation for goldens (flutter/engine#41574) 2023-04-28 maruel@gmail.com fuchsia: do not read version_history.json (flutter/engine#41585) 2023-04-28 skia-flutter-autoroll@skia.org Roll Dart SDK from c7160d4ea0b7 to 8e9bf2bb9878 (5 revisions) (flutter/engine#41586) 2023-04-28 kjlubick@users.noreply.github.com Replace deprecated and internal Skia EncodedImage calls with public versions (flutter/engine#41368) 2023-04-28 skia-flutter-autoroll@skia.org Roll Skia from 05d09f28250a to 9867fa253064 (18 revisions) (flutter/engine#41584) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC jsimmons@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
This reverts commit eb68df6.
… also" (flutter#41626) Reverts flutter#41094. context: updated the java/android timeouts and details in b/280204906
Description
This incorporates additional signal from
Activity.onWindowFocusChangedto help decide if the application isresumedorinactive.When the user pulls down the notification shade or opens the app switcher in iOS, then iOS sends a notification to the application that it no longer has input focus (is no longer "active" in Apple terminology).
However, Android (at least on a Pixel) doesn't send
onPauseandonResumeevents for these things, as one might expect. Instead, this PR changes things so that we listen toActivity.onWindowFocusChangedand see if any of the windows still have focus.If it doesn't have focus, then the lifecycle switches to
inactive(even ifonPausehasn't been called), and if it does have focus (andonResumehasn't been called) then we should go toresumed.State changes are determined and deduped in the
LifecycleChannelclass.Here's the old state table:
Here's the new state table:
("Window focused" means one or more windows managed by Flutter are focused)
The
inactivestate is for when the application is running and visible, but doesn't have the input focus. An example where this currently happens are when a phone call is in progress on top of the app, or on some OEMs when going into the app switcher (I've tested on Realme and it does that, at least). With the PR, it will also go intoinactivewhen the app has lost input focus, but is still in the AndroidonResumestate. This means that on phones that don't pause the app when they go into the app switcher or the notification window shade (Pixel, others), the app will go intoinactivewhen it didn't before. If developers weren't doing anything special in theinactivestate before, then this PR will have no change for them. If they were, they will go into that state more often (but more consistently across OEMs).Related Issues
Tests
onWindowFocusChanged.