From 202096373b1083163b9551a8da6a489f47e86d20 Mon Sep 17 00:00:00 2001 From: Johannes Gallmann Date: Thu, 1 Jun 2023 17:34:55 +0200 Subject: [PATCH] Fix inline reply defocus animation issues Instead of adding the RemoteInputView to an Overlay, it is kept normally in the view tree and is thus still receiving windowInsetsAnimation callbacks during the animation. In order to free up the space occupied by the RemoteInputView during the animation, a negative topMargin is set during the animation. Bug: 281847513 Flag: NOTIFICATION_INLINE_REPLY_ANIMATION Test: atest RemoteInputViewTest, Manual (i.e. analysing screenrecordings of inline reply defocus animation and verifying that "clear all" button is functioning normally afterwards) Change-Id: I8a9e1ab227d204ca18554a447f9f2ccc404be73e --- .../statusbar/policy/RemoteInputView.java | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index d585163aa2238..02b7e9176cf21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -21,7 +21,6 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD; import android.app.ActivityManager; -import android.app.Notification; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ColorStateList; @@ -47,7 +46,6 @@ import android.view.OnReceiveContentListener; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; -import android.view.ViewGroupOverlay; import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; @@ -58,6 +56,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; @@ -436,25 +435,23 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (animate && parent != null && mIsFocusAnimationFlagActive) { ViewGroup grandParent = (ViewGroup) parent.getParent(); - ViewGroupOverlay overlay = parent.getOverlay(); View actionsContainer = getActionsContainerLayout(); int actionsContainerHeight = actionsContainer != null ? actionsContainer.getHeight() : 0; - // After adding this RemoteInputView to the overlay of the parent (and thus removing - // it from the parent itself), the parent will shrink in height. This causes the - // overlay to be moved. To correct the position of the overlay we need to offset it. - int overlayOffsetY = actionsContainerHeight - getHeight(); - overlay.add(this); + // When defocusing, the notification needs to shrink. Therefore, we need to free + // up the space that was needed for the RemoteInputView. This is done by setting + // a negative top margin of the height difference of the RemoteInputView and its + // sibling (the actions_container_layout containing the Reply button etc.) + final int heightToShrink = actionsContainerHeight - getHeight(); + setTopMargin(heightToShrink); if (grandParent != null) grandParent.setClipChildren(false); - Animator animator = getDefocusAnimator(actionsContainer, overlayOffsetY); - View self = this; + final Animator animator = getDefocusAnimator(actionsContainer); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - overlay.remove(self); - parent.addView(self); + setTopMargin(0); if (grandParent != null) grandParent.setClipChildren(true); setVisibility(GONE); if (mWrapper != null) { @@ -499,6 +496,13 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + private void setTopMargin(int topMargin) { + if (!(getLayoutParams() instanceof FrameLayout.LayoutParams)) return; + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams(); + layoutParams.topMargin = topMargin; + setLayoutParams(layoutParams); + } + @VisibleForTesting protected void setViewRootImpl(ViewRootImpl viewRoot) { mTestableViewRootImpl = viewRoot; @@ -674,12 +678,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mProgressBar.setVisibility(INVISIBLE); mResetting = true; mSending = false; + mController.removeSpinning(mEntry.getKey(), mToken); onDefocus(true /* animate */, false /* logClose */, () -> { mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText()); mEditText.getText().clear(); mEditText.setEnabled(isAggregatedVisible()); mSendButton.setVisibility(VISIBLE); - mController.removeSpinning(mEntry.getKey(), mToken); updateSendButton(); setAttachment(null); mResetting = false; @@ -874,7 +878,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ValueAnimator scaleAnimator = ValueAnimator.ofFloat(FOCUS_ANIMATION_MIN_SCALE, 1f); scaleAnimator.addUpdateListener(valueAnimator -> { - setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue(), 0); + setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue()); }); scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION); scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN); @@ -901,9 +905,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene * Creates an animator for the defocus animation. * * @param fadeInView View that will be faded in during the defocus animation. - * @param offsetY The RemoteInputView will be offset by offsetY during the animation */ - private Animator getDefocusAnimator(@Nullable View fadeInView, int offsetY) { + private Animator getDefocusAnimator(@Nullable View fadeInView) { final AnimatorSet animatorSet = new AnimatorSet(); final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 1f, 0f); @@ -913,14 +916,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1f, FOCUS_ANIMATION_MIN_SCALE); scaleAnimator.addUpdateListener(valueAnimator -> { - setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue(), offsetY); + setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue()); }); scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION); scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN); scaleAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation, boolean isReverse) { - setFocusAnimationScaleY(1f /* scaleY */, 0 /* verticalOffset */); + setFocusAnimationScaleY(1f /* scaleY */); } }); @@ -942,9 +945,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene * Sets affected view properties for a vertical scale animation * * @param scaleY desired vertical view scale - * @param verticalOffset vertical offset to apply to the RemoteInputView during the animation */ - private void setFocusAnimationScaleY(float scaleY, int verticalOffset) { + private void setFocusAnimationScaleY(float scaleY) { int verticalBoundOffset = (int) ((1f - scaleY) * 0.5f * mContentView.getHeight()); Rect contentBackgroundBounds = new Rect(0, verticalBoundOffset, mContentView.getWidth(), mContentView.getHeight() - verticalBoundOffset); @@ -955,7 +957,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } else { mContentBackgroundBounds = contentBackgroundBounds; } - setTranslationY(verticalBoundOffset + verticalOffset); + setTranslationY(verticalBoundOffset); } /** Handler for button click on send action in IME. */