implement #68: renaming of apps

This commit is contained in:
Josia Pietsch 2024-11-27 00:56:29 +01:00
parent b8ef2a07c2
commit fac3991f96
Signed by: jrpie
GPG key ID: E70B571D66986A2D
13 changed files with 157 additions and 8 deletions

View file

@ -4,10 +4,12 @@ import android.os.Build
import android.os.Build.VERSION_CODES import android.os.Build.VERSION_CODES
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import de.jrpie.android.launcher.actions.TorchManager import de.jrpie.android.launcher.actions.TorchManager
import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
class Application : android.app.Application() { class Application : android.app.Application() {
var torchManager: TorchManager? = null var torchManager: TorchManager? = null
var customAppNames: HashMap<AppInfo, String> = HashMap()
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -18,5 +20,13 @@ class Application : android.app.Application() {
val preferences = PreferenceManager.getDefaultSharedPreferences(this) val preferences = PreferenceManager.getDefaultSharedPreferences(this)
LauncherPreferences.init(preferences, this.resources) LauncherPreferences.init(preferences, this.resources)
customAppNames = LauncherPreferences.apps().customNames()
LauncherPreferences.getSharedPreferences()
.registerOnSharedPreferenceChangeListener { _, pref ->
if (pref == getString(R.string.settings_apps_custom_names_key)) {
customAppNames = LauncherPreferences.apps().customNames()
}
}
} }
} }

View file

@ -185,7 +185,7 @@ fun loadApps(packageManager: PackageManager, context: Context) {
loadList.add(detailedAppInfo) loadList.add(detailedAppInfo)
} }
} }
loadList.sortBy { it.label.toString() } loadList.sortBy { it.getCustomLabel(context).toString() }
appsList.clear() appsList.clear()
appsList.addAll(loadList) appsList.addAll(loadList)
} }

View file

@ -58,7 +58,7 @@ class AppAction(val appInfo: AppInfo) : Action {
} }
override fun label(context: Context): String { override fun label(context: Context): String {
return DetailedAppInfo.fromAppInfo(appInfo, context)?.label.toString() return DetailedAppInfo.fromAppInfo(appInfo, context)?.getCustomLabel(context).toString()
} }
override fun getIcon(context: Context): Drawable? { override fun getIcon(context: Context): Drawable? {

View file

@ -1,5 +1,6 @@
package de.jrpie.android.launcher.apps package de.jrpie.android.launcher.apps
import android.content.Context
import de.jrpie.android.launcher.actions.Action import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.AppAction import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.actions.Gesture
@ -8,12 +9,14 @@ import java.util.Locale
import kotlin.text.Regex.Companion.escape import kotlin.text.Regex.Companion.escape
class AppFilter( class AppFilter(
var context: Context,
var search: String, var search: String,
var favoritesVisibility: AppSetVisibility = AppSetVisibility.VISIBLE, var favoritesVisibility: AppSetVisibility = AppSetVisibility.VISIBLE,
var hiddenVisibility: AppSetVisibility = AppSetVisibility.HIDDEN, var hiddenVisibility: AppSetVisibility = AppSetVisibility.HIDDEN,
) { ) {
operator fun invoke(apps: List<DetailedAppInfo>): List<DetailedAppInfo> { operator fun invoke(apps: List<DetailedAppInfo>): List<DetailedAppInfo> {
var apps = apps var apps =
apps.sortedBy { app -> app.getCustomLabel(context).toString().lowercase(Locale.ROOT) }
val hidden = LauncherPreferences.apps().hidden() ?: setOf() val hidden = LauncherPreferences.apps().hidden() ?: setOf()
val favorites = LauncherPreferences.apps().favorites() ?: setOf() val favorites = LauncherPreferences.apps().favorites() ?: setOf()
@ -51,7 +54,7 @@ class AppFilter(
val appsSecondary: MutableList<DetailedAppInfo> = ArrayList() val appsSecondary: MutableList<DetailedAppInfo> = ArrayList()
val normalizedText: String = normalize(search) val normalizedText: String = normalize(search)
for (item in apps) { for (item in apps) {
val itemLabel: String = normalize(item.label.toString()) val itemLabel: String = normalize(item.getCustomLabel(context).toString())
if (itemLabel.startsWith(normalizedText)) { if (itemLabel.startsWith(normalizedText)) {
r.add(item) r.add(item)

View file

@ -4,6 +4,9 @@ import android.content.Context
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherActivityInfo
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.Log
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.preferences.LauncherPreferences
/** /**
* Stores information used to create [AppsRecyclerAdapter] rows. * Stores information used to create [AppsRecyclerAdapter] rows.
@ -16,12 +19,36 @@ class DetailedAppInfo(
) { ) {
constructor(activityInfo: LauncherActivityInfo) : this( constructor(activityInfo: LauncherActivityInfo) : this(
AppInfo(activityInfo.applicationInfo.packageName, activityInfo.name, activityInfo.user.hashCode()), AppInfo(
activityInfo.applicationInfo.packageName,
activityInfo.name,
activityInfo.user.hashCode()
),
activityInfo.label, activityInfo.label,
activityInfo.getBadgedIcon(0), activityInfo.getBadgedIcon(0),
activityInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) != 0 activityInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) != 0
) )
fun getCustomLabel(context: Context): CharSequence {
val map = (context.applicationContext as? Application)?.customAppNames ?: return label
return map[app] ?: label
}
fun setCustomLabel(label: CharSequence?) {
Log.i("Launcher", "Setting custom label for ${this.app} to ${label}.")
val map = LauncherPreferences.apps().customNames() ?: HashMap<AppInfo, String>()
if (label.isNullOrEmpty()) {
map.remove(app)
} else {
map[app] = label.toString()
}
LauncherPreferences.apps().customNames(map)
}
companion object { companion object {
fun fromAppInfo(appInfo: AppInfo, context: Context): DetailedAppInfo? { fun fromAppInfo(appInfo: AppInfo, context: Context): DetailedAppInfo? {
return appInfo.getLauncherActivityInfo(context)?.let { DetailedAppInfo(it) } return appInfo.getLauncherActivityInfo(context)?.let { DetailedAppInfo(it) }

View file

@ -1,7 +1,15 @@
package de.jrpie.android.launcher.preferences; package de.jrpie.android.launcher.preferences;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import de.jrpie.android.launcher.R; import de.jrpie.android.launcher.R;
import de.jrpie.android.launcher.apps.AppInfo; import de.jrpie.android.launcher.apps.AppInfo;
@ -28,6 +36,7 @@ import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSeriali
@PreferenceGroup(name = "apps", prefix = "settings_apps_", suffix = "_key", value = { @PreferenceGroup(name = "apps", prefix = "settings_apps_", suffix = "_key", value = {
@Preference(name = "favorites", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class), @Preference(name = "favorites", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class),
@Preference(name = "hidden", 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 = "hide_bound_apps", type = boolean.class, defaultValue = "false"), @Preference(name = "hide_bound_apps", type = boolean.class, defaultValue = "false"),
}), }),
@PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = { @PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = {
@ -96,6 +105,46 @@ public final class LauncherPreferences$Config {
return deserialized; return deserialized;
} }
}
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;
}
} }
} }

View file

@ -5,14 +5,17 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.os.AsyncTask import android.os.AsyncTask
import android.text.InputType
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
@ -44,7 +47,7 @@ class AppsRecyclerAdapter(
private val intention: ListActivity.ListActivityIntention private val intention: ListActivity.ListActivityIntention
= ListActivity.ListActivityIntention.VIEW, = ListActivity.ListActivityIntention.VIEW,
private val forGesture: String? = "", private val forGesture: String? = "",
private var appFilter: AppFilter = AppFilter("") private var appFilter: AppFilter = AppFilter(activity, "")
) : ) :
RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() { RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() {
@ -69,7 +72,7 @@ class AppsRecyclerAdapter(
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) { override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val appLabel = appsListDisplayed[i].label.toString() val appLabel = appsListDisplayed[i].getCustomLabel(activity).toString()
val appIcon = appsListDisplayed[i].icon val appIcon = appsListDisplayed[i].icon
viewHolder.textView.text = appLabel viewHolder.textView.text = appLabel
@ -146,7 +149,10 @@ class AppsRecyclerAdapter(
if (favorites.contains(appInfo.app)) { if (favorites.contains(appInfo.app)) {
favorites.remove(appInfo.app) favorites.remove(appInfo.app)
Log.i("LAUNCHER", "Removing " + appInfo.app.serialize() + " from favorites.") Log.i(
"LAUNCHER",
"Removing " + appInfo.app.serialize() + " from favorites."
)
} else { } else {
Log.i("LAUNCHER", "Adding " + appInfo.app.serialize() + " to favorites.") Log.i("LAUNCHER", "Adding " + appInfo.app.serialize() + " to favorites.")
favorites.add(appInfo.app) favorites.add(appInfo.app)
@ -181,6 +187,30 @@ class AppsRecyclerAdapter(
true true
} }
R.id.app_menu_rename -> {
val builder = AlertDialog.Builder(activity, R.style.AlertDialogCustom)
val title = activity.getString(R.string.dialog_rename_title, appInfo.label)
builder.setTitle(title)
builder.setView(R.layout.dialog_rename_app)
builder.setNegativeButton(R.string.dialog_rename_cancel) { d, _ -> d.cancel() }
builder.setPositiveButton(R.string.dialog_rename_ok) { d, _ ->
appInfo.setCustomLabel(
(d as? AlertDialog)
?.findViewById<EditText>(R.id.dialog_rename_app_edit_text)
?.text.toString()
)
}
val dialog = builder.create()
dialog.show()
val input = dialog.findViewById<EditText>(R.id.dialog_rename_app_edit_text)
input?.setText(appInfo.getCustomLabel(activity))
input?.hint = appInfo.label
true
}
else -> false else -> false
} }
} }

View file

@ -66,6 +66,7 @@ class ListFragmentApps : Fragment(), UIObject {
AppsRecyclerAdapter( AppsRecyclerAdapter(
requireActivity(), binding.root, intention, forGesture, requireActivity(), binding.root, intention, forGesture,
appFilter = AppFilter( appFilter = AppFilter(
requireContext(),
"", "",
favoritesVisibility = favoritesVisibility, favoritesVisibility = favoritesVisibility,
hiddenVisibility = hiddenVisibility hiddenVisibility = hiddenVisibility

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/AlertDialogCustom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/dialog_rename_app_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="LabelFor" />
</LinearLayout>

View file

@ -6,6 +6,8 @@
android:title="@string/list_app_favorite_add" /> android:title="@string/list_app_favorite_add" />
<item android:id="@+id/app_menu_hidden" <item android:id="@+id/app_menu_hidden"
android:title="@string/list_app_hidden_add" /> android:title="@string/list_app_hidden_add" />
<item android:id="@+id/app_menu_rename"
android:title="@string/list_app_rename" />
<item android:id="@+id/app_menu_delete" <item android:id="@+id/app_menu_delete"
android:title="@string/list_app_delete" /> android:title="@string/list_app_delete" />
</menu> </menu>

View file

@ -136,6 +136,7 @@
<string name="list_app_delete">Deinstallieren</string> <string name="list_app_delete">Deinstallieren</string>
<string name="list_app_info">App Info</string> <string name="list_app_info">App Info</string>
<string name="list_app_hidden_remove">Sichtbar machen</string> <string name="list_app_hidden_remove">Sichtbar machen</string>
<string name="list_app_rename">Umbenennen</string>
<string name="list_removed">Die App wurde entfernt</string> <string name="list_removed">Die App wurde entfernt</string>
<string name="list_not_removed">Die App konnte nicht entfernt werden</string> <string name="list_not_removed">Die App konnte nicht entfernt werden</string>
<string name="list_apps_search_hint">Anwendungen suchen</string> <string name="list_apps_search_hint">Anwendungen suchen</string>
@ -188,4 +189,7 @@
<string name="device_admin_description">Die Aktion \"Bildschirm sperren\" aktivieren</string> <string name="device_admin_description">Die Aktion \"Bildschirm sperren\" aktivieren</string>
<string name="alert_no_torch_found">Es wurde keine geeignete Kamera gefunden.</string> <string name="alert_no_torch_found">Es wurde keine geeignete Kamera gefunden.</string>
<string name="alert_torch_access_exception">Fehler: Kein Zugriff auf die Kamera möglich.</string> <string name="alert_torch_access_exception">Fehler: Kein Zugriff auf die Kamera möglich.</string>
<string name="dialog_rename_cancel">Abbrechen</string>
<string name="dialog_rename_ok">Ok</string>
<string name="dialog_rename_title">%1$s umbenennen</string>
</resources> </resources>

View file

@ -11,6 +11,7 @@
<string name="settings_internal_version_code_key" translatable="false">internal.version_code</string> <string name="settings_internal_version_code_key" translatable="false">internal.version_code</string>
<string name="settings_apps_favorites_key" translatable="false">apps.favorites</string> <string name="settings_apps_favorites_key" translatable="false">apps.favorites</string>
<string name="settings_apps_hidden_key" translatable="false">apps.hidden</string> <string name="settings_apps_hidden_key" translatable="false">apps.hidden</string>
<string name="settings_apps_custom_names_key" translatable="false">apps.custom_names</string>
<string name="settings_apps_hide_bound_apps_key" translatable="false">apps.hide_bound_apps</string> <string name="settings_apps_hide_bound_apps_key" translatable="false">apps.hide_bound_apps</string>
<string name="settings_general_choose_home_screen_key" translatable="false">general.select_launcher</string> <string name="settings_general_choose_home_screen_key" translatable="false">general.select_launcher</string>

View file

@ -191,6 +191,7 @@
<string name="list_app_favorite_remove">Remove from favorites</string> <string name="list_app_favorite_remove">Remove from favorites</string>
<string name="list_app_hidden_add">Hide</string> <string name="list_app_hidden_add">Hide</string>
<string name="list_app_hidden_remove">Show</string> <string name="list_app_hidden_remove">Show</string>
<string name="list_app_rename">Rename</string>
<string name="list_removed">Removed the selected application</string> <string name="list_removed">Removed the selected application</string>
<string name="list_not_removed">Unable to remove application</string> <string name="list_not_removed">Unable to remove application</string>
@ -283,4 +284,7 @@
<string name="screen_lock_method_use_accessibility">Use Accessibility Service</string> <string name="screen_lock_method_use_accessibility">Use Accessibility Service</string>
<string name="screen_lock_method_use_device_admin">Use Device Admin</string> <string name="screen_lock_method_use_device_admin">Use Device Admin</string>
<string name="settings_actions_lock_method">Choose method for locking the screen</string> <string name="settings_actions_lock_method">Choose method for locking the screen</string>
<string name="dialog_rename_cancel">Cancel</string>
<string name="dialog_rename_ok">Ok</string>
<string name="dialog_rename_title">Rename %1$s</string>
</resources> </resources>