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 androidx.preference.PreferenceManager
import de.jrpie.android.launcher.actions.TorchManager
import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.preferences.LauncherPreferences
class Application : android.app.Application() {
var torchManager: TorchManager? = null
var customAppNames: HashMap<AppInfo, String> = HashMap()
override fun onCreate() {
super.onCreate()
@ -18,5 +20,13 @@ class Application : android.app.Application() {
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
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.sortBy { it.label.toString() }
loadList.sortBy { it.getCustomLabel(context).toString() }
appsList.clear()
appsList.addAll(loadList)
}

View file

@ -58,7 +58,7 @@ class AppAction(val appInfo: AppInfo) : Action {
}
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? {

View file

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

View file

@ -4,6 +4,9 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo
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.
@ -16,12 +19,36 @@ class DetailedAppInfo(
) {
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.getBadgedIcon(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 {
fun fromAppInfo(appInfo: AppInfo, context: Context): DetailedAppInfo? {
return appInfo.getLauncherActivityInfo(context)?.let { DetailedAppInfo(it) }

View file

@ -1,7 +1,15 @@
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.Set;
import java.util.stream.Collectors;
import de.jrpie.android.launcher.R;
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 = {
@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 = "hide_bound_apps", type = boolean.class, defaultValue = "false"),
}),
@PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = {
@ -96,6 +105,46 @@ public final class LauncherPreferences$Config {
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.graphics.Rect
import android.os.AsyncTask
import android.text.InputType
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import de.jrpie.android.launcher.R
@ -44,7 +47,7 @@ class AppsRecyclerAdapter(
private val intention: ListActivity.ListActivityIntention
= ListActivity.ListActivityIntention.VIEW,
private val forGesture: String? = "",
private var appFilter: AppFilter = AppFilter("")
private var appFilter: AppFilter = AppFilter(activity, "")
) :
RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() {
@ -69,7 +72,7 @@ class AppsRecyclerAdapter(
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
viewHolder.textView.text = appLabel
@ -146,7 +149,10 @@ class AppsRecyclerAdapter(
if (favorites.contains(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 {
Log.i("LAUNCHER", "Adding " + appInfo.app.serialize() + " to favorites.")
favorites.add(appInfo.app)
@ -181,6 +187,30 @@ class AppsRecyclerAdapter(
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
}
}

View file

@ -66,6 +66,7 @@ class ListFragmentApps : Fragment(), UIObject {
AppsRecyclerAdapter(
requireActivity(), binding.root, intention, forGesture,
appFilter = AppFilter(
requireContext(),
"",
favoritesVisibility = favoritesVisibility,
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" />
<item android:id="@+id/app_menu_hidden"
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"
android:title="@string/list_app_delete" />
</menu>

View file

@ -136,6 +136,7 @@
<string name="list_app_delete">Deinstallieren</string>
<string name="list_app_info">App Info</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_not_removed">Die App konnte nicht entfernt werden</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="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="dialog_rename_cancel">Abbrechen</string>
<string name="dialog_rename_ok">Ok</string>
<string name="dialog_rename_title">%1$s umbenennen</string>
</resources>

View file

@ -11,6 +11,7 @@
<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_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_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_hidden_add">Hide</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_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_device_admin">Use Device Admin</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>