Compare commits

...

11 commits

Author SHA1 Message Date
Hendika N.
03f9b66e79
Merge 941b06b258 into 3b2dca9af9 2025-03-11 22:38:57 +01:00
3b2dca9af9
merge translation update
Some checks are pending
Android CI / build (push) Waiting to run
2025-03-11 22:38:34 +01:00
1b12032750
add FUNDING.yml
Some checks are pending
Android CI / build (push) Waiting to run
2025-03-11 15:58:07 +01:00
55a54fb9a5
implement #45: show pinned shortcuts in app list
Some checks failed
Android CI / build (push) Has been cancelled
2025-03-05 13:04:15 +01:00
Vossa Excelencia
e39ff62613 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (253 of 253 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/pt_BR/
2025-03-05 00:07:17 +00:00
9fe1a37ed6
implement #45: show pinned shortcuts in app list
Some checks are pending
Android CI / build (push) Waiting to run
2025-03-05 00:31:43 +01:00
1f825d6f00
implement #113: option to reverse app list
Some checks are pending
Android CI / build (push) Waiting to run
2025-03-04 16:49:29 +01:00
5ea03d39fa
fix blurred text in dialogs
Some checks are pending
Android CI / build (push) Waiting to run
2025-03-03 21:26:39 +01:00
ae119ac4ce
remove unused code 2025-03-03 21:23:16 +01:00
941b06b258
minor reformatting 2025-02-22 03:03:52 +01:00
Hendika N
9935386ad8 Add option to hide navigation bar on home screen 2025-02-22 07:35:28 +07:00
32 changed files with 579 additions and 160 deletions

4
.github/FUNDING.yml vendored
View file

@ -1,3 +1,3 @@
# How you can support finnmglas/Launcher # How you can support jrpie/Launcher
custom: sponsor.finnmglas.com custom: https://s.jrpie.de/launcher-donate

View file

@ -15,15 +15,15 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
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.apps.AbstractAppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.AbstractDetailedAppInfo
import de.jrpie.android.launcher.apps.isPrivateSpaceLocked import de.jrpie.android.launcher.apps.isPrivateSpaceLocked
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.migratePreferencesToNewVersion import de.jrpie.android.launcher.preferences.migratePreferencesToNewVersion
import de.jrpie.android.launcher.preferences.resetPreferences import de.jrpie.android.launcher.preferences.resetPreferences
class Application : android.app.Application() { class Application : android.app.Application() {
val apps = MutableLiveData<List<DetailedAppInfo>>() val apps = MutableLiveData<List<AbstractDetailedAppInfo>>()
val privateSpaceLocked = MutableLiveData<Boolean>() val privateSpaceLocked = MutableLiveData<Boolean>()
private val profileAvailabilityBroadcastReceiver = object : BroadcastReceiver() { private val profileAvailabilityBroadcastReceiver = object : BroadcastReceiver() {
@ -82,10 +82,12 @@ class Application : android.app.Application() {
} }
var torchManager: TorchManager? = null var torchManager: TorchManager? = null
private var customAppNames: HashMap<AppInfo, String>? = null private var customAppNames: HashMap<AbstractAppInfo, String>? = null
private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, pref -> private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, pref ->
if (pref == getString(R.string.settings_apps_custom_names_key)) { if (pref == getString(R.string.settings_apps_custom_names_key)) {
customAppNames = LauncherPreferences.apps().customNames() customAppNames = LauncherPreferences.apps().customNames()
} else if (pref == LauncherPreferences.apps().keys().pinnedShortcuts()) {
loadApps()
} }
} }
@ -143,7 +145,7 @@ class Application : android.app.Application() {
loadApps() loadApps()
} }
fun getCustomAppNames(): HashMap<AppInfo, String> { fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
return (customAppNames ?: LauncherPreferences.apps().customNames() ?: HashMap()) return (customAppNames ?: LauncherPreferences.apps().customNames() ?: HashMap())
.also { customAppNames = it } .also { customAppNames = it }
} }

View file

@ -24,9 +24,12 @@ import androidx.annotation.RequiresApi
import de.jrpie.android.launcher.actions.Action import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.ShortcutAction import de.jrpie.android.launcher.actions.ShortcutAction
import de.jrpie.android.launcher.actions.shortcuts.PinnedShortcutInfo import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.apps.AbstractDetailedAppInfo
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.apps.DetailedPinnedShortcutInfo
import de.jrpie.android.launcher.apps.PinnedShortcutInfo
import de.jrpie.android.launcher.apps.getPrivateSpaceUser import de.jrpie.android.launcher.apps.getPrivateSpaceUser
import de.jrpie.android.launcher.apps.isPrivateSpaceSupported import de.jrpie.android.launcher.apps.isPrivateSpaceSupported
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
@ -99,9 +102,10 @@ fun removeUnusedShortcuts(context: Context) {
} }
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager
val boundActions: Set<PinnedShortcutInfo> = val boundActions: MutableSet<PinnedShortcutInfo> =
Gesture.entries.mapNotNull { Action.forGesture(it) as? ShortcutAction }.map { it.shortcut } Gesture.entries.mapNotNull { Action.forGesture(it) as? ShortcutAction }.map { it.shortcut }
.toSet() .toMutableSet()
LauncherPreferences.apps().pinnedShortcuts()?.let { boundActions.addAll(it) }
try { try {
userManager.userProfiles.filter { !userManager.isQuietModeEnabled(it) }.forEach { profile -> userManager.userProfiles.filter { !userManager.isQuietModeEnabled(it) }.forEach { profile ->
getShortcuts(profile)?.groupBy { it.`package` }?.forEach { (p, shortcuts) -> getShortcuts(profile)?.groupBy { it.`package` }?.forEach { (p, shortcuts) ->
@ -135,9 +139,12 @@ fun openTutorial(context: Context) {
/** /**
* Load all apps. * Load all apps.
*/ */
fun getApps(packageManager: PackageManager, context: Context): MutableList<DetailedAppInfo> { fun getApps(
val start = System.currentTimeMillis() packageManager: PackageManager,
val loadList = mutableListOf<DetailedAppInfo>() context: Context
): MutableList<AbstractDetailedAppInfo> {
var start = System.currentTimeMillis()
val loadList = mutableListOf<AbstractDetailedAppInfo>()
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager
@ -174,7 +181,7 @@ fun getApps(packageManager: PackageManager, context: Context): MutableList<Detai
i.addCategory(Intent.CATEGORY_LAUNCHER) i.addCategory(Intent.CATEGORY_LAUNCHER)
val allApps = packageManager.queryIntentActivities(i, 0) val allApps = packageManager.queryIntentActivities(i, 0)
for (ri in allApps) { for (ri in allApps) {
val app = AppInfo(ri.activityInfo.packageName, null, AppInfo.INVALID_USER) val app = AppInfo(ri.activityInfo.packageName, null, INVALID_USER)
val detailedAppInfo = DetailedAppInfo( val detailedAppInfo = DetailedAppInfo(
app, app,
ri.loadLabel(packageManager), ri.loadLabel(packageManager),
@ -186,8 +193,18 @@ fun getApps(packageManager: PackageManager, context: Context): MutableList<Detai
} }
loadList.sortBy { it.getCustomLabel(context).toString() } loadList.sortBy { it.getCustomLabel(context).toString() }
val end = System.currentTimeMillis() var end = System.currentTimeMillis()
Log.i(LOG_TAG, "${loadList.size} apps loaded (${end - start}ms)") Log.i(LOG_TAG, "${loadList.size} apps loaded (${end - start}ms)")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
start = System.currentTimeMillis()
LauncherPreferences.apps().pinnedShortcuts()
?.mapNotNull { DetailedPinnedShortcutInfo.fromPinnedShortcutInfo(it, context) }
?.let {
end = System.currentTimeMillis()
Log.i(LOG_TAG, "${it.size} shortcuts loaded (${end - start}ms)")
loadList.addAll(it)
}
}
return loadList return loadList
} }

View file

@ -11,7 +11,7 @@ import android.graphics.drawable.Drawable
import android.util.Log import android.util.Log
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
import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.ui.list.apps.openSettings import de.jrpie.android.launcher.ui.list.apps.openSettings
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
@ -67,7 +67,7 @@ class AppAction(val app: AppInfo) : Action {
} }
override fun getIcon(context: Context): Drawable? { override fun getIcon(context: Context): Drawable? {
return DetailedAppInfo.fromAppInfo(app, context)?.icon return DetailedAppInfo.fromAppInfo(app, context)?.getIcon(context)
} }
override fun isAvailable(context: Context): Boolean { override fun isAvailable(context: Context): Boolean {

View file

@ -6,7 +6,7 @@ import android.content.pm.LauncherApps
import android.graphics.Rect import android.graphics.Rect
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import de.jrpie.android.launcher.actions.shortcuts.PinnedShortcutInfo import de.jrpie.android.launcher.apps.PinnedShortcutInfo
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View file

@ -0,0 +1,22 @@
package de.jrpie.android.launcher.apps
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
/**
* This interface is implemented by [AppInfo] and [PinnedShortcutInfo].
*/
@Serializable
sealed interface AbstractAppInfo {
fun serialize(): String {
return Json.encodeToString(this)
}
companion object {
const val INVALID_USER = -1
fun deserialize(serialized: String): AbstractAppInfo {
return Json.decodeFromString(serialized)
}
}
}

View file

@ -0,0 +1,42 @@
package de.jrpie.android.launcher.apps
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.UserHandle
import android.util.Log
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.preferences.LauncherPreferences
/**
* This interface is implemented by [DetailedAppInfo] and [DetailedPinnedShortcutInfo]
*/
sealed interface AbstractDetailedAppInfo {
fun getRawInfo(): AbstractAppInfo
fun getLabel(): String
fun getIcon(context: Context): Drawable
fun getUser(context: Context): UserHandle
fun isPrivate(): Boolean
fun isRemovable(): Boolean
fun getAction(): Action
fun getCustomLabel(context: Context): String {
val map = (context.applicationContext as? Application)?.getCustomAppNames()
return map?.get(getRawInfo()) ?: getLabel()
}
fun setCustomLabel(label: CharSequence?) {
Log.i("Launcher", "Setting custom label for ${this.getRawInfo()} to ${label}.")
val map = LauncherPreferences.apps().customNames() ?: HashMap<AbstractAppInfo, String>()
if (label.isNullOrEmpty()) {
map.remove(getRawInfo())
} else {
map[getRawInfo()] = label.toString()
}
LauncherPreferences.apps().customNames(map)
}
}

View file

@ -6,6 +6,7 @@ import android.os.Build
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
import de.jrpie.android.launcher.actions.ShortcutAction
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import java.util.Locale import java.util.Locale
import kotlin.text.Regex.Companion.escape import kotlin.text.Regex.Companion.escape
@ -18,13 +19,14 @@ class AppFilter(
var privateSpaceVisibility: AppSetVisibility = AppSetVisibility.VISIBLE var privateSpaceVisibility: AppSetVisibility = AppSetVisibility.VISIBLE
) { ) {
operator fun invoke(apps: List<DetailedAppInfo>): List<DetailedAppInfo> { operator fun invoke(apps: List<AbstractDetailedAppInfo>): List<AbstractDetailedAppInfo> {
var apps = var apps =
apps.sortedBy { app -> app.getCustomLabel(context).toString().lowercase(Locale.ROOT) } apps.sortedBy { app -> app.getCustomLabel(context).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()
val private = apps.filter { it.isPrivateSpaceApp }.map { it.app }.toSet() val private = apps.filter { it.isPrivate() }
.map { it.getRawInfo() }.toSet()
apps = apps.filter { info -> apps = apps.filter { info ->
favoritesVisibility.predicate(favorites, info) favoritesVisibility.predicate(favorites, info)
@ -35,9 +37,13 @@ class AppFilter(
if (LauncherPreferences.apps().hideBoundApps()) { if (LauncherPreferences.apps().hideBoundApps()) {
val boundApps = Gesture.entries val boundApps = Gesture.entries
.filter(Gesture::isEnabled) .filter(Gesture::isEnabled)
.mapNotNull { g -> (Action.forGesture(g) as? AppAction)?.app } .mapNotNull { g -> Action.forGesture(g) }
.mapNotNull {
(it as? AppAction)?.app
?: (it as? ShortcutAction)?.shortcut
}
.toSet() .toSet()
apps = apps.filterNot { info -> boundApps.contains(info.app) } apps = apps.filterNot { info -> boundApps.contains(info.getRawInfo()) }
} }
// normalize text for search // normalize text for search
@ -57,11 +63,11 @@ class AppFilter(
if (query.isEmpty()) { if (query.isEmpty()) {
return apps return apps
} else { } else {
val r: MutableList<DetailedAppInfo> = ArrayList() val r: MutableList<AbstractDetailedAppInfo> = ArrayList()
val appsSecondary: MutableList<DetailedAppInfo> = ArrayList() val appsSecondary: MutableList<AbstractDetailedAppInfo> = ArrayList()
val normalizedQuery: String = normalize(query) val normalizedQuery: String = normalize(query)
for (item in apps) { for (item in apps) {
val itemLabel: String = normalize(item.getCustomLabel(context).toString()) val itemLabel: String = normalize(item.getCustomLabel(context))
if (itemLabel.startsWith(normalizedQuery)) { if (itemLabel.startsWith(normalizedQuery)) {
r.add(item) r.add(item)
@ -77,11 +83,11 @@ class AppFilter(
companion object { companion object {
enum class AppSetVisibility( enum class AppSetVisibility(
val predicate: (set: Set<AppInfo>, DetailedAppInfo) -> Boolean val predicate: (set: Set<AbstractAppInfo>, AbstractDetailedAppInfo) -> Boolean
) { ) {
VISIBLE({ _, _ -> true }), VISIBLE({ _, _ -> true }),
HIDDEN({ set, appInfo -> !set.contains(appInfo.app) }), HIDDEN({ set, appInfo -> !set.contains(appInfo.getRawInfo()) }),
EXCLUSIVE({ set, appInfo -> set.contains(appInfo.app) }), EXCLUSIVE({ set, appInfo -> set.contains(appInfo.getRawInfo()) }),
; ;
} }

View file

@ -4,21 +4,18 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps import android.content.pm.LauncherApps
import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.getUserFromId import de.jrpie.android.launcher.getUserFromId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
/** /**
* Represents an app installed on the users device. * Represents an app installed on the users device.
* Contains the minimal amount of data required to identify the app. * Contains the minimal amount of data required to identify the app.
*/ */
@Serializable @Serializable
class AppInfo(val packageName: String, val activityName: String?, val user: Int = INVALID_USER) { @SerialName("app")
class AppInfo(val packageName: String, val activityName: String?, val user: Int = INVALID_USER): AbstractAppInfo {
fun serialize(): String {
return Json.encodeToString(this)
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if(other is AppInfo) { if(other is AppInfo) {
@ -47,11 +44,4 @@ class AppInfo(val packageName: String, val activityName: String?, val user: Int
return "AppInfo {package=$packageName, activity=$activityName, user=$user}" return "AppInfo {package=$packageName, activity=$activityName, user=$user}"
} }
companion object {
const val INVALID_USER = -1
fun deserialize(serialized: String): AppInfo {
return Json.decodeFromString(serialized)
}
}
} }

View file

@ -4,20 +4,21 @@ 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 android.os.UserHandle
import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.getUserFromId
/** /**
* Stores information used to create [de.jrpie.android.launcher.ui.list.apps.AppsRecyclerAdapter] rows. * Stores information used to create [de.jrpie.android.launcher.ui.list.apps.AppsRecyclerAdapter] rows.
*/ */
class DetailedAppInfo( class DetailedAppInfo(
val app: AppInfo, private val app: AppInfo,
val label: CharSequence, private val label: CharSequence,
val icon: Drawable, private val icon: Drawable,
val isPrivateSpaceApp: Boolean, private val privateSpace: Boolean,
val isSystemApp: Boolean = false, private val removable: Boolean = true,
) { ): AbstractDetailedAppInfo {
constructor(activityInfo: LauncherActivityInfo, private: Boolean) : this( constructor(activityInfo: LauncherActivityInfo, private: Boolean) : this(
AppInfo( AppInfo(
@ -28,29 +29,41 @@ class DetailedAppInfo(
activityInfo.label, activityInfo.label,
activityInfo.getBadgedIcon(0), activityInfo.getBadgedIcon(0),
private, private,
activityInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) != 0 // App can be uninstalled iff it is not a system app
activityInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
) )
fun getCustomLabel(context: Context): CharSequence {
val map = (context.applicationContext as? Application)?.getCustomAppNames() ?: return label
return map[app] ?: label
override fun getLabel(): String {
return label.toString()
} }
fun setCustomLabel(label: CharSequence?) { override fun getIcon(context: Context): Drawable {
return icon
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)
} }
override fun getRawInfo(): AppInfo {
return app
}
override fun getUser(context: Context): UserHandle {
return getUserFromId(app.user, context)
}
override fun isPrivate(): Boolean {
return privateSpace
}
override fun isRemovable(): Boolean {
return removable
}
override fun getAction(): Action {
return AppAction(app)
}
companion object { companion object {
fun fromAppInfo(appInfo: AppInfo, context: Context): DetailedAppInfo? { fun fromAppInfo(appInfo: AppInfo, context: Context): DetailedAppInfo? {
return appInfo.getLauncherActivityInfo(context)?.let { return appInfo.getLauncherActivityInfo(context)?.let {

View file

@ -0,0 +1,66 @@
package de.jrpie.android.launcher.apps
import android.app.Service
import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.UserHandle
import androidx.annotation.RequiresApi
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.ShortcutAction
import de.jrpie.android.launcher.getUserFromId
@RequiresApi(Build.VERSION_CODES.N_MR1)
class DetailedPinnedShortcutInfo(
private val shortcutInfo: PinnedShortcutInfo,
private val label: String,
private val icon: Drawable,
private val privateSpace: Boolean
) : AbstractDetailedAppInfo {
constructor(context: Context, shortcut: ShortcutInfo) : this(
PinnedShortcutInfo(shortcut),
shortcut.longLabel.toString(),
(context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps)
.getShortcutBadgedIconDrawable(shortcut, 0),
shortcut.userHandle == getPrivateSpaceUser(context)
)
override fun getRawInfo(): AbstractAppInfo {
return shortcutInfo
}
override fun getLabel(): String {
return label
}
override fun getIcon(context: Context): Drawable {
return icon
}
override fun getUser(context: Context): UserHandle {
return getUserFromId(shortcutInfo.user, context)
}
override fun isPrivate(): Boolean {
return privateSpace
}
override fun isRemovable(): Boolean {
return true
}
override fun getAction(): Action {
return ShortcutAction(shortcutInfo)
}
companion object {
fun fromPinnedShortcutInfo(shortcutInfo: PinnedShortcutInfo, context: Context): DetailedPinnedShortcutInfo? {
return shortcutInfo.getShortcutInfo(context)?.let {
DetailedPinnedShortcutInfo(context, it)
}
}
}
}

View file

@ -1,4 +1,4 @@
package de.jrpie.android.launcher.actions.shortcuts package de.jrpie.android.launcher.apps
import android.app.Service import android.app.Service
import android.content.ComponentName import android.content.ComponentName
@ -9,17 +9,19 @@ import android.content.pm.ShortcutInfo
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import de.jrpie.android.launcher.getUserFromId import de.jrpie.android.launcher.getUserFromId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@RequiresApi(Build.VERSION_CODES.N_MR1) @RequiresApi(Build.VERSION_CODES.N_MR1)
@Serializable @Serializable
@SerialName("shortcut")
class PinnedShortcutInfo( class PinnedShortcutInfo(
val id: String, val id: String,
val packageName: String, val packageName: String,
val activityName: String, val activityName: String,
val user: Int val user: Int
) { ): AbstractAppInfo {
constructor(info: ShortcutInfo) : this(info.id, info.`package`, info.activity?.className ?: "", info.userHandle.hashCode()) constructor(info: ShortcutInfo) : this(info.id, info.`package`, info.activity?.className ?: "", info.userHandle.hashCode())

View file

@ -5,8 +5,9 @@ import java.util.Set;
import de.jrpie.android.launcher.R; import de.jrpie.android.launcher.R;
import de.jrpie.android.launcher.actions.lock.LockMethod; import de.jrpie.android.launcher.actions.lock.LockMethod;
import de.jrpie.android.launcher.preferences.serialization.MapAppInfoStringPreferenceSerializer; import de.jrpie.android.launcher.preferences.serialization.MapAbstractAppInfoStringPreferenceSerializer;
import de.jrpie.android.launcher.preferences.serialization.SetAppInfoPreferenceSerializer; import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer;
import de.jrpie.android.launcher.preferences.serialization.SetPinnedShortcutInfoPreferenceSerializer;
import de.jrpie.android.launcher.preferences.theme.Background; import de.jrpie.android.launcher.preferences.theme.Background;
import de.jrpie.android.launcher.preferences.theme.ColorTheme; import de.jrpie.android.launcher.preferences.theme.ColorTheme;
import de.jrpie.android.launcher.preferences.theme.Font; import de.jrpie.android.launcher.preferences.theme.Font;
@ -25,15 +26,17 @@ import eu.jonahbauer.android.preference.annotations.Preferences;
@Preference(name = "version_code", type = int.class, defaultValue = "-1"), @Preference(name = "version_code", type = int.class, defaultValue = "-1"),
}), }),
@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 = SetAppInfoPreferenceSerializer.class), @Preference(name = "favorites", type = Set.class, serializer = SetAbstractAppInfoPreferenceSerializer.class),
@Preference(name = "hidden", type = Set.class, serializer = SetAppInfoPreferenceSerializer.class), @Preference(name = "hidden", type = Set.class, serializer = SetAbstractAppInfoPreferenceSerializer.class),
@Preference(name = "custom_names", type = HashMap.class, serializer = MapAppInfoStringPreferenceSerializer.class), @Preference(name = "pinned_shortcuts", type = Set.class, serializer = SetPinnedShortcutInfoPreferenceSerializer.class),
@Preference(name = "custom_names", type = HashMap.class, serializer = MapAbstractAppInfoStringPreferenceSerializer.class),
@Preference(name = "hide_bound_apps", type = boolean.class, defaultValue = "false"), @Preference(name = "hide_bound_apps", type = boolean.class, defaultValue = "false"),
@Preference(name = "hide_paused_apps", type = boolean.class, defaultValue = "false"), @Preference(name = "hide_paused_apps", type = boolean.class, defaultValue = "false"),
@Preference(name = "hide_private_space_apps", type = boolean.class, defaultValue = "false"), @Preference(name = "hide_private_space_apps", type = boolean.class, defaultValue = "false"),
}), }),
@PreferenceGroup(name = "list", prefix = "settings_list_", suffix = "_key", value = { @PreferenceGroup(name = "list", prefix = "settings_list_", suffix = "_key", value = {
@Preference(name = "layout", type = ListLayout.class, defaultValue = "DEFAULT") @Preference(name = "layout", type = ListLayout.class, defaultValue = "DEFAULT"),
@Preference(name = "reverse_layout", type = boolean.class, defaultValue = "false")
}), }),
@PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = { @PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = {
}), }),
@ -61,6 +64,7 @@ import eu.jonahbauer.android.preference.annotations.Preferences;
@Preference(name = "screen_timeout_disabled", type = boolean.class, defaultValue = "false"), @Preference(name = "screen_timeout_disabled", type = boolean.class, defaultValue = "false"),
@Preference(name = "full_screen", type = boolean.class, defaultValue = "true"), @Preference(name = "full_screen", type = boolean.class, defaultValue = "true"),
@Preference(name = "rotate_screen", type = boolean.class, defaultValue = "true"), @Preference(name = "rotate_screen", type = boolean.class, defaultValue = "true"),
@Preference(name = "hide_navigation_bar", type = boolean.class, defaultValue = "false"),
}), }),
@PreferenceGroup(name = "functionality", prefix = "settings_functionality_", suffix = "_key", value = { @PreferenceGroup(name = "functionality", prefix = "settings_functionality_", suffix = "_key", value = {
@Preference(name = "search_auto_launch", type = boolean.class, defaultValue = "true"), @Preference(name = "search_auto_launch", type = boolean.class, defaultValue = "true"),

View file

@ -5,9 +5,12 @@ import android.util.Log
import de.jrpie.android.launcher.BuildConfig import de.jrpie.android.launcher.BuildConfig
import de.jrpie.android.launcher.actions.Action import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1 import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2 import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
import de.jrpie.android.launcher.ui.HomeActivity import de.jrpie.android.launcher.ui.HomeActivity
@ -15,7 +18,7 @@ import de.jrpie.android.launcher.ui.HomeActivity
* Increase when breaking changes are introduced and write an appropriate case in * Increase when breaking changes are introduced and write an appropriate case in
* `migratePreferencesToNewVersion` * `migratePreferencesToNewVersion`
*/ */
const val PREFERENCE_VERSION = 3 const val PREFERENCE_VERSION = 4
const val UNKNOWN_PREFERENCE_VERSION = -1 const val UNKNOWN_PREFERENCE_VERSION = -1
private const val TAG = "Launcher - Preferences" private const val TAG = "Launcher - Preferences"
@ -44,6 +47,10 @@ fun migratePreferencesToNewVersion(context: Context) {
migratePreferencesFromVersion2() migratePreferencesFromVersion2()
Log.i(TAG, "migration of preferences complete (2 -> ${PREFERENCE_VERSION}).") Log.i(TAG, "migration of preferences complete (2 -> ${PREFERENCE_VERSION}).")
} }
3 -> {
migratePreferencesFromVersion3()
Log.i(TAG, "migration of preferences complete (3 -> ${PREFERENCE_VERSION}).")
}
else -> { else -> {
Log.w( Log.w(
@ -66,16 +73,16 @@ fun resetPreferences(context: Context) {
LauncherPreferences.internal().versionCode(PREFERENCE_VERSION) LauncherPreferences.internal().versionCode(PREFERENCE_VERSION)
val hidden: MutableSet<AppInfo> = mutableSetOf() val hidden: MutableSet<AbstractAppInfo> = mutableSetOf()
val launcher = DetailedAppInfo.fromAppInfo( val launcher = DetailedAppInfo.fromAppInfo(
AppInfo( AppInfo(
BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID,
HomeActivity::class.java.name, HomeActivity::class.java.name,
AppInfo.INVALID_USER INVALID_USER
), context ), context
) )
launcher?.app?.let { hidden.add(it) } launcher?.getRawInfo()?.let { hidden.add(it) }
Log.i(TAG,"Hiding ${launcher?.app}") Log.i(TAG,"Hiding ${launcher?.getRawInfo()}")
LauncherPreferences.apps().hidden(hidden) LauncherPreferences.apps().hidden(hidden)
Action.resetToDefaultActions(context) Action.resetToDefaultActions(context)

View file

@ -5,15 +5,26 @@ import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.LauncherAction import de.jrpie.android.launcher.actions.LauncherAction
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
import de.jrpie.android.launcher.preferences.serialization.MapAppInfoStringPreferenceSerializer import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
@Serializable
private class LegacyMapEntry(val key: AppInfo, val value: String)
private fun serializeMapAppInfo(value: Map<AppInfo, String>?): Set<String>? {
return value?.map { (key, value) ->
Json.encodeToString(LegacyMapEntry(key, value))
}?.toSet()
}
val oldLauncherActionIds: Map<String, LauncherAction> = val oldLauncherActionIds: Map<String, LauncherAction> =
mapOf( mapOf(
Pair("launcher:settings", LauncherAction.SETTINGS), Pair("launcher:settings", LauncherAction.SETTINGS),
@ -77,7 +88,7 @@ private fun Action.Companion.legacyFromPreference(id: String): Action? {
private fun migrateAppInfoStringMap(key: String) { private fun migrateAppInfoStringMap(key: String) {
val preferences = LauncherPreferences.getSharedPreferences() val preferences = LauncherPreferences.getSharedPreferences()
MapAppInfoStringPreferenceSerializer().serialize( serializeMapAppInfo(
preferences.getStringSet(key, setOf())?.mapNotNull { entry -> preferences.getStringSet(key, setOf())?.mapNotNull { entry ->
try { try {
val obj = JSONObject(entry) val obj = JSONObject(entry)

View file

@ -12,9 +12,9 @@ import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
* (see [PREFERENCE_VERSION]) * (see [PREFERENCE_VERSION])
*/ */
fun migratePreferencesFromVersion2() { fun migratePreferencesFromVersion2() {
assert(PREFERENCE_VERSION == 3)
assert(LauncherPreferences.internal().versionCode() == 2) assert(LauncherPreferences.internal().versionCode() == 2)
// previously there was no setting for this // previously there was no setting for this
Action.setActionForGesture(Gesture.BACK, LauncherAction.CHOOSE) Action.setActionForGesture(Gesture.BACK, LauncherAction.CHOOSE)
LauncherPreferences.internal().versionCode(3) LauncherPreferences.internal().versionCode(3)
migratePreferencesFromVersion3()
} }

View file

@ -0,0 +1,85 @@
package de.jrpie.android.launcher.preferences.legacy
import android.content.SharedPreferences
import android.content.SharedPreferences.Editor
import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
import de.jrpie.android.launcher.preferences.serialization.MapAbstractAppInfoStringPreferenceSerializer
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.util.HashSet
/**
* Migrate preferences from version 3 (used until version 0.0.23) to the current format
* (see [PREFERENCE_VERSION])
*/
fun deserializeSet(value: Set<String>?): Set<AppInfo>? {
return value?.map {
Json.decodeFromString<AppInfo>(it)
}?.toHashSet()
}
fun deserializeMap(value: Set<String>?): HashMap<AppInfo, String>? {
return value?.associateTo(HashMap()) {
val entry = Json.decodeFromString<MapEntry>(it)
Pair(entry.key, entry.value)
}
}
@Serializable
private class MapEntry(val key: AppInfo, val value: String)
private fun migrateSetAppInfo(key: String, preferences: SharedPreferences, editor: Editor) {
try {
val serializer = SetAbstractAppInfoPreferenceSerializer()
val set = HashSet<AbstractAppInfo>()
deserializeSet(preferences.getStringSet(key, null))?.let {
set.addAll(it)
}
editor.putStringSet(
key,
serializer.serialize(set as java.util.Set<AbstractAppInfo>) as Set<String>?
)
} catch (e: Exception) {
e.printStackTrace()
editor.putStringSet(key, null)
}
}
private fun migrateMapAppInfoString(key: String, preferences: SharedPreferences, editor: Editor ) {
try {
val serializer = MapAbstractAppInfoStringPreferenceSerializer()
val map = HashMap<AbstractAppInfo, String>()
deserializeMap(preferences.getStringSet(key, null))?.let {
map.putAll(it)
}
editor.putStringSet(key, serializer.serialize(map) as Set<String>?)
} catch (e: Exception) {
e.printStackTrace()
editor.putStringSet(key, null)
}
}
fun migratePreferencesFromVersion3() {
assert(PREFERENCE_VERSION == 4)
assert(LauncherPreferences.internal().versionCode() == 3)
val preferences = LauncherPreferences.getSharedPreferences()
val editor = preferences.edit()
migrateSetAppInfo(LauncherPreferences.apps().keys().favorites(), preferences, editor)
migrateSetAppInfo(LauncherPreferences.apps().keys().hidden(), preferences, editor)
migrateMapAppInfoString(LauncherPreferences.apps().keys().customNames(), preferences, editor)
editor.apply()
LauncherPreferences.internal().versionCode(4)
}

View file

@ -2,7 +2,8 @@
package de.jrpie.android.launcher.preferences.serialization package de.jrpie.android.launcher.preferences.serialization
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AbstractAppInfo
import de.jrpie.android.launcher.apps.PinnedShortcutInfo
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -10,40 +11,61 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
// Serializers for [LauncherPreference$Config]
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class SetAppInfoPreferenceSerializer : class SetAbstractAppInfoPreferenceSerializer :
PreferenceSerializer<java.util.Set<AppInfo>?, java.util.Set<java.lang.String>?> { PreferenceSerializer<java.util.Set<AbstractAppInfo>?, java.util.Set<java.lang.String>?> {
@Throws(PreferenceSerializationException::class) @Throws(PreferenceSerializationException::class)
override fun serialize(value: java.util.Set<AppInfo>?): java.util.Set<java.lang.String> { override fun serialize(value: java.util.Set<AbstractAppInfo>?): java.util.Set<java.lang.String> {
return value?.map(AppInfo::serialize)?.toHashSet() as java.util.Set<java.lang.String> return value?.map(AbstractAppInfo::serialize)
?.toHashSet() as java.util.Set<java.lang.String>
} }
@Throws(PreferenceSerializationException::class) @Throws(PreferenceSerializationException::class)
override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.Set<AppInfo>? { override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.Set<AbstractAppInfo>? {
return value?.map (java.lang.String::toString)?.map(AppInfo::deserialize)?.toHashSet() as? java.util.Set<AppInfo> return value?.map(java.lang.String::toString)?.map(AbstractAppInfo::deserialize)
?.toHashSet() as? java.util.Set<AbstractAppInfo>
} }
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class MapAppInfoStringPreferenceSerializer : class SetPinnedShortcutInfoPreferenceSerializer :
PreferenceSerializer<java.util.HashMap<AppInfo, String>?, java.util.Set<java.lang.String>?> { PreferenceSerializer<java.util.Set<PinnedShortcutInfo>?, java.util.Set<java.lang.String>?> {
@Throws(PreferenceSerializationException::class)
@Serializable override fun serialize(value: java.util.Set<PinnedShortcutInfo>?): java.util.Set<java.lang.String> {
private class MapEntry(val key: AppInfo, val value: String) return value?.map { Json.encodeToString<PinnedShortcutInfo>(it) }
?.toHashSet() as java.util.Set<java.lang.String>
}
@Throws(PreferenceSerializationException::class) @Throws(PreferenceSerializationException::class)
override fun serialize(value: java.util.HashMap<AppInfo, String>?): java.util.Set<java.lang.String>? { override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.Set<PinnedShortcutInfo>? {
return value?.map(java.lang.String::toString)
?.map { Json.decodeFromString<PinnedShortcutInfo>(it) }
?.toHashSet() as? java.util.Set<PinnedShortcutInfo>
}
}
@Suppress("UNCHECKED_CAST")
class MapAbstractAppInfoStringPreferenceSerializer :
PreferenceSerializer<java.util.HashMap<AbstractAppInfo, String>?, java.util.Set<java.lang.String>?> {
@Serializable
private class MapEntry(val key: AbstractAppInfo, val value: String)
@Throws(PreferenceSerializationException::class)
override fun serialize(value: java.util.HashMap<AbstractAppInfo, String>?): java.util.Set<java.lang.String>? {
return value?.map { (key, value) -> return value?.map { (key, value) ->
Json.encodeToString(MapEntry(key, value)) Json.encodeToString(MapEntry(key, value))
}?.toHashSet() as? java.util.Set<java.lang.String> }?.toHashSet() as? java.util.Set<java.lang.String>
} }
@Throws(PreferenceSerializationException::class) @Throws(PreferenceSerializationException::class)
override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.HashMap<AppInfo, String>? { override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.HashMap<AbstractAppInfo, String>? {
return value?.associateTo(HashMap()) { return value?.associateTo(HashMap()) {
val entry = Json.decodeFromString<MapEntry>(it.toString()) val entry = Json.decodeFromString<MapEntry>(it.toString())
Pair(entry.key, entry.value) Pair(entry.key, entry.value)
} }
} }
} }

View file

@ -9,6 +9,9 @@ import android.util.DisplayMetrics
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.Window
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.window.OnBackInvokedDispatcher import android.window.OnBackInvokedDispatcher
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -99,6 +102,42 @@ class HomeActivity : UIObject, AppCompatActivity() {
} }
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus && LauncherPreferences.display().hideNavigationBar()) {
hideNavigationBar()
}
}
@Suppress("DEPRECATION")
private fun hideNavigationBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.insetsController?.apply {
hide(WindowInsets.Type.navigationBars())
systemBarsBehavior =
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
val decorView = window.decorView
val uiOptions = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
// Try to hide the navigation bar but do not hide the status bar
decorView.systemUiVisibility = uiOptions
// Add listener to hide the navigation bar
decorView.setOnSystemUiVisibilityChangeListener { visibility ->
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
decorView.systemUiVisibility = uiOptions
}
}
}
}
private fun updateSettingsFallbackButtonVisibility() { private fun updateSettingsFallbackButtonVisibility() {
// If µLauncher settings can not be reached from any action bound to an enabled gesture, // If µLauncher settings can not be reached from any action bound to an enabled gesture,
// show the fallback button. // show the fallback button.
@ -186,6 +225,7 @@ class HomeActivity : UIObject, AppCompatActivity() {
// Only used pre Android 13, cf. onBackInvokedDispatcher // Only used pre Android 13, cf. onBackInvokedDispatcher
handleBack() handleBack()
} }
KeyEvent.KEYCODE_VOLUME_UP -> { KeyEvent.KEYCODE_VOLUME_UP -> {
if (Action.forGesture(Gesture.VOLUME_UP) == LauncherAction.VOLUME_UP) { if (Action.forGesture(Gesture.VOLUME_UP) == LauncherAction.VOLUME_UP) {
// Let the OS handle the key event. This works better with some custom ROMs // Let the OS handle the key event. This works better with some custom ROMs

View file

@ -21,7 +21,7 @@ import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.actions.Action import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.ShortcutAction import de.jrpie.android.launcher.actions.ShortcutAction
import de.jrpie.android.launcher.actions.shortcuts.PinnedShortcutInfo import de.jrpie.android.launcher.apps.PinnedShortcutInfo
import de.jrpie.android.launcher.databinding.ActivityPinShortcutBinding import de.jrpie.android.launcher.databinding.ActivityPinShortcutBinding
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
@ -29,6 +29,7 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
private lateinit var binding: ActivityPinShortcutBinding private lateinit var binding: ActivityPinShortcutBinding
private var isBound = false private var isBound = false
private var request: PinItemRequest? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super<AppCompatActivity>.onCreate(savedInstanceState) super<AppCompatActivity>.onCreate(savedInstanceState)
@ -46,6 +47,7 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
val launcherApps = getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps val launcherApps = getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val request = launcherApps.getPinItemRequest(intent) val request = launcherApps.getPinItemRequest(intent)
this.request = request
if (request == null || request.requestType != PinItemRequest.REQUEST_TYPE_SHORTCUT) { if (request == null || request.requestType != PinItemRequest.REQUEST_TYPE_SHORTCUT) {
finish() finish()
return return
@ -84,6 +86,7 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
} }
binding.pinShortcutClose.setOnClickListener { finish() } binding.pinShortcutClose.setOnClickListener { finish() }
binding.pinShortcutButtonOk.setOnClickListener { finish() }
} }
override fun onStart() { override fun onStart() {
@ -91,6 +94,24 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
super<UIObject>.onStart() super<UIObject>.onStart()
} }
override fun onDestroy() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
super.onDestroy()
return
}
if(binding.pinShortcutSwitchVisible.isChecked) {
if(!isBound) {
request?.accept()
}
request?.shortcutInfo?.let {
val set = LauncherPreferences.apps().pinnedShortcuts() ?: mutableSetOf()
set.add(PinnedShortcutInfo(it))
LauncherPreferences.apps().pinnedShortcuts(set)
}
}
super.onDestroy()
}
override fun getTheme(): Resources.Theme { override fun getTheme(): Resources.Theme {
return modifyTheme(super.getTheme()) return modifyTheme(super.getTheme())
} }
@ -124,5 +145,6 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
override fun getItemCount(): Int { override fun getItemCount(): Int {
return gestures.size return gestures.size
} }
} }
} }

View file

@ -16,11 +16,10 @@ import androidx.recyclerview.widget.RecyclerView
import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.REQUEST_CHOOSE_APP import de.jrpie.android.launcher.REQUEST_CHOOSE_APP
import de.jrpie.android.launcher.actions.AppAction import de.jrpie.android.launcher.apps.AbstractDetailedAppInfo
import de.jrpie.android.launcher.apps.AppFilter import de.jrpie.android.launcher.apps.AppFilter
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.getUserFromId
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.ListLayout import de.jrpie.android.launcher.preferences.ListLayout
import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.list.ListActivity
@ -47,7 +46,7 @@ class AppsRecyclerAdapter(
RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() { RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() {
private val apps = (activity.applicationContext as Application).apps private val apps = (activity.applicationContext as Application).apps
private val appsListDisplayed: MutableList<DetailedAppInfo> = mutableListOf() private val appsListDisplayed: MutableList<AbstractDetailedAppInfo> = mutableListOf()
// temporarily disable auto launch // temporarily disable auto launch
var disableAutoLaunch: Boolean = false var disableAutoLaunch: Boolean = false
@ -83,11 +82,11 @@ class AppsRecyclerAdapter(
if (layout.useBadgedText) { if (layout.useBadgedText) {
appLabel = activity.packageManager.getUserBadgedLabel( appLabel = activity.packageManager.getUserBadgedLabel(
appLabel, appLabel,
getUserFromId(appsListDisplayed[i].app.user, activity) appsListDisplayed[i].getUser(activity)
).toString() ).toString()
} }
val appIcon = appsListDisplayed[i].icon val appIcon = appsListDisplayed[i].getIcon(activity)
viewHolder.textView.text = appLabel viewHolder.textView.text = appLabel
viewHolder.img.setImageDrawable(appIcon) viewHolder.img.setImageDrawable(appIcon)
@ -118,22 +117,26 @@ class AppsRecyclerAdapter(
@Suppress("SameReturnValue") @Suppress("SameReturnValue")
private fun showOptionsPopup( private fun showOptionsPopup(
viewHolder: ViewHolder, viewHolder: ViewHolder,
appInfo: DetailedAppInfo appInfo: AbstractDetailedAppInfo
): Boolean { ): Boolean {
//create the popup menu //create the popup menu
val popup = PopupMenu(activity, viewHolder.img) val popup = PopupMenu(activity, viewHolder.img)
popup.inflate(R.menu.menu_app) popup.inflate(R.menu.menu_app)
if (appInfo.isSystemApp) { if (!appInfo.isRemovable()) {
popup.menu.findItem(R.id.app_menu_delete).setVisible(false) popup.menu.findItem(R.id.app_menu_delete).setVisible(false)
} }
if (LauncherPreferences.apps().hidden()?.contains(appInfo.app) == true) { if (appInfo !is DetailedAppInfo) {
popup.menu.findItem(R.id.app_menu_info).setVisible(false)
}
if (LauncherPreferences.apps().hidden()?.contains(appInfo.getRawInfo()) == true) {
popup.menu.findItem(R.id.app_menu_hidden).setTitle(R.string.list_app_hidden_remove) popup.menu.findItem(R.id.app_menu_hidden).setTitle(R.string.list_app_hidden_remove)
} }
if (LauncherPreferences.apps().favorites()?.contains(appInfo.app) == true) { if (LauncherPreferences.apps().favorites()?.contains(appInfo.getRawInfo()) == true) {
popup.menu.findItem(R.id.app_menu_favorite).setTitle(R.string.list_app_favorite_remove) popup.menu.findItem(R.id.app_menu_favorite).setTitle(R.string.list_app_favorite_remove)
} }
@ -141,19 +144,19 @@ class AppsRecyclerAdapter(
popup.setOnMenuItemClickListener { popup.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.app_menu_delete -> { R.id.app_menu_delete -> {
appInfo.app.uninstall(activity); true appInfo.getRawInfo().uninstall(activity); true
} }
R.id.app_menu_info -> { R.id.app_menu_info -> {
appInfo.app.openSettings(activity); true (appInfo.getRawInfo() as? AppInfo)?.openSettings(activity); true
} }
R.id.app_menu_favorite -> { R.id.app_menu_favorite -> {
appInfo.app.toggleFavorite(); true appInfo.getRawInfo().toggleFavorite(); true
} }
R.id.app_menu_hidden -> { R.id.app_menu_hidden -> {
appInfo.app.toggleHidden(root); true appInfo.getRawInfo().toggleHidden(root); true
} }
R.id.app_menu_rename -> { R.id.app_menu_rename -> {
@ -188,12 +191,12 @@ class AppsRecyclerAdapter(
val appInfo = appsListDisplayed[pos] val appInfo = appsListDisplayed[pos]
when (intention) { when (intention) {
ListActivity.ListActivityIntention.VIEW -> { ListActivity.ListActivityIntention.VIEW -> {
AppAction(appInfo.app).invoke(activity, rect) appInfo.getAction().invoke(activity, rect)
} }
ListActivity.ListActivityIntention.PICK -> { ListActivity.ListActivityIntention.PICK -> {
val returnIntent = Intent() val returnIntent = Intent()
AppAction(appInfo.app).writeToIntent(returnIntent) appInfo.getAction().writeToIntent(returnIntent)
returnIntent.putExtra("forGesture", forGesture) returnIntent.putExtra("forGesture", forGesture)
activity.setResult(REQUEST_CHOOSE_APP, returnIntent) activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
activity.finish() activity.finish()
@ -211,8 +214,8 @@ class AppsRecyclerAdapter(
&& !disableAutoLaunch && !disableAutoLaunch
&& LauncherPreferences.functionality().searchAutoLaunch() && LauncherPreferences.functionality().searchAutoLaunch()
) { ) {
val info = appsListDisplayed[0] val app = appsListDisplayed[0]
AppAction(info.app).invoke(activity) app.getAction().invoke(activity)
val inputMethodManager = val inputMethodManager =
activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager

View file

@ -1,5 +1,6 @@
package de.jrpie.android.launcher.ui.list.apps package de.jrpie.android.launcher.ui.list.apps
import android.app.Activity
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -15,7 +16,10 @@ import com.google.android.material.snackbar.Snackbar
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.REQUEST_UNINSTALL import de.jrpie.android.launcher.REQUEST_UNINSTALL
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo
import de.jrpie.android.launcher.apps.AbstractDetailedAppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.apps.PinnedShortcutInfo
import de.jrpie.android.launcher.getUserFromId import de.jrpie.android.launcher.getUserFromId
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
@ -32,27 +36,33 @@ fun AppInfo.openSettings(
} }
} }
fun AppInfo.uninstall(activity: android.app.Activity) { fun AbstractAppInfo.uninstall(activity: Activity) {
val packageName = this.packageName if (this is AppInfo) {
val userId = this.user val packageName = this.packageName
val userId = this.user
Log.i(LOG_TAG, "uninstalling $this") Log.i(LOG_TAG, "uninstalling $this")
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE) val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE)
intent.data = Uri.parse("package:$packageName") intent.data = Uri.parse("package:$packageName")
getUserFromId(userId, activity).let { user -> getUserFromId(userId, activity).let { user ->
intent.putExtra(Intent.EXTRA_USER, user) intent.putExtra(Intent.EXTRA_USER, user)
}
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
activity.startActivityForResult(
intent,
REQUEST_UNINSTALL
)
} else if(this is PinnedShortcutInfo) {
val pinned = LauncherPreferences.apps().pinnedShortcuts() ?: mutableSetOf()
pinned.remove(this)
LauncherPreferences.apps().pinnedShortcuts(pinned)
} }
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
activity.startActivityForResult(
intent,
REQUEST_UNINSTALL
)
} }
fun AppInfo.toggleFavorite() { fun AbstractAppInfo.toggleFavorite() {
val favorites: MutableSet<AppInfo> = val favorites: MutableSet<AbstractAppInfo> =
LauncherPreferences.apps().favorites() ?: mutableSetOf() LauncherPreferences.apps().favorites() ?: mutableSetOf()
if (favorites.contains(this)) { if (favorites.contains(this)) {
@ -69,8 +79,8 @@ fun AppInfo.toggleFavorite() {
/** /**
* @param view: used to show a snackbar letting the user undo the action * @param view: used to show a snackbar letting the user undo the action
*/ */
fun AppInfo.toggleHidden(view: View) { fun AbstractAppInfo.toggleHidden(view: View) {
val hidden: MutableSet<AppInfo> = val hidden: MutableSet<AbstractAppInfo> =
LauncherPreferences.apps().hidden() ?: mutableSetOf() LauncherPreferences.apps().hidden() ?: mutableSetOf()
if (hidden.contains(this)) { if (hidden.contains(this)) {
hidden.remove(this) hidden.remove(this)
@ -87,9 +97,9 @@ fun AppInfo.toggleHidden(view: View) {
LauncherPreferences.apps().hidden(hidden) LauncherPreferences.apps().hidden(hidden)
} }
fun DetailedAppInfo.showRenameDialog(context: Context) { fun AbstractDetailedAppInfo.showRenameDialog(context: Context) {
AlertDialog.Builder(context, R.style.AlertDialogCustom).apply { AlertDialog.Builder(context, R.style.AlertDialogCustom).apply {
setTitle(context.getString(R.string.dialog_rename_title, label)) setTitle(context.getString(R.string.dialog_rename_title, getLabel()))
setView(R.layout.dialog_rename_app) setView(R.layout.dialog_rename_app)
setNegativeButton(R.string.dialog_cancel) { d, _ -> d.cancel() } setNegativeButton(R.string.dialog_cancel) { d, _ -> d.cancel() }
setPositiveButton(R.string.dialog_rename_ok) { d, _ -> setPositiveButton(R.string.dialog_rename_ok) { d, _ ->
@ -102,7 +112,7 @@ fun DetailedAppInfo.showRenameDialog(context: Context) {
}.create().also { it.show() }.apply { }.create().also { it.show() }.apply {
val input = findViewById<EditText>(R.id.dialog_rename_app_edit_text) val input = findViewById<EditText>(R.id.dialog_rename_app_edit_text)
input?.setText(getCustomLabel(context)) input?.setText(getCustomLabel(context))
input?.hint = label input?.hint = getLabel()
} }
} }

View file

@ -9,6 +9,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.apps.AppFilter import de.jrpie.android.launcher.apps.AppFilter
import de.jrpie.android.launcher.databinding.ListAppsBinding import de.jrpie.android.launcher.databinding.ListAppsBinding
@ -79,11 +81,18 @@ class ListFragmentApps : Fragment(), UIObject {
layout = LauncherPreferences.list().layout() layout = LauncherPreferences.list().layout()
) )
// set up the list / recycler // set up the list / recycler
binding.listAppsRview.apply { binding.listAppsRview.apply {
// improve performance (since content changes don't change the layout size) // improve performance (since content changes don't change the layout size)
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LauncherPreferences.list().layout().layoutManager(context) layoutManager = LauncherPreferences.list().layout().layoutManager(context)
.also {
if (LauncherPreferences.list().reverseLayout()) {
(it as? LinearLayoutManager)?.reverseLayout = true
(it as? GridLayoutManager)?.reverseLayout = true
}
}
adapter = appsRecyclerAdapter adapter = appsRecyclerAdapter
} }

View file

@ -80,7 +80,6 @@
android:minHeight="40dp" android:minHeight="40dp"
tools:drawableLeft="@drawable/baseline_settings_24" tools:drawableLeft="@drawable/baseline_settings_24"
tools:text="Shortcut name" /> tools:text="Shortcut name" />
<!--
<Space <Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="10dp" /> android:layout_height="10dp" />
@ -90,8 +89,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?android:textColor" android:textColor="?android:textColor"
android:checked="true"
android:text="@string/pin_shortcut_switch_visible" /> android:text="@string/pin_shortcut_switch_visible" />
-->
<Space <Space
android:layout_width="match_parent" android:layout_width="match_parent"
@ -103,8 +102,21 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/pin_shortcut_button_bind" /> android:text="@string/pin_shortcut_button_bind" />
<Space
android:layout_width="match_parent"
android:layout_height="10dp" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<Button
android:id="@+id/pin_shortcut_button_ok"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pin_shortcut_button_ok"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -40,8 +40,7 @@
android:src="@drawable/baseline_settings_24" android:src="@drawable/baseline_settings_24"
custom:layout_constraintBottom_toBottomOf="parent" custom:layout_constraintBottom_toBottomOf="parent"
custom:layout_constraintStart_toStartOf="parent" custom:layout_constraintStart_toStartOf="parent"
custom:layout_constraintTop_toTopOf="parent" custom:layout_constraintTop_toTopOf="parent" />
custom:type="solid" />
<TextView <TextView
android:id="@+id/list_heading" android:id="@+id/list_heading"
@ -70,8 +69,7 @@
android:src="@drawable/baseline_close_24" android:src="@drawable/baseline_close_24"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
custom:type="solid" />
<ImageView <ImageView
android:id="@+id/list_lock" android:id="@+id/list_lock"
android:visibility="gone" android:visibility="gone"
@ -85,8 +83,7 @@
android:src="@drawable/baseline_lock_24" android:src="@drawable/baseline_lock_24"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/list_close" app:layout_constraintEnd_toStartOf="@id/list_close"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
custom:type="solid" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout

View file

@ -47,8 +47,7 @@
android:src="@drawable/baseline_close_24" android:src="@drawable/baseline_close_24"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
custom:type="solid" />
<ImageView <ImageView
android:id="@+id/settings_system" android:id="@+id/settings_system"
@ -63,8 +62,7 @@
android:src="@drawable/baseline_settings_applications_24" android:src="@drawable/baseline_settings_applications_24"
custom:layout_constraintBottom_toBottomOf="parent" custom:layout_constraintBottom_toBottomOf="parent"
custom:layout_constraintStart_toStartOf="parent" custom:layout_constraintStart_toStartOf="parent"
custom:layout_constraintTop_toTopOf="parent" custom:layout_constraintTop_toTopOf="parent" />
custom:type="solid" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout

View file

@ -281,4 +281,32 @@
<string name="list_other_list_private_space">Espaço privado</string> <string name="list_other_list_private_space">Espaço privado</string>
<string name="tooltip_lock_private_space">Trancar espaço privado</string> <string name="tooltip_lock_private_space">Trancar espaço privado</string>
<string name="tooltip_unlock_private_space">Liberar espaço privado</string> <string name="tooltip_unlock_private_space">Liberar espaço privado</string>
</resources> <string name="settings_gesture_swipe_larger_reverse"><![CDATA[> (Reverse)]]></string>
<string name="settings_gesture_description_swipe_larger_reverse">Canto inferior esquerdo -&gt; centro direito -&gt; canto superior esquerdo</string>
<string name="settings_gesture_description_swipe_smaller">Canto superior direito -&gt; centro direito -&gt; canto inferior direito</string>
<string name="settings_gesture_swipe_smaller_reverse"><![CDATA[< (Reverse)]]></string>
<string name="settings_gesture_description_swipe_v">Canto superior esquerdo -&gt; centro médio -&gt; canto superior direito</string>
<string name="settings_gesture_description_swipe_v_reverse">Canto superior direito -&gt; centro médio -&gt; canto superior esquerdo</string>
<string name="settings_gesture_swipe_v_reverse">V (invertido)</string>
<string name="settings_gesture_description_swipe_lambda">Inferior esquerdo -&gt; superior médio -&gt; inferior direito</string>
<string name="settings_gesture_swipe_lambda_reverse">Λ (invertido)</string>
<string name="settings_gesture_swipe_larger"><![CDATA[>]]></string>
<string name="settings_gesture_swipe_smaller"><![CDATA[<]]></string>
<string name="settings_gesture_swipe_v">V</string>
<string name="settings_gesture_swipe_lambda">Λ</string>
<string name="settings_gesture_description_swipe_larger">Canto superior esquerdo -&gt; centro direito -&gt; canto inferior esquerdo</string>
<string name="settings_gesture_tap_up">Toque + pra cima</string>
<string name="settings_gesture_description_tap_up">Toque e deslize pra cima</string>
<string name="settings_gesture_tap_down">Toque + pra baixo</string>
<string name="settings_gesture_description_tap_down">Toque e deslize pra baixo</string>
<string name="settings_gesture_tap_left">Toque + esquerda</string>
<string name="settings_gesture_description_tap_left">Toque e deslize pra esquerda</string>
<string name="settings_gesture_tap_right">Toque + direita</string>
<string name="settings_gesture_description_tap_right">Toque e deslize pra direita</string>
<string name="pin_shortcut_title">Adicionar atalho</string>
<string name="pin_shortcut_button_bind">Vincular ao gesto</string>
<string name="pin_shortcut_switch_visible">Mostrar na lista de apps</string>
<string name="list_other_track_play_pause">Música: Reproduzir / Pausar</string>
<string name="settings_gesture_description_swipe_smaller_reverse">Canto inferior direito -&gt; centro esquerdo -&gt; canto superior direito</string>
<string name="settings_gesture_description_swipe_lambda_reverse">Inferior direito -&gt; superior médio -&gt; inferior esquerdo</string>
</resources>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This declares attributes for the FontAwesome TextView -->
<resources>
<declare-styleable name="FontAwesome">
<attr name="type" format="string" />
</declare-styleable>
</resources>

View file

@ -11,11 +11,13 @@
<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_pinned_shortcuts_key" translatable="false">apps.pinned_shortcuts</string>
<string name="settings_apps_custom_names_key" translatable="false">apps.custom_names</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_apps_hide_paused_apps_key" translatable="false">apps.hide_paused_apps</string> <string name="settings_apps_hide_paused_apps_key" translatable="false">apps.hide_paused_apps</string>
<string name="settings_apps_hide_private_space_apps_key" translatable="false">apps.hide_private_space_apps</string> <string name="settings_apps_hide_private_space_apps_key" translatable="false">apps.hide_private_space_apps</string>
<string name="settings_list_layout_key" translatable="false">list.layout</string> <string name="settings_list_layout_key" translatable="false">list.layout</string>
<string name="settings_list_reverse_layout_key" translatable="false">list.reverse_layout</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>
<!-- values of de.jrpie.android.launcher.preferences.ListLayout --> <!-- values of de.jrpie.android.launcher.preferences.ListLayout -->
@ -137,6 +139,7 @@
<string name="settings_display_screen_timeout_disabled_key" translatable="false">display.disable_timeout</string> <string name="settings_display_screen_timeout_disabled_key" translatable="false">display.disable_timeout</string>
<string name="settings_display_full_screen_key" translatable="false">display.use_full_screen</string> <string name="settings_display_full_screen_key" translatable="false">display.use_full_screen</string>
<string name="settings_display_rotate_screen_key" translatable="false">display.rotate_screen</string> <string name="settings_display_rotate_screen_key" translatable="false">display.rotate_screen</string>
<string name="settings_display_hide_navigation_bar_key" translatable="false">display.hide_navigation</string>
<string name="settings_enabled_gestures_double_swipe_key" translatable="false">enabled_gestures.double_actions</string> <string name="settings_enabled_gestures_double_swipe_key" translatable="false">enabled_gestures.double_actions</string>
<string name="settings_enabled_gestures_edge_swipe_key" translatable="false">enabled_gestures.edge_actions</string> <string name="settings_enabled_gestures_edge_swipe_key" translatable="false">enabled_gestures.edge_actions</string>

View file

@ -155,6 +155,7 @@
<string name="settings_display_screen_timeout_disabled">Keep screen on</string> <string name="settings_display_screen_timeout_disabled">Keep screen on</string>
<string name="settings_display_full_screen">Use full screen</string> <string name="settings_display_full_screen">Use full screen</string>
<string name="settings_display_rotate_screen">Rotate screen</string> <string name="settings_display_rotate_screen">Rotate screen</string>
<string name="settings_display_hide_navigation_bar">Hide navigation bar</string>
<string name="settings_launcher_section_functionality">Functionality</string> <string name="settings_launcher_section_functionality">Functionality</string>
@ -178,6 +179,7 @@
<string name="settings_apps_hide_paused_apps">Hide paused apps</string> <string name="settings_apps_hide_paused_apps">Hide paused apps</string>
<string name="settings_apps_hide_private_space_apps">Hide private space from app list</string> <string name="settings_apps_hide_private_space_apps">Hide private space from app list</string>
<string name="settings_list_layout">Layout of app list</string> <string name="settings_list_layout">Layout of app list</string>
<string name="settings_list_reverse_layout">Reverse app list</string>
<string name="settings_list_layout_item_default">Default</string> <string name="settings_list_layout_item_default">Default</string>
<string name="settings_list_layout_item_text">Text</string> <string name="settings_list_layout_item_text">Text</string>
@ -262,6 +264,7 @@
<!-- Pin shortcuts --> <!-- Pin shortcuts -->
<string name="pin_shortcut_title">Add Shortcut</string> <string name="pin_shortcut_title">Add Shortcut</string>
<string name="pin_shortcut_button_bind">Bind to gesture</string> <string name="pin_shortcut_button_bind">Bind to gesture</string>
<string name="pin_shortcut_button_ok">Ok</string>
<string name="pin_shortcut_switch_visible">Show in app list</string> <string name="pin_shortcut_switch_visible">Show in app list</string>
<!-- <!--

View file

@ -123,6 +123,9 @@
<style name="AlertDialogCustom" parent="Theme.AppCompat.Light.Dialog.Alert"> <style name="AlertDialogCustom" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:color">#000000</item> <item name="android:color">#000000</item>
<item name="android:textColor">@color/text_color_toggle</item> <item name="android:textColor">@color/text_color_toggle</item>
<item name="android:shadowRadius">0</item>
<item name="android:shadowDx">0</item>
<item name="android:shadowDy">0</item>
</style> </style>
<style name="AlertDialogDanger" parent="AlertDialogCustom"> <style name="AlertDialogDanger" parent="AlertDialogCustom">

View file

@ -164,6 +164,12 @@
android:summary="%s" android:summary="%s"
android:defaultValue="DEFAULT"/> android:defaultValue="DEFAULT"/>
<SwitchPreference
android:key="@string/settings_list_reverse_layout_key"
android:title="@string/settings_list_reverse_layout"
android:defaultValue="false"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/settings_launcher_section_display" android:title="@string/settings_launcher_section_display"
@ -180,6 +186,10 @@
android:key="@string/settings_display_screen_timeout_disabled_key" android:key="@string/settings_display_screen_timeout_disabled_key"
android:defaultValue="false" android:defaultValue="false"
android:title="@string/settings_display_screen_timeout_disabled"/> android:title="@string/settings_display_screen_timeout_disabled"/>
<SwitchPreference
android:key="@string/settings_display_hide_navigation_bar_key"
android:defaultValue="false"
android:title="@string/settings_display_hide_navigation_bar"/>
</PreferenceCategory> </PreferenceCategory>