Friday, December 23, 2016

How to intercept event Soft keyboard shown/hidden

This is solution that is composed of code from SO and my enhancement to support various API levels.
Inspired by SO post http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android

Updated 1/24/2017

Updated 15 Apr. 2017
I hope, this solution is final.

Preparation class:

package my.package.utils;

import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.ViewTreeObserver;

import java.util.LinkedList;
import java.util.List;

public class SoftKeyboardStateWatcher
        implements ViewTreeObserver.OnGlobalLayoutListener {

    public void onStart() {
        isSoftKeyboardOpened = false;
    }

    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);

        void onSoftKeyboardClosed();

    }

    private SoftKeyboardStateListener listener;

    private final View activityRootView;
    private boolean isSoftKeyboardOpened;
    private int resize;
    private int mKeyboardHeight;

    public SoftKeyboardStateWatcher(
            View activityRootView,
            SoftKeyboardStateListener listener
    ) {
        this(activityRootView, false);
        this.listener = listener;
    }

    private SoftKeyboardStateWatcher(
            View activityRootView,
            boolean isSoftKeyboardOpened
    ) {
        this.activityRootView = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override    public void onGlobalLayout() {
        Rect r = new Rect();
        activityRootView.getWindowVisibleDisplayFrame(r);
        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        int correction = r.top;
        if (!isSoftKeyboardOpened && heightDiff > correction &&
                heightDiff > getStatusBarHeight() + getNavBarHeight()) {
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff, correction);
        } else {
            if (isSoftKeyboardOpened && heightDiff <= correction) {
                isSoftKeyboardOpened = false;
                notifyOnSoftKeyboardClosed();
            }else{
                if (!isSoftKeyboardOpened) return;
                if (mKeyboardHeight == heightDiff - correction) return;
                //Keyboard height has changed
            }
        }
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx, int correction) {
        if (listener == null) return;
        mKeyboardHeight = keyboardHeightInPx - correction;
        listener.onSoftKeyboardOpened(keyboardHeightInPx);
    }

    private void notifyOnSoftKeyboardClosed() {
        if (listener == null) return;
        listener.onSoftKeyboardClosed();
    }

    public void removeSoftKeyboardStateListener() {
        listener = null;
    }

    private int getStatusBarHeight() {
        int result = 0;
        Resources resources = activityRootView.getResources();
        int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = resources.getDimensionPixelSize(resourceId);
        }
        return result;
    }

    private int getNavBarHeight() {
        Resources resources = activityRootView.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

}



The next step:
Your activity must implement 
SoftKeyboardStateWatcher.SoftKeyboardStateListener

Declare in your activity

protected SoftKeyboardStateWatcher mSoftKeyboardStateWatcher;

And run following code in onCreate:

int rootId = getRootId();//here you must supply root View id
View root;
if (rootId == 0 || (root = findViewById(rootId)) == null) return;
mSoftKeyboardStateWatcher = new SoftKeyboardStateWatcher(root);
mSoftKeyboardStateWatcher.addSoftKeyboardStateListener(his);

Now you have these methods:

@Override
public void onSoftKeyboardOpened(int keyboardHeightInPx) {
}

@Override
public void onSoftKeyboardClosed() {
}

Also do not forget to remove listener:

@Override
public void onDestroy() {
    if (mSoftKeyboardStateWatcher != null)
        mSoftKeyboardStateWatcher.removeSoftKeyboardStateListener(this);
    super.onDestroy();
}