From af06bf225ae0fe62f86c44d125437acaf82566cd Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 15 Jul 2016 12:26:49 -0700 Subject: [PATCH] Direct Reply: Fix circular reveal origin Makes sure the circular reveal origin matches the bounds of the text. This is necessairy because the TextView for the last action spans the full remaining width and is usually much larger than the text, which makes the reveal look uncentered. Also adds an animation for closing the direct reply view. Bug: 29876991 Change-Id: Ib0ae8a12423f0ecfc573928a39634404c8232e40 --- .../systemui/statusbar/BaseStatusBar.java | 18 ++++-- .../statusbar/policy/RemoteInputView.java | 59 ++++++++++++++++--- .../statusbar/stack/StackStateAnimator.java | 1 + 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 8530d667daa50..4c3bd7d04c44d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -472,20 +472,28 @@ public abstract class BaseStatusBar extends SystemUI implements } } - riv.setVisibility(View.VISIBLE); - int cx = view.getLeft() + view.getWidth() / 2; + int width = view.getWidth(); + if (view instanceof TextView) { + // Center the reveal on the text which might be off-center from the TextView + TextView tv = (TextView) view; + if (tv.getLayout() != null) { + int innerWidth = (int) tv.getLayout().getLineWidth(0); + innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight(); + width = Math.min(width, innerWidth); + } + } + int cx = view.getLeft() + width / 2; int cy = view.getTop() + view.getHeight() / 2; int w = riv.getWidth(); int h = riv.getHeight(); int r = Math.max( Math.max(cx + cy, cx + (h - cy)), Math.max((w - cx) + cy, (w - cx) + (h - cy))); - ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r) - .start(); + riv.setRevealParameters(cx, cy, r); riv.setPendingIntent(pendingIntent); riv.setRemoteInput(inputs, input); - riv.focus(); + riv.focusAnimated(); return true; } 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 a2289c84a3154..38dbaee576eac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; @@ -33,6 +35,7 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewParent; import android.view.inputmethod.CompletionInfo; @@ -47,11 +50,13 @@ import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto; +import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.stack.ScrollContainer; +import com.android.systemui.statusbar.stack.StackStateAnimator; /** * Host for the remote input. @@ -77,6 +82,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private View mScrollContainerChild; private boolean mRemoved; + private int mRevealCx; + private int mRevealCy; + private int mRevealR; + public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -180,14 +189,28 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return true; } - public void onDefocus() { + private void onDefocus(boolean animate) { mController.removeRemoteInput(mEntry); mEntry.remoteInputText = mEditText.getText(); // During removal, we get reattached and lose focus. Not hiding in that // case to prevent flicker. if (!mRemoved) { - setVisibility(INVISIBLE); + if (animate && mRevealR > 0) { + Animator reveal = ViewAnimationUtils.createCircularReveal( + this, mRevealCx, mRevealCy, mRevealR, 0); + reveal.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + reveal.setDuration(StackStateAnimator.ANIMATION_DURATION_CLOSE_REMOTE_INPUT); + reveal.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(INVISIBLE); + } + }); + reveal.start(); + } else { + setVisibility(INVISIBLE); + } } MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE, mEntry.notification.getPackageName()); @@ -223,6 +246,17 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.setHint(mRemoteInput.getLabel()); } + public void focusAnimated() { + if (getVisibility() != VISIBLE) { + Animator animator = ViewAnimationUtils.createCircularReveal( + this, mRevealCx, mRevealCy, 0, mRevealR); + animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + animator.start(); + } + focus(); + } + public void focus() { MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN, mEntry.notification.getPackageName()); @@ -253,7 +287,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mProgressBar.setVisibility(INVISIBLE); mController.removeSpinning(mEntry.key); updateSendButton(); - onDefocus(); + onDefocus(false /* animate */); } private void updateSendButton() { @@ -272,7 +306,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } public void close() { - mEditText.defocusIfNeeded(); + mEditText.defocusIfNeeded(false /* animated */); } @Override @@ -321,6 +355,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene other.close(); setPendingIntent(other.mPendingIntent); setRemoteInput(other.mRemoteInputs, other.mRemoteInput); + setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR); focus(); } @@ -374,6 +409,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mRemoved = true; } + public void setRevealParameters(int cx, int cy, int r) { + mRevealCx = cx; + mRevealCy = cy; + mRevealR = r; + } + /** * An EditText that changes appearance based on whether it's focusable and becomes * un-focusable whenever the user navigates away from it or it becomes invisible. @@ -389,14 +430,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mBackground = getBackground(); } - private void defocusIfNeeded() { + private void defocusIfNeeded(boolean animate) { if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()) { return; } if (isFocusable() && isEnabled()) { setInnerFocusable(false); if (mRemoteInputView != null) { - mRemoteInputView.onDefocus(); + mRemoteInputView.onDefocus(animate); } mShowImeOnInputConnection = false; } @@ -407,7 +448,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene super.onVisibilityChanged(changedView, visibility); if (!isShown()) { - defocusIfNeeded(); + defocusIfNeeded(false /* animate */); } } @@ -415,7 +456,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); if (!focused) { - defocusIfNeeded(); + defocusIfNeeded(true /* animate */); } } @@ -434,7 +475,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { - defocusIfNeeded(); + defocusIfNeeded(true /* animate */); final InputMethodManager imm = InputMethodManager.getInstance(); imm.hideSoftInputFromWindow(getWindowToken(), 0); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 659eaf7e209ce..3804b42dc5e4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -44,6 +44,7 @@ public class StackStateAnimator { public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448; public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; + public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150; public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650; public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230; public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;