Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba706d4d authored by Yining Liu's avatar Yining Liu Committed by Android (Google) Code Review
Browse files

Merge changes I5935fdb0,I7129529e into main

* changes:
  Add unit tests for End-aligning short shelf on split shade when minimalism enabled
  Right-align short shelf for split shade when minimalism enabled
parents 95495bd7 b6239bcb
Loading
Loading
Loading
Loading
+184 −10
Original line number Diff line number Diff line
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)
@@ -127,6 +130,177 @@ open class NotificationShelfTest : SysuiTestCase() {
        assertFalse(isYBelowShelfInView)
    }

    @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
@@ -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) }
+2 −2
Original line number Diff line number Diff line
@@ -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"
+83 −7
Original line number Diff line number Diff line
@@ -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 left, right;

        if (NotificationMinimalism.isEnabled()) {
            left = getShelfLeftBound();
            right = getShelfRightBound();
        } else {
            final float containerWidth = getWidth();
            final float shelfWidth = getActualWidth();

        final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
        final float right = isLayoutRtl() ? containerWidth : shelfWidth;
            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())
+16 −6
Original line number Diff line number Diff line
@@ -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
+46 −0
Original line number Diff line number Diff line
/*
 * 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
    }
}
Loading