android android-overlay

android - Cómo manejar el permiso SYSTEM_ALERT_WINDOW que no se otorga automáticamente en algunos dispositivos anteriores a Marshmallow



android-overlay (5)

1) en pre-API 23, el permiso ya está dado, porque el usuario lo otorgó en la instalación.

EDITAR: parece que hay un error en Android 6 (que se solucionará en 6.0.1 ), que si el usuario ha denegado este permiso, la aplicación se bloqueará con SecurityException. Sin embargo, no tengo idea de cómo lo solucionó Google.

2) De esta manera:

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

Luego, en onActivityResult, puede verificar si el permiso se otorga o no, como tal:

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

EDITAR: por el momento, si publica una aplicación en Play Store, su aplicación se otorgará automáticamente con este permiso. Puedes leer sobre esto here . Cuando pregunté al respecto, pensé que era parte de Android, ya que pensé que todo lo que necesitamos es apuntar a un valor lo suficientemente alto para targetSdkVersion. Lo que Google me escribió ( here ) es que querían evitar problemas en aplicaciones populares.

Sugiero que maneje este permiso correctamente incluso si lo otorga automáticamente.

He estado recibiendo informes de algunos dispositivos Xiaomi (por ejemplo, Mi 2, ejecutando API nivel 21) que no muestran superposiciones. Mi aplicación se dirige a la API 23.

Hay several posts por ahí con respecto a esto. Parece que los dispositivos MIUI no habilitan este permiso en el momento de la instalación (a diferencia de otros dispositivos anteriores a Marshmallow).

Desafortunadamente, Settings.canDrawOverlays() solo funciona en Android 23+.

  1. ¿Cuál es la forma correcta de verificar si este permiso aún no se ha habilitado antes de Marshmallow?
  2. ¿Hay alguna intención de llevar al usuario a la página de configuración de MUIU correspondiente? Tal vez: new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION", packageName) pero no tengo medios para probar esto.

Aquí está el paso a paso cómo manejar esto:

Al principio, dé permiso a continuación en el archivo de manifiesto:

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

O

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

Luego maneje el resto de las cosas usando el siguiente código:

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

Simplemente llame a checkDrawOverlayPermission() desde su LauncherActivity o desde cualquier lugar según sus requisitos.

Cuando ejecute el proyecto, verá una ventana y le pedirá que habilite el permiso. Después de permitir el permiso, podrá hacer algo al respecto.


Comprobar si tiene el permiso drawOverlays es más seguro usando esto:

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

Las ROM personalizadas pueden haber alterado el sistema operativo para que Settings.canDrawOverlays () no esté disponible. Esto me sucedió con los dispositivos Xiaomi y la aplicación se bloqueó.

Solicitando el permiso:

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


Para buscar un momento sobre el problema en Xiaomi, Meizu he encontrado esto. Está funcionando perfectamente ...

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

Reference


Puede verificar permisos como este:

boolean granted = activity.checkSelfPermission("android.permission.SYSTEM_ALERT_WINDOW") == PackageManager.PERMISSION_GRANTED;