Tuesday, February 22, 2011

File upload in through WebView on Android

Getting <input type="file" /> to work on Android in WebView of custom app is pain, but doable. I have googled for hours and found nothing about this, eventually I got Android's stock browsers source from their git repository, and found out they are using undocumented method of WebCrhomeClient to support this functionality. Note this method is hidden with @hide annotation which means it is not supposed to be used. It works well on 2.2 and 2.3, and I had limited success on 2.1. So here is the code which should give you idea:


public class MyAwesomeActivity extends Activity {
    
 private WebView wv;
 
 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;
            
  }
 }
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  wv = new WebView(this);
  wv.setWebViewClient(new WebViewClient());
  wv.setWebChromeClient(new WebChromeClient()
  {
         //The undocumented magic method override
         //Eclipse will swear at you if you try to put @Override here
         public void openFileChooser(ValueCallback<Uri> uploadMsg) {
         
          mUploadMessage = uploadMsg;
          Intent i = new Intent(Intent.ACTION_GET_CONTENT);
          i.addCategory(Intent.CATEGORY_OPENABLE);
          i.setType("image/*");
          MyAwesomeActivity.this.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);
   
         }
  });

  setContentView(wv);
 }
  


Edit (response to comments): 


Since there are still so many people interested in implementing this, and I am not very often on here, I'll try to respond the comments here:


1) I don't have a full code package with this that I can share. I don't do Android development lately (unfortunately) so I don't even have eclipse on my workstation at moment. Maybe I will do that some day. But I can assure you that this snippet worked and the apps I developed using this technique are still on the market.


2) Step by step process? Probably not... If you dig basics of how Activities and Intents work this code should be pretty self explanatory.


3) I am not 100% sure what file types would work using this technique. I used it for images only. I assume that everything that would work through the Browser app should work through here as well. Try experimenting with the types, follow the standards. Most likely the problem is on the web-server end rather than on Android, you could use Wireshark to see what's really going on.


4) The warning about openFileChooser never used locally is normal, in fact I'm pretty sure it is supposed to give you the warning. If it's not working out for you the problem is somewhere else.


5) I'd be surprised it'd work out of box on honeycomb. There is no source code for honeycomb available as far as I know so I guess no way to check out how the Browser app does it there. But the SDK is still available and someone with reverse engineering skills could quickly determine what methods are they calling there and give it a try.