web-dev-qa-db-fra.com

La permission SYSTEM_ALERT_WINDOW sur l'API 26 ne fonctionne pas comme prévu. Autorisation refusée pour le type de fenêtre 2002

J'utilise l'autorisation de superposition pour afficher certaines informations dans mon application. En le faisant fonctionner sur API 23 - 25, cela fonctionne bien (demande de permission, octroi, etc. selon

Impossible d'ajouter la fenêtre Android.view.ViewRoot$W@44da9bc0 - autorisation refusée pour ce type de fenêtre ). (Merci beaucoup à ceph3us!)

J'obtiens une erreur, essentiellement "permission refusée pour le type de fenêtre 2002" lors de l'appel

windowManager.addView(frameLayout, params);

Google a-t-il changé la façon dont la superposition fonctionne? N'importe quelle idée, comment obtenir mon texte en superposition à l'écran dans Android 8 (Oreo), API 26? Merci pour vos idées!

C'est le journal des erreurs:

08-24 16:41:56.730 2615-2615/net.zwittscha.testoverlay E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.zwittscha.testoverlay, PID: 2615
Java.lang.RuntimeException: Unable to start activity ComponentInfo{net.zwittscha.testoverlay/net.zwittscha.testoverlay.MainActivity}: 
                Android.view.WindowManager$BadTokenException: Unable to add window Android.view.ViewRootImpl$W@6fa0089 -- 
                permission denied for window type 2002
 at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2817)
 at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2892)
 at Android.app.ActivityThread.-wrap11(Unknown Source:0)
 at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1593)
 at Android.os.Handler.dispatchMessage(Handler.Java:105)
 at Android.os.Looper.loop(Looper.Java:164)
 at Android.app.ActivityThread.main(ActivityThread.Java:6541)
 at Java.lang.reflect.Method.invoke(Native Method)
 at com.Android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.Java:240)
 at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:767)
            Caused by: Android.view.WindowManager$BadTokenException: 
            Unable to add window Android.view.ViewRootImpl$W@6fa0089 -- 
            permission denied for window type 2002
 at Android.view.ViewRootImpl.setView(ViewRootImpl.Java:789)
 at Android.view.WindowManagerGlobal.addView(WindowManagerGlobal.Java:356)
 at Android.view.WindowManagerImpl.addView(WindowManagerImpl.Java:92)
 at net.zwittscha.testoverlay.MainActivity.createOnTopView(MainActivity.Java:46)
 at net.zwittscha.testoverlay.MainActivity.checkDrawOverlayPermission(MainActivity.Java:66)
 at net.zwittscha.testoverlay.MainActivity.onCreate(MainActivity.Java:28)
 at Android.app.Activity.performCreate(Activity.Java:6975)
 at Android.app.Instrumentation.callActivityOnCreate(Instrumentation.Java:1213)
 at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2770)
 at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2892) 
 at Android.app.ActivityThread.-wrap11(Unknown Source:0) 
 at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1593) 
 at Android.os.Handler.dispatchMessage(Handler.Java:105) 
 at Android.os.Looper.loop(Looper.Java:164) 
 at Android.app.ActivityThread.main(ActivityThread.Java:6541) 
 at Java.lang.reflect.Method.invoke(Native Method) 
 at com.Android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.Java:240) 
 at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:767) 

Dans mon manifeste, j'ai:

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

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

Ceci est mon activité principale:

    public class MainActivity extends AppCompatActivity {

DrawView dv;
FrameLayout frameLayout;
WindowManager windowManager;
LayoutInflater layoutInflater;
/** code to post/handler request for permission */
public final static int REQUEST_CODE = 1234;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    checkDrawOverlayPermission();
}

public void createOnTopView() {

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
            PixelFormat.TRANSLUCENT);
    params.gravity = Gravity.CENTER;

    if (frameLayout == null) frameLayout = new FrameLayout(getApplicationContext());
    if (dv == null) dv = new DrawView(getApplicationContext());

    windowManager = (WindowManager)
            getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    windowManager.addView(frameLayout, params);
    windowManager.addView(dv, params);

    layoutInflater = (LayoutInflater)
            getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // Here is the place where you can inject whatever layout you want.
    layoutInflater.inflate(R.layout.activity_main, frameLayout);
}

public void checkDrawOverlayPermission() {
    /* check if we already  have permission to draw over other apps */
    if (Android.os.Build.VERSION.SDK_INT > 22) {
        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);
        }
        else {
            createOnTopView();
        }
    }
}

@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 && Android.os.Build.VERSION.SDK_INT > 22) {
   /* if so check once again if we have permission */
        if (Settings.canDrawOverlays(this)) {
            createOnTopView();
        }
    }
}
}

Et voici le DrawView:

    public class DrawView extends View {

int w;
int h;
int r;
float screenFactor;
TextPaint startTextPaint;

public DrawView(Context activity) {
    super(activity);
}

@Override
protected void onSizeChanged(int width, int height, int oldw, int oldh) {

        w = width;
        h = height;
        r = w / 2;

    screenFactor = (r / 160f);

    if (startTextPaint == null) startTextPaint = new TextPaint();

    startTextPaint.setTextSize(100);
    startTextPaint.setTextAlign(Paint.Align.CENTER);
    startTextPaint.setTypeface(Typeface.create("Roboto Condensed", Typeface.BOLD));

    super.onSizeChanged(w, h, oldw, oldh);
}

@Override
protected void onDraw(Canvas canvas) {

        startTextPaint.setARGB(255,255,0,0);
        canvas.drawText("Test", w / 2, h / 2, startTextPaint);
}
}
19
tomseitz

Selon la documentation sur Changements de comportement sur Android 8.0 pour les applications ciblant Android 8.0:

Les applications qui utilisent l'autorisation SYSTEM_ALERT_WINDOW ne peuvent plus utiliser les types de fenêtre suivants pour afficher des fenêtres d'alerte au-dessus d'autres applications et fenêtres système:

TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR

Au lieu de cela, les applications doivent utiliser un nouveau type de fenêtre appelé TYPE_APPLICATION_OVERLAY.

Ainsi, votre application pourrait cibler une version inférieure. Dans ce cas, votre fenêtre d'alerte ...

apparaissent toujours sous les fenêtres qui utilisent le type de fenêtre TYPE_APPLICATION_OVERLAY. Si une application cible Android 8.0 (API de niveau 26), l'application utilise le type de fenêtre TYPE_APPLICATION_OVERLAY pour afficher des fenêtres d'alerte.

(cité de la même source)

41
0X0nosugar

Android Oreo (et les versions futures) n'autorisent pas l'utilisation de WindowManager.LayoutParams.TYPE_PHONE car il est déconseillé d'utiliser WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY. 

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,
                    PixelFormat.TRANSLUCENT);
        }else{
            params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT);
        }
}

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);
5
shakirullah orakzai
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
}
else
{
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}

changement uniquement dans ce paramètre du gestionnaire de fenêtres

1
Makvin

Utilisez le code suivant 

int LAYOUT_FLAG = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE;
    mWindowParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
            LAYOUT_FLAG, // Overlay over the other apps.
            LayoutParams.FLAG_NOT_FOCUSABLE    // This flag will enable the back key press.
                    | LayoutParams.FLAG_NOT_TOUCH_MODAL, // make the window to deliver the focus to the BG window.
            PixelFormat.TRANSPARENT);
0
MKY