How do I detect if software keyboard is visible on Android Device or not?
Asked Answered
C

38

333

Is there a way in Android to detect if the software (a.k.a. "soft") keyboard is visible on screen?

Consolute answered 20/1, 2011 at 10:32 Comment(5)
possible duplicate of Android: how can i tell if the soft keyboard is showing or not?Bienne
what can be a solution to this in certain cases (if a 3rd party keyboard is installed) is to check the global notifications as when a keyboard is open there is a system notification that says "change keyboard" - can be done with a NotificationListenerServiceHydrosphere
almost 8 years and still no solid solution, oh if they introduce one, its going to be for API > 30 anyway so never mind...Object
Possible duplicate of Android: Detect softkeyboard openHammel
If you're doing a UI test with Espresso or Jetpack Compose, you can use a solution with executeShellCommand()to detect if the keyboard is displayed on screen or not: #33971456Gauge
P
79

There is no direct way - see http://groups.google.com/group/android-platform/browse_thread/thread/1728f26f2334c060/5e4910f0d9eb898a where Dianne Hackborn from the Android team has replied. However, you can detect it indirectly by checking if the window size changed in #onMeasure. See How to check visibility of software keyboard in Android?.

Prickett answered 25/5, 2011 at 22:29 Comment(3)
this actually breaks when user do split screen apps.Dortheydorthy
You can write an extension function like this for API 21 and above. fun View.isKeyboardVisible(): Boolean { val insets = ViewCompat.getRootWindowInsets(this) return insets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false }Tarbes
This probably also breaks with floating keyboards.Lombroso
D
328

This works for me. Maybe this is always the best way for all versions.

It would be effective to make a property of keyboard visibility and observe this changes delayed because the onGlobalLayout method calls many times. Also it is good to check the device rotation and windowSoftInputMode is not adjustNothing.

boolean isKeyboardShowing = false;
void onKeyboardVisibilityChanged(boolean opened) {
    print("keyboard " + opened);
}

// ContentView is the root view of the layout of this activity/fragment    
contentView.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {

        Rect r = new Rect();
        contentView.getWindowVisibleDisplayFrame(r);
        int screenHeight = contentView.getRootView().getHeight();

        // r.bottom is the position above soft keypad or device button.
        // if keypad is shown, the r.bottom is smaller than that before.
        int keypadHeight = screenHeight - r.bottom;

        Log.d(TAG, "keypadHeight = " + keypadHeight);

        if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
            // keyboard is opened
            if (!isKeyboardShowing) {
                isKeyboardShowing = true;
                onKeyboardVisibilityChanged(true);
            }
        }
        else {
            // keyboard is closed
            if (isKeyboardShowing) {
                isKeyboardShowing = false;
                onKeyboardVisibilityChanged(false);
            }
        }
    }
});
Diacid answered 17/11, 2014 at 1:6 Comment(14)
Here is a working gist: gist.github.com/faruktoptas/e9778e1f718214938b00c2dcd2bed109Prorogue
Put this in a utils class and pass in the activity - now useful across the whole app.Burkhard
And where is contentView declared?Triplett
@Triplett In the activity/fragment you're looking to respond to soft keyboard changes. ContentView is the root view of the layout of this activity/fragment.Gare
Worked for me on Android 6 and 7.Bournemouth
This logic is only for portrait mode. And onGlobalLayout() this method calls so many times, still looking for better solution.Interest
@ShanXeeshi Good point! It would be effective to make a property of keyboard visibility and observe this changes delayed because the onGlobalLayout method calls many times. Also check the device rotation.Diacid
Working in lower and higher devicesLunchroom
This bad because it always being called, even when I just scroll though items in RecyclerViewKao
@vishalpatel If you use spited screen(fragment) with one activity, try to add listener to root of an activity.Diacid
where would you remove Global layout listener. The number of times a global layout is called is unimagnable.Lamothe
@antroid You need to create a property for checking keyboard visibility because GlobalLayoutListener called many many times. I updated codes.Diacid
the fun onKeyboardVisibilityChanged() is actually can be removed Its better to put the code from there directly to the onGlobalLayoutListener The rest is work for me +1 ThanksNystatin
so far, the only solution that still works in 2022!Evans
P
85

try this:

InputMethodManager imm = (InputMethodManager) getActivity()
            .getSystemService(Context.INPUT_METHOD_SERVICE);

    if (imm.isAcceptingText()) {
        writeToLog("Software Keyboard was shown");
    } else {
        writeToLog("Software Keyboard was not shown");
    }
Paletot answered 23/9, 2011 at 14:10 Comment(11)
This does not work for me. The keyboard shown branch triggers even in instance when the keyboard was never shown or was shown and then closed.Bartram
it always return true.Souter
For me it sometimes returns false - when the keyboard is actually visible.Bayly
It is pathetic that the Android framework is lacking, and worse, inconsistent in this regard. This ought to be super-simple.Romano
not correctly working in some cases! Need something elseOverstride
It is not working for me it returns always true. if the keyboard is not open still return true.Zaneta
It always returns false in my case when I have a keyboard open and I select an item in custom autocompleteviewLewin
No any way to detect is keyboard open or close. It's very embarrassing measure height for detecting keyboard state. I tried to find keyboard state inside of WindowManager because keyboard must attache some window.Savino
This obviously won't work since it's not a listener that listens to the soft keyboard's behavior.Pustulant
@VickyChijwani believe me Android SDK is much better than iOS SDK. It's much easier to develop for Android. To detect internet connection in iOS you have to use such things like github.com/ashleymills/Reachability.swift (in Android you need a couple of lines without any third-party code)Kao
working for me tested in 3 devices. motorola moto g5, micormax, nokia 6.1 plusWaterspout
L
82

I created a simple class that can be used for this: https://github.com/ravindu1024/android-keyboardlistener. Just copy it in to your project and use as follows:

KeyboardUtils.addKeyboardToggleListener(this, new KeyboardUtils.SoftKeyboardToggleListener()
{
    @Override
    public void onToggleSoftKeyboard(boolean isVisible)
    {
        Log.d("keyboard", "keyboard visible: "+isVisible);
    }
});
Longlegged answered 15/7, 2016 at 2:37 Comment(12)
Where in the code exaclty do I have to put this? I put this into an activity, however, it does not detect any keyboard appearance or disappearance.Seismography
Well, you can put it anywhere inside your activity. Just put it in the onCreate() method after the setContentView() call and you should be getting callbacks. Btw, what device are you trying it on?Longlegged
@MaulikDodia I checked and it works fine in fragments. Set it up like this: KeyboardUtils.addKeyboardToggleListener(getActivity(), this); and it should work. What device are you trying it on?Longlegged
I'm trying on Moto-G3 device.@LongleggedThud
Thanks for this snippet, I have one question that is this code required to remove listener?Electrothermal
@PratikButaniAndroidDev There's a static function called KeyboardUtils.removeAllKeyboardToggleListeners. Call that when your Activity is destroyed.Longlegged
Is it required or not?Electrothermal
It isn't required for it to work. However since the keyboard util class is holding a static reference to your activity to provide that callback you should unregister to avoid memory leaks.Longlegged
Works great. Thank you smPostexilian
but this is not working in other third-party appsHint
Works perfectly if you're using findViewById, if using View Binding we got error (act.findViewById<View>(… ViewGroup).getChildAt(0) must not be null any suggestion?Pragmatics
It works properly, TnxXerarch
P
79

There is no direct way - see http://groups.google.com/group/android-platform/browse_thread/thread/1728f26f2334c060/5e4910f0d9eb898a where Dianne Hackborn from the Android team has replied. However, you can detect it indirectly by checking if the window size changed in #onMeasure. See How to check visibility of software keyboard in Android?.

Prickett answered 25/5, 2011 at 22:29 Comment(3)
this actually breaks when user do split screen apps.Dortheydorthy
You can write an extension function like this for API 21 and above. fun View.isKeyboardVisible(): Boolean { val insets = ViewCompat.getRootWindowInsets(this) return insets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false }Tarbes
This probably also breaks with floating keyboards.Lombroso
S
45

With the new feature WindowInsetsCompat in androidx core release 1.5.0-alpha02 you could check the visibility of the soft keyboard easily as below

Quoting from reddit comment

val View.keyboardIsVisible: Boolean
    get() = WindowInsetsCompat
        .toWindowInsetsCompat(rootWindowInsets)
        .isVisible(WindowInsetsCompat.Type.ime())

Some note about backward compatibility, quoting from release notes

New Features

The WindowInsetsCompat APIs have been updated to those in the platform in Android 11. This includes the new ime() inset type, which allows checking the visibility and size of the on-screen keyboard.

Some caveats about the ime() type, it works very reliably on API 23+ when your Activity is using the adjustResize window soft input mode. If you’re instead using the adjustPan mode, it should work reliably back to API 14.

References

Sheffy answered 21/8, 2020 at 6:36 Comment(6)
See this video which also references this question! youtube.com/watch?v=acC7SR1EXsI 😁Bagasse
@TomGilder just go ahead and make an edit to add the video to references.Sheffy
requires api 20 at leastLabyrinth
I had to replace WindowInsetsCompat.toWindowInsetsCompat(rootWindowInsets) with ViewCompat.getRootWindowInsets(this) to get it working. Found it in Android video: youtu.be/acC7SR1EXsI?t=319Jeter
this returns true for Android 23-29 even if keyboard is not opened, it only works ok for Android 30Kao
It's unfortunate that this answer is buried at the bottom. All the older answers use APIs that are either unsupported or flaky. Let's bring this to the top!Ingrid
C
32

Very Easy

1. Put id on your root view

rootView is just a view pointing to my root view in this case a relative layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/addresses_confirm_root_view"
                android:background="@color/WHITE_CLR">

2. Initialize your root view in your Activity:

RelativeLayout rootView = (RelativeLayout) findViewById(R.id.addresses_confirm_root_view);

3. Detect if keyboard is opened or closed by using getViewTreeObserver()

    rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight();
                
                if (heightDiff > 100) { // Value should be less than keyboard's height 
                    Log.e("MyActivity", "keyboard opened");
                } else { 
                    Log.e("MyActivity", "keyboard closed");
                }
            }
        });
Charlottetown answered 11/2, 2016 at 19:11 Comment(8)
hey mate, could you please tell me where this magic 100 comes from? Why not 101 or 99? ThanksUnquote
@Karoly i think this may be and 1. No matter. Only this must be less than the real length of keyboardSieve
@Karoly basically, he's comparing the window size with your activity's root view size. the appearance of the soft keyboard doesn't affect the size of the main window. so you can still lower the value of 100.Futility
The magic number is dependent on your layout of topbar among other things. So it is relative to your app. I used 400 in one of mine.Dahliadahlstrom
remember that onGlobalLayout is called every frame, so make sure you don't do heavy stuff in there.Bussell
this will not be helpful if your layout involves a bottom sheet. Can't distinguish between a keyboard and bottom sheet.Gainor
it returns 190, so it says that keyboard is always opened, this hardcoding is very badKao
It is not 100 , vary depends on resolution of the screen, It worked with 500 for samsung A51Ebenezer
F
14

So after a long time of playing around with AccessibilityServices, window insets, screen height detection, etc, I think I found a way to do this.

Disclaimer: it uses a hidden method in Android, meaning it might not be consistent. However, in my testing, it seems to work.

The method is InputMethodManager#getInputMethodWindowVisibleHeight(), and it's existed since Lollipop (5.0).

Calling that returns the height, in pixels, of the current keyboard. In theory, a keyboard shouldn't be 0 pixels tall, so I did a simple height check (in Kotlin):

val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
if (imm.inputMethodWindowVisibleHeight > 0) {
    //keyboard is shown
else {
    //keyboard is hidden
}

I use Android Hidden API to avoid reflection when I call hidden methods (I do that a lot for the apps I develop, which are mostly hacky/tuner apps), but this should be possible with reflection as well:

val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
val windowHeightMethod = InputMethodManager::class.java.getMethod("getInputMethodWindowVisibleHeight")
val height = windowHeightMethod.invoke(imm) as Int
//use the height val in your logic
Fibrin answered 4/9, 2018 at 18:9 Comment(3)
Amazing use of reflectionsCroquet
They did hide this methodRondeau
Yes this works. Shame that we have to poll this, and some callback doesn't tell us when this changes.Scheming
M
11

You can use WindowInsetsCompat from androidx.core (version 1.5.0-rc01). This code will work from API 21 and above. Kotlin code example:

ViewCompat.setOnApplyWindowInsetsListener(root) { v, insets ->
    val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
    if (isKeyboardVisible) {
    }
}

root is the root view of your Activity.

Update

Today I was looking for how to detect keyboard visibility. At first, this code was not working. So I had to:

  1. Add android:windowSoftInputMode="adjustResize" to my AndroidManifest.xml file:
xml
        <activity android:name="com.soumicslabs.activitykt.StartActivity"
            android:theme="@style/AccountKitTheme.Default"
          android:configChanges="orientation|screenSize"
          android:screenOrientation="portrait"
          android:windowSoftInputMode="adjustResize"
          />
  1. In your activity, set WindowCompat.setDecorFitsSystemWindows(window, false), this tells android that we want to manually handle things/don't want to use system defaults:
        val window = this.window
        WindowCompat.setDecorFitsSystemWindows(window, false)  // <-- this tells android not to use system defaults, so we have to setup quite a lot of behaviors manually
  1. Finally, set you onApplyWindowInsetsListener:
val callBack = OnApplyWindowInsetsListener { view, insets ->
            val imeHeight = insets?.getInsets(WindowInsetsCompat.Type.ime())?.bottom?:0
            Log.e("tag", "onKeyboardOpenOrClose imeHeight = $imeHeight")
// todo: logic
val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
    if (isKeyboardVisible) {
       // do something
    }else{
        // do something else
    }
    insets?: WindowInsetsCompat(null)
  }

ViewCompat.setOnApplyWindowInsetsListener(mainContainer, callBack)

This worked for me.

Mathers answered 25/3, 2021 at 18:14 Comment(1)
this works but makes a mess of all other layouts that depend on setDecorFitsSystemWindowsDownstream
V
10

I used this as a basis: https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android

/**
* To capture the result of IMM hide/show soft keyboard
*/
public class IMMResult extends ResultReceiver {
     public int result = -1;
     public IMMResult() {
         super(null);
}

@Override 
public void onReceiveResult(int r, Bundle data) {
    result = r;
}

// poll result value for up to 500 milliseconds
public int getResult() {
    try {
        int sleep = 0;
        while (result == -1 && sleep < 500) {
            Thread.sleep(100);
            sleep += 100;
        }
    } catch (InterruptedException e) {
        Log.e("IMMResult", e.getMessage());
    }
    return result;
}
}

Then wrote this method:

public boolean isSoftKeyboardShown(InputMethodManager imm, View v) {
    
    IMMResult result = new IMMResult();
    int res;
    
    imm.showSoftInput(v, 0, result);

    // if keyboard doesn't change, handle the keypress
    res = result.getResult();
    if (res == InputMethodManager.RESULT_UNCHANGED_SHOWN ||
            res == InputMethodManager.RESULT_UNCHANGED_HIDDEN) {

        return true;
    }
    else
        return false;

}

You may then use this to test all fields (EditText, AutoCompleteTextView, etc) that may have opened a softkeyboard:

    InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
    if(isSoftKeyboardShown(imm, editText1) | isSoftKeyboardShown(imm, autocompletetextview1))
        //close the softkeyboard
        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

Addmittely not an ideal solution, but it gets the job done.

Vacant answered 27/6, 2015 at 15:38 Comment(3)
This works. If you implement as singelton you can apply to all edittexts on focus change and have one global keyboard listenerEllga
@depperm getActivity() is specific to Fragments, try YourActivityName.this instead. See also: #14480629Vacant
Thead.sleep - what is that? Making Main thread sleep?Kao
R
10

There is finally official support for this now in 2023!

Here is the documentation

To check if the Keyboard is visible, do this:

val insets = ViewCompat.getRootWindowInsets(view) ?: return
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom

To listen to changes in Keyboard visibility, do this:

ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
  val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
  val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
  insets
}

Note: Google recommends that you configure your app to display edge to edge in order for this to work properly. They also say "to achieve the best backward compatibility with this AndroidX implementation, set android:windowSoftInputMode="adjustResize" to the activity in AndroidManifest.xml."

Reliquiae answered 10/3, 2023 at 2:26 Comment(1)
This only works if edge-to-edge is enabled via WindowCompat.setDecorFitsSystemWindows(window, false). However, this makes status- and navigation bar overlap your layout. A quick fix for that is to wrap your layout into a FrameLayout and for the original root view of your layout set android:fitsSystemWindows="true"Bettis
O
7

You can use the callback result of showSoftInput() and hideSoftInput() to check for the status of the keyboard. Full details and example code at

https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android

Optimal answered 20/2, 2012 at 23:45 Comment(1)
Thead.sleep - what is that? Making Main thread sleep?Kao
F
7

There's finally a direct way starting from Android R based on Kotlin now.

 val imeInsets = requireView().rootWindowInsets.isVisible(WindowsInsetsCompat.Type.ime()) 
    if (imeInsets) { 
     //Ime is visible
     //Lets move our view by the height of the IME
     view.translationX = imeInsets.bottom }
Fulmer answered 21/2, 2020 at 4:21 Comment(2)
can't resolve WindowsInsetsCompatGallium
@ShoaibKhalid maybe it requires androidx.core:core-ktx:1.9.0Overreact
N
6

This was much less complicated for the requirements I needed. Hope this might help:

On the MainActivity:

public void dismissKeyboard(){
    InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mSearchBox.getWindowToken(), 0);
    mKeyboardStatus = false;
}

public void showKeyboard(){
    InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
    mKeyboardStatus = true;
}

private boolean isKeyboardActive(){
    return mKeyboardStatus;
}

The default primative boolean value for mKeyboardStatus will be initialized to false.

Then check the value as follows, and perform an action if necessary:

 mSearchBox.requestFocus();
    if(!isKeyboardActive()){
        showKeyboard();
    }else{
        dismissKeyboard();
    }
Nigritude answered 26/8, 2015 at 18:25 Comment(1)
easier solution ever for this situation ;)Climacteric
H
6

This should work if you need to check keyboard status:

fun Activity.isKeyboardOpened(): Boolean {
    val r = Rect()

    val activityRoot = getActivityRoot()
    val visibleThreshold = dip(UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP)

    activityRoot.getWindowVisibleDisplayFrame(r)

    val heightDiff = activityRoot.rootView.height - r.height()

    return heightDiff > visibleThreshold;
}

fun Activity.getActivityRoot(): View {
    return (findViewById<ViewGroup>(android.R.id.content)).getChildAt(0);
}

Where UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP = 100 and dip() is an anko func that convert dpToPx:

fun dip(value: Int): Int {
    return (value * Resources.getSystem().displayMetrics.density).toInt()
}
Hudak answered 23/11, 2018 at 11:19 Comment(3)
Спасибо! Ты спас мое время =)Oden
This workaround will work only when we have the below properties for activity in the Manifest file. android:windowSoftInputMode="adjustResize" or android:windowSoftInputMode="adjustPan" It will not work for below android:windowSoftInputMode="adjustNothing"Polard
UiUtils not foundSubversive
R
5

You can refer to this answer - https://mcmap.net/q/36498/-is-there-a-way-to-tell-if-the-soft-keyboard-is-shown

It worked for me everytime.

adb shell dumpsys window InputMethod | grep "mHasSurface"

It will return true, if software keyboard is visible.

Riotous answered 11/9, 2014 at 7:50 Comment(1)
This is only useful during development - not a solution for use in an app. (Users won't have adb running.)Baron
D
3

I did this by setting a GlobalLayoutListener, as follows:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });
Dorsoventral answered 21/2, 2014 at 21:59 Comment(3)
This will be called VERY oftenDeaton
In what instances will this be different than @BrownsooHan answer? I'm looking for a way that an app that draws over other apps to get out of the way of the keyboard is showing.Tallowy
His answer is fundamentally the same as mine, only I did mine many months before his, and he has more upvotes.Dorsoventral
G
3

Try this code it's really working if KeyboardShown is Shown then this function return true value....

private final String TAG = "TextEditor";
private TextView mTextEditor;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_editor);
    mTextEditor = (TextView) findViewById(R.id.text_editor);
    mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            isKeyboardShown(mTextEditor.getRootView());
        }
    });
}

private boolean isKeyboardShown(View rootView) {
    /* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
    final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;

    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    /* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
    int heightDiff = rootView.getBottom() - r.bottom;
    /* Threshold size: dp to pixels, multiply with display density */
    boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;

    Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
            + "root view height:" + rootView.getHeight() + ", rect:" + r);

    return isKeyboardShown;
}
Gutta answered 11/6, 2015 at 14:25 Comment(1)
The isKeyboardShown keep on calling itself when its not shown.Clifford
K
3

As you might know android Software keyboard will be visible only when there is a possible event of typing. In other words Keyboard get visible only when EditText is focused. that means you can get weather the Keyboard is visible or not by using OnFocusChangeListener.

//Declare this Globally

public boolean isKeyBoardVisible = false;

//In OnCreate *[For Activity]*, OnCreateView *[For Fragment]*

text_send.setOnFocusChangeListener(new View.OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if(hasFocus)
            isKeyBoardVisible = true;
        else
            isKeyBoardVisible = false;
    }
});

Now you can use isKeyBoardVisible variable anywhere in the class to get weather the keyboard is Open or Not. It worked well for me.

Note: This process doesn't work when the Keyboard is opened programmatically using InputMethodManager because that doesn't invoke OnFocusChangeListener.

Kunz answered 10/4, 2019 at 9:44 Comment(1)
not really a hack, didnt work in a nested fragment case. Can't say on activities as i didnt try this on that yet.Lamothe
B
3

Thanks all answers, I figure it out for my own circumstances

/**
 * Add global layout listener to observe system keyboard visibility
 */
private void initObserverForSystemKeyboardVisibility() {
    getRootView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //Add your own code here
            Log.d("TEST_CODE", "isSystemKeyboardVisible:" + isSystemKeyboardVisible())
        }
    });
}


/**
 * Check system keyboard visibility
 * @return true if visible
 */
public boolean isSystemKeyboardVisible() {
    try {
        final InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        final Method windowHeightMethod = InputMethodManager.class.getMethod("getInputMethodWindowVisibleHeight");
        final int height = (int) windowHeightMethod.invoke(manager);
        return height > 0;
    } catch (Exception e) {
        return false;
    }
}
Barbitone answered 17/4, 2021 at 3:14 Comment(2)
your code should have getWindow().getDecorView() /** * Add global layout listener to observe system keyboard visibility */ private void initObserverForSystemKeyboardVisibility() { getWindow().getDecorView().getRootView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //Add your own code here Log.d("TEST_CODE", "isSystemKeyboardVisible:" + isSystemKeyboardVisible()); } }); }Dalrymple
getInputMethodWindowVisibleHeight method is blocked as part of using non-SDK API lists. You cannot use this reflection in production. See more info here: developer.android.com/guide/app-compatibility/…Antepast
H
3
private fun isKeyboardVisible(rootView: View) =
    ViewCompat.getRootWindowInsets(rootView)!!.isVisible(WindowInsetsCompat.Type.ime())
Hagiarchy answered 15/10, 2021 at 17:11 Comment(3)
You need a very good reason to answer an old, abundantly answered question like this. New answers without any explanation how they complement other answers tend to get deleted in the review process.Robey
@GertArnold If this -1 is from you, it would be more professional to at least explain what is wrong with the answer.Hagiarchy
Didn't I do that? For all answers an explanation why it does what it does (and it's the best way) is always welcome, but new answers to old questions should also explain how they improve other answers. You don't want to how many people just drop answers without ever looking at existing answers and, thus, effectively only add noise. Either way, code-only answers are always deemed low quality answers and many of them get deleted by community moderation.Robey
D
2

In my case i had only one EditText to manage in my layout so i came up whit this solution. It works well, basically it is a custom EditText which listens for focus and sends a local broadcast if the focus changes or if the back/done button is pressed. To work you need to place a dummy View in your layout with android:focusable="true" and android:focusableInTouchMode="true" because when you call clearFocus() the focus will be reassigned to the first focusable view. Example of dummy view:

<View
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true"/>

Additional infos

The solution which detects the difference in layout changes doesn't work very well because it strongly depends on screen density, since 100px can be a lot in a certain device and nothing in some others you could get false positives. Also different vendors have different keyboards.

Duumvirate answered 30/9, 2015 at 11:53 Comment(0)
D
2

A little bit more compacted Kotlin version based on the answer of @bohdan-oliynyk

private const val KEYBOARD_VISIBLE_THRESHOLD_DP = 100

fun Activity.isKeyboardOpen(): Boolean {
    fun convertDpToPx(value: Int): Int =
        (value * Resources.getSystem().displayMetrics.density).toInt()

    val rootView = findViewById<View>(android.R.id.content)
    val visibleThreshold = Rect()
    rootView.getWindowVisibleDisplayFrame(visibleThreshold)
    val heightDiff = rootView.height - visibleThreshold.height()

    val accessibleValue = convertDpToPx(KEYBOARD_VISIBLE_THRESHOLD_DP)

    return heightDiff > accessibleValue
}

fun Activity.isKeyboardClosed(): Boolean {
    return isKeyboardOpen().not()
}
Desquamate answered 26/8, 2020 at 12:33 Comment(0)
K
1

In Android you can detect through ADB shell. I wrote and use this method:

{
        JSch jsch = new JSch();
        try {
            Session session = jsch.getSession("<userName>", "<IP>", 22);
            session.setPassword("<Password>");
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.connect();

            ChannelExec channel = (ChannelExec)session.openChannel("exec");
            BufferedReader in = new BufferedReader(new    
            InputStreamReader(channel.getInputStream()));
            channel.setCommand("C:/Android/android-sdk/platform-tools/adb shell dumpsys window 
            InputMethod | findstr \"mHasSurface\"");
            channel.connect();

            String msg = null;
            String msg2 = " mHasSurface=true";

            while ((msg = in.readLine()) != null) {
                Boolean isContain = msg.contains(msg2);
                log.info(isContain);
                if (isContain){
                    log.info("Hiding keyboard...");
                    driver.hideKeyboard();
                }
                else {
                    log.info("No need to hide keyboard.");
                }
            }

            channel.disconnect();
            session.disconnect();

        } catch (JSchException | IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Korfonta answered 3/8, 2015 at 11:44 Comment(1)
Can you improve this answer with a more concrete example, with all imports and a working example?Kalif
T
1
final View activityRootView = findViewById(R.id.rootlayout);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            Rect r = new Rect();
            activityRootView.getWindowVisibleDisplayFrame(r);

            int screenHeight = activityRootView.getRootView().getHeight();
            Log.e("screenHeight", String.valueOf(screenHeight));
            int heightDiff = screenHeight - (r.bottom - r.top);
            Log.e("heightDiff", String.valueOf(heightDiff));
            boolean visible = heightDiff > screenHeight / 3;
            Log.e("visible", String.valueOf(visible));
            if (visible) {
                Toast.makeText(LabRegister.this, "I am here 1", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(LabRegister.this, "I am here 2", Toast.LENGTH_SHORT).show();
            }
        }
});
Taler answered 27/5, 2016 at 10:25 Comment(0)
S
1

Answer of @iWantScala is great but not working for me
rootView.getRootView().getHeight() always has the same value

one way is to define two vars

private int maxRootViewHeight = 0;
private int currentRootViewHeight = 0;

add global listener

rootView.getViewTreeObserver()
    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            currentRootViewHeight = rootView.getHeight();
            if (currentRootViewHeight > maxRootViewHeight) {
                maxRootViewHeight = currentRootViewHeight;
            }
        }
    });

then check

if (currentRootViewHeight >= maxRootViewHeight) {
    // Keyboard is hidden
} else {
    // Keyboard is shown
}

works fine

Sieve answered 19/8, 2017 at 15:54 Comment(0)
B
1

You can get from WindowInsetsCompat which has isVisible function. Like this:

    val isShown = WindowInsetsCompat
        .toWindowInsetsCompat(binding.root.rootWindowInsets)
        .isVisible(WindowInsetsCompat.Type.ime())

    if (isShown) {
        // keyboard is opened here you can make what do you want. 
        // Also this can be inside a global layout listener
    } 
Banlieue answered 17/7, 2023 at 9:52 Comment(0)
B
1

Try this

val View.isKeyboardVisible: Boolean
get() = WindowInsetsCompat
    .toWindowInsetsCompat(rootWindowInsets)
    .isVisible(WindowInsetsCompat.Type.ime())

It is extension function written in Kotlin to detect Keyboard Visibility. Call it whenever you want to know the keyboard visibility state.

Use can access this like that

//View can be TextView or any other View that extends by View Class etc.
view.isKeyboardVisible  
Bath answered 26/10, 2023 at 7:37 Comment(0)
T
0

There is a direct method to find this out. And, it does not require the layout changes.
So it works in immersive fullscreen mode, too.
But, unfortunately, it does not work on all devices. So you have to test it with your device(s).

The trick is that you try to hide or show the soft keyboard and capture the result of that try.
If it works correct then the keyboard is not really shown or hidden. We just ask for the state.

To stay up-to-date, you simply repeat this operation, e.g. every 200 milliseconds, using a Handler.

The implementation below does just a single check.
If you do multiple checks, then you should enable all the (_keyboardVisible) tests.

public interface OnKeyboardShowHide
{
    void    onShowKeyboard( Object param );
    void    onHideKeyboard( Object param );
}

private static Handler      _keyboardHandler    = new Handler();
private boolean             _keyboardVisible    = false;
private OnKeyboardShowHide  _keyboardCallback;
private Object              _keyboardCallbackParam;

public void start( OnKeyboardShowHide callback, Object callbackParam )
{
    _keyboardCallback      = callback;
    _keyboardCallbackParam = callbackParam;
    //
    View view = getCurrentFocus();
    if (view != null)
    {
        InputMethodManager imm = (InputMethodManager) getSystemService( Activity.INPUT_METHOD_SERVICE );
        imm.hideSoftInputFromWindow( view.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY, _keyboardResultReceiver );
        imm.showSoftInput( view, InputMethodManager.SHOW_IMPLICIT, _keyboardResultReceiver );
    }
    else // if (_keyboardVisible)
    {
        _keyboardVisible = false;
        _keyboardCallback.onHideKeyboard( _keyboardCallbackParam );
    }
}

private ResultReceiver      _keyboardResultReceiver = new ResultReceiver( _keyboardHandler )
{
    @Override
    protected void onReceiveResult( int resultCode, Bundle resultData )
    {
        switch (resultCode)
        {
            case InputMethodManager.RESULT_SHOWN :
            case InputMethodManager.RESULT_UNCHANGED_SHOWN :
                // if (!_keyboardVisible)
                {
                    _keyboardVisible = true;
                    _keyboardCallback.onShowKeyboard( _keyboardCallbackParam );
                }
                break;
            case InputMethodManager.RESULT_HIDDEN :
            case InputMethodManager.RESULT_UNCHANGED_HIDDEN :
                // if (_keyboardVisible)
                {
                    _keyboardVisible = false;
                    _keyboardCallback.onHideKeyboard( _keyboardCallbackParam );
                }
                break;
        }
    }
};
Tremulant answered 19/12, 2014 at 13:24 Comment(1)
how to call it and where?Pathless
S
0

Here is a workaround to know if softkeyboard is visible.

  1. Check for running services on the system using ActivityManager.getRunningServices(max_count_of_services);
  2. From the returned ActivityManager.RunningServiceInfo instances, check clientCount value for soft keyboard service.
  3. The aforementioned clientCount will be incremented every time, the soft keyboard is shown. For example, if clientCount was initially 1, it would be 2 when the keyboard is shown.
  4. On keyboard dismissal, clientCount is decremented. In this case, it resets to 1.

Some of the popular keyboards have certain keywords in their classNames:

  1. Google AOSP = IME
  2. Swype = IME
  3. Swiftkey = KeyboardService
  4. Fleksy = keyboard
  5. Adaptxt = IME (KPTAdaptxtIME)
  6. Smart = Keyboard (SmartKeyboard)

From ActivityManager.RunningServiceInfo, check for the above patterns in ClassNames. Also, ActivityManager.RunningServiceInfo's clientPackage=android, indicating that the keyboard is bound to system.

The above mentioned information could be combined for a strict way to find out if soft keyboard is visible.

Syncarpous answered 1/6, 2016 at 12:41 Comment(0)
T
0

I converted the answer to the kotlin, hope this helps for kotlin users.

private fun checkKeyboardVisibility() {
    var isKeyboardShowing = false

    binding.coordinator.viewTreeObserver.addOnGlobalLayoutListener {
        val r = Rect()
        binding.coordinator.getWindowVisibleDisplayFrame(r)
        val screenHeight = binding.coordinator.rootView.height

        // r.bottom is the position above soft keypad or device button.
        // if keypad is shown, the r.bottom is smaller than that before.
        val keypadHeight = screenHeight - r.bottom


        if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
            // keyboard is opened
            if (!isKeyboardShowing) {
                isKeyboardShowing = true

            }
        } else {
            // keyboard is closed
            if (isKeyboardShowing) {
                isKeyboardShowing = false

            }
        }
    }
}
Tropaeolin answered 16/6, 2019 at 6:56 Comment(2)
And what is "binding"Tedric
@Tedric it is root layoutTropaeolin
W
0

If you support apis for AndroidR in your app then you can use the below method.

In kotlin :
    var imeInsets = view.rootWindowInsets.getInsets(Type.ime()) 
    if (imeInsets.isVisible) { 
        view.translationX = imeInsets.bottom 
    }

Note: This is only available for the AndroidR and below android version needs to follow some of other answer or i will update it for that.

Wolford answered 25/2, 2020 at 6:1 Comment(0)
I
0

It works with adjustNothing flag of activity and lifecycle events are used. Also with Kotlin:

/**
 * This class uses a PopupWindow to calculate the window height when the floating keyboard is opened and closed
 *
 * @param activity The parent activity
 *  The root activity that uses this KeyboardManager
 */
class KeyboardManager(private val activity: AppCompatActivity) : PopupWindow(activity), LifecycleObserver {

    private var observerList = mutableListOf<((keyboardTop: Int) -> Unit)>()

    /** The last value of keyboardTop */
    private var keyboardTop: Int = 0

    /** The view that is used to calculate the keyboard top  */
    private val popupView: View?

    /** The parent view  */
    private var parentView: View

    var isKeyboardShown = false
        private set

    /**
     * Create transparent view which will be stretched over to the full screen
     */
    private fun createFullScreenView(): View {
        val view = LinearLayout(activity)
        view.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT)
        view.background = ColorDrawable(Color.TRANSPARENT)
        return view
    }

    init {
        this.popupView = createFullScreenView()
        contentView = popupView

        softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE or LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
        inputMethodMode = INPUT_METHOD_NEEDED

        parentView = activity.findViewById(android.R.id.content)

        width = 0
        height = LayoutParams.MATCH_PARENT

        popupView.viewTreeObserver.addOnGlobalLayoutListener {
            val rect = Rect()
            popupView.getWindowVisibleDisplayFrame(rect)

            val keyboardTop = rect.bottom
            if (this.keyboardTop != keyboardTop) {
                isKeyboardShown = keyboardTop < this.keyboardTop
                this.keyboardTop = keyboardTop
                observerList.forEach { it(keyboardTop) }
            }
        }
        activity.lifecycle.addObserver(this)
    }

    /**
     * This must be called after the onResume of the Activity or inside view.post { } .
     * PopupWindows are not allowed to be registered before the onResume has finished
     * of the Activity
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun start() {
        parentView.post {
            if (!isShowing && parentView.windowToken != null) {
                setBackgroundDrawable(ColorDrawable(0))
                showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0)
            }
        }
    }

    /**
     * This manager will not be used anymore
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun close() {
        activity.lifecycle.removeObserver(this)
        observerList.clear()
        dismiss()
    }

    /**
     * Set the keyboard top observer. The observer will be notified when the keyboard top has changed.
     * For example when the keyboard is opened or closed
     *
     * @param observer The observer to be added to this provider
     */
    fun registerKeyboardTopObserver(observer: (keyboardTop: Int) -> Unit) {
        observerList.add(observer)
    }
}

Useful method to keep view always above the keyboard

fun KeyboardManager.updateBottomMarginIfKeyboardShown(
        view: View,
        activity: AppCompatActivity,
        // marginBottom of view when keyboard is hide
        marginBottomHideKeyboard: Int,
        // marginBottom of view when keybouard is shown
        marginBottomShowKeyboard: Int
) {
    registerKeyboardTopObserver { bottomKeyboard ->
        val bottomView = ViewUtils.getFullViewBounds(view).bottom
        val maxHeight = ScreenUtils.getFullScreenSize(activity.windowManager).y
        // Check that view is within the window size
        if (bottomView < maxHeight) {
            if (bottomKeyboard < bottomView) {
                ViewUtils.updateMargin(view, bottomMargin = bottomView - bottomKeyboard +
                        view.marginBottom + marginBottomShowKeyboard)
            } else ViewUtils.updateMargin(view, bottomMargin = marginBottomHideKeyboard)
        }
    }
}

Where getFullViewBounds

fun getLocationOnScreen(view: View): Point {
    val location = IntArray(2)
    view.getLocationOnScreen(location)
    return Point(location[0], location[1])
}

fun getFullViewBounds(view: View): Rect {
     val location = getLocationOnScreen(view)
     return Rect(location.x, location.y, location.x + view.width,
            location.y + view.height)
 }

Where getFullScreenSize

fun getFullScreenSize(wm: WindowManager? = null) =
            getScreenSize(wm) { getRealSize(it) }

private fun getScreenSize(wm: WindowManager? = null, block: Display.(Point) -> Unit): Point {
    val windowManager = wm ?: App.INSTANCE.getSystemService(Context.WINDOW_SERVICE)
            as WindowManager
    val point = Point()
    windowManager.defaultDisplay.block(point)
    return point
}

Where updateMargin

fun updateMargin(
        view: View,
        leftMargin: Int? = null,
        topMargin: Int? = null,
        rightMargin: Int? = null,
        bottomMargin: Int? = null
) {
    val layoutParams = view.layoutParams as ViewGroup.MarginLayoutParams
    if (leftMargin != null) layoutParams.leftMargin = leftMargin
    if (topMargin != null) layoutParams.topMargin = topMargin
    if (rightMargin != null) layoutParams.rightMargin = rightMargin
    if (bottomMargin != null) layoutParams.bottomMargin = bottomMargin
    view.layoutParams = layoutParams
}
Indigene answered 20/3, 2020 at 7:35 Comment(0)
D
0

This is how a soft keyboard state listener can be used in a fragment (works for API 21 and above)

    private var currentWindowInsets: WindowInsetsCompat = WindowInsetsCompat.Builder().build()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setWindowInsetsAnimationCallback(requireView().rootView, object : Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
            override fun onProgress(insets: WindowInsetsCompat, runningAnimations: MutableList<WindowInsetsAnimationCompat>): WindowInsetsCompat {
                currentWindowInsets = insets
                return insets
            }
    
            override fun onEnd(animation: WindowInsetsAnimationCompat) {
                super.onEnd(animation)
                val keyboardIsVisible = currentWindowInsets.isVisible(WindowInsetsCompat.Type.ime())
                if (keyboardIsVisible) { //do your stuff }
            }
        })
    }
Dnepropetrovsk answered 17/1, 2023 at 14:14 Comment(0)
D
0

There is my Kotlin way to implement it with binding:

In the Activity's onCreate method should add GlobalLayoutListener like this:

binding.root.apply {
            viewTreeObserver.addOnGlobalLayoutListener {
                Rect().let {
                    getWindowVisibleDisplayFrame(it)
                    val heightDiff = rootView.height - it.height()
                    isKeyboardVisible = heightDiff >= getKeyboardMarginOfError()                
                }
            }
        }

In the code above getKeyboardMarginOfError is an extension function and it looks like this:

fun View.getKeyboardMarginOfError(): Int = convertDpToPx(50F).roundToInt()

fun View.convertDpToPx(dp: Float): Float {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        dp,
        this.resources.displayMetrics
    )
}

The value 50F is the margins that verified the keyboard is shown.

Downstream answered 1/3, 2023 at 19:16 Comment(0)
H
-1

I had a similar problem. I needed to react to the Enter button on screen (which hid the keyboard). In this case you can subscribe to the OnEditorAction of the text view the keyboard was opend with - if you have multiple editable boxes, then subscribe to all of them.

In your Activity you have full control of the keyboard, so at no point will you face the problem whether the keyboard is opened or not, if you listen to all opening and closing events.

Hierophant answered 25/1, 2011 at 16:39 Comment(1)
Doesn't work for me. I only receive the Enter key in OnEditorAction.Nobukonoby
T
-1

I did this as follows, but its relevet only if your goal is to close / open the keyboad.

close example: (checking if keyboard already closed, if not - closing)

imm.showSoftInput(etSearch, InputMethodManager.HIDE_IMPLICIT_ONLY, new ResultReceiver(null) {
                    @Override
                    protected void onReceiveResult(int resultCode, Bundle resultData) {
                        super.onReceiveResult(resultCode, resultData);
                        if (resultCode != InputMethodManager.RESULT_UNCHANGED_HIDDEN)
                            imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
                    }
                });
Towrope answered 26/6, 2015 at 11:45 Comment(1)
The question was related to find out either keyboard is showing or notErethism
H
-1

a may be using :

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    Log.d(
    getClass().getSimpleName(), 
    String.format("conf: %s", newConfig));

    if (newConfig.hardKeyboardHidden != hardKeyboardHidden) {
        onHardwareKeyboardChange(newConfig.hardKeyboardHidden);

        hardKeyboardHidden = newConfig.hardKeyboardHidden;
    }

    if (newConfig.keyboardHidden != keyboardHidden) {
        onKeyboardChange(newConfig.keyboardHidden);

        keyboardHidden = newConfig.hardKeyboardHidden;
    }

}

public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
public static final int KEYBOARDHIDDEN_NO = 1;
public static final int KEYBOARDHIDDEN_YES = 2;
public static final int KEYBOARDHIDDEN_SOFT = 3;

//todo
private void onKeyboardChange(int keyboardHidden) {

}

//todo
private void onHardwareKeyboardChange(int hardKeyboardHidden) {

}
Hypnogenesis answered 3/6, 2016 at 15:38 Comment(1)
This will only work for hardware keyboard, not he software oneKp
H
-1

I wrote sample.

This repository can help to detect keyboard status without assumption that "keyboard should be more than X part of screen"

Holdover answered 14/9, 2019 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.