2022 May 08.
2022 May 05.
端末
android 11 ( APIレベル 30 )
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
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)
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"),
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)
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) {
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) {
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 xmlnsandroid="http://schemas.android.com/apk/res/android"
package="net.sytes.rokkosan.randomplayer">
<uses-permission androidname="android.permission.READ_EXTERNAL_STORAGE" />
<application
androidallowBackup="true"
androidicon="@mipmap/ic_launcher"
androidlabel="@string/app_name"
androidroundIcon="@mipmap/ic_launcher_round"
androidsupportsRtl="true"
androidtheme="@style/Theme.PlayRandomExternalStorageSound">
<activity
androidname=".MainActivity"
androidexported="true">
<intent-filter>
<action androidname="android.intent.action.MAIN" />
<category androidname="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'
}