blob: a9560bb505d3e62d362b7bfacdd36193b3d09fb8 [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;
22import android.net.Uri;
23import android.os.Environment;
24import android.provider.MediaStore;
25import android.webkit.ValueCallback;
Ben Murdoche4c0cae2011-02-18 11:25:38 +000026import android.widget.Toast;
Michael Kolb8233fac2010-10-26 16:08:53 -070027
28import java.io.File;
29import java.util.Vector;
30
31/**
32 * Handle the file upload callbacks from WebView here
33 */
34public 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
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000042 private boolean mHandled;
43 private boolean mCaughtActivityNotFoundException;
44
Michael Kolb8233fac2010-10-26 16:08:53 -070045 private Controller mController;
46
47 public UploadHandler(Controller controller) {
48 mController = controller;
49 }
50
51 String getFilePath() {
52 return mCameraFilePath;
53 }
54
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000055 boolean handled() {
56 return mHandled;
57 }
58
Michael Kolb8233fac2010-10-26 16:08:53 -070059 void onResult(int resultCode, Intent intent) {
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000060
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
Michael Kolb8233fac2010-10-26 16:08:53 -070068 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);
Ben Murdoch51f6a2f2011-02-21 12:27:07 +000089 mHandled = true;
90 mCaughtActivityNotFoundException = false;
Michael Kolb8233fac2010-10-26 16:08:53 -070091 }
92
Ben Murdoch8cad4132012-01-11 10:56:43 +000093 void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Michael Kolb8233fac2010-10-26 16:08:53 -070094
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
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000104 // According to the spec, media source can be 'filesystem' or 'camera' or 'camcorder'
Ben Murdoch8cad4132012-01-11 10:56:43 +0000105 // or 'microphone' and the default value should be 'filesystem'.
106 String mediaSource = mediaSourceValueFileSystem;
Michael Kolb8233fac2010-10-26 16:08:53 -0700107
Michael Kolb8233fac2010-10-26 16:08:53 -0700108 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
Ben Murdoch8cad4132012-01-11 10:56:43 +0000119 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 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700135 }
136 }
137 }
138
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000139 //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;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000148 } else {
Ben Murdoch8cad4132012-01-11 10:56:43 +0000149 // 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.
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000152 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;
Ben Murdoch8cad4132012-01-11 10:56:43 +0000163 } 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.
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000167 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;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000178 } else {
Ben Murdoch8cad4132012-01-11 10:56:43 +0000179 // 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
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000181 // 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
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 {
Ben Murdoch51f6a2f2011-02-21 12:27:07 +0000201 mCaughtActivityNotFoundException = true;
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000202 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
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.
Michael Kolb8233fac2010-10-26 16:08:53 -0700216 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
217 i.addCategory(Intent.CATEGORY_OPENABLE);
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000218 i.setType("*/*");
Michael Kolb8233fac2010-10-26 16:08:53 -0700219
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000220 Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
221 createSoundRecorderIntent());
222 chooser.putExtra(Intent.EXTRA_INTENT, i);
223 return chooser;
224 }
225
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
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
242 private Intent createCameraIntent() {
Michael Kolb8233fac2010-10-26 16:08:53 -0700243 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)));
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000252 return cameraIntent;
Michael Kolb8233fac2010-10-26 16:08:53 -0700253 }
254
Ben Murdoche4c0cae2011-02-18 11:25:38 +0000255 private Intent createCamcorderIntent() {
256 return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
257 }
258
259 private Intent createSoundRecorderIntent() {
260 return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
Michael Kolb8233fac2010-10-26 16:08:53 -0700261 }
262
263}