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