Android如何实现锁屏状态下弹窗
前言
想在锁屏上面实现弹窗,第一个想法就是利用WindowManager设置Window的Flag,通过设置Flag的显示优先级来让窗口显示在锁屏的上面。
接下来就是试验可能相关的WindowType属性,验证该方案是否可行。
在尝试各个WindowType属性之前需要明确各个Type所需要的权限,下面是com.android.internal.policy.impl.PhoneWindowManager.checkAddPermission的源码:
publicintcheckAddPermission(WindowManager.LayoutParamsattrs){
inttype=attrs.type;
if(type<WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
||type>WindowManager.LayoutParams.LAST_SYSTEM_WINDOW){
returnWindowManagerImpl.ADD_OKAY;
}
Stringpermission=null;
switch(type){
caseTYPE_TOAST:
//XXXrightnowtheappprocesshascompletecontrolover
//this...shouldintroduceatokentoletthesystem
//monitor/controlwhattheyaredoing.
break;
caseTYPE_INPUT_METHOD:
caseTYPE_WALLPAPER:
//Thewindowmanagerwillcheckthese.
break;
caseTYPE_PHONE:
caseTYPE_PRIORITY_PHONE:
caseTYPE_SYSTEM_ALERT:
caseTYPE_SYSTEM_ERROR:
caseTYPE_SYSTEM_OVERLAY:
permission=android.Manifest.permission.SYSTEM_ALERT_WINDOW;
break;
default:
permission=android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
}
if(permission!=null){
if(mContext.checkCallingOrSelfPermission(permission)
!=PackageManager.PERMISSION_GRANTED){
returnWindowManagerImpl.ADD_PERMISSION_DENIED;
}
}
returnWindowManagerImpl.ADD_OKAY;
}
明显不适合的Type:TYPE_TOAST,TYPE_INPUT_METHOD,TYPE_WALLPAPER;可能适合的Type:TYPE_PHONE,TYPE_PRIORITY_PHONE,TYPE_SYSTEM_ALERT,TYPE_SYSTEM_ERROR,TYPE_SYSTEM_OVERLAY;其它类型的Type:
需要系统签名权限:
android.Manifest.permission.INTERNAL_SYSTEM_WINDOW
而申请该权限需要系统签名,所以我们是无法获取权限的。
TYPE_PHONE
/** *Windowtype:phone.Thesearenon-applicationwindowsproviding *userinteractionwiththephone(inparticularincomingcalls). *Thesewindowsarenormallyplacedaboveallapplications,butbehind *thestatusbar. *Inmultiusersystemsshowsonallusers'windows. */ publicstaticfinalintTYPE_PHONE=FIRST_SYSTEM_WINDOW+2;
TYPE_PHONE类型的窗口可以显示在其它APP的上面,但不能显示在锁屏的上面,所以PASS。
TYPE_PRIORITY_PHONE
/** *Windowtype:priorityphoneUI,whichneedstobedisplayedevenif *thekeyguardisactive.Thesewindowsmustnottakeinput *focus,ortheywillinterferewiththekeyguard. *Inmultiusersystemsshowsonallusers'windows. */ publicstaticfinalintTYPE_PRIORITY_PHONE=FIRST_SYSTEM_WINDOW+7;
TYPE_PRIORITY_PHONE类型的窗口可以显示在其它APP的上面,但不能显示在锁屏的上面,所以PASS。而且实际的行为和注释并不相符,该类型的窗口是可以获取交互事件的,具体原因待查。
TYPE_SYSTEM_ALERT
/** *Windowtype:systemwindow,suchaslowpoweralert.Thesewindows *arealwaysontopofapplicationwindows. *Inmultiusersystemsshowsonlyontheowninguser'swindow. */ publicstaticfinalintTYPE_SYSTEM_ALERT=FIRST_SYSTEM_WINDOW+3;
TYPE_SYSTEM_ALERT类型的窗口可以显示在其它APP的上面,但不能显示在锁屏的上面,所以PASS。
TYPE_SYSTEM_OVERLAY
/** *Windowtype:systemoverlaywindows,whichneedtobedisplayed *ontopofeverythingelse.Thesewindowsmustnottakeinput *focus,ortheywillinterferewiththekeyguard. *Inmultiusersystemsshowsonlyontheowninguser'swindow. */ publicstaticfinalintTYPE_SYSTEM_OVERLAY=FIRST_SYSTEM_WINDOW+6;
TYPE_SYSTEM_OVERLAY类型的窗口可以显示在所有其它窗口的上面,包括锁屏,而且不会影响它下面窗口的交互事件响应,但是该属性窗口不能获得焦点,无法进行交互(如果该窗口可以获取焦点,那么就可以用来抓取用户的锁屏密码,出于安全考虑,系统是不会允许的),所以只能用来简单的展示内容,如果需要交互的锁屏弹窗,那么该属性PASS。
TYPE_SYSTEM_ERROR
/** *Windowtype:internalsystemerrorwindows,appearontopof *everythingtheycan. *Inmultiusersystemsshowsonlyontheowninguser'swindow. */ publicstaticfinalintTYPE_SYSTEM_ERROR=FIRST_SYSTEM_WINDOW+10;
在原生ROM5.1下试验是可以显示出来的,但根据注释来看(appearontopofeverythingtheycan)不是在所有情况下都可以显示在锁屏上面的,而且像MIUI和Flyme等ROM默认是屏蔽浮窗权限的,考虑到这点,利用WindowManager添加浮窗的方式实现锁屏弹窗的方案基本PASS。
使用Activity的方式实现
首先需要对Activity进行如下设置
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
finalWindowwin=getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
其中最主要也是必须要设置的就是:FLAG_SHOW_WHEN_LOCKED,顾名思义就是锁屏下显示该Activity。而其它几个Flag包括:解锁、保持屏幕常亮、点亮屏幕可以根据具体的需求选择设置。
在AndroidManifest.xml中声明Activity
同样该Activity也需要在AndroidManifest.xml中声明,声明时需注意添加android:excludeFromRecents="true"属性,是为了将该Activity从最近任务列表中去除,否则用户会觉得很奇怪。还有因为这个Activity会整个盖在锁屏上面,而且就算设置成背景透明,锁屏界面也不会显示在下面(系统主要是出于安全考虑),所以需要考虑下该Activity的背景,这里为了显示不要太突兀将主题设为壁纸。
<activityandroid:name=".LockScreenActivity" android:launchMode="singleInstance" android:excludeFromRecents="true" android:theme="@android:style/Theme.Wallpaper.NoTitleBar"/>
启动Activity
由于该Activity是为了在锁屏的情况下显示的,所以启动Activity时不要忘了判断手机是否处于锁屏状态,可以通过下面这种方式判断锁屏状态:
KeyguardManagerkm=(KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
if(km.inKeyguardRestrictedInputMode()){
//处于锁屏状态
}
总结
以上就是在Android中实现锁屏状态下弹窗效果的全部内容,希望本文的内容对大家开发Android的时候能有所帮助,如果有疑问欢迎大家留言讨论。