From a7ce5b922204fcfba7468ca6d24e243166ca6374 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Mon, 13 Jan 2025 02:50:41 +0100 Subject: [PATCH 001/121] update release script and BUILD.md --- .scripts/release.sh | 24 ++++++++++++++---------- BUILD.md | 19 +++++++++++++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/.scripts/release.sh b/.scripts/release.sh index e4e59f7..4bba806 100755 --- a/.scripts/release.sh +++ b/.scripts/release.sh @@ -23,13 +23,17 @@ echo "=======================" ./gradlew clean ./gradlew assembleDefaultRelease -mv app/build/outputs/apk/default/release/app-default-release-unsigned.apk "$OUTPUT_DIR/unsigned.apk" -$BUILD_TOOLS_DIR/apksigner sign --ks "$KEYSTORE" \ +mv app/build/outputs/apk/default/release/app-default-release-unsigned.apk "$OUTPUT_DIR/app-release.apk" +"$BUILD_TOOLS_DIR/apksigner" sign --ks "$KEYSTORE" \ --ks-key-alias key0 \ --ks-pass="pass:$KEYSTORE_PASS" \ --key-pass="pass:$KEYSTORE_PASS" \ - --v1-signing-enabled=true --v2-signing-enabled=true --v3-signing-enabled=true --v4-signing-enabled=true \ - "$OUTPUT_DIR/unsigned.apk" + --alignment-preserved \ + --v1-signing-enabled=true \ + --v2-signing-enabled=true \ + --v3-signing-enabled=true \ + --v4-signing-enabled=true \ + "$OUTPUT_DIR/app-release.apk" echo echo "=======================" @@ -38,17 +42,15 @@ echo "=======================" ./gradlew clean ./gradlew bundleDefaultRelease -mv app/build/outputs/bundle/defaultRelease/app-default-release.aab $OUTPUT_DIR/app-release.aab -$BUILD_TOOLS_DIR/apksigner sign --ks "$KEYSTORE" \ +mv app/build/outputs/bundle/defaultRelease/app-default-release.aab "$OUTPUT_DIR/app-release.aab" +"$BUILD_TOOLS_DIR/apksigner" sign --ks "$KEYSTORE" \ --ks-key-alias key0 \ --ks-pass="pass:$KEYSTORE_PASS" \ --key-pass="pass:$KEYSTORE_PASS" \ --v1-signing-enabled=true --v2-signing-enabled=true --v3-signing-enabled=true --v4-signing-enabled=true \ --min-sdk-version=21 \ - --target-sdk-version=35 \ "$OUTPUT_DIR/app-release.aab" - echo echo "=======================" echo " Accrescent (apks) " @@ -56,8 +58,10 @@ echo "=======================" ./gradlew clean ./gradlew bundleAccrescentRelease -mv app/build/outputs/bundle/accrescentRelease/app-accrescent-release.aab $OUTPUT_DIR/app-accrescent-release.aab -$JAVA_HOME/bin/java -jar /opt/android/bundletool.jar build-apks \ +mv app/build/outputs/bundle/accrescentRelease/app-accrescent-release.aab "$OUTPUT_DIR/app-accrescent-release.aab" + +# build apks using bundletool from https://github.com/google/bundletool/releases +"$JAVA_HOME/bin/java" -jar /opt/android/bundletool.jar build-apks \ --bundle="$OUTPUT_DIR/app-accrescent-release.aab" --output="$OUTPUT_DIR/launcher-accrescent.apks" \ --ks="$KEYSTORE_ACCRESCENT" \ --ks-pass="pass:$KEYSTORE_ACCRESCENT_PASS" \ diff --git a/BUILD.md b/BUILD.md index ac1f11d..75921f9 100644 --- a/BUILD.md +++ b/BUILD.md @@ -8,10 +8,25 @@ Make sure that `JAVA_HOME` and `ANDROID_HOME` are set correctly. ``` git clone https://github.com/jrpie/Launcher cd Launcher -./gradlew build + +./gradlew assembleDefaultRelease ``` -Note that you need to sign the apk. +This will create an apk file at `app/build/outputs/apk/default/release/app-default-release-unsigned.apk`. + +Note that you need to sign it: +``` +apksigner sign --ks "$YOUR_KEYSTORE" \ + --ks-key-alias "$YOUR_ALIAS" \ + --ks-pass="pass:$YOUR_PASSWORD" \ + --key-pass="pass:$YOUR_PASSWORD" \ + --alignment-preserved \ + --v1-signing-enabled=true \ + --v2-signing-enabled=true \ + --v3-signing-enabled=true \ + --v4-signing-enabled=true \ + app-default-release-unsigned.apk +``` See [this guide](https://developer.android.com/build/building-cmdline) From 5696ea73daf0116c1757c10584039bf0d78acb2e Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Mon, 13 Jan 2025 03:01:28 +0100 Subject: [PATCH 002/121] update short description --- fastlane/metadata/android/en-US/short_description.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt index 150a1b5..548e686 100644 --- a/fastlane/metadata/android/en-US/short_description.txt +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -1 +1 @@ -A distraction-free minimal homescreen for Android. +A distraction-free, minimal homescreen for Android. From a188e06342639a18d60da49b7b00b195ecc46699 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Mon, 13 Jan 2025 14:37:29 +0100 Subject: [PATCH 003/121] update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 207fe34..b29b44d 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,10 @@ This is a fork of [finnmglas's app Launcher][original-repo]. Get it on F-Droid +Get it on Accrescent Get it on Obtainium Get it on GitHub - -or download the latest APK from the [releases section](https://github.com/jrpie/Launcher/releases/latest). You can also [get it on Google Play](https://play.google.com/store/apps/details?id=de.jrpie.android.launcher), but I don't recommend that. ` of `fix/` and commit your changes. + - Create a new branch named `feature/` or `fix/` and commit your changes. - Open a new pull request. See [BUILD.md](BUILD.md) for instructions how to build this project. +The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds. +Note that those are not signed. ## Notable changes compared to [Finn's Launcher][original-repo]: From 95c9fcd2922fb0f978211544463280e0914eccdd Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Mon, 13 Jan 2025 16:48:34 +0100 Subject: [PATCH 004/121] shorten 'auto launch disabled' to 'no auto launch' --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fb91c0..5bb2c61 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -202,7 +202,7 @@ Unable to remove application Search - Search (auto launch disabled) + Search (no auto launch) µLauncher Settings All Applications From 679c90130db19d17c575841daa65043d407dcd70 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Tue, 21 Jan 2025 23:05:49 +0100 Subject: [PATCH 005/121] re-enable the light theme close #75 when the light theme is selected, the background is always set to solid, to prevent problems with dark wallpapers --- .../android/launcher/preferences/theme/Background.kt | 8 +++++++- .../android/launcher/preferences/theme/ColorTheme.kt | 2 +- .../ui/settings/launcher/SettingsFragmentLauncher.kt | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/theme/Background.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/theme/Background.kt index da301c0..ca385af 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/theme/Background.kt +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/theme/Background.kt @@ -16,7 +16,13 @@ enum class Background(val id: Int, val dim: Boolean = false, val blur: Boolean = ; fun applyToTheme(theme: Resources.Theme) { - theme.applyStyle(id, true) + var background = this + + // force a solid background when using the light theme + if (LauncherPreferences.theme().colorTheme() == ColorTheme.LIGHT) { + background = SOLID + } + theme.applyStyle(background.id, true) } fun applyToWindow(window: Window) { diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt index 1e57f9f..8cef124 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt @@ -26,7 +26,7 @@ enum class ColorTheme( R.style.colorThemeLight, R.string.settings_theme_color_theme_item_light, R.style.textShadowLight, - { false }), + { true }), DYNAMIC( R.style.colorThemeDynamic, R.string.settings_theme_color_theme_item_dynamic, diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt index 0ae08c2..0b054ce 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt @@ -34,6 +34,12 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() { ) val timeVisible = LauncherPreferences.clock().timeVisible() showSeconds?.isVisible = timeVisible + + val background = findPreference( + LauncherPreferences.theme().keys().background() + ) + val lightTheme = LauncherPreferences.theme().colorTheme() == ColorTheme.LIGHT + background?.isVisible = !lightTheme } override fun onStart() { From 785e024ddb81735cd91e538959ad87bcadc732e8 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Wed, 22 Jan 2025 01:44:51 +0100 Subject: [PATCH 006/121] basic support for private space (#98) * add an action to toggle private space lock * hide apps from private space if private space is locked --- .../de/jrpie/android/launcher/Application.kt | 28 +++++++++ .../de/jrpie/android/launcher/Functions.kt | 61 +++++++++++++------ .../launcher/actions/LauncherAction.kt | 56 ++++++++++++++++- .../LauncherPreferences$Config.java | 3 +- .../launcher/preferences/theme/ColorTheme.kt | 2 +- .../jrpie/android/launcher/ui/HomeActivity.kt | 4 -- .../ui/list/other/OtherRecyclerAdapter.kt | 3 +- .../launcher/SettingsFragmentLauncher.kt | 5 ++ .../res/drawable/baseline_security_24.xml | 12 ++++ app/src/main/res/values/donottranslate.xml | 1 + app/src/main/res/values/strings.xml | 7 +++ app/src/main/res/xml/preferences.xml | 5 ++ 12 files changed, 159 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_security_24.xml diff --git a/app/src/main/java/de/jrpie/android/launcher/Application.kt b/app/src/main/java/de/jrpie/android/launcher/Application.kt index e3e5f7c..6e924c2 100644 --- a/app/src/main/java/de/jrpie/android/launcher/Application.kt +++ b/app/src/main/java/de/jrpie/android/launcher/Application.kt @@ -1,5 +1,9 @@ package de.jrpie.android.launcher +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.SharedPreferences import android.content.pm.LauncherApps import android.content.pm.ShortcutInfo @@ -7,6 +11,7 @@ import android.os.AsyncTask import android.os.Build import android.os.Build.VERSION_CODES import android.os.UserHandle +import androidx.core.content.ContextCompat import androidx.lifecycle.MutableLiveData import androidx.preference.PreferenceManager import de.jrpie.android.launcher.actions.TorchManager @@ -19,6 +24,14 @@ import de.jrpie.android.launcher.preferences.resetPreferences class Application : android.app.Application() { val apps = MutableLiveData>() + private val profileAvailabilityBroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + // TODO: only update specific apps + // use Intent.EXTRA_USER + loadApps() + } + } + // TODO: only update specific apps private val launcherAppsCallback = object : LauncherApps.Callback() { override fun onPackageRemoved(p0: String?, p1: UserHandle?) { @@ -107,6 +120,21 @@ class Application : android.app.Application() { val launcherApps = getSystemService(LAUNCHER_APPS_SERVICE) as LauncherApps launcherApps.registerCallback(launcherAppsCallback) + if (Build.VERSION.SDK_INT >= VERSION_CODES.N) { + val filter = IntentFilter().also { + if (Build.VERSION.SDK_INT >= VERSION_CODES.VANILLA_ICE_CREAM) { + it.addAction(Intent.ACTION_PROFILE_AVAILABLE) + it.addAction(Intent.ACTION_PROFILE_UNAVAILABLE) + } else { + it.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) + it.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) + } + } + ContextCompat.registerReceiver(this, profileAvailabilityBroadcastReceiver, filter, + ContextCompat.RECEIVER_EXPORTED + ) + } + loadApps() } diff --git a/app/src/main/java/de/jrpie/android/launcher/Functions.kt b/app/src/main/java/de/jrpie/android/launcher/Functions.kt index e8d3851..48b5028 100644 --- a/app/src/main/java/de/jrpie/android/launcher/Functions.kt +++ b/app/src/main/java/de/jrpie/android/launcher/Functions.kt @@ -18,6 +18,7 @@ import de.jrpie.android.launcher.actions.Action import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo +import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.tutorial.TutorialActivity @@ -30,31 +31,36 @@ const val REQUEST_SET_DEFAULT_HOME = 42 const val LOG_TAG = "Launcher" -fun setDefaultHomeScreen(context: Context, checkDefault: Boolean = false) { - - if (checkDefault - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - && context is Activity - ) { +fun isDefaultHomeScreen(context: Context): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val roleManager = context.getSystemService(RoleManager::class.java) - if (!roleManager.isRoleHeld(RoleManager.ROLE_HOME)) { - context.startActivityForResult( - roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME), - REQUEST_SET_DEFAULT_HOME - ) - } - return - } - - if (checkDefault) { + return roleManager.isRoleHeld(RoleManager.ROLE_HOME) + } else { val testIntent = Intent(Intent.ACTION_MAIN) testIntent.addCategory(Intent.CATEGORY_HOME) val defaultHome = testIntent.resolveActivity(context.packageManager)?.packageName - if (defaultHome == context.packageName) { - // Launcher is already the default home app - return - } + return defaultHome == context.packageName } +} + +fun setDefaultHomeScreen(context: Context, checkDefault: Boolean = false) { + if (checkDefault && isDefaultHomeScreen(context)) { + // Launcher is already the default home app + return + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + && context is Activity + && checkDefault // using role manager only works when µLauncher is not already the default. + ) { + val roleManager = context.getSystemService(RoleManager::class.java) + context.startActivityForResult( + roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME), + REQUEST_SET_DEFAULT_HOME + ) + return + } + val intent = Intent(Settings.ACTION_HOME_SETTINGS) context.startActivity(intent) } @@ -94,6 +100,21 @@ fun getApps(packageManager: PackageManager, context: Context): MutableList= Build.VERSION_CODES.N) { + if (userManager.isQuietModeEnabled(user)) { + // hide paused apps + if (LauncherPreferences.apps().hidePausedApps()) { + continue + } + // hide apps from private space + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && + launcherApps.getLauncherUserInfo(user)?.userType == UserManager.USER_TYPE_PROFILE_PRIVATE + ) { + continue + } + } + } launcherApps.getActivityList(null, user).forEach { loadList.add(DetailedAppInfo(it)) } diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt index 3c641ff..2eddf16 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt @@ -1,17 +1,22 @@ package de.jrpie.android.launcher.actions +import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent +import android.content.pm.LauncherApps import android.graphics.Rect import android.graphics.drawable.Drawable import android.media.AudioManager import android.os.Build import android.os.SystemClock +import android.os.UserManager +import android.provider.Settings import android.view.KeyEvent import android.widget.Toast import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.R import de.jrpie.android.launcher.apps.AppFilter +import de.jrpie.android.launcher.isDefaultHomeScreen import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.settings.SettingsActivity @@ -33,7 +38,8 @@ enum class LauncherAction( val id: String, val label: Int, val icon: Int, - val launch: (Context) -> Unit + val launch: (Context) -> Unit, + val available: (Context) -> Boolean = { true } ) : Action { SETTINGS( "settings", @@ -53,6 +59,13 @@ enum class LauncherAction( R.drawable.baseline_favorite_24, { context -> openAppsList(context, true) } ), + TOGGLE_PRIVATE_SPACE_LOCK( + "toggle_private_space_lock", + R.string.list_other_toggle_private_space_lock, + R.drawable.baseline_security_24, + ::togglePrivateSpaceLock, + available = { Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM } + ), VOLUME_UP( "volume_up", R.string.list_other_volume_up, @@ -207,6 +220,46 @@ private fun expandNotificationsPanel(context: Context) { } } +private fun togglePrivateSpaceLock(context: Context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + Toast.makeText( + context, + context.getString(R.string.alert_requires_android_v), + Toast.LENGTH_LONG + ).show() + return + } + val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager + val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + val privateSpaceUser = userManager.userProfiles.firstOrNull { u -> + launcherApps.getLauncherUserInfo(u)?.userType == UserManager.USER_TYPE_PROFILE_PRIVATE + } + if (privateSpaceUser == null) { + Toast.makeText(context, context.getString(R.string.toast_private_space_not_available), Toast.LENGTH_LONG).show() + + if (!isDefaultHomeScreen(context)) { + Toast.makeText(context, context.getString(R.string.toast_private_space_default_home_screen), Toast.LENGTH_LONG).show() + return + } + + try { + context.startActivity(Intent(Settings.ACTION_PRIVACY_SETTINGS)) + } catch (_: ActivityNotFoundException) {} + return + } + if (userManager.isQuietModeEnabled(privateSpaceUser)) { + userManager.requestQuietModeEnabled(false, privateSpaceUser) + Toast.makeText( + context, + context.getString(R.string.toast_private_space_unlocked), + Toast.LENGTH_LONG + ).show() + return + } + userManager.requestQuietModeEnabled(true, privateSpaceUser) + Toast.makeText(context, context.getString(R.string.toast_private_space_locked), Toast.LENGTH_LONG).show() +} + private fun expandSettingsPanel(context: Context) { /* https://stackoverflow.com/a/31898506 */ try { @@ -260,6 +313,7 @@ private class LauncherActionSerializer : KSerializer { ) { element("value", String.serializer().descriptor) } + override fun deserialize(decoder: Decoder): LauncherAction { val s = decoder.decodeStructure(descriptor) { decodeElementIndex(descriptor) 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 640e6ce..98496ce 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 @@ -5,8 +5,8 @@ import java.util.Set; import de.jrpie.android.launcher.R; import de.jrpie.android.launcher.actions.lock.LockMethod; -import de.jrpie.android.launcher.preferences.serialization.SetAppInfoPreferenceSerializer; import de.jrpie.android.launcher.preferences.serialization.MapAppInfoStringPreferenceSerializer; +import de.jrpie.android.launcher.preferences.serialization.SetAppInfoPreferenceSerializer; import de.jrpie.android.launcher.preferences.theme.Background; import de.jrpie.android.launcher.preferences.theme.ColorTheme; import de.jrpie.android.launcher.preferences.theme.Font; @@ -29,6 +29,7 @@ import eu.jonahbauer.android.preference.annotations.Preferences; @Preference(name = "hidden", type = Set.class, serializer = SetAppInfoPreferenceSerializer.class), @Preference(name = "custom_names", type = HashMap.class, serializer = MapAppInfoStringPreferenceSerializer.class), @Preference(name = "hide_bound_apps", type = boolean.class, defaultValue = "false"), + @Preference(name = "hide_paused_apps", type = boolean.class, defaultValue = "false"), }), @PreferenceGroup(name = "list", prefix = "settings_list_", suffix = "_key", value = { @Preference(name = "layout", type = ListLayout.class, defaultValue = "DEFAULT") diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt index 8cef124..816d94f 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/theme/ColorTheme.kt @@ -2,8 +2,8 @@ package de.jrpie.android.launcher.preferences.theme import android.content.Context import android.content.res.Resources -import de.jrpie.android.launcher.R import com.google.android.material.color.DynamicColors +import de.jrpie.android.launcher.R @Suppress("unused") enum class ColorTheme( diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt index 5633113..b1a5cc0 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt @@ -3,7 +3,6 @@ package de.jrpie.android.launcher.ui import android.annotation.SuppressLint import android.content.SharedPreferences import android.content.res.Resources -import android.os.AsyncTask import android.os.Build import android.os.Bundle import android.util.DisplayMetrics @@ -20,10 +19,7 @@ import de.jrpie.android.launcher.actions.Action import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.actions.LauncherAction import de.jrpie.android.launcher.databinding.HomeBinding -import de.jrpie.android.launcher.openTutorial import de.jrpie.android.launcher.preferences.LauncherPreferences -import de.jrpie.android.launcher.preferences.migratePreferencesToNewVersion -import de.jrpie.android.launcher.preferences.resetPreferences import de.jrpie.android.launcher.ui.tutorial.TutorialActivity import java.text.SimpleDateFormat import java.util.Date diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt b/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt index ddfcfd1..97d1c84 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt @@ -23,7 +23,8 @@ import de.jrpie.android.launcher.ui.list.forGesture class OtherRecyclerAdapter(val activity: Activity) : RecyclerView.Adapter() { - private val othersList: Array = LauncherAction.values() + private val othersList: Array = + LauncherAction.entries.filter { it.isAvailable(activity) }.toTypedArray() inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt index 0b054ce..a8efb43 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt @@ -40,6 +40,11 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() { ) val lightTheme = LauncherPreferences.theme().colorTheme() == ColorTheme.LIGHT background?.isVisible = !lightTheme + + val hidePausedApps = findPreference( + LauncherPreferences.apps().keys().hidePausedApps() + ) + hidePausedApps?.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N } override fun onStart() { diff --git a/app/src/main/res/drawable/baseline_security_24.xml b/app/src/main/res/drawable/baseline_security_24.xml new file mode 100644 index 0000000..3c260ff --- /dev/null +++ b/app/src/main/res/drawable/baseline_security_24.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 0a30fac..b2e69a1 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -13,6 +13,7 @@ apps.hidden apps.custom_names apps.hide_bound_apps + apps.hide_paused_apps list.layout general.select_launcher diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5bb2c61..4cdebb1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -145,6 +145,7 @@ Apps Hidden apps Don\'t show apps that are bound to a gesture in the app list + Hide paused apps Layout of app list Default @@ -207,6 +208,7 @@ µLauncher Settings All Applications Favorite Applications + Toggle Private Space Lock Music: Louder Music: Quieter Music: Next @@ -245,6 +247,7 @@ More options Error: Can\'t expand status bar. This action is using functionality that is not part of the published Android API. Unfortunately, it does not seem to work on your device. This functionality requires Android 6.0 or later. + This functionality requires Android 15.0 or later. App hidden. You can make it visible again in settings. Undo Quick Settings @@ -255,6 +258,10 @@ Error: Can\'t access torch. Error: Failed to lock screen. (If you just upgraded the app, try to disable and re-enable the accessibility service in phone settings) μLauncher\'s accessibility service is not enabled. Please enable it in settings + Private space locked + Private space unlocked + Private space is not available + µLauncher needs to be the default home screen to access private space. Error: Locking the screen using accessibility is not supported on this device. Please use device admin instead. µLauncher - lock screen diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d1dcbe6..57e50df 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -145,6 +145,11 @@ android:title="@string/settings_apps_hide_bound_apps" android:defaultValue="false" /> + + Date: Wed, 22 Jan 2025 02:17:46 +0100 Subject: [PATCH 007/121] use TextClock instead of custom solution --- .../jrpie/android/launcher/ui/HomeActivity.kt | 44 ++++--------------- app/src/main/res/layout/home.xml | 4 +- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt index b1a5cc0..1d5381d 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt @@ -21,8 +21,6 @@ import de.jrpie.android.launcher.actions.LauncherAction import de.jrpie.android.launcher.databinding.HomeBinding import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.tutorial.TutorialActivity -import java.text.SimpleDateFormat -import java.util.Date import java.util.Locale import java.util.Timer import kotlin.concurrent.fixedRateTimer @@ -65,8 +63,6 @@ class HomeActivity : UIObject, AppCompatActivity(), private lateinit var mDetector: GestureDetectorCompat - // timers - private var clockTimer = Timer() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -100,30 +96,24 @@ class HomeActivity : UIObject, AppCompatActivity(), } - private fun updateClock() { - clockTimer.cancel() + private fun initClock() { val locale = Locale.getDefault() val dateVisible = LauncherPreferences.clock().dateVisible() val timeVisible = LauncherPreferences.clock().timeVisible() var dateFMT = "yyyy-MM-dd" var timeFMT = "HH:mm" - val period = 100L if (LauncherPreferences.clock().showSeconds()) { timeFMT += ":ss" } - /* - I thought about adding an option to show microseconds as well ( timeFMT += ".SSS" ). - However setting period ot 1L (or even 10L) causes high CPU load, - so that doesn't seem to be a good idea. - */ + if (LauncherPreferences.clock().localized()) { dateFMT = android.text.format.DateFormat.getBestDateTimePattern(locale, dateFMT) timeFMT = android.text.format.DateFormat.getBestDateTimePattern(locale, timeFMT) } - var upperFormat = SimpleDateFormat(dateFMT, locale) - var lowerFormat = SimpleDateFormat(timeFMT, locale) + var upperFormat = dateFMT + var lowerFormat = timeFMT var upperVisible = dateVisible var lowerVisible = timeVisible @@ -138,21 +128,10 @@ class HomeActivity : UIObject, AppCompatActivity(), binding.homeUpperView.setTextColor(LauncherPreferences.clock().color()) binding.homeLowerView.setTextColor(LauncherPreferences.clock().color()) - - clockTimer = fixedRateTimer("clockTimer", true, 0L, period) { - this@HomeActivity.runOnUiThread { - if (lowerVisible) { - val t = lowerFormat.format(Date()) - if (binding.homeLowerView.text != t) - binding.homeLowerView.text = t - } - if (upperVisible) { - val d = upperFormat.format(Date()) - if (binding.homeUpperView.text != d) - binding.homeUpperView.text = d - } - } - } + binding.homeLowerView.format24Hour = lowerFormat + binding.homeUpperView.format24Hour = upperFormat + binding.homeLowerView.format12Hour = lowerFormat + binding.homeUpperView.format12Hour = upperFormat } override fun getTheme(): Resources.Theme { @@ -172,12 +151,7 @@ class HomeActivity : UIObject, AppCompatActivity(), edgeWidth = LauncherPreferences.enabled_gestures().edgeSwipeEdgeWidth() / 100f - updateClock() - } - - override fun onPause() { - super.onPause() - clockTimer.cancel() + initClock() } override fun onDestroy() { diff --git a/app/src/main/res/layout/home.xml b/app/src/main/res/layout/home.xml index 5f34412..f59b211 100644 --- a/app/src/main/res/layout/home.xml +++ b/app/src/main/res/layout/home.xml @@ -10,7 +10,7 @@ android:fitsSystemWindows="true" tools:context=".ui.HomeActivity"> - - Date: Fri, 24 Jan 2025 23:45:19 +0100 Subject: [PATCH 008/121] implement #102 - show version in settings and add bug report dialog --- .../de/jrpie/android/launcher/Functions.kt | 20 +++++++ .../ui/settings/meta/SettingsFragmentMeta.kt | 42 ++++++++++++--- app/src/main/res/layout/dialog_report_bug.xml | 53 +++++++++++++++++++ app/src/main/res/layout/settings_meta.xml | 15 +++++- app/src/main/res/values/donottranslate.xml | 3 +- app/src/main/res/values/strings.xml | 7 +++ 6 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 app/src/main/res/layout/dialog_report_bug.xml diff --git a/app/src/main/java/de/jrpie/android/launcher/Functions.kt b/app/src/main/java/de/jrpie/android/launcher/Functions.kt index 48b5028..7566f5f 100644 --- a/app/src/main/java/de/jrpie/android/launcher/Functions.kt +++ b/app/src/main/java/de/jrpie/android/launcher/Functions.kt @@ -3,6 +3,8 @@ package de.jrpie.android.launcher import android.app.Activity import android.app.Service import android.app.role.RoleManager +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.content.pm.LauncherApps @@ -151,3 +153,21 @@ fun saveListActivityChoice(data: Intent?) { Gesture.byId(forGesture)?.let { Action.setActionForGesture(it, Action.fromIntent(data)) } } + +// used for the bug report button +fun getDeviceInfo(): String { + return """ + µLauncher version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) + Android version: ${Build.VERSION.RELEASE} (sdk ${Build.VERSION.SDK_INT}) + Model: ${Build.MODEL} + Device: ${Build.DEVICE} + Brand: ${Build.BRAND} + Manufacturer: ${Build.MANUFACTURER} + """.trimIndent() +} + +fun copyToClipboard(context: Context, text: String) { + val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipData = ClipData.newPlainText("Debug Info", text) + clipboardManager.setPrimaryClip(clipData); +} \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/meta/SettingsFragmentMeta.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/meta/SettingsFragmentMeta.kt index d53ea99..fd3a738 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/meta/SettingsFragmentMeta.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/meta/SettingsFragmentMeta.kt @@ -7,9 +7,14 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView import androidx.fragment.app.Fragment +import de.jrpie.android.launcher.BuildConfig import de.jrpie.android.launcher.R +import de.jrpie.android.launcher.copyToClipboard import de.jrpie.android.launcher.databinding.SettingsMetaBinding +import de.jrpie.android.launcher.getDeviceInfo import de.jrpie.android.launcher.openInBrowser import de.jrpie.android.launcher.preferences.resetPreferences import de.jrpie.android.launcher.ui.LegalInfoActivity @@ -88,13 +93,37 @@ class SettingsFragmentMeta : Fragment(), UIObject { // report a bug binding.settingsMetaButtonReportBug.setOnClickListener { - openInBrowser( - getString(R.string.settings_meta_report_bug_link), - requireContext() - ) + val deviceInfo = getDeviceInfo() + AlertDialog.Builder(context, R.style.AlertDialogCustom).apply { + setView(R.layout.dialog_report_bug) + setTitle(R.string.dialog_report_bug_title) + setPositiveButton(R.string.dialog_report_bug_create_report) { _, _ -> + openInBrowser( + getString(R.string.settings_meta_report_bug_link), + requireContext() + ) + } + setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.create().also { it.show() }.apply { + val info = findViewById(R.id.dialog_report_bug_device_info) + val buttonClipboard = findViewById