Reputation: 1013
I'm working on an app where I need to display a window with some info ON the Lock Screen (KeyGuard) without unlocking the phone. I figured I could probably do it with WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
But every time my app crashes with the following error:
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@40ec8528 -- permission denied for this window type
These posts (here, here and here) all give the same answer. To add the following permission in the Manifest file.
android.permission.SYSTEM_ALERT_WINDOW
Solution that I have implemented but I am still getting the same error. Any idea of what I'm doing wrong?
Here are the permissions in my manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.droidpilot.keyguardwindow" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
And this is the code I use to add the Window to the lock screen
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = mInflater.inflate(R.layout.lock_screen_notif, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
PixelFormat.TRANSLUCENT
);
wm.addView(mView, params);
Anybody got any idea?
P.S. I'm testing on an HTC Desire 620 DS running Android 4.4.2
Upvotes: 98
Views: 161846
Reputation: 2769
Make sure WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
is added for OS version < Oreo as it's been deprecated.
Upvotes: 0
Reputation: 3070
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.TOP;
params.x = left;
params.y = top;
windowManager.addView(view, params);
} else {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.TOP;
params.x = left;
params.y = top;
windowManager.addView(view, params);
}
Upvotes: 0
Reputation: 81
I just added simple view to WindowManager
with the following steps:
// 1. Show view
private void showCustomPopupMenu()
{
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
// LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// View view = layoutInflater.inflate(R.layout.dummy_layout, null);
ViewGroup valetModeWindow = (ViewGroup) View.inflate(this, R.layout.dummy_layout, null);
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,
PixelFormat.TRANSLUCENT);
params.gravity= Gravity.CENTER|Gravity.CENTER;
params.x=0;
params.y=0;
windowManager.addView(valetModeWindow, params);
}
// 2. Get permissions by asking
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1234);
}
With this you can add a view to WM.
Tested in Pie.
Upvotes: 6
Reputation: 3908
I struggled to find the working solution with ApplicationContext
and TYPE_SYSTEM_ALERT
and found confusing solutions, In case you want the dialog should be opened from any activity even the dialog is a singleton you have to use getApplicationContext()
, and if want the dialog should be TYPE_SYSTEM_ALERT
you will need the following steps:
first get the instance of dialog with correct theme, also you need to manage the version compatibility as I did in my following snippet:
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext(), R.style.Theme_AppCompat_Light);
After setting the title, message and buttons you have to build the dialog as:
AlertDialog alert = builder.create();
Now the type
plays the main roll here, since this is the reason of crash, I handled the compatibility as following:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
alert.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
Note: if you are using a custom dialog with AppCompatDialog
as below:
AppCompatDialog dialog = new AppCompatDialog(getApplicationContext(), R.style.Theme_AppCompat_Light);
you can directly define your type to the AppCompatDialog
instance as following:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
Don't forget to add the manifest permission:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Upvotes: 1
Reputation: 1177
The Main Reason for the denying permission is that we don’t have permission for drawing over another apps,We have to provide the permission for the Drawing over other apps that can be done by the following code
Request code for Permission
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
add this in your MainActivity
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
askPermission();
}
private void askPermission() {
Intent intent= new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
startActivityForResult(intent,ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
Add this also
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE){
if(!Settings.canDrawOverlays(this)){
askPermission();
}
}
}
Upvotes: 1
Reputation: 231
Firstly you make sure you have add permission in manifest file.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Check if the application has draw over other apps permission or not? This permission is by default available for API<23. But for API > 23 you have to ask for the permission in runtime.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}
Use This code:
public class ChatHeadService extends Service {
private WindowManager mWindowManager;
private View mChatHeadView;
WindowManager.LayoutParams params;
public ChatHeadService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Language language = new Language();
//Inflate the chat head layout we created
mChatHeadView = LayoutInflater.from(this).inflate(R.layout.dialog_incoming_call, null);
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
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
params.x = 0;
params.y = 100;
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mChatHeadView, params);
} else {
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
params.x = 0;
params.y = 100;
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mChatHeadView, params);
}
TextView tvTitle=mChatHeadView.findViewById(R.id.tvTitle);
tvTitle.setText("Incoming Call");
//Set the close button.
Button btnReject = (Button) mChatHeadView.findViewById(R.id.btnReject);
btnReject.setText(language.getText(R.string.reject));
btnReject.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//close the service and remove the chat head from the window
stopSelf();
}
});
//Drag and move chat head using user's touch action.
final Button btnAccept = (Button) mChatHeadView.findViewById(R.id.btnAccept);
btnAccept.setText(language.getText(R.string.accept));
LinearLayout linearLayoutMain=mChatHeadView.findViewById(R.id.linearLayoutMain);
linearLayoutMain.setOnTouchListener(new View.OnTouchListener() {
private int lastAction;
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
lastAction = event.getAction();
return true;
case MotionEvent.ACTION_UP:
//As we implemented on touch listener with ACTION_MOVE,
//we have to check if the previous action was ACTION_DOWN
//to identify if the user clicked the view or not.
if (lastAction == MotionEvent.ACTION_DOWN) {
//Open the chat conversation click.
Intent intent = new Intent(ChatHeadService.this, HomeActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//close the service and remove the chat heads
stopSelf();
}
lastAction = event.getAction();
return true;
case MotionEvent.ACTION_MOVE:
//Calculate the X and Y coordinates of the view.
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
mWindowManager.updateViewLayout(mChatHeadView, params);
lastAction = event.getAction();
return true;
}
return false;
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (mChatHeadView != null) mWindowManager.removeView(mChatHeadView);
}
}
Upvotes: 7
Reputation: 171
For Android API level of 8.0.0, you should use
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
instead of
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
or SYSTEM_ALERT
.
Upvotes: 17
Reputation: 141
change your flag Windowmanger flag "TYPE_SYSTEM_OVERLAY" to "TYPE_APPLICATION_OVERLAY" in your project to make compatible with Android O
WindowManager.LayoutParams.TYPE_PHONE
to WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
Upvotes: 4
Reputation: 356
try this code working perfectly
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);
Upvotes: 13
Reputation: 147
LayoutParams.TYPE_PHONE is deprecated. I have updated my code like these.
parameters = if (Build.VERSION.SDK_INT > 25) {
LayoutParams(
minHW * 2 / 3, minHW * 2 / 3,
LayoutParams.TYPE_APPLICATION_OVERLAY,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT)
}else {
LayoutParams(
minHW * 2 / 3, minHW * 2 / 3,
LayoutParams.TYPE_PHONE,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT)
}
LayoutParams.TYPE_APPLICATION_OVERLAY requires api level 26 or above.
Upvotes: 2
Reputation: 363
Search
Draw over other apps
in your setting and enable your app. For Android 8 Oreo, try
Settings > Apps & Notifications > App info > Display over other apps > Enable
Upvotes: 8
Reputation: 849
I tried my best to try all the examples available for this issue. Finally I got the answer for this I don know how much is it reliable but my app is not crashing now.
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
//here is all the science of params
final LayoutParams myParams = new LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
PixelFormat.TRANSLUCENT
);
In your manifest file just give the permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Addition to this you can also check for the API level if its >=23 then
if(Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(Activity.this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1234);
}
}
else
{
Intent intent = new Intent(Activity.this, Service.class);
startService(intent);
}
I hope it helps someone somewhere. Full example https://anam-android-codes.blogspot.in/?m=1
Upvotes: 55
Reputation: 1675
I guess you should differentiate the target (before and after Oreo)
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;
}
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
LAYOUT_FLAG,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
Upvotes: 89
Reputation: 1169
if you use apiLevel >= 19
, don't use
WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
which gets the following error:
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@40ec8528 -- permission denied for this window type
Use this instead:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
Upvotes: 116
Reputation: 1013
I did manage to finally display a window on the lock screen with the use of TYPE_SYSTEM_OVERLAY
instead of TYPE_KEYGUARD_DIALOG
. This works as expected and adds the window on the lock screen.
The problem with this is that the window is added on top of everything it possibly can. That is, the window will even appear on top of your keypad/pattern lock in case of secure lock screens. In the case of an unsecured lock screen, it will appear on top of the notification tray if you open it from the lock screen.
For me, that is unacceptable. I hope that this may help anybody else facing this problem.
Upvotes: 3
Reputation: 10326
For what should be completely obvious reasons, ordinary Apps are not allowed to create arbitrary windows on top of the lock screen. What do you think I could do if I created a window on your lockscreen that could perfectly imitate the real lockscreen so you couldn't tell the difference?
The technical reason for your error is the use of the TYPE_KEYGUARD_DIALOG
flag - it requires android.permission.INTERNAL_SYSTEM_WINDOW
which is a signature-level permission. This means that only Apps signed with the same certificate as the creator of the permission can use it.
The creator of android.permission.INTERNAL_SYSTEM_WINDOW
is the Android system itself, so unless your App is part of the OS, you don't stand a chance.
There are well defined and well documented ways of notifying the user of information from the lockscreen. You can create customised notifications which show on the lockscreen and the user can interact with them.
Upvotes: 38