Skip to content

Instantly share code, notes, and snippets.

@sjf
Last active May 27, 2024 16:49
Show Gist options
  • Save sjf/ae050683a8d790dcb3260b5ffc610b87 to your computer and use it in GitHub Desktop.
Save sjf/ae050683a8d790dcb3260b5ffc610b87 to your computer and use it in GitHub Desktop.
Android screen overlay example. Draws a button over other apps.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Overlay example"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.design.widget.CoordinatorLayout>
package io.sjf.overlay;
import android.annotation.TargetApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Overlay";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (checkDrawOverlayPermission()) {
startOverlayService();
}
}
private static int REQUEST_CODE = 1;
private boolean checkDrawOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
/** check if we already have permission to draw over other apps */
if (!Settings.canDrawOverlays(this)) {
Log.d(TAG, "canDrawOverlays NOK");
/** 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);
return false;
} else {
Log.d(TAG, "canDrawOverlays OK");
}
}
return true;
}
@Override
@TargetApi(Build.VERSION_CODES.M)
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) {
if (Settings.canDrawOverlays(this)) {
startOverlayService();
}
}
}
private void startOverlayService() {
Intent intent = new Intent(this, OverlayService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.sjf.overlay">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".OverlayService"
android:exported="false"/>
</application>
</manifest>
package io.sjf.overlay;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
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.widget.Button;
public class OverlayService extends Service implements OnTouchListener, OnClickListener {
private static String TAG = "OverlayService";
private WindowManager wm;
private Button button;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onCreate();
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "channel1";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"Overlay notification",
NotificationManager.IMPORTANCE_LOW);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("adsf")
.setContentText("asdf1")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build();
startForeground(1, notification);
}
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
button = new Button(this);
button.setBackgroundResource(R.drawable.ic_button1);
button.setText("Button");
button.setAlpha(1);
button.setBackgroundColor(Color.BLUE);
button.setOnClickListener(this);
int type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_PHONE;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
type,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.TOP;
params.x = 0;
params.y = 0;
wm.addView(button, params);
return START_NOT_STICKY;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, " ++++ On touch");
return false;
}
@Override
public void onClick(View v) {
Log.d(TAG, " ++++ On click");
}
@Override
public void onDestroy() {
super.onDestroy();
if (button != null) {
wm.removeView(button);
button = null;
}
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
}
@sjf
Copy link
Author

sjf commented Oct 2, 2019

When you start the app, if it doesn't have permission to draw over other apps, it will prompt you to enable it. It should look like this when it is running.

Screenshot_20191002-121955

@91labs
Copy link

91labs commented Nov 19, 2021

This is awesome. Thank you!!!!

Copy link

ghost commented Jun 13, 2023

Can it move able?

@not-inept
Copy link

not-inept commented Jul 9, 2023

@prokash447 you can control the positioning with the params.x/y passed to wm.addView here (Line 70)

@WaheedurRahman
Copy link

when i open the setting app(system setting) the overlay does not work. why? have any solution?

@dccv123
Copy link

dccv123 commented May 27, 2024

Hello,

I see a blank activity that blocks touch events to home screen/ apps underneath. Are we missing any other configurations?

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