현재 유지보수 중인 앱이 있는데, 이 앱 내에 storage 파일 리스트를 보여주는 외부 라이브러리를 사용중이다.
https://github.com/bartwell/ExFilePicker
GitHub - bartwell/ExFilePicker: Open source Android library. Implement choosing files and directories in your application.
Open source Android library. Implement choosing files and directories in your application. - GitHub - bartwell/ExFilePicker: Open source Android library. Implement choosing files and directories in...
github.com
근데 파일 리스트 중에 특정 파일들이 보여지지 않는 다는 문제가 보고 되었다.
재현을 해보니 100% 재현이 되었고 위 라이브러리 코드를 살펴보다가 원인을 발견하였다.
private void readDirectory(@NonNull File directory) {
...
File[] files = directory.listFiles();
...
}
확인결과 files 로 return 되는 파일 목록이 android 10 과 android 11 버전이 달랐다!
android 는 최근 storage 권한에 많은 변화가 있었는데 manifest 에 아래 코드를 추가하면 android 10 까지는 무시하고 사용할 수 있었다.
android:requestLegacyExternalStorage="true"
근데 앱의 targetSDK 버전을 30(android 11) 으로 올리면서 더 이상 저 무시하는 코드를 사용할 수 없게 된 것이다.
어쨌든 귀찮게 되었지만.. 버그는 버그니까 해결을 해야했다.
찾아보니 크게 2가지 방법으로 해결이 되었다.
Soulution 1
1. manifest 에 권한사용 코드를 넣어준다.
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
android:minSdkVersion="30" />
2. 시스템 -> 설정 -> 개인정보보호(권한..어쩌구) -> 권한관리자 -> 파일 및 미디어 에서 해당 앱의 파일 및 미디어 액세스 권한을 모두 허용으로 체크해준다.
3. 수동으로 하기 귀찮다면 아래 코드를 실행해 바로 권한을 체크하게끔 유도할 수 있다.
@RequiresApi(30)
fun requestStoragePermissionApi30(activity: AppCompatActivity) {
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
activity.startActivityForResult(intent, MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST)
}
4. 이렇게 권한을 부여해주고 나면 listFiles() 에서도 정상적으로 모든 파일을 return 하게 된다.
Solution 2
1. Android system 에서 제공하는 저장소 액세스 프레임워크(SAF) 를 사용한다.
2. 아래 코드로 SAF 를 실행한다.
fun openDocument() {
// 개별파일을 선택해서 가져오고 싶은 경우 (api 19 이상)
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT);
// 폴더내의 파일을 모두 가져오고 싶은 경우 (api 21 이상)
// Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.type = "*/*";
startActivityForResult(intent, 99)
}
2. SAF 를 실행하게 되면 아까처럼 파일이 몇개 빠져서 보인다거나 그런일은 없다.
그냥 system 에서 파일관리하는 앱을 실행시켜 파일을 가져온다고 생각하면 된다.
3. 받아온 데이터는 아래와 같이 처리가 가능하다.
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
if (requestCode == 99 && resultCode == RESULT_OK) {
// 1개의 파일만 선택했을 경우 data 로 내려온다.
resultData?.data?.let { uri ->
Log.d("TEST", "onActivityResult: $uri")
}
// 여러개의 파일을 선택했을 경우 clipData 로 내려온다.
resultData?.clipData.let { clipData ->
if (clipData?.itemCount!! > 0) {
for (index in 0 until clipData.itemCount) {
val item = clipData.getItemAt(index)
Log.d("TEST", "onActivityResult $index: ${item.uri}")
}
}
}
}
}
4. 참고로 android 19 미만에서는 ACTION_PICK 또는 ACTION_GET_CONTENT 를 사용해야 한다고 한다.
출처
https://developer.android.com/guide/topics/providers/document-provider?hl=ko
저장소 액세스 프레임워크를 사용하여 파일 열기 | Android 개발자 | Android Developers
저장소 액세스 프레임워크를 사용하여 파일 열기 Android 4.4(API 수준 19)에는 저장소 액세스 프레임워크(SAF)가 도입되었습니다. SAF는 사용자가 선호하는 문서 저장소 제공자 전체에서 문서, 이미지
developer.android.com
https://github.com/android/storage-samples/tree/master/StorageProvider
GitHub - android/storage-samples: Multiple samples showing the best practices in storage APIs on Android.
Multiple samples showing the best practices in storage APIs on Android. - GitHub - android/storage-samples: Multiple samples showing the best practices in storage APIs on Android.
github.com