From b3576ac366baf6d53b719de35543550cdd0feebf Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Wed, 23 Sep 2020 13:47:43 -0700 Subject: [PATCH 1/8] Update 1.22 engine to use Dart 2.10.0-110.5.beta --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 297af528b902e..3d56e8fd1dbc6 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '52130c19ca593b185ea9cf72b26b1d02455551ef', + 'dart_revision': '4215dca724fb80de592f51a6cdba51e7638d1723', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From f3492258f7e7402e79e9ba16dfe1358080f89530 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 21 Sep 2020 15:07:02 -0700 Subject: [PATCH 2/8] Dark mode friendly iOS debugging message (#21277) --- .../darwin/ios/framework/Source/FlutterViewController.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index ba7a8b5ca9471..09773b0c81744 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -326,7 +326,11 @@ - (void)pushRoute:(NSString*)route { auto placeholder = [[[UIView alloc] init] autorelease]; placeholder.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - placeholder.backgroundColor = UIColor.whiteColor; + if (@available(iOS 13.0, *)) { + placeholder.backgroundColor = UIColor.systemBackgroundColor; + } else { + placeholder.backgroundColor = UIColor.whiteColor; + } placeholder.autoresizesSubviews = YES; // Only add the label when we know we have failed to enable tracing (and it was necessary). From 1a1793a2c38399ce02e8c21b600c659db00ffe18 Mon Sep 17 00:00:00 2001 From: Tim Sneath Date: Wed, 23 Sep 2020 13:52:52 -0700 Subject: [PATCH 3/8] Update FlutterViewController.mm (#21362) Tweaked the label here a little for style ("apps", not "application" -- plural and shorter); and "launching" rather than "re-launching"? --- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 09773b0c81744..792256082fbf7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -343,9 +343,9 @@ - (void)pushRoute:(NSString*)route { messageLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; messageLabel.text = - @"In iOS 14+, Flutter application in debug mode can only be launched from Flutter tooling, " + @"In iOS 14+, debug mode Flutter apps can only be launched from Flutter tooling, " @"IDEs with Flutter plugins or from Xcode.\n\nAlternatively, build in profile or release " - @"modes to enable re-launching from the home screen."; + @"modes to enable launching from the home screen."; [placeholder addSubview:messageLabel]; } From f388cb6ab8245f2df23d3c7e4a3d95d63b4b122f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 21 Sep 2020 15:22:47 -0700 Subject: [PATCH 4/8] Fix iOS platform view's mask view blocking touch events. (#21286) --- .../Source/FlutterPlatformViews_Internal.mm | 9 ++++ .../ios/Scenarios/Scenarios/AppDelegate.m | 5 ++- .../PlatformViewGestureRecognizerTests.m | 45 +++++++++++++++++++ .../scenario_app/lib/src/platform_view.dart | 34 ++++++++++++-- 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 0179debf2c5a4..53cbff2202720 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -90,6 +90,15 @@ - (instancetype)initWithFrame:(CGRect)frame { return self; } +// In some scenarios, when we add this view as a maskView of the ChildClippingView, iOS added +// this view as a subview of the ChildClippingView. +// This results this view blocking touch events on the ChildClippingView. +// So we should always ignore any touch events sent to this view. +// See https://github.com/flutter/flutter/issues/66044 +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event { + return NO; +} + - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index d09cc4db033c4..b981938a05bfc 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -23,7 +23,9 @@ @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - + if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--maskview-blocking"]) { + self.window.tintColor = UIColor.systemPinkColor; + } NSDictionary* launchArgsMap = @{ // The Platform view golden test args should match `PlatformViewGoldenTestManager`. @"--locale-initialization" : @"locale_initialization", @@ -58,7 +60,6 @@ - (BOOL)application:(UIApplication*)application *stop = YES; } }]; - if (flutterViewControllerTestName) { [self setupFlutterViewControllerTest:flutterViewControllerTestName]; } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index 3d583e1d5e824..6771454c40318 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -110,9 +110,54 @@ - (void)testAccept { [[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView]; [platformView tap]; + + [self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView]; + XCTAssertEqualObjects(platformView.label, + @"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped"); +} + +- (void)testGestureWithMaskViewBlockingPlatformView { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--gesture-accept", @"--maskview-blocking" ]; + [app launch]; + + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier hasPrefix:@"platform_view"]; + }]; + XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; + if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { + NSLog(@"%@", app.debugDescription); + XCTFail(@"Failed due to not able to find any platformView with %@ seconds", + @(kSecondsToWaitForPlatformView)); + } + + XCTAssertNotNil(platformView); + XCTAssertEqualObjects(platformView.label, @""); + + NSPredicate* predicate = [NSPredicate + predicateWithFormat:@"label == %@", + @"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped"]; + XCTNSPredicateExpectation* expection = + [[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView]; + + XCUICoordinate* coordinate = + [self getNormalizedCoordinate:app + point:CGVectorMake(platformView.frame.origin.x + 10, + platformView.frame.origin.y + 10)]; + [coordinate tap]; + [self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView]; XCTAssertEqualObjects(platformView.label, @"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped"); } +- (XCUICoordinate*)getNormalizedCoordinate:(XCUIApplication*)app point:(CGVector)vector { + XCUICoordinate* appZero = [app coordinateWithNormalizedOffset:CGVectorMake(0, 0)]; + XCUICoordinate* coordinate = [appZero coordinateWithOffset:vector]; + return coordinate; +} + @end diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index e2d0d6a0e3d7b..90fc0693e0433 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -336,9 +336,9 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { + _nextFrame = _firstFrame; createPlatformView(window, 'platform view 1', firstId); createPlatformView(window, 'platform view 2', secondId); - _nextFrame = _firstFrame; } /// The platform view identifier to use for the first platform view. @@ -532,6 +532,8 @@ class PlatformViewForTouchIOSScenario extends Scenario int _viewId; bool _accept; + + VoidCallback _nextFrame; /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. @@ -545,14 +547,24 @@ class PlatformViewForTouchIOSScenario extends Scenario } else { createPlatformView(window, text, id); } + _nextFrame = _firstFrame; } @override void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); + _nextFrame(); + } - builder.pushOffset(0, 0); - finishBuilderByAddingPlatformViewAndPicture(builder, _viewId); + @override + void onDrawFrame() { + // Some iOS gesture recognizers bugs are introduced in the second frame (with a different platform view rect) after laying out the platform view. + // So in this test, we load 2 frames to ensure that we cover those cases. + // See https://github.com/flutter/flutter/issues/66044 + if (_nextFrame == _firstFrame) { + _nextFrame = _secondFrame; + window.scheduleFrame(); + } + super.onDrawFrame(); } @override @@ -585,6 +597,20 @@ class PlatformViewForTouchIOSScenario extends Scenario } } + + void _firstFrame() { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + finishBuilderByAddingPlatformViewAndPicture(builder, _viewId); + } + + void _secondFrame() { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(5, 5); + finishBuilderByAddingPlatformViewAndPicture(builder, _viewId); + } } mixin _BasePlatformViewScenarioMixin on Scenario { From 952a9b7e39ba5ee74ec440f9b1d5a464f6b849d6 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 21 Sep 2020 17:42:02 -0700 Subject: [PATCH 5/8] Disconnect the view's AndroidKeyProcessor when detaching from the engine (#21307) --- .../embedding/android/AndroidKeyProcessor.java | 9 +++++++++ .../flutter/embedding/android/FlutterView.java | 2 ++ .../android/AndroidKeyProcessorTest.java | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java index 094b8ef1c53e8..14d7effe56c11 100644 --- a/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java +++ b/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java @@ -70,6 +70,15 @@ public AndroidKeyProcessor( this.keyEventChannel.setEventResponseHandler(eventResponder); } + /** + * Detaches the key processor from the Flutter engine. + * + *

The AndroidKeyProcessor instance should not be used after calling this. + */ + public void destroy() { + keyEventChannel.setEventResponseHandler(null); + } + /** * Called when a key up event is received by the {@link FlutterView}. * diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 2f1942034a7e5..c5c92a05d9dc3 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -992,6 +992,8 @@ public void detachFromFlutterEngine() { textInputPlugin.getInputMethodManager().restartInput(this); textInputPlugin.destroy(); + androidKeyProcessor.destroy(); + if (mouseCursorPlugin != null) { mouseCursorPlugin.destroy(); } diff --git a/shell/platform/android/test/io/flutter/embedding/android/AndroidKeyProcessorTest.java b/shell/platform/android/test/io/flutter/embedding/android/AndroidKeyProcessorTest.java index 23369a2d3109b..8eddb009dc828 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/AndroidKeyProcessorTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/AndroidKeyProcessorTest.java @@ -2,7 +2,9 @@ import static junit.framework.TestCase.assertEquals; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.notNull; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,6 +58,22 @@ public void respondsTrueWhenHandlingNewEvents() { verify(fakeView, times(0)).dispatchKeyEvent(any(KeyEvent.class)); } + @Test + public void destroyTest() { + FlutterEngine flutterEngine = mockFlutterEngine(); + KeyEventChannel fakeKeyEventChannel = flutterEngine.getKeyEventChannel(); + View fakeView = mock(View.class); + + AndroidKeyProcessor processor = + new AndroidKeyProcessor(fakeView, fakeKeyEventChannel, mock(TextInputPlugin.class)); + + verify(fakeKeyEventChannel, times(1)) + .setEventResponseHandler(notNull(KeyEventChannel.EventResponseHandler.class)); + processor.destroy(); + verify(fakeKeyEventChannel, times(1)) + .setEventResponseHandler(isNull(KeyEventChannel.EventResponseHandler.class)); + } + public void synthesizesEventsWhenKeyDownNotHandled() { FlutterEngine flutterEngine = mockFlutterEngine(); KeyEventChannel fakeKeyEventChannel = flutterEngine.getKeyEventChannel(); From 8bf9564861c78cac5d0897a919aa683e389b1759 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 16 Sep 2020 14:04:50 -0700 Subject: [PATCH 6/8] Remove extraneous window inset call on IME animation (#21213) --- .../plugin/editing/TextInputPlugin.java | 38 +++++++++++++------ .../plugin/editing/TextInputPluginTest.java | 10 ++++- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index c11d264c21988..62abf762a4d68 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -199,7 +199,17 @@ class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback private View view; private WindowInsets lastWindowInsets; - private boolean started = false; + // True when an animation that matches deferredInsetTypes is active. + // + // While this is active, this class will capture the initial window inset + // sent into lastWindowInsets by flagging needsSave to true, and will hold + // onto the intitial inset until the animation is completed, when it will + // re-dispatch the inset change. + private boolean animating = false; + // When an animation begins, android sends a WindowInset with the final + // state of the animation. When needsSave is true, we know to capture this + // initial WindowInset. + private boolean needsSave = false; ImeSyncDeferringInsetsCallback( @NonNull View view, int overlayInsetTypes, int deferredInsetTypes) { @@ -212,34 +222,38 @@ class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback @Override public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { this.view = view; - if (started) { + if (needsSave) { + // Store the view and insets for us in onEnd() below. This captured inset + // is not part of the animation and instead, represents the final state + // of the inset after the animation is completed. Thus, we defer the processing + // of this WindowInset until the animation completes. + lastWindowInsets = windowInsets; + needsSave = false; + } + if (animating) { // While animation is running, we consume the insets to prevent disrupting // the animation, which skips this implementation and calls the view's // onApplyWindowInsets directly to avoid being consumed here. return WindowInsets.CONSUMED; } - // Store the view and insets for us in onEnd() below - lastWindowInsets = windowInsets; - // If no animation is happening, pass the insets on to the view's own // inset handling. return view.onApplyWindowInsets(windowInsets); } @Override - public WindowInsetsAnimation.Bounds onStart( - WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) { + public void onPrepare(WindowInsetsAnimation animation) { if ((animation.getTypeMask() & deferredInsetTypes) != 0) { - started = true; + animating = true; + needsSave = true; } - return bounds; } @Override public WindowInsets onProgress( WindowInsets insets, List runningAnimations) { - if (!started) { + if (!animating || needsSave) { return insets; } boolean matching = false; @@ -280,10 +294,10 @@ public WindowInsets onProgress( @Override public void onEnd(WindowInsetsAnimation animation) { - if (started && (animation.getTypeMask() & deferredInsetTypes) != 0) { + if (animating && (animation.getTypeMask() & deferredInsetTypes) != 0) { // If we deferred the IME insets and an IME animation has finished, we need to reset // the flags - started = false; + animating = false; // And finally dispatch the deferred insets to the view now. // Ideally we would just call view.requestApplyInsets() and let the normal dispatch diff --git a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java index 562f1f51dcf13..b038eb56000f3 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -669,6 +669,8 @@ public void ime_windowInsetsSync() { WindowInsets.Builder builder = new WindowInsets.Builder(); WindowInsets noneInsets = builder.build(); + // imeInsets0, 1, and 2 contain unique IME bottom insets, and are used + // to distinguish which insets were sent at each stage. builder.setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 100)); builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 40)); WindowInsets imeInsets0 = builder.build(); @@ -677,6 +679,10 @@ public void ime_windowInsetsSync() { builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 40)); WindowInsets imeInsets1 = builder.build(); + builder.setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 50)); + builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 40)); + WindowInsets imeInsets2 = builder.build(); + builder.setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 200)); builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 0)); WindowInsets deferredInsets = builder.build(); @@ -696,6 +702,8 @@ public void ime_windowInsetsSync() { imeSyncCallback.onPrepare(animation); imeSyncCallback.onApplyWindowInsets(testView, deferredInsets); imeSyncCallback.onStart(animation, null); + // Only the final state call is saved, extra calls are passed on. + imeSyncCallback.onApplyWindowInsets(testView, imeInsets2); verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); // No change, as deferredInset is stored to be passed in onEnd() @@ -723,7 +731,7 @@ public void ime_windowInsetsSync() { imeSyncCallback.onEnd(animation); verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); - // Values should be of deferredInsets + // Values should be of deferredInsets, not imeInsets2 assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom); assertEquals(10, viewportMetricsCaptor.getValue().paddingTop); assertEquals(200, viewportMetricsCaptor.getValue().viewInsetBottom); From e4d507b809ed35d35ad701908ce52dacfd4cc1a0 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Wed, 23 Sep 2020 15:05:56 -0700 Subject: [PATCH 7/8] update licenses signature --- ci/licenses_golden/licenses_third_party | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index f5805269c9196..69672b2b72f6f 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: a1bbcd05a2657658be7c5f38e0d366f4 +Signature: 52ed6d65d7e96daef749ee003a3463a0 UNUSED LICENSES: From ecfcb9ff614d192445a34df3ea6b8ac894d957cc Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 23 Sep 2020 09:17:02 -0700 Subject: [PATCH 8/8] Retain the WindowInsetsAnimation callback if code shrinking is enabled (#21330) --- .../android/io/flutter/plugin/editing/TextInputPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 62abf762a4d68..3090c6fba81fa 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -28,6 +28,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -192,6 +193,7 @@ public void sendAppPrivateCommand(String action, Bundle data) { @TargetApi(30) @RequiresApi(30) @SuppressLint({"NewApi", "Override"}) + @Keep class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback implements View.OnApplyWindowInsetsListener { private int overlayInsetTypes;