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

Commit d8f17fd7 authored by Darrell Shi's avatar Darrell Shi Committed by Android (Google) Code Review
Browse files

Merge "Log metrics for adding and removing widgets" into main

parents f1516fa7 f6a6e121
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ internal constructor(
    communalContent: List<CommunalContentModel>,
    private val onAddWidget:
        (componentName: ComponentName, user: UserHandle, priority: Int) -> Unit,
    private val onDeleteWidget: (id: Int) -> Unit,
    private val onDeleteWidget: (id: Int, componentName: ComponentName, priority: Int) -> Unit,
    private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit,
) {
    var list = communalContent.toMutableStateList()
@@ -74,7 +74,7 @@ internal constructor(
        if (list[indexToRemove].isWidgetContent()) {
            val widget = list[indexToRemove] as CommunalContentModel.WidgetContent
            list.apply { removeAt(indexToRemove) }
            onDeleteWidget(widget.appWidgetId)
            onDeleteWidget(widget.appWidgetId, widget.componentName, widget.priority)
        }
    }

+2 −2
Original line number Diff line number Diff line
@@ -696,7 +696,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                    CommunalWidgetContentModel.Pending(
                        appWidgetId = 2,
                        priority = 2,
                        packageName = "pk_2",
                        componentName = ComponentName("pk_2", "cls_2"),
                        icon = fakeIcon,
                        user = mainUser,
                    ),
@@ -731,7 +731,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                    CommunalWidgetContentModel.Pending(
                        appWidgetId = 1,
                        priority = 1,
                        packageName = "pk_1",
                        componentName = ComponentName("pk_1", "cls_1"),
                        icon = fakeIcon,
                        user = mainUser,
                    ),
+19 −73
Original line number Diff line number Diff line
@@ -19,10 +19,8 @@ package com.android.systemui.communal.domain.interactor

import android.app.admin.DevicePolicyManager
import android.app.admin.devicePolicyManager
import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.os.UserHandle
import android.os.UserManager
import android.os.userManager
@@ -52,7 +50,6 @@ import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
@@ -251,18 +248,16 @@ class CommunalInteractorTest : SysuiTestCase() {
            runCurrent()

            // Widgets available.
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)

            val widgetContent by collectLastValue(underTest.widgetContent)

            assertThat(widgetContent!!).isNotEmpty()
            widgetContent!!.forEachIndexed { index, model ->
                assertThat(model.appWidgetId).isEqualTo(widgets[index].appWidgetId)
            }
            assertThat(checkNotNull(widgetContent)).isNotEmpty()
            assertThat(widgetContent!![0].appWidgetId).isEqualTo(1)
            assertThat(widgetContent!![1].appWidgetId).isEqualTo(2)
            assertThat(widgetContent!![2].appWidgetId).isEqualTo(3)
        }

    @Test
@@ -839,11 +834,9 @@ class CommunalInteractorTest : SysuiTestCase() {

            val widgetContent by collectLastValue(underTest.widgetContent)
            // Given three widgets, and one of them is associated with pre-existing work profile.
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)

            // One widget is filtered out and the remaining two link to main user id.
            assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
@@ -882,11 +875,9 @@ class CommunalInteractorTest : SysuiTestCase() {
            whenever(userManager.isManagedProfile(eq(USER_INFO_WORK.id))).thenReturn(true)

            val widgetContent by collectLastValue(underTest.widgetContent)
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)

            // The work profile widget is in quiet mode, while other widgets are not.
            assertThat(widgetContent).hasSize(3)
@@ -927,11 +918,9 @@ class CommunalInteractorTest : SysuiTestCase() {

            val widgetContent by collectLastValue(underTest.widgetContent)
            // One available work widget, one pending work widget, and one regular available widget.
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createPendingWidgetForUser(2, userId = USER_INFO_WORK.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
            widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)

            setKeyguardFeaturesDisabled(
                USER_INFO_WORK,
@@ -962,11 +951,9 @@ class CommunalInteractorTest : SysuiTestCase() {

            val widgetContent by collectLastValue(underTest.widgetContent)
            // Given three widgets, and one of them is associated with work profile.
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createPendingWidgetForUser(2, userId = USER_INFO_WORK.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
            widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)

            setKeyguardFeaturesDisabled(
                USER_INFO_WORK,
@@ -1088,47 +1075,6 @@ class CommunalInteractorTest : SysuiTestCase() {
        )
    }

    private fun createWidgetForUser(
        appWidgetId: Int,
        userId: Int
    ): CommunalWidgetContentModel.Available =
        mock<CommunalWidgetContentModel.Available> {
            whenever(this.appWidgetId).thenReturn(appWidgetId)
            val providerInfo =
                mock<AppWidgetProviderInfo>().apply {
                    widgetCategory = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
                }
            whenever(providerInfo.profile).thenReturn(UserHandle(userId))
            whenever(this.providerInfo).thenReturn(providerInfo)
        }

    private fun createPendingWidgetForUser(
        appWidgetId: Int,
        priority: Int = 0,
        packageName: String = "",
        icon: Bitmap? = null,
        userId: Int = 0,
    ): CommunalWidgetContentModel.Pending {
        return CommunalWidgetContentModel.Pending(
            appWidgetId = appWidgetId,
            priority = priority,
            packageName = packageName,
            icon = icon,
            user = UserHandle(userId),
        )
    }

    private fun createWidgetWithCategory(
        appWidgetId: Int,
        category: Int
    ): CommunalWidgetContentModel =
        mock<CommunalWidgetContentModel.Available> {
            whenever(this.appWidgetId).thenReturn(appWidgetId)
            val providerInfo = mock<AppWidgetProviderInfo>().apply { widgetCategory = category }
            whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
            whenever(this.providerInfo).thenReturn(providerInfo)
        }

    private companion object {
        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
        val USER_INFO_WORK =
+93 −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.communal.log

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.log.CommunalMetricsLogger
import com.android.systemui.shared.system.SysUiStatsLog
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalMetricsLoggerTest : SysuiTestCase() {
    private val statsLogProxy = mock<CommunalMetricsLogger.StatsLogProxy>()

    private val loggablePrefixes = listOf("com.blue.", "com.red.")
    private lateinit var underTest: CommunalMetricsLogger

    @Before
    fun setUp() {
        underTest = CommunalMetricsLogger(loggablePrefixes, statsLogProxy)
    }

    @Test
    fun logAddWidget_componentNotLoggable_doNotLog() {
        underTest.logAddWidget(
            componentName = "com.green.package/my_test_widget",
            rank = 1,
        )
        verify(statsLogProxy, never())
            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
    }

    @Test
    fun logAddWidget_componentLoggable_logAddEvent() {
        underTest.logAddWidget(
            componentName = "com.blue.package/my_test_widget",
            rank = 1,
        )
        verify(statsLogProxy)
            .writeCommunalHubWidgetEventReported(
                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
                "com.blue.package/my_test_widget",
                1,
            )
    }

    @Test
    fun logRemoveWidget_componentNotLoggable_doNotLog() {
        underTest.logRemoveWidget(
            componentName = "com.yellow.package/my_test_widget",
            rank = 2,
        )
        verify(statsLogProxy, never())
            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
    }

    @Test
    fun logRemoveWidget_componentLoggable_logRemoveEvent() {
        underTest.logRemoveWidget(
            componentName = "com.red.package/my_test_widget",
            rank = 2,
        )
        verify(statsLogProxy)
            .writeCommunalHubWidgetEventReported(
                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
                "com.red.package/my_test_widget",
                2,
            )
    }
}
+14 −35
Original line number Diff line number Diff line
@@ -16,14 +16,13 @@

package com.android.systemui.communal.view.viewmodel

import android.appwidget.AppWidgetProviderInfo
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.UserInfo
import android.os.UserHandle
import android.provider.Settings
import android.widget.RemoteViews
import androidx.activity.result.ActivityResultLauncher
@@ -47,8 +46,8 @@ import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalMetricsLogger
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
@@ -86,9 +85,9 @@ import org.mockito.kotlin.spy
class CommunalEditModeViewModelTest : SysuiTestCase() {
    @Mock private lateinit var mediaHost: MediaHost
    @Mock private lateinit var uiEventLogger: UiEventLogger
    @Mock private lateinit var providerInfo: AppWidgetProviderInfo
    @Mock private lateinit var packageManager: PackageManager
    @Mock private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
    @Mock private lateinit var metricsLogger: CommunalMetricsLogger

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
@@ -120,7 +119,6 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            selectedUserIndex = 0,
        )
        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
        whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))

        underTest =
            CommunalEditModeViewModel(
@@ -133,6 +131,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
                logcatLogBuffer("CommunalEditModeViewModelTest"),
                kosmos.testDispatcher,
                kosmos.communalPrefsInteractor,
                metricsLogger,
            )
    }

@@ -142,20 +141,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)

            // Widgets available.
            val widgets =
                listOf(
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 0,
                        priority = 30,
                        providerInfo = providerInfo,
                    ),
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 1,
                        priority = 20,
                        providerInfo = providerInfo,
                    ),
                )
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
            widgetRepository.addWidget(appWidgetId = 1, priority = 20)

            // Smartspace available.
            smartspaceRepository.setTimers(
@@ -216,20 +203,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)

            // Widgets available.
            val widgets =
                listOf(
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 0,
                        priority = 30,
                        providerInfo = providerInfo,
                    ),
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 1,
                        priority = 20,
                        providerInfo = providerInfo,
                    ),
                )
            widgetRepository.setCommunalWidgets(widgets)
            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
            widgetRepository.addWidget(appWidgetId = 1, priority = 20)

            val communalContent by collectLastValue(underTest.communalContent)

@@ -240,14 +215,18 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            assertThat(communalContent?.get(1))
                .isInstanceOf(CommunalContentModel.WidgetContent::class.java)

            underTest.onDeleteWidget(widgets.get(0).appWidgetId)
            underTest.onDeleteWidget(
                id = 0,
                componentName = ComponentName("test_package", "test_class"),
                priority = 30,
            )

            // Only one widget and CTA tile remain.
            assertThat(communalContent?.size).isEqualTo(1)
            val item = communalContent?.get(0)
            val appWidgetId =
                if (item is CommunalContentModel.WidgetContent) item.appWidgetId else null
            assertThat(appWidgetId).isEqualTo(widgets.get(1).appWidgetId)
            assertThat(appWidgetId).isEqualTo(1)
        }

    @Test
Loading