diff --git a/app/build.gradle b/app/build.gradle index d851ef5..e977b66 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,9 +1,9 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' +apply plugin: 'kotlinx-serialization' android { - dataBinding { enabled = true } @@ -76,6 +76,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'com.google.android.material:material:1.12.0' + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") implementation "eu.jonahbauer:android-preference-annotations:1.1.2" annotationProcessor "eu.jonahbauer:android-preference-annotations:1.1.2" annotationProcessor "com.android.databinding:compiler:$android_plugin_version" diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt b/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt index 0912207..2d03061 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt @@ -8,61 +8,36 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.widget.Toast import de.jrpie.android.launcher.R -import de.jrpie.android.launcher.apps.AppInfo -import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER -import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.preferences.LauncherPreferences +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json -interface Action { + +@Serializable +sealed interface Action { fun invoke(context: Context, rect: Rect? = null): Boolean - fun bindToGesture(prefEditor: Editor, id: String) fun label(context: Context): String fun getIcon(context: Context): Drawable? fun isAvailable(context: Context): Boolean - fun writeToIntent(intent: Intent) + + fun bindToGesture(prefEditor: Editor, id: String) { + prefEditor.putString(id, Json.encodeToString(this)) + } + + fun writeToIntent(intent: Intent) { + intent.putExtra("action", Json.encodeToString(this)) + } companion object { - /** - * Get an action for a specific id. - * An id is of the form: - * - "launcher:${launcher_action_name}", see [LauncherAction] - * - "${package_name}", see [AppAction] - * - "${package_name}:${activity_name}", see [AppAction] - * - * @param id - * @param user a user id, ignored if the action is a [LauncherAction]. - * @param context used to complete [AppInfo] if possible - */ - private fun fromId(id: String, user: Int?, context: Context? = null): Action? { - if (id.isEmpty()) { - return null - } - if (LauncherAction.isOtherAction(id)) { - return LauncherAction.byId(id) - } - - val values = id.split(";") - - var info = AppInfo(values[0], values.getOrNull(1), user ?: INVALID_USER) - - // try to complete an incomplete AppInfo if a context is provided - if (context != null && (info.user == INVALID_USER || info.activityName == null)) { - info = DetailedAppInfo.fromAppInfo(info, context)?.app?:info - } - - return AppAction(info) - } fun forGesture(gesture: Gesture): Action? { val id = gesture.id val preferences = LauncherPreferences.getSharedPreferences() - val actionId = preferences.getString("$id.app", "")!! - var u: Int? = preferences.getInt("$id.user", INVALID_USER) - u = if (u == INVALID_USER) null else u - - return fromId(actionId, u) + val json = preferences.getString(id, "null")!! + return Json.decodeFromString(json) } fun resetToDefaultActions(context: Context) { @@ -72,11 +47,11 @@ interface Action { context.resources .getStringArray(gesture.defaultsResource) .filterNot { boundActions.contains(it) } - .map { Pair(it, fromId(it, null, context)) } - .firstOrNull { it.second?.isAvailable(context) ?: false } + .map { Pair(it, Json.decodeFromString(it)) } + .firstOrNull { it.second.isAvailable(context) } ?.apply { boundActions.add(first) - second?.bindToGesture(editor, gesture.id) + second.bindToGesture(editor, gesture.id) } } editor.apply() @@ -94,8 +69,7 @@ interface Action { fun clearActionForGesture(gesture: Gesture) { LauncherPreferences.getSharedPreferences().edit() - .putString(gesture.id + ".app", "") - .putInt(gesture.id + ".user", INVALID_USER) + .remove(gesture.id) .apply() } @@ -119,9 +93,8 @@ interface Action { } fun fromIntent(data: Intent): Action? { - val value = data.getStringExtra("action_id") ?: return null - val user = data.getIntExtra("user", INVALID_USER) - return fromId(value, user) + val json = data.getStringExtra("action") ?: return null + return Json.decodeFromString(json) } } } \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt index e3e5f33..2bb3780 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt @@ -4,7 +4,6 @@ import android.app.AlertDialog import android.app.Service import android.content.Context import android.content.Intent -import android.content.SharedPreferences import android.content.pm.LauncherApps import android.graphics.Rect import android.graphics.drawable.Drawable @@ -14,16 +13,20 @@ import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.ui.list.apps.openSettings +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable -class AppAction(val appInfo: AppInfo) : Action { +@Serializable +@SerialName("action:app") +class AppAction(val app: AppInfo) : Action { override fun invoke(context: Context, rect: Rect?): Boolean { - val packageName = appInfo.packageName.toString() - if (appInfo.user != INVALID_USER) { + val packageName = app.packageName + if (app.user != INVALID_USER) { val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps - appInfo.getLauncherActivityInfo(context)?.let { app -> - Log.i("Launcher", "Starting $appInfo") + app.getLauncherActivityInfo(context)?.let { app -> + Log.i("Launcher", "Starting ${this.app}") launcherApps.startMainActivity(app.componentName, app.user, rect, null) return true } @@ -44,7 +47,7 @@ class AppAction(val appInfo: AppInfo) : Action { .setTitle(context.getString(R.string.alert_cant_open_title)) .setMessage(context.getString(R.string.alert_cant_open_message)) .setPositiveButton(android.R.string.ok) { _, _ -> - appInfo.openSettings(context) + app.openSettings(context) } .setNegativeButton(android.R.string.cancel, null) .setIcon(android.R.drawable.ic_dialog_info) @@ -55,33 +58,15 @@ class AppAction(val appInfo: AppInfo) : Action { } override fun label(context: Context): String { - return DetailedAppInfo.fromAppInfo(appInfo, context)?.getCustomLabel(context).toString() + return DetailedAppInfo.fromAppInfo(app, context)?.getCustomLabel(context).toString() } override fun getIcon(context: Context): Drawable? { - return DetailedAppInfo.fromAppInfo(appInfo, context)?.icon + return DetailedAppInfo.fromAppInfo(app, context)?.icon } override fun isAvailable(context: Context): Boolean { // check if app is installed - return DetailedAppInfo.fromAppInfo(appInfo, context) != null - } - - override fun bindToGesture(editor: SharedPreferences.Editor, id: String) { - val u = appInfo.user - - // TODO: replace this by AppInfo#serialize (breaking change to SharedPreferences!) - var app = appInfo.packageName.toString() - if (appInfo.activityName != null) { - app += ";${appInfo.activityName}" - } - editor - .putString("$id.app", app) - .putInt("$id.user", u) - } - - override fun writeToIntent(intent: Intent) { - intent.putExtra("action_id", "${appInfo.packageName};${appInfo.activityName}") - intent.putExtra("user", appInfo.user) + return DetailedAppInfo.fromAppInfo(app, context) != null } } \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt index 6c23bf0..1362d09 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt @@ -2,7 +2,6 @@ package de.jrpie.android.launcher.actions import android.content.Context import android.content.Intent -import android.content.SharedPreferences.Editor import android.graphics.Rect import android.graphics.drawable.Drawable import android.media.AudioManager @@ -13,11 +12,20 @@ import android.widget.Toast import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.R import de.jrpie.android.launcher.apps.AppFilter -import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.settings.SettingsActivity +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +@Serializable(with = LauncherActionSerializer::class) +@SerialName("action:launcher") enum class LauncherAction( val id: String, val label: Int, @@ -25,68 +33,68 @@ enum class LauncherAction( val launch: (Context) -> Unit ) : Action { SETTINGS( - "launcher:settings", + "settings", R.string.list_other_settings, R.drawable.baseline_settings_24, ::openSettings ), CHOOSE( - "launcher:choose", + "choose", R.string.list_other_list, R.drawable.baseline_menu_24, ::openAppsList ), CHOOSE_FROM_FAVORITES( - "launcher:chooseFromFavorites", + "choose_from_favorites", R.string.list_other_list_favorites, R.drawable.baseline_favorite_24, { context -> openAppsList(context, true) } ), VOLUME_UP( - "launcher:volumeUp", + "volume_up", R.string.list_other_volume_up, R.drawable.baseline_volume_up_24, ::audioVolumeUp ), VOLUME_DOWN( - "launcher:volumeDown", + "volume_down", R.string.list_other_volume_down, R.drawable.baseline_volume_down_24, ::audioVolumeDown ), TRACK_NEXT( - "launcher:nextTrack", + "next_track", R.string.list_other_track_next, R.drawable.baseline_skip_next_24, ::audioNextTrack ), TRACK_PREV( - "launcher:previousTrack", + "previous_track", R.string.list_other_track_previous, R.drawable.baseline_skip_previous_24, ::audioPreviousTrack ), EXPAND_NOTIFICATIONS_PANEL( - "launcher:expandNotificationsPanel", + "expand_notifications_panel", R.string.list_other_expand_notifications_panel, R.drawable.baseline_notifications_24, ::expandNotificationsPanel ), EXPAND_SETTINGS_PANEL( - "launcher:expandSettingsPanel", + "expand_settings_panel", R.string.list_other_expand_settings_panel, R.drawable.baseline_settings_applications_24, ::expandSettingsPanel ), LOCK_SCREEN( - "launcher:lockScreen", + "lock_screen", R.string.list_other_lock_screen, R.drawable.baseline_lock_24px, { c -> LauncherPreferences.actions().lockMethod().lockOrEnable(c) } ), TORCH( - "launcher:toggleTorch", + "toggle_torch", R.string.list_other_torch, R.drawable.baseline_flashlight_on_24, ::toggleTorch ), - NOP("launcher:nop", R.string.list_other_nop, R.drawable.baseline_not_interested_24, {}); + NOP("nop", R.string.list_other_nop, R.drawable.baseline_not_interested_24, {}); override fun invoke(context: Context, rect: Rect?): Boolean { launch(context) @@ -101,16 +109,6 @@ enum class LauncherAction( return context.getDrawable(icon) } - override fun bindToGesture(editor: Editor, id: String) { - editor - .putString("$id.app", this.id) - .putInt("$id.user", INVALID_USER) - } - - override fun writeToIntent(intent: Intent) { - intent.putExtra("action_id", id) - } - override fun isAvailable(context: Context): Boolean { return true } @@ -119,10 +117,6 @@ enum class LauncherAction( fun byId(id: String): LauncherAction? { return entries.singleOrNull { it.id == id } } - - fun isOtherAction(id: String): Boolean { - return id.startsWith("launcher") - } } } @@ -252,4 +246,32 @@ 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. + */ +@Serializable +private class LauncherActionWrapper(val id: String) + +private class LauncherActionSerializer() : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor( + "action:launcher", + ) { + element("id", LauncherActionWrapper.serializer().descriptor) + } + override fun deserialize(decoder: Decoder): LauncherAction { + val wrapper = decoder.decodeSerializableValue(LauncherActionWrapper.serializer()) + return LauncherAction.byId(wrapper.id) ?: throw SerializationException() + } + + override fun serialize(encoder: Encoder, value: LauncherAction) { + encoder.encodeSerializableValue( + LauncherActionWrapper.serializer(), + LauncherActionWrapper(value.id) + ) + } + } \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt b/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt index 327fc9c..948249f 100644 --- a/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt +++ b/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt @@ -29,7 +29,7 @@ class AppFilter( if (LauncherPreferences.apps().hideBoundApps()) { val boundApps = Gesture.entries .filter(Gesture::isEnabled) - .mapNotNull { g -> (Action.forGesture(g) as? AppAction)?.appInfo } + .mapNotNull { g -> (Action.forGesture(g) as? AppAction)?.app } .toSet() apps = apps.filterNot { info -> boundApps.contains(info.app) } } diff --git a/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt b/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt index 1bf5bc8..21614f8 100644 --- a/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt +++ b/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt @@ -5,21 +5,19 @@ import android.content.Context import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherApps import de.jrpie.android.launcher.getUserFromId +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json /** * Represents an app installed on the users device. * Contains the minimal amount of data required to identify the app. */ -class AppInfo(val packageName: CharSequence, val activityName: CharSequence?, val user: Int = INVALID_USER) { - - // TODO: make activityName non nullable (breaking change to SharedPreferences!) +@Serializable +class AppInfo(val packageName: String, val activityName: String?, val user: Int = INVALID_USER) { fun serialize(): String { - val u = user - var ret = "$packageName;$u" - activityName?.let { ret += ";$activityName" } - - return ret + return Json.encodeToString(this) } override fun equals(other: Any?): Boolean { @@ -39,7 +37,7 @@ class AppInfo(val packageName: CharSequence, val activityName: CharSequence?, va ): LauncherActivityInfo? { val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps val userHandle = getUserFromId(user, context) - val activityList = launcherApps.getActivityList(packageName.toString(), userHandle) + val activityList = launcherApps.getActivityList(packageName, userHandle) return activityList.firstOrNull { app -> app.name == activityName } ?: activityList.firstOrNull() } @@ -53,11 +51,7 @@ class AppInfo(val packageName: CharSequence, val activityName: CharSequence?, va const val INVALID_USER = -1 fun deserialize(serialized: String): AppInfo { - val values = serialized.split(";") - val packageName = values[0] - val user = Integer.valueOf(values[1]) - val activityName = values.getOrNull(2) - return AppInfo(packageName, activityName, user) + return Json.decodeFromString(serialized) } } } \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java index 0ed22c9..866f0a1 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java @@ -107,6 +107,7 @@ public final class LauncherPreferences$Config { } } + // TODO migrate to version 2 public static class MapAppInfoStringSerializer implements PreferenceSerializer, Set> { @Override diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt index fb49ea2..21a448f 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt @@ -1,57 +1,24 @@ package de.jrpie.android.launcher.preferences import android.content.Context -import android.content.SharedPreferences import android.util.Log 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.theme.Background -import de.jrpie.android.launcher.preferences.theme.ColorTheme +import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown +import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1 import de.jrpie.android.launcher.ui.HomeActivity /* Current version of the structure of preferences. * Increase when breaking changes are introduced and write an appropriate case in * `migratePreferencesToNewVersion` */ -const val PREFERENCE_VERSION = 1 +const val PREFERENCE_VERSION = 2 const val UNKNOWN_PREFERENCE_VERSION = -1 private const val TAG = "Launcher - Preferences" -private fun migrateStringPreference( - oldPrefs: SharedPreferences, - newPreferences: SharedPreferences.Editor, - oldKey: String, - newKey: String, - default: String -) { - val s = oldPrefs.getString(oldKey, default) - newPreferences.putString(newKey, s) -} - -private fun migrateIntPreference( - oldPrefs: SharedPreferences, - newPreferences: SharedPreferences.Editor, - oldKey: String, - newKey: String, - default: Int -) { - val s = oldPrefs.getInt(oldKey, default) - newPreferences.putInt(newKey, s) -} - -private fun migrateBooleanPreference( - oldPrefs: SharedPreferences, - newPreferences: SharedPreferences.Editor, - oldKey: String, - newKey: String, - default: Boolean -) { - val s = oldPrefs.getBoolean(oldKey, default) - newPreferences.putBoolean(newKey, s) -} fun migratePreferencesToNewVersion(context: Context) { when (LauncherPreferences.internal().versionCode()) { @@ -60,356 +27,23 @@ fun migratePreferencesToNewVersion(context: Context) { } UNKNOWN_PREFERENCE_VERSION -> { /* still using the old preferences file */ - Log.i( - TAG, - "Unknown preference version, trying to restore preferences from old version." - ) + migratePreferencesFromVersionUnknown(context) - - val oldPrefs = context.getSharedPreferences( - "V3RYR4ND0MK3YCR4P", - Context.MODE_PRIVATE - ) - if (!oldPrefs.contains("startedBefore")) { - Log.i(TAG, "No old preferences found.") - return - } - val newPrefs = LauncherPreferences.getSharedPreferences().edit() - - migrateBooleanPreference( - oldPrefs, - newPrefs, - "startedBefore", - "internal.started_before", - false - ) - - migrateStringPreference( - oldPrefs, - newPrefs, - "action_volumeUpApp", - "action.volume_up.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_volumeUpApp_user", - "action.volume_up.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_volumeDownApp", - "action.volume_down.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_volumeDownApp_user", - "action.volume_down.user", - -1 - ) - migrateStringPreference(oldPrefs, newPrefs, "action_timeApp", "action.time.app", "") - migrateIntPreference(oldPrefs, newPrefs, "action_timeApp_user", "action.time.user", -1) - migrateStringPreference(oldPrefs, newPrefs, "action_dateApp", "action.date.app", "") - migrateIntPreference(oldPrefs, newPrefs, "action_dateApp_user", "action.date.user", -1) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_longClickApp", - "action.long_click.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_longClickApp_user", - "action.long_click.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_doubleClickApp", - "action.double_click.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_doubleClickApp_user", - "action.double_click.user", - -1 - ) - migrateStringPreference(oldPrefs, newPrefs, "action_upApp", "action.up.app", "") - migrateIntPreference(oldPrefs, newPrefs, "action_upApp_user", "action.up.user", -1) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_up_leftApp", - "action.up_left.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_up_leftApp_user", - "action.up_left.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_up_rightApp", - "action.up_right.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_up_rightApp_user", - "action.up_right.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_doubleUpApp", - "action.double_up.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_doubleUpApp_user", - "action.double_up.user", - -1 - ) - migrateStringPreference(oldPrefs, newPrefs, "action_downApp", "action.down.app", "") - migrateIntPreference(oldPrefs, newPrefs, "action_downApp_user", "action.down.user", -1) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_down_leftApp", - "action.down_left.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_down_leftApp_user", - "action.down_left.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_down_rightApp", - "action.down_right.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_down_rightApp_user", - "action.down_right.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_doubleDownApp", - "action.double_down.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_doubleDownApp_user", - "action.double_down.user", - -1 - ) - migrateStringPreference(oldPrefs, newPrefs, "action_leftApp", "action.left.app", "") - migrateIntPreference(oldPrefs, newPrefs, "action_leftApp_user", "action.left.user", -1) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_left_topApp", - "action.left_top.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_left_topApp_user", - "action.left_top.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_left_bottomApp", - "action.left_bottom.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_left_bottomApp_user", - "action.left_bottom.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_doubleLeftApp", - "action.double_left.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_doubleLeftApp_user", - "action.double_left.user", - -1 - ) - migrateStringPreference(oldPrefs, newPrefs, "action_rightApp", "action.right.app", "") - migrateIntPreference( - oldPrefs, - newPrefs, - "action_rightApp_user", - "action.right.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_right_topApp", - "action.right_top.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_right_topApp_user", - "action.right_top.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_right_bottomApp", - "action.right_bottom.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_right_bottomApp_user", - "action.right_bottom.user", - -1 - ) - migrateStringPreference( - oldPrefs, - newPrefs, - "action_doubleRightApp", - "action.double_right.app", - "" - ) - migrateIntPreference( - oldPrefs, - newPrefs, - "action_doubleRightApp_user", - "action.double_right.user", - -1 - ) - migrateBooleanPreference(oldPrefs, newPrefs, "timeVisible", "clock.time_visible", true) - migrateBooleanPreference(oldPrefs, newPrefs, "dateVisible", "clock.date_visible", true) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "dateLocalized", - "clock.date_localized", - false - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "dateTimeFlip", - "clock.date_time_flip", - false - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "disableTimeout", - "display.disable_timeout", - false - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "useFullScreen", - "display.use_full_screen", - true - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "enableDoubleActions", - "enabled_gestures.double_actions", - true - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "enableEdgeActions", - "enabled_gestures.edge_actions", - true - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "searchAutoLaunch", - "functionality.search_auto_launch", - true - ) - migrateBooleanPreference( - oldPrefs, - newPrefs, - "searchAutoKeyboard", - "functionality.search_auto_keyboard", - true - ) - - newPrefs.apply() - - when (oldPrefs.getString("theme", "finn")) { - "finn" -> { - LauncherPreferences.theme().colorTheme(ColorTheme.DEFAULT) - LauncherPreferences.theme().monochromeIcons(false) - LauncherPreferences.theme().background(Background.DIM) - } - - "dark" -> { - LauncherPreferences.theme().colorTheme(ColorTheme.DARK) - LauncherPreferences.theme().monochromeIcons(true) - LauncherPreferences.theme().background(Background.DIM) - } - } - - - LauncherPreferences.internal().versionCode(PREFERENCE_VERSION) Log.i(TAG, "migration of preferences complete.") - - // show the new tutorial - // context.startActivity(Intent(context, TutorialActivity::class.java)) + } + 1 -> { + migratePreferencesFromVersion1() + Log.i(TAG, "migration of preferences complete.") } - else -> {} + else -> { + Log.w( + TAG, + "Shared preferences were written by a newer version of the app (${ + LauncherPreferences.internal().versionCode() + })!" + ) + } } } diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/MigrationFrom1.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/MigrationFrom1.kt new file mode 100644 index 0000000..c6c143d --- /dev/null +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/MigrationFrom1.kt @@ -0,0 +1,94 @@ +package de.jrpie.android.launcher.preferences.legacy + +import de.jrpie.android.launcher.actions.Action +import de.jrpie.android.launcher.actions.AppAction +import de.jrpie.android.launcher.actions.Gesture +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 kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +val oldLauncherActionIds: Map = + mapOf( + Pair("launcher:settings", LauncherAction.SETTINGS), + Pair("launcher:choose", LauncherAction.CHOOSE), + Pair("launcher:chooseFromFavorites", LauncherAction.CHOOSE_FROM_FAVORITES), + Pair("launcher:volumeUp", LauncherAction.VOLUME_UP), + Pair("launcher:volumeDown", LauncherAction.VOLUME_DOWN), + Pair("launcher:nextTrack", LauncherAction.TRACK_NEXT), + Pair("launcher:previousTrack", LauncherAction.TRACK_PREV), + Pair("launcher:expandNotificationsPanel", LauncherAction.EXPAND_NOTIFICATIONS_PANEL), + Pair("launcher:expandSettingsPanel", LauncherAction.EXPAND_SETTINGS_PANEL), + Pair("launcher:lockScreen", LauncherAction.LOCK_SCREEN), + Pair("launcher:toggleTorch", LauncherAction.TORCH), + Pair("launcher:nop", LauncherAction.NOP), + ) + +private fun AppInfo.Companion.legacyDeserialize(serialized: String): AppInfo { + val values = serialized.split(";") + val packageName = values[0] + val user = Integer.valueOf(values[1]) + val activityName = values.getOrNull(2) ?: "" // TODO + return AppInfo(packageName, activityName, user) +} + +/** + * Get an action for a specific id. + * An id is of the form: + * - "launcher:${launcher_action_name}", see [LauncherAction] + * - "${package_name}", see [AppAction] + * - "${package_name}:${activity_name}", see [AppAction] + * + * @param id + * @param user a user id, ignored if the action is a [LauncherAction]. + */ +private fun Action.Companion.fromId(id: String, user: Int?): Action? { + if (id.isEmpty()) { + return null + } + oldLauncherActionIds[id]?.let { return it } + + val values = id.split(";") + + return AppAction( + AppInfo( + values[0], values.getOrNull(1) ?: "", user ?: INVALID_USER + ) + ) +} + +private fun Action.Companion.legacyFromPreference(id: String): Action? { + val preferences = LauncherPreferences.getSharedPreferences() + val actionId = preferences.getString("$id.app", "")!! + var u: Int? = preferences.getInt( + "$id.user", + AppInfo.INVALID_USER + ) + u = if (u == AppInfo.INVALID_USER) null else u + + return Action.fromId(actionId, u) +} + +private fun migrateAppInfoSet(key: String) { + (LauncherPreferences.getSharedPreferences().getStringSet(key, setOf()) ?: return) + .map(AppInfo.Companion::legacyDeserialize) + .map(AppInfo::serialize) + .toSet() + .let { LauncherPreferences.getSharedPreferences().edit().putStringSet(key, it).apply() } +} + +private fun migrateAction(key: String) { + Action.legacyFromPreference(key)?.let { action -> + LauncherPreferences.getSharedPreferences().edit() + .putString(key, Json.encodeToString(action)).apply() + } + +} + +fun migratePreferencesFromVersion1() { + Gesture.entries.forEach { g -> migrateAction(g.id) } + migrateAppInfoSet(LauncherPreferences.apps().keys().hidden()) + migrateAppInfoSet(LauncherPreferences.apps().keys().favorites()) +} \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/MigrationFromUnknown.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/MigrationFromUnknown.kt new file mode 100644 index 0000000..81c754c --- /dev/null +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/MigrationFromUnknown.kt @@ -0,0 +1,390 @@ +package de.jrpie.android.launcher.preferences.legacy + +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.theme.Background +import de.jrpie.android.launcher.preferences.theme.ColorTheme + +private fun migrateStringPreference( + oldPrefs: SharedPreferences, + newPreferences: SharedPreferences.Editor, + oldKey: String, + newKey: String, + default: String +) { + val s = oldPrefs.getString(oldKey, default) + newPreferences.putString(newKey, s) +} + +private fun migrateIntPreference( + oldPrefs: SharedPreferences, + newPreferences: SharedPreferences.Editor, + oldKey: String, + newKey: String, + default: Int +) { + val s = oldPrefs.getInt(oldKey, default) + newPreferences.putInt(newKey, s) +} + +private fun migrateBooleanPreference( + oldPrefs: SharedPreferences, + newPreferences: SharedPreferences.Editor, + oldKey: String, + newKey: String, + default: Boolean +) { + val s = oldPrefs.getBoolean(oldKey, default) + newPreferences.putBoolean(newKey, s) +} + +private const val TAG = "Preferences ? -> 1" + +fun migratePreferencesFromVersionUnknown(context: Context) { + Log.i( + TAG, + "Unknown preference version, trying to restore preferences from old version." + ) + + val oldPrefs = context.getSharedPreferences( + "V3RYR4ND0MK3YCR4P", + Context.MODE_PRIVATE + ) + if (!oldPrefs.contains("startedBefore")) { + Log.i(TAG, "No old preferences found. Probably this is a fresh installation.") + return + } + + val newPrefs = LauncherPreferences.getSharedPreferences().edit() + + migrateBooleanPreference( + oldPrefs, + newPrefs, + "startedBefore", + "internal.started_before", + false + ) + + migrateStringPreference( + oldPrefs, + newPrefs, + "action_volumeUpApp", + "action.volume_up.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_volumeUpApp_user", + "action.volume_up.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_volumeDownApp", + "action.volume_down.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_volumeDownApp_user", + "action.volume_down.user", + -1 + ) + migrateStringPreference(oldPrefs, newPrefs, "action_timeApp", "action.time.app", "") + migrateIntPreference(oldPrefs, newPrefs, "action_timeApp_user", "action.time.user", -1) + migrateStringPreference(oldPrefs, newPrefs, "action_dateApp", "action.date.app", "") + migrateIntPreference(oldPrefs, newPrefs, "action_dateApp_user", "action.date.user", -1) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_longClickApp", + "action.long_click.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_longClickApp_user", + "action.long_click.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_doubleClickApp", + "action.double_click.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_doubleClickApp_user", + "action.double_click.user", + -1 + ) + migrateStringPreference(oldPrefs, newPrefs, "action_upApp", "action.up.app", "") + migrateIntPreference(oldPrefs, newPrefs, "action_upApp_user", "action.up.user", -1) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_up_leftApp", + "action.up_left.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_up_leftApp_user", + "action.up_left.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_up_rightApp", + "action.up_right.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_up_rightApp_user", + "action.up_right.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_doubleUpApp", + "action.double_up.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_doubleUpApp_user", + "action.double_up.user", + -1 + ) + migrateStringPreference(oldPrefs, newPrefs, "action_downApp", "action.down.app", "") + migrateIntPreference(oldPrefs, newPrefs, "action_downApp_user", "action.down.user", -1) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_down_leftApp", + "action.down_left.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_down_leftApp_user", + "action.down_left.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_down_rightApp", + "action.down_right.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_down_rightApp_user", + "action.down_right.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_doubleDownApp", + "action.double_down.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_doubleDownApp_user", + "action.double_down.user", + -1 + ) + migrateStringPreference(oldPrefs, newPrefs, "action_leftApp", "action.left.app", "") + migrateIntPreference(oldPrefs, newPrefs, "action_leftApp_user", "action.left.user", -1) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_left_topApp", + "action.left_top.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_left_topApp_user", + "action.left_top.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_left_bottomApp", + "action.left_bottom.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_left_bottomApp_user", + "action.left_bottom.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_doubleLeftApp", + "action.double_left.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_doubleLeftApp_user", + "action.double_left.user", + -1 + ) + migrateStringPreference(oldPrefs, newPrefs, "action_rightApp", "action.right.app", "") + migrateIntPreference( + oldPrefs, + newPrefs, + "action_rightApp_user", + "action.right.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_right_topApp", + "action.right_top.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_right_topApp_user", + "action.right_top.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_right_bottomApp", + "action.right_bottom.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_right_bottomApp_user", + "action.right_bottom.user", + -1 + ) + migrateStringPreference( + oldPrefs, + newPrefs, + "action_doubleRightApp", + "action.double_right.app", + "" + ) + migrateIntPreference( + oldPrefs, + newPrefs, + "action_doubleRightApp_user", + "action.double_right.user", + -1 + ) + migrateBooleanPreference(oldPrefs, newPrefs, "timeVisible", "clock.time_visible", true) + migrateBooleanPreference(oldPrefs, newPrefs, "dateVisible", "clock.date_visible", true) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "dateLocalized", + "clock.date_localized", + false + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "dateTimeFlip", + "clock.date_time_flip", + false + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "disableTimeout", + "display.disable_timeout", + false + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "useFullScreen", + "display.use_full_screen", + true + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "enableDoubleActions", + "enabled_gestures.double_actions", + true + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "enableEdgeActions", + "enabled_gestures.edge_actions", + true + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "searchAutoLaunch", + "functionality.search_auto_launch", + true + ) + migrateBooleanPreference( + oldPrefs, + newPrefs, + "searchAutoKeyboard", + "functionality.search_auto_keyboard", + true + ) + + newPrefs.apply() + + when (oldPrefs.getString("theme", "finn")) { + "finn" -> { + LauncherPreferences.theme().colorTheme(ColorTheme.DEFAULT) + LauncherPreferences.theme().monochromeIcons(false) + LauncherPreferences.theme().background(Background.DIM) + } + + "dark" -> { + LauncherPreferences.theme().colorTheme(ColorTheme.DARK) + LauncherPreferences.theme().monochromeIcons(true) + LauncherPreferences.theme().background(Background.DIM) + } + } + LauncherPreferences.internal().versionCode(1) + Log.i(TAG, "migrated preferences to version 1.") + + migratePreferencesFromVersion1() +} \ No newline at end of file diff --git a/app/src/main/res/values/defaults.xml b/app/src/main/res/values/defaults.xml index e532965..fb71475 100644 --- a/app/src/main/res/values/defaults.xml +++ b/app/src/main/res/values/defaults.xml @@ -5,145 +5,145 @@ - launcher:choose + {\"type\": \"action:launcher\", \"id\": \"choose\"} - launcher:chooseFromFavorites + {\"type\": \"action:launcher\", \"id\": \"choose_from_favorites\"} - app.organicmaps - com.graphhopper.maps - net.osmand.plus - com.google.android.apps.maps + {\"type\": \"action:app\", \"app\": {\"packageName\": \"app.organicmaps\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.graphhopper.maps\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"net.osmand.plus\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.google.android.apps.maps\", \"activityName\": null}} - de.schildbach.oeffi - de.hafas.android.db + {\"type\": \"action:app\", \"app\": {\"packageName\": \"de.schildbach.oeffi\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"de.hafas.android.db\", \"activityName\": null}} - org.mozilla.firefox - org.mozilla.fennec - org.mozilla.fennec_fdroid - com.brave.browser - com.duckduckgo.mobile.android - com.sec.android.app.sbrowser - com.android.chrome + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.mozilla.firefox\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.mozilla.fennec\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.mozilla.fennec_fdroid\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.brave.browser\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.duckduckgo.mobile.android\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.sec.android.app.sbrowser\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.android.chrome\", \"activityName\": null}} - com.bnyro.translate - com.deepl.mobiletranslator - com.google.android.apps.translate - com.microsoft.translator - translate.speech.text.translation.voicetranslator + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.bnyro.translate\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.deepl.mobiletranslator\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.google.android.apps.translate\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.microsoft.translator\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"translate.speech.text.translation.voicetranslator\", \"activityName\": null}} - org.fdroid.fdroid - org.fdroid.basic - dev.imranr.obtainium - dev.imranr.obtainium.fdroid + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.fdroid.fdroid\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.fdroid.basic\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"dev.imranr.obtainium\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"dev.imranr.obtainium.fdroid\", \"activityName\": null}} - org.torproject.torbrowser + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.torproject.torbrowser\", \"activityName\": null}} - net.thunderbird.android - com.fsck.k9 - de.web.mobile.android.mail - com.samsung.android.email.provider - com.google.android.gm + {\"type\": \"action:app\", \"app\": {\"packageName\": \"net.thunderbird.android\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.fsck.k9\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"de.web.mobile.android.mail\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.samsung.android.email.provider\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.google.android.gm\", \"activityName\": null}} - com.android.calculator2 - com.sec.android.app.popupcalculator - org.mian.gitnex - com.github.android + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.android.calculator2\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.sec.android.app.popupcalculator\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.mian.gitnex\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.github.android\", \"activityName\": null}} - com.android.documentsui - com.android.google.documentsui + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.android.documentsui\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.android.google.documentsui\", \"activityName\": null}} - be.chvp.nanoledger - info.tangential.cone + {\"type\": \"action:app\", \"app\": {\"packageName\": \"be.chvp.nanoledger\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"info.tangential.cone\", \"activityName\": null}} - de.spiritcroc.riotx - io.element.android.x - im.vector.app - org.thoughtcrime.securesms - org.briarproject.briar.android - eu.siacs.conversations - ch.threema.app.libre - com.android.messaging - com.google.android.apps.messaging - com.samsung.android.messaging - com.whatsapp - org.telegram.messenger - com.discord + {\"type\": \"action:app\", \"app\": {\"packageName\": \"de.spiritcroc.riotx\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"io.element.android.x\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"im.vector.app\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.thoughtcrime.securesms\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.briarproject.briar.android\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"eu.siacs.conversations\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"ch.threema.app.libre\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.android.messaging\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.google.android.apps.messaging\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.samsung.android.messaging\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.whatsapp\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.telegram.messenger\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.discord\", \"activityName\": null}} - launcher:volumeUp + {\"type\": \"action:launcher\", \"id\": \"volume_up\"} - launcher:volumeDown + {\"type\": \"action:launcher\", \"id\": \"volume_down\"} - it.niedermann.owncloud.notes - com.samsung.android.app.notes - com.sec.android.widgetapp.diotek.smemo - launcher:lockScreen + {\"type\": \"action:app\", \"app\": {\"packageName\": \"it.niedermann.owncloud.notes\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.samsung.android.app.notes\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.sec.android.widgetapp.diotek.smemo\", \"activityName\": null}} + {\"type\": \"action:launcher\", \"id\": \"lock_screen\"} - com.beemdevelopment.aegis - org.fedorahosted.freeotp - proton.android.pass.fdroid - com.kunzisoft.keepass.libre - launcher:settings + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.beemdevelopment.aegis\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.fedorahosted.freeotp\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"proton.android.pass.fdroid\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.kunzisoft.keepass.libre\", \"activityName\": null}} + {\"type\": \"action:launcher\", \"id\": \"settings\"} - com.android.deskclock - com.google.android.deskclock - com.sec.android.app.clockpackage + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.android.deskclock\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.google.android.deskclock\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.sec.android.app.clockpackage\", \"activityName\": null}} - org.lineageos.etar - ws.xsoh.etar - com.google.android.calendar - com.samsung.android.calendar + {\"type\": \"action:app\", \"app\": {\"packageName\": \"org.lineageos.etar\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"ws.xsoh.etar\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.google.android.calendar\", \"activityName\": null}} + {\"type\": \"action:app\", \"app\": {\"packageName\": \"com.samsung.android.calendar\", \"activityName\": null}} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 06e2c56..7c5b9d3 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,8 @@ buildscript { classpath 'com.android.tools.build:gradle:8.7.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.android.tools.build:gradle:$android_plugin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files