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

Commit e4e5ddd8 authored by Andreas Miko's avatar Andreas Miko
Browse files

Add Bundle guts done/apply and switch logic

Bug: 409748420
Test: Unit tests and screenshot tests added
Flag: com.android.systemui.notification_bundle_ui
Change-Id: Ibd44d6ea193283af7ad2cba77b30a524f1833df6
parent 6b09a7a6
Loading
Loading
Loading
Loading
+5 −13
Original line number Diff line number Diff line
@@ -35,10 +35,6 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
@@ -138,12 +134,9 @@ private fun ContentRow(viewModel: BundleHeaderGutsViewModel, modifier: Modifier
            )
        }

        var checked by remember { mutableStateOf(true) }

        Switch(
            checked = checked,
            // TODO(b/409748420): Implement proper checked logic
            onCheckedChange = { checked = !checked },
            checked = viewModel.switchState,
            onCheckedChange = { viewModel.switchState = !viewModel.switchState },
            thumbContent = {
                Icon(
                    // TODO(b/409748420): Add correct icon
@@ -172,7 +165,7 @@ private fun BottomRow(viewModel: BundleHeaderGutsViewModel, modifier: Modifier =
                modifier
                    .padding(vertical = 13.dp)
                    .clickable(
                        onClick = viewModel.onDoneClicked,
                        onClick = viewModel.onDismissClicked,
                        indication = null,
                        interactionSource = null,
                    ),
@@ -180,16 +173,15 @@ private fun BottomRow(viewModel: BundleHeaderGutsViewModel, modifier: Modifier =

        Spacer(modifier = Modifier.weight(1f))

        // TODO(b/409748420): Implement done/apply switch
        Text(
            text = stringResource(R.string.done_label),
            text = stringResource(viewModel.getDoneOrApplyButtonText()),
            style = MaterialTheme.typography.titleSmallEmphasized,
            color = MaterialTheme.colorScheme.primary,
            modifier =
                modifier
                    .padding(vertical = 13.dp)
                    .clickable(
                        onClick = viewModel.onDismissClicked,
                        onClick = { viewModel.onDoneOrApplyClicked() },
                        indication = null,
                        interactionSource = null,
                    ),
+0 −1
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementColorAsState
import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.ui.graphics.painter.rememberDrawablePainter

+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.row.ui.viewmodel

import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.row.data.repository.TEST_BUNDLE_SPEC
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(NotificationBundleUi.FLAG_NAME)
class BundleHeaderGutsViewModelTest : SysuiTestCase() {

    @get:Rule val rule: MockitoRule = MockitoJUnit.rule()

    @Mock private lateinit var mockDisableBundle: () -> Unit

    @Mock private lateinit var mockCloseGuts: () -> Unit

    @Mock private lateinit var mockOnDismissClicked: () -> Unit

    @Mock private lateinit var mockOnSettingsClicked: () -> Unit

    private lateinit var underTest: BundleHeaderGutsViewModel

    @Before
    fun setUp() {
        MockitoAnnotations.openMocks(this)

        underTest =
            BundleHeaderGutsViewModel(
                titleText = TEST_BUNDLE_SPEC.titleText,
                summaryText = TEST_BUNDLE_SPEC.summaryText,
                bundleIcon = TEST_BUNDLE_SPEC.icon,
                disableBundle = mockDisableBundle,
                closeGuts = mockCloseGuts,
                onDismissClicked = mockOnDismissClicked,
                onSettingsClicked = mockOnSettingsClicked,
            )
    }

    @Test
    fun switchState_false_onDoneOrApplyClicked() {
        // Arrange
        underTest.switchState = false

        // Act
        underTest.onDoneOrApplyClicked()

        // Assert
        verify(mockDisableBundle).invoke()
        verify(mockOnDismissClicked).invoke()
        verify(mockCloseGuts, never()).invoke()
    }

    @Test
    fun switchState_true_onDoneOrApplyClicked() {
        // Arrange
        underTest.switchState = true

        // Act
        underTest.onDoneOrApplyClicked()

        // Assert
        verify(mockCloseGuts).invoke()
        verify(mockDisableBundle, never()).invoke()
        verify(mockOnDismissClicked, never()).invoke()
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -31,7 +31,13 @@ class BundleEntry(spec: BundleSpec) : PipelineEntry(spec.key) {
    override val bucket: Int = spec.bucket

    /** The model used by UI. */
    val bundleRepository = BundleRepository(spec.titleText, spec.icon, spec.summaryText)
    val bundleRepository =
        BundleRepository(
            titleText = spec.titleText,
            bundleIcon = spec.icon,
            summaryText = spec.summaryText,
            bundleType = spec.bundleType,
        )

    // TODO(b/394483200): move NotificationEntry's implementation to PipelineEntry?
    val isSensitive: MutableStateFlow<Boolean> = MutableStateFlow(false)
+11 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection

import android.app.NotificationChannel
import android.service.notification.Adjustment
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.android.internal.R
@@ -32,6 +33,12 @@ data class BundleSpec(
    @StringRes val summaryText: Int,
    @DrawableRes val icon: Int,
    @PriorityBucket val bucket: Int,

    /**
     * This is the id / [type] that identifies the bundle when calling APIs of
     * [android.app.INotificationManager]
     */
    @Adjustment.Types val bundleType: Int,
) {
    companion object {
        val PROMOTIONS =
@@ -42,6 +49,7 @@ data class BundleSpec(
                    com.android.systemui.res.R.string.notification_guts_promotions_summary,
                icon = com.android.settingslib.R.drawable.ic_promotions,
                bucket = BUCKET_PROMO,
                bundleType = Adjustment.TYPE_PROMOTION,
            )
        val SOCIAL_MEDIA =
            BundleSpec(
@@ -50,6 +58,7 @@ data class BundleSpec(
                summaryText = com.android.systemui.res.R.string.notification_guts_social_summary,
                icon = com.android.settingslib.R.drawable.ic_social,
                bucket = BUCKET_SOCIAL,
                bundleType = Adjustment.TYPE_SOCIAL_MEDIA,
            )
        val NEWS =
            BundleSpec(
@@ -58,6 +67,7 @@ data class BundleSpec(
                summaryText = com.android.systemui.res.R.string.notification_guts_news_summary,
                icon = com.android.settingslib.R.drawable.ic_news,
                bucket = BUCKET_NEWS,
                bundleType = Adjustment.TYPE_NEWS,
            )
        val RECOMMENDED =
            BundleSpec(
@@ -66,6 +76,7 @@ data class BundleSpec(
                summaryText = com.android.systemui.res.R.string.notification_guts_recs_summary,
                icon = com.android.settingslib.R.drawable.ic_recs,
                bucket = BUCKET_RECS,
                bundleType = Adjustment.TYPE_CONTENT_RECOMMENDATION,
            )
    }
}
Loading