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

Commit 2c6b945c authored by Yuri Lin's avatar Yuri Lin
Browse files

Add uievent IDs for QS modes dialog interactions & log them

Adds ModesDialogEventLogger to log these enums and injects it into the ModesDialogViewModel and ModesDialogDelegate to log the operations they each control.

Eldar for new UiEvent IDs: https://eldar.corp.google.com/assessments/756078032/drafts/343683331/sections/review

Bug: 356371233
Test: statsd_testdrive to verify logging, ModesDialogEventLoggerTest, ModesDialogDelegateTest, ModesDialogViewModelTest
Flag: android.app.modes_ui
Change-Id: Id8ef339d0cab29e709c94ff86412d3d6b5f21a85
parent dd0a5739
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