This commit is contained in:
Josia Pietsch 2024-11-18 19:37:41 +01:00
commit bcb3f74a21
Signed by: jrpie
GPG key ID: E70B571D66986A2D
12 changed files with 113 additions and 40 deletions

View file

@ -11,6 +11,8 @@ cd Launcher
./gradlew build ./gradlew build
``` ```
Note that you need to sign the apk.
See [this guide](https://developer.android.com/build/building-cmdline) See [this guide](https://developer.android.com/build/building-cmdline)
for further instructions. for further instructions.

View file

@ -1,11 +1,13 @@
<!-- Shields from shields.io --> <!-- Shields from shields.io -->
[![][shield-release]][latest-release] [![][shield-release]][latest-release]
[![Android CI](https://github.com/jrpie/Launcher/actions/workflows/android.yml/badge.svg)](https://github.com/jrpie/Launcher/actions/workflows/android.yml)
[![][shield-license]][license] [![][shield-license]][license]
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)][matrix] [![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)][matrix]
[![Chat on Discord](https://img.shields.io/badge/discord-join%20chat-007ec6.svg?style=flat)][discord] [![Chat on Discord](https://img.shields.io/badge/discord-join%20chat-007ec6.svg?style=flat)][discord]
# μLauncher # μLauncher
@ -16,9 +18,9 @@ Your home screen only displays the date, time and a wallpaper.
Pressing back or swiping up (this can be configures) opens a list Pressing back or swiping up (this can be configures) opens a list
of all installed apps, which can be searched efficiently. of all installed apps, which can be searched efficiently.
This is a fork of [finnmglas's app Launcher][original-repo]. This is a fork of [finnmglas's app Launcher][original-repo].
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid" alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/de.jrpie.android.launcher/) height="80">](https://f-droid.org/packages/de.jrpie.android.launcher/)
@ -28,6 +30,23 @@ This is a fork of [finnmglas's app Launcher][original-repo].
Or download the latest APK from the [Releases Section](https://github.com/jrpie/Launcher/releases/latest). Or download the latest APK from the [Releases Section](https://github.com/jrpie/Launcher/releases/latest).
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg"
alt="screenshot"
height="400">
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg"
alt="screenshot"
height="400">
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg"
alt="screenshot"
height="400">
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg"
alt="screenshot"
height="400">
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg"
alt="screenshot"
height="400">
## Contributing ## Contributing
There are several ways to contribute to this app: There are several ways to contribute to this app:
@ -39,6 +58,7 @@ There are several ways to contribute to this app:
- Open a new pull request. - Open a new pull request.
See [BUILD.md](BUILD.md) for instructions how to build this project.
## Notable changes compared to [Finn's Launcher][original-repo]: ## Notable changes compared to [Finn's Launcher][original-repo]:

View file

@ -10,6 +10,7 @@ import android.widget.Toast
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.AppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
interface Action { interface Action {
@ -22,7 +23,18 @@ interface Action {
fun writeToIntent(intent: Intent) fun writeToIntent(intent: Intent)
companion object { companion object {
private fun fromId(id: String, user: Int?): Action? { /**
* Get an action for a specific id.
* An id is of the form:
* - "launcher:${launcher_action_name}", see [LauncherAction]
* - "${package_name}", see [AppAction]
* - "${package_name}:${activity_name}", see [AppAction]
*
* @param id
* @param user a user id, ignored if the action is a [LauncherAction].
* @param context used to complete [AppInfo] if possible
*/
private fun fromId(id: String, user: Int?, context: Context? = null): Action? {
if (id.isEmpty()) { if (id.isEmpty()) {
return null return null
} }
@ -32,7 +44,14 @@ interface Action {
val values = id.split(";") val values = id.split(";")
return AppAction(AppInfo(values[0], values.getOrNull(1), user ?: INVALID_USER)) var info = AppInfo(values[0], values.getOrNull(1), user ?: INVALID_USER)
// try to complete an incomplete AppInfo if a context is provided
if (context != null && (info.user == INVALID_USER || info.activityName == null)) {
info = DetailedAppInfo.fromAppInfo(info, context)?.app?:info
}
return AppAction(info)
} }
fun forGesture(gesture: Gesture): Action? { fun forGesture(gesture: Gesture): Action? {
@ -48,12 +67,17 @@ interface Action {
fun resetToDefaultActions(context: Context) { fun resetToDefaultActions(context: Context) {
val editor = LauncherPreferences.getSharedPreferences().edit() val editor = LauncherPreferences.getSharedPreferences().edit()
val boundActions = HashSet<String>()
Gesture.entries.forEach { gesture -> Gesture.entries.forEach { gesture ->
context.resources context.resources
.getStringArray(gesture.defaultsResource) .getStringArray(gesture.defaultsResource)
.map { fromId(it, null) } .filterNot { boundActions.contains(it) }
.firstOrNull { it?.isAvailable(context) ?: false } .map { Pair(it, fromId(it, null, context)) }
?.bindToGesture(editor, gesture.id) .firstOrNull { it.second?.isAvailable(context) ?: false }
?.apply {
boundActions.add(first)
second?.bindToGesture(editor, gesture.id)
}
} }
editor.apply() editor.apply()
} }

View file

@ -16,7 +16,7 @@ import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.getIntent import de.jrpie.android.launcher.getIntent
import de.jrpie.android.launcher.openAppSettings import de.jrpie.android.launcher.openAppSettings
class AppAction(private var appInfo: AppInfo) : Action { class AppAction(val appInfo: AppInfo) : Action {
override fun invoke(context: Context, rect: Rect?): Boolean { override fun invoke(context: Context, rect: Rect?): Boolean {
val packageName = appInfo.packageName.toString() val packageName = appInfo.packageName.toString()

View file

@ -2,6 +2,7 @@ package de.jrpie.android.launcher.actions
import android.content.Context import android.content.Context
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.preferences.LauncherPreferences
/** /**
* @param id internal id to serialize the action. Used as a key in shared preferences. * @param id internal id to serialize the action. Used as a key in shared preferences.
@ -32,14 +33,18 @@ enum class Gesture(
R.string.settings_gesture_description_vol_down, R.string.settings_gesture_description_vol_down,
R.array.default_volume_down, 0, 0 R.array.default_volume_down, 0, 0
), ),
TIME("action.time", TIME(
"action.time",
R.string.settings_gesture_time, R.string.settings_gesture_time,
R.string.settings_gesture_description_time, R.string.settings_gesture_description_time,
R.array.default_time), R.array.default_time
DATE("action.date", ),
DATE(
"action.date",
R.string.settings_gesture_date, R.string.settings_gesture_date,
R.string.settings_gesture_description_date, R.string.settings_gesture_description_date,
R.array.default_date), R.array.default_date
),
LONG_CLICK( LONG_CLICK(
"action.long_click", "action.long_click",
R.string.settings_gesture_long_click, R.string.settings_gesture_long_click,
@ -52,11 +57,13 @@ enum class Gesture(
R.string.settings_gesture_description_double_click, R.string.settings_gesture_description_double_click,
R.array.default_double_click, 0, 0 R.array.default_double_click, 0, 0
), ),
SWIPE_UP("action.up", SWIPE_UP(
"action.up",
R.string.settings_gesture_up, R.string.settings_gesture_up,
R.string.settings_gesture_description_up, R.string.settings_gesture_description_up,
R.array.default_up, R.array.default_up,
R.anim.bottom_up), R.anim.bottom_up
),
SWIPE_UP_LEFT_EDGE( SWIPE_UP_LEFT_EDGE(
"action.up_left", "action.up_left",
R.string.settings_gesture_up_left_edge, R.string.settings_gesture_up_left_edge,
@ -110,28 +117,28 @@ enum class Gesture(
"action.left", "action.left",
R.string.settings_gesture_left, R.string.settings_gesture_left,
R.string.settings_gesture_description_left, R.string.settings_gesture_description_left,
R.array.default_left, R.array.default_messengers,
R.anim.right_left R.anim.right_left
), ),
SWIPE_LEFT_TOP_EDGE( SWIPE_LEFT_TOP_EDGE(
"action.left_top", "action.left_top",
R.string.settings_gesture_left_top_edge, R.string.settings_gesture_left_top_edge,
R.string.settings_gesture_description_left_top_edge, R.string.settings_gesture_description_left_top_edge,
R.array.default_left_top, R.array.default_messengers,
R.anim.right_left R.anim.right_left
), ),
SWIPE_LEFT_BOTTOM_EDGE( SWIPE_LEFT_BOTTOM_EDGE(
"action.left_bottom", "action.left_bottom",
R.string.settings_gesture_left_bottom_edge, R.string.settings_gesture_left_bottom_edge,
R.string.settings_gesture_description_left_bottom_edge, R.string.settings_gesture_description_left_bottom_edge,
R.array.default_left_bottom, R.array.default_messengers,
R.anim.right_left R.anim.right_left
), ),
SWIPE_LEFT_DOUBLE( SWIPE_LEFT_DOUBLE(
"action.double_left", "action.double_left",
R.string.settings_gesture_double_left, R.string.settings_gesture_double_left,
R.string.settings_gesture_description_double_left, R.string.settings_gesture_description_double_left,
R.array.default_double_left, R.array.default_messengers,
R.anim.right_left R.anim.right_left
), ),
SWIPE_RIGHT( SWIPE_RIGHT(
@ -170,6 +177,7 @@ enum class Gesture(
fun getLabel(context: Context): String { fun getLabel(context: Context): String {
return context.resources.getString(this.labelResource) return context.resources.getString(this.labelResource)
} }
fun getDescription(context: Context): String { fun getDescription(context: Context): String {
return context.resources.getString(this.descriptionResource) return context.resources.getString(this.descriptionResource)
} }
@ -242,6 +250,16 @@ enum class Gesture(
} }
} }
fun isEnabled(): Boolean {
if (isEdgeVariant()) {
return LauncherPreferences.enabled_gestures().edgeSwipe()
}
if (isDoubleVariant()) {
return LauncherPreferences.enabled_gestures().doubleSwipe()
}
return true
}
operator fun invoke(context: Context) { operator fun invoke(context: Context) {
val action = Action.forGesture(this) val action = Action.forGesture(this)
Action.launch(action, context, this.animationIn, this.animationOut) Action.launch(action, context, this.animationIn, this.animationOut)

View file

@ -1,5 +1,8 @@
package de.jrpie.android.launcher.apps package de.jrpie.android.launcher.apps
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import java.util.Locale import java.util.Locale
import kotlin.text.Regex.Companion.escapeReplacement import kotlin.text.Regex.Companion.escapeReplacement
@ -14,20 +17,29 @@ class AppFilter(
val hidden = LauncherPreferences.apps().hidden() ?: setOf() val hidden = LauncherPreferences.apps().hidden() ?: setOf()
val favorites = LauncherPreferences.apps().favorites() ?: setOf() val favorites = LauncherPreferences.apps().favorites() ?: setOf()
apps = apps.filter { info -> apps = apps.filter { info ->
favoritesVisibility.predicate(favorites, info) favoritesVisibility.predicate(favorites, info)
&& hiddenVisibility.predicate(hidden, info) && hiddenVisibility.predicate(hidden, info)
} }
if (LauncherPreferences.apps().hideBoundApps()) {
val boundApps = Gesture.entries
.filter(Gesture::isEnabled)
.mapNotNull { g -> (Action.forGesture(g) as? AppAction)?.appInfo }
.toSet()
apps = apps.filterNot { info -> boundApps.contains(info.app) }
}
// normalize text for search // normalize text for search
var allowedSpecialCharacters = search val allowedSpecialCharacters = search
.lowercase(Locale.ROOT) .lowercase(Locale.ROOT)
.toCharArray() .toCharArray()
.distinct() .distinct()
.filter { c -> !c.isLetter() } .filter { c -> !c.isLetter() }
.map { c -> escapeReplacement(c.toString()) } .map { c -> escapeReplacement(c.toString()) }
.fold("") { x, y -> x + y } .fold("") { x, y -> x + y }
var disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex() val disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex()
fun normalize(text: String): String { fun normalize(text: String): String {
return text.lowercase(Locale.ROOT).replace(disallowedCharsRegex, "") return text.lowercase(Locale.ROOT).replace(disallowedCharsRegex, "")

View file

@ -28,6 +28,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 = "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 = {
}), }),

View file

@ -155,12 +155,7 @@ class ActionsRecyclerAdapter(val activity: Activity) :
} }
init { init {
val doubleActions = LauncherPreferences.enabled_gestures().doubleSwipe() gesturesList = Gesture.entries.filter(Gesture::isEnabled) as ArrayList<Gesture>
val edgeActions = LauncherPreferences.enabled_gestures().edgeSwipe()
gesturesList = Gesture.entries.filter {
(doubleActions || !it.isDoubleVariant())
&& (edgeActions || !it.isEdgeVariant())
} as ArrayList<Gesture>
} }
fun updateActions() { fun updateActions() {

View file

@ -61,6 +61,7 @@
<!-- Swipe right - Mail --> <!-- Swipe right - Mail -->
<string-array name="default_right"> <string-array name="default_right">
<item>net.thunderbird.android</item> <!-- Thunderbird -->
<item>com.fsck.k9</item> <!-- k9-mail --> <item>com.fsck.k9</item> <!-- k9-mail -->
<item>de.web.mobile.android.mail</item> <!-- WebMail --> <item>de.web.mobile.android.mail</item> <!-- WebMail -->
<item>com.samsung.android.email.provider</item> <!-- Samsung Mail --> <item>com.samsung.android.email.provider</item> <!-- Samsung Mail -->
@ -87,31 +88,22 @@
<item>info.tangential.cone</item> <item>info.tangential.cone</item>
</string-array> </string-array>
<!-- Swipe left - Messengers --> <string-array name="default_messengers">
<string-array name="default_left">
<item>de.spiritcroc.riotx</item> <!-- SchildiChat --> <item>de.spiritcroc.riotx</item> <!-- SchildiChat -->
<item>io.element.android.x</item> <!-- Element X --> <item>io.element.android.x</item> <!-- Element X -->
<item>im.vector.app</item> <!-- Element --> <item>im.vector.app</item> <!-- Element -->
<item>org.thoughtcrime.securesms</item> <!-- Signal --> <item>org.thoughtcrime.securesms</item> <!-- Signal -->
</string-array> <item>org.briarproject.briar.android</item> <!-- Briar -->
<item>eu.siacs.conversations</item> <!-- Conversations -->
<string-array name="default_left_top"> <item>ch.threema.app.libre</item> <!-- Threema -->
<item>com.android.messaging</item> <!-- SMS --> <item>com.android.messaging</item> <!-- SMS -->
<item>com.android.google.messaging</item> <!-- SMS --> <item>com.google.android.apps.messaging</item> <!-- SMS -->
<item>com.samsung.android.messaging</item> <!-- Samsung SMS --> <item>com.samsung.android.messaging</item> <!-- Samsung SMS -->
</string-array>
<string-array name="default_left_bottom">
<item>org.thoughtcrime.securesms</item> <!-- Signal -->
</string-array>
<!-- Swipe double left - More messengers -->
<string-array name="default_double_left">
<item>com.whatsapp</item> <!-- WhatsApp --> <item>com.whatsapp</item> <!-- WhatsApp -->
<item>org.telegram.messenger</item> <!-- Telegram --> <item>org.telegram.messenger</item> <!-- Telegram -->
<item>com.discord</item>
</string-array> </string-array>
<!-- Volume up --> <!-- Volume up -->
<string-array name="default_volume_up"> <string-array name="default_volume_up">
<item>launcher:volumeUp</item> <item>launcher:volumeUp</item>
@ -134,6 +126,8 @@
<string-array name="default_long_click"> <string-array name="default_long_click">
<item>com.beemdevelopment.aegis</item> <!-- Aegis 2FA --> <item>com.beemdevelopment.aegis</item> <!-- Aegis 2FA -->
<item>org.fedorahosted.freeotp</item> <item>org.fedorahosted.freeotp</item>
<item>proton.android.pass.fdroid</item> <!-- Proton Pass -->
<item>com.kunzisoft.keepass.libre</item> <!-- KeePassDX -->
<item>launcher:settings</item> <!-- Launcher Settings --> <item>launcher:settings</item> <!-- Launcher Settings -->
</string-array> </string-array>

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_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

@ -142,6 +142,7 @@
<string name="settings_launcher_section_apps">Apps</string> <string name="settings_launcher_section_apps">Apps</string>
<string name="settings_apps_hidden">Hidden apps</string> <string name="settings_apps_hidden">Hidden apps</string>
<string name="settings_apps_hide_bound_apps">Don\'t show apps that are bound to a gesture in the app list</string>
<!-- <!--
- -

View file

@ -124,6 +124,11 @@
android:title="@string/settings_apps_hidden" android:title="@string/settings_apps_hidden"
/> />
<SwitchPreference
android:key="@string/settings_apps_hide_bound_apps_key"
android:title="@string/settings_apps_hide_bound_apps"
android:defaultValue="false" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/settings_launcher_section_display" android:title="@string/settings_launcher_section_display"