Skip to content

Commit e667838

Browse files
committed
fix: back press trigger the discard dialog
1 parent ebcf8e0 commit e667838

2 files changed

Lines changed: 101 additions & 11 deletions

File tree

AnkiDroid/src/main/java/com/ichi2/anki/DrawingFragment.kt

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import android.graphics.Color
2424
import android.net.Uri
2525
import android.os.Bundle
2626
import android.view.View
27+
import androidx.activity.OnBackPressedCallback
2728
import androidx.core.graphics.createBitmap
2829
import androidx.fragment.app.Fragment
2930
import com.ichi2.anki.common.time.TimeManager
@@ -47,17 +48,7 @@ class DrawingFragment : Fragment(R.layout.fragment_drawing) {
4748
) {
4849
super.onViewCreated(view, savedInstanceState)
4950
binding.toolbar.apply {
50-
setNavigationOnClickListener {
51-
// avoid showing the discard changes dialog only if the user hasn't drawn anything,
52-
// even if is is erased or undone, since they may want to undo/redo something.
53-
if (whiteboardFragment?.isEmpty() == true) {
54-
requireActivity().onBackPressedDispatcher.onBackPressed()
55-
} else {
56-
DiscardChangesDialog.showDialog(requireContext()) {
57-
requireActivity().onBackPressedDispatcher.onBackPressed()
58-
}
59-
}
60-
}
51+
setNavigationOnClickListener { handleBackNavigation() }
6152
setOnMenuItemClickListener { item ->
6253
when (item.itemId) {
6354
R.id.action_save -> onSaveDrawing()
@@ -66,6 +57,25 @@ class DrawingFragment : Fragment(R.layout.fragment_drawing) {
6657
true
6758
}
6859
}
60+
61+
requireActivity().onBackPressedDispatcher.addCallback(
62+
viewLifecycleOwner,
63+
object : OnBackPressedCallback(true) {
64+
override fun handleOnBackPressed() {
65+
handleBackNavigation()
66+
}
67+
},
68+
)
69+
}
70+
71+
private fun handleBackNavigation() {
72+
if (whiteboardFragment?.isEmpty() == true) {
73+
requireActivity().finish()
74+
} else {
75+
DiscardChangesDialog.showDialog(requireContext()) {
76+
requireActivity().finish()
77+
}
78+
}
6979
}
7080

7181
private fun onSaveDrawing() {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2026 Ashish Yadav <mailtoashish693@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11+
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with
14+
* this program. If not, see <http://www.gnu.org/licenses/>.
15+
*/
16+
package com.ichi2.anki
17+
18+
import android.graphics.Path
19+
import androidx.appcompat.app.AlertDialog
20+
import androidx.test.ext.junit.runners.AndroidJUnit4
21+
import com.ichi2.anki.ui.windows.reviewer.whiteboard.WhiteboardFragment
22+
import org.hamcrest.MatcherAssert.assertThat
23+
import org.hamcrest.Matchers.equalTo
24+
import org.junit.Test
25+
import org.junit.runner.RunWith
26+
import org.robolectric.shadows.ShadowDialog
27+
import kotlin.test.assertNotNull
28+
import kotlin.test.assertNull
29+
30+
/** Tests for [DrawingFragment] */
31+
@RunWith(AndroidJUnit4::class)
32+
class DrawingFragmentTest : RobolectricTest() {
33+
@Test
34+
fun `back press finishes the activity when the whiteboard is empty`() {
35+
val (activity, _) = launchDrawingActivity()
36+
37+
activity.onBackPressedDispatcher.onBackPressed()
38+
advanceRobolectricLooper()
39+
40+
assertNull(ShadowDialog.getLatestDialog(), "no discard dialog should be shown for an empty whiteboard")
41+
assertThat("activity finishes immediately", activity.isFinishing, equalTo(true))
42+
}
43+
44+
@Test
45+
fun `back press shows the discard dialog when the whiteboard has content`() {
46+
val (activity, whiteboard) = launchDrawingActivity()
47+
whiteboard.binding.whiteboardView.onNewPath
48+
?.invoke(samplePath())
49+
advanceRobolectricLooper()
50+
51+
activity.onBackPressedDispatcher.onBackPressed()
52+
advanceRobolectricLooper()
53+
54+
val dialog =
55+
assertNotNull(
56+
ShadowDialog.getLatestDialog() as? AlertDialog,
57+
"discard dialog should be shown",
58+
)
59+
assertThat("activity is not finishing while dialog is open", activity.isFinishing, equalTo(false))
60+
61+
dialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick()
62+
advanceRobolectricLooper()
63+
assertThat("activity finishes after discard is confirmed", activity.isFinishing, equalTo(true))
64+
}
65+
66+
private fun launchDrawingActivity(): Pair<SingleFragmentActivity, WhiteboardFragment> {
67+
val activity = startRegularActivity<SingleFragmentActivity>(DrawingFragment.getIntent(targetContext))
68+
val drawingFragment =
69+
activity.supportFragmentManager.findFragmentByTag(SingleFragmentActivity.FRAGMENT_TAG) as DrawingFragment
70+
val whiteboardFragment =
71+
drawingFragment.childFragmentManager.findFragmentById(R.id.fragment_container) as WhiteboardFragment
72+
return activity to whiteboardFragment
73+
}
74+
75+
private fun samplePath(): Path =
76+
Path().apply {
77+
moveTo(0f, 0f)
78+
lineTo(10f, 10f)
79+
}
80+
}

0 commit comments

Comments
 (0)