-
-
Save fiftyonemoon/433b563f652039e32c07d1d629f913fb to your computer and use it in GitHub Desktop.
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; | |
} | |
} |
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,
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)
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?
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)
Can we manage files on Android/data folder? Is there any workaround?