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

Commit 9bbeef28 authored by Ale Nijamkin's avatar Ale Nijamkin
Browse files

[flexiglass] Fixes exception when switching users.

When switching users, makes sure that the CoroutineScope associated with the old DataStore instance is fully cancelled before creating a new one.

Fix: 411761375
Test: Switched to secondary user and back while filtering logcat for
"DataStores". Before the change, I saw the message when switching back
to primary, after the change, it's not there.
Test: Made sure that I still get educational tooltips on the dual shades
and I get them separately between the two users.
Flag: com.android.systemui.scene_container

Change-Id: I84f01661dd0fd8b9872f1c5f0ad18b9493934aa4
parent 60e187e7
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -20,29 +20,33 @@ import android.annotation.UserIdInt
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.emptyPreferences
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserFileManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope

interface DataStoreWrapperFactory {
    fun create(dataStoreFileName: String, @UserIdInt userId: Int): DataStoreWrapper
    fun create(
        dataStoreFileName: String,
        @UserIdInt userId: Int,
        scope: CoroutineScope,
    ): DataStoreWrapper
}

class DataStoreWrapperFactoryImpl
@Inject
constructor(
    @Background private val backgroundScope: CoroutineScope,
    private val userFileManager: UserFileManager,
) : DataStoreWrapperFactory {
constructor(private val userFileManager: UserFileManager) : DataStoreWrapperFactory {

    override fun create(dataStoreFileName: String, userId: Int): DataStoreWrapper {
    override fun create(
        dataStoreFileName: String,
        userId: Int,
        scope: CoroutineScope,
    ): DataStoreWrapper {
        return DataStoreWrapperImpl(
            dataStore =
                PreferenceDataStoreFactory.create(
                    corruptionHandler =
                        ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
                    scope = backgroundScope,
                    scope = scope,
                ) {
                    userFileManager.getFile(dataStoreFileName, userId)
                }
+28 −21
Original line number Diff line number Diff line
@@ -32,10 +32,10 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.data.model.DualShadeEducationImpressionModel
import com.android.systemui.scene.shared.model.DualShadeEducationElement
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@@ -44,7 +44,6 @@ class DualShadeEducationRepository
@Inject
constructor(
    @Background private val backgroundScope: CoroutineScope,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val dataStoreWrapperFactory: DataStoreWrapperFactory,
) {

@@ -67,25 +66,34 @@ constructor(
        get() = _elementBounds

    private var dataStore: DataStoreWrapper? = null
    private var hydrationJob: Job? = null
    private var dataStoreJob: Job? = null

    /**
     * Keeps the repository data up-to-date for the user identified by [selectedUserId]; runs until
     * cancelled by the caller.
     */
    suspend fun activateFor(@UserIdInt selectedUserId: Int) {
        dataStoreJob?.cancelAndJoin()
        val dataStoreScope =
            CoroutineScope(
                context =
                    backgroundScope.coroutineContext + Job(backgroundScope.coroutineContext[Job])
            )
        dataStoreJob = dataStoreScope.coroutineContext[Job]
        val newDataStore =
            dataStoreWrapperFactory.create(
                dataStoreFileName = DATA_STORE_FILE_NAME,
                userId = selectedUserId,
                scope = dataStoreScope,
            )
        dataStore = newDataStore

        coroutineScope {
            hydrationJob?.cancel()
            hydrationJob =
                launch(backgroundDispatcher) {
        dataStoreScope.launch {
            repeatWhenPrefsChange(newDataStore) { prefs ->
                if (!isActive) {
                    return@repeatWhenPrefsChange
                }

                impressions =
                    DualShadeEducationImpressionModel(
                        everShownNotificationsShade =
@@ -100,7 +108,6 @@ constructor(
            }
        }
    }
    }

    suspend fun setEverShownNotificationsShade(value: Boolean) {
        persist(Keys.EverShownNotificationsShade, value)
+6 −1
Original line number Diff line number Diff line
@@ -18,12 +18,17 @@ package com.android.systemui.common.data.datastore

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import kotlinx.coroutines.CoroutineScope

val Kosmos.dataStoreWrapperFactory by Fixture {
    object : DataStoreWrapperFactory {
        private val cache = mutableMapOf<Int, DataStoreWrapper>()

        override fun create(dataStoreFileName: String, userId: Int): DataStoreWrapper {
        override fun create(
            dataStoreFileName: String,
            userId: Int,
            scope: CoroutineScope,
        ): DataStoreWrapper {
            return cache.getOrPut(userId) { FakeDataStoreWrapperImpl() }
        }
    }
+0 −2
Original line number Diff line number Diff line
@@ -20,12 +20,10 @@ import com.android.systemui.common.data.datastore.dataStoreWrapperFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.testDispatcher

val Kosmos.dualShadeEducationRepository by Fixture {
    DualShadeEducationRepository(
        backgroundScope = backgroundScope,
        backgroundDispatcher = testDispatcher,
        dataStoreWrapperFactory = dataStoreWrapperFactory,
    )
}