Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 75bef9f

Browse files
christopherfujinojason-simmonsChris Yang
authored
[flutter_releases] Flutter 1.22.1 Engine Cherrypicks (#21669)
* Update Dart to 2.10.1 * Extract the WindowInsetsAnimation.Callback subclass into a separate class that will be lazily loaded (#21548) WindowInsetsAnimation.Callback was introduced in API level 30. This PR moves the text input plugin's WindowInsetsAnimation.Callback subclass into a class that will only be loaded if the embedding has checked for a sufficient API level. See flutter/flutter#66908 * iOS: only add explicit transactions when there are platform views (only on main threads) (#21526) * update licenses golden * pin framework for build tests Co-authored-by: Jason Simmons <[email protected]> Co-authored-by: Chris Yang <[email protected]>
1 parent 5babba6 commit 75bef9f

File tree

11 files changed

+224
-179
lines changed

11 files changed

+224
-179
lines changed

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ task:
8787
fetch_framework_script: |
8888
mkdir -p $FRAMEWORK_PATH
8989
cd $FRAMEWORK_PATH
90-
git clone https://github.com/flutter/flutter.git
90+
git clone https://github.com/flutter/flutter.git -b flutter-1.22-candidate.12
9191
test_web_script: |
9292
cd $FRAMEWORK_PATH/flutter/dev/integration_tests/web
9393
../../../bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web

DEPS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ vars = {
3434
# Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS.
3535
# You can use //tools/dart/create_updated_flutter_deps.py to produce
3636
# updated revision list of existing dependencies.
37-
'dart_revision': '41eab9b49ccce8960f71c657861dc629f96295af',
37+
'dart_revision': 'efd753621946a89008b76b76d85d54d1aa57fce8',
3838

3939
# WARNING: DO NOT EDIT MANUALLY
4040
# The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardM
791791
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java
792792
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringCodec.java
793793
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java
794+
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java
794795
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java
795796
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
796797
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java

ci/licenses_golden/licenses_third_party

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Signature: 52ed6d65d7e96daef749ee003a3463a0
1+
Signature: fcda8704ef65a785787b4045cbe4f432
22

33
UNUSED LICENSES:
44

shell/platform/android/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ android_java_sources = [
212212
"io/flutter/plugin/common/StandardMethodCodec.java",
213213
"io/flutter/plugin/common/StringCodec.java",
214214
"io/flutter/plugin/editing/FlutterTextUtils.java",
215+
"io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java",
215216
"io/flutter/plugin/editing/InputConnectionAdaptor.java",
216217
"io/flutter/plugin/editing/TextInputPlugin.java",
217218
"io/flutter/plugin/localization/LocalizationPlugin.java",
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugin.editing;
6+
7+
import android.annotation.SuppressLint;
8+
import android.annotation.TargetApi;
9+
import android.graphics.Insets;
10+
import android.view.View;
11+
import android.view.WindowInsets;
12+
import android.view.WindowInsetsAnimation;
13+
import androidx.annotation.Keep;
14+
import androidx.annotation.NonNull;
15+
import androidx.annotation.RequiresApi;
16+
import androidx.annotation.VisibleForTesting;
17+
import java.util.List;
18+
19+
// Loosely based off of
20+
// https://github.com/android/user-interface-samples/blob/master/WindowInsetsAnimation/app/src/main/java/com/google/android/samples/insetsanimation/RootViewDeferringInsetsCallback.kt
21+
//
22+
// When the IME is shown or hidden, it immediately sends an onApplyWindowInsets call
23+
// with the final state of the IME. This initial call disrupts the animation, which
24+
// causes a flicker in the beginning.
25+
//
26+
// To fix this, this class extends WindowInsetsAnimation.Callback and implements
27+
// OnApplyWindowInsetsListener. We capture and defer the initial call to
28+
// onApplyWindowInsets while the animation completes. When the animation
29+
// finishes, we can then release the call by invoking it in the onEnd callback
30+
//
31+
// The WindowInsetsAnimation.Callback extension forwards the new state of the
32+
// IME inset from onProgress() to the framework. We also make use of the
33+
// onStart callback to detect which calls to onApplyWindowInsets would
34+
// interrupt the animation and defer it.
35+
//
36+
// By implementing OnApplyWindowInsetsListener, we are able to capture Android's
37+
// attempts to call the FlutterView's onApplyWindowInsets. When a call to onStart
38+
// occurs, we can mark any non-animation calls to onApplyWindowInsets() that
39+
// occurs between prepare and start as deferred by using this class' wrapper
40+
// implementation to cache the WindowInsets passed in and turn the current call into
41+
// a no-op. When onEnd indicates the end of the animation, the deferred call is
42+
// dispatched again, this time avoiding any flicker since the animation is now
43+
// complete.
44+
@VisibleForTesting
45+
@TargetApi(30)
46+
@RequiresApi(30)
47+
@SuppressLint({"NewApi", "Override"})
48+
@Keep
49+
class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback
50+
implements View.OnApplyWindowInsetsListener {
51+
private int overlayInsetTypes;
52+
private int deferredInsetTypes;
53+
54+
private View view;
55+
private WindowInsets lastWindowInsets;
56+
// True when an animation that matches deferredInsetTypes is active.
57+
//
58+
// While this is active, this class will capture the initial window inset
59+
// sent into lastWindowInsets by flagging needsSave to true, and will hold
60+
// onto the intitial inset until the animation is completed, when it will
61+
// re-dispatch the inset change.
62+
private boolean animating = false;
63+
// When an animation begins, android sends a WindowInset with the final
64+
// state of the animation. When needsSave is true, we know to capture this
65+
// initial WindowInset.
66+
private boolean needsSave = false;
67+
68+
ImeSyncDeferringInsetsCallback(
69+
@NonNull View view, int overlayInsetTypes, int deferredInsetTypes) {
70+
super(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE);
71+
this.overlayInsetTypes = overlayInsetTypes;
72+
this.deferredInsetTypes = deferredInsetTypes;
73+
this.view = view;
74+
}
75+
76+
// Add this object's event listeners to its view.
77+
void install() {
78+
view.setWindowInsetsAnimationCallback(this);
79+
view.setOnApplyWindowInsetsListener(this);
80+
}
81+
82+
// Remove this object's event listeners from its view.
83+
void remove() {
84+
view.setWindowInsetsAnimationCallback(null);
85+
view.setOnApplyWindowInsetsListener(null);
86+
}
87+
88+
@Override
89+
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
90+
this.view = view;
91+
if (needsSave) {
92+
// Store the view and insets for us in onEnd() below. This captured inset
93+
// is not part of the animation and instead, represents the final state
94+
// of the inset after the animation is completed. Thus, we defer the processing
95+
// of this WindowInset until the animation completes.
96+
lastWindowInsets = windowInsets;
97+
needsSave = false;
98+
}
99+
if (animating) {
100+
// While animation is running, we consume the insets to prevent disrupting
101+
// the animation, which skips this implementation and calls the view's
102+
// onApplyWindowInsets directly to avoid being consumed here.
103+
return WindowInsets.CONSUMED;
104+
}
105+
106+
// If no animation is happening, pass the insets on to the view's own
107+
// inset handling.
108+
return view.onApplyWindowInsets(windowInsets);
109+
}
110+
111+
@Override
112+
public void onPrepare(WindowInsetsAnimation animation) {
113+
if ((animation.getTypeMask() & deferredInsetTypes) != 0) {
114+
animating = true;
115+
needsSave = true;
116+
}
117+
}
118+
119+
@Override
120+
public WindowInsets onProgress(
121+
WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
122+
if (!animating || needsSave) {
123+
return insets;
124+
}
125+
boolean matching = false;
126+
for (WindowInsetsAnimation animation : runningAnimations) {
127+
if ((animation.getTypeMask() & deferredInsetTypes) != 0) {
128+
matching = true;
129+
continue;
130+
}
131+
}
132+
if (!matching) {
133+
return insets;
134+
}
135+
WindowInsets.Builder builder = new WindowInsets.Builder(lastWindowInsets);
136+
// Overlay the ime-only insets with the full insets.
137+
//
138+
// The IME insets passed in by onProgress assumes that the entire animation
139+
// occurs above any present navigation and status bars. This causes the
140+
// IME inset to be too large for the animation. To remedy this, we merge the
141+
// IME inset with other insets present via a subtract + reLu, which causes the
142+
// IME inset to be overlaid with any bars present.
143+
Insets newImeInsets =
144+
Insets.of(
145+
0,
146+
0,
147+
0,
148+
Math.max(
149+
insets.getInsets(deferredInsetTypes).bottom
150+
- insets.getInsets(overlayInsetTypes).bottom,
151+
0));
152+
builder.setInsets(deferredInsetTypes, newImeInsets);
153+
// Directly call onApplyWindowInsets of the view as we do not want to pass through
154+
// the onApplyWindowInsets defined in this class, which would consume the insets
155+
// as if they were a non-animation inset change and cache it for re-dispatch in
156+
// onEnd instead.
157+
view.onApplyWindowInsets(builder.build());
158+
return insets;
159+
}
160+
161+
@Override
162+
public void onEnd(WindowInsetsAnimation animation) {
163+
if (animating && (animation.getTypeMask() & deferredInsetTypes) != 0) {
164+
// If we deferred the IME insets and an IME animation has finished, we need to reset
165+
// the flags
166+
animating = false;
167+
168+
// And finally dispatch the deferred insets to the view now.
169+
// Ideally we would just call view.requestApplyInsets() and let the normal dispatch
170+
// cycle happen, but this happens too late resulting in a visual flicker.
171+
// Instead we manually dispatch the most recent WindowInsets to the view.
172+
if (lastWindowInsets != null && view != null) {
173+
view.dispatchApplyWindowInsets(lastWindowInsets);
174+
}
175+
}
176+
}
177+
}

0 commit comments

Comments
 (0)