在unity3d中实现Android 6.0权限

时间:2016-01-27 01:06:18

标签: android unity3d permissions

我已经安装了Android支持库但是在developer.android网站上说要在我的项目中实现它我需要编辑我没有的build.gradle文件,因为它是一个Unity项目。

我创建了一个build.gradle文件,复制了这个网站的内容:http://gradleplease.appspot.com/我将该文件放在我的Unity项目的根目录上,但是当我尝试使用该库时它不起作用

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 个答案:

答案 0 :(得分:25)

您需要Java代码来请求权限,并且您需要从Unity的C#运行时接口到所述Java代码。您需要创建Unity Plugin才能执行此操作。

下面是我创建的用于在运行时授予WRITE_EXTERNAL_STORAGE权限的插件。

您需要这样的项目结构:

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

你需要project.properties和一个虚拟的AndroidManifest.xml才能让Unity包装成一个在Plugins / Android / libs之外的jar

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>

如果PermissionRequestCallback提供了请求的权限枚举作为参数,那会很好,但UnityPlayer.UnitySendMessage只支持单个字符串参数,我决定不实现字符串序列化(使用JSON来做这个将是一个不错的选择)。

答案 1 :(得分:6)

Jason的Unity 5.3.3及其优秀代码(我使用的是5.4)的另一个补充,我将其添加到清单中以阻止Unity在启动时自动询问:

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

答案 2 :(得分:2)

除了Jason Knight的帖子(我用我自己的Unity插件处理运行时权限):

我使用Android studio创建插件。我按照以下网站上的说明进行了完美的工作:http://www.thegamecontriver.com/2015/04/android-plugin-unity-android-studio.html

我还使用了shouldShowRequestPermissionRationale()函数添加了另一个方法,因此如果用户拒绝了该权限并且选中了“不再询问”复选框,我就能隐藏某些UI元素。

答案 3 :(得分:2)

其他答案(尤其是Jason Knight)对我来说非常有帮助,但我必须调整代码才能让它发挥作用,所以我在这里分享这些变化。

如评论中所述,该代码在Android Studio中存在此错误: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); } } } 部分说&#34;片段应该是静态的,以便系统可以重新实例化,而匿名类不是静态&#34;

现在我不是Java专家,所以也许我做错了,但我尝试按照此处的说明调整内容:Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static

大多数情况下,我将Fragment拆分成一个新类,因此它不是一个匿名类。所以现在有两个Java文件:

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

这是主要的插件类,这里是片段:

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

为了完整性,这里是Unity中的C#:

 procedure TMyForm.ApplyFullScreen;
var
  tmpEscale: Extended;
begin
  BorderStyle := TFmxFormBorderStyle.None;
  Left := 0;
  Top := 0;

  tmpEscala := USER_DEFAULT_SCREEN_DPI / GetDeviceCaps(GetDC(0), LOGPIXELSX);

  Height := Round(Screen.Height * tmpEscala);
  Width := Round(Screen.Width * tmpEscala);
end;

答案 4 :(得分:2)

我已经使用Jason Knight的答案来创建完成这项工作的this plugin,整个代码都可以在github存储库上找到。

还有一个统一的打包文件,可以轻松集成。

答案 5 :(得分:1)

如果您可以使用Android Studio并编写java代码,那么......

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
    }

低于那个

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

然后你基本上可以在OnCreate方法中调用 RequestPermissions();

最重要的是你需要 support-compat-25.1.0.aar 文件(或此文件的最新版本)

  

“SDK&gt; extras&gt; Android&gt; m2repository&gt; com&gt; Android&gt; support&gt;   支持-compat的“

support-compat-25.1.0.aar 文件与其他插件文件一起放入Assets / Plugins / Android

多数民众赞成。除此之外,你可以使用hawkwood的清单示例来禁用Unity自己创建的额外权限。