37

I am developing an Android app. Basically it is a WebView and a progressBar. Facebook's mobile site (m.facebook.com) is loaded into the WebView.

When I click on the Choose File button to upload an image, nothing happens. I have tried ALL of the solutions and none of them work. I am testing on a Galaxy Note (GT-N7000) running 4.0.3. My minimum SDK version is version 8.

My App
(source: istyla.com)

Here is my code for more info...

public class IStyla extends Activity {
    private ValueCallback<Uri> mUploadMessage;
    private final static int FILECHOOSER_RESULTCODE = 1;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == FILECHOOSER_RESULTCODE) {
            if (null == mUploadMessage)
                return;
            Uri result = intent == null || resultCode != RESULT_OK ? null
                    : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;

        }
    }
    private class MyWebChromeClient extends WebChromeClient {
        public void openFileChooser(ValueCallback<Uri> uploadMsg) {
            mUploadMessage = uploadMsg;
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.setType("image/*");
            IStyla.this.startActivityForResult(Intent.createChooser(i, "Image Browser"), FILECHOOSER_RESULTCODE);
        }
    
        @Override
        public boolean onJsAlert(WebView view, String url, String message,final JsResult result) {
            //handle Alert event, here we are showing AlertDialog
            new AlertDialog.Builder(IStyla.this)
                .setTitle("JavaScript Alert !")
                .setMessage(message)
                .setPositiveButton(android.R.string.ok,
                    new AlertDialog.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            // do your stuff
                            result.confirm();
                        }
                    }).setCancelable(false).create().show();
            return true;
        }
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_istyla);
        WebView webView = (WebView) findViewById(R.id.webView1);
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webView.setWebChromeClient(new MyWebChromeClient(){
            public void onProgressChanged(WebView view, int progress) {
                // Activities and WebViews measure progress with different scales.
                // The progress meter will automatically disappear when we reach 100%
                ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar1);
                if(progress < 100 && progressBar.getVisibility() == ProgressBar.GONE){
                    progressBar.setVisibility(ProgressBar.VISIBLE);
                }
                progressBar.setProgress(progress);
                if(progress == 100) {
                    progressBar.setVisibility(ProgressBar.GONE);
                }
            }
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
                IStyla.this.startActivityForResult(Intent.createChooser(i, "Image Browser"), FILECHOOSER_RESULTCODE);
            }
        });
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar1);
                progressBar.setVisibility(ProgressBar.VISIBLE);
                return true;
            }
            
        });
        webView.loadUrl("https://m.facebook.com");

    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK){
            if(((WebView)findViewById(R.id.webView1)).canGoBack()){
                ((WebView)findViewById(R.id.webView1)).goBack();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_istyla, menu);
        return true;
    }

}

Thanks

Community
  • 1
  • 1
Jacques Blom
  • 1,794
  • 5
  • 24
  • 42
  • Maybe you need permisions, something like [READ_EXTERNAL_STORAGE](http://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE) – neworld Jul 30 '12 at 15:11
  • I have added the permission, but it still doesn't work. I have added my code above. – Jacques Blom Jul 30 '12 at 15:22
  • 2
    Look at http://stackoverflow.com/questions/5907369/file-upload-in-webview – David Esteves Jul 30 '12 at 21:52
  • 1
    How to handle this method after obfuscation ? I am using same method without obfuscation it works fine but when my app is deployed with obfuscation this methods is never called. I have keeps this method in proguard anyone seen this issue? – Harshawardhan Jun 25 '13 at 09:47
  • This `Webview` subclass handles file uploads automatically, might be helpful: https://github.com/delight-im/Android-AdvancedWebView – caw Jan 08 '15 at 01:55

5 Answers5

13

This is how i am using in my app

 private class MyWebChromeClient extends WebChromeClient {
    //The undocumented magic method override
    //Eclipse will swear at you if you try to put @Override here


    // For Android 3.0+
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE);
    }

    //For Android 4.1+ only
    protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        mUploadMessage = uploadMsg;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE);
    }

    protected void openFileChooser(ValueCallback<Uri> uploadMsg) {
        mUploadMessage = uploadMsg;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE);
    }

    // For Lollipop 5.0+ Devices
    public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        if (uploadMessage != null) {
            uploadMessage.onReceiveValue(null);
            uploadMessage = null;
        }

        uploadMessage = filePathCallback;
        Intent intent = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            intent = fileChooserParams.createIntent();
        }
        try {
            startActivityForResult(intent, REQUEST_SELECT_FILE);
        } catch (ActivityNotFoundException e) {
            uploadMessage = null;
            Toast.makeText(getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
            return false;
        }
        return true;
    }
Salman Nazir
  • 2,759
  • 2
  • 28
  • 42
5

Mike Olivier's reference shared by Fr33dan is important. I am sharing what worked for me under situation quite akin yours.

wv.setWebChromeClient(new WebChromeClient()  {

// For Android 3.0+
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {  
mUploadMessage = uploadMsg;  
Intent i = new Intent(Intent.ACTION_GET_CONTENT);  
i.addCategory(Intent.CATEGORY_OPENABLE);  
i.setType("image/*");  
MainActivity.this.startActivityForResult( Intent.createChooser( i, getString(R.string.fileselect) ), MainActivity.FILECHOOSER_RESULTCODE ); 
}

// For Android < 3.0
public void openFileChooser( ValueCallback<Uri> uploadMsg ) {
openFileChooser( uploadMsg, "" );
}


// For Android > 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
openFileChooser( uploadMsg, "" );
}
});

NOTE: I found this somewhere in stack overflow and m0s' blog.

Also, Ignore lint warnings. This one works pretty well for API 8 and above.

Community
  • 1
  • 1
Chirag
  • 1,189
  • 2
  • 21
  • 43
  • Many users are still using android 3.0 and below. Even, I found this at SO community. :) – Chirag Jul 13 '13 at 15:36
  • Nothing is happening even when I Implement this function. And does not it recursive function call? – Sagar Dec 05 '16 at 06:57
4

Given the high number of votes I'm guessing no one has noticed the 3rd comment (from David Esteves) contains a link with to answer to this question.

Michel Olivier said:

This solution also works for Honeycomb and Ice Cream Sandwich. Seems like Google introduced a cool new feature (accept attribute) and forgot to to implement an overload for backwards compatibility.

So you just need to add a openFileChooser overload to your MyWebChromeClient class that accepts the string parameter and then call the one that does not:

public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) 
{  
    this.openFileChooser(uploadMsg);
}

Doing this I was able to make your code run as intended.

Community
  • 1
  • 1
Fr33dan
  • 4,227
  • 3
  • 35
  • 62
3

When invoking AlertDialog in WebChromeClient.OpenFileChooser, you need to notify webchrome that no selection was done in case of back button. Since Back button can be pressed in you dialog, you have to handle OnCancel of the dialog and tell WebChrome that selection was finished.

eg:

private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 1;
private final static int CAMERA_RESULTCODE = 2;

...

    webView.setWebChromeClient(new WebChromeClient() {
      public boolean onConsoleMessage(ConsoleMessage cm) {
        Log.d("MyApplication", cm.message() + " -- From line "
                             + cm.lineNumber() + " of "
                             + cm.sourceId() );
        return true;
      }

      public void onExceededDatabaseQuota(String url, String
            databaseIdentifier, long currentQuota, long estimatedSize, long
            totalUsedQuota, QuotaUpdater quotaUpdater) {
                            quotaUpdater.updateQuota(estimatedSize+65536);
            } 

     @SuppressWarnings("unused")
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType, String capture) {
            this.openFileChooser(uploadMsg);
         }

     @SuppressWarnings("unused")
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType) {
        this.openFileChooser(uploadMsg);
     }

     public void openFileChooser(ValueCallback<Uri> uploadMsg) {

         mUploadMessage = uploadMsg;
        PesScreen.this.openImageIntent();
     }
    });        

...

private void openImageIntent() {

final File root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); //file storage


    root.mkdirs();
    int nCnt = 1;
    if ( root.listFiles() != null )
        nCnt = root.listFiles().length;
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    final String fname =  String.format("/dest-%s-%d.jpg", sdf.format(Calendar.getInstance().getTime()), nCnt);

    final File sdImageMainDirectory = new File(root.getAbsolutePath() + fname);
    outputFileUri = Uri.fromFile(sdImageMainDirectory);
//selection Photo/Gallery dialog
    AlertDialog.Builder alert = new AlertDialog.Builder(this);

    alert.setTitle("Select source");

    final CharSequence[] items = {"Photo", "Gallery"};
    alert.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int whichButton) {

            dialog.dismiss();
            if( whichButton == 0)
            {
                Intent chooserIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                chooserIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
                startActivityForResult(chooserIntent, CAMERA_RESULTCODE);
            }
            if( whichButton == 1)
            {
                Intent chooserIntent = new Intent(Intent.ACTION_GET_CONTENT);
                chooserIntent.setType("image/*");
                startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
            }
      }
    });
    alert.setOnCancelListener(new DialogInterface.OnCancelListener() {
        @Override
        public void onCancel(DialogInterface dialog) {

        //here we have to handle BACK button/cancel 
            if ( mUploadMessage!= null ){
                mUploadMessage.onReceiveValue(null);
            }
            mUploadMessage = null;
            dialog.dismiss();
        }
    });
    alert.create().show();
OSP
  • 1,458
  • 1
  • 14
  • 15
2

This code will help to solve the issue

Just update the MainActivity.Java file with the bellow code

public class MainActivity extends Activity{
    private WebView mWebview ;
    private ValueCallback<Uri> mUploadMessage;
    public ValueCallback<Uri[]> uploadMessage;
    public static final int REQUEST_SELECT_FILE = 100;
    private final static int FILECHOOSER_RESULTCODE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        mWebview  = (WebView) findViewById(R.id.help_webview);
        WebSettings webSettings = mWebview.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setPluginState(WebSettings.PluginState.ON);
        webSettings.setSupportZoom(true);
        webSettings.setAllowContentAccess(true);



        final Activity activity = this;
        mWebview.setWebViewClient(new WebViewClient() {
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                Toast.makeText(activity, description, Toast.LENGTH_SHORT).show();
            }
        });

        mWebview .loadUrl("http://www.google.com");

        String permission = Manifest.permission.CAMERA;
        int grant = ContextCompat.checkSelfPermission(this, permission);
        if (grant != PackageManager.PERMISSION_GRANTED) {
            String[] permission_list = new String[1];
            permission_list[0] = permission;
            ActivityCompat.requestPermissions(this, permission_list, 1);
        }


        mWebview.setWebChromeClient(new WebChromeClient()
        {
            // For 3.0+ Devices (Start)
            // onActivityResult attached before constructor
            protected void openFileChooser(ValueCallback uploadMsg, String acceptType)
            {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
                startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
            }


            // For Lollipop 5.0+ Devices
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
            {
                if (uploadMessage != null) {
                    uploadMessage.onReceiveValue(null);
                    uploadMessage = null;
                }

                uploadMessage = filePathCallback;

                Intent intent = fileChooserParams.createIntent();
                try
                {
                    startActivityForResult(intent, REQUEST_SELECT_FILE);
                } catch (ActivityNotFoundException e)
                {
                    uploadMessage = null;
                    Toast.makeText(MainActivity.this.getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
                    return false;
                }
                return true;
            }

            //For Android 4.1 only
            protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
            {
                mUploadMessage = uploadMsg;
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("image/*");
                startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE);
            }

            protected void openFileChooser(ValueCallback<Uri> uploadMsg)
            {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
                startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
            }
        });



    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent)
    {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            if (requestCode == REQUEST_SELECT_FILE)
            {
                if (uploadMessage == null)
                    return;
                uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
                uploadMessage = null;
            }
        }
        else if (requestCode == FILECHOOSER_RESULTCODE)
        {
            if (null == mUploadMessage)
                return;
            // Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
            // Use RESULT_OK only if you're implementing WebView inside an Activity
            Uri result = intent == null || resultCode != MainActivity.RESULT_OK ? null : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        }
        else
            Toast.makeText(MainActivity.this.getApplicationContext(), "Failed to Upload Image", Toast.LENGTH_LONG).show();
    }

Make sure to enable the permission in the AndroidManifest.xml update the following lines

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

It worked for me try with this code.

Pronab Roy
  • 1,058
  • 1
  • 14
  • 18
  • This works but it only allowed me to upload videos, it also did not bring up the camera option instead it just opened up the file chooser. – TheLearner Sep 21 '17 at 05:25
  • So what exactly you need if you tell it clearly it'll helpful @TheLearner – Pronab Roy Sep 21 '17 at 07:53
  • What I need is to upload images and videos to a website through a webview. I would like it if a user decided to use their camera the photo they took to be uploaded and also if they selected an image/video for it to be uploaded to the webview. – TheLearner Sep 21 '17 at 15:26
  • Another major question for me is, where is it in the code that the image is being returned? I am having a hard time finding that part out.Thank you. – TheLearner Sep 21 '17 at 15:27
  • The user can upload the photo and video and the other files with the help of this code it works fine, Can you please tell me the **apk** version of your Application because it varies the code I give here it works fine to the targeted **Marshmallow** version and while running the app it asks the user to allow the camera and the storage so I already upload the file @TheLearner – Pronab Roy Sep 22 '17 at 07:37
  • I noticed that the code works on Marshmellow devices but didn't work on the Google PIxel or Nexus running Nouget. Also since the permissions are written in the manifest why do we not need to ask for permissions inside the code to access the camera or read the external storage? – TheLearner Sep 22 '17 at 16:46
  • https://developer.android.com/training/permissions/requesting.html go to this link you'll know why it needs to write the code to grant the permissions @TheLearner – Pronab Roy Sep 23 '17 at 07:47
  • I understand why the code needs permissions but I don't see in you code how you handle them if one is denied . In this case would the app crash? – TheLearner Sep 23 '17 at 21:52
  • No App will not crash only after giving the permission you can use the specific feature, but if any problem occur then regarding that situation and based on the problem you can get the solution @TheLearner – Pronab Roy Sep 24 '17 at 06:59
  • How about if we want to ask for the camera permission when the file chooser is selected from webview? – TheLearner Sep 26 '17 at 19:25
  • The process is same you should see this question and will get the answer go to the following link https://stackoverflow.com/questions/45837123/how-to-get-permission-to-access-camera-in-android-webview – Pronab Roy Oct 03 '17 at 05:15
  • I've already added the permission in manifest file check that post @TheLearner – Pronab Roy Oct 06 '17 at 09:36