РАЗРЕШЕНИЕ SYSTEM_ALERT_WINDOW на API 26 не работает должным образом. Отказано в доступе для окна типа 2002

Я использую разрешение наложения для отображения определенной информации в моем приложении. Запустив его на API 23-25, он отлично работает (запрашивая разрешение, предоставляя и т. д. в соответствии с

Невозможно добавить окно android. view.ViewRoot$W@44da9bc0 -- доступ запрещен для этого типа окна ). (Большое спасибо ceph3us!)

Пытаясь сделать то же самое в API 26, я получаю сообщение об ошибке, в основном «отказано в доступе для типа окна 2002» при вызове

windowManager.addView(frameLayout, params);

Гугл изменил способ, оверлей работает? Любая идея, как получить мой текст в качестве наложения на экран в 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"/>

Это моя основная активность:

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

person tomseitz    schedule 24.08.2017    source источник


Ответы (6)


Согласно документации по изменениям поведения Android 8.0. для приложений, ориентированных на 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 для отображения окон предупреждений.

(цитата из того же источника)

person Bö macht Blau    schedule 24.08.2017
comment
как сделать макет оверлея интерактивным? - person Jerin A Mathews; 08.11.2017
comment
@Jerin Мэтьюз - вместо пользовательского представления, которое переопределяет onDraw(), можно также использовать макет с кнопками и раздувать этот макет (например, с помощью View.inflate() ), устанавливать OnClickListeners и так далее. - person Bö macht Blau; 08.11.2017
comment
@ 0X0nosugar Знаете ли вы, можно ли использовать TYPE_ACCESSIBILITY_OVERLAY для фильтра верхнего уровня, при котором события касания по-прежнему передаются действиям нижнего уровня? - person EM-Creations; 16.12.2017
comment
@EM-Creations - я очень серьезно сомневаюсь в этом, потому что это может быть проблемой безопасности, если мы говорим о действиях, принадлежащих сторонним приложениям. - person Bö macht Blau; 16.12.2017
comment
@ 0X0nosugar Я ценю вашу помощь ... хм, это вызовет серьезные проблемы для моего приложения, работающего с последней версией Android ... - person EM-Creations; 17.12.2017
comment
@0X0nosugar, ты сэкономил мне время. +1 голос за тебя. Спасибо - person Sagar Aghara; 20.12.2017
comment
но скажите мне одну вещь... когда я использовал тип окна TYPE_APPLICATION_OVERLAY в своем приложении. он работает нормально .. но я не могу никуда прикоснуться из-за этого типа окна .. тогда, пожалуйста, скажите мне, когда вы освободитесь, если вы это знаете. Еще раз спасибо - person Sagar Aghara; 20.12.2017
comment
@Sagar Aghara - вы имеете в виду, что вы не можете касаться ничего за пределами области диалога, чтобы базовые (возможно, сторонние) приложения по-прежнему получали событие касания? - person Bö macht Blau; 20.12.2017
comment
см.. у меня есть приложение SnakeonScreen. как змеи бегают на моем экране, когда я нажимаю кнопку «Пуск» с помощью службы. тут все делается..но когда по экрану в это время бегают змейки. Я не могу прикоснуться к любым приложениям в любом месте экрана, даже к кнопке «Домой» или «Назад». - person Sagar Aghara; 20.12.2017
comment
@Sagar Aghara - так что события касания вообще не могут передаваться другим компонентам, даже клавише BACK? Звучит сложно... обычно вы предоставляете кнопку или что-то в этом роде, чтобы закрыть диалоговое окно при нажатии кнопки. OTOH, может быть, вы могли бы позволить своему оверлейному представлению прослушивать какой-то жест, чтобы отклонить его? Или, если вы посмотрите на WindowManager.LayoutParams, особенно flags, вы найдете тот, который поможет вам решить вашу проблему? - person Bö macht Blau; 20.12.2017
comment
@0X0nosugar я попробую, как ты сказал. Я думаю, что это проблема флагов. Спасибо чувак - person Sagar Aghara; 21.12.2017

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);
        }
}
person Brayan Armando Yaquian Gonzale    schedule 22.01.2018
comment
пожалуйста, объясните свой ответ - person aldok; 23.01.2018
comment
Спасибо за этот фрагмент кода, который может предоставить некоторую ограниченную немедленную помощь. правильное объяснение значительно улучшит его долгосрочную ценность, показав, почему это хорошее решение. к проблеме и сделает его более полезным для будущих читателей с другими подобными вопросами. Пожалуйста, отредактируйте свой ответ, чтобы добавить некоторые пояснения, включая сделанные вами предположения. - person iBug; 23.01.2018
comment
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY не работает на экране блокировки - person Nikunj Paradva; 05.07.2018
comment
Здорово ! только этот очень помог! - person Ankur; 21.01.2019

попробуйте этот код работает отлично

 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);
person shakirullah orakzai    schedule 14.09.2018

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
}
else
{
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}

изменить только этот параметр оконного менеджера

person Makvin    schedule 20.07.2018

Используйте следующий код

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);
person MKY    schedule 19.08.2018

Вместо того, чтобы идти прямо на свою страницу, вы должны сначала запросить разрешение примерно так (в моем проекте главная страница является целевой страницей), вы можете найти больше в ИСПРАВИТЬ РАЗРЕШЕНИЕ ОТКАЗАНО ДЛЯ ЭТОГО ТИПА ОКНА

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;
        }
    }
}
person Abbas Rahimi    schedule 03.08.2019