Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Added robust keyboard visibility detection using WindowInsetsCompat. #17917

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import android.view.SubMenu
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.BaseAdapter
import android.widget.Spinner
import android.widget.TextView
Expand All @@ -42,6 +43,8 @@ import androidx.annotation.VisibleForTesting
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.ThemeUtils
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
Expand Down Expand Up @@ -174,7 +177,6 @@ open class CardBrowser :
private var mySearchesItem: MenuItem? = null
private var previewItem: MenuItem? = null
private var undoSnackbar: Snackbar? = null

private lateinit var exportingDelegate: ActivityExportingDelegate

// card that was clicked (not marked)
Expand Down Expand Up @@ -575,6 +577,19 @@ open class CardBrowser :
viewModel.flowOfColumnHeadings.launchCollectionInLifecycleScope(::onColumnNamesChanged)
}

fun isKeyboardVisible(view: View?): Boolean =
view?.let {
ViewCompat.getRootWindowInsets(it)?.isVisible(WindowInsetsCompat.Type.ime())
} ?: false

private fun hideKeyboard() {
Timber.d("hideKeyboard()")
searchView?.let { view ->
val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.hideSoftInputFromWindow(view.windowToken, 0)
Comment on lines +588 to +589
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why not use this since we are already using the WindowInsetsCompat: https://developer.android.com/reference/kotlin/androidx/core/view/SoftwareKeyboardControllerCompat#hide()

It may be that using the InputMethodManager directly is the best way, but I'm curious if that was a considered decision or not, and if so, why using InputMethodManager vs the Compat is best

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mikehardy,

hide() is essentially an abstraction over the InputMethodManager approach. Here's the snippet that shows what it does under the hood:

@Override
void hide() {
    if (mView != null) {
        ((InputMethodManager) mView.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE))
                .hideSoftInputFromWindow(mView.getWindowToken(), 0);
    }
}

I opted for the InputMethodManager method because when I searched for ways to hide the keyboard, this was the most popular solution. I tested it, and it reliably works across various Android versions.

Also, note that the alternative approach using

view.windowInsetsController?.hide(WindowInsets.Type.ime())

is deprecated.

}
}

// Finish initializing the activity after the collection has been correctly loaded
override fun onCollectionLoaded(col: Collection) {
super.onCollectionLoaded(col)
Expand Down Expand Up @@ -892,6 +907,13 @@ open class CardBrowser :
}

override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
if (item.actionView == searchView) {
if (isKeyboardVisible(searchView)) {
Timber.d("keyboard is visible, hiding it")
hideKeyboard()
return false
}
}
viewModel.setSearchQueryExpanded(false)
// SearchView doesn't support empty queries so we always reset the search when collapsing
searchView!!.setQuery("", false)
Expand Down