Home | History | Annotate | Download | only in browser
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.browser;
     18 
     19 import android.app.Activity;
     20 import android.content.ActivityNotFoundException;
     21 import android.content.ClipData;
     22 import android.content.Intent;
     23 import android.net.Uri;
     24 import android.provider.MediaStore;
     25 import android.support.v4.content.FileProvider;
     26 import android.webkit.WebChromeClient.FileChooserParams;
     27 import android.webkit.ValueCallback;
     28 import android.widget.Toast;
     29 
     30 import java.io.File;
     31 
     32 /**
     33  * Handle the file upload. This does not support selecting multiple files yet.
     34  */
     35 public class UploadHandler {
     36     private final static String IMAGE_MIME_TYPE = "image/*";
     37     private final static String VIDEO_MIME_TYPE = "video/*";
     38     private final static String AUDIO_MIME_TYPE = "audio/*";
     39 
     40     private final static String FILE_PROVIDER_AUTHORITY = "com.android.browser-classic.file";
     41 
     42     /*
     43      * The Object used to inform the WebView of the file to upload.
     44      */
     45     private ValueCallback<Uri[]> mUploadMessage;
     46 
     47     private boolean mHandled;
     48     private Controller mController;
     49     private FileChooserParams mParams;
     50     private Uri mCapturedMedia;
     51 
     52     public UploadHandler(Controller controller) {
     53         mController = controller;
     54     }
     55 
     56     boolean handled() {
     57         return mHandled;
     58     }
     59 
     60     void onResult(int resultCode, Intent intent) {
     61         Uri[] uris;
     62         // As the media capture is always supported, we can't use
     63         // FileChooserParams.parseResult().
     64         uris = parseResult(resultCode, intent);
     65         mUploadMessage.onReceiveValue(uris);
     66         mHandled = true;
     67     }
     68 
     69     void openFileChooser(ValueCallback<Uri[]> callback, FileChooserParams fileChooserParams) {
     70 
     71         if (mUploadMessage != null) {
     72             // Already a file picker operation in progress.
     73             return;
     74         }
     75 
     76         mUploadMessage = callback;
     77         mParams = fileChooserParams;
     78         Intent[] captureIntents = createCaptureIntent();
     79         assert(captureIntents != null && captureIntents.length > 0);
     80         Intent intent = null;
     81         // Go to the media capture directly if capture is specified, this is the
     82         // preferred way.
     83         if (fileChooserParams.isCaptureEnabled() && captureIntents.length == 1) {
     84             intent = captureIntents[0];
     85         } else {
     86             intent = new Intent(Intent.ACTION_CHOOSER);
     87             intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, captureIntents);
     88             intent.putExtra(Intent.EXTRA_INTENT, fileChooserParams.createIntent());
     89         }
     90         startActivity(intent);
     91     }
     92 
     93     private Uri[] parseResult(int resultCode, Intent intent) {
     94         if (resultCode == Activity.RESULT_CANCELED) {
     95             return null;
     96         }
     97         Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
     98                 : intent.getData();
     99 
    100         // As we ask the camera to save the result of the user taking
    101         // a picture, the camera application does not return anything other
    102         // than RESULT_OK. So we need to check whether the file we expected
    103         // was written to disk in the in the case that we
    104         // did not get an intent returned but did get a RESULT_OK. If it was,
    105         // we assume that this result has came back from the camera.
    106         if (result == null && intent == null && resultCode == Activity.RESULT_OK
    107                 && mCapturedMedia != null) {
    108             result = mCapturedMedia;
    109         }
    110 
    111         Uri[] uris = null;
    112         if (result != null) {
    113             uris = new Uri[1];
    114             uris[0] = result;
    115         }
    116         return uris;
    117     }
    118 
    119     private void startActivity(Intent intent) {
    120         try {
    121             mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
    122         } catch (ActivityNotFoundException e) {
    123             // No installed app was able to handle the intent that
    124             // we sent, so file upload is effectively disabled.
    125             Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
    126                     Toast.LENGTH_LONG).show();
    127         }
    128     }
    129 
    130     private Intent[] createCaptureIntent() {
    131         String mimeType = "*/*";
    132         String[] acceptTypes = mParams.getAcceptTypes();
    133         if ( acceptTypes != null && acceptTypes.length > 0) {
    134             mimeType = acceptTypes[0];
    135         }
    136         Intent[] intents;
    137         if (mimeType.equals(IMAGE_MIME_TYPE)) {
    138             intents = new Intent[1];
    139             intents[0] = createCameraIntent(createTempFileContentUri(".jpg"));
    140         } else if (mimeType.equals(VIDEO_MIME_TYPE)) {
    141             intents = new Intent[1];
    142             intents[0] = createCamcorderIntent();
    143         } else if (mimeType.equals(AUDIO_MIME_TYPE)) {
    144             intents = new Intent[1];
    145             intents[0] = createSoundRecorderIntent();
    146         } else {
    147             intents = new Intent[3];
    148             intents[0] = createCameraIntent(createTempFileContentUri(".jpg"));
    149             intents[1] = createCamcorderIntent();
    150             intents[2] = createSoundRecorderIntent();
    151         }
    152         return intents;
    153     }
    154 
    155     private Uri createTempFileContentUri(String suffix) {
    156         try {
    157             File mediaPath = new File(mController.getActivity().getFilesDir(), "captured_media");
    158             if (!mediaPath.exists() && !mediaPath.mkdir()) {
    159                 throw new RuntimeException("Folder cannot be created.");
    160             }
    161             File mediaFile = File.createTempFile(
    162                     String.valueOf(System.currentTimeMillis()), suffix, mediaPath);
    163             return FileProvider.getUriForFile(mController.getActivity(),
    164                     FILE_PROVIDER_AUTHORITY, mediaFile);
    165         } catch (java.io.IOException e) {
    166             throw new RuntimeException(e);
    167         }
    168     }
    169 
    170     private Intent createCameraIntent(Uri contentUri) {
    171         if (contentUri == null) throw new IllegalArgumentException();
    172         mCapturedMedia = contentUri;
    173         Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    174         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
    175                   Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    176         intent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedMedia);
    177         intent.setClipData(ClipData.newUri(mController.getActivity().getContentResolver(),
    178                 FILE_PROVIDER_AUTHORITY, mCapturedMedia));
    179         return intent;
    180     }
    181 
    182     private Intent createCamcorderIntent() {
    183         return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    184     }
    185 
    186     private Intent createSoundRecorderIntent() {
    187         return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
    188     }
    189 }
    190