androiduriandroid-contentresolvermediastorescoped-storage

How to select specific video files to upload to gallery in Android Q?


Having recorded a video using mediaRecorder and Camera API, I am trying to save the video to the user's gallery (either by saving to their external movies, DCIM directory, or other).

Unfortunately, the code I've found from the docs or StackOverflow seem to create their respective URIs without any reference to the recorded video or the file in which its kept. Specifically, the code I attempted to use is:

String videoFileName = "video_" + System.currentTimeMillis() + ".mp4";

    ContentValues valuesvideos;
    valuesvideos = new ContentValues();
    valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "Folder");
    valuesvideos.put(MediaStore.Video.Media.TITLE, videoFileName);
    valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName);
    valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
    valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
    valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
    valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 1);
    ContentResolver resolver = context.getContentResolver();
    Uri collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
    Uri uriSavedVideo = resolver.insert(collection, valuesvideos);

    ParcelFileDescriptor pfd;

    try {
        pfd = context.getContentResolver().openFileDescriptor(uriSavedVideo,"w");

        assert pfd != null;
        FileOutputStream out = new FileOutputStream(pfd.getFileDescriptor());

// Get the already saved video as fileinputstream from here
            File storageDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "Folder");
            File imageFile = new File(storageDir, "testStorage");

            FileInputStream in = new FileInputStream(imageFile);


            byte[] buf = new byte[8192];
            int len;
            while ((len = in.read(buf)) > 0) {

                out.write(buf, 0, len);
            }
            out.close();
            in.close();
            pfd.close();

        } catch (Exception e) {

            e.printStackTrace();
        }

The code and answers around it seem to imply that by accessing the MediaStore.VOLUME_EXTERNAL_PRIMARY the resolver can access the saved video file but I see no explicit reference to it. Prior to recording the video, I define the file assigned to mediaRecorder as such:

static File getMediaFile(Context c) {

    File mediaStorageDir = c.getExternalFilesDir(null);
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());

    return new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");

}

Upon stopping the video I get this error (referencing this line: Uri uriSavedVideo = resolver.insert(collection, valuesvideos);):

java.lang.UnsupportedOperationException: Unknown URI: content://media/external_primary/video/media at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java.167) ...

How do I create a URI properly and with reference to the saved video? Substituting the URI from file Uri.fromFile(f); in place of uriSavedVideo doesn't work either.


Solution

  • To save a specific video file to gallery, I passed the file as a parameter to new FileInputStream.

    Secondly, as the Android docs state:

    Only your app can view the file until your app changes the value of IS_PENDING back to 0.

    So in keeping with user @blackapps' advice, I manually changed that state. The changed code is below:

    private static void addToApi29Gallery(File file, Context context) {
        String videoFileName = "video_" + System.currentTimeMillis() + ".mp4";
    
        ContentValues valuesvideos = new ContentValues();
        valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "Trickshott");
        valuesvideos.put(MediaStore.Video.Media.TITLE, videoFileName);
        valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName);
        valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
        valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
        valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
        valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 1);
    
        ContentResolver resolver = context.getContentResolver();
        
        Uri collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); //all video files on primary external storage
        Uri uriSavedVideo = resolver.insert(collection, valuesvideos);
    
        ParcelFileDescriptor pfd;
    
        try {
            pfd = context.getContentResolver().openFileDescriptor(uriSavedVideo,"w");
    
            assert pfd != null;
            FileOutputStream out = new FileOutputStream(pfd.getFileDescriptor());
    
            // Get the already saved video as fileinputstream from here.
            FileInputStream in = new FileInputStream(file);
            
            byte[] buf = new byte[8192];
            int len;
            
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            
            out.close();
            in.close();
            pfd.close();
            
            valuesvideos.clear(); 
            valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 0); // Only your app can see the files until pending is turned into 0.
    
            context.getContentResolver().update(uriSavedVideo, valuesvideos, null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }