summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/layout/notification_material_action_list.xml1
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/res/layout/remote_input.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java192
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java68
9 files changed, 267 insertions, 20 deletions
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index a5608af7a081..7aef82a8aa3a 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -27,6 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
+ android:layout_gravity="bottom"
android:orientation="horizontal"
android:background="@color/notification_action_list_background_color"
>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e624441ca777..25fb24a3417f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -221,6 +221,7 @@ android_library {
"WindowManager-Shell",
"LowLightDreamLib",
"motion_tool_lib",
+ "androidx.core_core-animation-testing-nodeps",
],
}
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index a5b2f80a3158..f4b0a45a8d32 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -20,6 +20,7 @@
<com.android.systemui.statusbar.policy.RemoteInputView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/remote_input"
+ android:forceHasOverlappingRendering="false"
android:layout_height="match_parent"
android:layout_width="match_parent">
<LinearLayout
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 389639d23795..f3c03864382f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -100,6 +100,11 @@ object Flags {
// TODO(b/257315550): Tracking Bug
val NO_HUN_FOR_OLD_WHEN = unreleasedFlag(118, "no_hun_for_old_when")
+ // TODO(b/260335638): Tracking Bug
+ @JvmField
+ val NOTIFICATION_INLINE_REPLY_ANIMATION =
+ unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
+
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index f668528d8f5f..351603700f98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -463,7 +463,11 @@ public class NotificationRemoteInputManager implements Dumpable {
riv.getController().setRemoteInput(input);
riv.getController().setRemoteInputs(inputs);
riv.getController().setEditedSuggestionInfo(editedSuggestionInfo);
- riv.focusAnimated();
+ ViewGroup parent = view.getParent() != null ? (ViewGroup) view.getParent() : null;
+ if (parent != null) {
+ riv.setDefocusTargetHeight(parent.getHeight());
+ }
+ riv.focusAnimated(parent);
if (userMessageContent != null) {
riv.setEditTextContent(userMessageContent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 277ad8e54016..83d6abb4d977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -70,7 +70,7 @@ import java.util.List;
/**
* A frame layout containing the actual payload of the notification, including the contracted,
- * expanded and heads up layout. This class is responsible for clipping the content and and
+ * expanded and heads up layout. This class is responsible for clipping the content and
* switching between the expanded, contracted and the heads up view depending on its clipped size.
*/
public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
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 dd400b3fc0ff..9f5ad1c5fddc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -18,8 +18,8 @@ package com.android.systemui.statusbar.policy;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
+
import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
@@ -57,6 +57,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;
@@ -67,6 +68,11 @@ import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.animation.Animator;
+import androidx.core.animation.AnimatorListenerAdapter;
+import androidx.core.animation.AnimatorSet;
+import androidx.core.animation.ObjectAnimator;
+import androidx.core.animation.ValueAnimator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -74,6 +80,7 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.InterpolatorsAndroidX;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -97,6 +104,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
// A marker object that let's us easily find views of this class.
public static final Object VIEW_TAG = new Object();
+ private static final long FOCUS_ANIMATION_TOTAL_DURATION = ANIMATION_DURATION_STANDARD;
+ private static final long FOCUS_ANIMATION_CROSSFADE_DURATION = 50;
+ private static final long FOCUS_ANIMATION_FADE_IN_DELAY = 33;
+ private static final long FOCUS_ANIMATION_FADE_IN_DURATION = 83;
+ private static final float FOCUS_ANIMATION_MIN_SCALE = 0.5f;
+
public final Object mToken = new Object();
private final SendButtonTextWatcher mTextWatcher;
@@ -108,6 +121,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private RemoteEditText mEditText;
private ImageButton mSendButton;
+ private LinearLayout mContentView;
private GradientDrawable mContentBackground;
private ProgressBar mProgressBar;
private ImageView mDelete;
@@ -115,7 +129,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private boolean mColorized;
private int mTint;
private boolean mResetting;
- @Nullable private RevealParams mRevealParams;
+ @Nullable
+ private RevealParams mRevealParams;
+ private Rect mContentBackgroundBounds;
+ private boolean mIsFocusAnimationFlagActive;
// TODO(b/193539698): move these to a Controller
private RemoteInputController mController;
@@ -125,6 +142,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private boolean mSending;
private NotificationViewWrapper mWrapper;
+ private Integer mDefocusTargetHeight = null;
+
+
// TODO(b/193539698): remove this; views shouldn't have access to their controller, and places
// that need the controller shouldn't have access to the view
private RemoteInputViewController mViewController;
@@ -255,8 +275,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mDeleteBg.setImageTintBlendMode(BlendMode.SRC_IN);
mDelete.setImageTintBlendMode(BlendMode.SRC_IN);
mDelete.setOnClickListener(v -> setAttachment(null));
- LinearLayout contentView = findViewById(R.id.remote_input_content);
- contentView.setBackground(mContentBackground);
+ mContentView = findViewById(R.id.remote_input_content);
+ mContentView.setBackground(mContentBackground);
mEditText = findViewById(R.id.remote_input_text);
mEditText.setInnerFocusable(false);
// TextView initializes the spell checked when the view is attached to a window.
@@ -398,20 +418,70 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
return true;
}
- private void onDefocus(boolean animate, boolean logClose) {
+ /**
+ * View will ensure to use at most the provided defocusTargetHeight, when defocusing animated.
+ * This is to ensure that the parent can resize itself to the targetHeight while the defocus
+ * animation of the RemoteInputView is running.
+ *
+ * @param defocusTargetHeight The target height the parent will resize itself to. If null, the
+ * RemoteInputView will not resize itself.
+ */
+ public void setDefocusTargetHeight(Integer defocusTargetHeight) {
+ mDefocusTargetHeight = defocusTargetHeight;
+ }
+
+ @VisibleForTesting
+ void onDefocus(boolean animate, boolean logClose) {
mController.removeRemoteInput(mEntry, mToken);
mEntry.remoteInputText = mEditText.getText();
// During removal, we get reattached and lose focus. Not hiding in that
// case to prevent flicker.
if (!mRemoved) {
- if (animate && mRevealParams != null && mRevealParams.radius > 0) {
- Animator reveal = mRevealParams.createCircularHideAnimator(this);
+ if (animate && mIsFocusAnimationFlagActive) {
+ Animator animator = getDefocusAnimator();
+
+ // When defocusing, the notification needs to shrink. Therefore, we need to free
+ // up the space that is 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)
+ if (mDefocusTargetHeight != null && mDefocusTargetHeight < getHeight()
+ && mDefocusTargetHeight >= 0
+ && getLayoutParams() instanceof FrameLayout.LayoutParams) {
+ int heightToShrink = getHeight() - mDefocusTargetHeight;
+ FrameLayout.LayoutParams layoutParams =
+ (FrameLayout.LayoutParams) getLayoutParams();
+ layoutParams.topMargin = -heightToShrink;
+ setLayoutParams(layoutParams);
+ ((ViewGroup) getParent().getParent()).setClipChildren(false);
+ }
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ //reset top margin after the animation
+ if (getLayoutParams() instanceof FrameLayout.LayoutParams) {
+ FrameLayout.LayoutParams layoutParams =
+ (FrameLayout.LayoutParams) getLayoutParams();
+ layoutParams.topMargin = 0;
+ setLayoutParams(layoutParams);
+ ((ViewGroup) getParent().getParent()).setClipChildren(true);
+ }
+ setVisibility(GONE);
+ if (mWrapper != null) {
+ mWrapper.setRemoteInputVisible(false);
+ }
+ }
+ });
+ animator.start();
+
+ } else if (animate && mRevealParams != null && mRevealParams.radius > 0) {
+ android.animation.Animator reveal = mRevealParams.createCircularHideAnimator(this);
reveal.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
reveal.setDuration(StackStateAnimator.ANIMATION_DURATION_CLOSE_REMOTE_INPUT);
- reveal.addListener(new AnimatorListenerAdapter() {
+ reveal.addListener(new android.animation.AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationEnd(android.animation.Animator animation) {
setVisibility(GONE);
if (mWrapper != null) {
mWrapper.setRemoteInputVisible(false);
@@ -533,12 +603,29 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEditText.setText(editTextContent);
}
- public void focusAnimated() {
- if (getVisibility() != VISIBLE && mRevealParams != null) {
- Animator animator = mRevealParams.createCircularRevealAnimator(this);
+ /**
+ * Sets whether the feature flag for the updated inline reply animation is active or not.
+ * @param active
+ */
+ public void setIsFocusAnimationFlagActive(boolean active) {
+ mIsFocusAnimationFlagActive = active;
+ }
+
+ /**
+ * Focuses the RemoteInputView and animates its appearance
+ *
+ * @param crossFadeView view that will be crossfaded during the appearance animation
+ */
+ public void focusAnimated(View crossFadeView) {
+ if (!mIsFocusAnimationFlagActive && getVisibility() != VISIBLE
+ && mRevealParams != null) {
+ android.animation.Animator animator = mRevealParams.createCircularRevealAnimator(this);
animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.start();
+ } else if (mIsFocusAnimationFlagActive && getVisibility() != VISIBLE) {
+ setAlpha(0f);
+ getFocusAnimator(crossFadeView).start();
}
focus();
}
@@ -737,6 +824,81 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mOnSendListeners.remove(listener);
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mIsFocusAnimationFlagActive) setPivotY(getMeasuredHeight());
+ if (mContentBackgroundBounds != null) {
+ mContentBackground.setBounds(mContentBackgroundBounds);
+ }
+ }
+
+ private Animator getFocusAnimator(View crossFadeView) {
+ final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 0f, 1f);
+ alphaAnimator.setStartDelay(FOCUS_ANIMATION_FADE_IN_DELAY);
+ alphaAnimator.setDuration(FOCUS_ANIMATION_FADE_IN_DURATION);
+ alphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
+
+ ValueAnimator scaleAnimator = ValueAnimator.ofFloat(FOCUS_ANIMATION_MIN_SCALE, 1f);
+ scaleAnimator.addUpdateListener(valueAnimator -> {
+ setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue());
+ });
+ scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION);
+ scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN);
+
+ final Animator crossFadeViewAlphaAnimator =
+ ObjectAnimator.ofFloat(crossFadeView, View.ALPHA, 1f, 0f);
+ crossFadeViewAlphaAnimator.setDuration(FOCUS_ANIMATION_CROSSFADE_DURATION);
+ crossFadeViewAlphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
+ alphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation, boolean isReverse) {
+ crossFadeView.setAlpha(1f);
+ }
+ });
+
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(alphaAnimator, scaleAnimator, crossFadeViewAlphaAnimator);
+ return animatorSet;
+ }
+
+ private Animator getDefocusAnimator() {
+ final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 1f, 0f);
+ alphaAnimator.setDuration(FOCUS_ANIMATION_CROSSFADE_DURATION);
+ alphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
+
+ ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1f, FOCUS_ANIMATION_MIN_SCALE);
+ scaleAnimator.addUpdateListener(valueAnimator -> {
+ 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);
+ }
+ });
+
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(alphaAnimator, scaleAnimator);
+ return animatorSet;
+ }
+
+ /**
+ * Sets affected view properties for a vertical scale animation
+ *
+ * @param scaleY desired vertical view scale
+ */
+ private void setFocusAnimationScaleY(float scaleY) {
+ int verticalBoundOffset = (int) ((1f - scaleY) * 0.5f * mContentView.getHeight());
+ mContentBackgroundBounds = new Rect(0, verticalBoundOffset, mContentView.getWidth(),
+ mContentView.getHeight() - verticalBoundOffset);
+ mContentBackground.setBounds(mContentBackgroundBounds);
+ mContentView.setBackground(mContentBackground);
+ setTranslationY(verticalBoundOffset);
+ }
+
/** Handler for button click on send action in IME. */
private class EditorActionHandler implements TextView.OnEditorActionListener {
@@ -991,11 +1153,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
this.radius = radius;
}
- Animator createCircularHideAnimator(View view) {
+ android.animation.Animator createCircularHideAnimator(View view) {
return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, radius, 0);
}
- Animator createCircularRevealAnimator(View view) {
+ android.animation.Animator createCircularRevealAnimator(View view) {
return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 0, radius);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index f8451017b367..22b4c9d81d25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -30,6 +30,8 @@ import android.util.Log
import android.view.View
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.RemoteInputController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -61,6 +63,8 @@ interface RemoteInputViewController {
var revealParams: RevealParams?
+ val isFocusAnimationFlagActive: Boolean
+
/**
* Sets the smart reply that should be inserted in the remote input, or `null` if the user is
* not editing a smart reply.
@@ -117,7 +121,8 @@ class RemoteInputViewControllerImpl @Inject constructor(
private val remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler,
private val remoteInputController: RemoteInputController,
private val shortcutManager: ShortcutManager,
- private val uiEventLogger: UiEventLogger
+ private val uiEventLogger: UiEventLogger,
+ private val mFlags: FeatureFlags
) : RemoteInputViewController {
private val onSendListeners = ArraySet<OnSendRemoteInputListener>()
@@ -149,6 +154,9 @@ class RemoteInputViewControllerImpl @Inject constructor(
override val isActive: Boolean get() = view.isActive
+ override val isFocusAnimationFlagActive: Boolean
+ get() = mFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION)
+
override fun bind() {
if (isBound) return
isBound = true
@@ -159,6 +167,7 @@ class RemoteInputViewControllerImpl @Inject constructor(
view.setSupportedMimeTypes(it.allowedDataTypes)
}
view.setRevealParameters(revealParams)
+ view.setIsFocusAnimationFlagActive(isFocusAnimationFlagActive)
view.addOnEditTextFocusChangedListener(onFocusChangeListener)
view.addOnSendRemoteInputListener(onSendRemoteInputListener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 915e999c2646..1fe2008d2abe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy;
import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -57,6 +59,7 @@ import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
import androidx.annotation.NonNull;
+import androidx.core.animation.AnimatorTestRule;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -64,15 +67,19 @@ import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;
import org.junit.After;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -99,6 +106,9 @@ public class RemoteInputViewTest extends SysuiTestCase {
private BlockingQueueIntentReceiver mReceiver;
private final UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
+ @ClassRule
+ public static AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
@@ -294,6 +304,9 @@ public class RemoteInputViewTest extends SysuiTestCase {
/* invoke the captured callback */
onBackInvokedCallbackCaptor.getValue().onBackInvoked();
+ /* wait for RemoteInputView disappear animation to finish */
+ mAnimatorTestRule.advanceTimeBy(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
/* verify that the RemoteInputView goes away */
assertEquals(view.getVisibility(), View.GONE);
}
@@ -363,19 +376,70 @@ public class RemoteInputViewTest extends SysuiTestCase {
mUiEventLoggerFake.eventId(1));
}
+ @Test
+ public void testFocusAnimation() throws Exception {
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
+ RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+ bindController(view, row.getEntry());
+ view.setVisibility(View.GONE);
+
+ View crossFadeView = new View(mContext);
+
+ // Start focus animation
+ view.focusAnimated(crossFadeView);
+
+ // fast forward to end of animation
+ mAnimatorTestRule.advanceTimeBy(ANIMATION_DURATION_STANDARD);
+
+ // assert that crossFadeView's alpha is reset to 1f after the animation (hidden behind
+ // RemoteInputView)
+ assertEquals(1f, crossFadeView.getAlpha());
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(1f, view.getAlpha());
+ }
+
+ @Test
+ public void testDefocusAnimation() throws Exception {
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
+ RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+ bindController(view, row.getEntry());
+
+ // Start defocus animation
+ view.onDefocus(true, false);
+ assertEquals(View.VISIBLE, view.getVisibility());
+
+ // fast forward to end of animation
+ mAnimatorTestRule.advanceTimeBy(ANIMATION_DURATION_STANDARD);
+
+ // assert that RemoteInputView is no longer visible
+ assertEquals(View.GONE, view.getVisibility());
+ }
+
// NOTE: because we're refactoring the RemoteInputView and moving logic into the
- // RemoteInputViewController, it's easiest to just test the system of the two classes together.
+ // RemoteInputViewController, it's easiest to just test the system of the two classes together.
@NonNull
private RemoteInputViewController bindController(
RemoteInputView view,
NotificationEntry entry) {
+ FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
+ fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
view,
entry,
mRemoteInputQuickSettingsDisabler,
mController,
mShortcutManager,
- mUiEventLoggerFake);
+ mUiEventLoggerFake,
+ fakeFeatureFlags
+ );
viewController.bind();
return viewController;
}