【问题标题】:SYSTEM_ALERT_WINDOW PERMISSION on API 26 not working as expected. Permission denied for window type 2002API 26 上的 SYSTEM_ALERT_WINDOW PERMISSION 未按预期工作。窗口类型 2002 的权限被拒绝
【发布时间】:2018-02-02 16:27:06
【问题描述】:

我正在使用覆盖权限在我的应用中显示某些信息。在 API 23 - 25 上运行它可以正常工作(根据

请求许可、授予等

Unable to add window android.view.ViewRoot$W@44da9bc0 -- permission denied for this window type)。 (非常感谢 ceph3us!)

在 API 26 上尝试相同的操作时出现错误,基本上是“调用时窗口类型 2002 的权限被拒绝”

windowManager.addView(frameLayout, params);

Google 改变了覆盖方式吗?任何想法,如何在 Android 8 (Oreo) API 26 中将我的文本叠加到屏幕上? 感谢您的想法!

这是错误日志:

08-24 16:41:56.730 2615-2615/net.zwittscha.testoverlay E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.zwittscha.testoverlay, PID: 2615
java.lang.RuntimeException: Unable to start activity ComponentInfo{net.zwittscha.testoverlay/net.zwittscha.testoverlay.MainActivity}: 
                android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@6fa0089 -- 
                permission denied for window type 2002
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
 at android.app.ActivityThread.-wrap11(Unknown Source:0)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
 at android.os.Handler.dispatchMessage(Handler.java:105)
 at android.os.Looper.loop(Looper.java:164)
 at android.app.ActivityThread.main(ActivityThread.java:6541)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
            Caused by: android.view.WindowManager$BadTokenException: 
            Unable to add window android.view.ViewRootImpl$W@6fa0089 -- 
            permission denied for window type 2002
 at android.view.ViewRootImpl.setView(ViewRootImpl.java:789)
 at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
 at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:92)
 at net.zwittscha.testoverlay.MainActivity.createOnTopView(MainActivity.java:46)
 at net.zwittscha.testoverlay.MainActivity.checkDrawOverlayPermission(MainActivity.java:66)
 at net.zwittscha.testoverlay.MainActivity.onCreate(MainActivity.java:28)
 at android.app.Activity.performCreate(Activity.java:6975)
 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
 at android.app.ActivityThread.-wrap11(Unknown Source:0) 
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
 at android.os.Handler.dispatchMessage(Handler.java:105) 
 at android.os.Looper.loop(Looper.java:164) 
 at android.app.ActivityThread.main(ActivityThread.java:6541) 
 at java.lang.reflect.Method.invoke(Native Method) 
 at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 

在我的清单中,我有:

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

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

这是我的 MainActivity:

    public class MainActivity extends AppCompatActivity {

DrawView dv;
FrameLayout frameLayout;
WindowManager windowManager;
LayoutInflater layoutInflater;
/** code to post/handler request for permission */
public final static int REQUEST_CODE = 1234;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    checkDrawOverlayPermission();
}

public void createOnTopView() {

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
            PixelFormat.TRANSLUCENT);
    params.gravity = Gravity.CENTER;

    if (frameLayout == null) frameLayout = new FrameLayout(getApplicationContext());
    if (dv == null) dv = new DrawView(getApplicationContext());

    windowManager = (WindowManager)
            getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    windowManager.addView(frameLayout, params);
    windowManager.addView(dv, params);

    layoutInflater = (LayoutInflater)
            getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // Here is the place where you can inject whatever layout you want.
    layoutInflater.inflate(R.layout.activity_main, frameLayout);
}

public void checkDrawOverlayPermission() {
    /* check if we already  have permission to draw over other apps */
    if (android.os.Build.VERSION.SDK_INT > 22) {
        if (!Settings.canDrawOverlays(this)) {
        /* if not construct intent to request permission */
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
        /* request permission via start activity for result */
            startActivityForResult(intent, REQUEST_CODE);
        }
        else {
            createOnTopView();
        }
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
    /* check if received result code
     is equal our requested code for draw permission  */
    if (requestCode == REQUEST_CODE && android.os.Build.VERSION.SDK_INT > 22) {
   /* if so check once again if we have permission */
        if (Settings.canDrawOverlays(this)) {
            createOnTopView();
        }
    }
}
}

这是 DrawView:

    public class DrawView extends View {

int w;
int h;
int r;
float screenFactor;
TextPaint startTextPaint;

public DrawView(Context activity) {
    super(activity);
}

@Override
protected void onSizeChanged(int width, int height, int oldw, int oldh) {

        w = width;
        h = height;
        r = w / 2;

    screenFactor = (r / 160f);

    if (startTextPaint == null) startTextPaint = new TextPaint();

    startTextPaint.setTextSize(100);
    startTextPaint.setTextAlign(Paint.Align.CENTER);
    startTextPaint.setTypeface(Typeface.create("Roboto Condensed", Typeface.BOLD));

    super.onSizeChanged(w, h, oldw, oldh);
}

@Override
protected void onDraw(Canvas canvas) {

        startTextPaint.setARGB(255,255,0,0);
        canvas.drawText("Test", w / 2, h / 2, startTextPaint);
}
}

【问题讨论】:

    标签: android permissions android-8.0-oreo


    【解决方案1】:

    根据Android 8.0 Behavior Changes 上针对 Android 8.0 的应用的文档:

    使用 SYSTEM_ALERT_WINDOW 权限的应用程序不能再使用以下窗口类型在其他应用程序和系统窗口上方显示警报窗口:

    TYPE_PHONE
    TYPE_PRIORITY_PHONE
    TYPE_SYSTEM_ALERT
    TYPE_SYSTEM_OVERLAY
    TYPE_SYSTEM_ERROR
    

    相反,应用必须使用称为TYPE_APPLICATION_OVERLAY 的新窗口类型。

    因此您的应用可以针对某些较低版本。在这种情况下,您的警报窗口将...

    始终出现在使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口下方。如果应用面向 Android 8.0(API 级别 26),则该应用使用 TYPE_APPLICATION_OVERLAY 窗口类型来显示警报窗口。

    (引自同一来源)

    【讨论】:

    • 如何让overlay布局可交互?
    • @Jerin A Mathews - 除了覆盖 onDraw() 的自定义视图之外,还可以使用带有按钮的布局并扩展此布局(例如使用 View.inflate() )并设置 OnClickListeners 等等。
    • @0X0nosugar 你知道TYPE_ACCESSIBILITY_OVERLAY 是否可以用于拥有一个顶级过滤器,触摸事件仍会传递到下面的活动?
    • @EM-Creations - 我非常怀疑它,因为如果我们谈论的是属于第三方应用程序的活动,这将是一个安全问题。
    • @0X0nosugar 感谢您的帮助.. 嗯,这将导致我的应用在最新版本的 Android 上运行时出现严重问题..
    【解决方案2】:

    Android Oreo(和未来版本)不允许使用 WindowManager.LayoutParams.TYPE_PHONE 因为已弃用,而是使用 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY

    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,
                        PixelFormat.TRANSLUCENT);
            }else{
                params = new WindowManager.LayoutParams(
                        WindowManager.LayoutParams.WRAP_CONTENT,
                        WindowManager.LayoutParams.WRAP_CONTENT,
                        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                        PixelFormat.TRANSLUCENT);
            }
    }
    

    【讨论】:

    • 请解释一下你的答案
    • 感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation would greatly improve its long-term value 通过展示为什么这是一个很好的解决问题的方法,并且会使其对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
    • WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 无法在锁屏上工作
    • 太棒了!只有这个有很大帮助!
    【解决方案3】:

    试试这段代码是否完美

     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);
    

    【讨论】:

      【解决方案4】:
        int LAYOUT_FLAG;
      
          mOverlayView = LayoutInflater.from(this).inflate(R.layout.overlay_layout, null);
      
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
              LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
          } else {
              LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
          }
      
          final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                  WindowManager.LayoutParams.WRAP_CONTENT,
                  WindowManager.LayoutParams.WRAP_CONTENT,
                  LAYOUT_FLAG,
                  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                  PixelFormat.TRANSLUCENT);
      

      【讨论】:

        【解决方案5】:
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
        }
        else
        {
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        }
        

        只改变窗口管理器的这个参数

        【讨论】:

          【解决方案6】:

          使用以下代码

          int LAYOUT_FLAG = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
                      WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE;
              mWindowParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                      LAYOUT_FLAG, // Overlay over the other apps.
                      LayoutParams.FLAG_NOT_FOCUSABLE    // This flag will enable the back key press.
                              | LayoutParams.FLAG_NOT_TOUCH_MODAL, // make the window to deliver the focus to the BG window.
                      PixelFormat.TRANSPARENT);
          

          【讨论】:

            【解决方案7】:

            您必须先请求许可,而不是直接进入您的页面,这样的事情(在我的项目中,主页是目标页面) 您可以在FIX PERMISSION DENIED FOR THIS WINDOW TYPE 中找到更多信息

            private static final int OVERLAY_PERMISSION_CODE =5463 ; // can be anything
            
            @Override
            public void onCreate(...) {
            
                if (Build.VERSION.SDK_INT >= 23) {
                    if (!Settings.canDrawOverlays(this)) {
            
                        // Open the permission page
                        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                                Uri.parse("package:" + getPackageName()));
                        startActivityForResult(intent, OVERLAY_PERMISSION_CODE);
                        return;
                    }
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2019-02-03
              • 1970-01-01
              • 2018-01-18
              • 2018-09-17
              • 2016-04-12
              • 1970-01-01
              • 2018-02-22
              • 2017-05-01
              • 1970-01-01
              相关资源
              最近更新 更多