Androidで 静止画と動画のピッカーを表示して、単一のコンテンツを選択する方法です。
静止画と動画のピッカーを表示する
val REQUEST_PICK_MEDIA = 10001 // メディアピッカーを表示する val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) intent.type = "image/*" intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*")) startActivityForResult(intent, REQUEST_PICK_MEDIA)
実行すると下図のようにピッカーが表示されます。
選択したコンテンツのURIを取得する
選択したコンテンツのURIを取得することができます。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { val context = context ?: return if (requestCode == REQUEST_PICK_MEDIA) { val mediaUri = data?.data ?: return if (mediaUri.toString().contains("image")) { // 静止画の場合の処理 } else if (mediaUri.toString().contains("video")) { // 動画の場合の処理 } } }
これで、あとは静止画と動画かを振り分けて処理することができま……せん。
この時得られるURIは content://com.android.providers.media.documents/document/video%3A26
のようになっています。URIからファイルパスを得ることはできますが、読み取り権限がないためコンテンツにアクセスすることができません。
「ファイルパスを取得する権限」を取得する
https://stackoverflow.com/questions/32661221/android-cursor-didnt-have-data-column-not-found/33930169#33930169 を参考にして、URIからファイルパスを取得する処理を実装しました。権限がないと下記のようにエラーが発生します。
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=68538, result=-1, data=Intent { dat=content://com.android.providers.media.documents/document/video:27 flg=0x43 }} to activity {APP_NAME}: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/file from pid=27739, uid=10201 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
private var mCropImageUri: Uri? = null private val REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 2000 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { val context = context ?: return if (requestCode == REQUEST_PICK_MEDIA) { val mediaUri = data?.data ?: return if (mediaUri.toString().contains("image")) { //静止画の場合の処理 } else if (mediaUri.toString().contains("video")) { if (context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { mCropImageUri = mediaUri requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) } else { // 動画の場合の処理 } } } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { REQUEST_PERMISSION_READ_EXTERNAL_STORAGE -> { if (mCropImageUri != null && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 権限が取得できたので処理する!!! } else { // 権限が取れなかった場合 } } } }
URIからファイルパスを取得する
https://stackoverflow.com/questions/32661221/android-cursor-didnt-have-data-column-not-found/33930169#33930169 の処理をベースに一部だけ変更しています。これでURIからファイルパスを取得することができます。
@JvmStatic fun getPathFromUri(context: Context, uri: Uri?): String? { val uri = uri ?: return null // DocumentProvider Log.e("uri", "uri:" + uri.authority) if (DocumentsContract.isDocumentUri(context, uri)) { if ("com.android.externalstorage.documents" == uri.authority) { // ExternalStorageProvider val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).toTypedArray() val type = split[0] return if ("primary".equals(type, ignoreCase = true)) { Environment.getExternalStorageDirectory().toString() + "/" + split[1] } else { "/stroage/" + type + "/" + split[1] } } else if ("com.android.providers.downloads.documents" == uri.authority) { // DownloadsProvider val id = DocumentsContract.getDocumentId(uri) val contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)) return getDataColumn(context, contentUri, null, null) } else if ("com.android.providers.media.documents" == uri.authority) { // MediaProvider val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).toTypedArray() val type = split[0] var contentUri: Uri? = null contentUri = MediaStore.Files.getContentUri("external") val selection = "_id=?" val selectionArgs = arrayOf( split[1] ) return getDataColumn(context, contentUri, selection, selectionArgs) } } else if ("content".equals(uri.scheme, ignoreCase = true)) { //MediaStore return getDataColumn(context, uri, null, null) } else if ("file".equals(uri.scheme, ignoreCase = true)) { // File return uri.path } return null } fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String? { var cursor: Cursor? = null val projection = arrayOf( MediaStore.Files.FileColumns.DATA ) try { cursor = context.contentResolver.query( uri, projection, selection, selectionArgs, null) if (cursor != null && cursor.moveToFirst()) { val cindex = cursor.getColumnIndexOrThrow(projection[0]) return cursor.getString(cindex) } } finally { cursor?.close() } return null }
以上で content://com.android.providers.media.documents/document/video%3A26
のようなURIから /storage/emulated/0/DCIM/Camera/20200601_104448.mp4
といったファイルパスを取得することができました。
参考記事
- Android: Let user pick image or video from Gallery - Stack Overflow
- How to pick image or video on Android L? - Stack Overflow
動画確認環境
- Android Studio 4.0
- デバイス: Galaxy A7 (Android 9)