Skip to content

Commit 814e281

Browse files
david-allisonmikehardy
authored andcommitted
fix(set-due-date): show snackbar after IME hidden
Otherwise it may appears in the middle of the screen Some of Issue 19915: snackbar obscured answer button
1 parent 37e5fa9 commit 814e281

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateDialog.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import com.ichi2.anki.servicelayer.getFSRSStatus
5757
import com.ichi2.anki.showThemedToast
5858
import com.ichi2.anki.snackbar.showSnackbar
5959
import com.ichi2.anki.ui.internationalization.toSentenceCase
60+
import com.ichi2.anki.utils.doOnImeHidden
6061
import com.ichi2.anki.utils.openUrl
6162
import com.ichi2.anki.withProgress
6263
import com.ichi2.utils.AndroidUiUtils
@@ -430,7 +431,10 @@ private fun AnkiActivity.updateDueDate(
430431
return@asyncCatching null
431432
}
432433
Timber.d("updated %d cards", cardsUpdated)
433-
showSnackbar(TR.schedulingSetDueDateDone(cardsUpdated), Snackbar.LENGTH_SHORT)
434+
// Ensure the snackbar doesn't appear in the middle of the screen
435+
doOnImeHidden {
436+
showSnackbar(TR.schedulingSetDueDateDone(cardsUpdated), Snackbar.LENGTH_SHORT)
437+
}
434438
return@asyncCatching cardsUpdated
435439
}
436440

AnkiDroid/src/main/java/com/ichi2/anki/utils/AndroidUtils.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import android.view.inputmethod.InputMethodManager
2525
import androidx.annotation.StringRes
2626
import androidx.core.content.ContextCompat
2727
import androidx.core.net.toUri
28+
import androidx.core.view.ViewCompat
29+
import androidx.core.view.WindowInsetsCompat
30+
import androidx.core.view.WindowInsetsControllerCompat
2831
import androidx.fragment.app.Fragment
2932
import androidx.fragment.app.FragmentActivity
3033
import com.ichi2.anki.AnkiDroidApp
@@ -136,3 +139,38 @@ fun View.hideKeyboard() {
136139
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
137140
imm.hideSoftInputFromWindow(windowToken, 0)
138141
}
142+
143+
/**
144+
* Hides the IME, and runs [block] after the operation completes
145+
*
146+
* [block] may be executed immediately if the IME is already hidden
147+
*
148+
* @param block the code to run after the IME is hidden. Run at most once
149+
*/
150+
fun FragmentActivity.doOnImeHidden(block: () -> Unit) {
151+
val view = window.decorView
152+
WindowInsetsControllerCompat(window, view).hide(WindowInsetsCompat.Type.ime())
153+
154+
var blockHasRun = false
155+
156+
fun runBlockOnce() {
157+
if (blockHasRun) return
158+
blockHasRun = true
159+
Timber.v("IME hidden. executing...")
160+
block()
161+
}
162+
163+
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
164+
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
165+
166+
if (!imeVisible) {
167+
ViewCompat.setOnApplyWindowInsetsListener(view, null)
168+
runBlockOnce()
169+
}
170+
171+
insets
172+
}
173+
174+
// ensure the block is run immediately if the IME is already hidden
175+
ViewCompat.requestApplyInsets(view)
176+
}

0 commit comments

Comments
 (0)