From 5ba0504383837237760866c3d3c67b7e64b4fb63 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 7 Dec 2020 18:28:30 -0800 Subject: [PATCH] Move the WindowInsetsAnimation.Callback implementation to an inner class to avoid Android class loader warnings ImeSyncDeferringInsetsCallback had been a subclass of WindowInsetsAnimation.Callback, which was introduced in Android API level 30. The class loader on older versions of Android was logging warnings about unresolvable classes when loading TextInputPlugin, which holds a reference to ImeSyncDeferringInsetsCallback. See https://github.com/flutter/flutter/issues/66908 --- .../ImeSyncDeferringInsetsCallback.java | 59 ++++++++++++++++--- .../plugin/editing/TextInputPluginTest.java | 12 ++-- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java b/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java index 3942574fa3b95..be63643b2d77b 100644 --- a/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java +++ b/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java @@ -46,13 +46,15 @@ @RequiresApi(30) @SuppressLint({"NewApi", "Override"}) @Keep -class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback - implements View.OnApplyWindowInsetsListener { +class ImeSyncDeferringInsetsCallback { private int overlayInsetTypes; private int deferredInsetTypes; private View view; private WindowInsets lastWindowInsets; + private AnimationCallback animationCallback; + private InsetsListener insetsListener; + // True when an animation that matches deferredInsetTypes is active. // // While this is active, this class will capture the initial window inset @@ -67,7 +69,6 @@ class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback ImeSyncDeferringInsetsCallback( @NonNull View view, int overlayInsetTypes, int deferredInsetTypes) { - super(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); this.overlayInsetTypes = overlayInsetTypes; this.deferredInsetTypes = deferredInsetTypes; this.view = view; @@ -75,8 +76,10 @@ class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback // Add this object's event listeners to its view. void install() { - view.setWindowInsetsAnimationCallback(this); - view.setOnApplyWindowInsetsListener(this); + animationCallback = new AnimationCallback(); + insetsListener = new InsetsListener(); + view.setWindowInsetsAnimationCallback(animationCallback); + view.setOnApplyWindowInsetsListener(insetsListener); } // Remove this object's event listeners from its view. @@ -85,7 +88,48 @@ void remove() { view.setOnApplyWindowInsetsListener(null); } - @Override + @VisibleForTesting + View.OnApplyWindowInsetsListener getInsetsListener() { + return insetsListener; + } + + @VisibleForTesting + WindowInsetsAnimation.Callback getAnimationCallback() { + return animationCallback; + } + + // WindowInsetsAnimation.Callback was introduced in API level 30. The callback + // subclass is separated into an inner class in order to avoid warnings from + // the Android class loader on older platforms. + private class AnimationCallback extends WindowInsetsAnimation.Callback { + AnimationCallback() { + super(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); + } + + @Override + public void onPrepare(WindowInsetsAnimation animation) { + ImeSyncDeferringInsetsCallback.this.onPrepare(animation); + } + + @Override + public WindowInsets onProgress( + WindowInsets insets, List runningAnimations) { + return ImeSyncDeferringInsetsCallback.this.onProgress(insets, runningAnimations); + } + + @Override + public void onEnd(WindowInsetsAnimation animation) { + ImeSyncDeferringInsetsCallback.this.onEnd(animation); + } + } + + private class InsetsListener implements View.OnApplyWindowInsetsListener { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { + return ImeSyncDeferringInsetsCallback.this.onApplyWindowInsets(view, windowInsets); + } + } + public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { this.view = view; if (needsSave) { @@ -108,7 +152,6 @@ public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { return view.onApplyWindowInsets(windowInsets); } - @Override public void onPrepare(WindowInsetsAnimation animation) { if ((animation.getTypeMask() & deferredInsetTypes) != 0) { animating = true; @@ -116,7 +159,6 @@ public void onPrepare(WindowInsetsAnimation animation) { } } - @Override public WindowInsets onProgress( WindowInsets insets, List runningAnimations) { if (!animating || needsSave) { @@ -158,7 +200,6 @@ public WindowInsets onProgress( return insets; } - @Override public void onEnd(WindowInsetsAnimation animation) { if (animating && (animation.getTypeMask() & deferredInsetTypes) != 0) { // If we deferred the IME insets and an IME animation has finished, we need to reset 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 7797d323d72da..734fa9d6013e2 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -1076,9 +1076,9 @@ public void ime_windowInsetsSync() { assertEquals(0, viewportMetricsCaptor.getValue().viewInsetBottom); assertEquals(0, viewportMetricsCaptor.getValue().viewInsetTop); - imeSyncCallback.onPrepare(animation); - imeSyncCallback.onApplyWindowInsets(testView, deferredInsets); - imeSyncCallback.onStart(animation, null); + imeSyncCallback.getAnimationCallback().onPrepare(animation); + imeSyncCallback.getInsetsListener().onApplyWindowInsets(testView, deferredInsets); + imeSyncCallback.getAnimationCallback().onStart(animation, null); // Only the final state call is saved, extra calls are passed on. imeSyncCallback.onApplyWindowInsets(testView, imeInsets2); @@ -1089,7 +1089,7 @@ public void ime_windowInsetsSync() { assertEquals(0, viewportMetricsCaptor.getValue().viewInsetBottom); assertEquals(0, viewportMetricsCaptor.getValue().viewInsetTop); - imeSyncCallback.onProgress(imeInsets0, animationList); + imeSyncCallback.getAnimationCallback().onProgress(imeInsets0, animationList); verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); assertEquals(40, viewportMetricsCaptor.getValue().viewPaddingBottom); @@ -1097,7 +1097,7 @@ public void ime_windowInsetsSync() { assertEquals(60, viewportMetricsCaptor.getValue().viewInsetBottom); assertEquals(0, viewportMetricsCaptor.getValue().viewInsetTop); - imeSyncCallback.onProgress(imeInsets1, animationList); + imeSyncCallback.getAnimationCallback().onProgress(imeInsets1, animationList); verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); assertEquals(40, viewportMetricsCaptor.getValue().viewPaddingBottom); @@ -1105,7 +1105,7 @@ public void ime_windowInsetsSync() { assertEquals(0, viewportMetricsCaptor.getValue().viewInsetBottom); // Cannot be negative assertEquals(0, viewportMetricsCaptor.getValue().viewInsetTop); - imeSyncCallback.onEnd(animation); + imeSyncCallback.getAnimationCallback().onEnd(animation); verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); // Values should be of deferredInsets, not imeInsets2