web-dev-qa-db-fra.com

Comment gérer l'autorisation SYSTEM_ALERT_WINDOW non accordée automatiquement sur certains appareils pré-Marshmallow

J'ai reçu des rapports sur certains appareils Xiaomi (par exemple, Mi 2, exécutant l'API niveau 21) ne montrant pas de superpositions. Mon application cible l'API 23.

Il y a plusieursmessages là-bas à ce sujet. Il semble que les appareils MIUI n'activent pas cette autorisation au moment de l'installation (contrairement aux autres appareils pré-Marshmallow).

Malheureusement, Settings.canDrawOverlays() ne fonctionne que sur Android 23+.

  1. Quelle est la bonne façon de vérifier si cette autorisation n'a pas encore été activée avant Marshmallow?
  2. Existe-t-il une intention de diriger l'utilisateur vers la page de paramètres MUIU appropriée? Peut-être: new Intent("Android.settings.action.MANAGE_OVERLAY_PERMISSION", packageName) mais je n'ai aucun moyen de tester cela.
16
Mark

Il est plus sûr de vérifier si vous disposez de l'autorisation drawOverlays:

@SuppressLint("NewApi")
public static boolean canDrawOverlayViews(Context con){
    if(Build.VERSION.SDK_INT< Build.VERSION_CODES.Lollipop){return true;}
    try { 
        return Settings.canDrawOverlays(con); 
    }
    catch(NoSuchMethodError e){ 
        return canDrawOverlaysUsingReflection(con); 
    }
}


public static boolean canDrawOverlaysUsingReflection(Context context) {

    try {

        AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        Class clazz = AppOpsManager.class;
        Method dispatchMethod = clazz.getMethod("checkOp", new Class[] { int.class, int.class, String.class });
        //AppOpsManager.OP_SYSTEM_ALERT_WINDOW = 24
        int mode = (Integer) dispatchMethod.invoke(manager, new Object[] { 24, Binder.getCallingUid(), context.getApplicationContext().getPackageName() });

        return AppOpsManager.MODE_ALLOWED == mode;

    } catch (Exception e) {  return false;  }

}

Les ROM personnalisées peuvent avoir modifié le système d'exploitation de sorte que Settings.canDrawOverlays () n'est pas disponible. Cela m'est arrivé avec des appareils Xiaomi et l'application s'est bloquée.

Demande d'autorisation:

@SuppressLint("InlinedApi")
public static void requestOverlayDrawPermission(Activity act, int requestCode){
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + act.getPackageName()));
    act.startActivityForResult(intent, requestCode);

}
9
Anonymous

1) sur la pré-API 23, l'autorisation est déjà donnée, car l'utilisateur l'a accordée lors de l'installation.

EDIT: il semble qu'il y ait un bug sur Android 6 (qui --- sera corrigé sur 6.0.1 ), que si l'utilisateur a refusé cette autorisation, l'application se bloquera avec SecurityException. Je ne sais pas comment Google l'a corrigé.

2) De cette façon:

public static void requestSystemAlertPermission(Activity context, Fragment fragment, int requestCode) {
    if (VERSION.SDK_INT < VERSION_CODES.M)
        return;
    final String packageName = context == null ? fragment.getActivity().getPackageName() : context.getPackageName();
    final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + packageName));
    if (fragment != null)
        fragment.startActivityForResult(intent, requestCode);
    else
        context.startActivityForResult(intent, requestCode);
}

Ensuite, dans onActivityResult, vous pouvez vérifier si l'autorisation est donnée ou non, en tant que telle:

@TargetApi(VERSION_CODES.M)
public static boolean isSystemAlertPermissionGranted(Context context) {
    final boolean result = VERSION.SDK_INT < VERSION_CODES.M || Settings.canDrawOverlays(context);
    return result;
}

EDIT: pour le moment, si vous publiez une application sur le Play Store, votre application sera automatiquement accordée avec cette autorisation. Vous pouvez lire à ce sujet ici . Quand j'ai demandé à ce sujet, je pensais que cela faisait partie de Android lui-même, car je pensais que tout ce dont nous avons besoin est de cibler une valeur suffisamment élevée pour targetSdkVersion. Ce que Google m'a écrit ( ici ) est qu'ils voulaient éviter les problèmes sur les applications populaires.

Je suggère de gérer cette autorisation correctement même si vous l'obtiendrez automatiquement.

22
android developer

Voici étape par étape comment gérer cela:

Donnez d'abord l'autorisation ci-dessous dans le fichier manifeste:

<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW" />

Ou

<uses-permission-sdk-23 Android:name="Android.permission.SYSTEM_ALERT_WINDOW" />

Ensuite, gérez le reste des choses en utilisant le code ci-dessous:

 public final static int REQUEST_CODE = 65635;

    public void checkDrawOverlayPermission() {
        /** check if we already  have permission to draw over other apps */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                /** if not construct intent to request permission */
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                /** request permission via start activity for result */
                startActivityForResult(intent, REQUEST_CODE);
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
        /** check if received result code
         is equal our requested code for draw permission  */
        if (requestCode == REQUEST_CODE) {
            // ** if so check once again if we have permission */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (Settings.canDrawOverlays(this)) {
                    // continue here - permission was granted
                    goYourActivity();
                }
            }
        }
    }

Appelez simplement checkDrawOverlayPermission() depuis votre LauncherActivity ou n'importe où selon vos besoins.

Lorsque vous exécutez le projet, une fenêtre s'affiche et vous demande d'activer l'autorisation. Après avoir autorisé l'autorisation, vous pourrez faire quoi que ce soit à ce sujet.

3
0xAliHn

Pour avoir recherché un moment sur le problème dans Xiaomi, Meizu, j'ai trouvé cela. Cela fonctionne parfaitement ..

public static boolean isMiuiFloatWindowOpAllowed(@NonNull Context context) {
    final int version = Build.VERSION.SDK_INT;

    if (version >= 19) {
        return checkOp(context, OP_SYSTEM_ALERT_WINDOW); //See AppOpsManager.OP_SYSTEM_ALERT_WINDOW=24 /*@hide/
    } else {
        return (context.getApplicationInfo().flags & 1<<27) == 1;
    }
}

public static boolean checkOp(Context context, int op, String packageName, int uid) {
    final int version = Build.VERSION.SDK_INT;

    if (version >= 19) {
        AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        try {
            return (AppOpsManager.MODE_ALLOWED == (Integer) ReflectUtils.invokeMethod(manager, "checkOp", op, uid, packageName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        Flog.e("Below API 19 cannot invoke!");
    }
    return false;
}

ReflectUtils.Java

public static Object invokeMethod(@NonNull Object receiver, String methodName, Object... methodArgs) throws Exception {
Class<?>[] argsClass = null;
    if (methodArgs != null && methodArgs.length != 0) {
        int length = methodArgs.length;
        argsClass = new Class[length];
        for (int i=0; i<length; i++) {
            argsClass[i] = getBaseTypeClass(methodArgs[i].getClass());
        }
    }

    Method method = receiver.getClass().getMethod(methodName, argsClass);
    return method.invoke(receiver, methodArgs);
}

Référence

1
Keyur

Vous pouvez vérifier l'autorisation comme ceci:

boolean granted = activity.checkSelfPermission("Android.permission.SYSTEM_ALERT_WINDOW") == PackageManager.PERMISSION_GRANTED;
0
lxknvlk