Detect "Show virtual keyboard" setting programatically in Android - android

There's a setting in Android called "Show virtual keyboard", which in my phone is located in:
Settings > Languages & input > Physical keyboard. It controls whether to keep the virtual keyboard on the screen while a physical keyboard is active.
Is there a way of programatically querying whether it is enabled or disabled? I can't find it in the Settings.System list in Android developers.

I don't know exactly if you can query that information but if it helps you, there is a way to programatically detect if the keyboard is opened or not.
private boolean isKeyboardShowing = false;
private void detectKeyboard() {
rootView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int screenHeight = rootView.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15) {
// keyboard is opened
if (!isKeyboardShowing) {
isKeyboardShowing = true;
}
}
else {
// keyboard is closed
if (isKeyboardShowing) {
isKeyboardShowing = false;
}
}
}
});
}

Related

android detect Samsung Galaxy S8 navigation bar hide or show programmatically

On some devices such as Samsung S8, navigation bar can be hide or show, that's a question in some condition.
Samsung S8's navigation bar can be hide or show by click left bottom button
I didn't find straight way to determine even if in the Android sources code.
And I google some issues, such as A good solution to check for navigation bar , but it doesn't help.
Any help is very appreciated.
First, credit the original author: https://www.jianshu.com/p/ddfbabd614b6
For the Samsung phones (i.e, S8, S9, etc) you can detect if the navigation bar is showing via listening to a Samsung event.
private static final String SAMSUNG_NAVIGATION_EVENT = "navigationbar_hide_bar_enabled";
Then just listen to this event, and do your thing:
private void checkNavigationBar() {
if (isSamsungVersionNougat()) { // check if Samsung phones
// detect navigation bar
try {
// there are navigation bar
if (Settings.Global.getInt(activity.getContentResolver(), SAMSUNG_NAVIGATION_EVENT) == 0) {
// Your code
// example: galleryViewModel.navigationBarHeight.set(getNavigationBarHeight());
} else { // there are no navigation bar
// Your code
// example: galleryViewModel.navigationBarHeight.set(0);
}
} catch (Exception ignored) {
}
barHideEnableObserver = new BarHideEnableObserver(new Handler());
activity.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SAMSUNG_NAVIGATION_EVENT),
true, barHideEnableObserver);
} else {
galleryViewModel.navigationBarHeight.set(getNavigationBarHeight());
}
}
Use this method, worked for me. Make sure the view has been rendered first to make sure that getHeight() doesn't return 0. Also make sure that the rootView you are using is meant to take up the whole screen.
public static boolean hasNavBar (Activity activity, View rootView) {
if (activity == null || rootView == null)
return true;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
return true;
Display d = activity.getWindowManager().getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
d.getRealMetrics(realDisplayMetrics);
int viewHeight = rootView.getHeight();
if (viewHeight == 0)
return true;
int realHeight = realDisplayMetrics.heightPixels;
return realHeight != viewHeight;
}

How to validate virtual keyboard visibility?

I want to show a button when the virtual keyboard is open and hide this button if the virtual keyboard visibility is off.But I could not find any listeners to perform this activity.
Anybody knows how to do this?
As found here, you'll need to instantiate the SoftkeyBoard and add a listener.
/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{
#Override
public void onSoftKeyboardHide()
{
// Code here
}
#Override
public void onSoftKeyboardShow()
{
// Code here
}
});
/*
Open or close the soft keyboard programatically
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();
/*
SoftKeyboard can catch keyboard events when an EditText gains focus and keyboard appears
*/
/* Prevent memory leaks:
*/
#Override
public void onDestroy()
{
super.onDestroy();
softKeyboard.unRegisterSoftKeyboardCallback();
}
In his post, you will also find more information about bug fixes and possible problems.
add onGlobalLayoutListener to your parent view of activity/fragment and make your button visibility accordingly
final View parentView= findViewById(R.id.myrootview);
parentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = root.getRootView().getHeight() - root.getHeight();
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
//Soft KeyBoard Hidden---button visible
}else{
//Soft KeyBoard Shown---button hide
}
}
});
There is no direct event for keyboard open and close. but you can create observer on your full layout and then display buttons or whatever you want to do.
For Observer code check this - Hide part of activity_main.xml if keyboard is open (Android)

How to prevent windowpopup displacement after showing soft keyboard?

I'm created WindowPopup, with edittext. And when i focused on it, soft keyboard show and displace this popup above the top bounds, so i can't see what i'm typing. I want to show keyboard without any displace of views, just above them.
I read that i can change softInputMode for it, so i'm created class which extends from EditText, and tried to change inputMode in onFocusListener, but it didn't help.
setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View veiw, boolean has_focus) {
if (has_focus) {
//Try to change input mode to prevent displacing
((Activity) getContext()).getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
} else {
//.. return back previous input mode
}
});
I did so, because i need such behavior only in this popup, but i even try to change action attribute in my manifets file
android:windowSoftInputMode="adjustNothing"
or
android:windowSoftInputMode="stateHidden"
Can i show keyboard without any displacing of view?
P.S. i'm using Android API 15
When PopupWindow creates popup view, it sets to it new WindowManager.LayoutParams with softInputMode, which is overwrites behavior of Window.softInputMode. Here piece of code from PopupWindow
private WindowManager.LayoutParams createPopupLayout(IBinder token) {
WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = Gravity.LEFT | Gravity.TOP;
p.width = mLastWidth = mWidth;
p.height = mLastHeight = mHeight;
if (mBackground != null) {
p.format = mBackground.getOpacity();
} else {
p.format = PixelFormat.TRANSLUCENT;
}
p.flags = computeFlags(p.flags);
p.type = mWindowLayoutType;
p.token = token;
/*mSoftInputMode is the private field which is by default equals
to WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
*/
p.softInputMode = mSoftInputMode;
}
So to change softInputMode you need just to call public method of PopupWindow
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
And there is no need to remember previous soft input method, because this behavior will be only for this PopupWindow

Getting notified when virtual keyboard shown / dismissed for an EditText?

I'd like to get notified when the virtual keyboard is shown / dismissed. This does not seem to be possible, other than by using some layout resizing listener tricks:
How to check visibility of software keyboard in Android?
My activity has a single EditText. I could make it not have focus at activity startup, then add a focuschangelistener to it. When it gains focus, I can do my onVirtualKeyboardShown() stuff. If I could just listen for the back key being pressed in the EditText, I could then interpret that as the virtual keyboard being hidden. Something like:
EditText et = ...;
et.setOnFocusChangedListener(new OnFocusChangedListener() {
public void onFocusChanged(boolean focused) {
if (focused) {
// virtual keyboard probably showing.
}
}
});
et.setKeyListener(new KeyListener() {
public void onKeyPressed(int code) {
if (code == BACK_KEY) [
if (et.isFocused()) {
// virtual keyboard probably hiding.
// lose focus to set up for next time.
et.setFocused(false);
}
}
}
});
Seems like any approach is fraught with problems given all the differences between virtual keyboards, then we also have to deal with physical keyboards too,
Thanks
This does not seem to be possible, other than by using some layout resizing listener tricks
Correct.
I want to be notified so I can show my own suggestions ribbon above the virtual keyboard.
Not all Android devices use virtual keyboards. Some have physical keyboards. Since you need to support both types of devices, you need to come up with a UI design that does not assume that everyone has a virtual keyboard.
// Catch the keyboard height
final LinearLayout masterView = (LinearLayout) findViewById(R.id.conversation_prent);
masterView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect r = new Rect();
masterView.getWindowVisibleDisplayFrame(r);
int result = 0;
int resourceId = getResources().getIdentifier(
"status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(
resourceId);
}
int heightDiff = masterView.getRootView().getHeight()
- masterView.getHeight();
heightDiff = heightDiff - (ab.getHeight() + result);
Log.e("Keyboard Size", "Size: " + heightDiff);
if (heightDiff > 200) {
// The keyboard is shown
} else {
// The keyboard is hidden
}
}
});
if your app is running on android < 3 (HoneyComb) delete the parts of the code that are related to the actionbar.

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

Is there a way in Android to detect if the software (a.k.a. "soft") keyboard is visible on screen?
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)
}
}
}
});
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");
}
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?.
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);
}
});
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
Twitter WindowInsetsCompat announcement
Reddit thread
Androidx Core 1.5.0-alpha02 release notes
WindowInsetsCompat Docs
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");
}
}
});
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
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.
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:
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"
/>
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
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.
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
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 }
You can refer to this answer - https://stackoverflow.com/a/24105062/3629912
It worked for me everytime.
adb shell dumpsys window InputMethod | grep "mHasSurface"
It will return true, if software keyboard is visible.
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();
}
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()
}
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
}
}
});
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;
}
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.
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;
}
}
private fun isKeyboardVisible(rootView: View) =
ViewCompat.getRootWindowInsets(rootView)!!.isVisible(WindowInsetsCompat.Type.ime())
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.
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()
}
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();
}
}
}
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();
}
}
});
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
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.
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;
}
}
};
Here is a workaround to know if softkeyboard is visible.
Check for running services on the system using ActivityManager.getRunningServices(max_count_of_services);
From the returned ActivityManager.RunningServiceInfo instances, check clientCount value for soft keyboard service.
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.
On keyboard dismissal, clientCount is decremented. In this case, it resets to 1.
Some of the popular keyboards have certain keywords in their classNames:
Google AOSP = IME
Swype = IME
Swiftkey = KeyboardService
Fleksy = keyboard
Adaptxt = IME (KPTAdaptxtIME)
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.
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
}
}
}
}
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.
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
}

Categories

Resources