众所周知,想要屏蔽Android 的 Back 按键,很简单,像下面这样操作就可以了:

    @Override    public boolean onKeyDown(int keyCode, KeyEvent event)  {        if (符合某特定条件) {            if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {                return true;            }        }        return super.onKeyDown(keyCode, event);    }

 

那如果想要屏蔽 Home 的话,就不这么简单了。Home 按键事件的响应与否,是在 framework 中做决定的。(回想一下,如果在自己的应用的 Activity 节点下配置过滤条件 ,那点击了系统底部NavigationBar 中的 Home 按键之后,系统底部栏会弹出一个选择框,让用户选择设置系统默认的 Launcher3 作为主界面还是选择自己的app作为主界面。)因此,想要屏蔽 Home 按键响应与否,需要在 framework 中做一些事情。

 

当前项目组中做了一个儿童模式的应用:设置儿童模式下只能使用10分钟,10分钟倒计时结束之后,用户只有解锁设备之后,才可以继续使用设备。因此当倒计时结束之后、用户未解锁之前,back 按键以及Home 按键都不可以被触发。怎么实现呢?

 

前置条件:有整套可以编译通过的 Android 源码;有设备可以刷机验证;Windows。

基于现有条件,分析寻找解决方案:

如果想屏蔽掉Home 按键,首先就得在 PhoneWindowManager.java 的 interceptKeyBeforeDispatching() 方法中 return,如下所示:

    @Override    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {        //code ......           // 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 (符合某种条件) {                Log.i(TAG, "Ignoring HOME; Because currently is in child mode.");                return -1;            }            //code ......               }              //code ......    }

所以这里主要就是给某种条件赋值了,可以在 System的全局变量 Global  中写入一个数值,在  PhoneWindowManager.java 中的这个方法处使用这个数值:当这个数值为1的时候,则直接return 不处理Home 按键;当这个变量值为0的时候,默认Home按键是可以被点击的。

那么问题来了,当前儿童模式应用不是系统应用,它是没办法直接修改 framework下 System 下的 Global  中的全局变量的,怎么处理呢, 当前想到的简单的可实现的方式就是使用广播:当符合某种条件的情况下,发送广播给到系统应用SystemUI,在SystemUI 中修改 framework下的 System 下的 Global  中的全局变量,这样是可以达到目的的。

基于以上分析,可以分为三个步骤来实现这个需求:

【第一步】在儿童模式中发送广播:

    private final static String ACTION_CAN_HOME_BUTTON_BE_CLICKED = "com.xxxxx.xxxxx.xxxxx.be.clicked";    private final static String HOME_BUTTON_CAN_BE_CLICKED = "com.xxxxx.xxxxx.xxxxx.canbe.clicked";sendBroadcastTellPhoneWindowManagerCanHomeButtonBeClicked(true or false);    /**     * By sending a broadcast delivery message, it works with PhoneWindowManager to determine if     * the Home button can be clicked in the current situation.     *     * @param canHomeBtnBeClicked  true : Android System Home button can be clicked.     *                             false : Android System Home button can not be clicked.     */    private void sendBroadcastTellPhoneWindowManagerCanHomeButtonBeClicked(boolean canHomeBtnBeClicked) {        if (canHomeBtnBeClicked){            Log.i(TAG,"Send Broadcast to make HomeButton can be clicked.");        }else {            Log.i(TAG,"Send Broadcast to make HomeButton can not be clicked.");        }        Intent i = new Intent();        i.setAction(ACTION_CAN_HOME_BUTTON_BE_CLICKED);        i.putExtra(HOME_BUTTON_CAN_BE_CLICKED, canHomeBtnBeClicked);        this.sendBroadcast(i);    }

【第二步】在 Framework 下定义参数,并根据参数数值决定处不处理Home按键事件

在 folder\frameworks\base\core\java\android\provider\Settings.java 的 class Global中添加如下:

         public static final String HOMEBTN_CANUSE = "homebtn_canuse";

在  folder\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java 中修改如下:

//添加PWM全局变量int mHomeKeypadShield;//在 SettingsObserver 注册    class SettingsObserver extends ContentObserver {        SettingsObserver(Handler handler) {            super(handler);        }        void observe() {            // Observe all users' changes            //code......            resolver.registerContentObserver(Settings.Global.getUriFor(                    Settings.Global.HOMEBTN_CANUSE), false, this,                    UserHandle.USER_ALL);            //end            updateSettings();        }    }//在 updateSettings() 中修改    public void updateSettings() {        //code...        synchronized (mLock) {            mHomeKeypadShield = Settings.Global.getInt(resolver,            Settings.Global.HOMEBTN_CANUSE, 0);        }        //code...     }//关键代码!!    @Override    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {        //code ......        if (keyCode == KeyEvent.KEYCODE_HOME) {            if (1 == mHomeKeypadShield) {                Log.i(TAG, "Ignoring HOME; Because currently is in child mode.");                return -1;            }            //code ......               }              //code ......    }

【第三步】在 SystemUI 中处理接受到的广播事件

修改 folder\vendor\rockchip\xxxxx\apps\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java 中:

 

    // ================================================================================    // Constructing the view    // ================================================================================    protected void makeStatusBarView() {        //code...        //Make the Home button available by default        Settings.Global.putInt(mContext.getContentResolver(), "homebtn_canuse", 0);        //code....                HomeButtonWhetherCanBeClickedBroadcast mHomeButtonBroadcast = new HomeButtonWhetherCanBeClickedBroadcast();        IntentFilter filter3 = new IntentFilter();        filter3.addAction("com.xxxxx.xxxxx.xxxxx.be.clicked");        context.registerReceiver(mHomeButtonBroadcast,filter3);    }    public class HomeButtonWhetherCanBeClickedBroadcast extends BroadcastReceiver{        @Override        public void onReceive(Context context, Intent intent) {             Log.i(TAG, "--------------Receive HomeButtonWhetherCanBeClickedBroadcast---------------");             if (intent != null) {                 boolean homeButtonCanUse = intent.getBooleanExtra("com.xxxxx.xxxxx.xxxxx.canbe.clicked",false);                 if (homeButtonCanUse) {                    Log.i(TAG, "home button can use.");                    Settings.Global.putInt(mContext.getContentResolver(), "homebtn_canuse", 0);                 }else{                    Log.i(TAG, "home button can not use.");                    Settings.Global.putInt(mContext.getContentResolver(), "homebtn_canuse", 1);                 }             }        }    }

 

这样就可以了,当前来说没发现什么问题。

但是这个地方,其实还有待优化的地方:

1.比如这个广播最好做成带有权限的广播,可以防止其他人在自己的应用中恶意发广播。

2.还可以在儿童模式中添加 ANR 情况时候的处理机制(如果在儿童模式到期之后,用户所打开的大型游戏导致了内存吃进,甚至 ANR,用户在这种情况下用户又无法手动解锁。那可以设置:当 ANR 一定的时间之后,重启机器)。

3.当前是使用广播机制实现的这个功能,但是这样的话,缺少对操作结果的检验过程,当然如果当修改结果成功之后再次发送一个广播出来给到对应的 app 也不是不可以,但是这就有点儿消耗性能:广播满天飞。既然涉及到跨进程通讯,是不是还可以使用 AIDL 的方式来实现对变量的修改呢?待验证。

当然肯定还有其他更好的方式,如果你有更好的处理方式,欢迎评论留言大家交流一下哇。

最后,这个功能的实现参考了其他人的博客,再次表示感谢。

参考:

https://blog.csdn.net/u012169524/article/details/51147518

https://blog.csdn.net/jlminghui/article/details/39268419

 

 

 

 

更多相关文章

  1. 【Android(安卓)非常基础】contentResolver.update where条件不
  2. 联想笔记本运行Android(安卓)Studio时无法打开模拟器问题
  3. android 手机虚拟按键 震动过程的追溯(1)
  4. Android之Input子系统与输入法
  5. [置顶] android用户输入系统详细说明
  6. android OS系统如何适配蓝牙遥控器
  7. Android 指纹识别(给应用添加指纹解锁)
  8. Android TabActivity 按键响应

随机推荐

  1. Android 蓝牙设备的查找与连接
  2. Android中SQLite数据库操作(2)——使用SQLi
  3. android fitSystemWindow属性
  4. Android studio 学习1:实现点击事件的4种
  5. android中禁止横竖屏切换
  6. Android(安卓)2.2 API Demos -- setWallp
  7. Android中五大Manager详解及使用技巧
  8. Android(安卓)XmlResourceParser解析Xm文
  9. 解决方法:android 6.0(api 23) SDK,不再提
  10. Android手机开机动画的修改