Android の内部ストレージ、外部ストレージのパスを取得する。

カメラで撮影した画像ファイルにアクセスしたい。 フォルダは、大抵以下のフォルダになる。 2番目のフォルダのパスを取得するためにどうするか調べてみました。

/storage/emulated/0/DCIM
/storage/XXXX-XXXX/DCIM

以下の記事を参考にしました。Android7から13対応のコード

Androidでストレージの物理パスを取得する #Android - Qiita

Android13でアプリから画像・音声・動画にアクセスする | CSL

Android13から権限が変更されたようで、READ_EXTERNAL_STORAGEからREAD_MEDIA_XXXXの権限になっています。 したがってバージョンを分けてAndroidManifest.xmlに以下のpermissionを入れます。

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />/>
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
<!--
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:minSdkVersion="33" />
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:minSdkVersion="33" />
-->

サンプルコード

起動時にPermissionsのチェック、リクエストを行います。許可されていなかったら終了。 ストレージのパスは、getVolPaths()でまとめました。

package com.example.mytest1;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.os.Build;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.util.Log;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_EXTERNAL_STORAGE = 101;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.READ_MEDIA_IMAGES,
    };

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String[] permissions,
                                           int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        boolean start = false;
        if (requestCode == REQUEST_EXTERNAL_STORAGE) {
            if (grantResults.length > 0) {
                for (int i = 0; i < permissions.length; i++) {
                    switch (permissions[i]) {
                        case Manifest.permission.READ_EXTERNAL_STORAGE:
                        case Manifest.permission.READ_MEDIA_IMAGES:
                            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                                start = true;
                            }
                            break;
                    }
                }
            }
        }
        if (start = true) {
            Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(MainActivity.this, "Storage Permission Denied", Toast.LENGTH_SHORT).show();
            finish();
        }
    }

    private List<String> getVolPaths(Context context) {
        StorageManager sm = (StorageManager) context.getSystemService(STORAGE_SERVICE);

        List<String> volPaths =  new ArrayList<String>();

        List<StorageVolume> volList = sm.getStorageVolumes();

        for (StorageVolume volume : volList) {
            String vpath = null;
            // Android 11 - 13
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
                vpath = volume.getDirectory().toString();
                volPaths.add(vpath);
                Log.d("DEBUG",  "Android 11 vol Path = " + vpath);
            } else {
                // Android 7 - 10
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
                    try {
                        //noinspection JavaReflectionMemberAccess
                        vpath = (String) volume.getClass().getMethod("getPath").invoke(volume);
                    } catch (Exception e) {
                        vpath = null;
                    }
                    if (vpath == null || "".equals(vpath)) {
                        continue;
                    }
                    volPaths.add(vpath);
                    Log.d("DEBUG",  "Android 7 vol Path = " + vpath);
                }
            }
        }
        return volPaths;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int permission;
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES);
        }else {
            permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
        }
        if (permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    this,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }

        Context context = getApplicationContext();

        List<String> volPaths =  getVolPaths(context);

        int i = 0;
        for (String pathName : volPaths) {
            i += 1;
            Log.d("DEBUG",  "vol No=" + i + " Path = " + pathName);
        }
    }
}

実行結果(logcat)、Device Emulator, Android 9で実行

  D  Android 7 vol Path = /storage/emulated/0
  D  Android 7 vol Path = /storage/0A11-1F0B
  D  vol No=1 Path = /storage/emulated/0
  D  vol No=2 Path = /storage/0A11-1F0B

実行結果(logcat)、Device Emulator, Android 13で実行

 D  Android 11 vol Path = /storage/emulated/0
 D  Android 11 vol Path = /storage/0C07-090B
 D  vol No=1 Path = /storage/emulated/0
 D  vol No=2 Path = /storage/0C07-090B