黑马程序员技术交流社区

标题: Android怎么针对系统源码,屏蔽系统动作 [打印本页]

作者: 夜正黑    时间: 2016-2-19 19:22
标题: Android怎么针对系统源码,屏蔽系统动作
安卓,我之前做的锁屏,屏蔽系统home键,一直不成功,网上很多方法都不成功,现在我想到了一个方法,就是针对系统代码,屏蔽系统的代码,但是不知道怎么实现,有人做过类似的,屏蔽系统的动作,有的给个提示

作者: 风雨中前行88    时间: 2016-2-21 01:12
Java代码  收藏代码
public void onAttachedToWindow()  
{   
       this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);      
       super.onAttachedToWindow();   
}   
但是这个方法到了4.0的系统上就失效了,按home照样会返回桌面。apktool了几个锁屏的软件之后发现其实现方法,即用WindowManager的addview方法将view加到窗口上,加上的时候将view的layoutparamas的type设为LayoutParams.TYPE_SYSTEM_ERROR,这个值sdk的解释为



Java代码  收藏代码
public static final int TYPE_SYSTEM_ERROR   
Since: API Level 1   
Window type: internal system error windows, appear on top of everything they can.   
如果直接这样用会报错,还要在AndroidManifest.xml里面加上权限<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>,ok上完整代码,封装了一个类,其中Lock和hide方法实现锁屏和解锁。

Java代码  收藏代码
public class LockLayer {  
    private Activity mActivty;  
    private WindowManager mWindowManager;  
    private View mLockView;  
    private LayoutParams mLockViewLayoutParams;  
      
    public LockLayer(Activity act) {  
        mActivty = act;  
        init();  
    }  
  
    private void init(){  
        mWindowManager = mActivty.getWindowManager();  
        mLockViewLayoutParams = new LayoutParams();  
        mLockViewLayoutParams.width = LayoutParams.MATCH_PARENT;  
        mLockViewLayoutParams.height = LayoutParams.MATCH_PARENT;  
        //实现关键  
        mLockViewLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;  
        //apktool value,这个值具体是哪个变量还请网友帮忙  
        mLockViewLayoutParams.flags = 1280;  
    }  
    public void lock() {  
        if(mLockView!=null){  
            mWindowManager.addView(mLockView, mLockViewLayoutParams);  
        }  
    }  
    public void unlock() {  
        if(mWindowManager!=null){  
            mWindowManager.removeView(mLockView);  
        }  
    }  
    public void setLockView(View v){  
        mLockView = v;  
    }  
}  
使用方法如下

Java代码  收藏代码
public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        View lock = View.inflate(this, R.layout.main, null);  
        LockLayer lockLayer = new LockLayer(this);  
        lockLayer.setLockView(lock);  
        lockLayer.lock();  
    }  


ok,这时可以实现按home键不返回桌面的效果,其他的键需要用另外的代码实现,网上都有。这段代码只调用一次的时候没有问题,但是如果遇到多线程或者多次lock和hide就会有问题,修改如下,一是将LockLayer改为单例模式,二是在hide和lock的时候先判断状态。修改后代码如下

Java代码  收藏代码
public class LockLayer {  
    private Activity mActivty;  
    private WindowManager mWindowManager;  
    private View mLockView;  
    private LayoutParams mLockViewLayoutParams;  
    private static LockLayer mLockLayer;  
    private boolean isLocked;  
      
    public static synchronized LockLayer getInstance(Activity act){  
        if(mLockLayer == null){  
            mLockLayer = new LockLayer(act);  
        }  
        return mLockLayer;  
    }  
      
    private LockLayer(Activity act) {  
        mActivty = act;  
        init();  
    }  
  
    private void init(){  
        isLocked = false;  
        mWindowManager = mActivty.getWindowManager();  
        mLockViewLayoutParams = new LayoutParams();  
        mLockViewLayoutParams.width = LayoutParams.MATCH_PARENT;  
        mLockViewLayoutParams.height = LayoutParams.MATCH_PARENT;  
        //实现关键  
        mLockViewLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;  
        //apktool value,这个值具体是哪个变量还请网友帮忙  
        mLockViewLayoutParams.flags = 1280;  
    }  
    public synchronized void lock() {  
        if(mLockView!=null&&!isLocked){  
            mWindowManager.addView(mLockView, mLockViewLayoutParams);  
        }  
        isLocked = true;  
    }  
    public synchronized void unlock() {  
        if(mWindowManager!=null&&isLocked){  
            mWindowManager.removeView(mLockView);  
        }  
        isLocked = false;  
    }  
    public synchronized void setLockView(View v){  
        mLockView = v;  
    }  
}  

作者: 风雨中前行88    时间: 2016-2-21 01:13
  好了,言归正传,说说锁屏了,其实把锁屏做成apk的形式,会引起很多问题的,导致不必要的麻烦,就像市场上的流行的Go锁屏和91锁屏也是会有一些问题的,只是影响不大,最好的做法是修改源码,在源码中定制自己的锁屏,这样一劳永逸;
首先做锁屏考虑的事有以下四点;
        (1)替换掉系统的锁屏,不能让系统的锁屏显示出来;
        (2)屏蔽掉Home键盘,back键,menu键;
        (3)在其他界面或Launcher界面,长按home键,不能让自己的锁屏apk显示在最近任务中;
        (4)每次开机显示自己的锁屏

        针对这四点一一做解答,不一定是完整的代码,只是提供以下思路,有兴趣的同学可以提出疑问;
         针对(1),这个比较容易,就调用disableKeyguard掉系统的就可以了;
[java] view plain copy print?
mKeyguardManager = (KeyguardManager)Class.this.getSystemService(Context.KEYGUARD_SERVICE);  
        mKeyguardLock = mKeyguardManager.newKeyguardLock("my_lockscreen");   
        mKeyguardLock.disableKeyguard();  
注意:在Manifext.xml中加入权限:
           <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>

         针对(2),这个Home键的处理,我看网上有同学写博客了,有同学这么处理:
[java] view plain copy print?
public class LockLayer {     
    private Activity mActivty;     
    private WindowManager mWindowManager;     
    private View mLockView;     
    private LayoutParams mLockViewLayoutParams;     
    private static LockLayer mLockLayer;     
    private boolean isLocked;     
        
    public static synchronized LockLayer getInstance(Activity act){     
        if(mLockLayer == null){     
            mLockLayer = new LockLayer(act);     
        }     
        return mLockLayer;     
    }     
        
    private LockLayer(Activity act) {     
        mActivty = act;     
        init();     
    }     
   
    private void init(){     
        isLocked = false;     
        mWindowManager = mActivty.getWindowManager();     
        mLockViewLayoutParams = new LayoutParams();     
        mLockViewLayoutParams.width = LayoutParams.MATCH_PARENT;     
        mLockViewLayoutParams.height = LayoutParams.MATCH_PARENT;     
        //实现关键     
        mLockViewLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;     
        //apktool value,这个值具体是哪个变量还请网友帮忙     
        mLockViewLayoutParams.flags = 1280;     
    }     
    public synchronized void lock() {     
        if(mLockView!=null&&!isLocked){     
            mWindowManager.addView(mLockView, mLockViewLayoutParams);     
        }     
        isLocked = true;     
    }     
    public synchronized void unlock() {     
        if(mWindowManager!=null&&isLocked){     
            mWindowManager.removeView(mLockView);     
        }     
        isLocked = false;     
    }     
    public synchronized void setLockView(View v){     
        mLockView = v;     
    }     
}   
   原理:是把这个view当成系统的错误的view,
这个在PhoneWindowManager.java中的interceptKeyBeforeDispatching()方法中也有根据,请看真相:
[java] view plain copy print?
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;  
                    }  
                }  

看注释:如果设置了这两个属性的其中一个,就不做任何处理,home键不对这个app生效;
而WINDOW_TYPES_WHERE_HOME_DOESNT_WORK数据的定义如下:
[java] view plain copy print?
private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {  
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,  
            WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,  
        };  

这下你就明白为什么上述代码设置为TYPE_SYSTEM_ERROR,其实设置为TYPE_SYSTEM_ALERT也可以的。
再来看看sdk文档有解释说明,
Window type: internal system error windows, appear on top of everything they can. In multiuser systems shows only on the owning user's window.
这个error的view在所有的view的最上面,所以就可以达到屏蔽home键的效果了;
问题来了:在这个界面长按power键,关机界面也弹不出来了,这个我试验过,所以这个方法不可取;
       还有同学说这么做:
private static final int FLAG_HOMEKEY_DISPATCHED = 0x80000000;
[java] view plain copy print?
在app 的你要屏蔽home 键的activity 中,只需要添加该标志就可以了  
  
this.getWindow().setFlags(FLAG_HOMEKEY_DISPATCHED, FLAG_HOMEKEY_DISPATCHED);  
  
如果要使home 键有效,再 clearFlags 即可。  
  
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_HOMEKEY_DISPATCHED);  
注意:以上代码要加在setContentView()之前才行;
这个代码来源于PhoneWindowManager.java这个类的interceptKeyBeforeDispatching()方法中,
[html] view plain copy print?
if ((flag & WindowManager.LayoutParams.FLAG_HOMEKEY_DISPATCHED) != 0) {  
                   // the window wants to handle the home key, so dispatch it to it.  
                   return 0;  
               }      
这样设置,有的同学感觉好用,有的同学感觉不好用;
原因:android4.0的源码的PhoneWindowManager.java没有添加上述代码,所以不起作用,
而4.1的源码PhoneWindowManager.java中有这个代码,所以好用,这个请大家看看对应的源码是否有上述代码;

         针对(3),这个就模仿Go锁屏和91锁屏,在启动锁屏的activity中配置
[java] view plain copy print?
<category android:name="android.intent.category.HOME" />  
这个属性,然后先清除系统启动的默认设置,就是选择系统点击Home键启动哪个home,设置为启动自己的锁屏,然后再设置解锁后启动那个桌面,这个自己处理下逻辑就可以了,大致思路就是这个样子的;

             针对(4),这个Go锁屏还有91锁屏处理的都不太理想,回头我想到好的思路再更新,大致思路是:监听开机广播, <action android:name="android.intent.action.BOOT_COMPLETED"/>,这个写一个MYBootCompletedReceiver类,作用是disable掉系统的锁屏,然后启动自己的锁屏的服务,监听屏幕亮和灭的广播,这个屏幕亮和灭的广播要动态监听;大家有好的思路或见解可以留言讨论?
作者: 赵雄    时间: 2016-2-25 12:11
好多啊,看都没看懂,求黑马币




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2