mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
Migrate kvstore to kotlin (#5973)
* Get good test around the basic KvStore before starting conversion * Converted BasicKvStore to kotlin and removed dead code * Converted JsonKvStore to kotlin --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
d6c4cab207
commit
dac3657536
10 changed files with 562 additions and 320 deletions
|
|
@ -62,7 +62,7 @@ class SessionManager @Inject constructor(
|
|||
fun forceLogin(context: Context?) =
|
||||
context?.let { LoginActivity.startYourself(it) }
|
||||
|
||||
fun getPreference(key: String?): Boolean =
|
||||
fun getPreference(key: String): Boolean =
|
||||
defaultKvStore.getBoolean(key)
|
||||
|
||||
fun logout(): Completable = Completable.fromObservable(
|
||||
|
|
|
|||
|
|
@ -1,215 +0,0 @@
|
|||
package fr.free.nrw.commons.kvstore;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BasicKvStore implements KeyValueStore {
|
||||
private static final String KEY_VERSION = "__version__";
|
||||
/*
|
||||
This class only performs puts, sets and clears.
|
||||
A commit returns a boolean indicating whether it has succeeded, we are not throwing an exception as it will
|
||||
require the dev to handle it in every usage - instead we will pass on this boolean so it can be evaluated if needed.
|
||||
*/
|
||||
private final SharedPreferences _store;
|
||||
|
||||
public BasicKvStore(Context context, String storeName) {
|
||||
_store = context.getSharedPreferences(storeName, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* If you don't want onVersionUpdate to be called on a fresh creation, the first version supplied for the kvstore should be set to 0.
|
||||
*/
|
||||
public BasicKvStore(Context context, String storeName, int version) {
|
||||
this(context,storeName,version,false);
|
||||
}
|
||||
|
||||
public BasicKvStore(Context context, String storeName, int version, boolean clearAllOnUpgrade) {
|
||||
_store = context.getSharedPreferences(storeName, Context.MODE_PRIVATE);
|
||||
int oldVersion = getInt(KEY_VERSION);
|
||||
|
||||
if (version > oldVersion) {
|
||||
Timber.i("version updated from %s to %s, with clearFlag %b", oldVersion, version, clearAllOnUpgrade);
|
||||
onVersionUpdate(oldVersion, version, clearAllOnUpgrade);
|
||||
}
|
||||
|
||||
if (version < oldVersion) {
|
||||
throw new IllegalArgumentException(
|
||||
"kvstore downgrade not allowed, old version:" + oldVersion + ", new version: " +
|
||||
version);
|
||||
}
|
||||
//Keep this statement at the end so that clearing of store does not cause version also to get removed.
|
||||
putIntInternal(KEY_VERSION, version);
|
||||
}
|
||||
|
||||
public void onVersionUpdate(int oldVersion, int version, boolean clearAllFlag) {
|
||||
if(clearAllFlag) {
|
||||
clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getKeySet() {
|
||||
Map<String, ?> allContents = new HashMap<>(_store.getAll());
|
||||
allContents.remove(KEY_VERSION);
|
||||
return allContents.keySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, ?> getAll() {
|
||||
Map<String, ?> allContents = _store.getAll();
|
||||
if (allContents == null || allContents.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
allContents.remove(KEY_VERSION);
|
||||
return new HashMap<>(allContents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key) {
|
||||
return getString(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key) {
|
||||
return getBoolean(key, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key) {
|
||||
return getLong(key, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key) {
|
||||
return getInt(key, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String defaultValue) {
|
||||
return _store.getString(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return _store.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defaultValue) {
|
||||
return _store.getLong(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defaultValue) {
|
||||
return _store.getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
public void putAllStrings(Map<String, String> keyValuePairs) {
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
for (Map.Entry<String, String> keyValuePair : keyValuePairs.entrySet()) {
|
||||
putString(editor, keyValuePair.getKey(), keyValuePair.getValue(), false);
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putString(String key, String value) {
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
putString(editor, key, value, true);
|
||||
}
|
||||
|
||||
private void putString(SharedPreferences.Editor editor, String key, String value,
|
||||
boolean commit) {
|
||||
assertKeyNotReserved(key);
|
||||
editor.putString(key, value);
|
||||
if(commit) {
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBoolean(String key, boolean value) {
|
||||
assertKeyNotReserved(key);
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLong(String key, long value) {
|
||||
assertKeyNotReserved(key);
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
editor.putLong(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInt(String key, int value) {
|
||||
assertKeyNotReserved(key);
|
||||
putIntInternal(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return _store.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String key) {
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
editor.remove(key);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAll() {
|
||||
int version = getInt(KEY_VERSION);
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
putIntInternal(KEY_VERSION, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllWithVersion() {
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void putIntInternal(String key, int value) {
|
||||
SharedPreferences.Editor editor = _store.edit();
|
||||
editor.putInt(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void assertKeyNotReserved(String key) {
|
||||
if (key.equals(KEY_VERSION)) {
|
||||
throw new IllegalArgumentException(key + "is a reserved key");
|
||||
}
|
||||
}
|
||||
|
||||
public void registerChangeListener(SharedPreferences.OnSharedPreferenceChangeListener l) {
|
||||
_store.registerOnSharedPreferenceChangeListener(l);
|
||||
}
|
||||
|
||||
public void unregisterChangeListener(SharedPreferences.OnSharedPreferenceChangeListener l) {
|
||||
_store.unregisterOnSharedPreferenceChangeListener(l);
|
||||
}
|
||||
|
||||
public Set<String> getStringSet(String key){
|
||||
return _store.getStringSet(key, new HashSet<>());
|
||||
}
|
||||
|
||||
public void putStringSet(String key,Set<String> value){
|
||||
_store.edit().putStringSet(key,value).apply();
|
||||
}
|
||||
}
|
||||
152
app/src/main/java/fr/free/nrw/commons/kvstore/BasicKvStore.kt
Normal file
152
app/src/main/java/fr/free/nrw/commons/kvstore/BasicKvStore.kt
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package fr.free.nrw.commons.kvstore
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.content.edit
|
||||
import timber.log.Timber
|
||||
|
||||
open class BasicKvStore : KeyValueStore {
|
||||
/*
|
||||
This class only performs puts, sets and clears.
|
||||
A commit returns a boolean indicating whether it has succeeded, we are not throwing an exception as it will
|
||||
require the dev to handle it in every usage - instead we will pass on this boolean so it can be evaluated if needed.
|
||||
*/
|
||||
private val _store: SharedPreferences
|
||||
|
||||
constructor(context: Context, storeName: String?) {
|
||||
_store = context.getSharedPreferences(storeName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
/**
|
||||
* If you don't want onVersionUpdate to be called on a fresh creation, the first version supplied for the kvstore should be set to 0.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
storeName: String?,
|
||||
version: Int,
|
||||
clearAllOnUpgrade: Boolean = false
|
||||
) {
|
||||
_store = context.getSharedPreferences(storeName, Context.MODE_PRIVATE)
|
||||
val oldVersion = _store.getInt(KEY_VERSION, 0)
|
||||
|
||||
require(version >= oldVersion) {
|
||||
"kvstore downgrade not allowed, old version:" + oldVersion + ", new version: " +
|
||||
version
|
||||
}
|
||||
|
||||
if (version > oldVersion) {
|
||||
Timber.i(
|
||||
"version updated from %s to %s, with clearFlag %b",
|
||||
oldVersion,
|
||||
version,
|
||||
clearAllOnUpgrade
|
||||
)
|
||||
onVersionUpdate(oldVersion, version, clearAllOnUpgrade)
|
||||
}
|
||||
|
||||
//Keep this statement at the end so that clearing of store does not cause version also to get removed.
|
||||
_store.edit { putInt(KEY_VERSION, version) }
|
||||
}
|
||||
|
||||
val all: Map<String, *>?
|
||||
get() {
|
||||
val allContents = _store.all
|
||||
if (allContents == null || allContents.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
allContents.remove(KEY_VERSION)
|
||||
return HashMap(allContents)
|
||||
}
|
||||
|
||||
override fun getString(key: String): String? =
|
||||
getString(key, null)
|
||||
|
||||
override fun getBoolean(key: String): Boolean =
|
||||
getBoolean(key, false)
|
||||
|
||||
override fun getLong(key: String): Long =
|
||||
getLong(key, 0)
|
||||
|
||||
override fun getInt(key: String): Int =
|
||||
getInt(key, 0)
|
||||
|
||||
fun getStringSet(key: String?): MutableSet<String> =
|
||||
_store.getStringSet(key, HashSet())!!
|
||||
|
||||
override fun getString(key: String, defaultValue: String?): String? =
|
||||
_store.getString(key, defaultValue)
|
||||
|
||||
override fun getBoolean(key: String, defaultValue: Boolean): Boolean =
|
||||
_store.getBoolean(key, defaultValue)
|
||||
|
||||
override fun getLong(key: String, defaultValue: Long): Long =
|
||||
_store.getLong(key, defaultValue)
|
||||
|
||||
override fun getInt(key: String, defaultValue: Int): Int =
|
||||
_store.getInt(key, defaultValue)
|
||||
|
||||
fun putAllStrings(kvData: Map<String, String>) = assertKeyNotReserved(kvData.keys) {
|
||||
for ((key, value) in kvData) {
|
||||
putString(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun putString(key: String, value: String) = assertKeyNotReserved(key) {
|
||||
putString(key, value)
|
||||
}
|
||||
|
||||
override fun putBoolean(key: String, value: Boolean) = assertKeyNotReserved(key) {
|
||||
putBoolean(key, value)
|
||||
}
|
||||
|
||||
override fun putLong(key: String, value: Long) = assertKeyNotReserved(key) {
|
||||
putLong(key, value)
|
||||
}
|
||||
|
||||
override fun putInt(key: String, value: Int) = assertKeyNotReserved(key) {
|
||||
putInt(key, value)
|
||||
}
|
||||
|
||||
fun putStringSet(key: String?, value: Set<String?>?) =
|
||||
_store.edit{ putStringSet(key, value) }
|
||||
|
||||
override fun remove(key: String) = assertKeyNotReserved(key) {
|
||||
remove(key)
|
||||
}
|
||||
|
||||
override fun contains(key: String): Boolean {
|
||||
if (key == KEY_VERSION) return false
|
||||
return _store.contains(key)
|
||||
}
|
||||
|
||||
override fun clearAll() {
|
||||
val version = _store.getInt(KEY_VERSION, 0)
|
||||
_store.edit {
|
||||
clear()
|
||||
putInt(KEY_VERSION, version)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onVersionUpdate(oldVersion: Int, version: Int, clearAllFlag: Boolean) {
|
||||
if (clearAllFlag) {
|
||||
clearAll()
|
||||
}
|
||||
}
|
||||
|
||||
protected fun assertKeyNotReserved(key: Set<String>, block: SharedPreferences.Editor.() -> Unit) {
|
||||
key.forEach { require(it != KEY_VERSION) { "$it is a reserved key" } }
|
||||
_store.edit { block(this) }
|
||||
}
|
||||
|
||||
protected fun assertKeyNotReserved(key: String, block: SharedPreferences.Editor.() -> Unit) {
|
||||
require(key != KEY_VERSION) { "$key is a reserved key" }
|
||||
_store.edit { block(this) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
@VisibleForTesting
|
||||
const val KEY_VERSION: String = "__version__"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package fr.free.nrw.commons.kvstore;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class JsonKvStore extends BasicKvStore {
|
||||
private final Gson gson;
|
||||
|
||||
public JsonKvStore(Context context, String storeName, Gson gson) {
|
||||
super(context, storeName);
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
public JsonKvStore(Context context, String storeName, int version, Gson gson) {
|
||||
super(context, storeName, version);
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
public JsonKvStore(Context context, String storeName, int version, boolean clearAllOnUpgrade, Gson gson) {
|
||||
super(context, storeName, version, clearAllOnUpgrade);
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
public <T> void putAllJsons(Map<String, T> jsonMap) {
|
||||
Map<String, String> stringsMap = new HashMap<>(jsonMap.size());
|
||||
for (Map.Entry<String, T> keyValuePair : jsonMap.entrySet()) {
|
||||
String jsonString = gson.toJson(keyValuePair.getValue());
|
||||
stringsMap.put(keyValuePair.getKey(), jsonString);
|
||||
}
|
||||
putAllStrings(stringsMap);
|
||||
}
|
||||
|
||||
public <T> void putJson(String key, T object) {
|
||||
putString(key, gson.toJson(object));
|
||||
}
|
||||
|
||||
public <T> void putJsonWithTypeInfo(String key, T object, Type type) {
|
||||
putString(key, gson.toJson(object, type));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T getJson(String key, Class<T> clazz) {
|
||||
String jsonString = getString(key);
|
||||
try {
|
||||
return gson.fromJson(jsonString, clazz);
|
||||
} catch (JsonSyntaxException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T getJson(String key, Type type) {
|
||||
String jsonString = getString(key);
|
||||
try {
|
||||
return gson.fromJson(jsonString, type);
|
||||
} catch (JsonSyntaxException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/src/main/java/fr/free/nrw/commons/kvstore/JsonKvStore.kt
Normal file
52
app/src/main/java/fr/free/nrw/commons/kvstore/JsonKvStore.kt
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package fr.free.nrw.commons.kvstore
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonSyntaxException
|
||||
|
||||
class JsonKvStore : BasicKvStore {
|
||||
val gson: Gson
|
||||
|
||||
constructor(context: Context, storeName: String?, gson: Gson) : super(context, storeName) {
|
||||
this.gson = gson
|
||||
}
|
||||
|
||||
constructor(context: Context, storeName: String?, version: Int, gson: Gson) : super(
|
||||
context, storeName, version
|
||||
) {
|
||||
this.gson = gson
|
||||
}
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
storeName: String?,
|
||||
version: Int,
|
||||
clearAllOnUpgrade: Boolean,
|
||||
gson: Gson
|
||||
) : super(context, storeName, version, clearAllOnUpgrade) {
|
||||
this.gson = gson
|
||||
}
|
||||
|
||||
fun <T> putJson(key: String, value: T) = assertKeyNotReserved(key) {
|
||||
putString(key, gson.toJson(value))
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Migrate to newer Kotlin syntax",
|
||||
replaceWith = ReplaceWith("getJson<T>(key)")
|
||||
)
|
||||
fun <T> getJson(key: String, clazz: Class<T>?): T? = try {
|
||||
gson.fromJson(getString(key), clazz)
|
||||
} catch (e: JsonSyntaxException) {
|
||||
null
|
||||
}
|
||||
|
||||
// Later, when the calls are coming from Kotlin, this will allow us to
|
||||
// drop the "clazz" parameter, and just pick up the type at the call site.
|
||||
// The deprecation warning should help migration!
|
||||
inline fun <reified T> getJson(key: String): T? = try {
|
||||
gson.fromJson(getString(key), T::class.java)
|
||||
} catch (e: JsonSyntaxException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package fr.free.nrw.commons.kvstore;
|
||||
|
||||
public interface KeyValueStore {
|
||||
String getString(String key);
|
||||
|
||||
boolean getBoolean(String key);
|
||||
|
||||
long getLong(String key);
|
||||
|
||||
int getInt(String key);
|
||||
|
||||
String getString(String key, String defaultValue);
|
||||
|
||||
boolean getBoolean(String key, boolean defaultValue);
|
||||
|
||||
long getLong(String key, long defaultValue);
|
||||
|
||||
int getInt(String key, int defaultValue);
|
||||
|
||||
void putString(String key, String value);
|
||||
|
||||
void putBoolean(String key, boolean value);
|
||||
|
||||
void putLong(String key, long value);
|
||||
|
||||
void putInt(String key, int value);
|
||||
|
||||
boolean contains(String key);
|
||||
|
||||
void remove(String key);
|
||||
|
||||
void clearAll();
|
||||
|
||||
void clearAllWithVersion();
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package fr.free.nrw.commons.kvstore
|
||||
|
||||
interface KeyValueStore {
|
||||
fun getString(key: String): String?
|
||||
|
||||
fun getBoolean(key: String): Boolean
|
||||
|
||||
fun getLong(key: String): Long
|
||||
|
||||
fun getInt(key: String): Int
|
||||
|
||||
fun getString(key: String, defaultValue: String?): String?
|
||||
|
||||
fun getBoolean(key: String, defaultValue: Boolean): Boolean
|
||||
|
||||
fun getLong(key: String, defaultValue: Long): Long
|
||||
|
||||
fun getInt(key: String, defaultValue: Int): Int
|
||||
|
||||
fun putString(key: String, value: String)
|
||||
|
||||
fun putBoolean(key: String, value: Boolean)
|
||||
|
||||
fun putLong(key: String, value: Long)
|
||||
|
||||
fun putInt(key: String, value: Int)
|
||||
|
||||
fun contains(key: String): Boolean
|
||||
|
||||
fun remove(key: String)
|
||||
|
||||
fun clearAll()
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ class SystemThemeUtils @Inject constructor(
|
|||
// Returns true if the device is in night mode or false otherwise
|
||||
fun isDeviceInNightMode(): Boolean {
|
||||
return getSystemDefaultThemeBool(
|
||||
applicationKvStore.getString(Prefs.KEY_THEME_VALUE, getSystemDefaultTheme())
|
||||
applicationKvStore.getString(Prefs.KEY_THEME_VALUE, getSystemDefaultTheme())!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue