スマホ内で共有されるMediaStorageにアクセスして画像や音楽ファイルをContentProviderを使って探してみます。
2021.2.1
ContentProvider
ContentProviderは外部アプリに対しテーブルに似たものとしてデータを提供するものです。
Ref: コンテンツ プロバイダの基本
コンテンツ プロバイダ
コンテンツ プロバイダにアクセスする一般的なパターンは、以下のようになります。
- CursorLoader を使用してバックグラウンドで非同期クエリを実行
- UI の Activity/Fragment は、クエリに対して CursorLoader を呼び出し
- ContentResolver を使用して ContentProvider からデータを取得
Ref: コンテンツ プロバイダの基本
ContentResolver.query() を呼び出してデータ情報を取得。
1 2 3 4 5 6 7 |
cursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs.toTypedArray(), // Selection criteria sortOrder // The sort order for the returned rows ) |
Uri: DataBaseでのtable_nameに相当
例えば、画像であれば
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
を渡します。これは
content://media/external/images/media
でありここを検索していくことになります。
projection: DataBaseでのcolumnであり、データの種類
例えば画像では以下のようなcolumnがあります
- MediaStore.Images.Media._ID
- MediaStore.Images.Media.DATA
- MediaStore.Images.Media.DISPLAY_NAME
- MediaStore.Images.Media.TITLE
- MediaStore.Images.Media.DATE_TAKEN
- MediaStore.Images.Media.DATE_ADDED
selection:
DataBaseのWHEREキーワードと同じ
selectionArgs: Selectionと一緒に使用される
selectionの疑問符(?)に入る内容がSelection args、argsの型は String[]
sortOrder: クエリ結果の行が表示される順序を指定
タイムスタンプで昇順・降順に分けるなど
Permissions
ContentProviderを使ってスマホ内のMediaStre内を検索しますのでPermissionが必要になります。
AndroidManifest.xmlに追加
1 |
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
READ_EXTERNAL_STORAGE は Runtime Permission に該当するため、アプリ起動中に許可を得るように設定します。
サンプルコード
端末内のMediaStorageにある画像を検索してみます。
最初に読み出し許可の処理を行います。
MainActivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
//package your.package.name import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager import android.database.Cursor import android.os.Bundle import android.provider.MediaStore import android.widget.TextView import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts.RequestPermission import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { private val requestPermissionLauncher = registerForActivityResult( RequestPermission() ) { isGranted: Boolean -> if (isGranted) { readContent() } else { // それでも拒否された時の対応 val toast = Toast.makeText( this, "これ以上なにもできません", Toast.LENGTH_SHORT ) toast.show() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) { requestPermissionLauncher.launch( Manifest.permission.READ_EXTERNAL_STORAGE ) } else { readContent() } } @SuppressLint("Range") private fun readContent() { val textView = findViewById<TextView>(R.id.text_view) val contentResolver = contentResolver var cursor: Cursor? = null var sb: StringBuilder? = null // 例外を受け取る try { cursor = contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null ) if (cursor != null && cursor.moveToFirst()) { val str = String.format( "MediaStore.Images = %s\n\n", cursor.count ) sb = StringBuilder(str) do { sb.append("ID: ") sb.append( cursor.getString( cursor.getColumnIndex( MediaStore.Images.Media._ID ) ) ) sb.append("\n") sb.append("Title: ") sb.append( cursor.getString( cursor.getColumnIndex( MediaStore.Images.Media.TITLE ) ) ) sb.append("\n") sb.append("Path: ") sb.append( cursor.getString( cursor.getColumnIndex( MediaStore.Images.Media.DATA ) ) ) sb.append("\n\n") } while (cursor.moveToNext()) cursor.close() } } catch (e: Exception) { e.printStackTrace() } finally { if (cursor != null) { cursor.close() } } textView.text = sb } } |
actiity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#dfe" android:padding="5dp" tools:context=".MainActivity"> <TextView android:id="@+id/text_view" android:textColor="#000" android:textSize="20sp" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </ScrollView> |
READ_EXTERNAL_STORAGE とReadContentのAcitivtyを追加
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application ... </application> </manifest> |
実行するとリストが表示されます。
Audio:
音楽ファイルを探すときは、 MediaStore.Images…をMediaStore.Audio.Media.EXTERNAL_CONTENT_URI
に変更すると検索できます。
Video:
動画の場合は、同様にテーブルを MediaStore.Video.Media.EXTERNAL_CONTENT_URI に変えると確認できます。
実際にemulatorがどうなっているかはDevice File Explorerを使うと確認できます。
関連ページ:
Reference:
コンテンツ プロバイダの基本
共有ストレージからメディア ファイルにアクセスする