summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt194
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_shelf.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java30
7 files changed, 443 insertions, 33 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 1a1af2eecc00..87abd0a4494a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar.notification.stack
+import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
@@ -20,6 +21,7 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
@@ -30,6 +32,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -59,7 +62,7 @@ open class NotificationShelfTest : SysuiTestCase() {
.inflate(
/* resource = */ R.layout.status_bar_notification_shelf,
/* root = */ root,
- /* attachToRoot = */ false
+ /* attachToRoot = */ false,
) as NotificationShelf
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
@@ -128,6 +131,177 @@ open class NotificationShelfTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_splitShade_LTR() {
+ // Given: LTR mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: shelf should align to end
+ assertTrue(shelfSpy.isAlignedToEnd)
+ assertTrue(shelfSpy.isAlignedToRight)
+ assertTrue(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertTrue(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_nonSplitShade_LTR() {
+ // Given: LTR mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: shelf should not align to end
+ assertFalse(shelfSpy.isAlignedToEnd)
+ assertFalse(shelfSpy.isAlignedToRight)
+ assertFalse(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertFalse(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_splitShade_RTL() {
+ // Given: RTL mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: shelf should align to end, but to left due to RTL
+ assertTrue(shelfSpy.isAlignedToEnd)
+ assertFalse(shelfSpy.isAlignedToRight)
+ assertTrue(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertTrue(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_nonSplitShade_RTL() {
+ // Given: RTL mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: shelf should not align to end, but to right due to RTL
+ assertFalse(shelfSpy.isAlignedToEnd)
+ assertTrue(shelfSpy.isAlignedToRight)
+ assertFalse(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertFalse(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_splitShade_LTR() {
+ // Given: LTR mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to shelf's width - actual width
+ assertEquals(60f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_splitShade_LTR() {
+ // Given: LTR mode, split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's width
+ assertEquals(100f, shelfSpy.shelfRightBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_nonSplitShade_LTR() {
+ // Given: LTR mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to 0f
+ assertEquals(0f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_nonSplitShade_LTR() {
+ // Given: LTR mode, non split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's actual width
+ assertEquals(40f, shelfSpy.shelfRightBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_splitShade_RTL() {
+ // Given: RTL mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to 0f
+ assertEquals(0f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_splitShade_RTL() {
+ // Given: RTL mode, split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's actual width
+ assertEquals(40f, shelfSpy.shelfRightBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_nonSplitShade_RTL() {
+ // Given: RTL mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to shelf's width - actual width
+ assertEquals(60f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_nonSplitShade_RTL() {
+ // Given: LTR mode, non split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's width
+ assertEquals(100f, shelfSpy.shelfRightBound)
+ }
+
+ private fun prepareShelfSpy(
+ shelf: NotificationShelf,
+ rtl: Boolean,
+ splitShade: Boolean,
+ width: Int,
+ actualWidth: Int,
+ ): NotificationShelf {
+ val shelfSpy = spy(shelf)
+ whenever(shelfSpy.isLayoutRtl).thenReturn(rtl)
+ whenever(ambientState.useSplitShade).thenReturn(splitShade)
+ whenever(shelfSpy.width).thenReturn(width)
+ shelfSpy.setActualWidth(actualWidth.toFloat())
+ return shelfSpy
+ }
+
+ @Test
fun getAmountInShelf_lastViewBelowShelf_completelyInShelf() {
val shelfClipStart = 0f
val viewStart = 1f
@@ -152,7 +326,7 @@ open class NotificationShelfTest : SysuiTestCase() {
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(1f, amountInShelf)
}
@@ -182,7 +356,7 @@ open class NotificationShelfTest : SysuiTestCase() {
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(1f, amountInShelf)
}
@@ -212,7 +386,7 @@ open class NotificationShelfTest : SysuiTestCase() {
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(0.5f, amountInShelf)
}
@@ -241,7 +415,7 @@ open class NotificationShelfTest : SysuiTestCase() {
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(0f, amountInShelf)
}
@@ -250,7 +424,7 @@ open class NotificationShelfTest : SysuiTestCase() {
fun updateState_expansionChanging_shelfTransparent() {
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.25f,
- expectedAlpha = 0.0f
+ expectedAlpha = 0.0f,
)
}
@@ -260,7 +434,7 @@ open class NotificationShelfTest : SysuiTestCase() {
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.85f,
- expectedAlpha = 0.0f
+ expectedAlpha = 0.0f,
)
}
@@ -281,7 +455,7 @@ open class NotificationShelfTest : SysuiTestCase() {
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = expansionFraction,
- expectedAlpha = 0.123f
+ expectedAlpha = 0.123f,
)
}
@@ -330,7 +504,7 @@ open class NotificationShelfTest : SysuiTestCase() {
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(1f, amountInShelf)
}
@@ -628,7 +802,7 @@ open class NotificationShelfTest : SysuiTestCase() {
private fun updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction: Float,
- expectedAlpha: Float
+ expectedAlpha: Float,
) {
val sbnMock: StatusBarNotification = mock()
val mockEntry = mock<NotificationEntry>().apply { whenever(this.sbn).thenReturn(sbnMock) }
diff --git a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
index 58c545036b27..071b07631ff9 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
@@ -24,11 +24,11 @@
android:clickable="true"
>
- <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ <com.android.systemui.statusbar.notification.shelf.NotificationShelfBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.android.systemui.statusbar.phone.NotificationIconContainer
+ <com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d523bc1867c2..48cf7a83c324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -48,6 +48,9 @@ import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
+import com.android.systemui.statusbar.notification.shelf.NotificationShelfBackgroundView;
+import com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -76,7 +79,11 @@ public class NotificationShelf extends ActivatableNotificationView {
private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
private static final SourceType SHELF_SCROLL = SourceType.from("ShelfScroll");
- private NotificationIconContainer mShelfIcons;
+ @VisibleForTesting
+ public NotificationShelfIconContainer mShelfIcons;
+ // This field hides mBackgroundNormal from super class for short-shelf alignment
+ @VisibleForTesting
+ public NotificationShelfBackgroundView mBackgroundNormal;
private boolean mHideBackground;
private int mStatusBarHeight;
private boolean mEnableNotificationClipping;
@@ -116,6 +123,8 @@ public class NotificationShelf extends ActivatableNotificationView {
mShelfIcons.setClipChildren(false);
mShelfIcons.setClipToPadding(false);
+ mBackgroundNormal = (NotificationShelfBackgroundView) super.mBackgroundNormal;
+
setClipToActualHeight(false);
setClipChildren(false);
setClipToPadding(false);
@@ -268,19 +277,37 @@ public class NotificationShelf extends ActivatableNotificationView {
}
}
- private void setActualWidth(float actualWidth) {
+ /**
+ * Set the actual width of the shelf, this will only differ from width for short shelves.
+ */
+ @VisibleForTesting
+ public void setActualWidth(float actualWidth) {
setBackgroundWidth((int) actualWidth);
if (mShelfIcons != null) {
+ mShelfIcons.setAlignToEnd(isAlignedToEnd());
mShelfIcons.setActualLayoutWidth((int) actualWidth);
}
mActualWidth = actualWidth;
}
@Override
+ public void setBackgroundWidth(int width) {
+ super.setBackgroundWidth(width);
+ if (!NotificationMinimalism.isEnabled()) {
+ return;
+ }
+ if (mBackgroundNormal != null) {
+ mBackgroundNormal.setAlignToEnd(isAlignedToEnd());
+ }
+ }
+
+ @Override
public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
super.getBoundsOnScreen(outRect, clipToParent);
final int actualWidth = getActualWidth();
- if (isLayoutRtl()) {
+ final boolean alignedToRight = NotificationMinimalism.isEnabled() ? isAlignedToRight() :
+ isLayoutRtl();
+ if (alignedToRight) {
outRect.left = outRect.right - actualWidth;
} else {
outRect.right = outRect.left + actualWidth;
@@ -326,11 +353,17 @@ public class NotificationShelf extends ActivatableNotificationView {
*/
@Override
public boolean pointInView(float localX, float localY, float slop) {
- final float containerWidth = getWidth();
- final float shelfWidth = getActualWidth();
+ final float left, right;
- final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
- final float right = isLayoutRtl() ? containerWidth : shelfWidth;
+ if (NotificationMinimalism.isEnabled()) {
+ left = getShelfLeftBound();
+ right = getShelfRightBound();
+ } else {
+ final float containerWidth = getWidth();
+ final float shelfWidth = getActualWidth();
+ left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
+ right = isLayoutRtl() ? containerWidth : shelfWidth;
+ }
final float top = mClipTopAmount;
final float bottom = getActualHeight();
@@ -339,10 +372,53 @@ public class NotificationShelf extends ActivatableNotificationView {
&& isYInView(localY, slop, top, bottom);
}
+ /**
+ * @return The left boundary of the shelf.
+ */
+ @VisibleForTesting
+ public float getShelfLeftBound() {
+ if (isAlignedToRight()) {
+ return getWidth() - getActualWidth();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @return The right boundary of the shelf.
+ */
+ @VisibleForTesting
+ public float getShelfRightBound() {
+ if (isAlignedToRight()) {
+ return getWidth();
+ } else {
+ return getActualWidth();
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isAlignedToRight() {
+ return isAlignedToEnd() ^ isLayoutRtl();
+ }
+
+ /**
+ * When notification minimalism is on, on split shade, we want the notification shelf to align
+ * to the layout end (right for LTR; left for RTL).
+ * @return whether to align with the minimalism split shade style
+ */
+ @VisibleForTesting
+ public boolean isAlignedToEnd() {
+ if (!NotificationMinimalism.isEnabled()) {
+ return false;
+ }
+ return mAmbientState.getUseSplitShade();
+ }
+
@Override
public void updateBackgroundColors() {
super.updateBackgroundColors();
ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+
if (colorUpdateLogger != null) {
colorUpdateLogger.logEvent("Shelf.updateBackgroundColors()",
"normalBgColor=" + hexColorString(getNormalBgColor())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index e440d2728263..dd3a9c9dcf21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -169,12 +169,12 @@ public class NotificationBackgroundView extends View implements Dumpable,
&& !mExpandAnimationRunning) {
bottom -= mClipBottomAmount;
}
- final boolean isRtl = isLayoutRtl();
+ final boolean alignedToRight = isAlignedToRight();
final int width = getWidth();
final int actualWidth = getActualWidth();
- int left = isRtl ? width - actualWidth : 0;
- int right = isRtl ? width : actualWidth;
+ int left = alignedToRight ? width - actualWidth : 0;
+ int right = alignedToRight ? width : actualWidth;
if (mExpandAnimationRunning) {
// Horizontally center this background view inside of the container
@@ -185,6 +185,15 @@ public class NotificationBackgroundView extends View implements Dumpable,
return new Rect(left, top, right, bottom);
}
+ /**
+ * @return Whether the background view should be right-aligned. This only matters if the
+ * actualWidth is different than the full (measured) width. In other words, this is used to
+ * define the short-shelf alignment.
+ */
+ protected boolean isAlignedToRight() {
+ return isLayoutRtl();
+ }
+
private void draw(Canvas canvas, Drawable drawable) {
NotificationAddXOnHoverToDismiss.assertInLegacyMode();
@@ -196,12 +205,13 @@ public class NotificationBackgroundView extends View implements Dumpable,
&& !mExpandAnimationRunning) {
bottom -= mClipBottomAmount;
}
- final boolean isRtl = isLayoutRtl();
+
+ final boolean alignedToRight = isAlignedToRight();
final int width = getWidth();
final int actualWidth = getActualWidth();
- int left = isRtl ? width - actualWidth : 0;
- int right = isRtl ? width : actualWidth;
+ int left = alignedToRight ? width - actualWidth : 0;
+ int right = alignedToRight ? width : actualWidth;
if (mExpandAnimationRunning) {
// Horizontally center this background view inside of the container
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.kt
new file mode 100644
index 000000000000..d7eea0190e2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.shelf
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
+
+/** The background view for the NotificationShelf. */
+class NotificationShelfBackgroundView
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) :
+ NotificationBackgroundView(context, attrs) {
+
+ /** Whether the notification shelf is aligned to end, need to keep persistent with the shelf. */
+ var alignToEnd = false
+
+ /** @return whether the alignment of the notification shelf is right. */
+ @VisibleForTesting
+ public override fun isAlignedToRight(): Boolean {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.isAlignedToRight()
+ }
+ return alignToEnd xor isLayoutRtl
+ }
+
+ override fun toDumpString(): String {
+ return super.toDumpString() + " alignToEnd=" + alignToEnd
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
new file mode 100644
index 000000000000..64d165402759
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.shelf
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import kotlin.math.max
+
+/** The NotificationIconContainer for the NotificationShelf. */
+class NotificationShelfIconContainer
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) :
+ NotificationIconContainer(context, attrs) {
+
+ /** Whether the notification shelf is aligned to end. */
+ var alignToEnd = false
+
+ /**
+ * @return The left boundary (not the RTL compatible start) of the area that icons can be added.
+ */
+ override fun getLeftBound(): Float {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.getLeftBound()
+ }
+
+ if (isAlignedToRight) {
+ return (max(width - actualWidth, 0) + actualPaddingStart)
+ }
+ return actualPaddingStart
+ }
+
+ /**
+ * @return The right boundary (not the RTL compatible end) of the area that icons can be added.
+ */
+ override fun getRightBound(): Float {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.getRightBound()
+ }
+
+ if (isAlignedToRight) {
+ return width - actualPaddingEnd
+ }
+ return actualWidth - actualPaddingEnd
+ }
+
+ /**
+ * For RTL, the icons' x positions should be mirrored around the middle of the shelf so that the
+ * icons are also added to the shelf from right to left. This function should only be called
+ * when RTL.
+ */
+ override fun getRtlIconTranslationX(iconState: IconState, iconView: View): Float {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.getRtlIconTranslationX(iconState, iconView)
+ }
+
+ if (!isLayoutRtl) {
+ return iconState.xTranslation
+ }
+
+ if (isAlignedToRight) {
+ return width * 2 - actualWidth - iconState.xTranslation - iconView.width
+ }
+ return actualWidth - iconState.xTranslation - iconView.width
+ }
+
+ private val isAlignedToRight: Boolean
+ get() {
+ if (!NotificationMinimalism.isEnabled) {
+ return isLayoutRtl
+ }
+ return alignToEnd xor isLayoutRtl
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index ecd62bd6943b..c396512ce3a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -198,7 +198,7 @@ public class NotificationIconContainer extends ViewGroup {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
- canvas.drawRect(getActualPaddingStart(), 0, getLayoutEnd(), getHeight(), paint);
+ canvas.drawRect(getActualPaddingStart(), 0, getRightBound(), getHeight(), paint);
if (DEBUG_OVERFLOW) {
if (mLastVisibleIconState == null) {
@@ -469,11 +469,11 @@ public class NotificationIconContainer extends ViewGroup {
* If this is not a whole number, the fraction means by how much the icon is appearing.
*/
public void calculateIconXTranslations() {
- float translationX = getActualPaddingStart();
+ float translationX = getLeftBound();
int firstOverflowIndex = -1;
int childCount = getChildCount();
int maxVisibleIcons = mMaxIcons;
- float layoutEnd = getLayoutEnd();
+ float layoutRight = getRightBound();
mVisualOverflowStart = 0;
mFirstVisibleIconState = null;
for (int i = 0; i < childCount; i++) {
@@ -495,7 +495,7 @@ public class NotificationIconContainer extends ViewGroup {
final boolean forceOverflow = shouldForceOverflow(i, mSpeedBumpIndex,
iconState.iconAppearAmount, maxVisibleIcons);
final boolean isOverflowing = forceOverflow || isOverflowing(
- /* isLastChild= */ i == childCount - 1, translationX, layoutEnd, mIconSize);
+ /* isLastChild= */ i == childCount - 1, translationX, layoutRight, mIconSize);
// First icon to overflow.
if (firstOverflowIndex == -1 && isOverflowing) {
@@ -536,8 +536,7 @@ public class NotificationIconContainer extends ViewGroup {
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
IconState iconState = mIconStates.get(view);
- iconState.setXTranslation(
- getWidth() - iconState.getXTranslation() - view.getWidth());
+ iconState.setXTranslation(getRtlIconTranslationX(iconState, view));
}
}
if (mIsolatedIcon != null) {
@@ -553,6 +552,11 @@ public class NotificationIconContainer extends ViewGroup {
}
}
+ /** We need this to keep icons ordered from right to left when RTL. */
+ protected float getRtlIconTranslationX(IconState iconState, View iconView) {
+ return getWidth() - iconState.getXTranslation() - iconView.getWidth();
+ }
+
private float getDrawingScale(View view) {
return mUseIncreasedIconScale && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
@@ -563,11 +567,21 @@ public class NotificationIconContainer extends ViewGroup {
mUseIncreasedIconScale = useIncreasedIconScale;
}
- private float getLayoutEnd() {
+ /**
+ * @return The right boundary (not the RTL compatible end) of the area that icons can be added.
+ */
+ protected float getRightBound() {
return getActualWidth() - getActualPaddingEnd();
}
- private float getActualPaddingEnd() {
+ /**
+ * @return The left boundary (not the RTL compatible start) of the area that icons can be added.
+ */
+ protected float getLeftBound() {
+ return getActualPaddingStart();
+ }
+
+ protected float getActualPaddingEnd() {
if (mActualPaddingEnd == NO_VALUE) {
return getPaddingEnd();
}