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