Skip to content

Instantly share code, notes, and snippets.

@bjoernQ
Created October 14, 2013 13:02
Show Gist options
  • Save bjoernQ/6975256 to your computer and use it in GitHub Desktop.
Save bjoernQ/6975256 to your computer and use it in GitHub Desktop.
Creating a System Overlay (Always on Top over all Apps) in Android
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.mobilej.overlay"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="14" />
<application android:label="SystemOverlay" >
<activity
android:name="de.mobilej.overlay.Main"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="de.mobilej.overlay.OverlayShowingService"
android:exported="false" />
</application>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
</manifest>
package de.mobilej.overlay;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class Main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent svc = new Intent(this, OverlayShowingService.class);
startService(svc);
finish();
}
}
package de.mobilej.overlay;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.Button;
import android.widget.Toast;
public class OverlayShowingService extends Service implements OnTouchListener, OnClickListener {
private View topLeftView;
private Button overlayedButton;
private float offsetX;
private float offsetY;
private int originalXPos;
private int originalYPos;
private boolean moving;
private WindowManager wm;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
overlayedButton = new Button(this);
overlayedButton.setText("Overlay button");
overlayedButton.setOnTouchListener(this);
overlayedButton.setAlpha(0.0f);
overlayedButton.setBackgroundColor(0x55fe4444);
overlayedButton.setOnClickListener(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.TOP;
params.x = 0;
params.y = 0;
wm.addView(overlayedButton, params);
topLeftView = new View(this);
WindowManager.LayoutParams topLeftParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);
topLeftParams.gravity = Gravity.LEFT | Gravity.TOP;
topLeftParams.x = 0;
topLeftParams.y = 0;
topLeftParams.width = 0;
topLeftParams.height = 0;
wm.addView(topLeftView, topLeftParams);
}
@Override
public void onDestroy() {
super.onDestroy();
if (overlayedButton != null) {
wm.removeView(overlayedButton);
wm.removeView(topLeftView);
overlayedButton = null;
topLeftView = null;
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
float x = event.getRawX();
float y = event.getRawY();
moving = false;
int[] location = new int[2];
overlayedButton.getLocationOnScreen(location);
originalXPos = location[0];
originalYPos = location[1];
offsetX = originalXPos - x;
offsetY = originalYPos - y;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int[] topLeftLocationOnScreen = new int[2];
topLeftView.getLocationOnScreen(topLeftLocationOnScreen);
System.out.println("topLeftY="+topLeftLocationOnScreen[1]);
System.out.println("originalY="+originalYPos);
float x = event.getRawX();
float y = event.getRawY();
WindowManager.LayoutParams params = (LayoutParams) overlayedButton.getLayoutParams();
int newX = (int) (offsetX + x);
int newY = (int) (offsetY + y);
if (Math.abs(newX - originalXPos) < 1 && Math.abs(newY - originalYPos) < 1 && !moving) {
return false;
}
params.x = newX - (topLeftLocationOnScreen[0]);
params.y = newY - (topLeftLocationOnScreen[1]);
wm.updateViewLayout(overlayedButton, params);
moving = true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (moving) {
return true;
}
}
return false;
}
@Override
public void onClick(View v) {
Toast.makeText(this, "Overlay button click event", Toast.LENGTH_SHORT).show();
}
}
@RedZello
Copy link

RedZello commented Jun 7, 2016

Yes , In Jelly Ben 4.2.2 . It worked out, But in Marshmallow, It crashes the APP.

@gkuti
Copy link

gkuti commented Jun 30, 2016

There are ways you can "cheat" the android OS, set the targetSdkVersion to 15, then it won't ask you for permission on marshmallow

@THEb0nny
Copy link

I have a bug in Genymotion. The MM shield tablet that works.

@THEb0nny
Copy link

Need add permiss android.permission.SYSTEM_ALERT_WINDOW in system.

@THEb0nny
Copy link

THEb0nny commented Aug 14, 2016

Add you code
public static int OVERLAY_PERMISSION_REQ_CODE = 1;

@TargetApi(Build.VERSION_CODES.M)
    public void checkPermissionOverlay() {
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(MainActivity.this, "Нужны права на наложение поверх всех приложений", Toast.LENGTH_LONG).show();
            Intent intentSettings = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
            startActivityForResult(intentSettings, OVERLAY_PERMISSION_REQ_CODE);
        }
    }

and use the method checkPermissionOverlay() before starting Intent svc.

@vgrec
Copy link

vgrec commented Sep 16, 2016

Thank you for the gist!
Could you explain what is the purpose of "topLeftView" that is initialized with width/height of zero, and x/y position is set also to zero?

From observation I noticed that when moving the "overlayedButton", the x position of "topLeftView" is always zero, but the y position varies depending of the screen device the code is running on.
Removing the "topLeftView" makes the button to jump a little when starting to move it. So, "topLeftView" definitely is there for a reason. Could you please say in a couple of words what is the reason?

@bruenor41
Copy link

Hi people, I tried this code in Android 7 and it crashes on android 7 :(

java.lang.RuntimeException: Unable to create service test.test.chromeostest.OverlayShowingService: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@30bf239 -- permission denied for window type 2003

Please faces someone the same issue? My application targets sdk version 21

@hatamiarash7
Copy link

i have a problem .... when i move button to corner of screen, i can't touch system's view like Call button in the following image

https://i.stack.imgur.com/pqJlZ.png

how can i disable any touch in button ? ( ignore my view's touch and touch system's view )

@amitbhoraniya
Copy link

Can I capture screenshot by click on this overlay button? if yes then can you help me how ?

@JatinRaghav
Copy link

Hey ,
I try that code but the issue is overlaybutton not show anywhere in the screen please anyone guide me

@jewom
Copy link

jewom commented Jun 16, 2017

Hi,

The same, I have nothing...

@thanhbinh84
Copy link

thanhbinh84 commented Jun 26, 2017

Me too, only see the overlayButton on 4.4 device. I have checked permission on 7.0 but don't see anything

@StraleXY
Copy link

Hi guys this works great after removing setAlpha(0,0) also I had a small problems since i'm not in java but I manage to do it..
And now I was wondering how to blur background? Any ideas? I tried with flag_blur_behind (or something like that but nothing seams to happened so.. Cheers and thanks for sharing

@WuerfelDev
Copy link

WuerfelDev commented Nov 25, 2017

I have a button on the main activity which i want to use to destroy the screenoverlay. How to archive this?


It is as easy as calling stopService(svc);

@studymadara
Copy link

studymadara commented Dec 15, 2017

it did worked but the actions don't like ontouch and onclick .

SORRY IT DID WORKED !! but movement is not proper like it tends to go downside of the screen :(

@pattagobi
Copy link

hello everyone, with sdk26

did anybody achieve this?

I am trying to do it for a long week but all I am getting is permission denied for window type 2002

can anybody posts sample code please?

thank you

@SEAbdulbasit
Copy link

hello..
can you help me sir, i want to override the default incoming call screen in android. i have searched but got no help..
plz if you could help

@elich11
Copy link

elich11 commented Jul 11, 2018

For sdk26 you need to add:
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 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);

@bengaskin
Copy link

bengaskin commented Feb 23, 2019

This is an old thread, but here's my thoughts:
The color/alpha settings as-is worked for me on kitkat, but not on lollipop.
So I updated them similar to another commenter.

Changed to
overlayedButton.setBackgroundColor(Color.BLUE);
overlayedButton.setTextColor(Color.WHITE);
overlayedButton.getBackground().setAlpha(Math.round(0.33f*255));
(white on transparent light blue: 33% transparency).

All the topleft stuff didn't make sense to me at all. So I commented out all the topLeft / topLeftView code and it made very little difference.

I changed:

params.x = newX - (topLeftLocationOnScreen[0]);
params.y = newY - (topLeftLocationOnScreen[1]);

to:

offsetX = originalXPos - x;
offsetY = originalYPos - y;

I'd be glad to hear an explanation of the topLeft code. I suspect that if it is doing something, it could be done a lot more efficiently, and doesn't have to be a view object of it's own. (Or please tell me otherwise).

@REALmyenemy
Copy link

REALmyenemy commented Mar 15, 2019

I get an error on line 52 (OverlayShowingService) when I use it, target device 8.1 oreo, but project SDK 15. "FATAL EXCEPTION".
at de.mobilej.overlay.OverlayShowingService.onCreate(OverlayShowingService.java:52)

The line
wm.addView(overlayedButton, params);
Why is that?

[Edit]
Seems like you have to replace both
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT with LAYOUT_FLAG as elich11 said in order to work.

Thanks everyone

@mgood7123
Copy link

crashes on Pie with permission denied

@JackyBaby615
Copy link

Thanks.

@brucekim
Copy link

thanks

@baekseunghyeok
Copy link

baekseunghyeok commented Jan 15, 2020

I want to make a Overlay (Always on Top over all Apps) in Android 6.0. Your code doesn't work in my android studio.
Could you please help make system?

@alexcomo95
Copy link

alexcomo95 commented Dec 28, 2020

If you get "permission denied for window type 2003 at", here is my fix.

WindowManager.LayoutParams.TYPE_SYSTEM_ALERT is deprecated from SDK 26.

In OverlayShowingService, just replace it with WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY.

@D44IN
Copy link

D44IN commented Jan 6, 2021

I get this error

java.lang.RuntimeException: Unable to create service de.mobilej.overlay.OverlayShowingService: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@4929b7e -- permission denied for window type 2003
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3493)
at android.app.ActivityThread.-wrap4(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1776)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6753)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:482)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@4929b7e -- permission denied for window type 2003
at android.view.ViewRootImpl.setView(ViewRootImpl.java:949)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at de.mobilej.overlay.OverlayShowingService.onCreate(OverlayShowingService.java:52)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3483)
at android.app.ActivityThread.-wrap4(Unknown Source:0) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1776) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:164) 
at android.app.ActivityThread.main(ActivityThread.java:6753) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:482) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

Any way to fix this?

@alexcomo95
Copy link

For "permission denied for window type 2003
at". Please look at my comment and come back.

@D44IN
Copy link

D44IN commented Jan 6, 2021

Thanks, that solved the first error, though I now get 'permission denied for window type 2038' instead

@D44IN
Copy link

D44IN commented Jan 8, 2021

Ok for 'permission denied for window type 2038' you just need to allow drawing over other apps in settings on phone.

I am trying to use this to create an app to hide rounded screen corners by adding a black bar across top and bottom of screen.
At the moment these black bars appear below the status bar and above the navigation bar, as show in the image
(https://user-images.githubusercontent.com/77055803/104008998-3aeb8f00-51a2-11eb-90e6-43fa2704ce0d.jpg)

Anyone know how to make the overlays position to the absolute top and bottom of screen?

Also the overlay gets rotated with any app that goes to landscape mode, as shown here
(https://user-images.githubusercontent.com/77055803/104009159-7a19e000-51a2-11eb-89d7-7b4ad5d22660.jpg)

How can I make it always appear at the top and bottom edges of the screen, regardless of app rotation?

@asadkhan9564
Copy link

use this code it will solve your problem
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
wmParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_TOAST);
} else {
wmParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment