Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +27 −12 Original line number Original line Diff line number Diff line Loading @@ -326,9 +326,9 @@ public class NotificationShelf extends ActivatableNotificationView implements || child.isPinned(); || child.isPinned(); boolean isLastChild = child == lastChild; boolean isLastChild = child == lastChild; final float viewStart = child.getTranslationY(); final float viewStart = child.getTranslationY(); final float shelfClipStart = getTranslationY() - mPaddingBetweenElements; final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast, final float inShelfAmount = getAmountInShelf(i, child, scrollingFast, expandingAnimated, isLastChild); expandingAnimated, isLastChild, shelfClipStart); // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) { if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) { Loading Loading @@ -609,10 +609,18 @@ public class NotificationShelf extends ActivatableNotificationView implements } } /** /** * @return the amount how much this notification is in the shelf * @param i Index of the view in the host layout. * @param view The current ExpandableView. * @param scrollingFast Whether we are scrolling fast. * @param expandingAnimated Whether we are expanding a notification. * @param isLastChild Whether this is the last view. * @param shelfClipStart The point at which notifications start getting clipped by the shelf. * @return The amount how much this notification is in the shelf. * 0f is not in shelf. 1f is completely in shelf. */ */ private float updateShelfTransformation(int i, ExpandableView view, boolean scrollingFast, @VisibleForTesting boolean expandingAnimated, boolean isLastChild) { public float getAmountInShelf(int i, ExpandableView view, boolean scrollingFast, boolean expandingAnimated, boolean isLastChild, float shelfClipStart) { // Let's calculate how much the view is in the shelf // Let's calculate how much the view is in the shelf float viewStart = view.getTranslationY(); float viewStart = view.getTranslationY(); Loading @@ -635,29 +643,33 @@ public class NotificationShelf extends ActivatableNotificationView implements float viewEnd = viewStart + fullHeight; float viewEnd = viewStart + fullHeight; float fullTransitionAmount = 0.0f; float fullTransitionAmount = 0.0f; float iconTransitionAmount = 0.0f; float iconTransitionAmount = 0.0f; float shelfStart = getTranslationY() - mPaddingBetweenElements; // Don't animate shelf icons during shade expansion. if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) { if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) { // TODO(b/172289889) handle icon placement for notification that is clipped by the shelf // TODO(b/172289889) handle icon placement for notification that is clipped by the shelf if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) { if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) { fullTransitionAmount = 1f; fullTransitionAmount = 1f; iconTransitionAmount = 1f; iconTransitionAmount = 1f; } } } else if (viewEnd >= shelfStart } else if (viewEnd >= shelfClipStart && (!mAmbientState.isUnlockHintRunning() || view.isInShelf()) && (!mAmbientState.isUnlockHintRunning() || view.isInShelf()) && (mAmbientState.isShadeExpanded() && (mAmbientState.isShadeExpanded() || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) { || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) { if (viewStart < shelfStart) { if (viewStart < shelfClipStart && Math.abs(viewStart - shelfClipStart) > 0.001f) { float fullAmount = (shelfStart - viewStart) / fullHeight; // Partially clipped by shelf. float fullAmount = (shelfClipStart - viewStart) / fullHeight; fullAmount = Math.min(1.0f, fullAmount); fullAmount = Math.min(1.0f, fullAmount); fullTransitionAmount = 1.0f - fullAmount; fullTransitionAmount = 1.0f - fullAmount; if (isLastChild) { if (isLastChild) { // Reduce icon transform distance to completely fade in shelf icon // Reduce icon transform distance to completely fade in shelf icon // by the time the notification icon fades out, and vice versa // by the time the notification icon fades out, and vice versa iconTransitionAmount = (shelfStart - viewStart) iconTransitionAmount = (shelfClipStart - viewStart) / (iconTransformStart - viewStart); / (iconTransformStart - viewStart); } else { } else { iconTransitionAmount = (shelfStart - iconTransformStart) / transformDistance; iconTransitionAmount = (shelfClipStart - iconTransformStart) / transformDistance; } } iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f); iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f); iconTransitionAmount = 1.0f - iconTransitionAmount; iconTransitionAmount = 1.0f - iconTransitionAmount; Loading Loading @@ -772,6 +784,9 @@ public class NotificationShelf extends ActivatableNotificationView implements } } private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) { private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) { if (mShelfIcons == null) { return null; } return mShelfIcons.getIconState(icon); return mShelfIcons.getIconState(icon); } } Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +110 −2 Original line number Original line Diff line number Diff line Loading @@ -5,8 +5,9 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.NotificationShelf import junit.framework.Assert.assertFalse import com.android.systemui.statusbar.StatusBarIconView import junit.framework.Assert.assertTrue import com.android.systemui.statusbar.notification.row.ExpandableView import junit.framework.Assert.* import org.junit.Before import org.junit.Before import org.junit.Test import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith Loading Loading @@ -143,6 +144,113 @@ class NotificationShelfTest : SysuiTestCase() { assertFalse(isYBelowShelfInView) assertFalse(isYBelowShelfInView) } } @Test fun getAmountInShelf_lastViewBelowShelf_completelyInShelf() { val shelfClipStart = 0f val viewStart = 1f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(20) whenever(expandableView.minHeight).thenReturn(20) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(true) whenever(ambientState.isOnKeyguard).thenReturn(true) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isShadeExpanded).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(1f, amountInShelf) } @Test fun getAmountInShelf_lastViewAlmostBelowShelf_completelyInShelf() { val viewStart = 0f val shelfClipStart = 0.001f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(20) whenever(expandableView.minHeight).thenReturn(20) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(true) whenever(ambientState.isOnKeyguard).thenReturn(true) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isShadeExpanded).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(1f, amountInShelf) } @Test fun getAmountInShelf_lastViewHalfClippedByShelf_halfInShelf() { val viewStart = 0f val shelfClipStart = 10f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(25) whenever(expandableView.minHeight).thenReturn(25) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(true) whenever(ambientState.isOnKeyguard).thenReturn(true) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isShadeExpanded).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(0.5f, amountInShelf) } @Test fun getAmountInShelf_lastViewAboveShelf_notInShelf() { val viewStart = 0f val shelfClipStart = 15f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(10) whenever(expandableView.minHeight).thenReturn(10) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(false) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isOnKeyguard).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(0f, amountInShelf) } private fun setFractionToShade(fraction: Float) { private fun setFractionToShade(fraction: Float) { whenever(ambientState.fractionToShade).thenReturn(fraction) whenever(ambientState.fractionToShade).thenReturn(fraction) } } Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +27 −12 Original line number Original line Diff line number Diff line Loading @@ -326,9 +326,9 @@ public class NotificationShelf extends ActivatableNotificationView implements || child.isPinned(); || child.isPinned(); boolean isLastChild = child == lastChild; boolean isLastChild = child == lastChild; final float viewStart = child.getTranslationY(); final float viewStart = child.getTranslationY(); final float shelfClipStart = getTranslationY() - mPaddingBetweenElements; final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast, final float inShelfAmount = getAmountInShelf(i, child, scrollingFast, expandingAnimated, isLastChild); expandingAnimated, isLastChild, shelfClipStart); // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) { if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) { Loading Loading @@ -609,10 +609,18 @@ public class NotificationShelf extends ActivatableNotificationView implements } } /** /** * @return the amount how much this notification is in the shelf * @param i Index of the view in the host layout. * @param view The current ExpandableView. * @param scrollingFast Whether we are scrolling fast. * @param expandingAnimated Whether we are expanding a notification. * @param isLastChild Whether this is the last view. * @param shelfClipStart The point at which notifications start getting clipped by the shelf. * @return The amount how much this notification is in the shelf. * 0f is not in shelf. 1f is completely in shelf. */ */ private float updateShelfTransformation(int i, ExpandableView view, boolean scrollingFast, @VisibleForTesting boolean expandingAnimated, boolean isLastChild) { public float getAmountInShelf(int i, ExpandableView view, boolean scrollingFast, boolean expandingAnimated, boolean isLastChild, float shelfClipStart) { // Let's calculate how much the view is in the shelf // Let's calculate how much the view is in the shelf float viewStart = view.getTranslationY(); float viewStart = view.getTranslationY(); Loading @@ -635,29 +643,33 @@ public class NotificationShelf extends ActivatableNotificationView implements float viewEnd = viewStart + fullHeight; float viewEnd = viewStart + fullHeight; float fullTransitionAmount = 0.0f; float fullTransitionAmount = 0.0f; float iconTransitionAmount = 0.0f; float iconTransitionAmount = 0.0f; float shelfStart = getTranslationY() - mPaddingBetweenElements; // Don't animate shelf icons during shade expansion. if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) { if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) { // TODO(b/172289889) handle icon placement for notification that is clipped by the shelf // TODO(b/172289889) handle icon placement for notification that is clipped by the shelf if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) { if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) { fullTransitionAmount = 1f; fullTransitionAmount = 1f; iconTransitionAmount = 1f; iconTransitionAmount = 1f; } } } else if (viewEnd >= shelfStart } else if (viewEnd >= shelfClipStart && (!mAmbientState.isUnlockHintRunning() || view.isInShelf()) && (!mAmbientState.isUnlockHintRunning() || view.isInShelf()) && (mAmbientState.isShadeExpanded() && (mAmbientState.isShadeExpanded() || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) { || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) { if (viewStart < shelfStart) { if (viewStart < shelfClipStart && Math.abs(viewStart - shelfClipStart) > 0.001f) { float fullAmount = (shelfStart - viewStart) / fullHeight; // Partially clipped by shelf. float fullAmount = (shelfClipStart - viewStart) / fullHeight; fullAmount = Math.min(1.0f, fullAmount); fullAmount = Math.min(1.0f, fullAmount); fullTransitionAmount = 1.0f - fullAmount; fullTransitionAmount = 1.0f - fullAmount; if (isLastChild) { if (isLastChild) { // Reduce icon transform distance to completely fade in shelf icon // Reduce icon transform distance to completely fade in shelf icon // by the time the notification icon fades out, and vice versa // by the time the notification icon fades out, and vice versa iconTransitionAmount = (shelfStart - viewStart) iconTransitionAmount = (shelfClipStart - viewStart) / (iconTransformStart - viewStart); / (iconTransformStart - viewStart); } else { } else { iconTransitionAmount = (shelfStart - iconTransformStart) / transformDistance; iconTransitionAmount = (shelfClipStart - iconTransformStart) / transformDistance; } } iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f); iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f); iconTransitionAmount = 1.0f - iconTransitionAmount; iconTransitionAmount = 1.0f - iconTransitionAmount; Loading Loading @@ -772,6 +784,9 @@ public class NotificationShelf extends ActivatableNotificationView implements } } private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) { private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) { if (mShelfIcons == null) { return null; } return mShelfIcons.getIconState(icon); return mShelfIcons.getIconState(icon); } } Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +110 −2 Original line number Original line Diff line number Diff line Loading @@ -5,8 +5,9 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.NotificationShelf import junit.framework.Assert.assertFalse import com.android.systemui.statusbar.StatusBarIconView import junit.framework.Assert.assertTrue import com.android.systemui.statusbar.notification.row.ExpandableView import junit.framework.Assert.* import org.junit.Before import org.junit.Before import org.junit.Test import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith Loading Loading @@ -143,6 +144,113 @@ class NotificationShelfTest : SysuiTestCase() { assertFalse(isYBelowShelfInView) assertFalse(isYBelowShelfInView) } } @Test fun getAmountInShelf_lastViewBelowShelf_completelyInShelf() { val shelfClipStart = 0f val viewStart = 1f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(20) whenever(expandableView.minHeight).thenReturn(20) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(true) whenever(ambientState.isOnKeyguard).thenReturn(true) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isShadeExpanded).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(1f, amountInShelf) } @Test fun getAmountInShelf_lastViewAlmostBelowShelf_completelyInShelf() { val viewStart = 0f val shelfClipStart = 0.001f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(20) whenever(expandableView.minHeight).thenReturn(20) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(true) whenever(ambientState.isOnKeyguard).thenReturn(true) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isShadeExpanded).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(1f, amountInShelf) } @Test fun getAmountInShelf_lastViewHalfClippedByShelf_halfInShelf() { val viewStart = 0f val shelfClipStart = 10f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(25) whenever(expandableView.minHeight).thenReturn(25) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(true) whenever(ambientState.isOnKeyguard).thenReturn(true) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isShadeExpanded).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(0.5f, amountInShelf) } @Test fun getAmountInShelf_lastViewAboveShelf_notInShelf() { val viewStart = 0f val shelfClipStart = 15f val expandableView = mock(ExpandableView::class.java) whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java)) whenever(expandableView.translationY).thenReturn(viewStart) whenever(expandableView.actualHeight).thenReturn(10) whenever(expandableView.minHeight).thenReturn(10) whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY whenever(expandableView.isInShelf).thenReturn(false) whenever(ambientState.isExpansionChanging).thenReturn(false) whenever(ambientState.isOnKeyguard).thenReturn(true) val amountInShelf = shelf.getAmountInShelf(/* i= */ 0, /* view= */ expandableView, /* scrollingFast= */ false, /* expandingAnimated= */ false, /* isLastChild= */ true, shelfClipStart) assertEquals(0f, amountInShelf) } private fun setFractionToShade(fraction: Float) { private fun setFractionToShade(fraction: Float) { whenever(ambientState.fractionToShade).thenReturn(fraction) whenever(ambientState.fractionToShade).thenReturn(fraction) } } Loading