Я работаю над приложением , где мне нужно , чтобы отобразить окно с некоторой информацией ON на экране блокировки (KeyGuard) без разблокировки телефона. Я подумал, что смогу сделать это с WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
Но каждый раз мое приложение вылетает со следующей ошибкой:
android.view.WindowManager $ BadTokenException: невозможно добавить окно android.view.ViewRootImpl$W@40ec8528 - разрешение запрещено для этого типа окна
Эти сообщения ( здесь , здесь и здесь ) дают одинаковый ответ. Чтобы добавить следующее разрешение в файл манифеста.
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);
У кого-нибудь есть идеи?
PS Тестирую на HTC Desire 620 DS под управлением Android 4.4.2.
источник
если вы используете
apiLevel >= 19
, не используйтеWindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
что вызывает следующую ошибку:
Используйте вместо этого:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
источник
TYPE_SYSTEM_ALERT
нормально работает на Android 5.1 сandroid.permission.SYSTEM_ALERT_WINDOW
предоставленным в манифесте.android.permission.SYSTEM_ALERT_WINDOW
сLayoutParams.TYPE_TOAST
?Я думаю, вам следует различать цель (до и после 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);
источник
Build.VERSION_CODES.O
&WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
. Моя targetSdkVersion - 26Я изо всех сил старался попробовать все примеры, доступные для этой проблемы. Наконец-то я получил ответ на этот вопрос. Я не знаю, насколько он надежен, но мое приложение сейчас не дает сбоев.
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
источник
Для Android API уровня 8.0.0 вы должны использовать
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
вместо
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
или
SYSTEM_ALERT
.источник
попробуйте этот код работать отлично
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);
источник
Поиск
в настройках и включите приложение. Для Android 8 Oreo попробуйте
источник
Во-первых, убедитесь, что у вас есть разрешение на добавление в файл манифеста.
<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'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); }
}
источник
измените свой флаг Windowmanger flag "TYPE_SYSTEM_OVERLAY" на "TYPE_APPLICATION_OVERLAY" в своем проекте, чтобы сделать его совместимым с Android O
WindowManager.LayoutParams.TYPE_PHONE
кWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
источник
Мне удалось наконец отобразить окно на экране блокировки с использованием
TYPE_SYSTEM_OVERLAY
вместоTYPE_KEYGUARD_DIALOG
. Это работает, как ожидалось, и добавляет окно на экран блокировки.Проблема в том, что окно добавляется поверх всего, что возможно.То есть окно даже появится поверх вашей клавиатуры / графического ключа в случае защищенных экранов блокировки. В случае незащищенного экрана блокировки он появится поверх панели уведомлений, если вы откроете его с экрана блокировки.
Для меня это неприемлемо. Я надеюсь, что это поможет любому, кто столкнется с этой проблемой.
источник
TYPE_SYSTEM_OVERLAY
. Мой setOnDismissListener в AlertDialog не вызывается.Я просто добавил простой вид
WindowManager
с помощью следующих шагов:// 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); }
С его помощью вы можете добавить представление в WM.
Проверено в Pie.
источник
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 или выше.
источник
Основная причина отказа в разрешении заключается в том, что у нас нет разрешения на рисование поверх других приложений. Мы должны предоставить разрешение на рисование поверх других приложений, что может быть выполнено с помощью следующего кода
Код запроса для разрешения
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
добавьте это в свой 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); }
Добавьте это также
@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(); } } }
источник
Я изо всех сил , чтобы найти рабочее решение с
ApplicationContext
иTYPE_SYSTEM_ALERT
и нашли запутанным решения, в случае , если вы хотите , диалог должен быть открыт из любой деятельности , даже диалог одноэлементно вы должны использоватьgetApplicationContext()
, и если хотите , диалог должен бытьTYPE_SYSTEM_ALERT
вам потребуются следующие шаги :сначала получите экземпляр диалога с правильной темой, также вам необходимо управлять совместимостью версий, как я сделал в следующем фрагменте:
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext(), R.style.Theme_AppCompat_Light);
После установки заголовка, сообщения и кнопок вы должны построить диалог как:
AlertDialog alert = builder.create();
Теперь
type
основное внимание здесь уделяется, поскольку это причина сбоя, я обработал совместимость следующим образом: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); }
Примечание : если вы используете настраиваемый диалог,
AppCompatDialog
как показано ниже:AppCompatDialog dialog = new AppCompatDialog(getApplicationContext(), R.style.Theme_AppCompat_Light);
вы можете напрямую определить свой тип для
AppCompatDialog
экземпляра следующим образом: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); }
Не забудьте добавить разрешение манифеста:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
источник
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); }
источник
Убедитесь, что
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
он добавлен для версии ОС <Oreo, поскольку она устарела.источник