From 03f6b7c8c1f72b94532eafa51c7d2c94ca62ae16 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 19 Jul 2026 16:39:08 +0200 Subject: [PATCH] feat(settings): optional "Calendar" launcher name (#44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Settings → Appearance toggle that switches the app's launcher label between "Calendula" and "Calendar", for users on launchers that can't rename apps themselves. The launcher entry moves off MainActivity onto two components (DefaultNameAlias / CalendarNameAlias); exactly one is enabled at a time via PackageManager.setComponentEnabledSetting. MainActivity keeps every other intent filter; the android.app.shortcuts meta-data moves onto both aliases so the long-press shortcut still publishes. Component-enabled state is the single source of truth — no persisted preference. The ComponentName uses the applicationId for the package (carrying the .debug/.releasetest suffix) and the namespace for the class, since manifest ".Alias" names resolve against the namespace; switching to Calendar enables the target alias before disabling the other to avoid a zero-entry launcher transient. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/src/main/AndroidManifest.xml | 47 +++++++- .../data/appname/LauncherNameManager.kt | 109 ++++++++++++++++++ .../calendula/ui/settings/SettingsScreen.kt | 32 +++++ .../ui/settings/SettingsViewModel.kt | 22 ++++ app/src/main/res/values/strings.xml | 6 + .../data/appname/LauncherNameManagerTest.kt | 51 ++++++++ .../android/en-US/changelogs/21600.txt | 5 + 7 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/data/appname/LauncherNameManager.kt create mode 100644 app/src/test/java/de/jeanlucmakiola/calendula/data/appname/LauncherNameManagerTest.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 930ab70..7f73919 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,10 +63,9 @@ android:exported="true" android:launchMode="singleTop" android:windowSoftInputMode="adjustResize"> - - - - + + + + + + + + + - + + + + + + + + + Calendar Loading… @@ -329,6 +333,8 @@ Show calendar-week numbers in month view Today button in toolbar Show a jump-to-today button in the toolbar instead of a floating button + App name + Show Calendula as “Calendar” in your launcher. Only the launcher name changes; the icon may move to a new spot after switching. Time format Automatic 12-hour (2:00 PM) diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/data/appname/LauncherNameManagerTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/data/appname/LauncherNameManagerTest.kt new file mode 100644 index 0000000..c806dce --- /dev/null +++ b/app/src/test/java/de/jeanlucmakiola/calendula/data/appname/LauncherNameManagerTest.kt @@ -0,0 +1,51 @@ +package de.jeanlucmakiola.calendula.data.appname + +import android.content.pm.PackageManager +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test + +/** + * Pure decision logic behind the app-name toggle (issue #44). The framework seam + * ([LauncherNameManager.set]/[current], which call `PackageManager`) is covered + * on-device; these tests pin the read interpretation and the write ordering. + */ +class LauncherNameManagerTest { + + @Test + fun `enabled calendar alias reads as CALENDAR`() { + assertThat(launcherNameFor(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) + .isEqualTo(LauncherName.CALENDAR) + } + + @Test + fun `default and disabled calendar alias read as CALENDULA`() { + assertThat(launcherNameFor(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)) + .isEqualTo(LauncherName.CALENDULA) + assertThat(launcherNameFor(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)) + .isEqualTo(LauncherName.CALENDULA) + } + + @Test + fun `write plan enables the target alias before disabling the other`() { + val toCalendar = aliasWritePlan(LauncherName.CALENDAR) + assertThat(toCalendar).containsExactly( + AliasStateChange(LauncherAlias.CALENDAR, enabled = true), + AliasStateChange(LauncherAlias.DEFAULT, enabled = false), + ).inOrder() + + val toCalendula = aliasWritePlan(LauncherName.CALENDULA) + assertThat(toCalendula).containsExactly( + AliasStateChange(LauncherAlias.DEFAULT, enabled = true), + AliasStateChange(LauncherAlias.CALENDAR, enabled = false), + ).inOrder() + } + + @Test + fun `write plan never disables both aliases in the same step`() { + // The first step is always an enable, so the launcher can never observe a + // zero-entry transient regardless of switch direction. + for (target in LauncherName.entries) { + assertThat(aliasWritePlan(target).first().enabled).isTrue() + } + } +} diff --git a/fastlane/metadata/android/en-US/changelogs/21600.txt b/fastlane/metadata/android/en-US/changelogs/21600.txt index 2cf7d30..b96a9c1 100644 --- a/fastlane/metadata/android/en-US/changelogs/21600.txt +++ b/fastlane/metadata/android/en-US/changelogs/21600.txt @@ -11,6 +11,11 @@ setting (Settings → Appearance, off by default) swaps the floating corner button for a permanent today icon in the top bar — always there, matching the familiar calendar-app pattern. Leave it off to keep the floating button ([#60]). +- Show the app as "Calendar" in your launcher. A new App name setting + (Settings → Appearance) switches the launcher label from "Calendula" to the + generic "Calendar" for anyone who prefers it — handy on launchers that can't + rename apps themselves. Only the launcher name changes; your home-screen icon + may move to a new spot after switching ([#44]). ### Changed - Dates in the Month, Week and Day title bars now follow your language and