From 38a02974d518aed72b9ad40de4a36c04f44fb4f3 Mon Sep 17 00:00:00 2001 From: Seb3thehacker Date: Fri, 6 Nov 2026 14:35:06 -0800 Subject: [PATCH] Known-broken apps show with a red X and start unticked instead of being hidden --- .../seb3thehacker/gearslip/car/CarLauncher.kt | 4 ++++ .../seb3thehacker/gearslip/car/CarSettings.kt | 16 +++++++++++++ .../gearslip/host/CarAppCatalog.kt | 1 - .../seb3thehacker/gearslip/host/KnownApps.kt | 23 ++++++++++++------- .../gearslip/media/MediaCatalog.kt | 1 - .../gearslip/notify/MessagingCatalog.kt | 2 +- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt index caeac98..b5a4a6c 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt @@ -149,6 +149,10 @@ internal object LauncherCache { } } } + CarSettings.hideOnFirstSight(tiles.mapNotNull { entry -> + val pkg = (entry.template?.component ?: entry.media?.component)?.packageName ?: entry.messaging?.packageName + entry.componentId?.takeIf { pkg != null && KnownApps.startsHidden(context, pkg, entry.messaging != null) } + }) return tiles.sortedBy { it.label.lowercase() }.also { flow.value = it } } } diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarSettings.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarSettings.kt index 8907b8c..5824e1c 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarSettings.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarSettings.kt @@ -69,6 +69,7 @@ private const val KEY_LOCATION_WARNING = "car_location_warning" private const val KEY_PLAYER_TITLE = "car_player_title" private const val KEY_PLAYER_LEFT = "car_player_left" private const val KEY_NAV_MIC = "car_nav_mic" + private const val KEY_DEFAULT_HIDDEN = "car_default_hidden" private const val KEY_THEME = "car_app_theme" private const val KEY_UI_THEME = "car_ui_theme" private const val KEY_DARK_STYLE = "car_dark_style" @@ -586,6 +587,21 @@ private const val KEY_CLUSTER_VIDEO = "cluster_video_enabled" AppSettings.putString(app, KEY_PINNED, next.joinToString(",")) } + /** + * Unticks each of [ids] the first time it's seen, and never again: a known-broken app starts + * off the drawer, but once the driver ticks it back on it stays on. + */ + fun hideOnFirstSight(ids: List) { + if (!::app.isInitialized) return + val seen = AppSettings.getString(app, KEY_DEFAULT_HIDDEN, "").split(",").filter { it.isNotEmpty() }.toSet() + val fresh = ids.filter { it !in seen } + if (fresh.isEmpty()) return + AppSettings.putString(app, KEY_DEFAULT_HIDDEN, (seen + fresh).joinToString(",")) + val next = hiddenAppsFlow.value + fresh + hiddenAppsFlow.value = next + AppSettings.putString(app, KEY_HIDDEN_APPS, next.joinToString(",")) + } + fun setAppHidden(id: String, hide: Boolean) { val next = if (hide) hiddenAppsFlow.value + id else hiddenAppsFlow.value - id hiddenAppsFlow.value = next diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppCatalog.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppCatalog.kt index b8b77d7..5d5b4c3 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppCatalog.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppCatalog.kt @@ -44,7 +44,6 @@ object CarAppCatalog { .asSequence() .filter { it.serviceInfo.packageName != context.packageName } .filterNot { AndroidAuto.matches(it.serviceInfo.packageName, it.loadLabel(manager).toString()) } - .filterNot { KnownApps.isHidden(it.serviceInfo.packageName) } .map { resolved -> val info = resolved.serviceInfo TemplateApp( diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt index 60e2fa8..9319c3c 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt @@ -52,27 +52,34 @@ object KnownApps { fun screenBroken(packageName: String): Boolean = packageName in brokenScreen /** - * Registers a car template service but never gives Gearslip a usable screen - left off the - * launcher entirely rather than shown as a tile that goes nowhere. + * Google's own car apps, which never give Gearslip a usable screen. They used to be left off + * the launcher, which looked like a bug to anyone who had them; now they're listed like any + * other broken app, starting unticked (see [startsHidden]). */ - private val hidden = setOf( + private val google = setOf( "com.google.android.dialer", // Google Phone "com.google.android.googlequicksearchbox", // Google Assistant driving surfaces "com.google.android.apps.maps", // Google Maps: shows as a messenger, and its car UI is Android Auto's own ) - fun isHidden(packageName: String): Boolean = packageName in hidden - - /** Shown on the launcher with a red X: known broken, but expected enough that hiding it outright would look like a bug. */ - private val broken = setOf( + /** Shown on the launcher with a red X, and unticked to start with. */ + private val broken = google + setOf( "com.google.android.apps.messaging", // Google Messages - "com.waze", // Waze + "com.waze", // Waze: the official app refuses Gearslip; the Morphe-patched one works (see patched()) "com.generalmagic.magicearth", // Magic Earth: never gets past connecting ) fun isBroken(context: Context, packageName: String): Boolean = packageName in broken && !patched(context, packageName) + /** + * Whether a tile starts unticked in the phone's list of apps to show, so the drawer opens + * with only what works. Gearslip's own Messages screen works for any messenger, so a broken + * app's messaging tile still shows, except Google Maps', which has no messages worth reading. + */ + fun startsHidden(context: Context, packageName: String, messaging: Boolean): Boolean = + isBroken(context, packageName) && (!messaging || packageName in google) + /** * The certificate each broken app's own publisher signs it with. A copy signed with another * key has been patched to accept Gearslip, like Waze with the waze-gearslip Morphe patch, and diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/media/MediaCatalog.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/media/MediaCatalog.kt index 1abd7d6..9599583 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/media/MediaCatalog.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/media/MediaCatalog.kt @@ -33,7 +33,6 @@ object MediaCatalog { .asSequence() .filter { it.serviceInfo.packageName != context.packageName } .filterNot { AndroidAuto.matches(it.serviceInfo.packageName, it.loadLabel(manager).toString()) } - .filterNot { KnownApps.isHidden(it.serviceInfo.packageName) } .filter { declaresMedia(manager, it.serviceInfo) } .map { val info = it.serviceInfo diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/MessagingCatalog.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/MessagingCatalog.kt index e81c353..85c5d01 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/MessagingCatalog.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/MessagingCatalog.kt @@ -25,7 +25,7 @@ object MessagingCatalog { .map { it.activityInfo.packageName to it.loadLabel(manager).toString() } .distinctBy { it.first } .filter { (pkg, _) -> pkg != context.packageName } - .filterNot { (pkg, label) -> AndroidAuto.matches(pkg, label) || KnownApps.isHidden(pkg) } + .filterNot { (pkg, label) -> AndroidAuto.matches(pkg, label) } .filter { (pkg, _) -> CarDescriptor.uses(manager, pkg)?.contains("notification") == true } .map { (pkg, _) -> val label = runCatching {