blob: 349f67fbb40ccd8e023bd2d634946df353211a19 [file] [log] [blame]
Michael Kolb8233fac2010-10-26 16:08:53 -07001/*
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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Michael Kolb8233fac2010-10-26 16:08:53 -070018
19import android.app.Activity;
Ben Murdoche4c0cae2011-02-18 11:25:38 +000020import android.content.ActivityNotFoundException;
Michael Kolb8233fac2010-10-26 16:08:53 -070021import android.content.Intent;
kaiyiz4b59d262013-07-30 10:41:38 +080022import android.database.Cursor;
Michael Kolb8233fac2010-10-26 16:08:53 -070023import android.net.Uri;
24import android.os.Environment;
25import android.provider.MediaStore;
26import android.webkit.ValueCallback;
Ben Murdoche4c0cae2011-02-18 11:25:38 +000027import android.widget.Toast;
Michael Kolb8233fac2010-10-26 16:08:53 -070028
Bijan Amirzada41242f22014-03-21 12:12:18 -070029import com.android.browser.R;
30import com.android.browser.reflect.ReflectHelper;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080031
Michael Kolb8233fac2010-10-26 16:08:53 -070032import java.io.File;
33import java.util.Vector;
34
35/**
36 * Handle the file upload callbacks from WebView here
37 */
38public class UploadHandler {
39
40 /*
41 * The Object used to inform the WebView of the file to upload.
42 */
43 private ValueCallback<Uri> mUploadMessage;
44 private String mCameraFilePath;
45
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000046 private boolean mHandled;
47 private boolean mCaughtActivityNotFoundException;
48
Michael Kolb8233fac2010-10-26 16:08:53 -070049 private Controller mController;
50
51 public UploadHandler(Controller controller) {
52 mController = controller;
53 }
54
55 String getFilePath() {
56 return mCameraFilePath;
57 }
58
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000059 boolean handled() {
60 return mHandled;
61 }
62
kaiyiz4b59d262013-07-30 10:41:38 +080063 private boolean isDrmFileUpload(Uri uri) {
64 if (uri == null) return false;
65
66 String path = null;
67 String scheme = uri.getScheme();
68 if ("content".equals(scheme)) {
69 String[] proj = null;
70 if (uri.toString().contains("/images/")) {
71 proj = new String[]{MediaStore.Images.Media.DATA};
72 } else if (uri.toString().contains("/audio/")) {
73 proj = new String[]{MediaStore.Audio.Media.DATA};
74 } else if (uri.toString().contains("/video/")) {
75 proj = new String[]{MediaStore.Video.Media.DATA};
76 }
77 Cursor cursor = mController.getActivity().managedQuery(uri, proj, null, null, null);
78 if (cursor != null && cursor.moveToFirst() && proj != null) {
79 path = cursor.getString(0);
80 }
81 } else if ("file".equals(scheme)) {
82 path = uri.getPath();
83 }
84 if (path != null) {
85 if (path.endsWith(".fl") || path.endsWith(".dm")
86 || path.endsWith(".dcf") || path.endsWith(".dr") || path.endsWith(".dd")) {
87 Toast.makeText(mController.getContext(), R.string.drm_file_unsupported,
88 Toast.LENGTH_LONG).show();
89 return true;
90 }
91 }
92 return false;
93 }
94
Michael Kolb8233fac2010-10-26 16:08:53 -070095 void onResult(int resultCode, Intent intent) {
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000096
97 if (resultCode == Activity.RESULT_CANCELED && mCaughtActivityNotFoundException) {
98 // Couldn't resolve an activity, we are going to try again so skip
99 // this result.
100 mCaughtActivityNotFoundException = false;
101 return;
102 }
103
Michael Kolb8233fac2010-10-26 16:08:53 -0700104 Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
105 : intent.getData();
106
107 // As we ask the camera to save the result of the user taking
108 // a picture, the camera application does not return anything other
109 // than RESULT_OK. So we need to check whether the file we expected
110 // was written to disk in the in the case that we
111 // did not get an intent returned but did get a RESULT_OK. If it was,
112 // we assume that this result has came back from the camera.
113 if (result == null && intent == null && resultCode == Activity.RESULT_OK) {
114 File cameraFile = new File(mCameraFilePath);
115 if (cameraFile.exists()) {
116 result = Uri.fromFile(cameraFile);
117 // Broadcast to the media scanner that we have a new photo
118 // so it will be added into the gallery for the user.
119 mController.getActivity().sendBroadcast(
120 new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
121 }
122 }
123
kaiyiz4b59d262013-07-30 10:41:38 +0800124 // add unsupport uploading drm file feature for carrier.
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800125 Object[] params = {new String("persist.env.browser.drmupload"),
126 Boolean.valueOf(false)};
127 Class[] type = new Class[] {String.class, boolean.class};
Bijan Amirzada58383e72014-04-01 14:45:22 -0700128 Boolean drmUpload = (Boolean) ReflectHelper.invokeMethod(
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800129 "android.os.SystemProperties", "getBoolean", type, params);
kaiyiz4b59d262013-07-30 10:41:38 +0800130 if (drmUpload && isDrmFileUpload(result)) {
131 mUploadMessage.onReceiveValue(null);
132 } else {
133 mUploadMessage.onReceiveValue(result);
134 }
135
Ben Murdoch51f6a2f2011-02-21 12:27:07 +0000136 mHandled = true;
137 mCaughtActivityNotFoundException = false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700138 }
139
Ben Murdoch8cad4132012-01-11 10:56:43 +0000140 void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700141
142 final String imageMimeType = "image/*";
143 final String videoMimeType = "video/*";
144 final String audioMimeType = "audio/*";
145 final String mediaSourceKey = "capture";
146 final String mediaSourceValueCamera = "camera";
147 final String mediaSourceValueFileSystem = "filesystem";
148 final String mediaSourceValueCamcorder = "camcorder";
149 final String mediaSourceValueMicrophone = "microphone";
150
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000151 // According to the spec, media source can be 'filesystem' or 'camera' or 'camcorder'
Ben Murdoch8cad4132012-01-11 10:56:43 +0000152 // or 'microphone' and the default value should be 'filesystem'.
153 String mediaSource = mediaSourceValueFileSystem;
Michael Kolb8233fac2010-10-26 16:08:53 -0700154
Michael Kolb8233fac2010-10-26 16:08:53 -0700155 if (mUploadMessage != null) {
156 // Already a file picker operation in progress.
157 return;
158 }
159
160 mUploadMessage = uploadMsg;
161
162 // Parse the accept type.
163 String params[] = acceptType.split(";");
164 String mimeType = params[0];
165
Ben Murdoch8cad4132012-01-11 10:56:43 +0000166 if (capture.length() > 0) {
167 mediaSource = capture;
168 }
169
170 if (capture.equals(mediaSourceValueFileSystem)) {
171 // To maintain backwards compatibility with the previous implementation
172 // of the media capture API, if the value of the 'capture' attribute is
173 // "filesystem", we should examine the accept-type for a MIME type that
174 // may specify a different capture value.
175 for (String p : params) {
176 String[] keyValue = p.split("=");
177 if (keyValue.length == 2) {
178 // Process key=value parameters.
179 if (mediaSourceKey.equals(keyValue[0])) {
180 mediaSource = keyValue[1];
181 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700182 }
183 }
184 }
185
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000186 //Ensure it is not still set from a previous upload.
187 mCameraFilePath = null;
188
189 if (mimeType.equals(imageMimeType)) {
190 if (mediaSource.equals(mediaSourceValueCamera)) {
191 // Specified 'image/*' and requested the camera, so go ahead and launch the
192 // camera directly.
193 startActivity(createCameraIntent());
194 return;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000195 } else {
Ben Murdoch8cad4132012-01-11 10:56:43 +0000196 // Specified just 'image/*', capture=filesystem, or an invalid capture parameter.
197 // In all these cases we show a traditional picker filetered on accept type
198 // so launch an intent for both the Camera and image/* OPENABLE.
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000199 Intent chooser = createChooserIntent(createCameraIntent());
200 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
201 startActivity(chooser);
202 return;
203 }
204 } else if (mimeType.equals(videoMimeType)) {
205 if (mediaSource.equals(mediaSourceValueCamcorder)) {
206 // Specified 'video/*' and requested the camcorder, so go ahead and launch the
207 // camcorder directly.
208 startActivity(createCamcorderIntent());
209 return;
Ben Murdoch8cad4132012-01-11 10:56:43 +0000210 } else {
211 // Specified just 'video/*', capture=filesystem or an invalid capture parameter.
212 // In all these cases we show an intent for the traditional file picker, filtered
213 // on accept type so launch an intent for both camcorder and video/* OPENABLE.
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000214 Intent chooser = createChooserIntent(createCamcorderIntent());
215 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
216 startActivity(chooser);
217 return;
218 }
219 } else if (mimeType.equals(audioMimeType)) {
220 if (mediaSource.equals(mediaSourceValueMicrophone)) {
221 // Specified 'audio/*' and requested microphone, so go ahead and launch the sound
222 // recorder.
223 startActivity(createSoundRecorderIntent());
224 return;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000225 } else {
Ben Murdoch8cad4132012-01-11 10:56:43 +0000226 // Specified just 'audio/*', capture=filesystem of an invalid capture parameter.
227 // In all these cases so go ahead and launch an intent for both the sound
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000228 // recorder and audio/* OPENABLE.
229 Intent chooser = createChooserIntent(createSoundRecorderIntent());
230 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
231 startActivity(chooser);
232 return;
233 }
234 }
235
236 // No special handling based on the accept type was necessary, so trigger the default
237 // file upload chooser.
238 startActivity(createDefaultOpenableIntent());
239 }
240
241 private void startActivity(Intent intent) {
242 try {
243 mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
244 } catch (ActivityNotFoundException e) {
245 // No installed app was able to handle the intent that
246 // we sent, so fallback to the default file upload control.
247 try {
Ben Murdoch51f6a2f2011-02-21 12:27:07 +0000248 mCaughtActivityNotFoundException = true;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000249 mController.getActivity().startActivityForResult(createDefaultOpenableIntent(),
250 Controller.FILE_SELECTED);
251 } catch (ActivityNotFoundException e2) {
252 // Nothing can return us a file, so file upload is effectively disabled.
253 Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
254 Toast.LENGTH_LONG).show();
255 }
256 }
257 }
258
259 private Intent createDefaultOpenableIntent() {
260 // Create and return a chooser with the default OPENABLE
261 // actions including the camera, camcorder and sound
262 // recorder where available.
Michael Kolb8233fac2010-10-26 16:08:53 -0700263 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
264 i.addCategory(Intent.CATEGORY_OPENABLE);
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000265 i.setType("*/*");
Michael Kolb8233fac2010-10-26 16:08:53 -0700266
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000267 Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
268 createSoundRecorderIntent());
269 chooser.putExtra(Intent.EXTRA_INTENT, i);
270 return chooser;
271 }
272
273 private Intent createChooserIntent(Intent... intents) {
274 Intent chooser = new Intent(Intent.ACTION_CHOOSER);
275 chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
276 chooser.putExtra(Intent.EXTRA_TITLE,
277 mController.getActivity().getResources()
278 .getString(R.string.choose_upload));
279 return chooser;
280 }
281
282 private Intent createOpenableIntent(String type) {
283 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
284 i.addCategory(Intent.CATEGORY_OPENABLE);
285 i.setType(type);
286 return i;
287 }
288
289 private Intent createCameraIntent() {
Michael Kolb8233fac2010-10-26 16:08:53 -0700290 Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
291 File externalDataDir = Environment.getExternalStoragePublicDirectory(
292 Environment.DIRECTORY_DCIM);
293 File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
294 File.separator + "browser-photos");
295 cameraDataDir.mkdirs();
296 mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
297 System.currentTimeMillis() + ".jpg";
298 cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000299 return cameraIntent;
Michael Kolb8233fac2010-10-26 16:08:53 -0700300 }
301
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000302 private Intent createCamcorderIntent() {
303 return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
304 }
305
306 private Intent createSoundRecorderIntent() {
307 return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
Michael Kolb8233fac2010-10-26 16:08:53 -0700308 }
309
310}