mirror of
https://github.com/jrpie/Launcher.git
synced 2025-02-23 06:21:31 +01:00
improve json serialization
This commit is contained in:
parent
970c160f4a
commit
4ddb893d41
10 changed files with 169 additions and 138 deletions
|
@ -13,6 +13,8 @@ import de.jrpie.android.launcher.actions.TorchManager
|
|||
import de.jrpie.android.launcher.apps.AppInfo
|
||||
import de.jrpie.android.launcher.apps.DetailedAppInfo
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.preferences.migratePreferencesToNewVersion
|
||||
import de.jrpie.android.launcher.preferences.resetPreferences
|
||||
|
||||
class Application : android.app.Application() {
|
||||
val apps = MutableLiveData<List<DetailedAppInfo>>()
|
||||
|
@ -85,6 +87,19 @@ class Application : android.app.Application() {
|
|||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
LauncherPreferences.init(preferences, this.resources)
|
||||
|
||||
|
||||
// Try to restore old preferences
|
||||
migratePreferencesToNewVersion(this)
|
||||
|
||||
// First time opening the app: set defaults and start tutorial
|
||||
if (!LauncherPreferences.internal().started()) {
|
||||
resetPreferences(this)
|
||||
|
||||
LauncherPreferences.internal().started(true)
|
||||
openTutorial(this)
|
||||
}
|
||||
|
||||
|
||||
LauncherPreferences.getSharedPreferences()
|
||||
.registerOnSharedPreferenceChangeListener(listener)
|
||||
|
||||
|
|
|
@ -75,7 +75,9 @@ fun openInBrowser(url: String, context: Context) {
|
|||
}
|
||||
|
||||
fun openTutorial(context: Context) {
|
||||
context.startActivity(Intent(context, TutorialActivity::class.java))
|
||||
context.startActivity(Intent(context, TutorialActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,10 +19,13 @@ import kotlinx.serialization.KSerializer
|
|||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.encoding.decodeStructure
|
||||
import kotlinx.serialization.encoding.encodeStructure
|
||||
|
||||
@Serializable(with = LauncherActionSerializer::class)
|
||||
@SerialName("action:launcher")
|
||||
|
@ -248,30 +251,27 @@ fun openAppsList(context: Context, favorite: Boolean = false, hidden: Boolean =
|
|||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LauncherAction can't be serialized directly, since it needs a type annotation.
|
||||
* Thus this hack is needed.
|
||||
/* A custom serializer is required to store type information,
|
||||
see https://github.com/Kotlin/kotlinx.serialization/issues/1486
|
||||
*/
|
||||
@Serializable
|
||||
private class LauncherActionWrapper(val id: String)
|
||||
|
||||
private class LauncherActionSerializer() : KSerializer<LauncherAction> {
|
||||
private class LauncherActionSerializer : KSerializer<LauncherAction> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(
|
||||
"action:launcher",
|
||||
) {
|
||||
element("id", LauncherActionWrapper.serializer().descriptor)
|
||||
element("value", String.serializer().descriptor)
|
||||
}
|
||||
override fun deserialize(decoder: Decoder): LauncherAction {
|
||||
val wrapper = decoder.decodeSerializableValue(LauncherActionWrapper.serializer())
|
||||
return LauncherAction.byId(wrapper.id) ?: throw SerializationException()
|
||||
val s = decoder.decodeStructure(descriptor) {
|
||||
decodeElementIndex(descriptor)
|
||||
decodeSerializableElement(descriptor, 0, String.serializer())
|
||||
}
|
||||
return LauncherAction.byId(s) ?: throw SerializationException()
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: LauncherAction) {
|
||||
encoder.encodeSerializableValue(
|
||||
LauncherActionWrapper.serializer(),
|
||||
LauncherActionWrapper(value.id)
|
||||
)
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeSerializableElement(descriptor, 0, String.serializer(), value.id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +1,18 @@
|
|||
package de.jrpie.android.launcher.preferences;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import de.jrpie.android.launcher.R;
|
||||
import de.jrpie.android.launcher.actions.lock.LockMethod;
|
||||
import de.jrpie.android.launcher.apps.AppInfo;
|
||||
import de.jrpie.android.launcher.preferences.serialization.SetAppInfoPreferenceSerializer;
|
||||
import de.jrpie.android.launcher.preferences.serialization.MapAppInfoStringPreferenceSerializer;
|
||||
import de.jrpie.android.launcher.preferences.theme.Background;
|
||||
import de.jrpie.android.launcher.preferences.theme.ColorTheme;
|
||||
import de.jrpie.android.launcher.preferences.theme.Font;
|
||||
import eu.jonahbauer.android.preference.annotations.Preference;
|
||||
import eu.jonahbauer.android.preference.annotations.PreferenceGroup;
|
||||
import eu.jonahbauer.android.preference.annotations.Preferences;
|
||||
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException;
|
||||
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer;
|
||||
|
||||
@Preferences(
|
||||
name = "de.jrpie.android.launcher.preferences.LauncherPreferences",
|
||||
|
@ -30,9 +25,9 @@ import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSeriali
|
|||
@Preference(name = "version_code", type = int.class, defaultValue = "-1"),
|
||||
}),
|
||||
@PreferenceGroup(name = "apps", prefix = "settings_apps_", suffix = "_key", value = {
|
||||
@Preference(name = "favorites", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class),
|
||||
@Preference(name = "hidden", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class),
|
||||
@Preference(name = "custom_names", type = HashMap.class, serializer = LauncherPreferences$Config.MapAppInfoStringSerializer.class),
|
||||
@Preference(name = "favorites", type = Set.class, serializer = SetAppInfoPreferenceSerializer.class),
|
||||
@Preference(name = "hidden", type = Set.class, serializer = SetAppInfoPreferenceSerializer.class),
|
||||
@Preference(name = "custom_names", type = HashMap.class, serializer = MapAppInfoStringPreferenceSerializer.class),
|
||||
@Preference(name = "hide_bound_apps", type = boolean.class, defaultValue = "false"),
|
||||
}),
|
||||
@PreferenceGroup(name = "list", prefix = "settings_list_", suffix = "_key", value = {
|
||||
|
@ -78,74 +73,4 @@ import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSeriali
|
|||
@Preference(name = "lock_method", type = LockMethod.class, defaultValue = "DEVICE_ADMIN"),
|
||||
}),
|
||||
})
|
||||
public final class LauncherPreferences$Config {
|
||||
public static class AppInfoSetSerializer implements PreferenceSerializer<Set<AppInfo>, Set<String>> {
|
||||
|
||||
@Override
|
||||
public Set<String> serialize(Set<AppInfo> value) throws PreferenceSerializationException {
|
||||
if (value == null) return null;
|
||||
|
||||
var serialized = new HashSet<String>(value.size());
|
||||
for (var app : value) {
|
||||
serialized.add(app.serialize());
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AppInfo> deserialize(Set<String> value) throws PreferenceSerializationException {
|
||||
if (value == null) return null;
|
||||
|
||||
var deserialized = new HashSet<AppInfo>(value.size());
|
||||
|
||||
for (var s : value) {
|
||||
deserialized.add(AppInfo.Companion.deserialize(s));
|
||||
}
|
||||
|
||||
return deserialized;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO migrate to version 2
|
||||
public static class MapAppInfoStringSerializer implements PreferenceSerializer<HashMap<AppInfo, String>, Set<String>> {
|
||||
|
||||
@Override
|
||||
public Set<String> serialize(HashMap<AppInfo, String> value) throws PreferenceSerializationException {
|
||||
if (value == null) return null;
|
||||
|
||||
var serialized = new HashSet<String>(value.size());
|
||||
|
||||
for (var entry : value.entrySet()) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("key", entry.getKey().serialize());
|
||||
obj.put("value", entry.getValue());
|
||||
serialized.add(obj.toString());
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashMap<AppInfo, String> deserialize(Set<String> value) throws PreferenceSerializationException {
|
||||
if (value == null) return null;
|
||||
|
||||
var deserialized = new HashMap<AppInfo, String>();
|
||||
|
||||
for (var entry : value) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject(entry);
|
||||
AppInfo info = AppInfo.Companion.deserialize(obj.getString("key"));
|
||||
String s = obj.getString("value");
|
||||
deserialized.put(info, s);
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return deserialized;
|
||||
}
|
||||
}
|
||||
}
|
||||
public final class LauncherPreferences$Config {}
|
|
@ -6,8 +6,8 @@ import de.jrpie.android.launcher.BuildConfig
|
|||
import de.jrpie.android.launcher.actions.Action
|
||||
import de.jrpie.android.launcher.apps.AppInfo
|
||||
import de.jrpie.android.launcher.apps.DetailedAppInfo
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
|
||||
import de.jrpie.android.launcher.ui.HomeActivity
|
||||
|
||||
/* Current version of the structure of preferences.
|
||||
|
@ -19,31 +19,40 @@ const val UNKNOWN_PREFERENCE_VERSION = -1
|
|||
private const val TAG = "Launcher - Preferences"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Tries to detect preferences written by older versions of the app
|
||||
* and migrate them to the current format.
|
||||
*/
|
||||
fun migratePreferencesToNewVersion(context: Context) {
|
||||
when (LauncherPreferences.internal().versionCode()) {
|
||||
// Check versions, make sure transitions between versions go well
|
||||
PREFERENCE_VERSION -> { /* the version installed and used previously are the same */
|
||||
}
|
||||
try {
|
||||
when (LauncherPreferences.internal().versionCode()) {
|
||||
// Check versions, make sure transitions between versions go well
|
||||
PREFERENCE_VERSION -> { /* the version installed and used previously are the same */
|
||||
}
|
||||
|
||||
UNKNOWN_PREFERENCE_VERSION -> { /* still using the old preferences file */
|
||||
migratePreferencesFromVersionUnknown(context)
|
||||
UNKNOWN_PREFERENCE_VERSION -> { /* still using the old preferences file */
|
||||
migratePreferencesFromVersionUnknown(context)
|
||||
|
||||
Log.i(TAG, "migration of preferences complete.")
|
||||
}
|
||||
1 -> {
|
||||
migratePreferencesFromVersion1()
|
||||
Log.i(TAG, "migration of preferences complete.")
|
||||
}
|
||||
Log.i(TAG, "migration of preferences complete.")
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Shared preferences were written by a newer version of the app (${
|
||||
LauncherPreferences.internal().versionCode()
|
||||
})!"
|
||||
)
|
||||
1 -> {
|
||||
migratePreferencesFromVersion1()
|
||||
Log.i(TAG, "migration of preferences complete.")
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Shared preferences were written by a newer version of the app (${
|
||||
LauncherPreferences.internal().versionCode()
|
||||
})!"
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
|
||||
resetPreferences(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,12 @@ import de.jrpie.android.launcher.actions.LauncherAction
|
|||
import de.jrpie.android.launcher.apps.AppInfo
|
||||
import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
||||
import de.jrpie.android.launcher.preferences.serialization.MapAppInfoStringPreferenceSerializer
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
val oldLauncherActionIds: Map<String, LauncherAction> =
|
||||
mapOf(
|
||||
|
@ -44,7 +48,7 @@ private fun AppInfo.Companion.legacyDeserialize(serialized: String): AppInfo {
|
|||
* @param id
|
||||
* @param user a user id, ignored if the action is a [LauncherAction].
|
||||
*/
|
||||
private fun Action.Companion.fromId(id: String, user: Int?): Action? {
|
||||
private fun Action.Companion.legacyFromId(id: String, user: Int?): Action? {
|
||||
if (id.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
@ -68,7 +72,25 @@ private fun Action.Companion.legacyFromPreference(id: String): Action? {
|
|||
)
|
||||
u = if (u == AppInfo.INVALID_USER) null else u
|
||||
|
||||
return Action.fromId(actionId, u)
|
||||
return Action.legacyFromId(actionId, u)
|
||||
}
|
||||
|
||||
private fun migrateAppInfoStringMap(key: String) {
|
||||
val preferences = LauncherPreferences.getSharedPreferences()
|
||||
MapAppInfoStringPreferenceSerializer().serialize(
|
||||
preferences.getStringSet(key, setOf())?.mapNotNull { entry ->
|
||||
try {
|
||||
val obj = JSONObject(entry);
|
||||
val info = AppInfo.legacyDeserialize(obj.getString("key"))
|
||||
val value = obj.getString("value");
|
||||
Pair(info, value)
|
||||
} catch (_: JSONException) {
|
||||
null
|
||||
}
|
||||
}?.toMap(HashMap())
|
||||
)?.let {
|
||||
preferences.edit().putStringSet(key, it as Set<String>).apply()
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateAppInfoSet(key: String) {
|
||||
|
@ -82,13 +104,24 @@ private fun migrateAppInfoSet(key: String) {
|
|||
private fun migrateAction(key: String) {
|
||||
Action.legacyFromPreference(key)?.let { action ->
|
||||
LauncherPreferences.getSharedPreferences().edit()
|
||||
.putString(key, Json.encodeToString(action)).apply()
|
||||
.putString(key, Json.encodeToString(action))
|
||||
.remove("$key.app")
|
||||
.remove("$key.user")
|
||||
.apply()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate preferences from version 1 (used until version j-0.0.18) to the current format
|
||||
* (see [PREFERENCE_VERSION])
|
||||
*/
|
||||
fun migratePreferencesFromVersion1() {
|
||||
assert(PREFERENCE_VERSION == 2)
|
||||
assert(LauncherPreferences.internal().versionCode() == 1)
|
||||
Gesture.entries.forEach { g -> migrateAction(g.id) }
|
||||
migrateAppInfoSet(LauncherPreferences.apps().keys().hidden())
|
||||
migrateAppInfoSet(LauncherPreferences.apps().keys().favorites())
|
||||
migrateAppInfoStringMap(LauncherPreferences.apps().keys().customNames())
|
||||
LauncherPreferences.internal().versionCode(2)
|
||||
}
|
|
@ -4,9 +4,12 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
||||
import de.jrpie.android.launcher.preferences.theme.Background
|
||||
import de.jrpie.android.launcher.preferences.theme.ColorTheme
|
||||
|
||||
|
||||
|
||||
private fun migrateStringPreference(
|
||||
oldPrefs: SharedPreferences,
|
||||
newPreferences: SharedPreferences.Editor,
|
||||
|
@ -42,7 +45,13 @@ private fun migrateBooleanPreference(
|
|||
|
||||
private const val TAG = "Preferences ? -> 1"
|
||||
|
||||
/**
|
||||
* Try to migrate from a very old preference version, where no version number was stored
|
||||
* and a different file was used.
|
||||
*/
|
||||
fun migratePreferencesFromVersionUnknown(context: Context) {
|
||||
assert(PREFERENCE_VERSION == 2)
|
||||
|
||||
Log.i(
|
||||
TAG,
|
||||
"Unknown preference version, trying to restore preferences from old version."
|
|
@ -0,0 +1,49 @@
|
|||
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
|
||||
package de.jrpie.android.launcher.preferences.serialization
|
||||
|
||||
import de.jrpie.android.launcher.apps.AppInfo
|
||||
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException
|
||||
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
||||
// Serializers for [LauncherPreference$Config]
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class SetAppInfoPreferenceSerializer :
|
||||
PreferenceSerializer<java.util.Set<AppInfo>?, java.util.Set<java.lang.String>?> {
|
||||
@Throws(PreferenceSerializationException::class)
|
||||
override fun serialize(value: java.util.Set<AppInfo>?): java.util.Set<java.lang.String>? {
|
||||
return value?.map(AppInfo::serialize)?.toHashSet() as java.util.Set<java.lang.String>
|
||||
}
|
||||
|
||||
@Throws(PreferenceSerializationException::class)
|
||||
override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.Set<AppInfo>? {
|
||||
return value?.map (java.lang.String::toString)?.map(AppInfo::deserialize)?.toHashSet() as? java.util.Set<AppInfo>
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MapAppInfoStringPreferenceSerializer :
|
||||
PreferenceSerializer<java.util.HashMap<AppInfo, String>?, java.util.Set<java.lang.String>?> {
|
||||
|
||||
@Serializable()
|
||||
private class MapEntry(val key: AppInfo, val value: String)
|
||||
|
||||
@Throws(PreferenceSerializationException::class)
|
||||
override fun serialize(value: java.util.HashMap<AppInfo, String>?): java.util.Set<java.lang.String>? {
|
||||
return value?.map { (key, value) ->
|
||||
Json.encodeToString(MapEntry(key, value))
|
||||
}?.toHashSet() as? java.util.Set<java.lang.String>
|
||||
}
|
||||
|
||||
@Throws(PreferenceSerializationException::class)
|
||||
override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.HashMap<AppInfo, String>? {
|
||||
return value?.associateTo(HashMap()) {
|
||||
val entry = Json.decodeFromString<MapEntry>(it.toString())
|
||||
Pair(entry.key, entry.value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,17 +76,6 @@ class HomeActivity : UIObject, AppCompatActivity(),
|
|||
super<AppCompatActivity>.onCreate(savedInstanceState)
|
||||
super<UIObject>.onCreate()
|
||||
|
||||
// Try to restore old preferences
|
||||
migratePreferencesToNewVersion(this)
|
||||
|
||||
// First time opening the app: set defaults and start tutorial
|
||||
if (!LauncherPreferences.internal().started()) {
|
||||
resetPreferences(this)
|
||||
|
||||
LauncherPreferences.internal().started(true)
|
||||
openTutorial(this)
|
||||
}
|
||||
|
||||
// Initialise layout
|
||||
binding = HomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
<!-- Swipe up - Apps list -->
|
||||
<string-array name="default_up">
|
||||
<item>{\"type\": \"action:launcher\", \"id\": \"choose\"}</item> <!-- All Apps -->
|
||||
<item>{\"type\": \"action:launcher\", \"value\": \"choose\"}</item> <!-- All Apps -->
|
||||
</string-array>
|
||||
|
||||
<!-- Swipe up (left edge) - Favorite Apps -->
|
||||
<string-array name="default_up_left">
|
||||
<item>{\"type\": \"action:launcher\", \"id\": \"choose_from_favorites\"}</item>
|
||||
<item>{\"type\": \"action:launcher\", \"value\": \"choose_from_favorites\"}</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Swipe up (right edge) - Maps -->
|
||||
|
@ -106,12 +106,12 @@
|
|||
|
||||
<!-- Volume up -->
|
||||
<string-array name="default_volume_up">
|
||||
<item>{\"type\": \"action:launcher\", \"id\": \"volume_up\"}</item>
|
||||
<item>{\"type\": \"action:launcher\", \"value\": \"volume_up\"}</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Volume down -->
|
||||
<string-array name="default_volume_down">
|
||||
<item>{\"type\": \"action:launcher\", \"id\": \"volume_down\"}</item>
|
||||
<item>{\"type\": \"action:launcher\", \"value\": \"volume_down\"}</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Double click - Notes -->
|
||||
|
@ -119,7 +119,7 @@
|
|||
<item>{\"type\": \"action:app\", \"app\": {\"packageName\": \"it.niedermann.owncloud.notes\", \"activityName\": null}}</item>
|
||||
<item>{\"type\": \"action:app\", \"app\": {\"packageName\": \"com.samsung.android.app.notes\", \"activityName\": null}}</item> <!-- Samsung Notes -->
|
||||
<item>{\"type\": \"action:app\", \"app\": {\"packageName\": \"com.sec.android.widgetapp.diotek.smemo\", \"activityName\": null}}</item> <!-- S Memo (older devices) -->
|
||||
<item>{\"type\": \"action:launcher\", \"id\": \"lock_screen\"}</item>
|
||||
<item>{\"type\": \"action:launcher\", \"value\": \"lock_screen\"}</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Long click - Security -->
|
||||
|
@ -128,7 +128,7 @@
|
|||
<item>{\"type\": \"action:app\", \"app\": {\"packageName\": \"org.fedorahosted.freeotp\", \"activityName\": null}}</item>
|
||||
<item>{\"type\": \"action:app\", \"app\": {\"packageName\": \"proton.android.pass.fdroid\", \"activityName\": null}}</item> <!-- Proton Pass -->
|
||||
<item>{\"type\": \"action:app\", \"app\": {\"packageName\": \"com.kunzisoft.keepass.libre\", \"activityName\": null}}</item> <!-- KeePassDX -->
|
||||
<item>{\"type\": \"action:launcher\", \"id\": \"settings\"}</item> <!-- Launcher Settings -->
|
||||
<item>{\"type\": \"action:launcher\", \"value\": \"settings\"}</item> <!-- Launcher Settings -->
|
||||
</string-array>
|
||||
|
||||
<!-- Time / Clock -->
|
||||
|
|
Loading…
Add table
Reference in a new issue