반응형
Android 10(TargetSDK 29) 이후 버전에서 Scoped Storage 적용으로, 어떤 파일을 삭제할때 사용자로부터 허락을 받아야 할 수도 있습니다. 예외로, 앱이 추가한 미디어 파일은 허락없이 삭제가 가능합니다. (TargetSDK 29 미만에서 앱이 미디어 파일을 삭제할때는 WRITE_EXTERNAL_STORAGE 권한을 갖고 있어야 합니다.)
// TargetSDK 29 미만에서 앱 삭제
File file = new File("파일 절대(전체) 경로와 파일 이름");
if(file.delete()) {
// 삭제 성공
} else {
// 삭제 실패
}
Android 10(TargetSDK 29) 이후 버전부터는 MediaStore를 이용하여 삭제를 합니다. 해당 방법을 소개한 곳은 많았으나 실행을 하면 "Could not execute method for android :onClick" exception이 발생했었습니다.
제일 중요한 부분은 파일 아이디를 가져와서 Uri를 생성하는 것이였습니다.
아래 코드는 비디오, 이미지, 일반 파일 id를 가져오는 샘플입니다.
// 비디오 파일 id, 타이틀, 아티스트를 알아 오고 id를 리턴하는 함수 입니다.
private String getVideoMediaStoreIdQuery(String strPath) {
Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String[] columns = {
MediaStore.Video.VideoColumns._ID,
MediaStore.Video.VideoColumns.TITLE,
MediaStore.Video.VideoColumns.ARTIST
};
String selection = MediaStore.Video.VideoColumns.DATA + "=?";
String selectionArgs[] = { strPath };
Cursor cursor = cntxt.getContentResolver().query(uri, columns, selection, selectionArgs, null);
String strId = "";
if(cursor.moveToFirst()) {
String str0 = cursor.getString(0);
String str1 = cursor.getString(1);
String str2 = cursor.getString(2);
strId = str0;
}
return strId;
}
// 이미지 파일 id, 타이틀, 아티스트를 알아 오고 id를 리턴하는 함수 입니다.
private String getImageMediaStoreIdQuery(String strPath) {
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] columns = {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.TITLE,
MediaStore.Images.ImageColumns.ARTIST
};
String selection = MediaStore.Images.ImageColumns.DATA + "=?";
String selectionArgs[] = { strPath };
Cursor cursor = cntxt.getContentResolver().query(uri, columns, selection, selectionArgs, null);
String strId = "";
if(cursor.moveToFirst()) {
String str0 = cursor.getString(0);
String str1 = cursor.getString(1);
String str2 = cursor.getString(2);
strId = str0;
}
return strId;
}
// 일반 파일 id, 타이틀, 아티스트를 알아 오고 id를 리턴하는 함수 입니다.
private String getDataMediaStoreIdQuery(String strFileName) {
Uri uri = MediaStore.Files.getContentUri("external");
String[] columns = {
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.DATE_ADDED
};
String strExt = KwFileUtil.getFileExt(strFileName);
String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";
String[] selectionArgs = new String[] {
MimeTypeMap.getSingleton().getMimeTypeFromExtension(strExt)
};
Cursor cursor = cntxt.getContentResolver().query(uri, columns, selection, selectionArgs, null);
String strId = "";
if(cursor != null) {
cursor.moveToFirst();
String strF0 = cursor.getString(0);
String strF1 = cursor.getString(1);
if(strF1.equals(strFileName)) {
strId = strF0;
return strId;
}
while (cursor.moveToNext()) {
String str0 = cursor.getString(0);
String str1 = cursor.getString(1);
String str2 = cursor.getString(2);
if(str1.equals(strFileName)) {
strId = str0;
break;
}
}
}
return strId;
}
실제 삭제 코드는 아래 코드 입니다.
public void delete(ActivityResultLauncher<IntentSenderRequest> launcher, Uri uri) {
ContentResolver contentResolver = getApplicationContext().getContentResolver();
try {
int nResult = contentResolver.delete(uri, null, null);
if(nResult == 0) {
PendingIntent pendingIntent = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ArrayList<Uri> collection = new ArrayList<>();
collection.add(uri);
pendingIntent = MediaStore.createDeleteRequest(contentResolver, collection);
}
if (pendingIntent != null) {
IntentSender sender = pendingIntent.getIntentSender();
IntentSenderRequest request = new IntentSenderRequest.Builder(sender).build();
launcher.launch(request);
}
}
} catch (Exception e) {
PendingIntent pendingIntent = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ArrayList<Uri> collection = new ArrayList<>();
collection.add(uri);
pendingIntent = MediaStore.createDeleteRequest(contentResolver, collection);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//if exception is recoverable then again send delete request using intent
if (e instanceof RecoverableSecurityException) {
RecoverableSecurityException exception = (RecoverableSecurityException) e;
pendingIntent = exception.getUserAction().getActionIntent();
}
}
if (pendingIntent != null) {
IntentSender sender = pendingIntent.getIntentSender();
IntentSenderRequest request = new IntentSenderRequest.Builder(sender).build();
launcher.launch(request);
}
}
}
위 함수 사용법은 다음과 같다. 비디오와 이미지는 절대 경로와 파일 이름을 전달하고 일반 파일은 파일 이름만 전달하는 것만 다릅니다.
ActivityResultLauncher<IntentSenderRequest> myLauncher = registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == RESULT_OK){
// 삭제가 완료 되었을때 다음 할일을 작성
}
}
}
);
// 비디오의 경우
String strId = getVideoMediaStoreIdQuery("전체 경로와 이름");
Uri contentUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, Long.parseLong(strId));
delete(myLauncher, contentUri);
// 이미지의 경우
String strId = getImageMediaStoreIdQuery("전체 경로와 이름");
Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Long.parseLong(strId));
delete(myLauncher, contentUri);
// 일반 파일의 경우
String strId = getDataMediaStoreIdQuery("이름");
Uri contentUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), Long.parseLong(strId));
delete(myLauncher, contentUri);
위 코드에서 Uri는 아래와 같이 표현됩니다.
content://media/external/file/218
반응형
댓글