Je travaille sur une application où je dois afficher une fenêtre avec des informations SUR l'écran de verrouillage (KeyGuard) sans déverrouiller le téléphone. J'ai pensé que je pourrais probablement le faire avec WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
Mais chaque fois que mon application se bloque avec l'erreur suivante:
Android.view.WindowManager $ BadTokenException: impossible d'ajouter une fenêtre Android.view.ViewRootImpl$W@40ec8528 - autorisation refusée pour ce type de fenêtre
Ces messages ( ici , ici et ici ) donnent tous la même réponse. Pour ajouter l'autorisation suivante dans le fichier Manifest.
Android.permission.SYSTEM_ALERT_WINDOW
Solution que j'ai mise en œuvre mais je reçois toujours la même erreur. Une idée de ce que je fais mal?
Voici les autorisations dans mon fichier manifeste:
<?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" />
Et c'est le code que j'utilise pour ajouter la fenêtre à l'écran de verrouillage
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);
Quelqu'un a une idée?
P.S. Je teste mon HTC Desire 620 DS sous Android 4.4.2
Pour des raisons qui devraient être évidentes, les applications ordinaires ne sont pas autorisées à créer des fenêtres arbitraires en haut de l'écran de verrouillage . Que pensez-vous que je pourrais faire si je créais une fenêtre sur votre lockscreen qui pourrait parfaitement imiter le véritable lockscreen, de sorte que vous ne puissiez pas faire la différence?
La raison technique de votre erreur est l'utilisation de l'indicateur TYPE_KEYGUARD_DIALOG
- il nécessite Android.permission.INTERNAL_SYSTEM_WINDOW
, qui est une autorisation au niveau de la signature. Cela signifie que seules les applications signées avec le même certificat que le créateur de l'autorisation peuvent l'utiliser.
Le créateur de Android.permission.INTERNAL_SYSTEM_WINDOW
est le système Android lui-même. Par conséquent, à moins que votre application ne fasse partie du système d'exploitation, vous n'avez aucune chance.
Il existe des moyens bien définis et bien documentés de notifier l'utilisateur des informations depuis l'écran de verrouillage . Vous pouvez créer des notifications personnalisées qui s'affichent sur l'écran de verrouillage et l'utilisateur peut interagir avec elles.
si vous utilisez apiLevel >= 19
, n'utilisez pas
WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
qui obtient l'erreur suivante:
Android.view.WindowManager $ BadTokenException: impossible d'ajouter une fenêtre Android.view.ViewRootImpl$W@40ec8528 - autorisation refusée pour ce type de fenêtre
Utilisez ceci à la place:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
Je suppose que vous devriez différencier la cible (avant et après 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);
J'ai fait de mon mieux pour essayer tous les exemples disponibles pour ce numéro. Enfin, j’ai eu la réponse à cette question. Je ne sais pas combien c’est fiable, mais mon application ne plante pas pour le moment.
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
);
Dans votre fichier manifeste, donnez simplement la permission
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
En plus de cela, vous pouvez également vérifier le niveau de l’API si son> = 23, puis
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);
}
J'espère que ça aide quelqu'un quelque part. Exemple complet https://anam-Android-codes.blogspot.in/?m=1
Pour Android API de 8.0.0, vous devez utiliser
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
au lieu de
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
ou SYSTEM_ALERT
.
essayez ce code fonctionne parfaitement
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);
Chercher
Dessiner sur d'autres applications
dans votre cadre et activer votre application. Pour Android 8 Oreo, essayez
Paramètres> Applications et notifications> Informations sur l'application> Afficher d'autres applications> Activer
changez votre indicateur Windowmanger flag "TYPE_SYSTEM_OVERLAY" en "TYPE_APPLICATION_OVERLAY" dans votre projet pour le rendre compatible avec Android O
WindowManager.LayoutParams.TYPE_PHONE
à WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
J'ai finalement réussi à afficher une fenêtre sur l'écran de verrouillage en utilisant TYPE_SYSTEM_OVERLAY
au lieu de TYPE_KEYGUARD_DIALOG
. Cela fonctionne comme prévu et ajoute la fenêtre sur l'écran de verrouillage.
Le problème avec ceci est que la fenêtre est ajoutée en plus de tout ce qu'il est possible de faire. C'est-à-dire que la fenêtre apparaîtra même en haut de votre clavier/verrouillage de motif en cas d'écrans de verrouillage sécurisés. Dans le cas d'un écran de verrouillage non sécurisé, il apparaîtra en haut du panneau de notification si vous l'ouvrez à partir de l'écran de verrouillage.
Pour moi, c'est inacceptable. J'espère que cela pourra aider les autres personnes confrontées à ce problème.
LayoutParams.TYPE_PHONE est obsolète. J'ai mis à jour mon code comme celui-ci.
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 nécessite un niveau d'api supérieur ou égal à 26.
Premièrement, vous devez vous assurer que vous avez la permission d’ajouter dans le fichier manifeste.
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
Vérifiez si l'application a l'autorisation de dessiner par rapport aux autres applications ou non? Cette autorisation est disponible par défaut pour les API <23. Mais pour les API> 23, vous devez demander l'autorisation au moment de l'exécution.
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);
}
Utilisez ce 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);
}
}