1

There are plenty of answers explaining how to close Android's soft keyboard (such as this one), but they all assume that the keyboard was opened from within your own app, or that you already have views which can obtain focus, thereby trumping the needs of the previous app. Within Android's InputMethodManager class, requests to close a keyboard opened by another app are rejected, and the call to hideSoftInputFromWindow() returns false.

A developer on my team created an extension method on Activity that calls a block of code whenever keyboard state changes. Because Android doesn't fire keyboard show/hide events on its own (which is ridiculous), it's common practice to detect a reduction in the height of the Activity root layout, and assume this means the keyboard was opened. (Note: this only works with windowSoftInputMode="adjustResize".) This extension method, which is meant to be called in onCreate(), notes the height of the root layout at the time it's called, and watches for subsequent changes in height. If the height of the layout has become smaller, it's assumed that the keyboard has been shown. If it then gets bigger, it's assumed the keyboard was hidden. While this sort of non-deterministic hack is unfortunate, it can be useful, as long as you aren't depending on 100% accuracy.

Unfortunately, it doesn't work in this scenario:

  1. User start in another app, with the keyboard open
  2. User taps a notification from my app
  3. My app opens (cold start), with the keyboard still open from the other app
  4. When the extension method is called from onCreate(), the height of my Activity's root layout already reflects the fact that the keyboard is open, which breaks the concept of watching for it to get shorter when the keyboard is open

While there are potentially some ways to refine the logic to make it more robust, what I really want to do is reliably close the "foreign" keyboard that's throwing off my baseline measurement of the app's height. But I've tried setting windowSoftInputMode="stateHidden" in my AndroidManifest.xml file for the Activity in question, and I've tried calling InputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0). Neither approach works; if the keyboard was opened by another app, and I don't yet have a view that has focus, InputMethodManager rejects my attempts to close the keyboard, presumably as a way to prevent my gaining control over the visibility of a keyboard owned by another app.

Is there a way to immediately close a lingering keyboard from another app when starting your own Activity, such that the keyboard is gone by the time your first layout occurs? In my opinion, the fact that stateHidden doesn't do this for you is a bug.

Mark McClelland
  • 4,056
  • 3
  • 25
  • 46

1 Answers1

2

The fact that the keyboard is still up is a bug. But there's no way to fix this programatically that I know of. The problem is that the keyboard works on a connection basis, and the connection is with the previous app. So there's no way to talk to it until you have a connection.

The size hack you mentioned is really something that should be avoided because its nowhere near 100%. There's other things that break it such as split screen, picture in picture modes, and most likely the new foldable phones. Also connecting it to any monitor. It's "common" practice only by people who don't know better and don't test their code. Basically one of those broken pieces of code that floats around because it kind of works and people don't look deeper.

You can forcably set focus to a view in your window that accepts text input. That would force a connection. But then in closing it you'd have jank, and it would occur in cases where this isn't a problem.

In the end, the solution is to write your app such that it doesn't care about keyboard state and not attempt to change behavior or look of your app when the keyboard opens/closes, the OS isn't meant for it.

Totally agree with you that this should be supported by the OS. But I've been saying that since 2010 when I wrote keyboards. It hasn't changed yet, I wouldn't expect it... really ever. Google has never shown much interest in improving the keyboard API, even when I worked at the biggest 3rd party keyboard app in the world they didn't want feedback or put resources into it.

Gabe Sechan
  • 77,740
  • 9
  • 79
  • 113
  • I was afraid this would be the answer. I'm going to leave the question open a while before marking this as accepted, in the hope that someone else has found a trick that works. – Mark McClelland Apr 11 '19 at 14:37
  • Re: this comment, that "the solution is to write your app such that it doesn't care about keyboard state", I agree. But in our case this leads to complexity or sub-standard UX. We have a bottom-nav bar that we want to hide when the keyboard is visible. Otherwise it remains visible between the top of the keyboard and the bottom of the EditText, which lives in a scrollable list. We've found workarounds, but they require either bug-prone state management or a temporary switch from `adjustResize` to `adjustPan`, until the EditText loses focus. Knowing keyboard state is inherently valuable. – Mark McClelland Apr 11 '19 at 14:41
  • 1
    @MarkMcClelland I agree it would be great to be able to know that. You just can't. There is no workaround without major failure points. The size thing is the best one, and it has issues. Its an architectural issue with the OS. One they could easily fix if they wanted, but have no interest in. – Gabe Sechan Apr 11 '19 at 16:01