오래전에 File Explorer를 안드로이드 예제로 만든적이 있는데 아직도 많은 분들이 소스 요청을 하는 경우가 있었습니다.
https://swlock.blogspot.com/2017/03/making-file-explorer-in-android.html
위 링크에 있는 구글 drive에 있는 파일이 예전에는 누구나 소스 접근이 가능 했는데 지금은 권한 승락을 해야지만 가져갈 수 있도록 되어있어서 이번 기회에 API 31 Android 12.0(S) 로 새로 작업해서 공유 합니다.
내용은 이전과 같아서 앞에 내용을 이해하시고 소스는 새로 올린 github 에서 다운 받으시기 바랍니다.
https://github.com/donarts/sourcecode/blob/main/android/_01_fileexplorer/fileexplorer.zip
위의 예제를 실행시키면 파일이 전체적으로 나오지 않고 media 파일만 나오게 됩니다.
저장소 액세스 권한 요청
// 안드로이드 R 이상 전체 저장소 엑세스 권한
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
try {
Intent intent = new Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s",
getApplicationContext().getPackageName())));
startActivity(intent);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
}
}
}
androidmanifest.xml에 아래 내용 추가해야 합니다.
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
앱을 실행시키면 아래와 같은 화면이 뜨는데 사용자가 앱에 권한을 허용해 주어야 합니다.
(screenshot에서는 file explorer 라는 앱입니다.)
그리고 나서 다시 앱으로 돌아오면 미디어 파일 허용 액세스 권한을 요청하게 됩니다.
해당 내용은 아래 코드에 의해서 동작됩니다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED ||
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
}
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
}
물론 androidmanifest.xml에도 아래 내용을 추가해 주어야 합니다.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
추가 작업된 내용
파일 선택시 RUN버튼에 선택된 파일 갯수 출력
private void updateRunButtonText() {
List<String> data = getSelectedItems();
int len = data.size();
if (len>0) {
runButton.setText("(" + len + ") RUN");
}else{
runButton.setText("RUN");
}
}
updateRunButtonText() 함수를 호출 해주는 부분은 listview에서 checkbox 버튼을 누를때
public View getView(int position, View convertView, ViewGroup parent) {
View retView = super.getView(position,convertView,parent);
final int pos = position;
final View parView = retView;
CheckBox cb = (CheckBox)retView.findViewById(R.id.checkBox_saved);
cb.setOnClickListener(new View.OnClickListener() {
// 앞쪽의 checkbox 를 클릭했을때
@Override
public void onClick(View view) {
MyListItem item = listDispItems.get(pos);
item.checked = !item.checked;
if( item.checked ) totalSelected++;
item.selectedNumber=totalSelected;
Toast.makeText(MainActivity.this,
"1: Click "+pos+ "th " + item.checked +
" "+totalSelected, Toast.LENGTH_SHORT).show();
printDebug();
updateRunButtonText();
}
});
RUN 버튼 누를때 화면에 문구 출력
CurPath에는 현재 경로가 들어있고, item에는 파일 이름이 들어있게 됩니다.
그것을 화면에 출력하는 부분입니다.
public void onClick(View view) {
StringBuilder sb = new StringBuilder();
sb.append("Count:"+getSelectedItemCount()+"\n");
sb.append("path:"+CurPath+"\n");
List<String> data = getSelectedItems();
for(int i=0;i<data.size();i++){
String item = data.get(i);
// item 에는 file 이름이 들어옵니다.
// 만약 path라면 마지막에 "/" 이 더 붙도록 되어 있습니다.
// CurPath + item : 전체 path 를 가진 파일 이름이 됩니다.
sb.append(item+"\n");
}
new AlertDialog.Builder(MainActivity.this)
.setTitle("[ RUN ]")
.setMessage(sb.toString())
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
}).show();
// 뭔가를 실행후에는 초기화 한다는 예제로 만들어 보았습니다.
setupAdapter();
updateRunButtonText();
}
참고로 Run버튼을 누른 뒤에는 선택된 내용을 모두 초기화하는 setupAdapter()를 넣었습니다.
아래는 개선된 전체 소스입니다.
https://github.com/donarts/sourcecode/blob/main/android/_01_fileexplorer/fileexplorer2.zip