Android: невозможно добавить окно. Разрешение для этого типа окна запрещено
Я работаю над приложением, где мне нужно отображать окно с информацией ВКЛ экрана блокировки (KeyGuard) без разблокировки телефона. Я полагал, что мог бы сделать это с помощью WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
Но каждый раз, когда мое приложение вылетает со следующей ошибкой:
android.view.WindowManager $BadTokenException: невозможно добавить окно [email protected] - разрешение отклонено для этого тип окна
Эти сообщения (здесь, здесь и здесь) дают одинаковый ответ.
Чтобы добавить в файл манифеста следующее разрешение.
android.permission.SYSTEM_ALERT_WINDOW
Решение, которое я выполнил, но я все еще получаю ту же ошибку.
Любая идея о том, что я делаю неправильно?
Вот разрешения в моем файле манифеста:
<?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" />
И это код, который я использую для добавления окна в экран блокировки
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);
У кого-нибудь есть идея?
P.S. Я тестирую HTC Desire 620 DS под управлением Android 4.4.2.
Ответы
Ответ 1
Для того, что должно быть полностью очевидными причинами, обычные приложения не могут создавать произвольные окна поверх экрана блокировки. Как вы думаете, что я мог бы сделать, если бы я создал окно на вашем скриншоте, которое могло бы идеально подражать реальному экрану блокировки, чтобы вы не могли отличить его?
Техническая причина вашей ошибки заключается в использовании флага TYPE_KEYGUARD_DIALOG
- для этого требуется android.permission.INTERNAL_SYSTEM_WINDOW
, который является разрешением на уровне подписи. Это означает, что только приложения, подписанные с тем же сертификатом, что и создатель разрешения, могут его использовать.
Создателем android.permission.INTERNAL_SYSTEM_WINDOW
является сама система Android, поэтому, если ваше приложение не является частью ОС, у вас нет шансов.
Есть хорошо определенные и хорошо документированные способы уведомлять пользователя об информации с экрана блокировки. Вы можете создавать настраиваемые уведомления, которые отображаются на заблокированном экране, и пользователь может взаимодействовать с ними.
Ответ 2
если вы используете apiLevel >= 19
, не используйте
WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
который получает следующую ошибку:
android.view.WindowManager $BadTokenException: невозможно добавить окно [email protected] - разрешено отклонение для этого типа окна
Используйте это вместо:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
Ответ 3
Я предполагаю, что вы должны различать цель (до и после 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
Я попытался изо всех сил попробовать все примеры, доступные для этой проблемы. Наконец, я получил ответ на этот вопрос, я не знаю, насколько он надежный, но мое приложение теперь не рушится.
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
);
В файле манифеста просто дайте разрешение
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Кроме того, вы можете проверить уровень API, если его> = 23, тогда
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);
}
Надеюсь, это кому-то поможет. Полный пример https://anam-android-codes.blogspot.in/?m=1
Ответ 5
Для уровня API Android 8.0.0 вы должны использовать
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
вместо
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
или SYSTEM_ALERT
.
Ответ 6
попробуйте этот код работать отлично
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
Мне удалось наконец отобразить окно на экране блокировки с использованием TYPE_SYSTEM_OVERLAY
вместо TYPE_KEYGUARD_DIALOG
. Это работает так, как ожидалось, и добавляет окно на экран блокировки.
Проблема заключается в том, что окно добавлено поверх всего, что возможно. То есть, окно будет даже отображаться поверх блокировки клавиатуры/шаблона в случае экранов с защитой от блокировки, В случае незащищенного экрана блокировки он появится поверх лотка уведомлений, если вы откроете его из экрана блокировки.
Для меня это неприемлемо. Я надеюсь, что это может помочь кому-то еще столкнуться с этой проблемой.
Ответ 8
Поиск
Нарисуйте другие приложения
в настройках и включите приложение. Для Android 8 Oreo попробуйте
Настройки> Приложения и уведомления> Информация о приложении> Просмотр по другим приложениям> Включить
Ответ 9
LayoutParams.TYPE_PHONE устарел. Я обновил свой код, как это.
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 требует уровня api 26 или выше.
Ответ 10
Во-первых, убедитесь, что вы добавили разрешение в файл манифеста.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Проверьте, имеет ли приложение право на использование других приложений или нет? Это разрешение по умолчанию доступно для API <23. Но для API> 23 вы должны попросить разрешения во время выполнения.
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);
}
Использовать этот код:
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 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);
}
}
Ответ 11
измените свой флаг "Флаг окна" "TYPE_SYSTEM_OVERLAY" на "TYPE_APPLICATION_OVERLAY" в вашем проекте, чтобы сделать совместимым с Android O
WindowManager.LayoutParams.TYPE_PHONE
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
Ответ 12
Android: невозможно добавить окно. Разрешение для этого типа окна запрещено
Пожалуйста, следуйте по этой ссылке, вам нужно только разрешение. Я попытался решить мою проблему. Попробуй это поможет.