スマホ内のファイルを探し出したいときがあると思います。MediaStoreの画像などはコンテンツプロバイダーで検索できますが、特定のファイル検索はどうするのかと調べてみました。
Android 8.1.0
ファイルパス検索
ファイルパスは
Environment.getExternalStorageDirectory().getPath()
によって取得できましたがAPI 29から残念ながら使えなくなりました。
この方法はフォルダーにあるファイルを一個づつ確認するとてもベタなやり方ですが、ある意味どのようにでも使えます。
また、読出しが可能な範囲に限定され、更にストレージ内を読むのでREAD_EXTERNAL_STORAGEが必要となり、Android 6.0 Runtime Permissionに該当します。Permission checkが必要となります。
検索のやり方は
- java.io.File クラスのメソッド list() を使って
ディレクトリに含まれるファイル、ディレクトリのリストアップをする - subFile.isDirectory()
リストされた物がディレクトリかファイルか確認 - subFile.getName().endsWith(fileType)
ファイルの拡張子がpngかどうか
以下はお決まりの READ_EXTERNAL_STORAGE に関係するpermission checkとなります。
permissionが許可されたならば次のActivityに飛んで検索処理をします。
MainActivity.java
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 |
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private final static int REQUEST_PERMISSION = 1002; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= 23) { // permissionの確認 checkPermission(); } else { setupSearch(); } } private void setupSearch(){ // 検索用のActivityに移動する Intent intent = new Intent(getApplication(), SearchPath.class); startActivity(intent); } // Runtime Permission check private void checkPermission(){ // 既に許可している if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){ setupSearch(); } // 拒否していた場合 else{ requestPermission(); } } // 許可を求める private void requestPermission() { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION); } else { Toast toast = Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT); toast.show(); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,}, REQUEST_PERMISSION); } } // 結果の受け取り @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_PERMISSION) { // 使用が許可された if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { setupSearch(); } else { // それでも拒否された時の対応 Toast toast = Toast.makeText(this, "これ以上なにもできません", Toast.LENGTH_SHORT); toast.show(); } } } } |
ここからが本番です
SearchPath.java
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 |
package com.example.testsearchpath; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.TextView; import java.io.File; import java.util.ArrayList; import java.util.List; public class SearchPath extends AppCompatActivity { // String 型の ArrayList を生成 private List<String> listDirectory = new ArrayList<>(); private TextView testView; private String log = ""; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); testView = findViewById(R.id.text_view); // 外部ストレージのパスを取得する、パスは機種によって異なる File file = Environment.getExternalStorageDirectory(); String storagePath = file.getPath(); // 外部ストレージ内のファイルを検索 // 拡張子がjpgのケース searchImageFiles(storagePath, "jpg"); } private void searchImageFiles( String path, String fileType ){ listDirectory.add(path); int m = 0; int n = 0; String[] fileName; String imgPath = null; // dirList.size() は動的変化あり注意 while(listDirectory.size() > m){ // get(m) リスト内の指定された位置 m にある要素を返す File directory = new File(listDirectory.get(m)); // java.io.File クラスのメソッド list() // 指定したディレクトリに含まれるファイル、ディレクトリの一覧を String 型の配列で返す。 fileName = directory.list(); n = 0; while(fileName.length > n){ File subFile; subFile = new File(directory.getPath() + "/" + fileName[n]); if(subFile.isDirectory()){ Log.d("debug","isDirectory"); listDirectory.add(directory.getPath() + "/" + fileName[n]); imgPath = directory.getPath() + "/" + fileName[n]; }else if(subFile.getName().endsWith(fileType)){ Log.d("debug","getName"); imgPath = directory.getPath() + "/" + fileName[n]; // Log としてパスを出力 putLog(imgPath); } else{ putLog("nothing to do"); } n++; } m++; } } public void putLog(String mess){ log += mess + "\n"; testView.setText(log); } } |
レイアウトです、スクロールできるようにしておきました。
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/text_view" android:layout_margin="20dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> </ScrollView> |
READ_EXTERNAL_STORAGEとAcivityを追加しておきます
AndroidManifest.xml
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 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testsearchpath"> <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/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SearchPath" android:label="@string/app_name" > </activity> </application> </manifest> |
検索する項目を自由に変更して、目的に合わせてファイル検索できますが、
ルート直下だと Permission でブロックされるディレクトリが多いので注意
File file = Environment.getExternalStorageDirectory();
として外部ストレージ内の検索をしています
といってもAndroid4以降はSDカードを指していません、
スマホ内部のメモリ領域の外部ストレージを指しているケースが多いでしょう。
Google developer: Using the External Storage
In this case, the SD card is not part of the external storage and your app cannot access it (the extra storage is intended only for user-provided media that the system scans).
だそうです。