点击打开链接

首先声明我是做系统开发的(高通平台),所以下面介绍的方法并不适合应用开发者。

最经有个需求要屏蔽HOME按键返回桌面并且实现自己的功能,发现以前的方式报错用不了,上网搜索了一下,发现都是抄来抄去基本是无用的。网上的方法不外乎这几种:

第一, 大家最常用的重写onAttachedToWindow()方法,然后在HOME点击事件KeyEvent.KEYCODE_HOME中做自己想做的事情,但是这个方法google处于安全考虑在android2.3.3之后就不支持了。

第二, 抓取系统log日志,判断有没有打印“Android.intent.category.HOME”信息来获得是否按下了HOME按键, 这样做就算代码知道是按下HOME键,那怎么阻止返回桌面呢? 这只能是截获HOME按键,并不能屏蔽它返回桌面的功能。

第三, 修改framework源码,在PhoneWindowManager中处理HOME按键的地方发送一个消息,然后在上层应用中捕获这个消息,这和上面是一样的,只能截获HOME按键,并不能阻止返回桌面的动作。

第四, 在setContentView之前getWindow().setFlags(FLAG_HOMEKEY_DISPATCHED, FLAG_HOMEKEY_DISPATCHED); 这个FLAG_HOMEKEY_DISPATCHED其实是个标志位,是个常量,值为0x80000000, 这个方法确实可以,但仅限于MTK(联发科)平台的系统,因为MTK自己实现了一套机制来规避HOME按键,而其它的平台,如高通、博通、展迅的代码中是没有加这个属性的,所以也不行。

还有极少数思路很极端的方式,看上去都觉得很繁琐,根本没耐心细看。

现在介绍我的思路,首先还是复写onAttachedToWindow()方法,具体代码是在activity中加入这一段:

@Override
public void onAttachedToWindow() {
        this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
        super.onAttachedToWindow();

}

结果当然是一进入这个activity就报错,logcat查看错误信息,报的错误是"Window type can not be changed after the window is added.",发现是WindowManagerService.java中报的错,源码位置frameworks/base/services/java/com/android/server/wm/WindowManagerService.java,具体代码段是relayoutWindow方法中判断窗口类型的时候报错:

if (attrs != null) {
                if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }
                flagChanges = win.mAttrs.flags ^= attrs.flags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
                        | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
                    win.mLayoutNeeded = true;
                }
     }

刚才说了,google可能是出于安全原因不能让你把窗口类型设为WindowManager.LayoutParams.TYPE_KEYGUARD了, 设置了就报错,要屏蔽这个错误只需要把if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }

注释掉就行了,再次运行就不会报错了,但是按HOME键还是返回桌面。继续看分发HOME按键事件的代码,源码位置frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中的if (keyCode == KeyEvent.KEYCODE_HOME) {...}中,这里就是处理上层用户按下HOME键的代码,在里面会看到// If a system window has focus, then it doesn't make sense
            // right now to interact with applications.
            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
            if (attrs != null) {
                final int type = attrs.type;
                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                    // the "app" is keyguard, so give it the key
                    return 0;
                }
                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
                for (int i=0; i<typeCount; i++) {
                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
                        // don't do anything, but also don't pass it to the app
                        return -1;
                    }
                }
            }

这一段代码,意思就是如果设置了窗口类型为 WindowManager.LayoutParams.TYPE_KEYGUARD(值为2004) 或者WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,那就return 0,交给用户自己处理,不返回桌面,否则返回-1,返回桌面。通过打log发现代码确实是return 0,但还是返回桌面,后来才发现还有方法在返回桌面:/**
     * A home key -> launch home action was detected.  Take the appropriate action
     * given the situation with the keyguard.
     */
    void launchHomeFromHotKey() {
        if (mKeyguardMediator != null && mKeyguardMediator.isShowingAndNotHidden()) {
            // don't launch home if keyguard showing
        } else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) {
            // when in keyguard restricted mode, must first verify unlock
            // before launching home
            mKeyguardMediator.verifyUnlock(new OnKeyguardExitResult() {
                public void onKeyguardExitResult(boolean success) {
                    if (success) {
                        try {
                            ActivityManagerNative.getDefault().stopAppSwitches();
                        } catch (RemoteException e) {
                        }
                        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
                        startDockOrHome();
                    }
                }
            });
        } else {
            // no keyguard stuff to worry about, just launch home!
            try {
                ActivityManagerNative.getDefault().stopAppSwitches();
            } catch (RemoteException e) {
            }
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
            startDockOrHome();
        }
    }

不知道高通怎么改的,就算return 0也会执行这个方法,也会返回桌面,因为这个方法在判断窗口类型前面调用,现在要做的就简单了,只需要加个判断,就可以屏蔽返回HOME了:

boolean isGoHome = true;
                WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
                if (attrs != null) {
                    final int type = attrs.type;
                    if (type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
                        isGoHome = false;
                    }
                }
                // Go home!
                if (isGoHome) {
                    launchHomeFromHotKey();
                }

整个HOME按键的代码段:

// First we always handle the home key here, so applications
        // can never break it, although if keyguard is on, we do let
        // it handle it, because that gives us the correct 5 second
        // timeout.
        if (keyCode == KeyEvent.KEYCODE_HOME) {

            // If we have released the home key, and didn't do anything else
            // while it was pressed, then it is time to go home!
            if (!down) {
                cancelPreloadRecentApps();
                mHomePressed = false;
                if (mHomeConsumed) {
                    mHomeConsumed = false;
                    return -1;
                }

                if (canceled) {
                    Log.i(TAG, "Ignoring HOME; event canceled.");
                    return -1;
                }

                // If an incoming call is ringing, HOME is totally disabled.
                // (The user is already on the InCallScreen at this point,
                // and his ONLY options are to answer or reject the call.)
                try {
                    ITelephony telephonyService = getTelephonyService();
                    if (telephonyService != null && telephonyService.isRinging()) {
                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
                        return -1;
                    }
                } catch (RemoteException ex) {
                    Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
                }

                // Delay handling home if a double-tap is possible.
                if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
                    mHomeDoubleTapPending = true;
                    mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
                            ViewConfiguration.getDoubleTapTimeout());
                    return -1;
                }

                boolean isGoHome = true;
                WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
                if (attrs != null) {
                    final int type = attrs.type;
                    if (type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
                        isGoHome = false;
                    }
                }
                // Go home!
                if (isGoHome) {
                    launchHomeFromHotKey();
                }
                return -1;
            }

            // If a system window has focus, then it doesn't make sense
            // right now to interact with applications.
            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
            if (attrs != null) {
                final int type = attrs.type;
                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                    // the "app" is keyguard, so give it the key
                    return 0;
                }
                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
                for (int i=0; i<typeCount; i++) {
                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
                        // don't do anything, but also don't pass it to the app
                        return -1;
                    }
                }
            }

            // Remember that home is pressed and handle special actions.
            if (repeatCount == 0) {
                mHomePressed = true;
                if (mHomeDoubleTapPending) {
                    mHomeDoubleTapPending = false;
                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
                    handleDoubleTapOnHome();
                } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI
                        || mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
                    preloadRecentApps();
                }
            } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                if (!keyguardOn) {
                    handleLongPressOnHome();
                }
            }
            return -1;
        }

至此就可以屏蔽HOME按键了,在需在上层应用中复写onAttachedToWindow()方法即可,然后

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_HOME:
// 想实现的功能

break;
        }
        return true;
    }

至此分析完毕,如果要屏蔽HOME键返回桌面不修改源码是不行的,至少上面介绍的那些方法是不行的(MTK除外),所以有这个需求的童鞋找方法的时候就不要浪费时间去看那些抄来抄去的帖子了。

Android4.3 屏蔽HOME按键返回桌面详解(源码环境下)的更多相关文章

  1. ArrayList详解-源码分析

    ArrayList详解-源码分析 1. 概述 在平时的开发中,用到最多的集合应该就是ArrayList了,本篇文章将结合源代码来学习ArrayList. ArrayList是基于数组实现的集合列表 支 ...

  2. LinkedList详解-源码分析

    LinkedList详解-源码分析 LinkedList是List接口的第二个具体的实现类,第一个是ArrayList,前面一篇文章已经总结过了,下面我们来结合源码,学习LinkedList. 基于双 ...

  3. Java开源生鲜电商平台-盈利模式详解(源码可下载)

    Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...

  4. Shiro的Filter机制详解---源码分析

    Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...

  5. Shiro的Filter机制详解---源码分析(转)

    Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...

  6. Python-Flask框架之——图书管理系统 , 附详解源码和效果图 !

    该图书管理系统要实现的功能: 1. 可以通过添加窗口添加书籍或作者, 如果要添加的作者和书籍已存在于书架上, 则给出相应的提示. 2. 如果要添加的作者存在, 而要添加的书籍书架上没有, 则将该书籍添 ...

  7. udhcp详解源码(序)

    最近负责接入模块,包括dhcp.ipoe和pppoe等等.所以需要对dhcp和ppp这几个app的源代码进行一些分析.网上有比较好的文章,参考并补充自己的分析. 这篇udhcp详解是基于busybox ...

  8. RecyclerView实现瀑布流效果(图文详解+源码奉送)

    最近有时间研究了一下RecyclerView,果然功能强大啊,能实现的效果还是比较多的,那么今天给大家介绍一个用RecyclerView实现的瀑布流效果. 先来一张效果图: 看看怎么实现吧: 整体工程 ...

  9. Java和Ibatis调用存储过程并取得返回值详解

    Java和Ibatis调用存储过程并取得返回值详解 2011-07-19 17:33 jiandanfeng2 CSDN博客 字号:T | T 本文主要介绍了Java和Ibatis调用存储过程的方法, ...

随机推荐

  1. 机器学习基石:09 Linear Regression

    线性回归假设: 代价函数------均方误差: 最小化样本内代价函数: 只有满秩方阵才有逆矩阵. 线性回归算法流程: 线性回归算法是隐式迭代的. 线性回归算法泛化可能的保证: 根据矩阵的迹的性质:tr ...

  2. python绘制散点图,柱状图和折线图

    示例:散点图 最常见的散点图之一是x-y散点图.下面的代码会大致告诉你一个matplotlib是如何工作的,你会看到如何一点点建立起一个散点图. 我们正在使用点的x和y位置的一些构成数据.运行下面的代 ...

  3. Unix系统使用的地址索引结构有什么特点?

    使用地址索引表.索引节点中地址索引表标识出含有文件数据的磁盘块的分布情况.

  4. 如何理解Spring IOC

    Spring IOC 思维导图 要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Pr ...

  5. jupyter notebook 更换主题的方法

    参考 https://github.com/dunovank/jupyter-themes install with pip # install jupyterthemes pip install j ...

  6. JavaScript正则表达式模式匹配(6)——常用的正则表达式

    1.检查邮政编码 var pattern=/[1-9][0-9]{5}/; // 必须是6位,必须是数字,第一位不能为0 var str='274200'; alert(pattern.test(st ...

  7. Luogu P1919 【模板】A*B Problem升级版(FFT快速傅里叶_FFT

    这其实就是一道裸的FFT 核心思想:把两个数拆成两个多项式用FFT相乘,再反序输出 py解法如下: input() print(int(input())*int(input())) 皮一下hihi f ...

  8. 关于mysql安装到最后一步老是停留在starting server,显示无响应

    从昨天晚上到今天安装MySQL花了好长的时间,一直是在后面starting server 这部就显示无响应,查资料了解到是MySQL有残留,有些注册表文件需要手动清理,下面是具体方法. 1.先用卸载软 ...

  9. Rabbitmq集群

    分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 Rabbitmq集群高 ...

  10. FJUT寒假作业涨姿势题解

    题意非常简单易懂,对于涨姿势0,数据非常小,比较容易想到的是直接循环暴力解题完成任务.把数据放入数组arr,循环i,j控制所有区间算和.结果记入vis. 到了涨姿势1,2,3,我们观察数据变化,发现数 ...