web-dev-qa-db-fra.com

Implémentation des autorisations Android 6.0 dans unity3d

J'ai installé la bibliothèque de support Android, mais dans le site developer.Android indique que pour l'implémenter dans mon projet, je dois éditer mon fichier build.gradle que je n'ai pas, car il s'agit d'un projet Unity.

J'ai créé un fichier build.gradle en copiant le contenu de ce site: http://gradleplease.appspot.com/ et je l'ai mis à la racine de mon projet Unity, mais lorsque j'essaie d'utiliser la bibliothèque. ça ne marche pas

if (ContextCompat.checkSelfPermission(thisActivity,
                                      Manifest.permission.READ_CONTACTS)
    != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                                                            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                                          new String[]{Manifest.permission.READ_CONTACTS},
        MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}
6
mithrandir

Vous avez besoin de code Java pour demander une autorisation, et vous avez besoin d'une interface dans ledit code Java à partir du runtime C # de Unity. Vous devez créer un Unity Plugin pour le faire.

Vous trouverez ci-dessous le plug-in que j'ai créé pour accorder l'autorisation WRITE_EXTERNAL_STORAGE au moment de l'exécution.

Vous avez besoin d'une structure de projet comme celle-ci:

Plugins/
  Android/
    NoodlePermissionGranter/
      project.properties
      AndroidManifest.xml
      NoodlePermissionGranter.cs
      libs/
        NoodlePermissionGranter.jar

NoodlePermissionGranter.cs:

      ///////////////////////////////////////////////////////////
     ///////////////// NoodlePermissionGranter /////////////////
    /// Implements runtime granting of Android permissions. ///
   /// This is necessary for Android M (6.0) and above. //////
  ///////////////////////////////////////////////////////////   
 //////////////////// Noodlecake Studios ///////////////////
///////////////////////////////////////////////////////////

using UnityEngine;
using System.Collections;
using System;

public class NoodlePermissionGranter : MonoBehaviour {

    // subscribe to this callback to see if your permission was granted.
    public static Action<bool> PermissionRequestCallback;

    // for now, it only implements the external storage permission
    public enum NoodleAndroidPermission
    {
        WRITE_EXTERNAL_STORAGE
    }
    public static void GrantPermission(NoodleAndroidPermission permission) 
    {
        if (!initialized)
            initialize ();

        noodlePermissionGranterClass.CallStatic ("grantPermission", activity, (int)permission);
    }







      //////////////////////////////
     /// Initialization Stuff /////
    //////////////////////////////

    // it's a singleton, but no one needs to know about it. hush hush. dont touch me.
    private static NoodlePermissionGranter instance;
    private static bool initialized = false;

    public void Awake()
    {
        // instance is also set in initialize.
        // having it here ensures this thing doesnt break
        // if you added this component to the scene manually
        instance = this;
        DontDestroyOnLoad (this.gameObject);
        // object name must match UnitySendMessage call in NoodlePermissionGranter.Java
        if (name != NOODLE_PERMISSION_GRANTER)
            name = NOODLE_PERMISSION_GRANTER;
    }


    private static void initialize()
    {
        // runs once when you call GrantPermission

        // add object to scene
        if (instance == null) {
            GameObject go = new GameObject();
            // instance will also be set in awake, but having it here as well seems extra safe
            instance = go.AddComponent<NoodlePermissionGranter>();
            // object name must match UnitySendMessage call in NoodlePermissionGranter.Java
            go.name = NOODLE_PERMISSION_GRANTER; 
        }

        // get the jni stuff. we need the activty class and the NoodlePermissionGranter class.
        noodlePermissionGranterClass = new AndroidJavaClass("com.noodlecake.unityplugins.NoodlePermissionGranter");
        AndroidJavaClass u3d = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
        activity = u3d.GetStatic<AndroidJavaObject> ("currentActivity");

        initialized = true;
    }







      ///////////////////
     //// JNI Stuff ////
    ///////////////////

    static AndroidJavaClass noodlePermissionGranterClass;
    static AndroidJavaObject activity;
    private const string WRITE_EXTERNAL_STORAGE="WRITE_EXTERNAL_STORAGE";
    private const string PERMISSION_GRANTED = "PERMISSION_GRANTED"; // must match NoodlePermissionGranter.Java
    private const string PERMISSION_DENIED = "PERMISSION_DENIED"; // must match NoodlePermissionGranter.Java
    private const string NOODLE_PERMISSION_GRANTER = "NoodlePermissionGranter"; // must match UnitySendMessage call in NoodlePermissionGranter.Java

    private void permissionRequestCallbackInternal(string message)
    {
        // were calling this method from the Java side.
        // the method name and gameobject must match NoodlePermissionGranter.Java's UnitySendMessage
        bool permissionGranted = (message == PERMISSION_GRANTED);
        if (PermissionRequestCallback != null)
            PermissionRequestCallback (permissionGranted);
    }
}

NoodlePermissionGranter.Java:

package com.noodlecake.unityplugins;


      ///////////////////////////////////////////////////////////
     ///////////////// NoodlePermissionGranter /////////////////
    /// Implements runtime granting of Android permissions. ///
   /// This is necessary for Android M (6.0) and above. //////
  ///////////////////////////////////////////////////////////   
 //////////////////// Noodlecake Studios ///////////////////
///////////////////////////////////////////////////////////

import Android.Manifest;
import Android.os.Build;
import Android.app.Activity;
import Android.app.Fragment;
import Android.app.FragmentManager;
import Android.app.FragmentTransaction;
import Android.util.Log;
import Android.content.pm.PackageManager;
import Java.io.File;
import com.unity3d.player.UnityPlayerActivity;
import com.unity3d.player.UnityPlayer;

public class NoodlePermissionGranter
{
    // Only implements WRITE_EXTERNAL_STORAGE so far.
    // Implement the rest by matching the enum in NoodlePermissionGranter.cs
    // to the getPermissionStringFromEnumInt below.

    private final static String UNITY_CALLBACK_GAMEOBJECT_NAME = "NoodlePermissionGranter";
    private final static String UNITY_CALLBACK_METHOD_NAME = "permissionRequestCallbackInternal";
    private final static String PERMISSION_GRANTED = "PERMISSION_GRANTED"; // this will be an arg to the above method
    private final static String PERMISSION_DENIED = "PERMISSION_DENIED";

    public static String getPermissionStringFromEnumInt(int permissionEnum) throws Exception
    {
        switch (permissionEnum)
        {
            case 0:
                return Manifest.permission.WRITE_EXTERNAL_STORAGE;
            // "and the rest is still unwritten" - Natasha Bedingfield
        }
        Log.e("NoodlePermissionGranter", "Error. Unknown permissionEnum " + permissionEnum);
        throw new Exception(String.format("Error. Unknown permissionEnum %d",permissionEnum));
    }

    public static void grantPermission(Activity currentActivity, int permissionEnum)
    {
        // permission enum must match ordering in NoodlePermissionGranter.cs
        final Activity act = currentActivity;
        Log.i("NoodlePermissionGranter","grantPermission " + permissionEnum) ;
        if (Build.VERSION.SDK_INT < 23) {
            Log.i("NoodlePermissionGranter","Build.VERSION.SDK_INT < 23 (" + Build.VERSION.SDK_INT+")");
            UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
            return;
        }

        try
        {
            final int PERMISSIONS_REQUEST_CODE = permissionEnum;
            final String permissionFromEnumInt = getPermissionStringFromEnumInt(permissionEnum);
            if (currentActivity.checkCallingOrSelfPermission(permissionFromEnumInt) == PackageManager.PERMISSION_GRANTED) {
                Log.i("NoodlePermissionGranter", "already granted");
                UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
                return;
            }

            final FragmentManager fragmentManager = currentActivity.getFragmentManager();
            final Fragment request = new Fragment() {

                @Override public void onStart()
                {
                    super.onStart();
                    Log.i("NoodlePermissionGranter","fragment start");
                    String[] permissionsToRequest = new String [] {permissionFromEnumInt};
                    Log.i("NoodlePermissionGranter","fragment start " + permissionsToRequest[0]);
                    requestPermissions(permissionsToRequest, PERMISSIONS_REQUEST_CODE);
                }

                @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
                {
                    Log.i("NoodlePermissionGranter", "onRequestPermissionsResult");
                    if (requestCode != PERMISSIONS_REQUEST_CODE)
                        return;

                    if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        // permission was granted, yay! Do the
                        // contacts-related task you need to do.
                        Log.i("NoodlePermissionGranter", PERMISSION_GRANTED);
                        UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
                    } else {

                        // permission denied, boo! Disable the
                        // functionality that depends on this permission.
                        Log.i("NoodlePermissionGranter",PERMISSION_DENIED);
                        UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_DENIED);
                    }


                    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                    fragmentTransaction.remove(this);
                    fragmentTransaction.commit();

                    // shouldBeOkayToStartTheApplicationNow();
                }
            };

            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.add(0, request);
            fragmentTransaction.commit();
        }
        catch(Exception error)
        {
            Log.w("[NoodlePermissionGranter]", String.format("Unable to request permission: %s", error.getMessage()));
            UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_DENIED);
        }
    }

}

BuildNoodlePermissionGranter.sh

export Java_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
ClASSPATH=$UNITY_ROOT"/Unity.app/Contents/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar"

javac NoodlePermissionGranter.Java -bootclasspath $Android_SDK_ROOT/platforms/Android-23/Android.jar -classpath $ClASSPATH -d .
javap -s com.noodlecake.unityplugins.NoodlePermissionGranter
jar cvfM NoodlePermissionGranter.jar com/
rm -rf com

Vous avez besoin de project.properties et d’un mannequin AndroidManifest.xml afin de permettre à Unity de créer un fichier jar en dehors de Plugins/Android/libs.

project.properties

target=Android-9
Android.library=true

AndroidManifest.xml

<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
    package="com.noodlecake.unityplugins.noodlepermissiongranter"
    Android:versionCode="1"
    Android:versionName="1.0" >
    <uses-sdk Android:targetSdkVersion="23" />
</manifest>

Ce serait bien si PermissionRequestCallback fournissait l'énumération d'autorisation demandée en tant que paramètre, mais UnityPlayer.UnitySendMessage ne prend en charge qu'un seul argument de chaîne et j'ai décidé de ne pas implémenter la sérialisation de chaîne (utiliser JSON pour ce faire serait un bon choix).

25
Jason Knight

Un autre ajout à l'excellent code de Jason pour Unity 5.3.3 et plus récent (j'utilise 5.4), j'ai ajouté ceci au manifeste pour empêcher Unity de demander automatiquement au lancement:

 <application>
     <meta-data Android:name="unityplayer.SkipPermissionsDialog" Android:value="true" />
 </application>
6
hawkwood

En plus du message de Jason Knight (que j'ai utilisé pour mon propre plug-in Unity pour gérer les autorisations d'exécution):

J'ai utilisé Android Studio pour créer un plugin. J'ai suivi les instructions sur le site suivant et a parfaitement fonctionné: http://www.thegamecontriver.com/2015/04/Android-plugin-unity-Android-studio.html

J'ai également ajouté une autre méthode à l'aide de la fonction shouldShowRequestPermissionRationale (), ce qui me permet de masquer certains éléments de l'interface utilisateur si l'utilisateur refuse l'autorisation et coche la case "Ne plus demander".

2
Jasper

J'ai utilisé la réponse de Jason Knight pour créer ce plugin qui fait le travail, tout le code est disponible sur le dépôt github.

Il existe également un fichier de package unit pour une intégration facile.

2
Jimmar

Les autres réponses (surtout celle de Jason Knight) m'ont été d'une grande aide, mais j'ai dû ajuster le code pour qu'il fonctionne, alors je partage ces modifications ici.

Comme indiqué dans les commentaires, ce code présente cette erreur dans Android Studio: la partie final Fragment request = new Fragment(); dit: "Les fragments doivent être statiques de manière à pouvoir être ré-instanciés par le système, et les classes anonymes ne le sont pas."

Maintenant, je ne suis pas un expert de Java, alors j'ai peut-être mal agi, mais j'ai essayé d'ajuster les choses comme expliqué ici: Les fragments doivent être statiques pour pouvoir être ré-instanciés par le système et les classes anonymes ne le sont pas

La plupart du temps, j'ai divisé le fragment dans une nouvelle classe, de sorte que ce ne soit pas une classe anonyme. Alors maintenant, il y a deux fichiers Java:

package com.synapse.unityplugins;

import Android.Manifest;
import Android.os.Build;
import Android.app.Activity;
import Android.app.Fragment;
import Android.app.FragmentManager;
import Android.app.FragmentTransaction;
import Android.util.Log;
import Android.content.pm.PackageManager;

import com.unity3d.player.UnityPlayer;

public class PermissionGranter {
    public final static String UNITY_CALLBACK_GAMEOBJECT_NAME = "SynapsePlugin_listener";
    public final static String UNITY_CALLBACK_METHOD_NAME = "permissionRequestCallbackInternal";
    public final static String PERMISSION_GRANTED = "PERMISSION_GRANTED";
    public final static String PERMISSION_DENIED = "PERMISSION_DENIED";

    // only implemented WRITE_EXTERNAL_STORAGE so far
    public static String getPermissionStringFromEnumInt(int permissionEnum) throws Exception
    {
        switch (permissionEnum) {
            case 0:
                return Manifest.permission.WRITE_EXTERNAL_STORAGE;
            // "and the rest is still unwritten" - Natasha Bedingfield
        }
        Log.e("PermissionGranter", "Error. Unknown permissionEnum " + permissionEnum);
        throw new Exception(String.format("Error. Unknown permissionEnum %d",permissionEnum));
    }

    public static void grantPermission(int permissionEnum)
    {
        final Activity act = UnityPlayer.currentActivity;
        Log.i("PermissionGranter","grantPermission " + permissionEnum) ;
        if (Build.VERSION.SDK_INT < 23) {
            Log.i("PermissionGranter","Build.VERSION.SDK_INT < 23 (" + Build.VERSION.SDK_INT+")");
            UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
            return;
        }

        try {
            final String permissionFromEnumInt = getPermissionStringFromEnumInt(permissionEnum);
            if (act.checkCallingOrSelfPermission(permissionFromEnumInt) == PackageManager.PERMISSION_GRANTED) {
                Log.i("PermissionGranter", "already granted");
                UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
                return;
            }

            final Fragment request = PermissionRequestFragment.newInstance(permissionEnum);
            final FragmentManager fragmentManager = act.getFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.add(0, request);
            fragmentTransaction.commit();
        }
        catch(Exception error)
        {
            Log.w("PermissionGranter", String.format("Unable to request permission: %s", error.getMessage()));
            UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_DENIED);
        }
    }
}

C'est la classe du plugin principal, et voici le fragment:

package com.synapse.unityplugins;

import Android.app.Activity;
import Android.app.Fragment;
import Android.app.FragmentManager;
import Android.app.FragmentTransaction;
import Android.content.pm.PackageManager;
import Android.os.Bundle;
import Android.util.Log;

import com.unity3d.player.UnityPlayer;

public class PermissionRequestFragment extends Fragment {
    public static PermissionRequestFragment newInstance(int permissionEnum) {
        PermissionRequestFragment frag = new PermissionRequestFragment();
        Bundle args = new Bundle();
        args.putInt("requested", permissionEnum);
        frag.setArguments(args);
        return frag;
    }

    @Override
    public void onStart() {
        super.onStart();
        int permissionEnum = getArguments().getInt("requested");
        final int PERMISSIONS_REQUEST_CODE = permissionEnum;

        try {
            final String permissionFromEnumInt = PermissionGranter.getPermissionStringFromEnumInt(permissionEnum);
            String[] permissionsToRequest = new String[]{permissionFromEnumInt};
            Log.i("PermissionGranter", "fragment start " + permissionsToRequest[0]);
            requestPermissions(permissionsToRequest, PERMISSIONS_REQUEST_CODE);
        } catch (Exception error) {
            Log.w("PermissionGranter", String.format("Unable to request permission: %s", error.getMessage()));
            UnityPlayer.UnitySendMessage(PermissionGranter.UNITY_CALLBACK_GAMEOBJECT_NAME,
                    PermissionGranter.UNITY_CALLBACK_METHOD_NAME, PermissionGranter.PERMISSION_DENIED);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Log.i("PermissionGranter", "onRequestPermissionsResult");
        int permissionEnum = getArguments().getInt("requested");
        final int PERMISSIONS_REQUEST_CODE = permissionEnum;
        if (requestCode != PERMISSIONS_REQUEST_CODE)
            return;

        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // permission was granted, yay! Do the task now
            Log.i("PermissionGranter", PermissionGranter.PERMISSION_GRANTED);
            UnityPlayer.UnitySendMessage(PermissionGranter.UNITY_CALLBACK_GAMEOBJECT_NAME,
                    PermissionGranter.UNITY_CALLBACK_METHOD_NAME, PermissionGranter.PERMISSION_GRANTED);
        } else {

            // permission denied, boo! Disable the functionality that needed it
            Log.i("PermissionGranter", PermissionGranter.PERMISSION_DENIED);
            UnityPlayer.UnitySendMessage(PermissionGranter.UNITY_CALLBACK_GAMEOBJECT_NAME,
                    PermissionGranter.UNITY_CALLBACK_METHOD_NAME, PermissionGranter.PERMISSION_DENIED);
        }

        final Activity act = UnityPlayer.currentActivity;
        final FragmentManager fragmentManager = act.getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.remove(this);
        fragmentTransaction.commit();
    }
}

Et pour être complet, voici le C # dans Unity:

using UnityEngine;
using System.Collections;
using System;

public class SynapsePlugin : MonoBehaviour {

    // subscribe to this callback to see if your permission was granted.
    public static Action<bool> PermissionRequestCallback;

    // for now, it only implements the external storage permission
    public enum AndroidPermission {
        WRITE_EXTERNAL_STORAGE
    }

    public static void GrantPermission(AndroidPermission permission) {
        if (!initialized)
            initialize ();

        PermissionGranterClass.CallStatic ("grantPermission", (int)permission);
    }







    //////////////////////////////
    /// Initialization Stuff /////
    //////////////////////////////

    private const string PLUGIN_LISTENER_NAME = "SynapsePlugin_listener"; // must match UnitySendMessage call in Java

    // it's a singleton, but no one needs to know about it. hush hush. dont touch me.
    private static SynapsePlugin instance;
    private static bool initialized = false;

    static AndroidJavaClass PermissionGranterClass;
    private const string PERMISSION_GRANTED = "PERMISSION_GRANTED"; // must match Java
    private const string PERMISSION_DENIED = "PERMISSION_DENIED"; // must match Java

    // runs automatically when making calls, or can pre-init manually
    public static void initialize() {

        // add object to scene
        if (instance == null) {
            GameObject go = new GameObject();
            go.name = PLUGIN_LISTENER_NAME;

            // instance will also be set in awake, but having it here as well seems extra safe
            instance = go.AddComponent<SynapsePlugin>();
        }

        // get the jni stuff
        new AndroidJavaClass("com.synapse.unityplugins.PermissionGranter");

        initialized = true;
    }

    public void Awake() {
        DontDestroyOnLoad (this.gameObject);

        // instance is also set in initialize.
        // having it here ensures this thing doesnt break
        // if you added this component to the scene manually
        instance = this;

        if (name != PLUGIN_LISTENER_NAME)
            name = PLUGIN_LISTENER_NAME;
    }

    // we're calling this method from the Java side.
    // the method name and gameobject must match Java's UnitySendMessage
    private void permissionRequestCallbackInternal(string message) {
        bool permissionGranted = (message == PERMISSION_GRANTED);
        if (PermissionRequestCallback != null)
            PermissionRequestCallback(permissionGranted);
    }
}
2
jhocking

Eh bien, si vous pouvez utiliser Android Studio et écrire des codes Java, alors ...

public interface PermissionAction {
    int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL = 1;
    //You can add other integers too if you want
}

public void RequestPermissions(){

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // Here, thisActivity is the current activity
        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    PermissionAction.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL);
        }
        //More if statements for other permissions
    }

Et en dessous

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PermissionAction.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the contacts-related task you need to do.
                Toast.makeText(this, "WRITE SUCCESS", Toast.LENGTH_SHORT).show();

            } else {
                // permission denied, boo! Disable the functionality that depends on this permission.
                Toast.makeText(this, "Permission denied to write your External storage", Toast.LENGTH_LONG).show();

            }
            return;
        }
        //More Cases for other permissions
    }
}

Ensuite, vous pouvez essentiellement appeler RequestPermissions (); dans la méthode OnCreate

Et la chose la plus importante ici vous avez besoin support-compat-25.1.0.aar fichier (ou la dernière version de ce fichier) de votre 

"SDK> extras> Android> m2repository> com> Android> support> Support-compat"

Placez le fichier support-compat-25.1.0.aar dans Assets/Plugins/Android avec vos autres fichiers de plug-in.

C'est tout. En plus de cela, vous pouvez utiliser l'exemple manifeste de hawkwood pour désactiver les autorisations supplémentaires de l'unité, puisque vous avez créé les vôtres.

1
TheCoffee