问题
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
回答1:
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.
回答2:
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
回答3:
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);
回答4:
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
回答5:
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
.
回答6:
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);
回答7:
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
回答8:
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
回答9:
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);
}
}
回答10:
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.
回答11:
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.
来源:https://stackoverflow.com/questions/32224452/android-unable-to-add-window-permission-denied-for-this-window-type