我已经安装了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.
}
}
答案 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自己创建的额外权限。