package com.bit.utils; import android.annotation.TargetApi; import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import java.lang.reflect.Field; public final class KeyboardUtils { private static int sDecorViewInvisibleHeightPre; private static OnGlobalLayoutListener onGlobalLayoutListener; private static OnSoftInputChangedListener onSoftInputChangedListener; private static int sContentViewInvisibleHeightPre5497; private KeyboardUtils() { throw new UnsupportedOperationException("u can't instantiate me..."); } /** * Show the soft input. * * @param activity The activity. */ public static void showSoftInput(final Activity activity) { showSoftInput(activity, InputMethodManager.SHOW_FORCED); } /** * Show the soft input. * * @param activity The activity. * @param flags Provides additional operating flags. Currently may be * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} bit set. */ public static void showSoftInput(final Activity activity, final int flags) { View view = activity.getCurrentFocus(); if (view == null) { view = new View(activity); } showSoftInput(view, flags); } /** * Show the soft input. * * @param view The view. */ public static void showSoftInput(final View view) { showSoftInput(view, InputMethodManager.SHOW_FORCED); } /** * Show the soft input. * * @param view The view. * @param flags Provides additional operating flags. Currently may be * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} bit set. */ public static void showSoftInput(final View view, final int flags) { InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); if (imm == null) return; view.setFocusable(true); view.setFocusableInTouchMode(true); view.requestFocus(); imm.showSoftInput(view, flags, new ResultReceiver(new Handler()) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == InputMethodManager.RESULT_UNCHANGED_HIDDEN || resultCode == InputMethodManager.RESULT_HIDDEN) { toggleSoftInput(); } } }); } /** * Hide the soft input. * * @param activity The activity. */ public static void hideSoftInput(final Activity activity) { View view = activity.getCurrentFocus(); if (view == null) { view = new View(activity); } hideSoftInput(view); } /** * Hide the soft input. * * @param view The view. */ public static void hideSoftInput(final View view) { InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); if (imm == null) return; imm.hideSoftInputFromWindow(view.getWindowToken(), 0, new ResultReceiver(new Handler()) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN || resultCode == InputMethodManager.RESULT_SHOWN) { toggleSoftInput(); } } }); } /** * Toggle the soft input display or not. */ public static void toggleSoftInput() { InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); //noinspection ConstantConditions imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } /** * Return whether soft input is visible. * * @param activity The activity. * @return {@code true}: yes<br>{@code false}: no */ public static boolean isSoftInputVisible(final Activity activity) { return getDecorViewInvisibleHeight(activity) > 0; } private static int sDecorViewDelta = 0; private static int getDecorViewInvisibleHeight(final Activity activity) { final View decorView = activity.getWindow().getDecorView(); if (decorView == null) return sDecorViewInvisibleHeightPre; final Rect outRect = new Rect(); decorView.getWindowVisibleDisplayFrame(outRect); Log.d("KeyboardUtils", "getDecorViewInvisibleHeight: " + (decorView.getBottom() - outRect.bottom)); int delta = Math.abs(decorView.getBottom() - outRect.bottom); if (delta <= getNavBarHeight()) { sDecorViewDelta = delta; return 0; } return delta - sDecorViewDelta; } /** * Register soft input changed listener. * * @param activity The activity. * @param listener The soft input changed listener. */ public static void registerSoftInputChangedListener(final Activity activity, final OnSoftInputChangedListener listener) { final int flags = activity.getWindow().getAttributes().flags; if ((flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) != 0) { activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } final FrameLayout contentView = activity.findViewById(android.R.id.content); sDecorViewInvisibleHeightPre = getDecorViewInvisibleHeight(activity); onSoftInputChangedListener = listener; onGlobalLayoutListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (onSoftInputChangedListener != null) { int height = getDecorViewInvisibleHeight(activity); if (sDecorViewInvisibleHeightPre != height) { onSoftInputChangedListener.onSoftInputChanged(height); sDecorViewInvisibleHeightPre = height; } } } }; contentView.getViewTreeObserver() .addOnGlobalLayoutListener(onGlobalLayoutListener); } /** * Unregister soft input changed listener. * * @param activity The activity. */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static void unregisterSoftInputChangedListener(final Activity activity) { final View contentView = activity.findViewById(android.R.id.content); contentView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); onSoftInputChangedListener = null; onGlobalLayoutListener = null; } /** * Fix the bug of 5497 in Android. * <p>Don't set adjustResize</p> * * @param activity The activity. */ public static void fixAndroidBug5497(final Activity activity) { // Window window = activity.getWindow(); // int softInputMode = window.getAttributes().softInputMode; // window.setSoftInputMode(softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); final FrameLayout contentView = activity.findViewById(android.R.id.content); final View contentViewChild = contentView.getChildAt(0); final int paddingBottom = contentViewChild.getPaddingBottom(); sContentViewInvisibleHeightPre5497 = getContentViewInvisibleHeight(activity); contentView.getViewTreeObserver() .addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int height = getContentViewInvisibleHeight(activity); if (sContentViewInvisibleHeightPre5497 != height) { contentViewChild.setPadding( contentViewChild.getPaddingLeft(), contentViewChild.getPaddingTop(), contentViewChild.getPaddingRight(), paddingBottom + getDecorViewInvisibleHeight(activity) ); sContentViewInvisibleHeightPre5497 = height; } } }); } private static int getContentViewInvisibleHeight(final Activity activity) { final View contentView = activity.findViewById(android.R.id.content); if (contentView == null) return sContentViewInvisibleHeightPre5497; final Rect outRect = new Rect(); contentView.getWindowVisibleDisplayFrame(outRect); Log.d("KeyboardUtils", "getContentViewInvisibleHeight: " + (contentView.getBottom() - outRect.bottom)); int delta = Math.abs(contentView.getBottom() - outRect.bottom); if (delta <= getStatusBarHeight() + getNavBarHeight()) { return 0; } return delta; } /** * Fix the leaks of soft input. * <p>Call the function in {@link Activity#onDestroy()}.</p> * * @param activity The activity. */ public static void fixSoftInputLeaks(final Activity activity) { if (activity == null) return; InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); if (imm == null) return; String[] leakViews = new String[]{"mLastSrvView", "mCurRootView", "mServedView", "mNextServedView"}; for (String leakView : leakViews) { try { Field leakViewField = InputMethodManager.class.getDeclaredField(leakView); if (leakViewField == null) continue; if (!leakViewField.isAccessible()) { leakViewField.setAccessible(true); } Object obj = leakViewField.get(imm); if (!(obj instanceof View)) continue; View view = (View) obj; if (view.getRootView() == activity.getWindow().getDecorView().getRootView()) { leakViewField.set(imm, null); } } catch (Throwable ignore) { /**/ } } } /** * Click blank area to hide soft input. * <p>Copy the following code in ur activity.</p> */ public static void clickBlankArea2HideSoftInput() { Log.i("KeyboardUtils", "Please refer to the following code."); /* @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { View v = getCurrentFocus(); if (isShouldHideKeyboard(v, ev)) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS ); } } return super.dispatchTouchEvent(ev); } // Return whether touch the view. private boolean isShouldHideKeyboard(View v, MotionEvent event) { if (v != null && (v instanceof EditText)) { int[] l = {0, 0}; v.getLocationInWindow(l); int left = l[0], top = l[1], bottom = top + v.getHeight(), right = left + v.getWidth(); return !(event.getX() > left && event.getX() < right && event.getY() > top && event.getY() < bottom); } return false; } */ } private static int getStatusBarHeight() { Resources resources = Resources.getSystem(); int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); return resources.getDimensionPixelSize(resourceId); } private static int getNavBarHeight() { Resources res = Resources.getSystem(); int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId != 0) { return res.getDimensionPixelSize(resourceId); } else { return 0; } } /////////////////////////////////////////////////////////////////////////// // interface /////////////////////////////////////////////////////////////////////////// public interface OnSoftInputChangedListener { void onSoftInputChanged(int height); } }