summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt92
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt93
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt57
16 files changed, 377 insertions, 249 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index c5dab3347e5f..38a347465318 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -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)
}
}
@@ -110,7 +110,7 @@ internal constructor(
// reorder and then add the new widget
onReorderWidgets(widgetIdToPriorityMap)
if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
- onAddWidget(newItemComponentName, newItemUser, /*priority=*/ list.size - newItemIndex)
+ onAddWidget(newItemComponentName, newItemUser, /* priority= */ list.size - newItemIndex)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index c707ebf0a2c4..ca81838bcdaa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -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,
),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 9539c0492056..0242c2d6b89d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -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 =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
new file mode 100644
index 000000000000..35df641035f7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
@@ -0,0 +1,93 @@
+/*
+ * 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,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index f8906adc33d4..61487b0a8c73 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -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
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index c480aa8e4a3d..d862a21d8e99 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -16,9 +16,7 @@
package com.android.systemui.communal.view.viewmodel
-import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
-import android.os.UserHandle
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.widget.RemoteViews
@@ -44,7 +42,6 @@ import com.android.systemui.communal.domain.interactor.communalSettingsInteracto
import com.android.systemui.communal.domain.interactor.communalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
import com.android.systemui.communal.ui.viewmodel.PopupType
@@ -110,7 +107,6 @@ import platform.test.runner.parameterized.Parameters
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
- @Mock private lateinit var providerInfo: AppWidgetProviderInfo
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -153,7 +149,6 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
userInfos = listOf(MAIN_USER_INFO),
selectedUserIndex = 0,
)
- whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
whenever(mediaHost.visible).thenReturn(true)
kosmos.powerInteractor.setAwakeForTest()
@@ -212,20 +207,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : 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(
@@ -314,15 +297,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
testScope.runTest {
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
- widgetRepository.setCommunalWidgets(
- listOf(
- CommunalWidgetContentModel.Available(
- appWidgetId = 1,
- priority = 1,
- providerInfo = providerInfo,
- )
- ),
- )
+ widgetRepository.addWidget(appWidgetId = 1, priority = 1)
mediaRepository.mediaInactive()
smartspaceRepository.setTimers(emptyList())
@@ -676,20 +651,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
)
// 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)
// Then hub shows widgets and the CTA tile
assertThat(communalContent).hasSize(3)
@@ -743,20 +706,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
)
// And 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)
// Then emits widgets and the CTA tile
assertThat(communalContent).hasSize(3)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 3d2eabf2a07c..c9f3f1453492 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,10 +16,7 @@
package com.android.systemui.communal.widgets
-import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
-import android.graphics.Bitmap
-import android.os.UserHandle
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -27,7 +24,6 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -38,7 +34,6 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
@@ -172,26 +167,23 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
// Set up communal widgets
- val widget1 =
- mock<CommunalWidgetContentModel.Available> {
- whenever(this.appWidgetId).thenReturn(1)
- }
- val widget2 =
- mock<CommunalWidgetContentModel.Available> {
- whenever(this.appWidgetId).thenReturn(2)
- }
- val widget3 =
- mock<CommunalWidgetContentModel.Available> {
- whenever(this.appWidgetId).thenReturn(3)
- }
- fakeCommunalWidgetRepository.setCommunalWidgets(listOf(widget1, widget2, widget3))
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3)
underTest.start()
// Assert communal widgets has 3
val communalWidgets by
collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
- assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+ assertThat(communalWidgets).hasSize(3)
+
+ val widget1 = communalWidgets!![0]
+ val widget2 = communalWidgets!![1]
+ val widget3 = communalWidgets!![2]
+ assertThat(widget1.appWidgetId).isEqualTo(1)
+ assertThat(widget2.appWidgetId).isEqualTo(2)
+ assertThat(widget3.appWidgetId).isEqualTo(3)
// Report app widget 1 to remove and assert widget removed
appWidgetIdToRemove.emit(1)
@@ -216,18 +208,26 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
selectedUserIndex = 0,
)
// One work widget, one pending work widget, and one personal widget.
- val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
- val widget2 = createPendingWidgetForUser(2, USER_INFO_WORK.id)
- val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
- val widgets = listOf(widget1, widget2, widget3)
- fakeCommunalWidgetRepository.setCommunalWidgets(widgets)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addPendingWidget(
+ appWidgetId = 2,
+ userId = USER_INFO_WORK.id
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
underTest.start()
runCurrent()
val communalWidgets by
collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
- assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+ assertThat(communalWidgets).hasSize(3)
+
+ val widget1 = communalWidgets!![0]
+ val widget2 = communalWidgets!![1]
+ val widget3 = communalWidgets!![2]
+ assertThat(widget1.appWidgetId).isEqualTo(1)
+ assertThat(widget2.appWidgetId).isEqualTo(2)
+ assertThat(widget3.appWidgetId).isEqualTo(3)
// Unlock the device and remove work profile.
fakeKeyguardRepository.setKeyguardShowing(false)
@@ -259,32 +259,6 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
)
}
- private fun createWidgetForUser(
- appWidgetId: Int,
- userId: Int
- ): CommunalWidgetContentModel.Available =
- mock<CommunalWidgetContentModel.Available> {
- whenever(this.appWidgetId).thenReturn(appWidgetId)
- val providerInfo = mock<AppWidgetProviderInfo>()
- whenever(providerInfo.profile).thenReturn(UserHandle(userId))
- whenever(this.providerInfo).thenReturn(providerInfo)
- }
-
- private fun createPendingWidgetForUser(
- appWidgetId: Int,
- userId: Int,
- priority: Int = 0,
- packageName: String = "",
- icon: Bitmap? = null,
- ): CommunalWidgetContentModel.Pending =
- CommunalWidgetContentModel.Pending(
- appWidgetId = appWidgetId,
- priority = priority,
- packageName = packageName,
- icon = icon,
- user = UserHandle(userId),
- )
-
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index a44533555a04..ba2b7bf96a30 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -28,6 +28,8 @@ import com.android.systemui.communal.data.repository.CommunalSmartspaceRepositor
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
+import com.android.systemui.communal.shared.log.CommunalStatsLogProxyImpl
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.communal.util.CommunalColorsImpl
@@ -44,6 +46,7 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
@Module(
@@ -74,6 +77,11 @@ interface CommunalModule {
@Binds fun bindCommunalColors(impl: CommunalColorsImpl): CommunalColors
@Binds
+ fun bindCommunalStatsLogProxy(
+ impl: CommunalStatsLogProxyImpl
+ ): CommunalMetricsLogger.StatsLogProxy
+
+ @Binds
@IntoMap
@ClassKey(CommunalSceneTransitionInteractor::class)
abstract fun bindCommunalSceneTransitionInteractor(
@@ -81,6 +89,8 @@ interface CommunalModule {
): CoreStartable
companion object {
+ const val LOGGABLE_PREFIXES = "loggable_prefixes"
+
@Provides
@Communal
@SysUISingleton
@@ -107,5 +117,14 @@ interface CommunalModule {
): CommunalBackupUtils {
return CommunalBackupUtils(context)
}
+
+ /** The prefixes of widgets packages names that are considered loggable. */
+ @Provides
+ @Named(LOGGABLE_PREFIXES)
+ fun provideLoggablePrefixes(@Application context: Context): List<String> {
+ return context.resources
+ .getStringArray(com.android.internal.R.array.config_loggable_dream_prefixes)
+ .toList()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e65e5e5688f5..ad0bfc76ebbf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -398,16 +398,13 @@ constructor(
)
}
- val session =
- installSessions.firstOrNull {
- it.packageName ==
- ComponentName.unflattenFromString(entry.componentName)?.packageName
- }
- return if (session != null) {
+ val componentName = ComponentName.unflattenFromString(entry.componentName)
+ val session = installSessions.firstOrNull { it.packageName == componentName?.packageName }
+ return if (componentName != null && session != null) {
CommunalWidgetContentModel.Pending(
appWidgetId = entry.appWidgetId,
priority = entry.priority,
- packageName = session.packageName,
+ componentName = componentName,
icon = session.icon,
user = session.user,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index e13161f91f16..846114c1e196 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -413,6 +413,7 @@ constructor(
is CommunalWidgetContentModel.Available -> {
WidgetContent.Widget(
appWidgetId = widget.appWidgetId,
+ priority = widget.priority,
providerInfo = widget.providerInfo,
appWidgetHost = appWidgetHost,
inQuietMode = isQuietModeEnabled(widget.providerInfo.profile)
@@ -421,7 +422,8 @@ constructor(
is CommunalWidgetContentModel.Pending -> {
WidgetContent.PendingWidget(
appWidgetId = widget.appWidgetId,
- packageName = widget.packageName,
+ priority = widget.priority,
+ componentName = widget.componentName,
icon = widget.icon,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 122240daed52..73c6ce30b202 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.communal.domain.model
import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
+import android.content.ComponentName
import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.widget.RemoteViews
@@ -46,14 +47,18 @@ sealed interface CommunalContentModel {
sealed interface WidgetContent : CommunalContentModel {
val appWidgetId: Int
+ val priority: Int
+ val componentName: ComponentName
data class Widget(
override val appWidgetId: Int,
+ override val priority: Int,
val providerInfo: AppWidgetProviderInfo,
val appWidgetHost: CommunalAppWidgetHost,
val inQuietMode: Boolean,
) : WidgetContent {
override val key = KEY.widget(appWidgetId)
+ override val componentName: ComponentName = providerInfo.provider
// Widget size is always half.
override val size = CommunalContentSize.HALF
@@ -66,9 +71,11 @@ sealed interface CommunalContentModel {
data class DisabledWidget(
override val appWidgetId: Int,
+ override val priority: Int,
val providerInfo: AppWidgetProviderInfo
) : WidgetContent {
override val key = KEY.disabledWidget(appWidgetId)
+ override val componentName: ComponentName = providerInfo.provider
// Widget size is always half.
override val size = CommunalContentSize.HALF
@@ -78,7 +85,8 @@ sealed interface CommunalContentModel {
data class PendingWidget(
override val appWidgetId: Int,
- val packageName: String,
+ override val priority: Int,
+ override val componentName: ComponentName,
val icon: Bitmap? = null,
) : WidgetContent {
override val key = KEY.pendingWidget(appWidgetId)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
new file mode 100644
index 000000000000..12099f708b9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.shared.log
+
+import com.android.systemui.communal.dagger.CommunalModule.Companion.LOGGABLE_PREFIXES
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+import javax.inject.Inject
+import javax.inject.Named
+
+@SysUISingleton
+class CommunalMetricsLogger
+@Inject
+constructor(
+ @Named(LOGGABLE_PREFIXES) private val loggablePrefixes: List<String>,
+ private val statsLogProxy: StatsLogProxy,
+) {
+ /** Logs an add widget event for metrics. No-op if widget is not loggable. */
+ fun logAddWidget(componentName: String, rank: Int) {
+ if (!componentName.isLoggable()) {
+ return
+ }
+
+ statsLogProxy.writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
+ componentName,
+ rank,
+ )
+ }
+
+ /** Logs a remove widget event for metrics. No-op if widget is not loggable. */
+ fun logRemoveWidget(componentName: String, rank: Int) {
+ if (!componentName.isLoggable()) {
+ return
+ }
+
+ statsLogProxy.writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
+ componentName,
+ rank,
+ )
+ }
+
+ /** Whether the component name matches any of the loggable prefixes. */
+ private fun String.isLoggable(): Boolean {
+ return loggablePrefixes.any { loggablePrefix -> startsWith(loggablePrefix) }
+ }
+
+ /** Proxy of [SysUiStatsLog] for testing purpose. */
+ interface StatsLogProxy {
+ /** Logs a [SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED] stats event. */
+ fun writeCommunalHubWidgetEventReported(
+ action: Int,
+ componentName: String,
+ rank: Int,
+ )
+ }
+}
+
+/** Redirects calls to [SysUiStatsLog]. */
+@SysUISingleton
+class CommunalStatsLogProxyImpl @Inject constructor() : CommunalMetricsLogger.StatsLogProxy {
+ override fun writeCommunalHubWidgetEventReported(
+ action: Int,
+ componentName: String,
+ rank: Int,
+ ) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED,
+ action,
+ componentName,
+ rank,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 53aecc199c4b..7cddb7226601 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.shared.model
import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
import android.graphics.Bitmap
import android.os.UserHandle
@@ -36,7 +37,7 @@ sealed interface CommunalWidgetContentModel {
data class Pending(
override val appWidgetId: Int,
override val priority: Int,
- val packageName: String,
+ val componentName: ComponentName,
val icon: Bitmap?,
val user: UserHandle,
) : CommunalWidgetContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 01ed2b71bea0..623e702a85fe 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -99,18 +99,6 @@ abstract class BaseCommunalViewModel(
communalSceneInteractor.setTransitionState(transitionState)
}
- /**
- * Called when a widget is added via drag and drop from the widget picker into the communal hub.
- */
- open fun onAddWidget(
- componentName: ComponentName,
- user: UserHandle,
- priority: Int,
- configurator: WidgetConfigurator? = null
- ) {
- communalInteractor.addWidget(componentName, user, priority, configurator)
- }
-
open fun onOpenEnableWidgetDialog() {}
open fun onOpenEnableWorkProfileDialog() {}
@@ -136,8 +124,20 @@ abstract class BaseCommunalViewModel(
/** Called as the UI request to dismiss the any displaying popup */
open fun onHidePopup() {}
+ /** Called as the UI requests adding a widget. */
+ open fun onAddWidget(
+ componentName: ComponentName,
+ user: UserHandle,
+ priority: Int,
+ configurator: WidgetConfigurator? = null,
+ ) {}
+
/** Called as the UI requests deleting a widget. */
- open fun onDeleteWidget(id: Int) {}
+ open fun onDeleteWidget(
+ id: Int,
+ componentName: ComponentName,
+ priority: Int,
+ ) {}
/**
* Called as the UI requests reordering widgets.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 0353d2c043e8..2d458b9ab1cf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -18,9 +18,11 @@ package com.android.systemui.communal.ui.viewmodel
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Resources
+import android.os.UserHandle
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import com.android.internal.logging.UiEventLogger
@@ -31,8 +33,10 @@ 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.EditModeState
+import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -72,6 +76,7 @@ constructor(
@CommunalLog logBuffer: LogBuffer,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val communalPrefsInteractor: CommunalPrefsInteractor,
+ private val metricsLogger: CommunalMetricsLogger,
) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
private val logger = Logger(logBuffer, "CommunalEditModeViewModel")
@@ -113,7 +118,24 @@ constructor(
override val reorderingWidgets: StateFlow<Boolean>
get() = _reorderingWidgets
- override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
+ override fun onAddWidget(
+ componentName: ComponentName,
+ user: UserHandle,
+ priority: Int,
+ configurator: WidgetConfigurator?
+ ) {
+ communalInteractor.addWidget(componentName, user, priority, configurator)
+ metricsLogger.logAddWidget(componentName.flattenToString(), priority)
+ }
+
+ override fun onDeleteWidget(
+ id: Int,
+ componentName: ComponentName,
+ priority: Int,
+ ) {
+ communalInteractor.deleteWidget(id)
+ metricsLogger.logRemoveWidget(componentName.flattenToString(), priority)
+ }
override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index f7ce367ebb18..c00454f3bc48 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -2,6 +2,9 @@ package com.android.systemui.communal.data.repository
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.graphics.Bitmap
import android.os.UserHandle
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -13,6 +16,8 @@ import kotlinx.coroutines.launch
/** Fake implementation of [CommunalWidgetRepository] */
class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
CommunalWidgetRepository {
+ private val fakeDatabase = mutableMapOf<Int, CommunalWidgetContentModel>()
+
private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
@@ -38,12 +43,54 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
}
}
- override fun deleteWidget(widgetId: Int) {
- if (_communalWidgets.value.none { it.appWidgetId == widgetId }) {
- return
- }
+ fun addWidget(
+ appWidgetId: Int,
+ componentName: String = "pkg/cls",
+ priority: Int = 0,
+ category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+ userId: Int = 0,
+ ) {
+ fakeDatabase[appWidgetId] =
+ CommunalWidgetContentModel.Available(
+ appWidgetId = appWidgetId,
+ priority = priority,
+ providerInfo =
+ AppWidgetProviderInfo().apply {
+ provider = ComponentName.unflattenFromString(componentName)!!
+ widgetCategory = category
+ providerInfo =
+ ActivityInfo().apply {
+ applicationInfo =
+ ApplicationInfo().apply {
+ uid = userId * UserHandle.PER_USER_RANGE
+ }
+ }
+ },
+ )
+ _communalWidgets.value = fakeDatabase.values.toList()
+ }
- _communalWidgets.value = _communalWidgets.value.filter { it.appWidgetId != widgetId }
+ fun addPendingWidget(
+ appWidgetId: Int,
+ componentName: String = "pkg/cls",
+ priority: Int = 0,
+ icon: Bitmap? = null,
+ userId: Int = 0,
+ ) {
+ fakeDatabase[appWidgetId] =
+ CommunalWidgetContentModel.Pending(
+ appWidgetId = appWidgetId,
+ priority = priority,
+ componentName = ComponentName.unflattenFromString(componentName)!!,
+ icon = icon,
+ user = UserHandle(userId),
+ )
+ _communalWidgets.value = fakeDatabase.values.toList()
+ }
+
+ override fun deleteWidget(widgetId: Int) {
+ fakeDatabase.remove(widgetId)
+ _communalWidgets.value = fakeDatabase.values.toList()
}
override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {}