24

I am using overlay permission to display certain information in my app. Running it on API 23 - 25 it works fine (asking for permission, granting, etc. according to

Unable to add window android.view.ViewRoot$W@44da9bc0 -- permission denied for this window type ). (Many thanks to ceph3us!)

Trying the same on API 26 I am getting an error, basically "permission denied for window type 2002" when calling

windowManager.addView(frameLayout, params);

Did Google change the way, overlay works? Any idea, how to get my text as an overlay onto the screen in Android 8 (Oreo), API 26? Thanks for your ideas!

This is the error-log:

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) 

In my Manifest, I have:

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

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

This is my 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();
        }
    }
}
}

And this is the 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);
}
}
tomseitz
  • 503
  • 2
  • 4
  • 14

6 Answers6

50

According to the documentation on Android 8.0 Behavior Changes for apps targeting Android 8.0:

Apps that use the SYSTEM_ALERT_WINDOW permission can no longer use the following window types to display alert windows above other apps and system windows:

TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR

Instead, apps must use a new window type called TYPE_APPLICATION_OVERLAY.

So your app could target some lower version. In this case, your alert window will ...

always appear beneath the windows that use the TYPE_APPLICATION_OVERLAY window type. If an app targets Android 8.0 (API level 26), the app uses the TYPE_APPLICATION_OVERLAY window type to display alert windows.

(quoted from the same source)

Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
  • how to make the overlay layout interactable? – Jerin A Mathews Nov 08 '17 at 04:11
  • @Jerin A Mathews - instead of a custom View which overrides onDraw() one can also use a layout with Buttons and inflate this layout (e.g. with `View.inflate()` ) and set OnClickListeners and so on. – Bö macht Blau Nov 08 '17 at 19:56
  • @0X0nosugar Do you know if `TYPE_ACCESSIBILITY_OVERLAY` can be used to have a top level filter where touch events are still passed through to activities underneath? – EM-Creations Dec 16 '17 at 14:50
  • 1
    @EM-Creations - I very seriously doubt it because this would be a security issue if we are talking about Activities belonging to third party apps. – Bö macht Blau Dec 16 '17 at 15:37
  • @0X0nosugar I appreciate your help.. hmm this is going to cause serious issues for my app working with the latest version of Android.. – EM-Creations Dec 17 '17 at 14:15
  • @0X0nosugar you saved my time. +1 vote for u. Thanks – Sagar Aghara Dec 20 '17 at 05:16
  • but tell me one thing..when i used window type TYPE_APPLICATION_OVERLAY in my app. it is working fine..but i cant touch anywhere because of this windowtype.. then please tell me when you free if you know that. Thanks again – Sagar Aghara Dec 20 '17 at 05:18
  • @Sagar Aghara - do you mean you can't touch anywhere outside the dialog area so that the underlying (maybe third party) apps still receive the touch event? – Bö macht Blau Dec 20 '17 at 06:09
  • see..i have SnakeonScreen app. like the Snakes are running on my Screen when i press the Start button by using Service. all done here..but when snakes are running on the screen at that time. i can not touch the any apps means anywhere in the screen even home or back button. – Sagar Aghara Dec 20 '17 at 06:27
  • @Sagar Aghara - so no touch events at all can go to other components, not even the BACK key? Sounds complicated... normally you provide a Button or something so you can dismiss the dialog when the Button is clicked. OTOH maybe you could let your overlay View listen for some gesture to dismiss it? Or if you look into the [WindowManager.LayoutParams](https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html), especially the flags, you'll find one which will help you solve your issue? – Bö macht Blau Dec 20 '17 at 18:43
  • @0X0nosugar i will try as you say. i think it is the problem of flags. Thanks man – Sagar Aghara Dec 21 '17 at 04:48
25

Android Oreo (and future versions) doesn't allow use WindowManager.LayoutParams.TYPE_PHONE because is deprecated instead use 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);
        }
}
  • 5
    please provide an explanation for your answer – aldok Jan 23 '18 at 04:45
  • 2
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation would greatly improve its long-term value](//meta.stackexchange.com/q/114762/350567) by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – iBug Jan 23 '18 at 05:53
  • WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY is not working over the lockscreen – Nikunj Paradva Jul 05 '18 at 12:24
  • Great ! only this one helped a lot ! – Ankur Jan 21 '19 at 10:37
9

try this code working perfect

 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);
2
  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);
suhas sasuke
  • 640
  • 6
  • 7
1
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
}
else
{
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}

only change in this parameter of Window manager

Makvin
  • 3,475
  • 27
  • 26
0

Use following code

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);
Mukesh Y.
  • 869
  • 1
  • 16
  • 36