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.Intent;
     22 import android.net.Uri;
     23 import android.os.Environment;
     24 import android.provider.MediaStore;
     25 import android.webkit.ValueCallback;
     26 import android.widget.Toast;
     27 
     28 import java.io.File;
     29 import java.util.Vector;
     30 
     31 /**
     32  * Handle the file upload callbacks from WebView here
     33  */
     34 public class UploadHandler {
     35 
     36     /*
     37      * The Object used to inform the WebView of the file to upload.
     38      */
     39     private ValueCallback<Uri> mUploadMessage;
     40     private String mCameraFilePath;
     41 
     42     private boolean mHandled;
     43     private boolean mCaughtActivityNotFoundException;
     44 
     45     private Controller mController;
     46 
     47     public UploadHandler(Controller controller) {
     48         mController = controller;
     49     }
     50 
     51     String getFilePath() {
     52         return mCameraFilePath;
     53     }
     54 
     55     boolean handled() {
     56         return mHandled;
     57     }
     58 
     59     void onResult(int resultCode, Intent intent) {
     60 
     61         if (resultCode == Activity.RESULT_CANCELED && mCaughtActivityNotFoundException) {
     62             // Couldn't resolve an activity, we are going to try again so skip
     63             // this result.
     64             mCaughtActivityNotFoundException = false;
     65             return;
     66         }
     67 
     68         Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
     69                 : intent.getData();
     70 
     71         // As we ask the camera to save the result of the user taking
     72         // a picture, the camera application does not return anything other
     73         // than RESULT_OK. So we need to check whether the file we expected
     74         // was written to disk in the in the case that we
     75         // did not get an intent returned but did get a RESULT_OK. If it was,
     76         // we assume that this result has came back from the camera.
     77         if (result == null && intent == null && resultCode == Activity.RESULT_OK) {
     78             File cameraFile = new File(mCameraFilePath);
     79             if (cameraFile.exists()) {
     80                 result = Uri.fromFile(cameraFile);
     81                 // Broadcast to the media scanner that we have a new photo
     82                 // so it will be added into the gallery for the user.
     83                 mController.getActivity().sendBroadcast(
     84                         new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
     85             }
     86         }
     87 
     88         mUploadMessage.onReceiveValue(result);
     89         mHandled = true;
     90         mCaughtActivityNotFoundException = false;
     91     }
     92 
     93     void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
     94 
     95         final String imageMimeType = "image/*";
     96         final String videoMimeType = "video/*";
     97         final String audioMimeType = "audio/*";
     98         final String mediaSourceKey = "capture";
     99         final String mediaSourceValueCamera = "camera";
    100         final String mediaSourceValueFileSystem = "filesystem";
    101         final String mediaSourceValueCamcorder = "camcorder";
    102         final String mediaSourceValueMicrophone = "microphone";
    103 
    104         // According to the spec, media source can be 'filesystem' or 'camera' or 'camcorder'
    105         // or 'microphone'.
    106         String mediaSource = "";
    107 
    108         if (mUploadMessage != null) {
    109             // Already a file picker operation in progress.
    110             return;
    111         }
    112 
    113         mUploadMessage = uploadMsg;
    114 
    115         // Parse the accept type.
    116         String params[] = acceptType.split(";");
    117         String mimeType = params[0];
    118 
    119         for (String p : params) {
    120             String[] keyValue = p.split("=");
    121             if (keyValue.length == 2) {
    122                 // Process key=value parameters.
    123                 if (mediaSourceKey.equals(keyValue[0])) {
    124                     mediaSource = keyValue[1];
    125                 }
    126             }
    127         }
    128 
    129         //Ensure it is not still set from a previous upload.
    130         mCameraFilePath = null;
    131 
    132         if (mimeType.equals(imageMimeType)) {
    133             if (mediaSource.equals(mediaSourceValueCamera)) {
    134                 // Specified 'image/*' and requested the camera, so go ahead and launch the
    135                 // camera directly.
    136                 startActivity(createCameraIntent());
    137                 return;
    138             } else if (mediaSource.equals(mediaSourceValueFileSystem)) {
    139                 // Specified 'image/*' and requested the filesystem, so go ahead and launch an
    140                 // OPENABLE intent.
    141                 startActivity(createOpenableIntent(imageMimeType));
    142                 return;
    143             } else {
    144                 // Specified just 'image/*', so launch an intent for both the Camera and image/*
    145                 // OPENABLE.
    146                 Intent chooser = createChooserIntent(createCameraIntent());
    147                 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
    148                 startActivity(chooser);
    149                 return;
    150             }
    151         } else if (mimeType.equals(videoMimeType)) {
    152             if (mediaSource.equals(mediaSourceValueCamcorder)) {
    153                 // Specified 'video/*' and requested the camcorder, so go ahead and launch the
    154                 // camcorder directly.
    155                 startActivity(createCamcorderIntent());
    156                 return;
    157             } else if (mediaSource.equals(mediaSourceValueFileSystem)) {
    158                 // Specified 'video/*' and requested the filesystem, so go ahead and launch an
    159                 // an OPENABLE intent.
    160                 startActivity(createOpenableIntent(videoMimeType));
    161                 return;
    162             } else {
    163                 // Specified just 'video/*', so go ahead and launch an intent for both camcorder and
    164                 // video/* OPENABLE.
    165                 Intent chooser = createChooserIntent(createCamcorderIntent());
    166                 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
    167                 startActivity(chooser);
    168                 return;
    169             }
    170         } else if (mimeType.equals(audioMimeType)) {
    171             if (mediaSource.equals(mediaSourceValueMicrophone)) {
    172                 // Specified 'audio/*' and requested microphone, so go ahead and launch the sound
    173                 // recorder.
    174                 startActivity(createSoundRecorderIntent());
    175                 return;
    176             } else if (mediaSource.equals(mediaSourceValueFileSystem)) {
    177                 // Specified 'audio/*' and requested filesystem, so go ahead and launch an
    178                 // OPENABLE intent.
    179                 startActivity(createOpenableIntent(audioMimeType));
    180                 return;
    181             } else {
    182                 // Specified just 'audio/*', so go ahead and launch an intent for both the sound
    183                 // recorder and audio/* OPENABLE.
    184                 Intent chooser = createChooserIntent(createSoundRecorderIntent());
    185                 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
    186                 startActivity(chooser);
    187                 return;
    188             }
    189         }
    190 
    191         // No special handling based on the accept type was necessary, so trigger the default
    192         // file upload chooser.
    193         startActivity(createDefaultOpenableIntent());
    194     }
    195 
    196     private void startActivity(Intent intent) {
    197         try {
    198             mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
    199         } catch (ActivityNotFoundException e) {
    200             // No installed app was able to handle the intent that
    201             // we sent, so fallback to the default file upload control.
    202             try {
    203                 mCaughtActivityNotFoundException = true;
    204                 mController.getActivity().startActivityForResult(createDefaultOpenableIntent(),
    205                         Controller.FILE_SELECTED);
    206             } catch (ActivityNotFoundException e2) {
    207                 // Nothing can return us a file, so file upload is effectively disabled.
    208                 Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
    209                         Toast.LENGTH_LONG).show();
    210             }
    211         }
    212     }
    213 
    214     private Intent createDefaultOpenableIntent() {
    215         // Create and return a chooser with the default OPENABLE
    216         // actions including the camera, camcorder and sound
    217         // recorder where available.
    218         Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    219         i.addCategory(Intent.CATEGORY_OPENABLE);
    220         i.setType("*/*");
    221 
    222         Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
    223                 createSoundRecorderIntent());
    224         chooser.putExtra(Intent.EXTRA_INTENT, i);
    225         return chooser;
    226     }
    227 
    228     private Intent createChooserIntent(Intent... intents) {
    229         Intent chooser = new Intent(Intent.ACTION_CHOOSER);
    230         chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
    231         chooser.putExtra(Intent.EXTRA_TITLE,
    232                 mController.getActivity().getResources()
    233                         .getString(R.string.choose_upload));
    234         return chooser;
    235     }
    236 
    237     private Intent createOpenableIntent(String type) {
    238         Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    239         i.addCategory(Intent.CATEGORY_OPENABLE);
    240         i.setType(type);
    241         return i;
    242     }
    243 
    244     private Intent createCameraIntent() {
    245         Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    246         File externalDataDir = Environment.getExternalStoragePublicDirectory(
    247                 Environment.DIRECTORY_DCIM);
    248         File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
    249                 File.separator + "browser-photos");
    250         cameraDataDir.mkdirs();
    251         mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
    252                 System.currentTimeMillis() + ".jpg";
    253         cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
    254         return cameraIntent;
    255     }
    256 
    257     private Intent createCamcorderIntent() {
    258         return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    259     }
    260 
    261     private Intent createSoundRecorderIntent() {
    262         return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
    263     }
    264 
    265 }
    266