后台监听按键键值探索

探讨Android实现后台(Service)按键监听的功能
Android 在Service中监听软键盘显示状态

项目里有需求:在后台监听到物理按键的键值,而且不能依赖activity
最后方案定为用一个像素悬浮窗,通过service控制启动和移除,利用悬浮窗的view来监听按键键值。
但是,悬浮窗的view必须获取到焦点才可以监听到键值,另外,还要及时在用户触摸屏幕或点击返回按键时释放焦点。因为悬浮窗view获取了焦点后,系统的软键盘就弹不出来,返回键也会失效

public class ButtonFloatService extends Service implements View.OnTouchListener, View.OnKeyListener,SoftKeyboardDetector.SoftKeyboardListener {
    private static final String TAG = "ButtonFloatService";
    private WindowManager windowManager;
    private WindowManager.LayoutParams params;
    private boolean isShowing;
    private View view;
    //private Timer timer;
    //private TimerTask timerTask;
    private boolean isRelease = false;
    private static final int MSG = 180;
    private boolean isSoftKeyboardShowing = false;
    @Override
    public void onCreate() {
        super.onCreate();
        LayoutInflater layoutInflater = LayoutInflater.from(this);
        windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        view = layoutInflater.inflate(R.layout.pix_layout, null);
        initWindow();
        view.setOnTouchListener(this);
        view.setOnKeyListener(this);

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onstart");
        show();
        return START_NOT_STICKY;
    }

    private void initWindow() {
        Log.d(TAG, "initWindow ");
        isRelease = false;
        params = new WindowManager.LayoutParams();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            params.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
        params.format = PixelFormat.TRANSPARENT;
        params.gravity = Gravity.START | Gravity.TOP;
        params.x = 0;
        params.y = 0;
        params.width = 1;
        params.height = 1;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        view.post(() -> {
            view.setFocusable(true);
            view.setFocusableInTouchMode(true);
            view.requestFocus();
        });
    }

    private void refreshWindowCatchFocus() {
        if (isRelease) {
            Log.d(TAG, "refreshWindowCatchFocus: 获取焦点");
            isRelease = false;
            params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            view.post(() -> {
                view.setFocusable(true);
                view.setFocusableInTouchMode(true);
                view.requestFocus();
            });
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (view.isAttachedToWindow()) {
                    windowManager.updateViewLayout(view, params);
                }
            } else {
                if (view.getParent() != null) {
                    try {
                        windowManager.updateViewLayout(view, params);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }


        }
    }


    private void refreshWindowReleaseFocus() {
        if (!isRelease) {
            Log.d(TAG, "refreshWindowReleaseFocus: 释放焦点");
            isRelease = true;
            params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            view.post(() -> {
                view.setFocusable(false);
                view.setFocusableInTouchMode(false);
                view.clearFocus();
            });
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (view.isAttachedToWindow()) {
                    windowManager.updateViewLayout(view, params);
                }
            } else {
                if (view.getParent() != null) {
                    try {
                        windowManager.updateViewLayout(view, params);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            SoftKeyboardDetector.getInstance().setSoftKeyboardListener(this);
            SoftKeyboardDetector.getInstance().initSoftKeyboardDetector();
        }
    }

    private boolean addToWindow() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Log.d(TAG, "addToWindow: 未开启悬浮窗权限");
                return false;
            }
        }
        if (windowManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (!view.isAttachedToWindow()) {
                    windowManager.addView(view, params);
                    return true;
                } else {
                    return false;
                }
            } else {
                try {
                    if (view.getParent() == null) {
                        windowManager.addView(view, params);
                    }
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        } else {
            return false;
        }
    }

    private void show() {
        Log.d(TAG, "show float-->isShowing=" + isShowing);
        if (isShowing)
            return;
        addToWindow();
        isShowing = true;
    }

    private void remove() {
        Log.d(TAG, "remove float-->isShowing=" + isShowing);
        if (!isShowing) return;
        removeFromWindow();
        isShowing = false;
    }

    private void removeFromWindow() {
        try {
            if (windowManager != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    if (view.isAttachedToWindow()) {
                        Log.d(TAG, "float is removeViewImmediate");
                        windowManager.removeViewImmediate(view);
                    } else {
                        if (view.getParent() != null) {
                            Log.d(TAG, "float is removeView");
                            windowManager.removeView(view);
//                            windowManager.removeViewImmediate(view);
                        }
                    }
                } else {
                    if (view.getParent() != null) {
                        windowManager.removeViewImmediate(view);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        SoftKeyboardDetector.getInstance().clearSoftKeyboardDetector();
        remove();
        super.onDestroy();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d(TAG, "onTouch: event=" + event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_OUTSIDE:
                refreshWindowReleaseFocus();
                break;
        }
        return false;
    }


   /* private void timerStart2CatchFocus() {
        if (isRelease) {
            Log.d(TAG, "timerStart2CatchFocus: 启动计时器准备获取焦点");
            if (timerTask != null) {
                timerTask.cancel();
                timerTask = null;
            }
            timerTask = new TimerTask() {
                @Override
                public void run() {
                    //判断是否有软键盘,如果没有,获取焦点;否则,无动作
                    if (!SoftKeyboardDetector.getInstance().isInputMethodShowing(Utils.getContext())) {
                        Message m = Message.obtain();
                        m.what = MSG;
                        handler.sendMessage(m);
                    }
                }
            };
            if (timer != null) {
                timer.cancel();
                timer = null;
            }
            timer = new Timer();
            timer.schedule(timerTask, 1500);
        }
    }*/

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        Log.d(TAG, "onKey: keyCode=" + keyCode + "--keyEvent=" + event);
        //桌面按键监听
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK){
            refreshWindowReleaseFocus();
           // timerStart2CatchFocus();
            return false;
        }
        if (event.getAction() == KeyEvent.ACTION_UP) {
            ToastUtils.showShortToast("keyCode==" + keyCode);
        }
  
        return false;
    }


  private ButtonHandler handler = new ButtonHandler(this);
    static class ButtonHandler extends Handler{
        WeakReference<ButtonFloatService> weakReference;

        ButtonHandler(ButtonFloatService buttonFloatService){
            weakReference = new WeakReference<>(buttonFloatService);
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG && weakReference.get() != null)
                weakReference.get().refreshWindowCatchFocus();
            super.handleMessage(msg);
        }
    }


    @Override
    public void onShowKeyboard() {
        isSoftKeyboardShowing = true;
    }

    @Override
    public void onDismissKeyboard() {
        isSoftKeyboardShowing = false;
        Message m = Message.obtain();
        m.what = MSG;
        handler.sendMessage(m);
    }
}

/**
 * 软键盘实时检测器,无需Activity
 */

public class SoftKeyboardDetector {
    private static final String TAG = "SoftKeyboard";
    private SoftKeyboardListener softKeyboardListener;
    private static SoftKeyboardDetector instance;
    private TimerTask timerTask;
    private Timer timer;

    public static SoftKeyboardDetector getInstance() {
        if (instance == null) {
            synchronized (SoftKeyboardDetector.class) {
                if (instance == null) {
                    instance = new SoftKeyboardDetector();
                }
            }
        }
        return instance;
    }

    public void setSoftKeyboardListener(SoftKeyboardListener softKeyboardListener) {
        this.softKeyboardListener = softKeyboardListener;
    }

    /**
     * 初始化软键盘检测器
     */
    public void initSoftKeyboardDetector() {
        clearSoftKeyboardDetector();
        timerTask = new TimerTask() {
            @Override
            public void run() {
                if (isInputMethodShowing(Utils.getContext())) {
                    if (softKeyboardListener != null) {
                        softKeyboardListener.onShowKeyboard();
                    }
                } else {
                    if (softKeyboardListener != null) {
                        softKeyboardListener.onDismissKeyboard();
                    }
                }
            }
        };
        timer = new Timer();
        timer.schedule(timerTask, 500, 500);//周期为1s
    }

    /**
     * 清理软键盘检测器
     */
    public void clearSoftKeyboardDetector() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        if (timerTask != null) {
            timerTask.cancel();
            timerTask = null;
        }
    }

    /**
     * 判断软键盘是否显示
     * @param context application的上下文
     * @return
     */
    public boolean isInputMethodShowing(Context context) {
        //得到默认输入法包名
        String defaultInputName = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
        defaultInputName = defaultInputName.substring(0, defaultInputName.indexOf("/"));
        boolean isInputing = false;
        if (android.os.Build.VERSION.SDK_INT > 20) {
            try {
                InputMethodManager imm = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                Class clazz = imm.getClass();
                Method method = clazz.getMethod("getInputMethodWindowVisibleHeight", null);
                method.setAccessible(true);
                int height = (Integer) method.invoke(imm, null);
                if (height > 100) {
                    isInputing = true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();

            for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
                if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
                    if (appProcess.processName.equals(defaultInputName)) {
                        isInputing = true;
                        break;
                    }
                }
            }
        }
        if (isInputing) {
            Log.d(TAG, "软键盘显示中");
        } else {
            Log.d(TAG, "软键盘隐藏中");
        }
        return isInputing;
    }


    public interface SoftKeyboardListener {

        void onShowKeyboard();

        void onDismissKeyboard();
    }
}

pix_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <View
        android:background="@color/transparent"
        android:layout_width="1dp"
        android:layout_height="1dp">
    </View>
</RelativeLayout>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值