Estoy trabajando en una aplicación donde necesito para mostrar una ventana con algo de información EN la pantalla de bloqueo (bloqueo de teclas) sin desbloquear el teléfono. Pensé que probablemente podría hacerlo con WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
Pero cada vez que mi aplicación falla con el siguiente error:
android.view.WindowManager $ BadTokenException: no se puede agregar la ventana android.view.ViewRootImpl$W@40ec8528 - permiso denegado para este tipo de ventana
Todas estas publicaciones ( aquí , aquí y aquí ) dan la misma respuesta. Para agregar el siguiente permiso en el archivo de manifiesto.
android.permission.SYSTEM_ALERT_WINDOW
Solución que he implementado pero sigo recibiendo el mismo error. ¿Alguna idea de lo que estoy haciendo mal?
Estos son los permisos en mi archivo de manifiesto:
<?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" />
Y este es el código que utilizo para agregar la ventana a la pantalla de bloqueo
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);
¿Alguien tiene alguna idea?
PD: Estoy probando en un HTC Desire 620 DS con Android 4.4.2
fuente
si lo usa
apiLevel >= 19
, no lo useWindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
que obtiene el siguiente error:
Use esto en su lugar:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
fuente
TYPE_SYSTEM_ALERT
bien en Android 5.1 con loandroid.permission.SYSTEM_ALERT_WINDOW
concedido en el manifiesto.android.permission.SYSTEM_ALERT_WINDOW
conLayoutParams.TYPE_TOAST
?Supongo que deberías diferenciar el objetivo (antes y después de 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);
fuente
Build.VERSION_CODES.O
&WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
. Mi targetSdkVersion es 26Hice todo lo posible para probar todos los ejemplos disponibles para este problema. Finalmente obtuve la respuesta para esto, no sé cuánto es confiable, pero mi aplicación no falla ahora.
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 );
En su archivo de manifiesto, simplemente dé el permiso
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Además de esto, también puede verificar el nivel de API si es> = 23 entonces
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); }
Espero que ayude a alguien en alguna parte. Ejemplo completo https://anam-android-codes.blogspot.in/?m=1
fuente
Para el nivel de API de Android 8.0.0, debe usar
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
en vez de
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
o
SYSTEM_ALERT
.fuente
prueba este código funcionando perfectamente
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);
fuente
Buscar
en su configuración y habilite su aplicación. Para Android 8 Oreo, prueba
fuente
En primer lugar, asegúrese de tener permiso para agregar en el archivo de manifiesto.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
¿Compruebe si la aplicación tiene permiso para dibujar sobre otras aplicaciones o no? Este permiso está disponible de forma predeterminada para API <23. Pero para API> 23, debe solicitar el permiso en tiempo de ejecución.
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); }
Utilice este código:
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); }
}
fuente
cambie su bandera Windowmanger flag "TYPE_SYSTEM_OVERLAY" a "TYPE_APPLICATION_OVERLAY" en su proyecto para que sea compatible con Android O
WindowManager.LayoutParams.TYPE_PHONE
aWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
fuente
Me las arreglé para mostrar finalmente una ventana en la pantalla de bloqueo con el uso de en
TYPE_SYSTEM_OVERLAY
lugar deTYPE_KEYGUARD_DIALOG
. Esto funciona como se esperaba y agrega la ventana a la pantalla de bloqueo.El problema con esto es que la ventana se agrega encima de todo lo posible. Es decir, la ventana incluso aparecerá en la parte superior de su teclado / patrón de bloqueo en caso de pantallas de bloqueo seguras. En el caso de una pantalla de bloqueo no segura, aparecerá en la parte superior de la bandeja de notificaciones si la abre desde la pantalla de bloqueo.
Para mí, eso es inaceptable. Espero que esto pueda ayudar a cualquier otra persona que se enfrente a este problema.
fuente
TYPE_SYSTEM_OVERLAY
. Mi setOnDismissListener en AlertDialog no se invoca.Acabo de agregar una vista simple
WindowManager
con los siguientes pasos:// 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); }
Con esto puede agregar una vista a WM.
Probado en Pie.
fuente
LayoutParams.TYPE_PHONE está obsoleto. He actualizado mi código como estos.
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 requiere API nivel 26 o superior.
fuente
La razón principal para denegar el permiso es que no tenemos permiso para dibujar sobre otras aplicaciones, tenemos que proporcionar el permiso para dibujar sobre otras aplicaciones que se pueden hacer con el siguiente código
Solicitar código de permiso
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
agregue esto en su 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); }
Agrega esto también
@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(); } } }
fuente
Luché para encontrar la solución de trabajo con
ApplicationContext
yTYPE_SYSTEM_ALERT
encontré soluciones confusas, en caso de que desee que el cuadro de diálogo se abra desde cualquier actividad, incluso el cuadro de diálogo es un singleton que debe usargetApplicationContext()
, y si lo desea, el cuadro de diálogo debería serTYPE_SYSTEM_ALERT
, necesitará los siguientes pasos :primero obtenga la instancia de diálogo con el tema correcto, también necesita administrar la compatibilidad de la versión como lo hice en mi siguiente fragmento:
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext(), R.style.Theme_AppCompat_Light);
Después de configurar el título, el mensaje y los botones, debe crear el cuadro de diálogo como:
AlertDialog alert = builder.create();
Ahora
type
juega el papel principal aquí, ya que esta es la razón del bloqueo, manejé la compatibilidad de la siguiente manera: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); }
Nota : si está utilizando un cuadro de diálogo personalizado con el
AppCompatDialog
siguiente:AppCompatDialog dialog = new AppCompatDialog(getApplicationContext(), R.style.Theme_AppCompat_Light);
puede definir directamente su tipo en la
AppCompatDialog
instancia de la siguiente manera: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); }
No olvide agregar el permiso de manifiesto:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
fuente
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); }
fuente
Asegúrese de que
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
se agregue para la versión del sistema operativo <Oreo, ya que está obsoleto.fuente