6

Here, this renameFile(..) func is working in Android API 30. But, it is not working in Android API 29 and shows the error like :

java.lang.IllegalArgumentException: Movement of content://media/external/file/116 which isn't part of well-defined collection not allowed

Update-Note:

---Begins---

In-order to work with sdk-29 we have to use Uri as extUri = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL) like:

private static Uri extUri = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL);

in place of below code. And also update MediaStore.Files.FileColumns to MediaStore.Downloads

---Ends---

Uri extUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
String relativeLocation = Environment.DIRECTORY_DOWNLOADS + File.separator + "AppFolder";

function renameFile(...)

boolean renameFile(Context context, String newName, String displayName) {

    try {
        Long id = getIdFromDisplayName(displayName);
        ContentResolver contentResolver = context.getContentResolver();
        Uri mUri = ContentUris.withAppendedId(extUri, id);
        ContentValues contentValues = new ContentValues();

        contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
        contentResolver.update(mUri, contentValues, null, null);

        contentValues.clear();
        contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, newName);
        // contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE, "files/pdf");
        // contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH, relativeLocation);
        // contentValues.put(MediaStore.Files.FileColumns.TITLE, "SomeName");
        // contentValues.put(MediaStore.Files.FileColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
        // contentValues.put(MediaStore.Files.FileColumns.DATE_TAKEN, System.currentTimeMillis());
        contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
        contentResolver.update(mUri, contentValues, null, null);
        return true;
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return false;
}

function getIdFromDisplayName(...)

@RequiresApi(api = Build.VERSION_CODES.Q)
Long getIdFromDisplayName(String displayName) {
    String[] projection;
    projection = new String[]{MediaStore.Files.FileColumns._ID};

    // TODO This will break if we have no matching item in the MediaStore.
    Cursor cursor = getContentResolver().query(extUri, projection,
            MediaStore.Files.FileColumns.DISPLAY_NAME + " LIKE ?", new String[]{displayName}, null);
    assert cursor != null;
    cursor.moveToFirst();

    if (cursor.getCount() > 0) {
        int columnIndex = cursor.getColumnIndex(projection[0]);
        long fileId = cursor.getLong(columnIndex);

        cursor.close();
        return fileId;
    }
    return null;
}
jazzbpn
  • 6,441
  • 16
  • 63
  • 99
  • Meanwhile you could read: https://stackoverflow.com/questions/55314476/how-to-rename-a-file-in-android-knowing-only-its-media-content-uri – blackapps Aug 25 '20 at 14:17

3 Answers3

6

java.lang.IllegalArgumentException: Movement of content://media/external/file/116 which isn't part of well-defined collection not allowed

So it is for Android Q not allowed if you use the collection;

Uri extUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);

But is is allowed for a 'well-defined collection' like:

Uri extUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
// Use  "Pictures/MyFolder" for RELATIVE_PATH

I leave it to you to find other well-defined collections.

Why this is only for Android Q i dont know.

You can see the message in the java file: https://android.googlesource.com/platform/packages/providers/MediaProvider/+/refs/heads/master/src/com/android/providers/media/MediaProvider.java

Quote:

     // We only support movement under well-defined collections
        switch (match) {
            case AUDIO_MEDIA_ID:
            case VIDEO_MEDIA_ID:
            case IMAGES_MEDIA_ID:
            case DOWNLOADS_ID:
                break;
            default:
                throw new IllegalArgumentException("Movement of " + uri
                        + " which isn't part of well-defined collection not allowed");
        }

If the rename fails use SAF (as mentioned before). How to rename a file in Android knowing only its media content Uri

blackapps
  • 8,011
  • 2
  • 11
  • 25
1

I think it's a system bug in Android 10 mediaprovider. In android 10,it is like this

       // We only support movement under well-defined collections
        switch (match) {
            case AUDIO_MEDIA_ID:
            case VIDEO_MEDIA_ID:
            case IMAGES_MEDIA_ID:
            case DOWNLOADS_ID:
                break;
            default:
                throw new IllegalArgumentException("Movement of " + uri
                        + " which isn't part of well-defined collection not allowed");
        }

in Android 11,it is like this

        final boolean allowMovement = extras.getBoolean(MediaStore.QUERY_ARG_ALLOW_MOVEMENT,
            !isCallingPackageSelf());
    if (containsAny(initialValues.keySet(), sPlacementColumns)
            && !initialValues.containsKey(MediaColumns.DATA)
            && !isThumbnail
            && allowMovement) {
        Trace.beginSection("movement");
        // We only support movement under well-defined collections
        switch (match) {
            case AUDIO_MEDIA_ID:
            case AUDIO_PLAYLISTS_ID:
            case VIDEO_MEDIA_ID:
            case IMAGES_MEDIA_ID:
            case DOWNLOADS_ID:
            case FILES_ID:
                break;
            default:
                throw new IllegalArgumentException("Movement of " + uri
                        + " which isn't part of well-defined collection not allowed");
        }
jason lin
  • 11
  • 2
0

I had to face the rename problem myself (Android 29) and the solution above did not suffice.

This was because I had a physical SD card on which were located the files I wanted to rename.

Then, instruction:

extUri = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);

did not work; instead,I had to:

  1. list the "external volumes"(according to Android terms)

    Set<String> lVls = MediaStore.getExternalVolumeNames(this);
    

    ..which gave me 2 volumes:

    "external_primary"    (the built-in external storage)
    "bc21-eafa"           (the SD card external storage)
    
  2. Initialize 'extUri' with that second value, like that:

    extUri = MediaStore.Audio.Media.getContentUri("bc21-eafa");
    
  3. Apply the rest of the procedure as described in this article. Thanks to all !

jleib
  • 1
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 17 '22 at 17:03