寫在前面
這個(gè)sdk在前一段時(shí)間就完成了,今天又拿來修改了一下,徹底把權(quán)限問題多解決了。經(jīng)過這次寫這個(gè)sdk,深深的發(fā)現(xiàn)安卓在一次又一次升級(jí)用戶體驗(yàn),但是,但是很是折磨程序員啊。一開始我以為這是一個(gè)很簡(jiǎn)單的事,結(jié)果被各種權(quán)限版本設(shè)置搞得都要奔潰了。

開始
其實(shí)網(wǎng)上的文章都很詳細(xì)比如文章1(http://www.voidcn.com/article/p-ehdfbrsl-nh.html )文章2(http://www.voidcn.com/article/p-ehdfbrsl-nh.html )。 其實(shí)實(shí)現(xiàn)過程都大同小異,我們主要來看我遇到得的問題,當(dāng)然這些問題都是在6.0以上的版本出現(xiàn)的問題,之前的都沒有。
安卓6.0以上獲取攝像機(jī)權(quán)限
在最開始我們?cè)贏ndroidManifest.xml文件中添加獲取相機(jī)權(quán)限,如下
<uses-permission android:name="android.permission.CAMERA"/>
然后運(yùn)行程序第一次并沒有提示我獲得相機(jī)權(quán)限,然后再點(diǎn)擊打開相機(jī)閃退,媽耶。然后就查資料說,相機(jī)這類權(quán)限屬于危險(xiǎn)級(jí),需要?jiǎng)討B(tài)請(qǐng)求。所以這里就開始編寫動(dòng)態(tài)請(qǐng)求,網(wǎng)上蠻多文章說這個(gè)的,大多都需要使用其他什么插件得的。官方給出的代碼:
// Here, thisActivity is the current activity
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.
}
}
更多詳細(xì)的可以看看官網(wǎng)文檔(https://developer.android.com/training/permissions/requesting)。 這里用的ActivityCompat是使用的Android.support.v4的架包。所以導(dǎo)入unity后我們還需要導(dǎo)入這個(gè)架包不然的話會(huì)報(bào)錯(cuò)??从形恼抡f這個(gè)架包在安卓SDK的目錄下可以找到,目錄: AndroidSDK/extras/android/m2repository/com/android/support/support-v4
里面有很多版本的,移到unity中,沒有用,還是報(bào)錯(cuò)?。?!可能是我用的版本不對(duì)。這里我使用的是之前接GooglePlay的SDK里面的架包。加進(jìn)去了沒有報(bào)錯(cuò)了。但是·······

點(diǎn)擊打開相機(jī)出來了,選擇權(quán)限框,但是是閃現(xiàn)的。對(duì)就是閃現(xiàn)的。

沒辦法,查了很久,然后看unity官方文檔(https://docs.unity3d.com/Manual/android-manifest.html ) 中說的是需要再加一個(gè)設(shè)置到AndroidManifest里,加在Application之間或者Activity即可。
<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />
因?yàn)閡nity本身是不支持動(dòng)態(tài)權(quán)限系統(tǒng)。同時(shí)我又看到一個(gè)玄學(xué)答案,在你的代碼里加入這個(gè):
/// <summary>
/// 無用方法,用來打開攝像頭權(quán)限
/// </summary>
/// <returns></returns>
private IEnumerator OpenCameraPermisson()
{
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) yield break;
WebCamDevice[] devices = WebCamTexture.devices;
}
這兩個(gè)是我一起加的好像都有效果,調(diào)用webcam時(shí)unity會(huì)自動(dòng)提示你打開相機(jī)權(quán)限。就這樣解決了問題。
在動(dòng)態(tài)獲取權(quán)限那個(gè)地方修改了一下寫法,不太想使用其他的架包啥的,我的寫法是:
//先檢測(cè)權(quán)限
public void OpenTakePhoto() {
if (Build.VERSION.SDK_INT > 22) {
if (this.checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//先判斷有沒有權(quán)限 ,沒有就在這里進(jìn)行權(quán)限的申請(qǐng)
this.requestPermissions(
new String[]{android.Manifest.permission.CAMERA}, CAMERA_OK);
} else {
TakePhoto();
}
} else {
TakePhoto();
}
}
簡(jiǎn)單點(diǎn)的說就是把ActivityCompat替換成當(dāng)前的Activity就可以。這樣就可以不使用support.v4架包。
相機(jī)打開閃退
對(duì)的,你沒看錯(cuò)。不過這個(gè)很快就解決了。這個(gè)問題出現(xiàn)的原因主要是由于在Android 7.0以后,用了Content Uri 替換了原本的File Uri,故在targetSdkVersion=24的時(shí)候,部分 “Uri.fromFile()“方法就不適用了。 File Uri 與 Content Uri 的區(qū)別 - File Uri 對(duì)應(yīng)的是文件本身的存儲(chǔ)路徑 - Content Uri 對(duì)應(yīng)的是文件在Content Provider的路徑 所以在android 7.0 以上,我們就需要將File Uri轉(zhuǎn)換為 Content Uri。這里寫一個(gè)轉(zhuǎn)化的方法:
/**
* 轉(zhuǎn)換 content:// uri
* 7.0以后使用
*
* @param imageFile
* @return
*/
public Uri getImageContentUri(File imageFile) {
String filePath = imageFile.getAbsolutePath();
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID},
MediaStore.Images.Media.DATA + "=? ",
new String[]{filePath}, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor
.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/images/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (imageFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, filePath);
return getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
然后再打開相機(jī)的地方做一個(gè)選擇:
//打開相機(jī)
private void TakePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
File outFile = new File(UnityUsePicturePath);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getImageContentUri(outFile));
} else {
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(UnityUsePicturePath)));
}
startActivityForResult(intent, PHOTOHRAPH);
}
存儲(chǔ)權(quán)限問題
這里我們需要添加存儲(chǔ)的權(quán)限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
我們可以在打開相冊(cè)的時(shí)候檢測(cè)一下,動(dòng)態(tài)請(qǐng)求一下。
代碼
我把java代碼寫成了Activity類,然后我們直接在unity中的調(diào)用傳入?yún)?shù)即可:
//獲得照片
public void GetPhoto(GetPhotoType photoType, bool isCutPicture = false)
{
var strType = Enum.GetName(typeof(GetPhotoType), photoType);
AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent", unityActivity, gallerySdk);
intentObject.Call<AndroidJavaObject>("putExtra", "type", strType);
intentObject.Call<AndroidJavaObject>("putExtra", "UnityPersistentDataPath", Application.persistentDataPath);
intentObject.Call<AndroidJavaObject>("putExtra", "isCutPicture", isCutPicture);
unityActivity.Call("startActivity", intentObject);
Debug.Log("GetPhoto Android startActivity");
}
這樣我們就可以不需要重新寫AndroidManifest了,只需要在其中申明一下Activity即可:
<!-- 打開相冊(cè)及相機(jī) 設(shè)置為透明 -->
<!-- android:screenOrientation="portrait" android:configChanges="orientation|screenSize|keyboardHidden|keyboard|"解決相機(jī)Activity無法關(guān)閉問題 -->
<activity android:name="com.unity.gallerylibrary.GalleryManager" android:screenOrientation="portrait" android:configChanges="orientation|screenSize|keyboardHidden|keyboard|">
<!-- 解決這個(gè)問題 Only fullscreen opaque activities can request orientation -->
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowDisablePreview">true</item>
</activity>
其他的看一下源碼就可以了。很簡(jiǎn)單。
示例代碼
void OnGUI()
{
if (GUILayout.Button("Take Photo", GUILayout.Width(200), GUILayout.Height(200)))
{
GalleryManager.Instance.GetPhoto(GetPhotoType.Carmera, (texture) => { rawImage.texture = texture; });
}
if (GUILayout.Button("Open Gallery", GUILayout.Width(200), GUILayout.Height(200)))
{
GalleryManager.Instance.GetPhoto(GetPhotoType.Gallery, (texture) => { rawImage.texture = texture; });
}
if (GUILayout.Button("Show Image", GUILayout.Width(200), GUILayout.Height(200)))
{
StartCoroutine(GetImageByPath(Application.persistentDataPath + "/UNITY_GALLERY_PICTUER.png"));
}
}
效果圖



參考文章:
http://www.voidcn.com/article/p-ehdfbrsl-nh.html
https://www.xuanyusong.com/archives/1480
https://blog.csdn.net/dengmengxin/article/details/38702467
權(quán)限:
http://m.itdecent.cn/p/765603bebced
https://blog.csdn.net/beijinghsj/article/details/53581764
https://docs.unity3d.com/Manual/android-manifest.html
https://blog.csdn.net/haojiagou/article/details/80761709
