【Unity】调用安卓相机及相册

写在前面

这个sdk在前一段时间就完成了,今天又拿来修改了一下,彻底把权限问题多解决了。经过这次写这个sdk,深深的发现安卓在一次又一次升级用户体验,但是,但是很是折磨程序员啊。一开始我以为这是一个很简单的事,结果被各种权限版本设置搞得都要奔溃了。

开始

其实网上的文章都很详细比如文章1http://www.voidcn.com/article/p-ehdfbrsl-nh.html文章2http://www.voidcn.com/article/p-ehdfbrsl-nh.html )。 其实实现过程都大同小异,我们主要来看我遇到得的问题,当然这些问题都是在6.0以上的版本出现的问题,之前的都没有。

安卓6.0以上获取摄像机权限

在最开始我们在AndroidManifest.xml文件中添加获取相机权限,如下

<uses-permission android:name="android.permission.CAMERA"/>

然后运行程序第一次并没有提示我获得相机权限,然后再点击打开相机闪退,妈耶。然后就查资料说,相机这类权限属于危险级,需要动态请求。所以这里就开始编写动态请求,网上蛮多文章说这个的,大多都需要使用其他什么插件得的。官方给出的代码:

// 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.
    }
}

更多详细的可以看看官网文档https://developer.android.com/training/permissions/requesting)。 这里用的ActivityCompat是使用的Android.support.v4的架包。所以导入unity后我们还需要导入这个架包不然的话会报错。看有文章说这个架包在安卓SDK的目录下可以找到,目录: AndroidSDK/extras/android/m2repository/com/android/support/support-v4

里面有很多版本的,移到unity中,没有用,还是报错!!!可能是我用的版本不对。这里我使用的是之前接GooglePlay的SDK里面的架包。加进去了没有报错了。但是·······

点击打开相机出来了,选择权限框,但是是闪现的。对就是闪现的。

权限闪现

没办法,查了很久,然后看unity官方文档https://docs.unity3d.com/Manual/android-manifest.html ) 中说的是需要再加一个设置到AndroidManifest里,加在Application之间或者Activity即可。

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

因为unity本身是不支持动态权限系统。同时我又看到一个玄学答案,在你的代码里加入这个:

/// <summary>
/// 无用方法,用来打开摄像头权限
/// </summary>
/// <returns></returns>
private IEnumerator OpenCameraPermisson()
{
    yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
    if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) yield break;
    WebCamDevice[] devices = WebCamTexture.devices;
}

这两个是我一起加的好像都有效果,调用webcam时unity会自动提示你打开相机权限。就这样解决了问题。

在动态获取权限那个地方修改了一下写法,不太想使用其他的架包啥的,我的写法是:

//先检测权限
public void OpenTakePhoto() {
    if (Build.VERSION.SDK_INT > 22) {
        if (this.checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

            //先判断有没有权限 ,没有就在这里进行权限的申请
            this.requestPermissions(
                    new String[]{android.Manifest.permission.CAMERA}, CAMERA_OK);
        } else {
            TakePhoto();
        }
    } else {
        TakePhoto();
    }

}

简单点的说就是把ActivityCompat替换成当前的Activity就可以。这样就可以不使用support.v4架包。

相机打开闪退

对的,你没看错。不过这个很快就解决了。这个问题出现的原因主要是由于在Android 7.0以后,用了Content Uri 替换了原本的File Uri,故在targetSdkVersion=24的时候,部分 “Uri.fromFile()“方法就不适用了。 File Uri 与 Content Uri 的区别 - File Uri 对应的是文件本身的存储路径 - Content Uri 对应的是文件在Content Provider的路径 所以在android 7.0 以上,我们就需要将File Uri转换为 Content Uri。这里写一个转化的方法:

/**
 * 转换 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;
        }
    }

}

然后再打开相机的地方做一个选择:

//打开相机
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);
}

存储权限问题

这里我们需要添加存储的权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

我们可以在打开相册的时候检测一下,动态请求一下。

代码

我把java代码写成了Activity类,然后我们直接在unity中的调用传入参数即可:

    //获得照片
    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即可:

<!-- 打开相册及相机 设置为透明 -->
        <!--  android:screenOrientation="portrait" android:configChanges="orientation|screenSize|keyboardHidden|keyboard|"解决相机Activity无法关闭问题 -->
        <activity android:name="com.unity.gallerylibrary.GalleryManager"  android:screenOrientation="portrait" android:configChanges="orientation|screenSize|keyboardHidden|keyboard|">
           <!-- 解决这个问题 Only fullscreen opaque activities can request orientation -->
            <item name="android:windowIsTranslucent">false</item>
            <item name="android:windowDisablePreview">true</item>
        </activity>

其他的看一下源码就可以了。很简单。

java代码

unity 代码

AndroidManifest

示例代码

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
权限:
https://www.jianshu.com/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

3.jpg

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,873评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,483评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,525评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,595评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,018评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,958评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,118评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,873评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,643评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,813评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,293评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,615评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,306评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,170评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,968评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,107评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,894评论 2 278

推荐阅读更多精彩内容