Skip to content

Commit

Permalink
Move the WindowInsetsAnimation.Callback implementation to an inner cl…
Browse files Browse the repository at this point in the history
…ass 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 flutter/flutter#66908
  • Loading branch information
jason-simmons committed Dec 8, 2020
1 parent ff87b1d commit 5ba0504
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -67,16 +69,17 @@ 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;
}

// 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.
Expand All @@ -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<WindowInsetsAnimation> 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) {
Expand All @@ -108,15 +152,13 @@ public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
return view.onApplyWindowInsets(windowInsets);
}

@Override
public void onPrepare(WindowInsetsAnimation animation) {
if ((animation.getTypeMask() & deferredInsetTypes) != 0) {
animating = true;
needsSave = true;
}
}

@Override
public WindowInsets onProgress(
WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
if (!animating || needsSave) {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -1089,23 +1089,23 @@ 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);
assertEquals(10, viewportMetricsCaptor.getValue().viewPaddingTop);
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);
assertEquals(10, viewportMetricsCaptor.getValue().viewPaddingTop);
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
Expand Down

0 comments on commit 5ba0504

Please sign in to comment.