본문 바로가기
프로그램 개발해서 돈벌기/Android

Android 10(TargetSDK 29) 이후 버전에서 파일 삭제

by ubmuhan 2022. 11. 14.
반응형

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

 

반응형

댓글