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