From 73ae48ed9518c046397868b42b1268f15d2c287e Mon Sep 17 00:00:00 2001 From: StoneHub Date: Mon, 24 Aug 2026 23:30:31 -0400 Subject: [PATCH 1/4] chore: configure agent workflow --- AGENTS.md | 14 ++++++++++++++ docs/agents/domain.md | 9 +++++++++ docs/agents/issue-tracker.md | 19 +++++++++++++++++++ docs/agents/triage-labels.md | 11 +++++++++++ 4 files changed, 53 insertions(+) create mode 100644 docs/agents/domain.md create mode 100644 docs/agents/issue-tracker.md create mode 100644 docs/agents/triage-labels.md diff --git a/AGENTS.md b/AGENTS.md index afbac00..502a5f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,3 +14,17 @@ Add JVM tests alongside production packages in `app/src/test` using JUnit4, nami ## Commit & Pull Request Guidelines The current snapshot lacks Git history, so adopt Conventional Commit prefixes (`feat:`, `fix:`, `chore:`) for clarity. Keep the first line under 72 characters in the imperative mood and link issues using `Refs #123` in the body. Pull requests should summarize functional changes, call out affected components (e.g., tile service, resources), include screenshots or screen recordings when UI changes occur, and list verification steps such as emulator/API levels tested. + +## Agent skills + +### Issue tracker + +Issues and specs live in `StoneHub/screen-timeout-tile` GitHub Issues. Read `docs/agents/issue-tracker.md` before issue or planning work. + +### Triage labels + +Use the default Matt triage roles. Read `docs/agents/triage-labels.md` before triage work. + +### Domain docs + +This is a single-context project. Read `docs/agents/domain.md` before domain-modeling or architecture work. diff --git a/docs/agents/domain.md b/docs/agents/domain.md new file mode 100644 index 0000000..5bb0b81 --- /dev/null +++ b/docs/agents/domain.md @@ -0,0 +1,9 @@ +# Domain docs + +This repository uses a single domain context. + +Before domain-modeling or architecture work, read `CONTEXT.md` and relevant decisions under `docs/adr/` when they exist. Their absence is not an error. Create them only when the user settles a domain term or a durable architectural decision. + +`CONTEXT.md` is a glossary. Keep implementation details and feature requirements elsewhere. Use its canonical terms in issues, plans, code proposals, and tests. + +ADRs record decisions that are hard to reverse, surprising without context, and based on a real tradeoff. Call out any proposal that contradicts an existing ADR. diff --git a/docs/agents/issue-tracker.md b/docs/agents/issue-tracker.md new file mode 100644 index 0000000..97eda8b --- /dev/null +++ b/docs/agents/issue-tracker.md @@ -0,0 +1,19 @@ +# Issue tracker + +Issues and specs for this repository live in GitHub Issues under `StoneHub/screen-timeout-tile`. Use the authenticated `gh` CLI from this checkout. + +## Operations + +- Create: `gh issue create --repo StoneHub/screen-timeout-tile` +- Read: `gh issue view --repo StoneHub/screen-timeout-tile --comments` +- List: `gh issue list --repo StoneHub/screen-timeout-tile` +- Comment: `gh issue comment --repo StoneHub/screen-timeout-tile` +- Label or close: use `gh issue edit` or `gh issue close` with the same explicit repository. + +## Pull requests + +Pull requests are not a triage request source. Inspect a PR when an issue, commit, or user request points to it. + +## Skill routing + +When a skill says to publish a ticket, create a GitHub issue. When it says to fetch a ticket, read the issue and its comments. Use GitHub sub-issues and native issue dependencies when a multi-ticket plan needs parent or blocking relationships. diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md new file mode 100644 index 0000000..35f8058 --- /dev/null +++ b/docs/agents/triage-labels.md @@ -0,0 +1,11 @@ +# Triage labels + +| Matt role | GitHub label | Meaning | +| --- | --- | --- | +| `needs-triage` | `needs-triage` | Maintainer review is required | +| `needs-info` | `needs-info` | Reporter information is required | +| `ready-for-agent` | `ready-for-agent` | Fully specified for an agent | +| `ready-for-human` | `ready-for-human` | Human action or judgment is required | +| `wontfix` | `wontfix` | The project will not pursue the issue | + +Use the GitHub label in the middle column whenever a skill names a Matt role. From 1c0164dd4893ceebe77a3184d07fe98b5208f287 Mon Sep 17 00:00:00 2001 From: StoneHub Date: Mon, 24 Aug 2026 23:50:42 -0400 Subject: [PATCH 2/4] feat: add guided setup screen Implements Variant B selected from the codex/prototype-settings-screen design branch while preserving the existing setup state and timeout controller interfaces. --- .../MainActivityInstrumentedTest.kt | 6 +- .../screentimeouttile/MainActivity.kt | 110 ++++- .../res/drawable/setup_step_badge_current.xml | 5 + .../res/drawable/setup_step_badge_done.xml | 5 + .../res/drawable/setup_step_badge_pending.xml | 8 + .../res/drawable/setup_timeout_background.xml | 5 + app/src/main/res/layout/activity_main.xml | 389 +++++++++--------- app/src/main/res/values/colors.xml | 10 + app/src/main/res/values/strings.xml | 22 +- app/src/main/res/values/themes.xml | 13 +- 10 files changed, 357 insertions(+), 216 deletions(-) create mode 100644 app/src/main/res/drawable/setup_step_badge_current.xml create mode 100644 app/src/main/res/drawable/setup_step_badge_done.xml create mode 100644 app/src/main/res/drawable/setup_step_badge_pending.xml create mode 100644 app/src/main/res/drawable/setup_timeout_background.xml diff --git a/app/src/androidTest/java/com/stonecode/screentimeouttile/MainActivityInstrumentedTest.kt b/app/src/androidTest/java/com/stonecode/screentimeouttile/MainActivityInstrumentedTest.kt index a5c581a..92bf0ca 100644 --- a/app/src/androidTest/java/com/stonecode/screentimeouttile/MainActivityInstrumentedTest.kt +++ b/app/src/androidTest/java/com/stonecode/screentimeouttile/MainActivityInstrumentedTest.kt @@ -18,7 +18,7 @@ class MainActivityInstrumentedTest { fun shouldUseExpectedPackageName_whenAppContextIsLoaded() { val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.stonecode.screentimeouttile", appContext.packageName) + assertEquals("com.stonecode.screentimeouttile.debug", appContext.packageName) } @Test @@ -28,6 +28,10 @@ class MainActivityInstrumentedTest { onView(withId(R.id.permissionRationale)).check(matches(isDisplayed())) onView(withId(R.id.tileButton)).check(matches(isDisplayed())) onView(withId(R.id.nextAction)).check(matches(isDisplayed())) + onView(withId(R.id.setupProgress)).check(matches(isDisplayed())) + onView(withId(R.id.permissionStepBadge)).check(matches(isDisplayed())) + onView(withId(R.id.tileStepBadge)).check(matches(isDisplayed())) + onView(withId(R.id.useStepBadge)).check(matches(isDisplayed())) } } } diff --git a/app/src/main/java/com/stonecode/screentimeouttile/MainActivity.kt b/app/src/main/java/com/stonecode/screentimeouttile/MainActivity.kt index 0d7caab..6fed5f8 100644 --- a/app/src/main/java/com/stonecode/screentimeouttile/MainActivity.kt +++ b/app/src/main/java/com/stonecode/screentimeouttile/MainActivity.kt @@ -7,25 +7,33 @@ import android.graphics.drawable.Icon import android.os.Build import android.os.Bundle import android.provider.Settings +import android.view.View import android.widget.Button import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.core.net.toUri import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding +import com.google.android.material.progressindicator.LinearProgressIndicator class MainActivity : AppCompatActivity() { private lateinit var controller: ScreenTimeoutController private lateinit var statusView: TextView private lateinit var timeoutView: TextView + private lateinit var permissionRationaleView: TextView private lateinit var nextActionView: TextView private lateinit var actionButton: Button private lateinit var tileButton: Button private lateinit var tileRequestStatusView: TextView + private lateinit var permissionStepBadge: TextView + private lateinit var tileStepBadge: TextView + private lateinit var useStepBadge: TextView + private lateinit var setupProgress: LinearProgressIndicator private val permissionLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() @@ -43,10 +51,15 @@ class MainActivity : AppCompatActivity() { controller = ScreenTimeoutController(this) statusView = findViewById(R.id.permissionStatus) timeoutView = findViewById(R.id.timeoutValue) + permissionRationaleView = findViewById(R.id.permissionRationale) nextActionView = findViewById(R.id.nextAction) actionButton = findViewById(R.id.permissionButton) tileButton = findViewById(R.id.tileButton) tileRequestStatusView = findViewById(R.id.tileRequestStatus) + permissionStepBadge = findViewById(R.id.permissionStepBadge) + tileStepBadge = findViewById(R.id.tileStepBadge) + useStepBadge = findViewById(R.id.useStepBadge) + setupProgress = findViewById(R.id.setupProgress) actionButton.setOnClickListener { openWriteSettings() } tileButton.setOnClickListener { requestTilePlacement() } @@ -54,8 +67,8 @@ class MainActivity : AppCompatActivity() { private fun configureSystemBars() { WindowCompat.getInsetsController(window, window.decorView).apply { - isAppearanceLightStatusBars = false - isAppearanceLightNavigationBars = false + isAppearanceLightStatusBars = true + isAppearanceLightNavigationBars = true } ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.screenContent)) { view, insets -> val safeDrawing = insets.getInsets( @@ -99,24 +112,6 @@ class MainActivity : AppCompatActivity() { canRequestTilePlacement = canRequestTilePlacement(), ) - statusView.text = if (hasPermission) { - getString(R.string.permission_granted_status) - } else { - getString(R.string.permission_needed_status) - } - - actionButton.isEnabled = state.primaryAction == SetupAction.GRANT_PERMISSION - tileButton.isEnabled = state.canRequestTilePlacement - nextActionView.text = when (state.primaryAction) { - SetupAction.GRANT_PERMISSION -> getString(R.string.next_action_grant_permission) - SetupAction.ADD_TILE -> getString(R.string.next_action_add_tile) - SetupAction.MANUAL_ADD_TILE -> getString(R.string.next_action_manual_add_tile) - } - - if (!state.canRequestTilePlacement && hasPermission) { - tileRequestStatusView.text = getString(R.string.tile_request_status_unsupported) - } - timeoutView.text = when (val status = state.timeoutStatus) { is TimeoutStatus.Available -> getString( R.string.current_timeout_value, @@ -124,6 +119,67 @@ class MainActivity : AppCompatActivity() { ) TimeoutStatus.Unavailable -> getString(R.string.current_timeout_unknown) } + + renderSetupState(state) + } + + private fun renderSetupState(state: SetupUiState) { + val needsPermission = state.primaryAction == SetupAction.GRANT_PERMISSION + + if (needsPermission) { + setupProgress.setProgressCompat(PROGRESS_PERMISSION, true) + nextActionView.text = getString(R.string.setup_progress_step_one) + statusView.text = getString(R.string.setup_permission_title) + permissionRationaleView.visibility = View.VISIBLE + timeoutView.visibility = View.GONE + actionButton.visibility = View.VISIBLE + actionButton.isEnabled = true + tileButton.visibility = View.VISIBLE + tileButton.isEnabled = false + tileRequestStatusView.text = getString(R.string.setup_tile_locked_description) + renderBadge(permissionStepBadge, "1", R.drawable.setup_step_badge_current, R.color.white) + renderBadge(tileStepBadge, "2", R.drawable.setup_step_badge_pending, R.color.setup_text_muted) + renderBadge(useStepBadge, "3", R.drawable.setup_step_badge_pending, R.color.setup_text_muted) + return + } + + setupProgress.setProgressCompat(PROGRESS_TILE, true) + nextActionView.text = getString(R.string.setup_progress_step_two) + statusView.text = getString(R.string.setup_permission_complete_title) + permissionRationaleView.visibility = View.GONE + timeoutView.visibility = View.VISIBLE + actionButton.visibility = View.GONE + renderBadge(permissionStepBadge, CHECK_MARK, R.drawable.setup_step_badge_done, R.color.white) + renderBadge(tileStepBadge, "2", R.drawable.setup_step_badge_current, R.color.white) + renderBadge(useStepBadge, "3", R.drawable.setup_step_badge_pending, R.color.setup_text_muted) + + if (state.canRequestTilePlacement) { + tileButton.visibility = View.VISIBLE + tileButton.isEnabled = true + tileRequestStatusView.text = getString(R.string.setup_tile_ready_description) + } else { + tileButton.visibility = View.GONE + tileRequestStatusView.text = getString(R.string.setup_tile_manual_description) + } + } + + private fun renderBadge( + badge: TextView, + label: String, + backgroundResource: Int, + textColorResource: Int, + ) { + badge.text = label + badge.setBackgroundResource(backgroundResource) + badge.setTextColor(ContextCompat.getColor(this, textColorResource)) + } + + private fun renderSetupComplete() { + setupProgress.setProgressCompat(PROGRESS_COMPLETE, true) + nextActionView.text = getString(R.string.setup_progress_complete) + tileButton.isEnabled = false + renderBadge(tileStepBadge, CHECK_MARK, R.drawable.setup_step_badge_done, R.color.white) + renderBadge(useStepBadge, CHECK_MARK, R.drawable.setup_step_badge_done, R.color.white) } private fun canRequestTilePlacement(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU @@ -145,6 +201,9 @@ class MainActivity : AppCompatActivity() { mainExecutor, ) { result -> tileRequestStatusView.text = tileRequestStatusText(result) + if (isTilePlacementComplete(result)) { + renderSetupComplete() + } } }.onFailure { tileRequestStatusView.text = getString(R.string.tile_request_status_error) @@ -167,4 +226,15 @@ class MainActivity : AppCompatActivity() { } else { getString(R.string.tile_request_status_unsupported) } + + private fun isTilePlacementComplete(result: Int): Boolean = + result == StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED || + result == StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED + + companion object { + private const val CHECK_MARK = "✓" + private const val PROGRESS_PERMISSION = 33 + private const val PROGRESS_TILE = 67 + private const val PROGRESS_COMPLETE = 100 + } } diff --git a/app/src/main/res/drawable/setup_step_badge_current.xml b/app/src/main/res/drawable/setup_step_badge_current.xml new file mode 100644 index 0000000..e300722 --- /dev/null +++ b/app/src/main/res/drawable/setup_step_badge_current.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/setup_step_badge_done.xml b/app/src/main/res/drawable/setup_step_badge_done.xml new file mode 100644 index 0000000..4b2714e --- /dev/null +++ b/app/src/main/res/drawable/setup_step_badge_done.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/setup_step_badge_pending.xml b/app/src/main/res/drawable/setup_step_badge_pending.xml new file mode 100644 index 0000000..c0c8eb0 --- /dev/null +++ b/app/src/main/res/drawable/setup_step_badge_pending.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/drawable/setup_timeout_background.xml b/app/src/main/res/drawable/setup_timeout_background.xml new file mode 100644 index 0000000..f659c7d --- /dev/null +++ b/app/src/main/res/drawable/setup_timeout_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 9e90053..6a148bf 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,6 +4,8 @@ android:id="@+id/screenContent" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/setup_background" + android:clipToPadding="false" android:fillViewport="true"> + android:paddingBottom="28dp"> + + + android:layout_marginTop="8dp" + android:fontFamily="sans-serif-medium" + android:text="@string/setup_title" + android:textColor="@color/setup_text_primary" + android:textSize="32sp" /> + + + + + android:gravity="top" + android:orientation="horizontal"> - - - + android:layout_marginStart="12dp" + android:layout_weight="1" + app:cardBackgroundColor="@color/setup_surface" + app:cardCornerRadius="18dp" + app:cardElevation="0dp" + app:strokeColor="@color/setup_outline_current" + app:strokeWidth="1dp"> - + - + - + - + + + + + + android:layout_marginTop="14dp" + android:gravity="top" + android:orientation="horizontal"> - - + android:id="@+id/tileStepBadge" + android:layout_width="34dp" + android:layout_height="34dp" + android:background="@drawable/setup_step_badge_pending" + android:fontFamily="sans-serif-medium" + android:gravity="center" + android:text="2" + android:textColor="@color/setup_text_muted" + android:textSize="15sp" /> - - - + android:layout_marginStart="12dp" + android:layout_weight="1" + app:cardBackgroundColor="@color/setup_surface" + app:cardCornerRadius="18dp" + app:cardElevation="0dp" + app:strokeColor="@color/setup_outline" + app:strokeWidth="1dp"> + android:orientation="vertical" + android:padding="18dp"> + android:fontFamily="sans-serif-medium" + android:text="@string/setup_tile_title" + android:textColor="@color/setup_text_primary" + android:textSize="18sp" /> - - - - - - - - + android:layout_marginTop="8dp" + android:accessibilityLiveRegion="polite" + android:lineSpacingExtra="1dp" + android:text="@string/setup_tile_locked_description" + android:textColor="@color/setup_text_secondary" + android:textSize="14sp" /> - - - + app:icon="@drawable/ic_qs_timeout_long" + app:iconGravity="textStart" + app:iconPadding="8dp" /> - + + - + - + + + + android:orientation="vertical" + android:padding="18dp"> + android:fontFamily="sans-serif-medium" + android:text="@string/setup_use_title" + android:textColor="@color/setup_text_primary" + android:textSize="18sp" /> + android:layout_marginTop="8dp" + android:lineSpacingExtra="1dp" + android:text="@string/instructions_text" + android:textColor="@color/setup_text_secondary" + android:textSize="14sp" /> - + + android:layout_marginTop="22dp" + android:gravity="center" + android:lineSpacingExtra="1dp" + android:text="@string/setup_privacy_note" + android:textColor="@color/setup_text_muted" + android:textSize="12sp" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 279f113..9b27f8c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,6 +3,16 @@ #FF2563EB #FF1D4ED8 #FF14B8A6 + #FFF4F2EC + #FFFFFFFF + #FF172033 + #FF5B6473 + #FF8A919D + #FFE2DED5 + #FFBBD2FF + #FFDCE3EE + #FF16815A + #FFF1F5FA #FF101418 #FF182026 #FFF7FAFC diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3866a1..8dcc4c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,20 @@ Screen Timeout Tile Switch between 30 seconds and 10 minutes from Quick Settings. + Two-minute setup + Get your tile ready + Complete these steps once. After that, every timeout change is one tap away. + Step 1 of 3 + Step 2 of 3 + Setup complete + Allow timeout access + Timeout access allowed + Add the Quick Settings tile + This unlocks after timeout access is allowed. + Android will ask where to place Screen Timeout in Quick Settings. + Open Quick Settings edit mode and drag Screen Timeout into an active slot. + Tap whenever you need it + No account, analytics, internet access, or background service. Screen timeout Grant write settings Timeout unavailable @@ -16,8 +30,8 @@ %1$d minutes timeout Open system settings - Android’s Modify system settings access is required only to read and change the screen timeout. This app does not change any other setting. - Could not open Modify system settings. Open Android Settings, search for “Modify system settings,” and allow Screen Timeout Tile. + Android requires explicit permission to read and change the screen timeout. No other setting is changed. + Could not open Modify system settings. Open Android Settings, search for "Modify system settings," and allow Screen Timeout Tile. Add tile Permission required Ready to toggle screen timeout @@ -33,12 +47,12 @@ Tile placement request failed. Use the manual Quick Settings edit flow. Direct tile placement is available on Android 13 and newer. What the tile means - The tile is never “off.” It always shows one of two screen timeout presets. + The tile is never "off." It always shows one of two screen timeout presets. Hourglass · 10 minutes Highlighted tile. Keeps the screen on longer. Clock with bolt · 30 seconds Dim tile. Returns to the short timeout. Lock · Permission needed Tap the tile, then allow Modify system settings. - After setup, each tap switches between the 30-second and 10-minute timeout. + Each tap switches between 30 seconds and 10 minutes. The tile is never off. diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index d07533f..a3554b7 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,15 +1,16 @@ - @@ -18,6 +19,6 @@ 0dp 0dp false - 10dp + 12dp From 718c00094a0b6dd5aa2ccbd741f677a38443053a Mon Sep 17 00:00:00 2001 From: StoneHub Date: Mon, 24 Aug 2026 23:50:56 -0400 Subject: [PATCH 3/4] docs: reconcile release direction Records the paid Play Store lane, free GitHub distribution, Family Library target, published v1.1.0 proof, and the second signing-key backup gate. --- PROJECT_GOAL.md | 2 +- README.md | 6 ++++-- docs/modernization-summary.md | 12 ++++++++++-- docs/release-signing.md | 14 ++++++++++++-- docs/release-todo.md | 19 +++++++++++++++---- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/PROJECT_GOAL.md b/PROJECT_GOAL.md index 2c2ff0a..d52126e 100644 --- a/PROJECT_GOAL.md +++ b/PROJECT_GOAL.md @@ -91,4 +91,4 @@ implementation sample here. * [ ] Test on API 24-32 devices for manual tile placement. * [ ] Test on API 33+ devices for direct tile placement. * [x] Handle permission flow. -* [ ] Build release APK/AAB. +* [x] Build and verify the release APK/AAB for version 1.1.0. diff --git a/README.md b/README.md index cc03f44..8e9c6e7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ It is built for the common Android workflow where you want the screen to stay aw ## Download -Download the signed APK from the [latest GitHub release](https://github.com/StoneHub/screen-timeout-tile/releases/latest). Android 7.0 or newer is required. +Download the signed APK for free from the [latest GitHub release](https://github.com/StoneHub/screen-timeout-tile/releases/latest). Android 7.0 or newer is required. The free APK will also remain available from the project website when that page is published. + +A paid Google Play listing is planned at a one-time price between $1 and $3, with no ads or subscription. Purchases should be eligible for [Google Play Family Library](https://support.google.com/googleplay/answer/7007852). Play Store publication has not happened yet. Because this app is distributed outside Google Play, Android may ask you to allow installs from the browser or file manager you used to download it. That permission can be turned off again immediately after installation. @@ -132,7 +134,7 @@ app/src/androidTest/java/ Instrumentation smoke tests ## Current Status -Version 1.1.0 is the first signed GitHub release. Play Store publishing remains a separate future distribution path. +Version 1.1.0 is published as the first signed GitHub release. The next distribution lane is a paid Google Play listing, while the signed GitHub and website APK stays free. ## Contributing diff --git a/docs/modernization-summary.md b/docs/modernization-summary.md index 1a8013d..cefe295 100644 --- a/docs/modernization-summary.md +++ b/docs/modernization-summary.md @@ -3,7 +3,7 @@ ## What Changed - Refactored timeout behavior behind `ScreenTimeoutSettingsStore` and pure policy/state models so core behavior is covered by JVM tests. -- Reworked the launcher setup screen into a compact dark UI that fits the tested phone at `font_scale=1.3`. +- Reworked the launcher setup screen into a compact UI that fits the tested phone at `font_scale=1.3`. - Added Android 13+ direct Quick Settings tile placement from the setup screen. - Added custom Quick Settings tile icons for short timeout, long timeout, and missing-permission states. - Added target-SDK-36 edge-to-edge inset handling and accessible live status announcements. @@ -12,7 +12,7 @@ - Replaced placeholder unit/instrumentation tests with focused behavior and launch smoke tests. - Updated README and project goal notes to match the current setup flow. -## Device Verification +## Historical Version 1.0 Device Verification - Device: `SM_F956U1` - ADB serial: `RFCX61FNYPY` @@ -35,3 +35,11 @@ git diff --check - Permission rationale, tile state accessibility, local release-signing plumbing, CI, privacy policy, and wrapper checksum verification were added. - Logged JVM tests, Android-test compilation, release lint, debug assembly, and unsigned release bundling passed. - The exact debug APK was installed and cold-launched on Gina’s `SM-F766U1`; visual QA, permission approval, and tile placement remain blocked by the phone lock/human-consent gate. + +## August 24, 2026 Guided Setup + +- Replaced the combined status panel with the selected three-step guided setup screen. +- Kept the existing `SetupUiState` and `ScreenTimeoutController` interfaces. The change is presentation-only. +- Preserved Android 13+ direct tile placement and Android 7-12 manual tile instructions. +- Fixed the instrumentation package assertion for the isolated `.debug` application ID. +- Verified production Kotlin and Android-test compilation with the documented local Android SDK. diff --git a/docs/release-signing.md b/docs/release-signing.md index 803ea4a..bd1a2b7 100644 --- a/docs/release-signing.md +++ b/docs/release-signing.md @@ -1,6 +1,14 @@ # Release Signing -The GitHub APK signing identity is intentionally stored outside the repository. +Release signing identities are intentionally stored outside the repository. + +## Distribution identities + +- GitHub APK updates use the existing Screen Timeout Tile release keystore documented below. +- Google Play will use [Play App Signing](https://developer.android.com/studio/publish/app-signing) for the store signing identity and a separate upload key for Monroe's submissions. +- The Play upload key has not been created or connected to this project yet. + +Do not treat a signed GitHub APK, a signed Play upload bundle, and a Play-distributed artifact as the same proof gate. ## Local source of truth @@ -28,4 +36,6 @@ Verify the resulting APK with Android SDK `apksigner` before publishing it. ## Recovery requirement -Back up both the `.jks` keystore and its Keychain password in a secure location. GitHub-distributed updates must keep using this signing key. Never commit the keystore, passwords, or `release-signing.properties`. +Before publishing version 1.1.1 or any later GitHub update, verify a second encrypted backup of both the `.jks` keystore and its password in a separate physical location. This is a hard release blocker. GitHub-distributed updates must keep using this signing key. + +Keep the Play upload key and its recovery record under the same two-backup rule once created. Never commit a keystore, password, or `release-signing.properties`. diff --git a/docs/release-todo.md b/docs/release-todo.md index b895232..c857aa3 100644 --- a/docs/release-todo.md +++ b/docs/release-todo.md @@ -1,9 +1,16 @@ # Release TODO +## Product Decisions + +- [x] Keep the signed APK free on GitHub and the planned project website. +- [x] Sell the Google Play app as a one-time purchase between $1 and $3. +- [x] Keep the app free of ads, subscriptions, analytics, accounts, and network access. +- [x] Keep the repository without an open-source license for now. +- [ ] Confirm the published Play listing reports eligibility for Google Play Family Library. + ## Monroe To Do - Pick the public app name: keep `Screen Timeout Tile` or choose a shorter store name. -- Decide whether the Play listing should be free-only, donation-supported, or paid. - Create/confirm the Google Play Console app entry. - Create a simple privacy policy page: - no ads @@ -20,7 +27,11 @@ - Get a screenshot of the low-contrast active Quick Settings tile state if it still happens after the bold icon update. - Write the store short description and full description. - Decide whether to publish under `StoneHub`/`StoneCode` branding. -- Create an upload key and enable Play App Signing. +- Create a separate Play upload key and enable Play App Signing. + +## Release Blockers + +- [ ] Before version 1.1.1 or any later GitHub release, verify a second encrypted backup of the GitHub APK signing keystore and password in a separate physical location. ## Code / Release Engineering To Do @@ -31,9 +42,9 @@ - [x] Bump to `versionCode=2` and `versionName=1.1.0`. - [x] Add release notes for 1.1.0. - [x] Create the permanent GitHub APK signing key and store its password in macOS Keychain. -- [ ] Back up the GitHub APK signing keystore and Keychain password in a second secure location. +- [ ] Record and verify the backup location without writing the secret into this repository. - [x] Build and verify the production-signed APK and AAB for version 1.1.0. -- [ ] Publish GitHub Release `v1.1.0` with the signed APK and checksums. +- [x] Published GitHub Release `v1.1.0` with the signed APK and checksums on August 18, 2026. - Run manual QA on at least: - Samsung Android 16 phone - Android 13+ device for direct tile placement From f907ad852a4f6d8d89830f08613ce7814e702d6c Mon Sep 17 00:00:00 2001 From: StoneHub Date: Mon, 24 Aug 2026 23:53:56 -0400 Subject: [PATCH 4/4] fix: keep light navigation icons compatible --- app/src/main/res/values/themes.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index a3554b7..899ea5b 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -7,7 +7,6 @@ @color/brand_secondary @color/black true - true @color/setup_background @color/setup_background @color/setup_background