cordova-plugin-media-capture icon indicating copy to clipboard operation
cordova-plugin-media-capture copied to clipboard

Provide the ability to store capture files in private app storage rather than external storage

Open jschillingApollo opened this issue 1 year ago • 6 comments

Feature Request

Motivation Behind Feature

The app that I help develop is currently able to record video and pictures and, after those files have been uploading to a server, the files need to be deleted from the device. This is becoming a very big issue for our Android users. Videos are automatically stored to external storage and are visible in the photo gallery. There are currently no options to prevent storage to external storage and because of this there is no way to delete the video files with our app in API 29+.

This problem would also exist for our users when taking photos, but we opted to use a different plugin for pictures. In the cordova-plugin-camera plugin, there is a cameraOption called saveToPhotoAlbum which can be set to false to save the photo file to app storage instead of the external storage. In this case, the developer can use the filesystem plugin to delete the file when necessary and this works in API 29+. The one downside is that the camera plugin doesn't offer video recording which is why we are using media-capture plugin for video.

Feature Description

The media-capture plugin should be updated to support storing captures to locations other than external storage since modifying files in that location is no longer possible on newer versions (API 29+) of Android.

At minimum, the developer should be able to configure the plugin to store captures to app storage, outside of the device's gallery.

Being able to configure the plugin to store to any storage location on the device would be a bonus, though not fully necessary.

Alternatives or Workarounds

I have not yet found any alternative solution to this problem.

I did find another issue from early 2021 regarding a similar feature request, but progress seemed to have entirely stalled on that issue.

jschillingApollo avatar Dec 15 '22 19:12 jschillingApollo

Hey @jschillingApollo I was wondering if you were able to find any solution to your problem since it's been a few months now since you posted. I find myself in the same boat.. as do many others I imagine. Having all sorts of troubles on Android (IOS works perfectly) due to the whole external storage fiasco.

In fact.. I haven't even yet figured out how to upload a video to the server on Android since I'm targeting API 31. Could you possibly share a small code snippet that shows how you get access to the just-recorded video so you can upload it somewhere?

Would really appreciate if you could comment on what you ended up doing about this too.

Many thanks!

vesper8 avatar Mar 17 '23 02:03 vesper8

@vesper8 No solution yet unfortunately. We ended up removing video capture functionality from the Android version of our application temporarily while we look for a better option because this plugin is not going to work for us in its broken state.

As far as accessing the video file on Android its pretty straightforward. Its the same for iOS or Android for us and we are able to upload videos just fine. Its just where the media-capture plugin stores them that's the problem. They can still be accessed the same way iOS videos are accessed using the file plugin and getting a FileEntry object from the file path.

The docs give a decent overview of how the .captureVideo() function works and what parameters it accepts. This function returns a Promise of type MediaFile[] (don't forget to import MediaFile from the media-capture plugin) so you'll need a way to extract that information from the Promise (i.e. .then or async/await). Then you just loop through the array and collect the paths to each captured video file using the .fullPath attribute. On iOS you will likely need to replace the start of the fullPath string from '/private' to 'file://' but on Android you shouldn't need to change that. The process for accessing/reading the file for upload should be the same for either platform and we use the awesome-cordova-plugins-file plugin for that.

jschillingApollo avatar Mar 22 '23 19:03 jschillingApollo

I hope it's not too late for you the solution I have. I just had to modify the android plugin exactly the Capture.java class to save the video in the external storage, then I can delete the video. I tried it on sdk 30, 31, 32 and 33 and it works without problems.

The steps were Step 1: Create a new prop videoUri private Uri videoUri;

Step 2: Update the method captureVideo

private void captureVideo(Request req) {

    if(cameraPermissionInManifest && !PermissionHelper.hasPermission(this, Manifest.permission.CAMERA)) {
        PermissionHelper.requestPermission(this, req.requestCode, Manifest.permission.CAMERA);

    } else {

        Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
        ContentResolver contentResolver = this.cordova.getActivity().getContentResolver();
        ContentValues cv = new ContentValues();
        cv.put(MediaStore.Video.Media.MIME_TYPE, VIDEO_MP4);
        videoUri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, cv);

        if(Build.VERSION.SDK_INT > 7){
            intent.putExtra("android.intent.extra.durationLimit", req.duration);
            intent.putExtra("android.intent.extra.videoQuality", req.quality);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri);
        }
        this.cordova.startActivityForResult((CordovaPlugin) this, intent, req.requestCode);
    }
}

Step 3: Update the method onVideoActivityResult

public void onVideoActivityResult(Request req, Intent intent) {
    Uri data = null;

    if (intent != null){
        // Get the uri of the video clip
        intent.setData(videoUri);
        data = intent.getData();
    }

    if( data == null){
        File movie = new File(getTempDirectoryPath(), "Capture.avi");
        data = Uri.fromFile(movie);
    }

    // create a file object from the uri
    if(data == null) {
        pendingRequests.resolveWithFailure(req, createErrorObject(CAPTURE_NO_MEDIA_FILES, "Error: data is null"));
    }
    else {
        req.results.put(createMediaFile(videoUri));

        if (req.results.length() >= req.limit) {
            // Send Uri back to JavaScript for viewing video
            pendingRequests.resolveWithSuccess(req);
        } else {
            // still need to capture more video clips
            captureVideo(req);
        }
    }
}

losrak avatar Jun 13 '23 22:06 losrak

@losrak Thank you for the suggestion! I appreciate the help and I'll give this a try in the meantime

For the package maintainers however, I would like to note that this is only a temporary solution and that the plugin itself still does not address the issue. Updating or doing a clean install of the package would not include the temporary fix and it would need to be changed manually each time and on each development environment.

jschillingApollo avatar Jun 16 '23 16:06 jschillingApollo

@jschillingApollo I know is not the best solution but you can fork the plugin, update the changes and install the fork repository instead of the original plugin in your project to have a clean installation

losrak avatar Jun 16 '23 16:06 losrak

@losrak I tried your solution but the video file is still showing up on the phone's media gallery. Attempting to delete the video using Cordova file gives me error 5.

danemco avatar Dec 29 '23 23:12 danemco