Embedding permissions for Android 6.0 in unit3d

I installed the Android support library, but the developer.android website says that to implement it in my project, I need to edit the build.gradle file, which I don’t have, because it is a Unity project.

I created a build.gradle file that copies the contents of this website: http://gradleplease.appspot.com/ , and I put this file in the root of my Unity project, but when I try to use the library, it does not work.

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 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
source share
5 answers

You need Java code to request permission, and you need an interface to the specified Java code from Unity C # runtime. To do this, you need to create a Unity Plugin .

Below is the plugin that I created to grant WRITE_EXTERNAL_STORAGE permission at runtime.

You need a project structure, for example:

 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 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 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 

You need project.properties and dummy AndroidManifest.xml so that the Unity package is a bank outside of the 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> 

It would be nice if PermissionRequestCallback provided the requested enum permission as a parameter, but UnityPlayer.UnitySendMessage supports only one string argument, and I decided not to implement string serialization (using JSON would be a good choice for this).

+24
source

Another addition to Jason's excellent code for Unity 5.3.3 and higher (I use 5.4), I added this to the manifest to block Unity from automatically asking at startup:

  <application> <meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" /> </application> 
+4
source

In addition to the Jason Knight post (which I used for my own Unity module to handle runtime permissions):

I used android studio to create a plugin. I followed the directions on the following site and worked perfectly: http://www.thegamecontriver.com/2015/04/android-plugin-unity-android-studio.html

I also added another method using the shouldShowRequestPermissionRationale () function so that I could hide certain user interface elements if the user refused permission and checked the "Do not ask again" checkbox.

+2
source

Well, if you can use Android Studio and write java codes, then ...

 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 } 

And below that

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

Then you can basically call RequestPermissions (); in the OnCreate method

And most importantly, here you will need the support-compat-25.1.0.aar file (or the latest version of this file) from

"SDK> Advanced> Android> m2repository> com> Android> Support> Support-Compat"

Put the support-compat-25.1.0.aar file in Assets / Plugins / Android with your other plugin files

Here it is. in addition to this, you can use the hawkwood manifest example to disable additional permissions as you created your own.

+1
source
The other answers (especially Jason Knight) were very helpful to me, but I had to tweak the code to make it work, so I share these changes here.

As noted in the comments, this code has this error in Android Studio: final Fragment request = new Fragment(); the part says: "Fragments must be static so that they can be re-created by the system, and anonymous classes are not static."

Now I'm not a Java expert, so maybe I did something wrong, but I tried to set things up as described here: Fragments must be static so that they can be re-created by the system, and anonymous classes are not static

Basically, I broke Fragment into a new class, so this is not an anonymous class. So now there are two Java files:

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

This is the main class of the plugin, and here is a 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(); } } 

And for completeness, here is C # in 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 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 UnitySendMessage private void permissionRequestCallbackInternal(string message) { bool permissionGranted = (message == PERMISSION_GRANTED); if (PermissionRequestCallback != null) PermissionRequestCallback(permissionGranted); } } 
+1
source

All Articles