android - net - windowmanager layoutparams flag_secure
Android: no se puede agregar la ventana. Permiso denegado para este tipo de ventana (11)
Buscar
Dibuja sobre otras aplicaciones
en su configuración y habilite su aplicación. Para Android 8 Oreo, prueba
Configuración> Aplicaciones y notificaciones> Información de la aplicación> Mostrar sobre otras aplicaciones> Habilitar
Estoy trabajando en una aplicación donde necesito mostrar una ventana con información en la pantalla de bloqueo (KeyGuard) 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
Estas publicaciones ( here , here y here ) 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?
Aquí están 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 uso 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
En primer lugar, asegúrese de tener permiso para agregar en el archivo de manifiesto.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
¿Verifica si la aplicación tiene permiso 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);
}
Use 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);
}
}
Finalmente pude mostrar una ventana en la pantalla de bloqueo con el uso de
TYPE_SYSTEM_OVERLAY
lugar de
TYPE_KEYGUARD_DIALOG
.
Esto funciona como se esperaba y agrega la ventana en la pantalla de bloqueo.
El problema con esto es que la ventana se agrega sobre todo lo que posiblemente puede. Es decir, la ventana incluso aparecerá en la parte superior del bloqueo del teclado / patrón en caso de pantallas de bloqueo seguro. 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 cualquiera que se enfrente a este problema.
Hice 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 se bloquea 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 tu archivo de manifiesto solo da 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 y luego
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
LayoutParams.TYPE_PHONE está en desuso. 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 un nivel de API 26 o superior.
Para el nivel de Android API 8.0.0, debe usar
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
en lugar de
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
o
SYSTEM_ALERT
.
Por razones que deberían ser completamente obvias, las aplicaciones comunes no pueden crear ventanas arbitrarias en la parte superior de la pantalla de bloqueo . ¿Qué crees que podría hacer si creara una ventana en tu pantalla de bloqueo que pudiera imitar perfectamente la pantalla de bloqueo real para que no pudieras notar la diferencia?
La razón técnica de su error es el uso del indicador
TYPE_KEYGUARD_DIALOG
: requiere
android.permission.INTERNAL_SYSTEM_WINDOW
que es un permiso de nivel de firma.
Esto significa que solo las aplicaciones firmadas con el
mismo certificado
que el creador del permiso pueden usarlo.
El creador de
android.permission.INTERNAL_SYSTEM_WINDOW
es el sistema Android en sí mismo, por lo que a menos que su aplicación sea parte del sistema operativo, no tiene ninguna posibilidad.
Existen formas bien definidas y bien documentadas de notificar al usuario la información de la pantalla de bloqueo . Puede crear notificaciones personalizadas que se muestran en la pantalla de bloqueo y el usuario puede interactuar con ellas.
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);
cambie su marca el indicador de Windowmanger "TYPE_SYSTEM_OVERLAY" a "TYPE_APPLICATION_OVERLAY" en su proyecto para que sea compatible con Android O
WindowManager.LayoutParams.TYPE_PHONE
a
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
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);
si usa
apiLevel >= 19
, no use
WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
que obtiene 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
Use esto en su lugar:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL