web-dev-qa-db-ja.com

一部のPre-MarshmallowデバイスでSYSTEM_ALERT_WINDOW権限が自動付与されない場合の対処方法

一部のXiaomiデバイス(Mi 2など、APIレベル21を実行)がオーバーレイを表示しないというレポートを受け取っています。私のアプリはAPI 23をターゲットにしています。

これに関しては severalposts があります。 MIUIデバイスは、インストール時にこの許可を有効にしないようです(他のマシュマロ以前のデバイスとは異なります)。

残念ながら、Settings.canDrawOverlays()はAndroid 23+でのみ機能します。

  1. この許可がマシュマロ以前にまだ有効になっていないかどうかを確認する正しい方法は何ですか?
  2. ユーザーを関連するMUIU設定ページに移動する意図はありますか?多分:new Intent("Android.settings.action.MANAGE_OVERLAY_PERMISSION", packageName)ですが、これをテストする手段がありません。
16
Mark

これを使用すると、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;  }

}

カスタムROMによってOSが変更され、Settings.canDrawOverlays()が使用できなくなる場合があります。これはXiaomiデバイスで私に起こり、アプリがクラッシュしました。

許可の要求:

@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)API 23以前では、インストール時にユーザーが許可したため、許可はすでに付与されています。

編集:Android 6にバグがあるようです(それは が6.1.1で修正されます )、ユーザーがこの権限を拒否した場合、アプリはSecurityExceptionでクラッシュしますが、Googleがどのように修正したかはわかりません。

2)この方法:

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

次に、onActivityResultで、権限が付与されているかどうかを確認できます。

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

編集:とりあえず、Playストアにアプリを公開すると、アプリにはこの権限が自動的に付与されます。あなたはそれについて読むことができます ここ 。私はそれについて尋ねたとき、私はそれがAndroid自体の一部であると思ったので、必要なのはtargetSdkVersionの十分に高い値をターゲットにすることだけだと思っていました。Googleが私に書いたもの( here )は、人気のあるアプリでの問題を回避したかったということです。

自動許可が付与される場合でも、この許可を正しく処理することをお勧めします。

22

これを処理する方法を段階的に次に示します。

まず、マニフェストファイルで以下の権限を付与します。

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

または

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

次に、以下のコードを使用して残りのことを処理します。

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

LauncherActivityまたは必要に応じてどこからでもcheckDrawOverlayPermission()を呼び出すだけです。

プロジェクトを実行すると、ウィンドウが表示され、権限を有効にするように求められます。許可を与えると、これについて何でもできるようになります。

3
0xAliHn

Xiaomi、Meizuの問題についてしばらく検索したところ、これが見つかりました。完全に機能しています。

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

参考

1
Keyur

あなたはこのような許可をチェックすることができます:

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