Rename file of the Mediastore which is created by app in android 10. Working on Android API 30 but shows error in API 29

huangapple go评论97阅读模式
英文:

Rename file of the Mediastore which is created by app in android 10. Working on Android API 30 but shows error in API 29

问题

以下是您提供的代码的翻译部分:

这里这个 renameFile(..) 函数在 Android API 30 中正常工作但是在 Android API 29 中却无法工作并显示如下错误

*java.lang.IllegalArgumentException: 不允许移动不属于明确定义的集合的 content://media/external/file/116*

**更新说明**

**---开始---**

为了在 sdk-29 中正常工作我们需要使用 UriextUri = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL)代码如下

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

同时,将下面的代码:

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

更新为使用 MediaStore.Downloads,代码如下:

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

---结束---

函数 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;
}

函数 getIdFromDisplayName(...)

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

    // TODO 如果在 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;
}

<details>
<summary>英文:</summary>

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&#39;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 + &quot;AppFolder&quot;;

**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, &quot;files/pdf&quot;);
            // contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH, relativeLocation);
            // contentValues.put(MediaStore.Files.FileColumns.TITLE, &quot;SomeName&quot;);
            // 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 + &quot; LIKE ?&quot;, new String[]{displayName}, null);
        assert cursor != null;
        cursor.moveToFirst();

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

            cursor.close();
            return fileId;
        }
        return null;
    }



</details>


# 答案1
**得分**: 6

```java
java.lang.IllegalArgumentException: 不允许移动不属于明确定义的集合的内容:content://media/external/file/116

因此,对于使用集合的情况,Android Q不允许;

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

但是对于“明确定义的集合”是允许的,比如:

    Uri extUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
    // 使用 "Pictures/MyFolder" 作为 RELATIVE_PATH

我将其他明确定义的集合的查找工作交给你。

为什么这仅限于Android Q,我不清楚。

您可以在Java文件中看到此消息:https://android.googlesource.com/platform/packages/providers/MediaProvider/+/refs/heads/master/src/com/android/providers/media/MediaProvider.java

摘录:

         // 我们仅支持在明确定义的集合下进行移动
            switch (match) {
                case AUDIO_MEDIA_ID:
                case VIDEO_MEDIA_ID:
                case IMAGES_MEDIA_ID:
                case DOWNLOADS_ID:
                    break;
                default:
                    throw new IllegalArgumentException("不允许移动不属于明确定义的集合的内容:" + uri);
            }

如果重命名失败,请使用SAF(如前所述)。https://stackoverflow.com/questions/55314476/how-to-rename-a-file-in-android-knowing-only-its-media-content-uri
英文:

>> 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  &quot;Pictures/MyFolder&quot; 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(&quot;Movement of &quot; + uri
                        + &quot; which isn&#39;t part of well-defined collection not allowed&quot;);
        }

If the rename fails use SAF (as mentioned before). https://stackoverflow.com/questions/55314476/how-to-rename-a-file-in-android-knowing-only-its-media-content-uri

答案2

得分: 1

我认为这是Android 10 mediaprovider中的一个系统错误。
在Android 10中,代码如下:

// 仅支持在明确定义的集合下进行移动
switch (match) {
    case AUDIO_MEDIA_ID:
    case VIDEO_MEDIA_ID:
    case IMAGES_MEDIA_ID:
    case DOWNLOADS_ID:
        break;
    default:
        throw new IllegalArgumentException("不允许移动不属于明确定义的集合的 " + uri);
}

在Android 11中,代码如下:

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");
    // 仅支持在明确定义的集合下进行移动
    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("不允许移动不属于明确定义的集合的 " + uri);
    }
}
英文:

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(&quot;Movement of &quot; + uri
                        + &quot; which isn&#39;t part of well-defined collection not allowed&quot;);
        }

in Android 11,it is like this

        final boolean allowMovement = extras.getBoolean(MediaStore.QUERY_ARG_ALLOW_MOVEMENT,
            !isCallingPackageSelf());
    if (containsAny(initialValues.keySet(), sPlacementColumns)
            &amp;&amp; !initialValues.containsKey(MediaColumns.DATA)
            &amp;&amp; !isThumbnail
            &amp;&amp; allowMovement) {
        Trace.beginSection(&quot;movement&quot;);
        // 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(&quot;Movement of &quot; + uri
                        + &quot; which isn&#39;t part of well-defined collection not allowed&quot;);
        }

答案3

得分: 0

我不得不亲自面对重命名问题(Android 29),上述解决方案不足够。

这是因为我有一张物理 SD 卡,上面有我想要重命名的文件。

然后,以下指令:

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

没有起作用;相反,我不得不:

  1. 列出“外部卷”(根据 Android 的术语)
Set<String> lVls = MediaStore.getExternalVolumeNames(this);

..这给了我两个卷:

"external_primary"内置外部存储
"bc21-eafa"SD 卡外部存储
  1. 使用第二个值来初始化 'extUri',像这样:
extUri = MediaStore.Audio.Media.getContentUri("bc21-eafa");
  1. 像本文中描述的那样,应用剩下的步骤。感谢大家!
英文:

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&lt;String&gt; lVls = MediaStore.getExternalVolumeNames(this);
    

    ..which gave me 2 volumes:

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

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

huangapple
  • 本文由 发表于 2020年8月23日 12:32:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63543414.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定