Skip to content

Commit 21d6413

Browse files
committed
fix(ui): keep a hint asked for as the last one closes
When a permission hint's snackbar finished in the same frame a second hint was asked for, the first one's effect resumed after the second was set and cleared it, so the second hint never showed. The effect now clears the pending hint only when it's still its own. Found by the batch verification pass on 671824c. Tests: one new PermissionHintTest case closes the first snackbar and asks for the Default apps hint in one main-thread step, then waits for it. No existing test changed. Without the identity check it fails with a ComposeTimeoutException after 10 s.
1 parent 671824c commit 21d6413

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<p align="center">
1313
<a href="https://github.com/SysAdminDoc/CallShield/releases/latest"><img src="https://img.shields.io/github/v/release/SysAdminDoc/CallShield?style=flat-square&color=a6e3a1" alt="Release"></a>
1414
<img src="https://img.shields.io/badge/Spam%20Numbers-51%2C403-f38ba8?style=flat-square" alt="51,403 Numbers">
15-
<img src="https://img.shields.io/badge/JVM%20unit%20tests-2070-94e2d5?style=flat-square" alt="2070 JVM unit tests">
15+
<img src="https://img.shields.io/badge/JVM%20unit%20tests-2071-94e2d5?style=flat-square" alt="2071 JVM unit tests">
1616
<img src="https://img.shields.io/badge/Android-10%2B-89b4fa?style=flat-square" alt="Android 10+">
1717
<img src="https://img.shields.io/badge/License-MIT-cba6f7?style=flat-square" alt="MIT License">
1818
<img src="https://img.shields.io/badge/API%20Keys-None-fab387?style=flat-square" alt="No required API keys">
@@ -592,12 +592,12 @@ RELEASE_KEY_PASSWORD=...
592592
## Testing
593593

594594
```bash
595-
./gradlew testDebugUnitTest # 2070 tests
595+
./gradlew testDebugUnitTest # 2071 tests
596596
./gradlew connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.sysadmindoc.callshield.platform.TargetSdkBehaviorSmokeTest
597597
./gradlew verifyPipelineTests # Cloudflare Worker (node) + data-pipeline and translation checks (python)
598598
```
599599

600-
The suite is **2070 total JVM unit tests**, run with Robolectric wherever a screen, a service or the database is involved.
600+
The suite is **2071 total JVM unit tests**, run with Robolectric wherever a screen, a service or the database is involved.
601601

602602
Two GitHub workflows run without building the app. **Validation** runs the Worker and
603603
pipeline suites on every push except report-only ones (`run-pipeline-tests.ps1 -CorrectnessOnly`),
@@ -634,7 +634,7 @@ language in [issue #7](https://github.com/SysAdminDoc/CallShield/issues/7).
634634
| Community API | Cloudflare Workers |
635635
| URL Safety | Local spam-domain data, with optional PhishTank and OpenPhish |
636636
| Verification | Local Gradle, lint, and release-artifact checks |
637-
| Tests | 2070 JVM unit tests (JUnit) |
637+
| Tests | 2071 JVM unit tests (JUnit) |
638638
| Strings | 1796 string resources and 49 plural groups (translation-ready) |
639639
| Accessibility | 100+ content descriptions, 48dp touch targets |
640640
| Min SDK | 29 (Android 10) |

‎app/src/main/java/com/sysadmindoc/callshield/ui/PermissionRecovery.kt‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ fun PermissionHintsHost(
116116
hints.snackbars.currentSnackbarData?.dismiss()
117117
val result = hints.snackbars.showSnackbar(SettingsHintVisuals(message, actionLabel))
118118
if (result == SnackbarResult.ActionPerformed) context.startActivitySafely(hint.page.intent(context))
119-
hints.pending = null
119+
// A newer hint asked for as this one closed is still waiting to show.
120+
if (hints.pending === hint) hints.pending = null
120121
}
121122
}
122123
SnackbarHost(hints.snackbars, modifier) { Snackbar(it, actionOnNewLine = it.visuals is SettingsHintVisuals) }

‎app/src/test/java/com/sysadmindoc/callshield/ui/PermissionHintTest.kt‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,30 @@ class PermissionHintTest {
235235
assertEquals(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS, awaitStartedActivity().action)
236236
}
237237

238+
@Test
239+
fun `a hint asked for just as the last one closes still shows`() {
240+
var hints: PermissionHints? = null
241+
var askForRole: () -> Unit = {}
242+
show {
243+
hints = LocalPermissionHints.current
244+
val appInfo = rememberPermissionRequest(R.string.permission_core_in_app_info)
245+
val role = rememberRoleRequest(R.string.role_call_screening_in_default_apps, roleHeld = { false })
246+
askForRole = { role(Intent("test.REQUEST_ROLE")) }
247+
Button(onClick = { appInfo(listOf(Manifest.permission.READ_CALL_LOG)) }) { Text("Ask") }
248+
}
249+
composeRule.onNodeWithText("Ask").performClick()
250+
awaitText(composeRule.activity.getString(R.string.permission_core_in_app_info))
251+
252+
// The first snackbar closes, and the second hint is asked for before
253+
// the first one's effect gets to run again.
254+
composeRule.runOnUiThread {
255+
hints!!.snackbars.currentSnackbarData!!.dismiss()
256+
askForRole()
257+
}
258+
259+
awaitText(composeRule.activity.getString(R.string.role_call_screening_in_default_apps))
260+
}
261+
238262
@Test
239263
fun `with no hint host App info opens straight away`() {
240264
show(withHost = false) {

0 commit comments

Comments
 (0)