Android:无法添加窗口。此窗口类型的权限被拒绝


85

我工作的一个应用程序,我需要显示一些信息的窗口ON未解锁手机的锁屏(键盘保护)。我想我可以使用WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG做到这一点

但是,每次我的应用崩溃时,都会出现以下错误:

android.view.WindowManager $ BadTokenException:无法添加窗口android.view.ViewRootImpl$W@40ec8528-此窗口类型的权限被拒绝

这些帖子(此处此处此处)都给出相同的答案。在清单文件中添加以下权限。

android.permission.SYSTEM_ALERT_WINDOW

我已实施的解决方案,但仍然遇到相同的错误。有什么想法我做错了吗?

这是清单文件中的权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.droidpilot.keyguardwindow" >

<uses-sdk
    android:minSdkVersion="16"
    android:targetSdkVersion="21" />

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />

这是我用来将Window添加到锁定屏幕的代码

WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    mView = mInflater.inflate(R.layout.lock_screen_notif, null);

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
            PixelFormat.TRANSLUCENT
    );

    wm.addView(mView, params);

有人知道吗?

PS我正在运行Android 4.4.2的HTC Desire 620 DS上进行测试

Answers:


30

出于完全显而易见的原因,不允许普通的Apps在锁定屏幕顶部创建任意窗口。如果我在您的锁屏上创建了一个窗口,可以完美地模仿真实的锁屏,以至于您看不出区别,您认为我该怎么办?

错误的技术原因是使用TYPE_KEYGUARD_DIALOG标志-它要求android.permission.INTERNAL_SYSTEM_WINDOW这是签名级权限。这意味着只有使用与权限创建者相同的证书签名的Apps才能使用它。

创建者android.permission.INTERNAL_SYSTEM_WINDOW是Android系统本身,因此,除非您的应用是操作系统的一部分,否则您就没有机会。

有完善的定义和有据可查的从锁屏通知用户信息的方式。您可以创建在锁定屏幕上显示的自定义通知,用户可以与它们进行交互。


2
我个人在寻找类似Facebook应用程序的锁定屏幕通知之类的东西。我已经知道了新的锁屏通知,但目前仅在Android 22中有效。我需要一个可以在Android 16及更高版本上运行的解决方案。但是您的回答很合理,我将这样接受。我确实设法在锁定屏幕上显示了一个窗口,但不是我所需要的,我将在下面发布找到的解决方案。
DroidPilot 2015年

1
如果应用程序需要该权限,则可以在安装和打开应用程序后授予该权限。
SkorpEN

我认为,自奥利奥以来,这是正确的答案stackoverflow.com/a/48481628/504179
ATom

107

如果您使用apiLevel >= 19,请不要使用

WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT 

出现以下错误:

android.view.WindowManager $ BadTokenException:无法添加窗口android.view.ViewRootImpl$W@40ec8528-此窗口类型的权限被拒绝

使用此代替:

LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL

3
我已经在清单中的Granted的TYPE_SYSTEM_ALERTAndroid 5.1上正常工作android.permission.SYSTEM_ALERT_WINDOW
山姆

我必须使用android.permission.SYSTEM_ALERT_WINDOWLayoutParams.TYPE_TOAST
t0m

1
我尝试了LayoutParams.TYPE_TOAST。它对我的Lollipop和Marshmallow都是API级别6.0.1。在此之前,我使用的是WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,它仅适用于Lollipop。
Prashant

不幸的是,如果某些应用程序请求权限对话框(如果您的视图处于活动状态),它会导致“检测到屏幕重叠”
Siarhei17年

如果您尝试从服务或广播接收器中显示窗口,则此方法将无效
FindOutIslamNow

78

我猜你应该区分目标(奥利奥之前和之后)

int LAYOUT_FLAG;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}

params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    LAYOUT_FLAG,
    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT);

我正在尝试使用类似的技术,但是android studio无法识别Build.VERSION_CODES.OWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY。我的目标是20
Gabe O'Leary

从我这边感谢您的帮助+1。
Saveen

@ GabeO'Leary我面临着同样的问题,您对此有解决方案吗?
萨加尔(Sagar)

49

我尽力尝试了所有可用于此问题的示例。最终我得到了答案,我不知道它的可靠性如何,但是我的应用程序现在没有崩溃。

windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
    //here is all the science of params
    final LayoutParams myParams = new LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            LayoutParams.TYPE_SYSTEM_ERROR,
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
            PixelFormat.TRANSLUCENT
    );

在清单文件中,只授予权限

 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

此外,如果其> = 23,您还可以检查API级别,然后

 if(Build.VERSION.SDK_INT >= 23) {
    if (!Settings.canDrawOverlays(Activity.this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, 1234);
    }
}
            else
{
    Intent intent = new Intent(Activity.this, Service.class);
    startService(intent);
}

希望对某人有所帮助。完整示例 https://anam-android-codes.blogspot.in/?m=1


13

对于8.0.0的Android API级别,您应该使用

WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY

代替

LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL

SYSTEM_ALERT


TYPE_APPLICATION_OVERLAY不在Android Studio中提供的选项列表中。我得到“无法解析符号类型”。我想念什么?
philcruz

11

试试这个代码完美地工作

int layout_parms;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 

    {  
         layout_parms = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

    }

     else {

            layout_parms = WindowManager.LayoutParams.TYPE_PHONE;

    }

    yourparams = new WindowManager.LayoutParams(       
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            layout_parms,
            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT);

您救了我的命
佛卡

@BakaWaii,您想在其中显示小部件以将该代码放入其中的方法可以正常工作
shakirullah orakzai

6

搜索

绘制其他应用程序

在您的设置中并启用您的应用。对于Android 8 Oreo,请尝试

设置>应用和通知>应用信息>在其他应用上显示>启用


1
您是救生员
Noor Hossain

5

首先,请确保您在清单文件中具有添加权限。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

检查该应用程序是否具有覆盖其他应用程序的权限?默认情况下,此权限可用于API <23。但是对于API> 23,您必须在运行时请求权限。

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {

    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
            Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, 1);
} 

使用此代码:

public class ChatHeadService extends Service {

private WindowManager mWindowManager;
private View mChatHeadView;

WindowManager.LayoutParams params;

public ChatHeadService() {
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();

    Language language = new Language();
    //Inflate the chat head layout we created
    mChatHeadView = LayoutInflater.from(this).inflate(R.layout.dialog_incoming_call, null);


    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
                PixelFormat.TRANSLUCENT);

        params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
        params.x = 0;
        params.y = 100;
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mWindowManager.addView(mChatHeadView, params);

    } else {
        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
                PixelFormat.TRANSLUCENT);


        params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
        params.x = 0;
        params.y = 100;
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mWindowManager.addView(mChatHeadView, params);
    }

    TextView tvTitle=mChatHeadView.findViewById(R.id.tvTitle);
    tvTitle.setText("Incoming Call");

    //Set the close button.
    Button btnReject = (Button) mChatHeadView.findViewById(R.id.btnReject);
    btnReject.setText(language.getText(R.string.reject));
    btnReject.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //close the service and remove the chat head from the window
            stopSelf();
        }
    });

    //Drag and move chat head using user's touch action.
    final Button btnAccept = (Button) mChatHeadView.findViewById(R.id.btnAccept);
    btnAccept.setText(language.getText(R.string.accept));


    LinearLayout linearLayoutMain=mChatHeadView.findViewById(R.id.linearLayoutMain);



    linearLayoutMain.setOnTouchListener(new View.OnTouchListener() {
        private int lastAction;
        private int initialX;
        private int initialY;
        private float initialTouchX;
        private float initialTouchY;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:

                    //remember the initial position.
                    initialX = params.x;
                    initialY = params.y;

                    //get the touch location
                    initialTouchX = event.getRawX();
                    initialTouchY = event.getRawY();

                    lastAction = event.getAction();
                    return true;
                case MotionEvent.ACTION_UP:
                    //As we implemented on touch listener with ACTION_MOVE,
                    //we have to check if the previous action was ACTION_DOWN
                    //to identify if the user clicked the view or not.
                    if (lastAction == MotionEvent.ACTION_DOWN) {
                        //Open the chat conversation click.
                        Intent intent = new Intent(ChatHeadService.this, HomeActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        startActivity(intent);

                        //close the service and remove the chat heads
                        stopSelf();
                    }
                    lastAction = event.getAction();
                    return true;
                case MotionEvent.ACTION_MOVE:
                    //Calculate the X and Y coordinates of the view.
                    params.x = initialX + (int) (event.getRawX() - initialTouchX);
                    params.y = initialY + (int) (event.getRawY() - initialTouchY);

                    //Update the layout with new X & Y coordinate
                    mWindowManager.updateViewLayout(mChatHeadView, params);
                    lastAction = event.getAction();
                    return true;
            }
            return false;
        }
    });
}

@Override
public void onDestroy() {
    super.onDestroy();
    if (mChatHeadView != null) mWindowManager.removeView(mChatHeadView);
}

}


您能否共享xml代码并在github中查看或其他内容对我有帮助。
帕维尔

4

将您项目中的Windowmanger标志“ TYPE_SYSTEM_OVERLAY”更改为“ TYPE_APPLICATION_OVERLAY”以与Android O兼容

WindowManager.LayoutParams.TYPE_PHONEWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY


3

我确实设法通过使用TYPE_SYSTEM_OVERLAY而不是在锁定屏幕上显示了一个窗口TYPE_KEYGUARD_DIALOG。这将按预期工作,并在锁定屏幕上添加窗口。

这样做的问题是,该窗口被添加到了它可能的所有内容之上。也就是说,在安全锁定屏幕的情况下,该窗口甚至会出现在键盘/图案锁的顶部。如果使用的是不安全的锁定屏幕,则从锁定屏幕将其打开时,它将显示在通知托盘的顶部。

对我来说,这是不可接受的。我希望这可以帮助其他面临此问题的人。


非常感谢您的解决方案。有没有办法消除它?将对话框添加为时,无法关闭对话框TYPE_SYSTEM_OVERLAY。我在AlertDialog上的setOnDismissListener没有被调用。
汤姆·泰勒

3

我只是WindowManager通过以下步骤添加了简单视图:

  1. 创建一个布局文件进行显示(在我的情况下为dummy_layout)
  2. 在清单中动态添加权限。
// 1. Show view
private void showCustomPopupMenu()
{
    windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
    // LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // View view = layoutInflater.inflate(R.layout.dummy_layout, null);
    ViewGroup valetModeWindow = (ViewGroup) View.inflate(this, R.layout.dummy_layout, null);
    int LAYOUT_FLAG;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
        LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
    }
    WindowManager.LayoutParams params=new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        LAYOUT_FLAG,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT);

    params.gravity= Gravity.CENTER|Gravity.CENTER;
    params.x=0;
    params.y=0;
    windowManager.addView(valetModeWindow, params);
}

// 2. Get permissions by asking
if (!Settings.canDrawOverlays(this)) {
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, 1234);
}

这样,您可以向WM添加视图。

在Pie中测试。


2

LayoutParams.TYPE_PHONE已弃用。我已经更新了这些代码。

parameters = if (Build.VERSION.SDK_INT > 25) {
        LayoutParams(
                minHW * 2 / 3, minHW * 2 / 3,
                LayoutParams.TYPE_APPLICATION_OVERLAY,
                LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT)
    }else {
        LayoutParams(
                minHW * 2 / 3, minHW * 2 / 3,
                LayoutParams.TYPE_PHONE,
                LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT)
    }

LayoutParams.TYPE_APPLICATION_OVERLAY需要api级别26或更高。


0

拒绝权限的主要原因是我们没有权限绘制其他应用程序,我们必须提供权限以绘制其他应用程序,这可以通过以下代码完成

请求许可代码

    public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;

将此添加到您的MainActivity中


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            askPermission();
}


private void askPermission() {
        Intent intent= new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
        startActivityForResult(intent,ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}

也添加此

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE){
            if(!Settings.canDrawOverlays(this)){
                askPermission();
            }
        }
    }


0

我很难找到合适的解决方案,ApplicationContextTYPE_SYSTEM_ALERT找到了令人困惑的解决方案,如果您希望从任何活动中打开对话框,即使该对话框是您必须使用的单例getApplicationContext(),如果希望该对话框也应使用TYPE_SYSTEM_ALERT,则需要执行以下步骤:

首先获取具有正确主题的对话框实例,还需要像下面的代码片段一样管理版本兼容性:

AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext(), R.style.Theme_AppCompat_Light);

设置标题,消息和按钮后,您必须将对话框构建为:

AlertDialog alert = builder.create();

现在在type这里发挥主要作用,由于这是崩溃的原因,因此我按以下方式处理了兼容性:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        alert.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1);

    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    }

注意:如果您使用的自定义对话框AppCompatDialog如下:

AppCompatDialog dialog = new AppCompatDialog(getApplicationContext(), R.style.Theme_AppCompat_Light);

您可以直接将AppCompatDialog实例的类型定义如下:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1);

    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    }

不要忘记添加清单权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
            PixelFormat.TRANSLUCENT);

    params.gravity = Gravity.START | Gravity.TOP;
    params.x = left;
    params.y = top;
    windowManager.addView(view, params);

} else {
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
            PixelFormat.TRANSLUCENT);


    params.gravity = Gravity.START | Gravity.TOP;
    params.x = left;
    params.y = top;
    windowManager.addView(view, params);
}

0

确保WindowManager.LayoutParams.TYPE_SYSTEM_ERROR已为OS版本<Oreo添加了该版本,因为该版本已弃用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.