1

The codes below works fine prior to Android 11. I have tested it on Android 9 and it works.
Method to get basic info about the video:

private static void videoRename ( AppCompatActivity activity , VideoModel model , VideosLoader videosLoader ) {
        String videoTitleWithExtension =  model.getVideoTitle ( );
        int extensionIndex = videoTitleWithExtension.lastIndexOf ( '.' );
        final String videoTitleWithoutExtension;
        String extensionValue;
        if ( extensionIndex > 0 ) {
            videoTitleWithoutExtension = videoTitleWithExtension.substring ( 0 , extensionIndex );
            extensionValue = videoTitleWithExtension.substring ( extensionIndex, videoTitleWithExtension.length ( ) );
        } else {
            videoTitleWithoutExtension = videoTitleWithExtension;
            extensionValue = "";
        }

        showSelectedVideoRenameDialog ( activity , videoTitleWithoutExtension, model.getPath ( ) , extensionValue, model.getVideoId ( ) , videosLoader );

}

passing data to the dialog , then showing the dialog:

private static void showSelectedVideoRenameDialog ( final AppCompatActivity activity, final String videoTitleWithoutExtension , final String videoPath, final String extensionValue, final long videoId , final VideosLoader videosLoader ) {
        final AlertDialog dialog = new AlertDialog.Builder ( activity ).create ( );
        LayoutInflater inflater = LayoutInflater.from ( activity );
        View v = inflater.inflate ( R.layout.layout_video_rename, null );
        final TextInputEditText input = v.findViewById ( R.id.video_rename_edit_text );
        input.setText ( videoTitleWithoutExtension );
        input.setHint ( videoTitleWithoutExtension );

        Button confirmButton = v.findViewById ( R.id.renameButton );
        Button cancelButton = v.findViewById ( R.id.cancelButton );

        dialog.setView ( v );
        cancelButton.setOnClickListener ( new View.OnClickListener ( ) {
            @Override
            public void onClick ( View p1 ) {
                dialog.dismiss ( );
            }
        } );

        confirmButton.setOnClickListener ( new View.OnClickListener ( ) {
            @Override
            public void onClick ( View p1 ) {
                final String typedName = input.getText ( ).toString ( );
                final File originalName = new File ( videoPath );
                final File newFileName = new File ( videoPath.replace ( videoTitleWithoutExtension , typedName ) );
                if ( typedName.length ( ) == 0 ) {
                    input.setError ( "Name can't be empty" );
                } else if ( newFileName.exists ( ) ) {
                    input.setError ( "File name already exists" );
                } else {
                    String newTitleWithExtension = typedName + extensionValue;
                    originalName.renameTo ( newFileName );
                    String newFilePath = newFileName.toString ( );
                    videosLoader.updateOnVideoRenamed ( videoId , newFilePath , newTitleWithExtension );
                    videosLoader.updateOnMediaStoreChanged ( );

                    dialog.dismiss ( );
                }
            }
        } );
        dialog.show ( );
}

Finally, updating the values using ContentValues and MediaStore:

@Override
    public void updateOnVideoRenamed ( long id, String newPath, String newTitle ) {
        try {
            ContentValues contentValues = new ContentValues(2);
            contentValues.put(MediaStore.Video.Media.DATA, newPath);
            contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, newTitle);
            mContext.getContentResolver ( ).update ( MediaStore.Video.Media.EXTERNAL_CONTENT_URI , contentValues, MediaStore.Video.Media._ID + "=" + id, null );
        } catch (Exception e) {
            Toast.makeText( mContext , "Can't rename on Android 11: " + " " + e.getMessage() , Toast.LENGTH_SHORT).show();
        }
}

This final step obviously throws the java.lang.IllegalArgumentException.
I tried the answer here but still in vain. I might be missing something.
P.S. Renaming also fails for the videos inside folders I created using the default Samsung File manager.

private static
  • 745
  • 8
  • 17
  • A lot of irrelevant code. What we miss are the values for the parameters of last function. And which argument is illegal? – blackapps Jan 09 '22 at 14:17
  • Yes they are passed by the caller. But you did not tell their values. I asked for values. Tell them! – blackapps Jan 09 '22 at 16:09
  • You forgot the value of id and oldPath. And why not put that info in the first lines of that function? Makes it much easier to follow all. Also tell which app put those files in your 'manually' (whatever that would be) folder. – blackapps Jan 09 '22 at 18:24
  • What is old path of your file? (I did ask this before). Also i still dont know how these files were added. – blackapps Jan 09 '22 at 19:38
  • 1
    For Android Q+ one does not use the .DATA column but .RELATIVE_PATH. And IS_PENDING. Further you should first get an uri for your file and then do the update() on that uri. – blackapps Jan 09 '22 at 20:19
  • This seems to have solved the IllegalArgumentException. But now throws the RecoverableSecurityException which I'm searching to find a solution for. – private static Jan 09 '22 at 20:56
  • @blackapps Update: Ignore my previous comment it's gone. The exception now says ```Primary directory (invalid) not allowed```. Though the video DOES get renamed – private static Jan 09 '22 at 21:04

1 Answers1

1

First of all, I had to add this permission to manifest in order to be able to access all files on device:

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

Then I added this line to application tag:

android:requestLegacyExternalStorage="true"

Now after you change the name of the video, you need to update the media store with the new name:

if (ContextUtils.isAndroidR()) {
            Uri mUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI , videoId);
            try {
                ContentValues contentValues = new ContentValues(3);
                contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
                mContext.getContentResolver().update(mUri, contentValues, null, null);
                contentValues.clear();
                contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, newTitle);
                contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
                mContext.getContentResolver().update(mUri, contentValues, null, null);
            } catch (Exception exception) {
                if (ContextUtils.isAndroidQ()) {
                    RecoverableSecurityException recoverableSecurityException;
                    if (exception instanceof RecoverableSecurityException) {
                        recoverableSecurityException = (RecoverableSecurityException) exception;
                    } else {
                        ContextUtils.makeShortToast( "Maybe make sure you request permissions first?" );
                    }
                    try {
                        ContentResolver contentResolver = mContext.getContentResolver();
                        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, newTitle);
                        contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
                        contentResolver.update(mUri, contentValues, null, null);
                    } catch (Exception e) {
                        e.printStackTrace();
                        ContextUtils.makeShortToast( String.valueOf(e) );
                    }
                } else {
                    throw new RuntimeException ( exception.getMessage() , exception );
                }
            }
        } else {
            ContentValues contentValues = new ContentValues(2);
            contentValues.put(MediaStore.Video.Media.DATA, newPath);
            contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, newTitle);
            Uri extUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
            mContext.getContentResolver().update(extUri , contentValues, MediaStore.Video.Media._ID + "=" + videoId, null);
        }

This is how I got renaming a video on Android 11 to work with no issues.

private static
  • 745
  • 8
  • 17