Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android clicking input type=file brings up system file browser, not camera options #816

Closed
3 tasks done
alexcroox opened this issue Aug 21, 2019 · 38 comments · Fixed by #1609
Closed
3 tasks done

Comments

@alexcroox
Copy link

alexcroox commented Aug 21, 2019

Bug Report

Problem

When clicking on an html type="file" input a system file picker appears instead of camera options.

ss

What is expected to happen?

If I perform the same action in the system browser I get the expected dialog:

ss2

Version information

"cordova-android": "^8.0.0",
"cordova-plugin-camera": "^4.1.0"

cordova-cli 9.0.0

Full test case repo here

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above
@alexcroox
Copy link
Author

I've added a minimal test case repo here: https://bitbucket.org/alex-crooks/android-file-test/src/master/

@adityak74
Copy link

adityak74 commented Aug 22, 2019

@alexcroox
You could use cordova-plugin-camera's window.navigator.camera.getPicture to trigger the right options on android. This method will let you choose your destination using https://github.com/apache/cordova-plugin-camera#cameradestinationtype--enum.

Technically add an onClick listener to your tag and handle it yourself by using the above API method, that should resolve your issue.

I guess type="file" is more generic and hence they handle it by showing a system file picker.

@xyozio
Copy link

xyozio commented Aug 27, 2019

The implementation of openFileChooser function is very simple and doesn't handle capture:

public void openFileChooser(final ValueCallback<Uri> uploadMsg, String acceptType, String capture)
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
LOG.d(LOG_TAG, "Receive file chooser URL: " + result);
uploadMsg.onReceiveValue(result);
}
}, intent, FILECHOOSER_RESULTCODE);
}

@xyozio
Copy link

xyozio commented Aug 27, 2019

In XWalk this function got more attention:

https://github.com/crosswalk-project/crosswalk/blob/1b9b80835e83e77390bd6cdbc03beb63f2a6f550/runtime/android/core/src/org/xwalk/core/XWalkFileChooser.java

Unfortunatelly XWalk is not developed anymore so you can either use the last version of XWalk, use the cordova-camera plugin or try and submit a PR that improves the openFileChooser function.

@adityak74
Copy link

adityak74 commented Aug 27, 2019

I think you can submit a PR if you want to translate the HTML Image element's accept property into intent type for the openFileChooser method.

<input accept="file_extension|audio/*|video/*|image/*|media_type">

@jalixdesign
Copy link

I currently have the same problem.
Unfortunately, I have no Java / Android skills, so I could extend the function myself.

Is there someone who can extend the function?

@biodiv
Copy link

biodiv commented Nov 6, 2019

I agree that this is a problem. It should be handled according to the W3C recommendations, like the system browser does.

@biodiv
Copy link

biodiv commented Nov 7, 2019

You could use cordova-plugin-camera's window.navigator.camera.getPicture to trigger the right options on android. This method will let you choose your destination using https://github.com/apache/cordova-plugin-camera#cameradestinationtype--enum.

Technically add an onClick listener to your tag and handle it yourself by using the above API method, that should resolve your issue.

This approach works but has limitations. For example, <input type="file">.files is a FileList instance, which does not offer a push method (like regular Arrays) for good reasons. So, you cannot retrieve a File using the camera plugin and simply append it to <input type="file">.files using javascript. You have to implement your own form/file handling if you use the camera plugin.

FileList on MDN

@DzTheRage
Copy link

Any update on this?

@Aarbel
Copy link

Aarbel commented Feb 10, 2020

Hello guys, do you have more updates / insights to fix this problem ? Thanks a lot !

@HarelM
Copy link

HarelM commented Jun 16, 2020

This is half related to this issue, let me know if you want me to open another issue.
I'm using <input type="file"> which open the system file picker.
I then click dropbox, it opens dropbox and I select the file, it downloads it, closes dropbox, returns to the file picker but doesn't return to cordova app.
Has anyone encountered this?
I simply would like to be able to open a file saved in dropbox, nothing fancy...

@trylovetom
Copy link

Any update on this?

@HarelM
Copy link

HarelM commented Jul 7, 2020

If anyone is interested I'm using the following workaround that uses angular/ionic directive:
https://github.com/IsraelHikingMap/Site/blob/fe227a44b871f310d4f8c102714dd32bd05a3e9c/IsraelHiking.Web/sources/application/directives/image-capture.directive.ts#L13
It works good for some time in production including all kind of fixes related to picture orientation, status bar issues etc.

@hslimi
Copy link

hslimi commented Jul 12, 2020

Any update?

@giantjeaks
Copy link

giantjeaks commented Jul 13, 2020

If some one have any simple solution please share.
I can confirm that

cordova-plugin-camera

won't help in my scenario
because I need to use dropzone.js with my project the input type file work properly on iOS but android it's not

@Mahesh554
Copy link

Mahesh554 commented Sep 24, 2020

Any update on this issue?
I need to support both browser and hybrid app, so I couldn't use cordova-plugin-camera

@liangwenzhong
Copy link

any solutions?

@kortgat
Copy link

kortgat commented May 7, 2021

what I did (which is still in progress since im learning android Java for this) is to modify the onShowFileChooser() method of "platforms\android\CordovaLib\src\org\apache\cordova\engine\SystemWebChromeClient.java"

add import:
import android.provider.MediaStore;

and changed the method to below:

 public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
        // Check if multiple-select is specified
        LOG.d(LOG_TAG, "file chooser opened");
        Boolean selectMultiple = false;
        if (fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE) {
            selectMultiple = true;
        }
        String[] acceptTypes = fileChooserParams.getAcceptTypes(); 
        LOG.d(LOG_TAG,"File types: "+ toCSV(acceptTypes));
        Intent intent =  fileChooserParams.createIntent();
        intent.addCategory(Intent.CATEGORY_OPENABLE); 
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, selectMultiple);
        
        // Uses Intent.EXTRA_MIME_TYPES to pass multiple mime types.
        if (acceptTypes.length > 1) {
            intent.setType("*/*"); // Accept all, filter mime types by Intent.EXTRA_MIME_TYPES.
            intent.putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes);
        }
        
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        Intent GalleryIntent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

        try {
            Intent chooserIntent;

                chooserIntent = Intent.createChooser(intent, "Open file");
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{GalleryIntent,cameraIntent, intent});
   
            parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
                @Override
                public void onActivityResult(int requestCode, int resultCode, Intent intent) {
                    Uri[] result = null;
                    if (resultCode ==  Activity.RESULT_OK && intent != null) {
                        if (intent.getClipData() != null) {
                            // handle multiple-selected files
                            final int numSelectedFiles = intent.getClipData().getItemCount();
                            result = new Uri[numSelectedFiles];
                            for (int i = 0; i < numSelectedFiles; i++) {
                                result[i] = intent.getClipData().getItemAt(i).getUri();
                                LOG.d(LOG_TAG, "Receive file chooser URL: " + result[i]);
                            }
                        }
                        else if (intent.getData() != null) {
                            // handle single-selected file
                            result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
                            LOG.d(LOG_TAG, "Receive file chooser URL: " + result);
                        }
                    }
                    filePathsCallback.onReceiveValue(result);
                }
            }, chooserIntent, FILECHOOSER_RESULTCODE);
        } catch (ActivityNotFoundException e) {
            LOG.w("No activity found to handle file chooser intent.", e);
            filePathsCallback.onReceiveValue(null);
        }
        return true;
    }

I still need to figure out how to unbundle the gallery options but atleast it works for now

@Aarbel
Copy link

Aarbel commented May 7, 2021

@kortgat can you make a pr about it ?

@kortgat
Copy link

kortgat commented Aug 11, 2021

@kortgat can you make a pr about it ?

Unfortunately i still havent finished it to have a robust workin solution but will try to finish it and make a PR when i have some time

@Aarbel
Copy link

Aarbel commented Aug 11, 2021

Thanks a lot @kortgat, do you have any idea of the delay about it ?

@ghwrivas
Copy link

ghwrivas commented Aug 11, 2021

@kortgat
Copy link

kortgat commented Aug 11, 2021

Nice, but my problem is something like this should not have to be worked around, especially if you build pwa, android and ios. Im still learning the android functions in java and figuring out how, to do it in the cordova library so I can at least help others.

@Delagen
Copy link

Delagen commented Aug 11, 2021

@korgat can you review my solution for camera mode when capture flag present on input. I think i try to resolve similar issue.
Look at my PR on cordova-plugin-inappbrowser

@Aarbel
Copy link

Aarbel commented Aug 11, 2021

we use https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos as workaround

You should also check Capacitor PWA plugins, they directly provide UI components to take a picture, cf https://capacitorjs.com/docs/web/pwa-elements.
Btw a native support for camera on these inputs would be absolutely great for many cases

@kortgat
Copy link

kortgat commented Aug 13, 2021

SystemWebChromeClient.zip
So I have a working cordova fix to enable camera for all input file fields, someone can refine it and do a PR if they want to but I attatched the file to replace "platforms\android\CordovaLib\src\org\apache\cordova\engine\SystemWebChromeClient.java"

one usefull use case is when generating forms with a tool lik formIO where you dont have control over the generated html it should will also work

@Aarbel
Copy link

Aarbel commented Aug 13, 2021

@kortgat it didn't work allowing camera only on <input capture="user" type="file" > or <input capture="environment" type="file"> ?

@kortgat
Copy link

kortgat commented Aug 13, 2021

my input is as follows
<input id="fileselect" name="file" type="file" accept="image/*,application/pdf" />

@Aarbel
Copy link

Aarbel commented Aug 13, 2021

If you don't put a "capture" key, this will even not open the camera on iOS.

Check https://w3c.github.io/html-media-capture/#the-capture-attribute, and input behavior with wkwebview andchrome for android.

Do you need more details to make the pr ?

@it-xtech-dev
Copy link

Whats the status of this issue? I thought input type="file" behavior would be consistent with web specifications but it seems not.

@dylanfpaul
Copy link

dylanfpaul commented Dec 15, 2021

I agree with a lot of people here saying this is something that we probably shouldn't need a workaround for. @kortgat , with regards to changing the Java file, Is there a way I can make sure this happens after every build or platform add without having to manually change the file every time? The cordova project I am working on sits in a repo unbuilt so I can't really live with a solution that has me changing outputted files.

@kortgat
Copy link

kortgat commented Dec 20, 2021

Hi, you could add a hook after platform add or before build https://cordova.apache.org/docs/en/10.x/guide/appdev/hooks/ and just create a script to copy it from a root folder or so, mine is called hotfixes, I just added my file here that works for me on android incase I made changes to it since last post.

hotfixes.zip

@dylanfpaul
Copy link

@kortgat right on, I was able to add the hook, replace the corresponding file with yours and the camera functionality now works as expected with input elements.

@Djones4822
Copy link

Using @kortgat's hotfix I was able to get the camera to work, however the images were unusually small - I believe that is because the java code is using the picture's thumbnail instead of the actual picture data, however I cannot seem to get the actual picture data to pass.

I've documented my research in PR #1385 - I would appreciate anyone's feedback on the issue.

@igorsantos07
Copy link

igorsantos07 commented Aug 13, 2022

It's completely baffling how some dark corners of Cordova just don't work as one would expect and get relegated to oblivion for THREE years. Not to mention this is an inconsistency in a platform that is expected to bring consistency to hybrid development - iOS shows a dropdown to select from the library or camera just fine (albeit you need to add "usage descriptions").

<input accept="image/*;capture*/> works completely fine in Android Chrome (asking for a file or camera), but I guess we need someone to bless #1385 to get it merged and working. The capture param force-opens the camera instead, which is not what the OP was asking.

Even then, none of the options trigger the default Android camera interface. Using a plugin is not acceptable either, obviously.

@KenCorbettJr
Copy link
Contributor

I created a cordova plugin to patch this issue until someone gets around to fixing this in cordova-android once and for all: https://www.npmjs.com/package/cordova-plugin-android-image-file-input

@ravi-brightree
Copy link

ravi-brightree commented Jan 3, 2024

what I did (which is still in progress since im learning android Java for this) is to modify the onShowFileChooser() method of "platforms\android\CordovaLib\src\org\apache\cordova\engine\SystemWebChromeClient.java"

add import: import android.provider.MediaStore;

and changed the method to below:

 public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
        // Check if multiple-select is specified
        LOG.d(LOG_TAG, "file chooser opened");
        Boolean selectMultiple = false;
        if (fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE) {
            selectMultiple = true;
        }
        String[] acceptTypes = fileChooserParams.getAcceptTypes(); 
        LOG.d(LOG_TAG,"File types: "+ toCSV(acceptTypes));
        Intent intent =  fileChooserParams.createIntent();
        intent.addCategory(Intent.CATEGORY_OPENABLE); 
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, selectMultiple);
        
        // Uses Intent.EXTRA_MIME_TYPES to pass multiple mime types.
        if (acceptTypes.length > 1) {
            intent.setType("*/*"); // Accept all, filter mime types by Intent.EXTRA_MIME_TYPES.
            intent.putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes);
        }
        
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        Intent GalleryIntent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

        try {
            Intent chooserIntent;

                chooserIntent = Intent.createChooser(intent, "Open file");
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{GalleryIntent,cameraIntent, intent});
   
            parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
                @Override
                public void onActivityResult(int requestCode, int resultCode, Intent intent) {
                    Uri[] result = null;
                    if (resultCode ==  Activity.RESULT_OK && intent != null) {
                        if (intent.getClipData() != null) {
                            // handle multiple-selected files
                            final int numSelectedFiles = intent.getClipData().getItemCount();
                            result = new Uri[numSelectedFiles];
                            for (int i = 0; i < numSelectedFiles; i++) {
                                result[i] = intent.getClipData().getItemAt(i).getUri();
                                LOG.d(LOG_TAG, "Receive file chooser URL: " + result[i]);
                            }
                        }
                        else if (intent.getData() != null) {
                            // handle single-selected file
                            result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
                            LOG.d(LOG_TAG, "Receive file chooser URL: " + result);
                        }
                    }
                    filePathsCallback.onReceiveValue(result);
                }
            }, chooserIntent, FILECHOOSER_RESULTCODE);
        } catch (ActivityNotFoundException e) {
            LOG.w("No activity found to handle file chooser intent.", e);
            filePathsCallback.onReceiveValue(null);
        }
        return true;
    }

I still need to figure out how to unbundle the gallery options but atleast it works for now

Hello i add same code and open camera but after capture photo not working not getting any result.

Below is my updated code in inAppBroswe.java file

// File Chooser Implemented ChromeClient
                inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView) {
                    public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
                    {
                        LOG.d(LOG_TAG, "File Chooser 5.0+");
                        // If callback exists, finish it.
                        if(mUploadCallback != null) {
                            mUploadCallback.onReceiveValue(null);
                        }
                        mUploadCallback = filePathCallback;

                        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                        Intent GalleryIntent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

                        // Create File Chooser Intent
                        Intent content = new Intent(Intent.ACTION_GET_CONTENT);
                        content.addCategory(Intent.CATEGORY_OPENABLE);
                        content.setType("*/*");

                        Intent chooserIntent;

                        chooserIntent = Intent.createChooser(content, "Open file");
                        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{GalleryIntent,cameraIntent, content});

                        // Run cordova startActivityForResult
                        cordova.startActivityForResult(InAppBrowser.this, chooserIntent, FILECHOOSER_REQUESTCODE);
                        return true;
                    }
                });```

@jacobg
Copy link

jacobg commented Jan 23, 2024

It seems that while it works on a Samsung Galaxy A50 running Android 11, it neither works on any Android simulator SDK version (30-34), nor does it work on Pixel 8 running Android 14. "Doesn't work" means that after taking a photo and ok'ing it, the input change event does not fire.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.