点击打开链接

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

最经有个需求要屏蔽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. Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载)

    Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载) 说明:主要是针对一些中大型的项目需要进行分布式以及负载均衡的架构提一些思路与建议. 面对大量用户访问.高并发请求,海量 ...

  2. 最全解释:Linux操作系统下的软件安装与管理详解(源码安装、rpm/dpkg、yum/apt-get安装)

      在linux上安装软件,安装方式和软件包获取的途径都远远比windows的丰富,那当然这就变得复杂很多,本文旨在理解linux下繁杂的软件安装.管理原理 ,学习软件的安装方式.源码包格式.远程软件 ...

  3. Android SharedPreferences 详解 源码解析

    1.实现类 SharedPreferences 只是一个接口,其实现类是SharedPreferencesImpl. 工作流程分析: 创建sp 的时候,会去查看是否有bak文件,如果有的话,把bak文 ...

  4. SpringBoot入门详解源码分析

    注:文章内容来自于黑马的虎哥,个人感觉写的挺好的,所以只是做了简单整理,我只是文章的搬运工! # 0.学习目标 - 了解SpringBoot的作用 - 掌握java配置的方式 - 了解SpringBo ...

  5. linux设备驱动开发详解源码,linux设备驱动开发详解光盘源码.rar

    压缩包 : linux设备驱动开发详解光盘源码.rar 列表 19/busybox源代码/busybox-1.2.1.tar.bz2 19/MTD工具/mtd-utils-1.0.0.tar.gz 1 ...

  6. 短信平台专业版软件客户端功能详解源码搭建|移讯云短信系统

    国际短信平台专业版软件客户端功能详解|移讯云短信系统 首页显示 剩余条数 充值总数 提交总数 成功数量 失败数量 未知数量 代发数量 签名数量 最新提交 平台公告 API接口文档 短信发送 发送短信选 ...

  7. boos里的AHCI RAID_希洛克团本详解 DNF国服环境下Raid困难模式

    随着9月底的临近,大部分玩家都在期待着本月底的金秋版本更新,而全新的金秋版本给大家带来的主要内容之一当然还是大家期待了很久的全新团本希洛克Raid.那么国服里的希洛克具体是什么样的呢?快跟小编一起走进 ...

  8. 【QT学习】QSS样式表实现界面换肤(图文详解+源码)

    文章目录 前言 一.实现效果 二.QSS简介及用法 1.什么是QSS? 2.怎么使用QSS? 三.QSS用法一:单个控件调用setStyleSheet函数 四.QSS用法二:编写单个界面.qss文件的 ...

  9. Qt之布局设置setLayout详解-源码剖析(下)

    一.简述 大家好,我是前行中的小猪,今天呢给大家继续上一篇Qt之布局设置setLayout详解(上)之后的内容,再给大家进行一下拓展. 1.1 setLayout源码剖析 上篇我们说到如何清空部件上的 ...

最新文章

  1. 松本行弘:我的编程人生
  2. 四、垃圾收集之垃圾收集算法
  3. python入门到实践-一本书搞定Python入门到实践
  4. HTML(格式,文本标签)
  5. [jstips]向数组中插入一个元素
  6. js 控制按钮点击后不可用(用于短信或者邮箱验证)
  7. python2d 平滑插值处理_python中平滑的、通用的2D线性插值
  8. 为什么鞋带总是松开?罪魁祸首其实是…
  9. 1093 字符串A+B (20分)
  10. 42. HTTP Cookie
  11. Echarts数据可视化特效散点图点动态闪烁效果
  12. 毕设题目:Matlab图像增强
  13. 模糊数学与matlab
  14. 树莓派RaspberryPi Zero W 快速安装tips
  15. 信息安全—WIFI攻击实验
  16. devise 自定义手机号登录
  17. docker一键部署springboot项目(三)
  18. uc保存html,UC浏览器如何保存网页?UC浏览器保存网页教程图文详解
  19. 30支队伍将在阿布扎比角逐奖金达500万美元的2020年穆罕默德-本-扎耶德国际机器人挑战赛
  20. 工业智能网关BL110应用之九十六: 实现西门子S7-1500 PLC接入华为云平台

热门文章

  1. 040_Unicode对照表六
  2. php中 可替代curl,laravel-PHP-为什么使用Guzzle代替cURL?
  3. 蓝湖怎么切图标注_【蓝湖指北】一张图教你如何选择标注尺寸
  4. 菜鸟自学数据结构系列——(一)如何写出能够在VC下运行的单链表生成程序
  5. Android应用开发:网络编程-2
  6. OCP12C题库,62数据库备份与恢复(admin,install and upgrade accelerated, backup and recovery workshop -62)(新增)
  7. 3264位Visio 2016怎么下载安装激活方法视频
  8. linux下串口通信程序,关于Linux下串口通信的一点心得
  9. sharedpreferences 重启不保存_MMKV为什么可以替换SharedPreferences
  10. android mvvm livedata_再谈Android应用架构——Jetpack VS 生命周期