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

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

Merge changes Ia67bb901,I6bfc9483,Ieb59f601,Id43281e1 into main

* changes:
  Restore communal widgets after host is restored
  Request backup when communal data changes
  Backup & restore communal state
  Introduce communal state proto for backup & restore
parents 708e7587 2c072561
Loading
Loading
Loading
Loading
+152 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.communal

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.content.mockedContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.widgets.CommunalWidgetModule
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.google.common.truth.Truth.assertThat
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.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalBackupRestoreStartableTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope

    @Mock private lateinit var communalInteractor: CommunalInteractor

    private val mapCaptor = kotlinArgumentCaptor<Map<Int, Int>>()

    private lateinit var context: Context
    private lateinit var broadcastDispatcher: FakeBroadcastDispatcher
    private lateinit var underTest: CommunalBackupRestoreStartable

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)

        context = kosmos.mockedContext
        broadcastDispatcher = kosmos.broadcastDispatcher

        underTest =
            CommunalBackupRestoreStartable(
                broadcastDispatcher,
                communalInteractor,
                logcatLogBuffer("CommunalBackupRestoreStartable"),
            )
    }

    @Test
    fun testRestoreWidgetsUponHostRestored() =
        testScope.runTest {
            underTest.start()

            // Verify restore widgets not called
            verify(communalInteractor, never()).restoreWidgets(any())

            // Trigger app widget host restored
            val intent =
                Intent().apply {
                    action = AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED
                    putExtra(
                        AppWidgetManager.EXTRA_HOST_ID,
                        CommunalWidgetModule.APP_WIDGET_HOST_ID
                    )
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, intArrayOf(1, 2, 3))
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(7, 8, 9))
                }
            broadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)

            // Verify restore widgets called
            verify(communalInteractor).restoreWidgets(mapCaptor.capture())
            val oldToNewWidgetIdMap = mapCaptor.value
            assertThat(oldToNewWidgetIdMap)
                .containsExactlyEntriesIn(
                    mapOf(
                        Pair(1, 7),
                        Pair(2, 8),
                        Pair(3, 9),
                    )
                )
        }

    @Test
    fun testDoNotRestoreWidgetsIfNotForCommunalWidgetHost() =
        testScope.runTest {
            underTest.start()

            // Trigger app widget host restored, but for another host
            val hostId = CommunalWidgetModule.APP_WIDGET_HOST_ID + 1
            val intent =
                Intent().apply {
                    action = AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED
                    putExtra(AppWidgetManager.EXTRA_HOST_ID, hostId)
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, intArrayOf(1, 2, 3))
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(7, 8, 9))
                }
            broadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)

            // Verify restore widgets not called
            verify(communalInteractor, never()).restoreWidgets(any())
        }

    @Test
    fun testAbortRestoreWidgetsIfOldToNewIdsMappingInvalid() =
        testScope.runTest {
            underTest.start()

            // Trigger app widget host restored, but new ids list is one too many for old ids
            val oldIds = intArrayOf(1, 2, 3)
            val newIds = intArrayOf(6, 7, 8, 9)
            val intent =
                Intent().apply {
                    action = AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED
                    putExtra(
                        AppWidgetManager.EXTRA_HOST_ID,
                        CommunalWidgetModule.APP_WIDGET_HOST_ID
                    )
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds)
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds)
                }
            broadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)

            // Verify restore widgets aborted
            verify(communalInteractor).abortRestoreWidgets()
            verify(communalInteractor, never()).restoreWidgets(any())
        }
}
+192 −2
Original line number Original line Diff line number Diff line
@@ -16,18 +16,23 @@


package com.android.systemui.communal.data.repository
package com.android.systemui.communal.data.repository


import android.app.backup.BackupManager
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
import android.content.ComponentName
import android.content.ComponentName
import android.content.applicationContext
import android.os.UserHandle
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.backup.CommunalBackupUtils
import com.android.systemui.communal.data.db.CommunalItemRank
import com.android.systemui.communal.data.db.CommunalItemRank
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.data.db.CommunalWidgetItem
import com.android.systemui.communal.data.db.CommunalWidgetItem
import com.android.systemui.communal.nano.CommunalHubState
import com.android.systemui.communal.proto.toByteArray
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.communal.widgets.CommunalWidgetHost
@@ -42,6 +47,7 @@ import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -68,7 +74,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
    @Mock private lateinit var providerInfoA: AppWidgetProviderInfo
    @Mock private lateinit var providerInfoA: AppWidgetProviderInfo
    @Mock private lateinit var communalWidgetHost: CommunalWidgetHost
    @Mock private lateinit var communalWidgetHost: CommunalWidgetHost
    @Mock private lateinit var communalWidgetDao: CommunalWidgetDao
    @Mock private lateinit var communalWidgetDao: CommunalWidgetDao
    @Mock private lateinit var backupManager: BackupManager


    private lateinit var backupUtils: CommunalBackupUtils
    private lateinit var logBuffer: LogBuffer
    private lateinit var logBuffer: LogBuffer
    private lateinit var fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>>
    private lateinit var fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>>


@@ -89,6 +97,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
        MockitoAnnotations.initMocks(this)
        MockitoAnnotations.initMocks(this)
        fakeWidgets = MutableStateFlow(emptyMap())
        fakeWidgets = MutableStateFlow(emptyMap())
        logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoImplTest")
        logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoImplTest")
        backupUtils = CommunalBackupUtils(kosmos.applicationContext)


        setAppWidgetIds(emptyList())
        setAppWidgetIds(emptyList())


@@ -106,6 +115,8 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                communalWidgetHost,
                communalWidgetHost,
                communalWidgetDao,
                communalWidgetDao,
                logBuffer,
                logBuffer,
                backupManager,
                backupUtils,
            )
            )
    }
    }


@@ -129,6 +140,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        priority = communalItemRankEntry.rank,
                        priority = communalItemRankEntry.rank,
                    )
                    )
                )
                )

            // Verify backup not requested
            verify(backupManager, never()).dataChanged()
        }
        }


    @Test
    @Test
@@ -152,6 +166,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {


            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao).addWidget(id, provider, priority)
            verify(communalWidgetDao).addWidget(id, provider, priority)

            // Verify backup requested
            verify(backupManager).dataChanged()
        }
        }


    @Test
    @Test
@@ -176,6 +193,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
            verify(appWidgetHost).deleteAppWidgetId(id)
            verify(appWidgetHost).deleteAppWidgetId(id)

            // Verify backup not requested
            verify(backupManager, never()).dataChanged()
        }
        }


    @Test
    @Test
@@ -202,6 +222,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
            verify(appWidgetHost).deleteAppWidgetId(id)
            verify(appWidgetHost).deleteAppWidgetId(id)

            // Verify backup not requested
            verify(backupManager, never()).dataChanged()
        }
        }


    @Test
    @Test
@@ -225,10 +248,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {


            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao).addWidget(id, provider, priority)
            verify(communalWidgetDao).addWidget(id, provider, priority)

            // Verify backup requested
            verify(backupManager).dataChanged()
        }
        }


    @Test
    @Test
    fun deleteWidget_deletefromDbTrue_alsoDeleteFromHost() =
    fun deleteWidget_deleteFromDbTrue_alsoDeleteFromHost() =
        testScope.runTest {
        testScope.runTest {
            val id = 1
            val id = 1
            whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(true)
            whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(true)
@@ -237,10 +263,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {


            verify(communalWidgetDao).deleteWidgetById(id)
            verify(communalWidgetDao).deleteWidgetById(id)
            verify(appWidgetHost).deleteAppWidgetId(id)
            verify(appWidgetHost).deleteAppWidgetId(id)

            // Verify backup requested
            verify(backupManager).dataChanged()
        }
        }


    @Test
    @Test
    fun deleteWidget_deletefromDbFalse_doesNotDeleteFromHost() =
    fun deleteWidget_deleteFromDbFalse_doesNotDeleteFromHost() =
        testScope.runTest {
        testScope.runTest {
            val id = 1
            val id = 1
            whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(false)
            whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(false)
@@ -249,6 +278,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {


            verify(communalWidgetDao).deleteWidgetById(id)
            verify(communalWidgetDao).deleteWidgetById(id)
            verify(appWidgetHost, never()).deleteAppWidgetId(id)
            verify(appWidgetHost, never()).deleteAppWidgetId(id)

            // Verify backup not requested
            verify(backupManager, never()).dataChanged()
        }
        }


    @Test
    @Test
@@ -259,6 +291,147 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            runCurrent()
            runCurrent()


            verify(communalWidgetDao).updateWidgetOrder(widgetIdToPriorityMap)
            verify(communalWidgetDao).updateWidgetOrder(widgetIdToPriorityMap)

            // Verify backup requested
            verify(backupManager).dataChanged()
        }

    @Test
    fun restoreWidgets_deleteStateFileIfRestoreFails() =
        testScope.runTest {
            // Write a state file that is invalid, and verify it is written
            backupUtils.writeBytesToDisk(byteArrayOf(1, 2, 3, 4, 5, 6))
            assertThat(backupUtils.fileExists()).isTrue()

            // Try to restore widgets
            underTest.restoreWidgets(emptyMap())
            runCurrent()

            // The restore should fail, and verify that the file is deleted
            assertThat(backupUtils.fileExists()).isFalse()
        }

    @Test
    fun restoreWidgets_deleteStateFileAfterWidgetsRestored() =
        testScope.runTest {
            // Write a state file, and verify it is written
            backupUtils.writeBytesToDisk(fakeState.toByteArray())
            assertThat(backupUtils.fileExists()).isTrue()

            // Set up app widget host with widget ids
            setAppWidgetIds(listOf(11, 12))

            // Restore widgets
            underTest.restoreWidgets(mapOf(Pair(1, 11), Pair(2, 12)))
            runCurrent()

            // Verify state restored
            verify(communalWidgetDao).restoreCommunalHubState(any())

            // Verify state file deleted
            assertThat(backupUtils.fileExists()).isFalse()
        }

    @Test
    fun restoreWidgets_restoredWidgetsNotRegisteredWithHostAreSkipped() =
        testScope.runTest {
            // Write fake state to file
            backupUtils.writeBytesToDisk(fakeState.toByteArray())

            // Set up app widget host with widget ids. Widget 12 (previously 2) is absent.
            setAppWidgetIds(listOf(11))

            // Restore widgets.
            underTest.restoreWidgets(mapOf(Pair(1, 11), Pair(2, 12)))
            runCurrent()

            // Verify state restored, and widget 2 skipped
            val restoredState =
                withArgCaptor<CommunalHubState> {
                    verify(communalWidgetDao).restoreCommunalHubState(capture())
                }
            val restoredWidgets = restoredState.widgets.toList()
            assertThat(restoredWidgets).hasSize(1)

            val restoredWidget = restoredWidgets.first()
            val expectedWidget = fakeState.widgets.first()

            // Verify widget id is updated, and the rest remain the same as expected
            assertThat(restoredWidget.widgetId).isEqualTo(11)
            assertThat(restoredWidget.componentName).isEqualTo(expectedWidget.componentName)
            assertThat(restoredWidget.rank).isEqualTo(expectedWidget.rank)
        }

    @Test
    fun restoreWidgets_registeredWidgetsNotRestoredAreRemoved() =
        testScope.runTest {
            // Write fake state to file
            backupUtils.writeBytesToDisk(fakeState.toByteArray())

            // Set up app widget host with widget ids. Widget 13 will not be restored.
            setAppWidgetIds(listOf(11, 12, 13))

            // Restore widgets.
            underTest.restoreWidgets(mapOf(Pair(1, 11), Pair(2, 12)))
            runCurrent()

            // Verify widget 1 and 2 are restored, and are now 11 and 12.
            val restoredState =
                withArgCaptor<CommunalHubState> {
                    verify(communalWidgetDao).restoreCommunalHubState(capture())
                }
            val restoredWidgets = restoredState.widgets.toList()
            assertThat(restoredWidgets).hasSize(2)

            val restoredWidget1 = restoredWidgets[0]
            val expectedWidget1 = fakeState.widgets[0]
            assertThat(restoredWidget1.widgetId).isEqualTo(11)
            assertThat(restoredWidget1.componentName).isEqualTo(expectedWidget1.componentName)
            assertThat(restoredWidget1.rank).isEqualTo(expectedWidget1.rank)

            val restoredWidget2 = restoredWidgets[1]
            val expectedWidget2 = fakeState.widgets[1]
            assertThat(restoredWidget2.widgetId).isEqualTo(12)
            assertThat(restoredWidget2.componentName).isEqualTo(expectedWidget2.componentName)
            assertThat(restoredWidget2.rank).isEqualTo(expectedWidget2.rank)

            // Verify widget 13 removed since it is not restored
            verify(appWidgetHost).deleteAppWidgetId(13)
        }

    @Test
    fun restoreWidgets_onlySomeWidgetsGotNewIds() =
        testScope.runTest {
            // Write fake state to file
            backupUtils.writeBytesToDisk(fakeState.toByteArray())

            // Set up app widget host with widget ids. Widget 2 gets a new id: 12, but widget 1 does
            // not.
            setAppWidgetIds(listOf(1, 12))

            // Restore widgets.
            underTest.restoreWidgets(mapOf(Pair(2, 12)))
            runCurrent()

            // Verify widget 1 and 2 are restored, and are now 1 and 12.
            val restoredState =
                withArgCaptor<CommunalHubState> {
                    verify(communalWidgetDao).restoreCommunalHubState(capture())
                }
            val restoredWidgets = restoredState.widgets.toList()
            assertThat(restoredWidgets).hasSize(2)

            val restoredWidget1 = restoredWidgets[0]
            val expectedWidget1 = fakeState.widgets[0]
            assertThat(restoredWidget1.widgetId).isEqualTo(1)
            assertThat(restoredWidget1.componentName).isEqualTo(expectedWidget1.componentName)
            assertThat(restoredWidget1.rank).isEqualTo(expectedWidget1.rank)

            val restoredWidget2 = restoredWidgets[1]
            val expectedWidget2 = fakeState.widgets[1]
            assertThat(restoredWidget2.widgetId).isEqualTo(12)
            assertThat(restoredWidget2.componentName).isEqualTo(expectedWidget2.componentName)
            assertThat(restoredWidget2.rank).isEqualTo(expectedWidget2.rank)
        }
        }


    private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
    private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
@@ -278,5 +451,22 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                widgetFeatures =
                widgetFeatures =
                    WIDGET_FEATURE_CONFIGURATION_OPTIONAL or WIDGET_FEATURE_RECONFIGURABLE
                    WIDGET_FEATURE_CONFIGURATION_OPTIONAL or WIDGET_FEATURE_RECONFIGURABLE
            }
            }
        val fakeState =
            CommunalHubState().apply {
                widgets =
                    listOf(
                            CommunalHubState.CommunalWidgetItem().apply {
                                widgetId = 1
                                componentName = "pk_name/fake_widget_1"
                                rank = 1
                            },
                            CommunalHubState.CommunalWidgetItem().apply {
                                widgetId = 2
                                componentName = "pk_name/fake_widget_2"
                                rank = 2
                            },
                        )
                        .toTypedArray()
            }
    }
    }
}
}
+8 −2
Original line number Original line Diff line number Diff line
@@ -28,8 +28,9 @@ import android.os.ParcelFileDescriptor
import android.os.UserHandle
import android.os.UserHandle
import android.util.Log
import android.util.Log
import com.android.app.tracing.traceSection
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.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED
import com.android.systemui.communal.data.backup.CommunalBackupHelper
import com.android.systemui.communal.data.backup.CommunalBackupUtils
import com.android.systemui.communal.domain.backup.CommunalPrefsBackupHelper
import com.android.systemui.communal.domain.backup.CommunalPrefsBackupHelper
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
@@ -59,6 +60,7 @@ open class BackupHelper : BackupAgentHelper() {
            "systemui.keyguard.quickaffordance.shared_preferences"
            "systemui.keyguard.quickaffordance.shared_preferences"
        private const val COMMUNAL_PREFS_BACKUP_KEY =
        private const val COMMUNAL_PREFS_BACKUP_KEY =
            "systemui.communal.shared_preferences"
            "systemui.communal.shared_preferences"
        private const val COMMUNAL_STATE_BACKUP_KEY = "systemui.communal_state"
        val controlsDataLock = Any()
        val controlsDataLock = Any()
        const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
        const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
        const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
        const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
@@ -89,6 +91,10 @@ open class BackupHelper : BackupAgentHelper() {
                    userId = userHandle.identifier,
                    userId = userHandle.identifier,
                )
                )
            )
            )
            addHelper(
                COMMUNAL_STATE_BACKUP_KEY,
                CommunalBackupHelper(userHandle, CommunalBackupUtils(context = this)),
            )
        }
        }
    }
    }


@@ -116,7 +122,7 @@ open class BackupHelper : BackupAgentHelper() {
    }
    }


    private fun communalEnabled(): Boolean {
    private fun communalEnabled(): Boolean {
        return resources.getBoolean(R.bool.config_communalServiceEnabled) && communalHub()
        return resources.getBoolean(R.bool.config_communalServiceEnabled)
    }
    }


    /**
    /**
+83 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.communal

import android.appwidget.AppWidgetManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.widgets.CommunalWidgetModule
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import javax.inject.Inject

@SysUISingleton
class CommunalBackupRestoreStartable
@Inject
constructor(
    private val broadcastDispatcher: BroadcastDispatcher,
    private val communalInteractor: CommunalInteractor,
    @CommunalLog logBuffer: LogBuffer,
) : CoreStartable, BroadcastReceiver() {

    private val logger = Logger(logBuffer, TAG)

    override fun start() {
        broadcastDispatcher.registerReceiver(
            receiver = this,
            filter = IntentFilter(AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED),
        )
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent == null) {
            logger.w("On app widget host restored, but intent is null")
            return
        }

        if (intent.action != AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED) {
            return
        }

        val hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0)
        if (hostId != CommunalWidgetModule.APP_WIDGET_HOST_ID) {
            return
        }

        val oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS)
        val newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)

        if (oldIds == null || newIds == null || oldIds.size != newIds.size) {
            logger.w("On app widget host restored, but old to new ids mapping is invalid")
            communalInteractor.abortRestoreWidgets()
            return
        }

        val oldToNewWidgetIdMap = oldIds.zip(newIds).toMap()
        communalInteractor.restoreWidgets(oldToNewWidgetIdMap)
    }

    companion object {
        const val TAG = "CommunalBackupRestoreStartable"
    }
}
+10 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.systemui.communal.dagger
package com.android.systemui.communal.dagger


import android.content.Context
import com.android.systemui.communal.data.backup.CommunalBackupUtils
import com.android.systemui.communal.data.db.CommunalDatabaseModule
import com.android.systemui.communal.data.db.CommunalDatabaseModule
import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule
import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule
@@ -78,5 +80,13 @@ interface CommunalModule {
                )
                )
            return SceneDataSourceDelegator(applicationScope, config)
            return SceneDataSourceDelegator(applicationScope, config)
        }
        }

        @Provides
        @SysUISingleton
        fun providesCommunalBackupUtils(
            @Application context: Context,
        ): CommunalBackupUtils {
            return CommunalBackupUtils(context)
        }
    }
    }
}
}
Loading