diff --git a/BUILD.md b/BUILD.md index 5f62a30..ac1f11d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -11,6 +11,8 @@ cd Launcher ./gradlew build ``` +Note that you need to sign the apk. + See [this guide](https://developer.android.com/build/building-cmdline) for further instructions. diff --git a/README.md b/README.md index 3379726..81efe24 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ [![][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] [![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] + # μ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 of all installed apps, which can be searched efficiently. - This is a fork of [finnmglas's app Launcher][original-repo]. + [Get it on F-Droid](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). +screenshot + screenshot + screenshot + screenshot + screenshot + + ## Contributing 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. +See [BUILD.md](BUILD.md) for instructions how to build this project. ## Notable changes compared to [Finn's Launcher][original-repo]: diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt b/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt index 1eab2ee..0912207 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt @@ -10,6 +10,7 @@ import android.widget.Toast import de.jrpie.android.launcher.R import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER +import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.preferences.LauncherPreferences interface Action { @@ -22,7 +23,18 @@ interface Action { fun writeToIntent(intent: Intent) 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()) { return null } @@ -32,7 +44,14 @@ interface Action { 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? { @@ -48,12 +67,17 @@ interface Action { fun resetToDefaultActions(context: Context) { val editor = LauncherPreferences.getSharedPreferences().edit() + val boundActions = HashSet() Gesture.entries.forEach { gesture -> context.resources .getStringArray(gesture.defaultsResource) - .map { fromId(it, null) } - .firstOrNull { it?.isAvailable(context) ?: false } - ?.bindToGesture(editor, gesture.id) + .filterNot { boundActions.contains(it) } + .map { Pair(it, fromId(it, null, context)) } + .firstOrNull { it.second?.isAvailable(context) ?: false } + ?.apply { + boundActions.add(first) + second?.bindToGesture(editor, gesture.id) + } } editor.apply() } diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt index 29fb4b7..c8a75cf 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/AppAction.kt @@ -16,7 +16,7 @@ import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.getIntent 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 { val packageName = appInfo.packageName.toString() diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/Gesture.kt b/app/src/main/java/de/jrpie/android/launcher/actions/Gesture.kt index 6270eba..e7358ba 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/Gesture.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/Gesture.kt @@ -2,6 +2,7 @@ package de.jrpie.android.launcher.actions import android.content.Context 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. @@ -32,14 +33,18 @@ enum class Gesture( R.string.settings_gesture_description_vol_down, R.array.default_volume_down, 0, 0 ), - TIME("action.time", + TIME( + "action.time", R.string.settings_gesture_time, R.string.settings_gesture_description_time, - R.array.default_time), - DATE("action.date", + R.array.default_time + ), + DATE( + "action.date", R.string.settings_gesture_date, R.string.settings_gesture_description_date, - R.array.default_date), + R.array.default_date + ), LONG_CLICK( "action.long_click", R.string.settings_gesture_long_click, @@ -52,11 +57,13 @@ enum class Gesture( R.string.settings_gesture_description_double_click, R.array.default_double_click, 0, 0 ), - SWIPE_UP("action.up", + SWIPE_UP( + "action.up", R.string.settings_gesture_up, R.string.settings_gesture_description_up, R.array.default_up, - R.anim.bottom_up), + R.anim.bottom_up + ), SWIPE_UP_LEFT_EDGE( "action.up_left", R.string.settings_gesture_up_left_edge, @@ -110,28 +117,28 @@ enum class Gesture( "action.left", R.string.settings_gesture_left, R.string.settings_gesture_description_left, - R.array.default_left, + R.array.default_messengers, R.anim.right_left ), SWIPE_LEFT_TOP_EDGE( "action.left_top", R.string.settings_gesture_left_top_edge, R.string.settings_gesture_description_left_top_edge, - R.array.default_left_top, + R.array.default_messengers, R.anim.right_left ), SWIPE_LEFT_BOTTOM_EDGE( "action.left_bottom", R.string.settings_gesture_left_bottom_edge, R.string.settings_gesture_description_left_bottom_edge, - R.array.default_left_bottom, + R.array.default_messengers, R.anim.right_left ), SWIPE_LEFT_DOUBLE( "action.double_left", R.string.settings_gesture_double_left, R.string.settings_gesture_description_double_left, - R.array.default_double_left, + R.array.default_messengers, R.anim.right_left ), SWIPE_RIGHT( @@ -170,6 +177,7 @@ enum class Gesture( fun getLabel(context: Context): String { return context.resources.getString(this.labelResource) } + fun getDescription(context: Context): String { 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) { val action = Action.forGesture(this) Action.launch(action, context, this.animationIn, this.animationOut) diff --git a/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt b/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt index 004c992..b51adf2 100644 --- a/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt +++ b/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt @@ -1,5 +1,8 @@ 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 java.util.Locale import kotlin.text.Regex.Companion.escapeReplacement @@ -14,20 +17,29 @@ class AppFilter( val hidden = LauncherPreferences.apps().hidden() ?: setOf() val favorites = LauncherPreferences.apps().favorites() ?: setOf() + apps = apps.filter { info -> favoritesVisibility.predicate(favorites, 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 - var allowedSpecialCharacters = search + val allowedSpecialCharacters = search .lowercase(Locale.ROOT) .toCharArray() .distinct() .filter { c -> !c.isLetter() } .map { c -> escapeReplacement(c.toString()) } .fold("") { x, y -> x + y } - var disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex() + val disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex() fun normalize(text: String): String { return text.lowercase(Locale.ROOT).replace(disallowedCharsRegex, "") diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java index c876fc3..b98a157 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java @@ -28,6 +28,7 @@ import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSeriali @PreferenceGroup(name = "apps", prefix = "settings_apps_", suffix = "_key", value = { @Preference(name = "favorites", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class), @Preference(name = "hidden", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class), + @Preference(name = "hide_bound_apps", type = boolean.class, defaultValue = "false"), }), @PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = { }), diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/actions/SettingsFragmentActionsRecycler.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/actions/SettingsFragmentActionsRecycler.kt index e8465e0..6a23caf 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/actions/SettingsFragmentActionsRecycler.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/actions/SettingsFragmentActionsRecycler.kt @@ -155,12 +155,7 @@ class ActionsRecyclerAdapter(val activity: Activity) : } init { - val doubleActions = LauncherPreferences.enabled_gestures().doubleSwipe() - val edgeActions = LauncherPreferences.enabled_gestures().edgeSwipe() - gesturesList = Gesture.entries.filter { - (doubleActions || !it.isDoubleVariant()) - && (edgeActions || !it.isEdgeVariant()) - } as ArrayList + gesturesList = Gesture.entries.filter(Gesture::isEnabled) as ArrayList } fun updateActions() { diff --git a/app/src/main/res/values/defaults.xml b/app/src/main/res/values/defaults.xml index 84e4781..e532965 100644 --- a/app/src/main/res/values/defaults.xml +++ b/app/src/main/res/values/defaults.xml @@ -61,6 +61,7 @@ + net.thunderbird.android com.fsck.k9 de.web.mobile.android.mail com.samsung.android.email.provider @@ -87,31 +88,22 @@ info.tangential.cone - - + de.spiritcroc.riotx io.element.android.x im.vector.app org.thoughtcrime.securesms - - - + org.briarproject.briar.android + eu.siacs.conversations + ch.threema.app.libre com.android.messaging - com.android.google.messaging + com.google.android.apps.messaging com.samsung.android.messaging - - - org.thoughtcrime.securesms - - - - - com.whatsapp org.telegram.messenger + com.discord - launcher:volumeUp @@ -134,6 +126,8 @@ com.beemdevelopment.aegis org.fedorahosted.freeotp + proton.android.pass.fdroid + com.kunzisoft.keepass.libre launcher:settings diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index d3cac4b..fc255ab 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -11,6 +11,7 @@ internal.version_code apps.favorites apps.hidden + apps.hide_bound_apps general.select_launcher