web-dev-qa-db-fra.com

Android: Impossible d'ajouter une fenêtre. Autorisation refusée pour ce type de fenêtre

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

71
DroidPilot

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.

26
adelphus

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
102
recotone2000

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);
63
Cédric Dufouil

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

47
Anam Ansari

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.

11
Mingquan Liu

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);
7
shakirullah orakzai

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

3
JeffNhan

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.

2
DroidPilot

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.

2
Ashiqul Islam

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);
}

}

2
Abhijeet Sharma