diff options
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>) {} |