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

Commit 45ebc209 authored by Yuri Lin's avatar Yuri Lin Committed by Android (Google) Code Review
Browse files

Merge "Add uievent IDs for QS modes dialog interactions & log them" into main

parents 6b1a31b4 2c6b945c
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenModeConfig.tryParseCountdownConditionId;
import static android.service.notification.ZenModeConfig.tryParseEventConditionId;
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
@@ -129,7 +130,11 @@ public class ZenMode implements Parcelable {
    }

    public static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
        return new ZenMode(MANUAL_DND_MODE_ID, manualRule,
        // Manual rule is owned by the system, so we set it here
        AutomaticZenRule manualRuleWithPkg = new AutomaticZenRule.Builder(manualRule)
                .setPackage(PACKAGE_ANDROID)
                .build();
        return new ZenMode(MANUAL_DND_MODE_ID, manualRuleWithPkg,
                isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED, true);
    }

+2 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settingslib.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -67,6 +68,7 @@ public class ZenModeTest {
        assertThat(manualMode.canEditNameAndIcon()).isFalse();
        assertThat(manualMode.canBeDeleted()).isFalse();
        assertThat(manualMode.isActive()).isFalse();
        assertThat(manualMode.getRule().getPackageName()).isEqualTo(PACKAGE_ANDROID);
    }

    @Test
+91 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.settingslib.notification.modes.ZenMode
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
@@ -30,6 +31,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,6 +42,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.times
import org.mockito.kotlin.verify

@SmallTest
@@ -50,9 +53,16 @@ class ModesDialogViewModelTest : SysuiTestCase() {
    private val repository = kosmos.fakeZenModeRepository
    private val interactor = kosmos.zenModeInteractor
    private val mockDialogDelegate = kosmos.mockModesDialogDelegate
    private val mockDialogEventLogger = kosmos.mockModesDialogEventLogger

    private val underTest =
        ModesDialogViewModel(context, interactor, kosmos.testDispatcher, mockDialogDelegate)
        ModesDialogViewModel(
            context,
            interactor,
            kosmos.testDispatcher,
            mockDialogDelegate,
            mockDialogEventLogger
        )

    @Test
    fun tiles_filtersOutUserDisabledModes() =
@@ -432,4 +442,84 @@ class ModesDialogViewModelTest : SysuiTestCase() {
            assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
                .isEqualTo("B")
        }

    @Test
    fun onClick_logsOnOffEvents() =
        testScope.runTest {
            val tiles by collectLastValue(underTest.tiles)

            repository.addModes(
                listOf(
                    TestModeBuilder.MANUAL_DND_ACTIVE,
                    TestModeBuilder()
                        .setId("id1")
                        .setName("Inactive Mode One")
                        .setActive(false)
                        .setManualInvocationAllowed(true)
                        .build(),
                    TestModeBuilder()
                        .setId("id2")
                        .setName("Active Non-Invokable Mode Two") // but can be turned off by tile
                        .setActive(true)
                        .setManualInvocationAllowed(false)
                        .build(),
                )
            )
            runCurrent()

            assertThat(tiles?.size).isEqualTo(3)

            // Trigger onClick for each tile in sequence
            tiles?.forEach { it.onClick.invoke() }
            runCurrent()

            val onModeCaptor = argumentCaptor<ZenMode>()
            val offModeCaptor = argumentCaptor<ZenMode>()

            // manual mode and mode 2 should have turned off
            verify(mockDialogEventLogger, times(2)).logModeOff(offModeCaptor.capture())
            val off0 = offModeCaptor.firstValue
            assertThat(off0.isManualDnd).isTrue()

            val off1 = offModeCaptor.secondValue
            assertThat(off1.id).isEqualTo("id2")

            // should also have logged turning mode 1 on
            verify(mockDialogEventLogger).logModeOn(onModeCaptor.capture())
            val on = onModeCaptor.lastValue
            assertThat(on.id).isEqualTo("id1")
        }

    @Test
    fun onLongClick_logsSettingsEvents() =
        testScope.runTest {
            val tiles by collectLastValue(underTest.tiles)

            repository.addModes(
                listOf(
                    TestModeBuilder.MANUAL_DND_ACTIVE,
                    TestModeBuilder()
                        .setId("id1")
                        .setName("Inactive Mode One")
                        .setActive(false)
                        .setManualInvocationAllowed(true)
                        .build(),
                )
            )
            runCurrent()

            assertThat(tiles?.size).isEqualTo(2)
            val modeCaptor = argumentCaptor<ZenMode>()

            // long click manual DND and then automatic mode
            tiles?.forEach { it.onLongClick.invoke() }
            runCurrent()

            verify(mockDialogEventLogger, times(2)).logModeSettings(modeCaptor.capture())
            val manualMode = modeCaptor.firstValue
            assertThat(manualMode.isManualDnd).isTrue()

            val automaticMode = modeCaptor.lastValue
            assertThat(automaticMode.id).isEqualTo("id1")
        }
}
+37 −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.qs

import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger

/** Events of user interactions with modes from the QS Modes dialog. {@see ModesDialogViewModel} */
enum class QSModesEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
    @UiEvent(doc = "User turned manual Do Not Disturb on via modes dialog") QS_MODES_DND_ON(1870),
    @UiEvent(doc = "User turned manual Do Not Disturb off via modes dialog") QS_MODES_DND_OFF(1871),
    @UiEvent(doc = "User opened mode settings from the Do Not Disturb tile in the modes dialog")
    QS_MODES_DND_SETTINGS(1872),
    @UiEvent(doc = "User turned automatic mode on via modes dialog") QS_MODES_MODE_ON(1873),
    @UiEvent(doc = "User turned automatic mode off via modes dialog") QS_MODES_MODE_OFF(1874),
    @UiEvent(doc = "User opened mode settings from a mode tile in the modes dialog")
    QS_MODES_MODE_SETTINGS(1875),
    @UiEvent(doc = "User clicked on Settings from the modes dialog") QS_MODES_SETTINGS(1876),
    @UiEvent(doc = "User clicked on Do Not Disturb tile, opening the time selection dialog")
    QS_MODES_DURATION_DIALOG(1879);

    override fun getId() = _id
}
+4 −1
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ constructor(
    private val activityStarter: ActivityStarter,
    // Using a provider to avoid a circular dependency.
    private val viewModel: Provider<ModesDialogViewModel>,
    private val dialogEventLogger: ModesDialogEventLogger,
    @Main private val mainCoroutineContext: CoroutineContext,
) : SystemUIDialog.Delegate {
    // NOTE: This should only be accessed/written from the main thread.
@@ -102,7 +103,9 @@ constructor(
        )
    }

    private fun openSettings(dialog: SystemUIDialog) {
    @VisibleForTesting
    fun openSettings(dialog: SystemUIDialog) {
        dialogEventLogger.logDialogSettings()
        val animationController =
            dialogTransitionAnimator.createActivityTransitionController(dialog)
        if (animationController == null) {
Loading