Android关于Activity屏蔽/拦截Home键
2018-02-03 18:17
691 查看
写在前面:
这篇文章并没有提供屏蔽Home键的方法,仅仅是阐释一些原理,引发一些思考。
1.奇淫技巧的源泉:PhoneWindowManager#interceptKeyBeforeDispatching
拦截home键的思想大致由此发源。Input事件分发时,Service端就会过滤了一些事件,而Home的过滤,就在此方法。方法注释说明了,如果app是keyguard类型,则将home键事件派发给app。源码(API 19)如下:
2.传说中的在onAttachedToWindow中调用getWindow().setType设置Window Type
方式如下:
本人基于4.4(API 19)及以上系统版本进行测试,都报异常:
所以,这种方式并不可行。
并且,如果你的compileSdkVersion >= 21,TYPE_KEYGUARD这种Window类型都没有了……
3.思考IllegalArgumentException: Window type can not be changed after the window is added
报完错之后有种想法,既然在onAttachedToWindow方法中设置type不行,那在onCreate中或者onResume中,这两个生命周期方法调用时期早于onAttachedToWindow,可以一试。
然而发现并不行。在onKeyDown中打个断点,发现Window的type是TYPE_BASE_APPLICATION,也就是说,之前虽然该了type,但是没有生效,或者说,在我更改了type之后,系统又进行了更改,于是进入了下一步,撸源码。
4.何处设置了TYPE_BASE_APPLICATION
Activity Window从产生到WindowMnager#addView被调用,详细过程请自行查找,网上有较多的阐释。
我需要了解的核心大致如下:
在Activity#attach方法中产生PhoneWindow。
在ActivityThread#handleResumeActivity将DecorView添加到WindowManager。源码(API 19)如下:
好吧,找到了原因:add前,android“帮你”重设了type。
5.另有蹊径:WindowManager.LayoutParams privateFlags ???
撸Android 5.1(API 21)源码,发现它的PhoneWindowManager#interceptKeyBeforeDispatching方法home键事件处理逻辑与Android 4.4(API 19)有些许不同,如下:
于是乎想,能不能在这个privateFlags上做文章。
WindowManager.LayoutParams中,privateFlags和PRIVATE_FLAG_KEYGUARD虽然是public的,但是是@hide注解的,所以访问不到,需要通过反射取值和修改,代码如下:
在Activity onCreate中调用这个方法。好吧,还是无效。但奇怪的是,我在onKeyDown中打断点,发现privateFlags确实等于PRIVATE_FLAG_KEYGUARD。然后就想不通了,如果博友们有通了的,麻烦回复下,感谢!
至此,拦截home宣告失败,但明晰了一些原理,还是颇有收获的。
敢于怀疑,勇于探索。
这篇文章并没有提供屏蔽Home键的方法,仅仅是阐释一些原理,引发一些思考。
1.奇淫技巧的源泉:PhoneWindowManager#interceptKeyBeforeDispatching
拦截home键的思想大致由此发源。Input事件分发时,Service端就会过滤了一些事件,而Home的过滤,就在此方法。方法注释说明了,如果app是keyguard类型,则将home键事件派发给app。源码(API 19)如下:
if (keyCode == KeyEvent.KEYCODE_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_SCRIM || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { // the "app" is keyguard, so give it the key return 0; } } ... }
2.传说中的在onAttachedToWindow中调用getWindow().setType设置Window Type
方式如下:
@Override public void onAttachedToWindow() { getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD); super.onAttachedToWindow(); }
本人基于4.4(API 19)及以上系统版本进行测试,都报异常:
java.lang.IllegalArgumentException: Window type can not be changed after the window is added.
所以,这种方式并不可行。
并且,如果你的compileSdkVersion >= 21,TYPE_KEYGUARD这种Window类型都没有了……
3.思考IllegalArgumentException: Window type can not be changed after the window is added
报完错之后有种想法,既然在onAttachedToWindow方法中设置type不行,那在onCreate中或者onResume中,这两个生命周期方法调用时期早于onAttachedToWindow,可以一试。
然而发现并不行。在onKeyDown中打个断点,发现Window的type是TYPE_BASE_APPLICATION,也就是说,之前虽然该了type,但是没有生效,或者说,在我更改了type之后,系统又进行了更改,于是进入了下一步,撸源码。
4.何处设置了TYPE_BASE_APPLICATION
Activity Window从产生到WindowMnager#addView被调用,详细过程请自行查找,网上有较多的阐释。
我需要了解的核心大致如下:
在Activity#attach方法中产生PhoneWindow。
在ActivityThread#handleResumeActivity将DecorView添加到WindowManager。源码(API 19)如下:
if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. }
好吧,找到了原因:add前,android“帮你”重设了type。
5.另有蹊径:WindowManager.LayoutParams privateFlags ???
撸Android 5.1(API 21)源码,发现它的PhoneWindowManager#interceptKeyBeforeDispatching方法home键事件处理逻辑与Android 4.4(API 19)有些许不同,如下:
if (keyCode == KeyEvent.KEYCODE_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_SCRIM || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { // the "app" is keyguard, so give it the key return 0; } } ... }
于是乎想,能不能在这个privateFlags上做文章。
WindowManager.LayoutParams中,privateFlags和PRIVATE_FLAG_KEYGUARD虽然是public的,但是是@hide注解的,所以访问不到,需要通过反射取值和修改,代码如下:
public static void changeWindowPrivateFlags(Window window) { WindowManager.LayoutParams attrObj = window.getAttributes(); Class<?> attrCls = attrObj.getClass(); try { //拿PRIVATE_FLAG_KEYGUARD的值 Field privateFlagConstField = attrCls.getField("PRIVATE_FLAG_KEYGUARD"); privateFlagConstField.setAccessible(true); int privateFlagConst = privateFlagConstField.getInt(attrCls); //设置privateFlags为PRIVATE_FLAG_KEYGUARD Field field = attrCls.getField("privateFlags"); field.setAccessible(true); field.setInt(attrObj, privateFlagConst); } catch (Exception e) { e.printStackTrace(); } }
在Activity onCreate中调用这个方法。好吧,还是无效。但奇怪的是,我在onKeyDown中打断点,发现privateFlags确实等于PRIVATE_FLAG_KEYGUARD。然后就想不通了,如果博友们有通了的,麻烦回复下,感谢!
至此,拦截home宣告失败,但明晰了一些原理,还是颇有收获的。
敢于怀疑,勇于探索。
相关文章推荐
- 如何在Android App中屏蔽(拦截)Home按键及其他按键
- Android系统onKeyDown监控/拦截/监听/屏蔽返回键、菜单键和Home键
- Android系统onKeyDown监控/拦截/监听/屏蔽返回键、菜单键和Home键
- Android4.0以后屏蔽以及HOME按键事件拦截
- Android App中屏蔽(拦截)Home按键及其他按键
- 如何在Android App中屏蔽(拦截)Home按键及其他按键
- 如何在Android App中屏蔽(拦截)Home按键及其他按键
- android4.1 activity内屏蔽HOME按键功能
- 关于Android创建Activity需要注意的地方
- 举例说明关于android编程中遇到的Unable to find explicit activity class错误的原因及解决办法
- 探究Android 关于Activity调用finish()方法后的内存释放情况
- Android中关于Activity的生命周期
- 关于android Activity中注解的使用,省去无用的findviewbyid....setonclick.....
- Android关于startActivityForResult的一切
- 关于Android activity生命周期
- 关于android初学者必须掌握的Activity的四大知识点
- Android Activity 禁止或屏蔽横竖屏切换
- android关于onActivityResult提前调用的问题
- 关于Android隐式启动Activity
- Android开发:Activity初始化时屏蔽EditText的自动获取焦点的事件