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

Commit be52a8ff authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] DataStoreWrapper" into main

parents 822fd118 edf33ae6
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.common.data

import com.android.systemui.common.data.datastore.DataStoreWrapperFactory
import com.android.systemui.common.data.datastore.DataStoreWrapperFactoryImpl
import com.android.systemui.common.data.repository.BatteryRepository
import com.android.systemui.common.data.repository.BatteryRepositoryImpl
import com.android.systemui.common.data.repository.PackageChangeRepository
@@ -31,4 +33,9 @@ abstract class CommonDataLayerModule {
    ): PackageChangeRepository

    @Binds abstract fun bindBatteryRepository(impl: BatteryRepositoryImpl): BatteryRepository

    @Binds
    abstract fun bindDataStoreWrapperFactory(
        impl: DataStoreWrapperFactoryImpl
    ): DataStoreWrapperFactory
}
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.common.data.datastore

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
 * Wraps an instance of Jetpack [DataStore]; intended to make unit testing possible.
 *
 * The sacrifice made is that all values must be [String] (but that's okay, you can convert anything
 * to/from `String`).
 */
interface DataStoreWrapper {
    val data: Flow<Map<String, String>>

    suspend fun edit(block: (MutableMap<String, String>) -> Unit)
}

class DataStoreWrapperImpl(private val dataStore: DataStore<Preferences>) : DataStoreWrapper {

    override val data: Flow<Map<String, String>> = dataStore.data.map { it.toStringMap() }

    override suspend fun edit(block: (MutableMap<String, String>) -> Unit) {
        dataStore.edit { prefs ->
            val mutableMap = prefs.toStringMap().toMutableMap()
            block(mutableMap)
            prefs.clear()
            mutableMap.forEach { (name, value) -> prefs[stringPreferencesKey(name)] = value }
        }
    }

    private fun Preferences.toStringMap(): Map<String, String> {
        return this.asMap().map { (key, value) -> key.name to value.toString() }.toMap()
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.common.data.datastore

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
}

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

    override fun create(dataStoreFileName: String, userId: Int): DataStoreWrapper {
        return DataStoreWrapperImpl(
            dataStore =
                PreferenceDataStoreFactory.create(
                    corruptionHandler =
                        ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
                    scope = backgroundScope,
                ) {
                    userFileManager.getFile(dataStoreFileName, userId)
                }
        )
    }
}
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.common.data.datastore

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture

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

        override fun create(dataStoreFileName: String, userId: Int): DataStoreWrapper {
            return cache.getOrPut(userId) { FakeDataStoreWrapperImpl() }
        }
    }
}
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.common.data.datastore

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow

class FakeDataStoreWrapperImpl : DataStoreWrapper {

    private val _data = MutableStateFlow(emptyMap<String, String>())
    override val data: Flow<Map<String, String>> = _data

    override suspend fun edit(block: (MutableMap<String, String>) -> Unit) {
        val mutableMap = _data.value.toMutableMap()
        block(mutableMap)
        _data.value = mutableMap.toMap()
    }
}