Move NotificationsInteractor & related repos to shared.

We need a way to access notifications-related secure settings in new
sysui recommended architecture code. WallpaperPicker already had code
for this (in the form of a NotificationsInteractor,
NotificationsRepository and SecureSettingsRepository), so instead of
having multiple sources of truth for the same type of data, I moved
these to the shared package so both SystemUI and WallpaperPicker can
access them (see other CLs in topic).

I also renamed NotificationsInteractor and NotificationsRepository to
NotificationsSettingsInteractor and NotificationSettingsRepository
respectively, to better reflect the kind of data they handle.

This move also includes a very small behavior change - there was a
circular dependency between Notifications[Settings]Interactor and
NotificationsSnapshotRestorer, which deals with restoring setting when
the "Reset" button is pressed in WallpaperPicker. Since the snapshot
restorer only needs to exist in WallpaperPicker code, I split it from
the interactor and made it collect the settings flow from the interactor
directly.
I tested that this works correctly by running WallpaperPickerGoogle and
doing the following steps:
- open Lock screen customization page
- toggle "Show notifications on the lockscreen"
- lock the device to verify that the correct setting is applied
- unlock the device and press "Reset"
- verify that the toggle state is updated
- lock the device to verify that the old setting is applied

Keeping the test file in this package for now, because moving it is
complicated. See b/315806189.

Bug: 293167744
Test: manual (see steps above) + atest NotificationSettingsRepositoryTest
Flag: NONE

Change-Id: Iea3711af1871bb66ffed6f01c3fb1490d441cf02
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index f22e562..ffe3817 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -56,8 +56,6 @@
 import com.android.customization.picker.grid.domain.interactor.GridInteractor
 import com.android.customization.picker.grid.domain.interactor.GridSnapshotRestorer
 import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel
-import com.android.customization.picker.notifications.data.repository.NotificationsRepository
-import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
 import com.android.customization.picker.notifications.domain.interactor.NotificationsSnapshotRestorer
 import com.android.customization.picker.notifications.ui.viewmodel.NotificationSectionViewModel
 import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
@@ -67,6 +65,8 @@
 import com.android.systemui.shared.clocks.ClockRegistry
 import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
 import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
+import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import com.android.wallpaper.config.BaseFlags
 import com.android.wallpaper.module.CustomizationSections
 import com.android.wallpaper.module.FragmentFactory
@@ -91,6 +91,7 @@
 internal constructor(
     @MainDispatcher private val mainScope: CoroutineScope,
     @MainDispatcher private val mainDispatcher: CoroutineDispatcher,
+    @BackgroundDispatcher private val bgScope: CoroutineScope,
     @BackgroundDispatcher private val bgDispatcher: CoroutineDispatcher,
     private val userEventLogger: ThemesUserEventLogger,
 ) : WallpaperPicker2Injector(mainScope, bgDispatcher, userEventLogger), CustomizationInjector {
@@ -110,7 +111,7 @@
     private var clockCarouselViewModelFactory: ClockCarouselViewModel.Factory? = null
     private var clockViewFactory: ClockViewFactory? = null
     private var clockPickerSnapshotRestorer: ClockPickerSnapshotRestorer? = null
-    private var notificationsInteractor: NotificationsInteractor? = null
+    private var notificationSettingsInteractor: NotificationSettingsInteractor? = null
     private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null
     private var colorPickerInteractor: ColorPickerInteractor? = null
     private var colorPickerViewModelFactory: ColorPickerViewModel.Factory? = null
@@ -297,19 +298,17 @@
 
     private fun getNotificationsInteractor(
         context: Context,
-    ): NotificationsInteractor {
-        val appContext = context.applicationContext
-        return notificationsInteractor
-            ?: NotificationsInteractor(
+    ): NotificationSettingsInteractor {
+        return notificationSettingsInteractor
+            ?: NotificationSettingsInteractor(
                     repository =
-                        NotificationsRepository(
+                        NotificationSettingsRepository(
                             scope = getApplicationCoroutineScope(),
                             backgroundDispatcher = bgDispatcher,
                             secureSettingsRepository = getSecureSettingsRepository(context),
                         ),
-                    snapshotRestorer = { getNotificationsSnapshotRestorer(appContext) },
                 )
-                .also { notificationsInteractor = it }
+                .also { notificationSettingsInteractor = it }
     }
 
     private fun getNotificationsSnapshotRestorer(context: Context): NotificationsSnapshotRestorer {
@@ -319,6 +318,7 @@
                         getNotificationsInteractor(
                             context = context,
                         ),
+                    backgroundScope = bgScope,
                 )
                 .also { notificationsSnapshotRestorer = it }
     }
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
index cc4079a..e329bc9 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -23,7 +23,7 @@
 import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import com.android.systemui.plugins.clocks.ClockMetadata
 import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.wallpaper.settings.data.repository.SecureSettingsRepository
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/src/com/android/customization/picker/notifications/data/repository/NotificationsRepository.kt b/src/com/android/customization/picker/notifications/data/repository/NotificationsRepository.kt
deleted file mode 100644
index c75ddce..0000000
--- a/src/com/android/customization/picker/notifications/data/repository/NotificationsRepository.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2023 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.customization.picker.notifications.data.repository
-
-import android.provider.Settings
-import com.android.customization.picker.notifications.shared.model.NotificationSettingsModel
-import com.android.wallpaper.settings.data.repository.SecureSettingsRepository
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.withContext
-
-/** Provides access to state related to notifications. */
-class NotificationsRepository(
-    scope: CoroutineScope,
-    private val backgroundDispatcher: CoroutineDispatcher,
-    private val secureSettingsRepository: SecureSettingsRepository,
-) {
-    /** The current state of the notification setting. */
-    val settings: SharedFlow<NotificationSettingsModel> =
-        secureSettingsRepository
-            .intSetting(
-                name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-            )
-            .map { lockScreenShowNotificationsInt ->
-                NotificationSettingsModel(
-                    isShowNotificationsOnLockScreenEnabled = lockScreenShowNotificationsInt == 1,
-                )
-            }
-            .shareIn(
-                scope = scope,
-                started = SharingStarted.WhileSubscribed(),
-                replay = 1,
-            )
-
-    suspend fun getSettings(): NotificationSettingsModel {
-        return withContext(backgroundDispatcher) {
-            NotificationSettingsModel(
-                isShowNotificationsOnLockScreenEnabled =
-                    secureSettingsRepository.get(
-                        name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                        defaultValue = 0,
-                    ) == 1
-            )
-        }
-    }
-
-    suspend fun setSettings(model: NotificationSettingsModel) {
-        withContext(backgroundDispatcher) {
-            secureSettingsRepository.set(
-                name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                value = if (model.isShowNotificationsOnLockScreenEnabled) 1 else 0,
-            )
-        }
-    }
-}
diff --git a/src/com/android/customization/picker/notifications/domain/interactor/NotificationsInteractor.kt b/src/com/android/customization/picker/notifications/domain/interactor/NotificationsInteractor.kt
deleted file mode 100644
index 1f892f0..0000000
--- a/src/com/android/customization/picker/notifications/domain/interactor/NotificationsInteractor.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2023 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.customization.picker.notifications.domain.interactor
-
-import com.android.customization.picker.notifications.data.repository.NotificationsRepository
-import com.android.customization.picker.notifications.shared.model.NotificationSettingsModel
-import javax.inject.Provider
-import kotlinx.coroutines.flow.Flow
-
-/** Encapsulates business logic for interacting with notifications. */
-class NotificationsInteractor(
-    private val repository: NotificationsRepository,
-    private val snapshotRestorer: Provider<NotificationsSnapshotRestorer>,
-) {
-    /** The current state of the notification setting. */
-    val settings: Flow<NotificationSettingsModel> = repository.settings
-
-    /** Toggles the setting to show or hide notifications on the lock screen. */
-    suspend fun toggleShowNotificationsOnLockScreenEnabled() {
-        val currentModel = repository.getSettings()
-        setSettings(
-            currentModel.copy(
-                isShowNotificationsOnLockScreenEnabled =
-                    !currentModel.isShowNotificationsOnLockScreenEnabled,
-            )
-        )
-    }
-
-    suspend fun setSettings(model: NotificationSettingsModel) {
-        repository.setSettings(model)
-        snapshotRestorer.get().storeSnapshot(model)
-    }
-
-    suspend fun getSettings(): NotificationSettingsModel {
-        return repository.getSettings()
-    }
-}
diff --git a/src/com/android/customization/picker/notifications/domain/interactor/NotificationsSnapshotRestorer.kt b/src/com/android/customization/picker/notifications/domain/interactor/NotificationsSnapshotRestorer.kt
index c782b74..9dc8872 100644
--- a/src/com/android/customization/picker/notifications/domain/interactor/NotificationsSnapshotRestorer.kt
+++ b/src/com/android/customization/picker/notifications/domain/interactor/NotificationsSnapshotRestorer.kt
@@ -12,24 +12,28 @@
  * 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.customization.picker.notifications.domain.interactor
 
-import com.android.customization.picker.notifications.shared.model.NotificationSettingsModel
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
+import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel
+import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
 import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
 import com.android.wallpaper.picker.undo.domain.interactor.SnapshotStore
 import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 /** Handles state restoration for notification settings. */
 class NotificationsSnapshotRestorer(
-    private val interactor: NotificationsInteractor,
+    private val interactor: NotificationSettingsInteractor,
+    @BackgroundDispatcher private val backgroundScope: CoroutineScope,
 ) : SnapshotRestorer {
 
     private var snapshotStore: SnapshotStore = SnapshotStore.NOOP
 
-    fun storeSnapshot(model: NotificationSettingsModel) {
+    private fun storeSnapshot(model: NotificationSettingsModel) {
         snapshotStore.store(snapshot(model))
     }
 
@@ -37,6 +41,7 @@
         store: SnapshotStore,
     ): RestorableSnapshot {
         snapshotStore = store
+        backgroundScope.launch { interactor.settings.collect { model -> storeSnapshot(model) } }
         return snapshot(interactor.getSettings())
     }
 
diff --git a/src/com/android/customization/picker/notifications/shared/model/NotificationSettingsModel.kt b/src/com/android/customization/picker/notifications/shared/model/NotificationSettingsModel.kt
deleted file mode 100644
index 7ce388b..0000000
--- a/src/com/android/customization/picker/notifications/shared/model/NotificationSettingsModel.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2023 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.customization.picker.notifications.shared.model
-
-/** Models notification settings. */
-data class NotificationSettingsModel(
-    /** Whether notifications are shown on the lock screen. */
-    val isShowNotificationsOnLockScreenEnabled: Boolean = false,
-)
diff --git a/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt b/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt
index 1a5254f..86a2693 100644
--- a/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt
+++ b/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt
@@ -22,7 +22,7 @@
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.viewModelScope
 import com.android.customization.module.logging.ThemesUserEventLogger
-import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
@@ -31,7 +31,7 @@
 class NotificationSectionViewModel
 @VisibleForTesting
 constructor(
-    private val interactor: NotificationsInteractor,
+    private val interactor: NotificationSettingsInteractor,
     private val logger: ThemesUserEventLogger,
 ) : ViewModel() {
 
@@ -50,7 +50,7 @@
     }
 
     class Factory(
-        private val interactor: NotificationsInteractor,
+        private val interactor: NotificationSettingsInteractor,
         private val logger: ThemesUserEventLogger,
     ) : ViewModelProvider.Factory {
         @Suppress("UNCHECKED_CAST")
diff --git a/tests/Android.bp b/tests/Android.bp
index 74dc6a1..33f1c3f 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -47,6 +47,7 @@
         "WallpaperPicker2TestLib",
         "WallpaperPicker2TestRunner",
         "ThemePickerTestLib",
+        "SystemUICustomizationTestUtils",
         "androidx.test.espresso.core",
         "androidx.test.espresso.contrib",
         "androidx.test.espresso.intents",
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index d4dde59..296d2ca 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -24,6 +24,7 @@
         "junit",
         "kotlinx_coroutines_test",
         "truth",
+        "SystemUICustomizationTestUtils",
     ],
 
     libs: [
diff --git a/tests/robotests/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModelTest.kt b/tests/robotests/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModelTest.kt
index 1ff1fc8..8966de5 100644
--- a/tests/robotests/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModelTest.kt
+++ b/tests/robotests/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModelTest.kt
@@ -20,16 +20,13 @@
 import androidx.test.filters.SmallTest
 import com.android.customization.module.logging.TestThemesUserEventLogger
 import com.android.customization.module.logging.ThemesUserEventLogger
-import com.android.customization.picker.notifications.data.repository.NotificationsRepository
-import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
-import com.android.customization.picker.notifications.domain.interactor.NotificationsSnapshotRestorer
-import com.android.wallpaper.testing.FakeSecureSettingsRepository
-import com.android.wallpaper.testing.FakeSnapshotStore
+import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
+import com.android.systemui.shared.settings.data.repository.FakeSecureSettingsRepository
 import com.android.wallpaper.testing.collectLastValue
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.resetMain
@@ -51,7 +48,7 @@
     private lateinit var underTest: NotificationSectionViewModel
 
     private lateinit var testScope: TestScope
-    private lateinit var interactor: NotificationsInteractor
+    private lateinit var interactor: NotificationSettingsInteractor
 
     @Before
     fun setUp() {
@@ -59,19 +56,13 @@
         Dispatchers.setMain(testDispatcher)
         testScope = TestScope(testDispatcher)
         interactor =
-            NotificationsInteractor(
+            NotificationSettingsInteractor(
                 repository =
-                    NotificationsRepository(
+                    NotificationSettingsRepository(
                         scope = testScope.backgroundScope,
                         backgroundDispatcher = testDispatcher,
                         secureSettingsRepository = FakeSecureSettingsRepository(),
                     ),
-                snapshotRestorer = {
-                    NotificationsSnapshotRestorer(
-                            interactor = interactor,
-                        )
-                        .apply { runBlocking { setUpSnapshotRestorer(FakeSnapshotStore()) } }
-                },
             )
 
         underTest =
diff --git a/tests/robotests/src/com/android/customization/picker/notifications/data/repository/NotificationsRepositoryTest.kt b/tests/robotests/src/com/android/customization/picker/repository/NotificationSettingsRepositoryTest.kt
similarity index 85%
rename from tests/robotests/src/com/android/customization/picker/notifications/data/repository/NotificationsRepositoryTest.kt
rename to tests/robotests/src/com/android/customization/picker/repository/NotificationSettingsRepositoryTest.kt
index be799db..bb6f292 100644
--- a/tests/robotests/src/com/android/customization/picker/notifications/data/repository/NotificationsRepositoryTest.kt
+++ b/tests/robotests/src/com/android/customization/picker/repository/NotificationSettingsRepositoryTest.kt
@@ -12,18 +12,17 @@
  * 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.customization.picker.notifications.data.repository
+package com.android.customization.picker.repository
 
 import android.provider.Settings
 import androidx.test.filters.SmallTest
-import com.android.customization.picker.notifications.shared.model.NotificationSettingsModel
-import com.android.wallpaper.testing.FakeSecureSettingsRepository
+import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
+import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel
+import com.android.systemui.shared.settings.data.repository.FakeSecureSettingsRepository
 import com.android.wallpaper.testing.collectLastValue
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -32,12 +31,11 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(JUnit4::class)
-class NotificationsRepositoryTest {
+class NotificationSettingsRepositoryTest {
 
-    private lateinit var underTest: NotificationsRepository
+    private lateinit var underTest: NotificationSettingsRepository
 
     private lateinit var testScope: TestScope
     private lateinit var secureSettingsRepository: FakeSecureSettingsRepository
@@ -49,7 +47,7 @@
         secureSettingsRepository = FakeSecureSettingsRepository()
 
         underTest =
-            NotificationsRepository(
+            NotificationSettingsRepository(
                 scope = testScope.backgroundScope,
                 backgroundDispatcher = testDispatcher,
                 secureSettingsRepository = secureSettingsRepository,