SharedPreferencesMigration.kt
/*
* Copyright 2020 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 androidx.datastore.preferences
import android.content.Context
import android.content.SharedPreferences
import androidx.datastore.migrations.SharedPreferencesView
import androidx.datastore.migrations.SharedPreferencesMigration
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
/**
* Creates a SharedPreferencesMigration for DataStore<Preferences>.
*
* Note: This migration only supports the basic SharedPreferences types: boolean, float, int,
* long, string and string set. If the result of getAll contains other types, they will be ignored.
*
* @param produceSharedPreferences Should return the instance of SharedPreferences to migrate from.
* @param keysToMigrate The list of keys to migrate. The keys will be mapped to
* datastore.Preferences with their same values. If the key is already present in the new
* Preferences, the key will not be migrated again. If the key is not present in the
* SharedPreferences it will not be migrated. If keysToMigrate is not set, all keys will be
* migrated from the existing SharedPreferences.
*/
@JvmOverloads // Generate methods for default params for java users.
public fun SharedPreferencesMigration(
produceSharedPreferences: () -> SharedPreferences,
keysToMigrate: Set<String> = MIGRATE_ALL_KEYS
): SharedPreferencesMigration<Preferences> =
if (keysToMigrate === MIGRATE_ALL_KEYS) {
SharedPreferencesMigration(
produceSharedPreferences = produceSharedPreferences,
shouldRunMigration = getShouldRunMigration(keysToMigrate),
migrate = getMigrationFunction(),
)
} else {
SharedPreferencesMigration(
produceSharedPreferences = produceSharedPreferences,
keysToMigrate = keysToMigrate,
shouldRunMigration = getShouldRunMigration(keysToMigrate),
migrate = getMigrationFunction(),
)
}
/**
* Creates a SharedPreferencesMigration for DataStore<Preferences>.
*
* If the SharedPreferences is empty once the migration completes, this migration will attempt to
* delete it.
*
* @param context Context used for getting SharedPreferences.
* @param sharedPreferencesName The name of the SharedPreferences.
* @param keysToMigrate The list of keys to migrate. The keys will be mapped to
* datastore.Preferences with their same values. If the key is already present in the new
* Preferences, the key will not be migrated again. If the key is not present in the
* SharedPreferences it will not be migrated. If keysToMigrate is not set, all keys will be
* migrated from the existing SharedPreferences.
*/
@JvmOverloads // Generate methods for default params for java users.
public fun SharedPreferencesMigration(
context: Context,
sharedPreferencesName: String,
keysToMigrate: Set<String> = MIGRATE_ALL_KEYS,
): SharedPreferencesMigration<Preferences> =
if (keysToMigrate === MIGRATE_ALL_KEYS) {
SharedPreferencesMigration(
context = context,
sharedPreferencesName = sharedPreferencesName,
shouldRunMigration = getShouldRunMigration(keysToMigrate),
migrate = getMigrationFunction()
)
} else {
SharedPreferencesMigration(
context = context,
sharedPreferencesName = sharedPreferencesName,
keysToMigrate = keysToMigrate,
shouldRunMigration = getShouldRunMigration(keysToMigrate),
migrate = getMigrationFunction()
)
}
private fun getMigrationFunction(): suspend (SharedPreferencesView, Preferences) -> Preferences =
{ sharedPrefs: SharedPreferencesView, currentData: Preferences ->
// prefs.getAll is already filtered to our key set, but we don't want to overwrite
// already existing keys.
val currentKeys = currentData.asMap().keys.map { it.name }
val filteredSharedPreferences =
sharedPrefs.getAll().filter { (key, _) -> key !in currentKeys }
val mutablePreferences = currentData.toMutablePreferences()
for ((key, value) in filteredSharedPreferences) {
when (value) {
is Boolean -> mutablePreferences[
booleanPreferencesKey(key)
] = value
is Float -> mutablePreferences[
floatPreferencesKey(key)
] = value
is Int -> mutablePreferences[
intPreferencesKey(key)
] = value
is Long -> mutablePreferences[
longPreferencesKey(key)
] = value
is String -> mutablePreferences[
stringPreferencesKey(key)
] = value
is Set<*> -> {
@Suppress("UNCHECKED_CAST")
mutablePreferences[
stringSetPreferencesKey(key)
] = value as Set<String>
}
}
}
mutablePreferences.toPreferences()
}
private fun getShouldRunMigration(keysToMigrate: Set<String>): suspend (Preferences) -> Boolean =
{ prefs ->
// If any key hasn't been migrated to currentData, we can't skip the migration. If
// the key set is not specified, we can't skip the migration.
val allKeys = prefs.asMap().keys.map { it.name }
if (keysToMigrate === MIGRATE_ALL_KEYS) {
true
} else {
keysToMigrate.any { it !in allKeys }
}
}
internal val MIGRATE_ALL_KEYS: Set<String> = mutableSetOf()