Merge "Add content fade to notification dismissal" into tm-qpr-dev

This commit is contained in:
Johannes Gallmann
2022-10-07 17:55:37 +00:00
committed by Android (Google) Code Review
8 changed files with 121 additions and 18 deletions

View File

@@ -337,9 +337,6 @@
have been scrolled off-screen. --> have been scrolled off-screen. -->
<bool name="config_showNotificationShelf">true</bool> <bool name="config_showNotificationShelf">true</bool>
<!-- Whether or not the notifications should always fade as they are dismissed. -->
<bool name="config_fadeNotificationsOnDismiss">false</bool>
<!-- Whether or not the fade on the notification is based on the amount that it has been swiped <!-- Whether or not the fade on the notification is based on the amount that it has been swiped
off-screen. --> off-screen. -->
<bool name="config_fadeDependingOnAmountSwiped">false</bool> <bool name="config_fadeDependingOnAmountSwiped">false</bool>

View File

@@ -38,6 +38,8 @@ import android.view.View;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.Interpolators;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags; import com.android.systemui.flags.Flags;
@@ -66,7 +68,7 @@ public class SwipeHelper implements Gefingerpoken {
private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec
private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width public static final float SWIPE_PROGRESS_FADE_END = 0.6f; // fraction of thumbnail width
// beyond which swipe progress->0 // beyond which swipe progress->0
public static final float SWIPED_FAR_ENOUGH_SIZE_FRACTION = 0.6f; public static final float SWIPED_FAR_ENOUGH_SIZE_FRACTION = 0.6f;
static final float MAX_SCROLL_SIZE_FRACTION = 0.3f; static final float MAX_SCROLL_SIZE_FRACTION = 0.3f;
@@ -235,7 +237,11 @@ public class SwipeHelper implements Gefingerpoken {
return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress); return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
} }
private float getSwipeAlpha(float progress) { /**
* Returns the alpha value depending on the progress of the swipe.
*/
@VisibleForTesting
public float getSwipeAlpha(float progress) {
if (mFadeDependingOnAmountSwiped) { if (mFadeDependingOnAmountSwiped) {
// The more progress has been fade, the lower the alpha value so that the view fades. // The more progress has been fade, the lower the alpha value so that the view fades.
return Math.max(1 - progress, 0); return Math.max(1 - progress, 0);
@@ -260,7 +266,7 @@ public class SwipeHelper implements Gefingerpoken {
animView.setLayerType(View.LAYER_TYPE_NONE, null); animView.setLayerType(View.LAYER_TYPE_NONE, null);
} }
} }
animView.setAlpha(getSwipeAlpha(swipeProgress)); updateSwipeProgressAlpha(animView, getSwipeAlpha(swipeProgress));
} }
} }
invalidateGlobalRegion(animView); invalidateGlobalRegion(animView);
@@ -561,6 +567,14 @@ public class SwipeHelper implements Gefingerpoken {
mCallback.onChildSnappedBack(animView, targetLeft); mCallback.onChildSnappedBack(animView, targetLeft);
} }
/**
* Called to update the content alpha while the view is swiped
*/
protected void updateSwipeProgressAlpha(View animView, float alpha) {
animView.setAlpha(alpha);
}
/** /**
* Give the swipe helper itself a chance to do something on snap back so NSSL doesn't have * Give the swipe helper itself a chance to do something on snap back so NSSL doesn't have
* to tell us what to do * to tell us what to do

View File

@@ -71,7 +71,9 @@ public class Flags {
public static final UnreleasedFlag NOTIFICATION_MEMORY_MONITOR_ENABLED = new UnreleasedFlag(112, public static final UnreleasedFlag NOTIFICATION_MEMORY_MONITOR_ENABLED = new UnreleasedFlag(112,
false); false);
// next id: 112 public static final UnreleasedFlag NOTIFICATION_DISMISSAL_FADE = new UnreleasedFlag(113, true);
// next id: 114
/***************************************/ /***************************************/
// 200 - keyguard/lockscreen // 200 - keyguard/lockscreen

View File

@@ -1477,6 +1477,20 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} }
} }
/**
* Sets the alpha on the content, while leaving the background of the row itself as is.
*
* @param alpha alpha value to apply to the notification content
*/
public void setContentAlpha(float alpha) {
for (NotificationContentView l : mLayouts) {
l.setAlpha(alpha);
}
if (mChildrenContainer != null) {
mChildrenContainer.setAlpha(alpha);
}
}
public void setIsLowPriority(boolean isLowPriority) { public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority; mIsLowPriority = isLowPriority;
mPrivateLayout.setIsLowPriority(isLowPriority); mPrivateLayout.setIsLowPriority(isLowPriority);

View File

@@ -56,12 +56,13 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.ExpandHelper; import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken; import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper; import com.android.systemui.SwipeHelper;
import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -178,6 +179,7 @@ public class NotificationStackScrollLayoutController {
@Nullable private Boolean mHistoryEnabled; @Nullable private Boolean mHistoryEnabled;
private int mBarState; private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController; private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final FeatureFlags mFeatureFlags;
private View mLongPressedView; private View mLongPressedView;
@@ -639,7 +641,8 @@ public class NotificationStackScrollLayoutController {
InteractionJankMonitor jankMonitor, InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger, StackStateLogger stackLogger,
NotificationStackScrollLogger logger, NotificationStackScrollLogger logger,
NotificationStackSizeCalculator notificationStackSizeCalculator) { NotificationStackSizeCalculator notificationStackSizeCalculator,
FeatureFlags featureFlags) {
mStackStateLogger = stackLogger; mStackStateLogger = stackLogger;
mLogger = logger; mLogger = logger;
mAllowLongPress = allowLongPress; mAllowLongPress = allowLongPress;
@@ -675,6 +678,7 @@ public class NotificationStackScrollLayoutController {
mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager; mRemoteInputManager = remoteInputManager;
mShadeController = shadeController; mShadeController = shadeController;
mFeatureFlags = featureFlags;
updateResources(); updateResources();
} }
@@ -739,9 +743,7 @@ public class NotificationStackScrollLayoutController {
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly mFadeNotificationsOnDismiss = mFeatureFlags.isEnabled(Flags.NOTIFICATION_DISMISSAL_FADE);
mResources.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);

View File

@@ -185,6 +185,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
return false; return false;
} }
@Override
protected void updateSwipeProgressAlpha(View animView, float alpha) {
if (animView instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) animView).setContentAlpha(alpha);
}
}
@VisibleForTesting @VisibleForTesting
protected void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity, protected void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity,
NotificationMenuRowPlugin menuRow) { NotificationMenuRowPlugin menuRow) {

View File

@@ -45,6 +45,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
@@ -127,6 +128,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private NotificationStackScrollLogger mLogger; @Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private ShadeTransitionController mShadeTransitionController; @Mock private ShadeTransitionController mShadeTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Captor @Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -174,7 +176,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mJankMonitor, mJankMonitor,
mStackLogger, mStackLogger,
mLogger, mLogger,
mNotificationStackSizeCalculator mNotificationStackSizeCalculator,
mFeatureFlags
); );
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);

View File

@@ -18,10 +18,13 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -51,10 +54,15 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/** /**
* Tests for {@link NotificationSwipeHelper}. * Tests for {@link NotificationSwipeHelper}.
*/ */
@@ -74,7 +82,11 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
private Runnable mFalsingCheck; private Runnable mFalsingCheck;
private FeatureFlags mFeatureFlags; private FeatureFlags mFeatureFlags;
@Rule public MockitoRule mockito = MockitoJUnit.rule(); private static final int FAKE_ROW_WIDTH = 20;
private static final int FAKE_ROW_HEIGHT = 20;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -444,8 +456,8 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
doReturn(5f).when(mEvent).getRawX(); doReturn(5f).when(mEvent).getRawX();
doReturn(10f).when(mEvent).getRawY(); doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mView).getWidth(); doReturn(FAKE_ROW_WIDTH).when(mView).getWidth();
doReturn(20).when(mView).getHeight(); doReturn(FAKE_ROW_HEIGHT).when(mView).getHeight();
Answer answer = (Answer) invocation -> { Answer answer = (Answer) invocation -> {
int[] arr = invocation.getArgument(0); int[] arr = invocation.getArgument(0);
@@ -472,8 +484,8 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
doReturn(5f).when(mEvent).getRawX(); doReturn(5f).when(mEvent).getRawX();
doReturn(10f).when(mEvent).getRawY(); doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mNotificationRow).getWidth(); doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getWidth();
doReturn(20).when(mNotificationRow).getActualHeight(); doReturn(FAKE_ROW_HEIGHT).when(mNotificationRow).getActualHeight();
Answer answer = (Answer) invocation -> { Answer answer = (Answer) invocation -> {
int[] arr = invocation.getArgument(0); int[] arr = invocation.getArgument(0);
@@ -491,4 +503,56 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
assertFalse("Touch is not within the view", assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow)); mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
} }
@Test
public void testContentAlphaRemainsUnchangedWhenNotificationIsNotDismissible() {
doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
mSwipeHelper.onTranslationUpdate(mNotificationRow, 12, false);
verify(mNotificationRow, never()).setContentAlpha(anyFloat());
}
@Test
public void testContentAlphaRemainsUnchangedWhenFeatureFlagIsDisabled() {
// Returning true prevents alpha fade. In an unmocked scenario the callback is instantiated
// within NotificationStackScrollLayoutController and returns the inverted value of the
// feature flag
doReturn(true).when(mCallback).updateSwipeProgress(any(), anyBoolean(), anyFloat());
doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
mSwipeHelper.onTranslationUpdate(mNotificationRow, 12, true);
verify(mNotificationRow, never()).setContentAlpha(anyFloat());
}
@Test
public void testContentAlphaFadeAnimationSpecs() {
// The alpha fade should be linear from 1f to 0f as translation progresses from 0 to 60% of
// view-width, and 0f at translations higher than that.
doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
List<Integer> translations = Arrays.asList(
-FAKE_ROW_WIDTH * 2,
-FAKE_ROW_WIDTH,
(int) (-FAKE_ROW_WIDTH * 0.3),
0,
(int) (FAKE_ROW_WIDTH * 0.3),
(int) (FAKE_ROW_WIDTH * 0.6),
FAKE_ROW_WIDTH,
FAKE_ROW_WIDTH * 2);
List<Float> expectedAlphas = translations.stream().map(translation ->
mSwipeHelper.getSwipeAlpha(Math.abs((float) translation / FAKE_ROW_WIDTH)))
.collect(Collectors.toList());
for (Integer translation : translations) {
mSwipeHelper.onTranslationUpdate(mNotificationRow, translation, true);
}
ArgumentCaptor<Float> capturedValues = ArgumentCaptor.forClass(Float.class);
verify(mNotificationRow, times(translations.size())).setContentAlpha(
capturedValues.capture());
assertEquals(expectedAlphas, capturedValues.getAllValues());
}
} }