• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UploadHandler(Controller controller)47     public UploadHandler(Controller controller) {
48         mController = controller;
49     }
50 
getFilePath()51     String getFilePath() {
52         return mCameraFilePath;
53     }
54 
handled()55     boolean handled() {
56         return mHandled;
57     }
58 
onResult(int resultCode, Intent intent)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 
openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)93     void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
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' and the default value should be 'filesystem'.
106         String mediaSource = mediaSourceValueFileSystem;
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         if (capture.length() > 0) {
120             mediaSource = capture;
121         }
122 
123         if (capture.equals(mediaSourceValueFileSystem)) {
124             // To maintain backwards compatibility with the previous implementation
125             // of the media capture API, if the value of the 'capture' attribute is
126             // "filesystem", we should examine the accept-type for a MIME type that
127             // may specify a different capture value.
128             for (String p : params) {
129                 String[] keyValue = p.split("=");
130                 if (keyValue.length == 2) {
131                     // Process key=value parameters.
132                     if (mediaSourceKey.equals(keyValue[0])) {
133                         mediaSource = keyValue[1];
134                     }
135                 }
136             }
137         }
138 
139         //Ensure it is not still set from a previous upload.
140         mCameraFilePath = null;
141 
142         if (mimeType.equals(imageMimeType)) {
143             if (mediaSource.equals(mediaSourceValueCamera)) {
144                 // Specified 'image/*' and requested the camera, so go ahead and launch the
145                 // camera directly.
146                 startActivity(createCameraIntent());
147                 return;
148             } else {
149                 // Specified just 'image/*', capture=filesystem, or an invalid capture parameter.
150                 // In all these cases we show a traditional picker filetered on accept type
151                 // so launch an intent for both the Camera and image/* OPENABLE.
152                 Intent chooser = createChooserIntent(createCameraIntent());
153                 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
154                 startActivity(chooser);
155                 return;
156             }
157         } else if (mimeType.equals(videoMimeType)) {
158             if (mediaSource.equals(mediaSourceValueCamcorder)) {
159                 // Specified 'video/*' and requested the camcorder, so go ahead and launch the
160                 // camcorder directly.
161                 startActivity(createCamcorderIntent());
162                 return;
163            } else {
164                 // Specified just 'video/*', capture=filesystem or an invalid capture parameter.
165                 // In all these cases we show an intent for the traditional file picker, filtered
166                 // on accept type so launch an intent for both camcorder and video/* OPENABLE.
167                 Intent chooser = createChooserIntent(createCamcorderIntent());
168                 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
169                 startActivity(chooser);
170                 return;
171             }
172         } else if (mimeType.equals(audioMimeType)) {
173             if (mediaSource.equals(mediaSourceValueMicrophone)) {
174                 // Specified 'audio/*' and requested microphone, so go ahead and launch the sound
175                 // recorder.
176                 startActivity(createSoundRecorderIntent());
177                 return;
178             } else {
179                 // Specified just 'audio/*',  capture=filesystem of an invalid capture parameter.
180                 // In all these cases so go ahead and launch an intent for both the sound
181                 // recorder and audio/* OPENABLE.
182                 Intent chooser = createChooserIntent(createSoundRecorderIntent());
183                 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
184                 startActivity(chooser);
185                 return;
186             }
187         }
188 
189         // No special handling based on the accept type was necessary, so trigger the default
190         // file upload chooser.
191         startActivity(createDefaultOpenableIntent());
192     }
193 
startActivity(Intent intent)194     private void startActivity(Intent intent) {
195         try {
196             mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
197         } catch (ActivityNotFoundException e) {
198             // No installed app was able to handle the intent that
199             // we sent, so fallback to the default file upload control.
200             try {
201                 mCaughtActivityNotFoundException = true;
202                 mController.getActivity().startActivityForResult(createDefaultOpenableIntent(),
203                         Controller.FILE_SELECTED);
204             } catch (ActivityNotFoundException e2) {
205                 // Nothing can return us a file, so file upload is effectively disabled.
206                 Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
207                         Toast.LENGTH_LONG).show();
208             }
209         }
210     }
211 
createDefaultOpenableIntent()212     private Intent createDefaultOpenableIntent() {
213         // Create and return a chooser with the default OPENABLE
214         // actions including the camera, camcorder and sound
215         // recorder where available.
216         Intent i = new Intent(Intent.ACTION_GET_CONTENT);
217         i.addCategory(Intent.CATEGORY_OPENABLE);
218         i.setType("*/*");
219 
220         Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
221                 createSoundRecorderIntent());
222         chooser.putExtra(Intent.EXTRA_INTENT, i);
223         return chooser;
224     }
225 
createChooserIntent(Intent... intents)226     private Intent createChooserIntent(Intent... intents) {
227         Intent chooser = new Intent(Intent.ACTION_CHOOSER);
228         chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
229         chooser.putExtra(Intent.EXTRA_TITLE,
230                 mController.getActivity().getResources()
231                         .getString(R.string.choose_upload));
232         return chooser;
233     }
234 
createOpenableIntent(String type)235     private Intent createOpenableIntent(String type) {
236         Intent i = new Intent(Intent.ACTION_GET_CONTENT);
237         i.addCategory(Intent.CATEGORY_OPENABLE);
238         i.setType(type);
239         return i;
240     }
241 
createCameraIntent()242     private Intent createCameraIntent() {
243         Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
244         File externalDataDir = Environment.getExternalStoragePublicDirectory(
245                 Environment.DIRECTORY_DCIM);
246         File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
247                 File.separator + "browser-photos");
248         cameraDataDir.mkdirs();
249         mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
250                 System.currentTimeMillis() + ".jpg";
251         cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
252         return cameraIntent;
253     }
254 
createCamcorderIntent()255     private Intent createCamcorderIntent() {
256         return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
257     }
258 
createSoundRecorderIntent()259     private Intent createSoundRecorderIntent() {
260         return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
261     }
262 
263 }
264