Window Type的优先级和权限(用于悬浮窗或窗口实现界面)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wanglikun7342/article/details/68068078

Type

  1. TYPE_PHONE:电话窗口,这是非应用窗口,用于来电的界面,该窗口通常置于所有应用之上,但在状态栏下。
  2. TYPE_SYSTEM_ALERT:系统窗口,例如低电量警告弹窗,在应用窗口之上。
  3. TYPE_TOAST:透明通知,不会拦截触摸事件,可以向下透传。

优先级

TYPE_SYSTEM_ALERT > TYPE_PHONE > TYPE_TOAST

  • Android版本19以下TYPE_TOAST不能响应点击事件,不能获取焦点

  • TYPE_PHONE权限拉下通知栏不可见,权限比TYPE_SYSTEM_ALERT

  • TYPE_PHONE和TYPE_SYSTEM_ALERT需要权限<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>,且在23以上版本增加了动态权限管理特性,DrawOverOtherApps选项下可以动态修改悬浮窗权限。

系统源码

  • Windows Type的相关逻辑都在PhoneWindowManager
@Override
public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) {
    switch (attrs.type) {
        default:
        break;
        ...
        // These are the windows that by default are shown to all users. However, to
        // protect against spoofing, check permissions below.
        case TYPE_PHONE:
        ...
        break;
    // Check if third party app has set window to system window type.
    return mContext.checkCallingOrSelfPermission(
        android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)
        != PackageManager.PERMISSION_GRANTED;
}
  • 可以看到TYPE_PHONE是展示给所有用户的,因此需要检测权限。
  • TYPE_SYSTEM_ALERT只展示给持有用户。
@Override
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
    int type = attrs.type;
    switch (type) {
        case TYPE_TOAST:
            // XXX right now the app process has complete control over
            // this...  should introduce a token to let the system
            // monitor/control what they are doing.
            outAppOp[0] = AppOpsManager.OP_TOAST_WINDOW;
            break;
            ...
        case TYPE_PHONE:
        case TYPE_SYSTEM_ALERT:
        case TYPE_SYSTEM_ERROR:
        case TYPE_SYSTEM_OVERLAY:
            ...
            permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW;
            outAppOp[0] = AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
            break;
        default:
            permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
    }
    if (permission != null) {
        if (mContext.checkCallingOrSelfPermission(permission)
                != PackageManager.PERMISSION_GRANTED) {
            return WindowManagerGlobal.ADD_PERMISSION_DENIED;
        }
    }
    return WindowManagerGlobal.ADD_OKAY;
}
  • TYPE_TOAST不进行权限验证,直接返回WindowManagerGlobal.ADD_OKAYTYPE_PHONETYPE_SYSTEM_ALERT需要验证android.Manifest.permission.SYSTEM_ALERT_WINDOW权限。
  • 另外,其他Window Type需要申请android.Manifest.permission.INTERNAL_SYSTEM_WINDOW权限,该权限只有系统应用才能获取,所以不能用于悬浮窗使用。

Android 19以下版本Toast无法获取焦点和触发触摸事件

//Android 2.0 - 2.3.7 PhoneWindowManager
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
    switch (attrs.type) {
        case TYPE_SYSTEM_OVERLAY:
        case TYPE_SECURE_SYSTEM_OVERLAY:
        case TYPE_TOAST:
            // These types of windows can't receive input events.
            attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
            break;
    }
}
//Android 4.0.1 - 4.3.1 PhoneWindowManager
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
    switch (attrs.type) {
        case TYPE_SYSTEM_OVERLAY:
        case TYPE_SECURE_SYSTEM_OVERLAY:
        case TYPE_TOAST:
            // These types of windows can't receive input events.
            attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
            attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
            break;
    }
}
//Android 4.4 PhoneWindowManager
@Override
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
    switch (attrs.type) {
        case TYPE_SYSTEM_OVERLAY:
        case TYPE_SECURE_SYSTEM_OVERLAY:
            // These types of windows can't receive input events.
            attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
            attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
            break;
    }
}
  • 在4.0.1以前,当使用TYPE_TOAST时,Android会给我们加上FLAG_NOT_FOCUSABLE和FLAG_NOT_TOUCHABLE。
  • 从4.0.1开始,会额外再去掉FLAG_WATCH_OUTSIDE_TOUCH,这样真的是什么事件都没了。
  • 从版本19开始,Case中的TYPE_TOAST被移除了。

**所以从4.4开始, 使用TYPE_TOAST的同时还可以接收触摸事件和按键事件了, 而4.4以前只能显示出来, 不能交互.
**API level 18及以下使用TYPE_TOAST无法接收触摸事件的原因也找到了。

悬浮窗权限适配(多版本多机型)

展开阅读全文

没有更多推荐了,返回首页