Skip to content

Instantly share code, notes, and snippets.

@fiftyonemoon
Created July 12, 2021 14:57
Show Gist options
  • Save fiftyonemoon/433b563f652039e32c07d1d629f913fb to your computer and use it in GitHub Desktop.
Save fiftyonemoon/433b563f652039e32c07d1d629f913fb to your computer and use it in GitHub Desktop.
File create, rename, duplicate and delete in Android11.
import android.app.PendingIntent;
import android.app.RecoverableSecurityException;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.IntentSender;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.IntentSenderRequest;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
/**
* 9th July 2021.
* <p>
* A class to read write external shared storage for android R.
* Since Android 11 you can only access the android specified directories such as
* DCIM, Download, Documents, Pictures, Movies, Music etc.
* <p>
* This class is just for an example class.
*
* @author <a href="https://github.com/fiftyonemoon">hardkgosai</a>.
* @since 1.0.3.2
*/
public class AndroidXI {
private Context context;
public static AndroidXI getInstance() {
return new AndroidXI();
}
public AndroidXI with(Context context) {
this.context = context;
return this;
}
/**
* Create new media uri.
*/
public Uri create(String directory, String filename, String mimetype) {
ContentResolver contentResolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
//Set filename, if you don't system automatically use current timestamp as name
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, filename);
//Set mimetype if you want
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimetype);
//To create folder in Android directories use below code
//pass your folder path here, it will create new folder inside directory
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, directory);
}
//pass new ContentValues() for no values.
//Specified uri will save object automatically in android specified directories.
//ex. MediaStore.Images.Media.EXTERNAL_CONTENT_URI will save object into android Pictures directory.
//ex. MediaStore.Videos.Media.EXTERNAL_CONTENT_URI will save object into android Movies directory.
//if content values not provided, system will automatically add values after object was written.
return contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
}
/**
* Delete file.
* <p>
* If {@link ContentResolver} failed to delete the file, use trick,
* SDK version is >= 29(Q)? use {@link SecurityException} and again request for delete.
* SDK version is >= 30(R)? use {@link MediaStore#createDeleteRequest(ContentResolver, Collection)}.
*/
public void delete(ActivityResultLauncher<IntentSenderRequest> launcher, Uri uri) {
ContentResolver contentResolver = context.getContentResolver();
try {
//delete object using resolver
contentResolver.delete(uri, null, null);
} catch (SecurityException 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);
}
}
}
/**
* Rename file.
*
* @param uri - filepath
* @param rename - the name you want to replace with original.
*/
public void rename(Uri uri, String rename) {
//create content values with new name and update
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, rename);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
context.getContentResolver().update(uri, contentValues, null);
}
}
/**
* Duplicate file.
*
* @param uri - filepath.
*/
public Uri duplicate(Uri uri) {
ContentResolver contentResolver = context.getContentResolver();
Uri output = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
String input = getPathFromUri(uri);
try (InputStream inputStream = new FileInputStream(input)) { //input stream
OutputStream out = contentResolver.openOutputStream(output); //output stream
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
out.write(buf, 0, len); //write input file data to output file
}
} catch (IOException e) {
e.printStackTrace();
}
return output;
}
/**
* Get file path from uri.
*/
private String getPathFromUri(Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
String text = null;
if (cursor.moveToNext()) {
text = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
}
cursor.close();
return text;
}
}
@courville
Copy link

Thank you for sharing this. Could you please provide a simple example how to use the class to delete a file?

@yugrbinfo007
Copy link

thank you

@fiftyonemoon
Copy link
Author

fiftyonemoon commented Jul 27, 2021

Thank you for sharing this. Could you please provide a simple example how to use the class to delete a file?

Here is a full code with example: https://github.com/fiftyonemoon/AndroidXI

@akhilakhi84
Copy link

akhilakhi84 commented Aug 21, 2021

Can we manage files on Android/data folder? Is there any workaround?

@quanmltya
Copy link

For the rename function, on Android R (target api level is 32), after renamed, the file is not accessible and disappears from the gallery/media store (accept apps with the MANAGE_EXTERNAL_STORAGE privilege).
The input uri is the media uri (after request via createWriteRequest()).

@MaheshPhuyal02
Copy link

Hello,
Deleting is working good but I am getting error while renaming file.
this is the error.
android.app.RecoverableSecurityException: com.mp.story.saver has no access to content://media/external_primary/video/media/136 at android.app.RecoverableSecurityException$1.createFromParcel(RecoverableSecurityException.java:197) at android.app.RecoverableSecurityException$1.createFromParcel(RecoverableSecurityException.java:194) at android.os.Parcel.readParcelable(Parcel.java:3281) at android.os.Parcel.createExceptionOrNull(Parcel.java:2368) at android.os.Parcel.createException(Parcel.java:2357) at android.os.Parcel.readException(Parcel.java:2340) at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190) at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142) at android.content.ContentProviderProxy.update(ContentProviderNative.java:649) at android.content.ContentResolver.update(ContentResolver.java:2356) at com.mp.story.saver.DownloadedListAdapter$2$2$2.onClick(DownloadedListAdapter.java:161) at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:174) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67)

@lzyang187
Copy link

For the rename function, on Android R (target api level is 32), after renamed, the file is not accessible and disappears from the gallery/media store (accept apps with the MANAGE_EXTERNAL_STORAGE privilege). The input uri is the media uri (after request via createWriteRequest()).

Hello,I also have this problem,Did you solve it?

@dungbk811
Copy link

dungbk811 commented Nov 19, 2022

Hello,
Deleting is working good but I am getting error while renaming file.
this is the error.
android.app.RecoverableSecurityException: com.mp.story.saver has no access to content://media/external_primary/video/media/136 at android.app.RecoverableSecurityException$1.createFromParcel(RecoverableSecurityException.java:197) at android.app.RecoverableSecurityException$1.createFromParcel(RecoverableSecurityException.java:194) at android.os.Parcel.readParcelable(Parcel.java:3281) at android.os.Parcel.createExceptionOrNull(Parcel.java:2368) at android.os.Parcel.createException(Parcel.java:2357) at android.os.Parcel.readException(Parcel.java:2340) at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190) at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142) at android.content.ContentProviderProxy.update(ContentProviderNative.java:649) at android.content.ContentResolver.update(ContentResolver.java:2356) at com.mp.story.saver.DownloadedListAdapter$2$2$2.onClick(DownloadedListAdapter.java:161) at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:174) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67)

You have to request permission firstly.

val uriList: List<Uri> = listOf( Uri.withAppendedPath( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, fileModel._id ) ) val pi = MediaStore.createWriteRequest(context!!.contentResolver, uriList) val request = IntentSenderRequest.Builder(pi.intentSender).build() renameResult.launch(request)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment