7

I'm trying to detect the virtual keyboard height in Android.

I found a similar topic: Get the height of virtual keyboard in Android

It seems the author found a way to detect the height:

I found a way to get it. After I request to open virtual keyboard, I send pointer event that I generate. their y coordinate starts from height of device and decreases.

I don't understand how to do that.

Community
  • 1
  • 1
Qing Xu
  • 2,704
  • 2
  • 22
  • 21

2 Answers2

3

I'll be using the code provided at the link that you posted:

// Declare Variables

int softkeyboard_height = 0;
boolean calculated_keyboard_height;
Instrumentation instrumentation;

// Initialize instrumentation sometime before starting the thread
instrumentation = new Instrumentation();

mainScreenView is your base view, your activity's view. m(ACTION_DOWN) and m1(ACTION_UP) are touch events that are dispatched using Instrumentation#sendPointerSync(MotionEvent). The logic is that a MotionEvent dispatched to where the keyboard is being displayed will cause the following SecurityException:

java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission

So, we start at the bottom of the screen and make our way up (by decrementing y) on every iteration of the loop. For certain number of iterations, we will get a SecurityException (which we'll catch): this would imply that the MotionEvent is happening over the keyboard. The moment y gets small enough (when its just above the keyboard), we'll break out of the loop and calculate the keyboard's height using:

softkeyboard_height = mainScreenView.getHeight() - y;

Code:

Thread t = new Thread(){
        public void run() {
            int y = mainScreenView.getHeight()-2;
            int x = 10;
            int counter = 0;
            int height = y;
            while (true){
                final MotionEvent m = MotionEvent.obtain(
                        SystemClock.uptimeMillis(),
                        SystemClock.uptimeMillis(),
                        MotionEvent.ACTION_DOWN,
                        x, 
                        y,
                        1);
                final MotionEvent m1 = MotionEvent.obtain(
                        SystemClock.uptimeMillis(),
                        SystemClock.uptimeMillis(),
                        MotionEvent.ACTION_UP,
                        x, 
                        y,
                        1);
                boolean pointer_on_softkeyboard = false;
                try {
                    instrumentation.sendPointerSync(m);
                    instrumentation.sendPointerSync(m1);
                } catch (SecurityException e) {
                    pointer_on_softkeyboard = true;
                }
                if (!pointer_on_softkeyboard){
                    if (y == height){
                        if (counter++ < 100){
                            Thread.yield();
                            continue;
                        }
                    } else if (y > 0){
                        softkeyboard_height = mainScreenView.getHeight() - y;
                        Log.i("", "Soft Keyboard's height is: " + softkeyboard_height);
                    }
                    break;
                }
                y--;

            }
            if (softkeyboard_height > 0 ){
                // it is calculated and saved in softkeyboard_height
            } else {
                calculated_keyboard_height = false;
            }
        }
    };
    t.start();

Instrumentation#sendPointerSync(MotionEvent):

Dispatch a pointer event. Finished at some point after the recipient has returned from its event processing, though it may not have completely finished reacting from the event -- for example, if it needs to update its display as a result, it may still be in the process of doing that.

Vikram
  • 51,313
  • 11
  • 93
  • 122
  • Is this a public API? I can't find documentation about `INTERNAL_POINTER_META_STATE`. – rid Sep 24 '13 at 07:34
  • @rid Sorry about that. `INTERNAL_POINTER_META_STATE` is an integer constant. For the question's purpose, using any integer value would work. I have edited the code above. Thanks for pointing it out. – Vikram Sep 24 '13 at 07:50
  • Still, is it safe to use this without the functionality having a chance to disappear in any future version of Android? Is that `1` guaranteed to always be the same? Is it documented anywhere officially by Google? Can you rely on this without fear of producing brittle code? – rid Sep 24 '13 at 08:01
  • @rid About this functionality disappearing any time soon: I would guess `no`. The SecurityException that we are counting on is thrown because of a missing permission: `INJECT_EVENTS`. This permission can only be obtained by system apps: Romain Guy confirms this [here](https://code.google.com/p/android/issues/detail?id=8422). `Can you rely on this without fear of producing brittle code?` Strictly speaking, this is a stereotypical hack. Use it, but be ready for when it lets up. – Vikram Sep 24 '13 at 08:34
  • @rid The `1` is there for face-value. It does not take part in the program's logic. It maps to `KeyEvent.META_SHIFT_ON`. If you'd feel better using a value that isn't mapped, use zero. But I can't recommend a value since they're all just filling the .... That sixth argument can take values of the form KeyEvent.META_X. Shift / alt / ctrl / caps-on etc. make up the meta/modifier keys. – Vikram Sep 24 '13 at 08:42
1

Use OnGlobalLayoutListener, it works perfectly for me.

Swetank
  • 1,577
  • 11
  • 14