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

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

Merge changes from topic "room-schema" into main

* changes:
  Restore work profile widgets on hub
  Add user serial number to communal db schema
parents 1c303cdc 96be44e8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -669,6 +669,7 @@ android_library {
    ],
    asset_dirs: [
        "tests/goldens",
        "schemas",
    ],
    static_libs: [
        "SystemUI-res",
@@ -708,6 +709,7 @@ android_library {
        "androidx-constraintlayout_constraintlayout",
        "androidx.exifinterface_exifinterface",
        "androidx.room_room-runtime",
        "androidx.room_room-testing",
        "androidx.room_room-ktx",
        "androidx.datastore_datastore-preferences",
        "device_state_flags_lib",
+74 −7
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.content.mockedContext
import android.os.Handler
import android.os.fakeExecutorHandler
import android.provider.Settings.Secure.USER_SETUP_COMPLETE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -27,11 +30,13 @@ 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.concurrency.fakeExecutor
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.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -41,6 +46,8 @@ import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor

@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -50,10 +57,13 @@ class CommunalBackupRestoreStartableTest : SysuiTestCase() {

    @Mock private lateinit var communalInteractor: CommunalInteractor

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

    private lateinit var context: Context
    private lateinit var broadcastDispatcher: FakeBroadcastDispatcher
    private lateinit var secureSettings: SecureSettings
    private lateinit var handler: Handler
    private lateinit var fakeExecutor: FakeExecutor
    private lateinit var underTest: CommunalBackupRestoreStartable

    @Before
@@ -62,18 +72,28 @@ class CommunalBackupRestoreStartableTest : SysuiTestCase() {

        context = kosmos.mockedContext
        broadcastDispatcher = kosmos.broadcastDispatcher
        secureSettings = kosmos.fakeSettings
        handler = kosmos.fakeExecutorHandler
        fakeExecutor = kosmos.fakeExecutor

        secureSettings.putInt(USER_SETUP_COMPLETE, 0)

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

    @Test
    fun testRestoreWidgetsUponHostRestored() =
    fun restoreWidgets_userSetUpComplete_performRestore() =
        testScope.runTest {
            // User set up complete
            secureSettings.putInt(USER_SETUP_COMPLETE, 1)

            underTest.start()

            // Verify restore widgets not called
@@ -94,7 +114,7 @@ class CommunalBackupRestoreStartableTest : SysuiTestCase() {

            // Verify restore widgets called
            verify(communalInteractor).restoreWidgets(mapCaptor.capture())
            val oldToNewWidgetIdMap = mapCaptor.value
            val oldToNewWidgetIdMap = mapCaptor.firstValue
            assertThat(oldToNewWidgetIdMap)
                .containsExactlyEntriesIn(
                    mapOf(
@@ -106,10 +126,54 @@ class CommunalBackupRestoreStartableTest : SysuiTestCase() {
        }

    @Test
    fun testDoNotRestoreWidgetsIfNotForCommunalWidgetHost() =
    fun restoreWidgets_userSetUpNotComplete_restoreWhenUserSetupComplete() =
        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 not called because user setup not complete
            verify(communalInteractor, never()).restoreWidgets(any())

            // User setup complete
            secureSettings.putInt(USER_SETUP_COMPLETE, 1)
            fakeExecutor.runAllReady()

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

    @Test
    fun restoreWidgets_broadcastNotForCommunalWidgetHost_doNotPerformRestore() =
        testScope.runTest {
            // User set up complete
            secureSettings.putInt(USER_SETUP_COMPLETE, 1)

            underTest.start()

            // Trigger app widget host restored, but for another host
            val hostId = CommunalWidgetModule.APP_WIDGET_HOST_ID + 1
            val intent =
@@ -126,8 +190,11 @@ class CommunalBackupRestoreStartableTest : SysuiTestCase() {
        }

    @Test
    fun testAbortRestoreWidgetsIfOldToNewIdsMappingInvalid() =
    fun restoreWidgets_oldToNewIdsMappingInvalid_abortRestore() =
        testScope.runTest {
            // User set up complete
            secureSettings.putInt(USER_SETUP_COMPLETE, 1)

            underTest.start()

            // Trigger app widget host restored, but new ids list is one too many for old ids
+208 −49

File changed.

Preview size limit exceeded, changes collapsed.

+81 −0
Original line number Diff line number Diff line
{
  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "02e2da2d36e6955200edd5fb49e63c72",
    "entities": [
      {
        "tableName": "communal_widget_table",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL, `user_serial_number` INTEGER NOT NULL DEFAULT -1)",
        "fields": [
          {
            "fieldPath": "uid",
            "columnName": "uid",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "widgetId",
            "columnName": "widget_id",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "componentName",
            "columnName": "component_name",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "itemId",
            "columnName": "item_id",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "userSerialNumber",
            "columnName": "user_serial_number",
            "affinity": "INTEGER",
            "notNull": true,
            "defaultValue": "-1"
          }
        ],
        "primaryKey": {
          "autoGenerate": true,
          "columnNames": [
            "uid"
          ]
        }
      },
      {
        "tableName": "communal_item_rank_table",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)",
        "fields": [
          {
            "fieldPath": "uid",
            "columnName": "uid",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "rank",
            "columnName": "rank",
            "affinity": "INTEGER",
            "notNull": true,
            "defaultValue": "0"
          }
        ],
        "primaryKey": {
          "autoGenerate": true,
          "columnNames": [
            "uid"
          ]
        }
      }
    ],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02e2da2d36e6955200edd5fb49e63c72')"
    ]
  }
}
 No newline at end of file
+56 −2
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings.Secure.USER_SETUP_COMPLETE
import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -29,6 +32,7 @@ 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 com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject

@SysUISingleton
@@ -38,10 +42,15 @@ constructor(
    private val broadcastDispatcher: BroadcastDispatcher,
    private val communalInteractor: CommunalInteractor,
    @CommunalLog logBuffer: LogBuffer,
    private val secureSettings: SecureSettings,
    handler: Handler,
) : CoreStartable, BroadcastReceiver() {

    private val logger = Logger(logBuffer, TAG)

    private var oldToNewWidgetIdMap = emptyMap<Int, Int>()
    private var userSetupComplete = false

    override fun start() {
        broadcastDispatcher.registerReceiver(
            receiver = this,
@@ -73,8 +82,53 @@ constructor(
            return
        }

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

        logger.i({ "On old to new widget ids mapping updated: $str1" }) {
            str1 = oldToNewWidgetIdMap.toString()
        }

        maybeRestoreWidgets()

        // Start observing if user setup is not complete
        if (!userSetupComplete) {
            startObservingUserSetupComplete()
        }
    }

    private val userSetupObserver =
        object : ContentObserver(handler) {
            override fun onChange(selfChange: Boolean) {
                maybeRestoreWidgets()

                // Stop observing once user setup is complete
                if (userSetupComplete) {
                    stopObservingUserSetupComplete()
                }
            }
        }

    private fun maybeRestoreWidgets() {
        val newValue = secureSettings.getInt(USER_SETUP_COMPLETE) > 0

        if (userSetupComplete != newValue) {
            userSetupComplete = newValue
            logger.i({ "User setup complete: $bool1" }) { bool1 = userSetupComplete }
        }

        if (userSetupComplete && oldToNewWidgetIdMap.isNotEmpty()) {
            logger.i("Starting to restore widgets")
            communalInteractor.restoreWidgets(oldToNewWidgetIdMap.toMap())
            oldToNewWidgetIdMap = emptyMap()
        }
    }

    private fun startObservingUserSetupComplete() {
        secureSettings.registerContentObserverSync(USER_SETUP_COMPLETE, userSetupObserver)
    }

    private fun stopObservingUserSetupComplete() {
        secureSettings.unregisterContentObserverSync(userSetupObserver)
    }

    companion object {
Loading