blob: 544419b662334764ec6429d71212b1ff3af0c121 [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
17package com.android.browser;
18
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;
kaiyiz4b59d262013-07-30 10:41:38 +080025import android.os.SystemProperties;
Michael Kolb8233fac2010-10-26 16:08:53 -070026import android.provider.MediaStore;
27import android.webkit.ValueCallback;
Ben Murdoche4c0cae2011-02-18 11:25:38 +000028import android.widget.Toast;
Michael Kolb8233fac2010-10-26 16:08:53 -070029
30import java.io.File;
31import java.util.Vector;
32
33/**
34 * Handle the file upload callbacks from WebView here
35 */
36public class UploadHandler {
37
38 /*
39 * The Object used to inform the WebView of the file to upload.
40 */
41 private ValueCallback<Uri> mUploadMessage;
42 private String mCameraFilePath;
43
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000044 private boolean mHandled;
45 private boolean mCaughtActivityNotFoundException;
46
Michael Kolb8233fac2010-10-26 16:08:53 -070047 private Controller mController;
48
49 public UploadHandler(Controller controller) {
50 mController = controller;
51 }
52
53 String getFilePath() {
54 return mCameraFilePath;
55 }
56
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000057 boolean handled() {
58 return mHandled;
59 }
60
kaiyiz4b59d262013-07-30 10:41:38 +080061 private boolean isDrmFileUpload(Uri uri) {
62 if (uri == null) return false;
63
64 String path = null;
65 String scheme = uri.getScheme();
66 if ("content".equals(scheme)) {
67 String[] proj = null;
68 if (uri.toString().contains("/images/")) {
69 proj = new String[]{MediaStore.Images.Media.DATA};
70 } else if (uri.toString().contains("/audio/")) {
71 proj = new String[]{MediaStore.Audio.Media.DATA};
72 } else if (uri.toString().contains("/video/")) {
73 proj = new String[]{MediaStore.Video.Media.DATA};
74 }
75 Cursor cursor = mController.getActivity().managedQuery(uri, proj, null, null, null);
76 if (cursor != null && cursor.moveToFirst() && proj != null) {
77 path = cursor.getString(0);
78 }
79 } else if ("file".equals(scheme)) {
80 path = uri.getPath();
81 }
82 if (path != null) {
83 if (path.endsWith(".fl") || path.endsWith(".dm")
84 || path.endsWith(".dcf") || path.endsWith(".dr") || path.endsWith(".dd")) {
85 Toast.makeText(mController.getContext(), R.string.drm_file_unsupported,
86 Toast.LENGTH_LONG).show();
87 return true;
88 }
89 }
90 return false;
91 }
92
Michael Kolb8233fac2010-10-26 16:08:53 -070093 void onResult(int resultCode, Intent intent) {
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000094
95 if (resultCode == Activity.RESULT_CANCELED && mCaughtActivityNotFoundException) {
96 // Couldn't resolve an activity, we are going to try again so skip
97 // this result.
98 mCaughtActivityNotFoundException = false;
99 return;
100 }
101
Michael Kolb8233fac2010-10-26 16:08:53 -0700102 Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
103 : intent.getData();
104
105 // As we ask the camera to save the result of the user taking
106 // a picture, the camera application does not return anything other
107 // than RESULT_OK. So we need to check whether the file we expected
108 // was written to disk in the in the case that we
109 // did not get an intent returned but did get a RESULT_OK. If it was,
110 // we assume that this result has came back from the camera.
111 if (result == null && intent == null && resultCode == Activity.RESULT_OK) {
112 File cameraFile = new File(mCameraFilePath);
113 if (cameraFile.exists()) {
114 result = Uri.fromFile(cameraFile);
115 // Broadcast to the media scanner that we have a new photo
116 // so it will be added into the gallery for the user.
117 mController.getActivity().sendBroadcast(
118 new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
119 }
120 }
121
kaiyiz4b59d262013-07-30 10:41:38 +0800122 // add unsupport uploading drm file feature for carrier.
123 boolean drmUpload = SystemProperties.getBoolean("persist.env.browser.drmupload", false);
124 if (drmUpload && isDrmFileUpload(result)) {
125 mUploadMessage.onReceiveValue(null);
126 } else {
127 mUploadMessage.onReceiveValue(result);
128 }
129
Ben Murdoch51f6a2f2011-02-21 12:27:07 +0000130 mHandled = true;
131 mCaughtActivityNotFoundException = false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700132 }
133
Ben Murdoch8cad4132012-01-11 10:56:43 +0000134 void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700135
136 final String imageMimeType = "image/*";
137 final String videoMimeType = "video/*";
138 final String audioMimeType = "audio/*";
139 final String mediaSourceKey = "capture";
140 final String mediaSourceValueCamera = "camera";
141 final String mediaSourceValueFileSystem = "filesystem";
142 final String mediaSourceValueCamcorder = "camcorder";
143 final String mediaSourceValueMicrophone = "microphone";
144
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000145 // According to the spec, media source can be 'filesystem' or 'camera' or 'camcorder'
Ben Murdoch8cad4132012-01-11 10:56:43 +0000146 // or 'microphone' and the default value should be 'filesystem'.
147 String mediaSource = mediaSourceValueFileSystem;
Michael Kolb8233fac2010-10-26 16:08:53 -0700148
Michael Kolb8233fac2010-10-26 16:08:53 -0700149 if (mUploadMessage != null) {
150 // Already a file picker operation in progress.
151 return;
152 }
153
154 mUploadMessage = uploadMsg;
155
156 // Parse the accept type.
157 String params[] = acceptType.split(";");
158 String mimeType = params[0];
159
Ben Murdoch8cad4132012-01-11 10:56:43 +0000160 if (capture.length() > 0) {
161 mediaSource = capture;
162 }
163
164 if (capture.equals(mediaSourceValueFileSystem)) {
165 // To maintain backwards compatibility with the previous implementation
166 // of the media capture API, if the value of the 'capture' attribute is
167 // "filesystem", we should examine the accept-type for a MIME type that
168 // may specify a different capture value.
169 for (String p : params) {
170 String[] keyValue = p.split("=");
171 if (keyValue.length == 2) {
172 // Process key=value parameters.
173 if (mediaSourceKey.equals(keyValue[0])) {
174 mediaSource = keyValue[1];
175 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700176 }
177 }
178 }
179
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000180 //Ensure it is not still set from a previous upload.
181 mCameraFilePath = null;
182
183 if (mimeType.equals(imageMimeType)) {
184 if (mediaSource.equals(mediaSourceValueCamera)) {
185 // Specified 'image/*' and requested the camera, so go ahead and launch the
186 // camera directly.
187 startActivity(createCameraIntent());
188 return;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000189 } else {
Ben Murdoch8cad4132012-01-11 10:56:43 +0000190 // Specified just 'image/*', capture=filesystem, or an invalid capture parameter.
191 // In all these cases we show a traditional picker filetered on accept type
192 // so launch an intent for both the Camera and image/* OPENABLE.
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000193 Intent chooser = createChooserIntent(createCameraIntent());
194 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
195 startActivity(chooser);
196 return;
197 }
198 } else if (mimeType.equals(videoMimeType)) {
199 if (mediaSource.equals(mediaSourceValueCamcorder)) {
200 // Specified 'video/*' and requested the camcorder, so go ahead and launch the
201 // camcorder directly.
202 startActivity(createCamcorderIntent());
203 return;
Ben Murdoch8cad4132012-01-11 10:56:43 +0000204 } else {
205 // Specified just 'video/*', capture=filesystem or an invalid capture parameter.
206 // In all these cases we show an intent for the traditional file picker, filtered
207 // on accept type so launch an intent for both camcorder and video/* OPENABLE.
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000208 Intent chooser = createChooserIntent(createCamcorderIntent());
209 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
210 startActivity(chooser);
211 return;
212 }
213 } else if (mimeType.equals(audioMimeType)) {
214 if (mediaSource.equals(mediaSourceValueMicrophone)) {
215 // Specified 'audio/*' and requested microphone, so go ahead and launch the sound
216 // recorder.
217 startActivity(createSoundRecorderIntent());
218 return;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000219 } else {
Ben Murdoch8cad4132012-01-11 10:56:43 +0000220 // Specified just 'audio/*', capture=filesystem of an invalid capture parameter.
221 // In all these cases so go ahead and launch an intent for both the sound
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000222 // recorder and audio/* OPENABLE.
223 Intent chooser = createChooserIntent(createSoundRecorderIntent());
224 chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
225 startActivity(chooser);
226 return;
227 }
228 }
229
230 // No special handling based on the accept type was necessary, so trigger the default
231 // file upload chooser.
232 startActivity(createDefaultOpenableIntent());
233 }
234
235 private void startActivity(Intent intent) {
236 try {
237 mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
238 } catch (ActivityNotFoundException e) {
239 // No installed app was able to handle the intent that
240 // we sent, so fallback to the default file upload control.
241 try {
Ben Murdoch51f6a2f2011-02-21 12:27:07 +0000242 mCaughtActivityNotFoundException = true;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000243 mController.getActivity().startActivityForResult(createDefaultOpenableIntent(),
244 Controller.FILE_SELECTED);
245 } catch (ActivityNotFoundException e2) {
246 // Nothing can return us a file, so file upload is effectively disabled.
247 Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
248 Toast.LENGTH_LONG).show();
249 }
250 }
251 }
252
253 private Intent createDefaultOpenableIntent() {
254 // Create and return a chooser with the default OPENABLE
255 // actions including the camera, camcorder and sound
256 // recorder where available.
Michael Kolb8233fac2010-10-26 16:08:53 -0700257 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
258 i.addCategory(Intent.CATEGORY_OPENABLE);
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000259 i.setType("*/*");
Michael Kolb8233fac2010-10-26 16:08:53 -0700260
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000261 Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
262 createSoundRecorderIntent());
263 chooser.putExtra(Intent.EXTRA_INTENT, i);
264 return chooser;
265 }
266
267 private Intent createChooserIntent(Intent... intents) {
268 Intent chooser = new Intent(Intent.ACTION_CHOOSER);
269 chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
270 chooser.putExtra(Intent.EXTRA_TITLE,
271 mController.getActivity().getResources()
272 .getString(R.string.choose_upload));
273 return chooser;
274 }
275
276 private Intent createOpenableIntent(String type) {
277 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
278 i.addCategory(Intent.CATEGORY_OPENABLE);
279 i.setType(type);
280 return i;
281 }
282
283 private Intent createCameraIntent() {
Michael Kolb8233fac2010-10-26 16:08:53 -0700284 Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
285 File externalDataDir = Environment.getExternalStoragePublicDirectory(
286 Environment.DIRECTORY_DCIM);
287 File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
288 File.separator + "browser-photos");
289 cameraDataDir.mkdirs();
290 mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
291 System.currentTimeMillis() + ".jpg";
292 cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000293 return cameraIntent;
Michael Kolb8233fac2010-10-26 16:08:53 -0700294 }
295
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000296 private Intent createCamcorderIntent() {
297 return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
298 }
299
300 private Intent createSoundRecorderIntent() {
301 return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
Michael Kolb8233fac2010-10-26 16:08:53 -0700302 }
303
304}