From 784f92bd40a1113a4343366eb497a50fd69266af Mon Sep 17 00:00:00 2001 From: Wally Hackenslacker Date: Thu, 30 Apr 2026 12:57:53 -0400 Subject: [PATCH] fix: block board click-through while opening settings --- .../kanbn4droid/app/BoardsFlowTest.kt | 25 +++++++++++++ .../kanbn4droid/app/BoardsActivity.kt | 14 ++++++- .../app/boards/BoardsClickGuards.kt | 8 ++++ .../app/boards/BoardsClickGuardTest.kt | 37 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuards.kt create mode 100644 app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuardTest.kt diff --git a/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt b/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt index a6ed9e2..0ab0eee 100644 --- a/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt +++ b/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt @@ -18,6 +18,7 @@ import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra +import androidx.test.espresso.intent.VerificationModes.times import androidx.test.espresso.matcher.RootMatchers.isDialog import androidx.test.espresso.matcher.ViewMatchers.isEnabled import androidx.test.espresso.matcher.ViewMatchers.isDisplayed @@ -578,6 +579,30 @@ class BoardsFlowTest { onView(withId(R.id.settingsSaveAndCloseButton)).check(matches(isDisplayed())) } + @Test + fun boardTapIsIgnoredWhileSettingsOpenIsPending() { + MainActivity.dependencies.apiClientFactory = { + FakeBoardsApiClient( + boards = mutableListOf(BoardSummary("1", "Alpha")), + templates = emptyList(), + ) + } + + val scenario = ActivityScenario.launch(BoardsActivity::class.java) + scenario.onActivity { activity -> + val field = activity.javaClass.getDeclaredField("pendingOpenSettingsAfterDrawerClose") + field.isAccessible = true + field.setBoolean(activity, true) + } + + onView(withText("Alpha")).perform(click()) + + Intents.intended( + hasComponent(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity::class.java.name), + times(0), + ) + } + private fun openSettingsFromDrawer() { onView(withId(R.id.boardsDrawerLayout)).perform(DrawerActions.open()) onView(withId(R.id.drawerSettingsButton)).perform(click()) diff --git a/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt b/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt index 1ac9a36..2488d4b 100644 --- a/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt +++ b/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt @@ -43,6 +43,7 @@ import space.hackenslacker.kanbn4droid.app.boards.BoardsUiState import space.hackenslacker.kanbn4droid.app.boards.BoardsViewModel import space.hackenslacker.kanbn4droid.app.boards.BoardsDrawerWidthCalculator import space.hackenslacker.kanbn4droid.app.boards.DrawerDataErrorCode +import space.hackenslacker.kanbn4droid.app.boards.shouldIgnoreBoardClick import space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity import space.hackenslacker.kanbn4droid.app.settings.SettingsDialogFragment @@ -140,7 +141,11 @@ class BoardsActivity : AppCompatActivity() { private fun setupRecycler() { boardsAdapter = BoardsAdapter( - onBoardClick = { board -> navigateToBoard(board) }, + onBoardClick = { board -> + if (!shouldIgnoreBoardClick()) { + navigateToBoard(board) + } + }, onBoardLongClick = { board -> showDeleteConfirmation(board) }, ) recyclerView.layoutManager = LinearLayoutManager(this) @@ -458,6 +463,13 @@ class BoardsActivity : AppCompatActivity() { .show(supportFragmentManager, SettingsDialogFragment.TAG) } + private fun shouldIgnoreBoardClick(): Boolean { + return shouldIgnoreBoardClick( + pendingOpenSettingsAfterDrawerClose = pendingOpenSettingsAfterDrawerClose, + isDrawerOpen = drawerLayout.isDrawerOpen(GravityCompat.START), + ) + } + private fun navigateToBoard(board: BoardSummary) { startActivity( Intent(this, BoardDetailActivity::class.java) diff --git a/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuards.kt b/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuards.kt new file mode 100644 index 0000000..895a1fd --- /dev/null +++ b/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuards.kt @@ -0,0 +1,8 @@ +package space.hackenslacker.kanbn4droid.app.boards + +internal fun shouldIgnoreBoardClick( + pendingOpenSettingsAfterDrawerClose: Boolean, + isDrawerOpen: Boolean, +): Boolean { + return pendingOpenSettingsAfterDrawerClose || isDrawerOpen +} diff --git a/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuardTest.kt b/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuardTest.kt new file mode 100644 index 0000000..227d7ac --- /dev/null +++ b/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsClickGuardTest.kt @@ -0,0 +1,37 @@ +package space.hackenslacker.kanbn4droid.app.boards + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class BoardsClickGuardTest { + @Test + fun ignoresBoardClickWhenSettingsOpenIsPending() { + assertTrue( + shouldIgnoreBoardClick( + pendingOpenSettingsAfterDrawerClose = true, + isDrawerOpen = false, + ), + ) + } + + @Test + fun ignoresBoardClickWhenDrawerIsOpen() { + assertTrue( + shouldIgnoreBoardClick( + pendingOpenSettingsAfterDrawerClose = false, + isDrawerOpen = true, + ), + ) + } + + @Test + fun allowsBoardClickWhenNoSettingsOpenIsPendingAndDrawerClosed() { + assertFalse( + shouldIgnoreBoardClick( + pendingOpenSettingsAfterDrawerClose = false, + isDrawerOpen = false, + ), + ) + } +}