-
Notifications
You must be signed in to change notification settings - Fork 6k
[Android R] Sync keyboard animation with view insets vs Android 11/R/API 30 WindowInsetsAnimation #20843
Conversation
155c5d5 to
a6bef30
Compare
shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
Outdated
Show resolved
Hide resolved
| if ((View.SYSTEM_UI_FLAG_HIDE_NAVIGATION & mView.getWindowSystemUiVisibility()) == 0) { | ||
| mask = mask | WindowInsets.Type.navigationBars(); | ||
| } | ||
| if ((View.SYSTEM_UI_FLAG_FULLSCREEN & mView.getWindowSystemUiVisibility()) == 0) { |
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.
does full screen also independently affect navigation bar above too?
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.
These are the two values we use to compute viewPadding, so for consistency, I include it together to keep in sync with FlutterView.onApplyWindowInsets. Any padding we consume will also be accounted for here this way.
| mask, // Overlay | ||
| WindowInsets.Type.ime() // Deferred | ||
| ); | ||
| view.setWindowInsetsAnimationCallback(imeSyncCallback); |
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.
add comment on why you need 2
| WindowInsets.Type.ime() // Deferred | ||
| ); | ||
| view.setWindowInsetsAnimationCallback(imeSyncCallback); | ||
| view.setOnApplyWindowInsetsListener(imeSyncCallback); |
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.
do you remove this in destroy?
| // Loosely based off of | ||
| // https://github.com/android/user-interface-samples/blob/master/WindowInsetsAnimation/app/src/main/java/com/google/android/samples/insetsanimation/RootViewDeferringInsetsCallback.kt | ||
| // When the IME is shown or hidden, it sends an onApplyWindowInsets call with the | ||
| // final state of the IME. This defers the final call to allow the animation to |
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.
Sentence not clear to me. What's 'this'? 'final call' to what?
| super(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); | ||
| this.overlayInsetTypes = overlayInsetTypes; | ||
| this.deferredInsetTypes = deferredInsetTypes; | ||
| this.view = 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.
preconditions checknotnull
| @Override | ||
| public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { | ||
| this.view = view; | ||
| if (!started) { |
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.
ya, these apis are pretty confusing. I think this needs a lot more comments. Comments at the class level for what the flow should be for the persistent vs delayed stuff are. When this !started should happen, when should deferredInsets happen.
i.e. I don't think the next maintainer would know what to do here if there was a bug and you're out.
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'll make a pass and document the whole thing
| @Override | ||
| public WindowInsetsAnimation.Bounds onStart( | ||
| WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) { | ||
| if (deferredInsets && (animation.getTypeMask() & deferredInsetTypes) != 0) { |
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.
comments here too. What's the difference between onprepare and onstart? i.e. why not just set both deferredInsets and started to true here? Can you start and not prepare?
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 initially thought that both were necessary, turns out only start is required.
| - insets.getInsets(overlayInsetTypes).bottom, | ||
| 0)); | ||
| builder.setInsets(deferredInsetTypes, newImeInsets); | ||
| // Directly call onApplyWindowInsets as we want to skip this class' version of this call. |
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 isn't super clear. Maybe elaborate a bit more.
| @Config(sdk = 30) | ||
| public void ime_windowInsetsSync() { | ||
| if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { | ||
| return; |
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.
if you say this needs sdk 30 and it's not sdk 30, we should just crash :D
4613d49 to
25f9d52
Compare
67e924d to
e7b3adc
Compare
xster
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.
LGTM
| // Loosely based off of | ||
| // https://github.com/android/user-interface-samples/blob/master/WindowInsetsAnimation/app/src/main/java/com/google/android/samples/insetsanimation/RootViewDeferringInsetsCallback.kt | ||
| // | ||
| // When the IME is shown or hidden, it immediately sends an onApplyWindowInsets call |
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.
Ah, this is much better, thanks! Does this class also handle all the non-animating inset changes? Like the nav bar, status bar going in and out of fullscreen etc? How do all the phases like onStart, onApplyWindowInsets etc affect those insets?
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.
Non-animating changes are just passed right on through to FlutterView.onApplyWindowInsets without impact. The ime-type filtering also prevents this from doing anything on any non-ime animations. During animations, changes to the window are handled by the animation controller, which will end the current animation, and restart a new one with the modified settings to continue. For example, rotating the device mid-keyboard animation just causes the animation to continue where it left off but scaled to the rotated window.
| ArgumentCaptor<FlutterRenderer.ViewportMetrics> viewportMetricsCaptor = | ||
| ArgumentCaptor.forClass(FlutterRenderer.ViewportMetrics.class); | ||
|
|
||
| imeSyncCallback.onApplyWindowInsets(testView, deferredInsets); |
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 was gonna say I'm surprised you have to do this on your callback directly but then I actually can't find this on any of the robolectric shadows https://github.com/robolectric/robolectric/search?q=onApplyWindowInsets 🤷♂️
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.
🤷♂️
|
🙌 |
…id 11/R/API 30 WindowInsetsAnimation (flutter/engine#20843)
* 30b829e Populates fuchsia node actions in semantics updates. (flutter/engine#20451) * c2e7010 Roll Skia from 1ee21cdfb6fe to 6763a713f957 (1 revision) (flutter/engine#20982) * 841401d restore FML_DCHECK removed due to a code reviewing error (flutter/engine#20953) * 367c6db Don't use GetTaskQueueId() in rasterizer as it breaks Fuchsia (flutter/engine#20983) * b22a8c6 Let FlutterActivity/Fragment/FragmentActivity have an app bundle path override instead of eager resolving during construction (flutter/engine#20769) * 0f0ae68 Update test Dart code to pass the latest Dart analyzer rules (flutter/engine#20986) * d77dd31 Manual roll of Dart b29f228f62...016e8880f0 (flutter/engine#20967) * c7b3d53 Roll Fuchsia Mac SDK from gOI3W1UNU... to EN2ycWLxi... (flutter/engine#20985) * 6a6986d improve sensitivity of BackdropFilter web tests (flutter/engine#20915) * 9fc9cb2 Roll Dart SDK from 016e8880f0ab to 0f0cff3922ad (7 revisions) (flutter/engine#20990) * 242d522 [Android R] Sync keyboard animation with view insets vs Android 11/R/API 30 WindowInsetsAnimation (flutter/engine#20843) * b4e0896 Roll Fuchsia Linux SDK from 81O8Kg_Rw... to A0PKwETay... (flutter/engine#20998) * d77c4e5 adjust blur radius for HTML to match CanvasKit (flutter/engine#20840) * 0628492 Roll Dart SDK from 0f0cff3922ad to f3a9ca88b664 (1 revision) (flutter/engine#21000) * d1d848e Roll Fuchsia Mac SDK from EN2ycWLxi... to sih5f60Gt... (flutter/engine#20999)
Adds a
WindowInsetsAnimation.CallbackandOnApplyWindowInsetsListenerclass that handles sending view inset changes from the keyboard animating to the framework.When keyboard is shown or hidden, a call to
onApplyWindowInsets()is made immediately with the keyboard's final state. This causes a flicker as the animation kicks in a frame later. To solve this, we check for this initial call and capture the state, deferring it being called until the animation is completed.During the animation, we pass the window insets to the framework on each frame.
There is an issue where the
onProgressWindowInsets received has a bottom ime inset that is higher than expected. It is actually the sum of the navigation bars on top of the keyboard height. There is a workaround implemented to merge the navbar height with the keyboard height to resolve this.Partially resolves flutter/flutter#62876