こんにちは。ちゃらいプログラマです。
最近Pluginを作成する機会が増えました。今回はギャラリーに保存されている画像をUnity側で表示/アップロードする処理をご紹介させて頂きます。
まずは、Android側からですが、今まで紹介したPlugin作成のようにUnity側のみで完結できればよかったのですが、残念ながらできませんでした。
大きく以下の流れとなります。
1.Activityからギャラリーを開く処理を投げる
2.ギャラリー側で選択した画像結果を受け取り、Unity側で参照可能なフォルダに保存する
3.保存したファイルをアップロードする
※作業環境は、Unity 2019.2.17f1になります。
Assets/Plugins/Androidフォルダ以下にファイルを作成する
AndroidManifest.xml UnityPluginActivity.java →メイン処理を記述します android-support-v4.jar →Android6以降をサポートする場合に必要
AndroidManifest.xmlの記述
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="※1" > <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ←必ず必要 <application android:banner="@drawable/app_banner" android:debuggable="true" android:icon="@mipmap/app_icon" android:isGame="true" android:label="@string/app_name" android:theme="@style/UnityThemeSelector" > <activity android:name="※1.UnityPluginActivity" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density" android:hardwareAccelerated="true" android:label="@string/app_name" android:launchMode="singleTask" android:screenOrientation="sensorPortrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" /> </intent-filter> <meta-data android:name="unityplayer.UnityActivity" android:value="true" /> </activity> </application> </manifest>
UnityPluginActivity.javaの記述
package ※1; import com.unity3d.player.UnityPlayer; import com.unity3d.player.UnityPlayerActivity; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.app.Activity; import android.content.Intent; import android.content.res.Configuration; import android.content.ContentResolver; import android.content.Context; import android.content.ContentUris; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.Build; import android.net.Uri; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.Manifest; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.FileOutputStream; public class UnityPluginActivity extends UnityPlayerActivity { private final int INTENT_IMAGE = 1000; private final int INTENT_PERMISSION_READ_EXTERNAL_STORAGE = 1001; private static String _gameObjectName = ""; // Unity側へパラメータを渡す際に必要となるゲームオブジェクト名 private static String _saveFilePath = ""; // 保存するファイルパス // UnityPlayer.currentActivity.startActivityForResult( intent, INTENT_IMAGE ); 実行結果 // resultData.getData()で選択した画像のURIを取得できる // resultCode != RESULT_OKの場合は、物理キーの戻るボタンが押された @Override protected void onActivityResult( int requestCode, int resultCode, Intent resultData ) { super.onActivityResult( requestCode, resultCode, resultData ); String savePath = ""; switch( requestCode ) { case INTENT_IMAGE: if( resultCode == RESULT_OK ) { Uri uri = resultData.getData(); String path = getPath( uri ); if( path != "" ) { // 保存ファイル名の生成 savePath = _saveFilePath + "/tmpImage" + path.substring( path.lastIndexOf(".") ); // ファイルコピー try( InputStream inputStream = getContentResolver().openInputStream( uri ); OutputStream outputStream = new FileOutputStream( new File( savePath ) ) ) { byte[] buffer = new byte[1024]; int len = 0; while( (len = inputStream.read( buffer )) != -1 ) { outputStream.write( buffer, 0, len ); } } catch (Exception e) { savePath = e.toString(); } } } // C#側へメッセージ送信 UnityPlayer.UnitySendMessage( _gameObjectName, "CallbackOpen", savePath ); break; default: break; } } // ActivityCompat.requestPermissions 実行結果 @Override public void onRequestPermissionsResult( int requestCode, String permissions[], int[] grantResults ) { switch( requestCode ) { case INTENT_PERMISSION_READ_EXTERNAL_STORAGE: if( grantResults[0] == PackageManager.PERMISSION_GRANTED ) { startActivity(); } break; default: break; } } // C#側から実行される処理 public void open( String gameObjectName, String savePath ) { _gameObjectName = gameObjectName; _saveFilePath = savePath; // ユーザー認証許可がない場合は許可を得るダイアログを表示する if( ContextCompat.checkSelfPermission( UnityPlayer.currentActivity, Manifest.permission.READ_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED ) { ActivityCompat.requestPermissions( UnityPlayer.currentActivity, new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE }, INTENT_PERMISSION_READ_EXTERNAL_STORAGE ); } else { startActivity(); } } // ギャラリーを開く処理 private void startActivity() { UnityPlayer.currentActivity.runOnUiThread( new Runnable() { public void run() { Intent intent = new Intent(); intent.setType( "image/*" ); intent.setAction( Intent.ACTION_OPEN_DOCUMENT ); intent.addCategory( Intent.CATEGORY_OPENABLE ); UnityPlayer.currentActivity.startActivityForResult( intent, INTENT_IMAGE ); } }); } // URIから実際に格納されているファイルパスを得る private String getPath( Uri uri ) { String path = ""; String scheme = uri.getScheme(); String authority = uri.getAuthority(); // SDカード if( "com.android.externalstorage.documents".equals( authority ) ) { String docId = DocumentsContract.getDocumentId( uri ); String[] split = docId.split( ":" ); path = "/Storage/" + split[0] + "/" + split[1]; } // ダウンロード else if( "com.android.providers.downloads.documents".equals( authority ) ) { String docId = DocumentsContract.getDocumentId( uri ); Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId) ); path = getDataColumn( contentUri, null, null ); } // 一般 else if( "com.android.providers.media.documents".equals( authority ) ) { String docId = DocumentsContract.getDocumentId( uri ); String[] split = docId.split( ":" ); Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; String selection = "_id=?"; String[] selectionArgs = { split[1] }; path = getDataColumn( contentUri, selection, selectionArgs ); } return path; } // ファイルパス取得 private String getDataColumn( Uri uri, String selection, String[] selectionArgs ) { Cursor cursor = null; String[] projection = { MediaStore.Files.FileColumns.DATA }; try { cursor = getContentResolver().query( uri, projection, selection, selectionArgs, null ); if( cursor != null && cursor.moveToFirst() ) { return cursor.getString( cursor.getColumnIndexOrThrow(projection[0]) ); } } catch (Exception e) { return ""; } finally { if( cursor != null ) { cursor.close(); } } return ""; } }
※1…Unity側の「PackageName」と同じにしてください
重量な部分は以下になります
1.C#側で「open」メソッドを呼び出す
2.UnityPlayer.UnitySendMessageメソッドでC#側に処理結果を渡す
長くなるので続きます。。。