summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Coco Duan <cocod@google.com> 2024-04-03 20:38:21 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-04-03 20:38:21 +0000
commit6e887ee1e10fed8e1eac460f343d33abce09ff88 (patch)
tree63374ab788081a8b872538d80b9e2f9c154962bc
parentef9a6d7c37cbb5a2819faca06a24886e9e4fb5bc (diff)
parent8744ef63202774408dda38d74d353dc0ccd070e8 (diff)
Merge "Bakup and restore communal shared preferences" into main
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/backup/CommunalPrefsBackupHelper.kt37
4 files changed, 137 insertions, 11 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
index 6bff0dc7bd9e..5e120b5f9560 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -16,11 +16,15 @@
package com.android.systemui.communal.data.repository
+import android.content.Context
+import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.backup.BackupHelper
+import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryImpl.Companion.FILE_NAME
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
@@ -34,16 +38,22 @@ import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.FakeSharedPreferences
import com.google.common.truth.Truth.assertThat
import java.io.File
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@@ -69,20 +79,12 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
USER_INFOS[1].id to FakeSharedPreferences()
)
)
- underTest =
- CommunalPrefsRepositoryImpl(
- testScope.backgroundScope,
- kosmos.testDispatcher,
- userRepository,
- userFileManager,
- logcatLogBuffer("CommunalPrefsRepositoryImplTest"),
- tableLogBuffer,
- )
}
@Test
fun isCtaDismissedValue_byDefault_isFalse() =
testScope.runTest {
+ underTest = createCommunalPrefsRepositoryImpl(userFileManager)
val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
assertThat(isCtaDismissed).isFalse()
}
@@ -90,6 +92,7 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
@Test
fun isCtaDismissedValue_onSet_isTrue() =
testScope.runTest {
+ underTest = createCommunalPrefsRepositoryImpl(userFileManager)
val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
underTest.setCtaDismissedForCurrentUser()
@@ -99,6 +102,7 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
@Test
fun isCtaDismissedValue_whenSwitchUser() =
testScope.runTest {
+ underTest = createCommunalPrefsRepositoryImpl(userFileManager)
val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
underTest.setCtaDismissedForCurrentUser()
@@ -118,6 +122,44 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
assertThat(isCtaDismissed).isTrue()
}
+ @Test
+ fun getSharedPreferences_whenFileRestored() =
+ testScope.runTest {
+ val userFileManagerSpy = Mockito.spy(userFileManager)
+ underTest = createCommunalPrefsRepositoryImpl(userFileManagerSpy)
+
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ assertThat(isCtaDismissed).isFalse()
+ clearInvocations(userFileManagerSpy)
+
+ // Received restore finished event.
+ kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(BackupHelper.ACTION_RESTORE_FINISHED),
+ )
+ runCurrent()
+
+ // Get shared preferences from the restored file.
+ verify(userFileManagerSpy, atLeastOnce())
+ .getSharedPreferences(
+ FILE_NAME,
+ Context.MODE_PRIVATE,
+ userRepository.getSelectedUserInfo().id
+ )
+ }
+
+ private fun createCommunalPrefsRepositoryImpl(userFileManager: UserFileManager) =
+ CommunalPrefsRepositoryImpl(
+ testScope.backgroundScope,
+ kosmos.testDispatcher,
+ userRepository,
+ userFileManager,
+ kosmos.broadcastDispatcher,
+ logcatLogBuffer("CommunalPrefsRepositoryImplTest"),
+ tableLogBuffer,
+ )
+
private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
UserFileManager {
override fun getFile(fileName: String, userId: Int): File {
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index c4d282e24a92..a667de2351d7 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -28,10 +28,14 @@ import android.os.ParcelFileDescriptor
import android.os.UserHandle
import android.util.Log
import com.android.app.tracing.traceSection
+import com.android.systemui.Flags.communalHub
+import com.android.systemui.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED
+import com.android.systemui.communal.domain.backup.CommunalPrefsBackupHelper
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
import com.android.systemui.people.widget.PeopleBackupHelper
+import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManagerImpl
/**
@@ -53,6 +57,8 @@ open class BackupHelper : BackupAgentHelper() {
private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY =
"systemui.keyguard.quickaffordance.shared_preferences"
+ private const val COMMUNAL_PREFS_BACKUP_KEY =
+ "systemui.communal.shared_preferences"
val controlsDataLock = Any()
const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
@@ -75,6 +81,15 @@ open class BackupHelper : BackupAgentHelper() {
userId = userHandle.identifier,
),
)
+ if (communalEnabled()) {
+ addHelper(
+ COMMUNAL_PREFS_BACKUP_KEY,
+ CommunalPrefsBackupHelper(
+ context = this,
+ userId = userHandle.identifier,
+ )
+ )
+ }
}
override fun onRestoreFinished() {
@@ -100,6 +115,10 @@ open class BackupHelper : BackupAgentHelper() {
}
}
+ private fun communalEnabled(): Boolean {
+ return resources.getBoolean(R.bool.config_communalServiceEnabled) && communalHub()
+ }
+
/**
* Helper class for restoring files ONLY if they are not present.
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index 0e9b32ffd12a..40d744015498 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -17,8 +17,11 @@
package com.android.systemui.communal.data.repository
import android.content.Context
+import android.content.IntentFilter
import android.content.SharedPreferences
import android.content.pm.UserInfo
+import com.android.systemui.backup.BackupHelper
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
@@ -30,15 +33,18 @@ import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.settings.UserFileManager
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
+import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -65,14 +71,36 @@ constructor(
@Background private val bgDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
private val userFileManager: UserFileManager,
+ broadcastDispatcher: BroadcastDispatcher,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
) : CommunalPrefsRepository {
private val logger = Logger(logBuffer, "CommunalPrefsRepositoryImpl")
+ /**
+ * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
+ * initial value.
+ */
+ private val backupRestorationEvents: Flow<Unit> =
+ broadcastDispatcher.broadcastFlow(
+ filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
+ flags = Context.RECEIVER_NOT_EXPORTED,
+ permission = BackupHelper.PERMISSION_SELF,
+ )
+
override val isCtaDismissed: Flow<Boolean> =
- userRepository.selectedUserInfo
+ combine(
+ userRepository.selectedUserInfo,
+ // Make sure combine can emit even if we never get a Backup & Restore event,
+ // which is the most common case as restoration only happens on initial device
+ // setup.
+ backupRestorationEvents.emitOnStart().onEach {
+ logger.i("Restored state for communal preferences.")
+ },
+ ) { user, _ ->
+ user
+ }
.flatMapLatest(::observeCtaDismissState)
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/backup/CommunalPrefsBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/backup/CommunalPrefsBackupHelper.kt
new file mode 100644
index 000000000000..55c6ec88bd11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/backup/CommunalPrefsBackupHelper.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.domain.backup
+
+import android.app.backup.SharedPreferencesBackupHelper
+import android.content.Context
+import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryImpl.Companion.FILE_NAME
+import com.android.systemui.settings.UserFileManagerImpl
+
+/** Helper to backup & restore the shared preferences in glanceable hub for the current user. */
+class CommunalPrefsBackupHelper(
+ context: Context,
+ userId: Int,
+) :
+ SharedPreferencesBackupHelper(
+ context,
+ UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = FILE_NAME,
+ )
+ .path
+ )