2022 May 08.
2022 May 05.
端末
ContentResolverでの音声メディア・動画メディアへのクエリで得られるメディア情報
クエリできたメディア
システムにデフォルト内蔵の音声ファイル
システムメモリ(プライマリストレージ)のMoviesディレクトリ・Musicディレクトリに保存したテレビ録画ファイル・ラジオ録音ファイル
SDカード内に作成した(Movies、Music以外のディレクトリ名の)ディレクトリに保存したテレビ録画ファイル・ラジオ録音ファイル
クエリのUriコレクションに指定した値
MediaStore.Files.getContentUri("external")もしくはMediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)。どちらも同じ結果だった
クエリのプロジェクションに指定する値とその内容
MediaStore.MediaColumns._ID
URI取得に利用できるID
MediaStore.Files.FileColumns.MEDIA_TYPE
メディアのタイプ
0:none 1:image 2:audio 3:video 4:playlist 5:subtitle 6:document
MediaStore.Files.FileColumns.DURATION
再生時間(ミリ秒)
MediaStore.Files.FileColumns.TITLE
ファイル名から拡張子を除いた文字列(パスは無い)。ファイル名の先頭から最後の"-"(ハイフン)までの文字列は削除されていた。ファイル名に含まれる"_"(アンダースコア)は半角空白に変換されていた
MediaStore.Files.FileColumns.DISPLAY_NAME
拡張子が付いたファイル名(パスは無い)
MediaStore.Files.FileColumns.RELATIVE_PATH
フルパス文字列から、先頭のパーティション部分("/srorage/external_primary/"、"/srorage/emulated/0/"、"/srorage/xxxx-xxxx/")と末尾のファイル名を取り除いた文字列。先頭に"/"(スラッシュ)は付かない。末尾に"/"(スラッシュ)が付く 。
"/srorage/external_primary"と"/srorage/emulated/0"は同じもの。"/srorage/external_primary"はMediaStore上の表現。"/srorage/emulated/0"はファイルシステム上の表現。
MediaStore.Files.FileColumns.VOLUME_NAME
パーティション文字列("/srorage/external_primary"、"/srorage/xxxx-xxxx")から"/srorage/"を取り除いた文字列(external_primary、xxxx-xxxx)。前後に"/"(スラッシュ)は付かない。プライマリストレージは、"emulated/0"ではなく、"external_primary"となる
MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME
直上のディレクトリ名文字列(親ディレクトリ)。前後に"/"(スラッシュ)は付かない
"_data"
メディアファイルの、ファイルシステム上のフルパス文字列。先頭に"/"(スラッシュ)が付く
(_dataを使わない)フルパス取得は次の文字列の連結。プライマリストレージのボリューム名は、"emulated/0"ではなく、"external_primary"となる
"/srorage/"
MediaStore.Files.FileColumns.VOLUME_NAME
"/"
MediaStore.Files.FileColumns.RELATIVE_PATH
MediaStore.Files.FileColumns.DISPLAY_NAME
式
"/srorage/" + MediaStore.Files.FileColumns.VOLUME_NAME + "/" + MediaStore.Files.FileColumns.RELATIVE_PATH + MediaStore.Files.FileColumns.DISPLAY_NAME
("/srorage/" + MediaStore.Files.FileColumns.VOLUME_NAME + "/" + MediaStore.Files.FileColumns.RELATIVE_PATH + MediaStore.Files.FileColumns.DISPLAY_NAME).replace("external_primary", "emulated/0")
確認実験したコード
MainActivity.kt
ファイル読み込み権限取得コードを省いているので、端末のメニューのアプリ設定で許可しておく必要あり
package net.sytes.rokkosan.randomplayer /* 2022 May 05. Ryuichi Hashimoto. */ import android.content.ContentUris import android.net.Uri import android.os.Build import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.provider.MediaStore import android.util.Log import androidx.annotation.RequiresApi import net.sytes.rokkosan.randomplayer.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // setContentView(R.layout.activity_main) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.buttonPlay.setOnClickListener { getMediaInfo() } } @RequiresApi(Build.VERSION_CODES.Q) private fun getMediaInfo() { val contentResolver = this.contentResolver val proj = arrayOf( MediaStore.MediaColumns._ID, MediaStore.Files.FileColumns.MEDIA_TYPE, MediaStore.Files.FileColumns.DURATION, MediaStore.Files.FileColumns.TITLE, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.RELATIVE_PATH, MediaStore.Files.FileColumns.VOLUME_NAME, MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME, "_data" ) val selection = (MediaStore.Files.FileColumns.MEDIA_TYPE + "=" + MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO + " OR " + MediaStore.Files.FileColumns.MEDIA_TYPE + "=" + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO ) val query = contentResolver.query( MediaStore.Files.getContentUri("external"), //MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), proj, selection, null, MediaStore.Files.FileColumns.DATE_ADDED + " DESC" ) query?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID) val typeColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE) val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DURATION) val titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.TITLE) val displayNameColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME) val relativePathColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.RELATIVE_PATH) val volumeNameColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.VOLUME_NAME) val bucketDisplayNameColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME) val pathColumn = cursor.getColumnIndexOrThrow("_data") while (cursor.moveToNext()) { val mediaType = cursor.getInt(typeColumn) // mediaType 0:none 1:image 2:audio 3:video 4:playlist 5:subtitle 6:document // referrence // https://developer.android.com/reference/android/provider/MediaStore.Files.FileColumns#MEDIA_TYPE // audioとvideoのURIを取得 // referrence // https://developer.android.google.cn/training/data-storage/shared/media?hl=ja var mediaId: Long var mediaUri: Uri var mediaDuration: Long var mediaTitle: String var mediaDisplayName: String var mediaRelativePath: String var mediaVolumeName: String var mediaBucketDisplayName: String var mediaPath: String if (mediaType == 2) { // audio mediaId = cursor.getLong(idColumn) mediaUri = ContentUris.withAppendedId( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mediaId ) mediaDuration = cursor.getLong(durationColumn) mediaTitle = cursor.getString(titleColumn) mediaDisplayName = cursor.getString(displayNameColumn) mediaRelativePath = cursor.getString(relativePathColumn) mediaVolumeName = cursor.getString(volumeNameColumn) mediaBucketDisplayName = cursor.getString(bucketDisplayNameColumn) mediaPath = cursor.getString(pathColumn) } else if (mediaType == 3) { // video mediaId = cursor.getLong(idColumn) mediaUri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaId ) mediaDuration = cursor.getLong(durationColumn) mediaTitle = cursor.getString(titleColumn) mediaDisplayName = cursor.getString(displayNameColumn) mediaRelativePath = cursor.getString(relativePathColumn) mediaVolumeName = cursor.getString(volumeNameColumn) mediaBucketDisplayName = cursor.getString(bucketDisplayNameColumn) mediaPath = cursor.getString(pathColumn) } else { throw java.lang.Exception() } Log.d("MyTag", "mediaUri: $mediaUri") Log.d("MyTag","mediaDuration: " + (mediaDuration/1000).toString() + "Sec") Log.d("MyTag","mediaTitle: $mediaTitle") Log.d("MyTag","mediaDisplayName: $mediaDisplayName") Log.d("MyTag","mediaRelativePath: $mediaRelativePath") Log.d("MyTag","mediaVolumeName: $mediaVolumeName") Log.d("MyTag","mediaBucketDisplayName: $mediaBucketDisplayName") Log.d("MyTag","mediaPath: $mediaPath") } } query?.close() } }
AndroidManifest.xml
READ_EXTERNAL_STORAGE権限を許可する
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.sytes.rokkosan.randomplayer"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.PlayRandomExternalStorageSound"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
build.gradle(Module: app)
plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' } android { compileSdk 31 defaultConfig { applicationId "net.sytes.rokkosan.randomplayer" minSdk 24 targetSdk 31 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } buildFeatures { viewBinding true } } dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' }