OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.ui; |
| 6 |
| 7 import android.app.Activity; |
| 8 import android.content.ContentResolver; |
| 9 import android.content.Intent; |
| 10 import android.database.Cursor; |
| 11 import android.net.Uri; |
| 12 import android.os.Environment; |
| 13 import android.provider.MediaStore; |
| 14 |
| 15 import java.io.File; |
| 16 import java.util.ArrayList; |
| 17 import java.util.Arrays; |
| 18 import java.util.List; |
| 19 |
| 20 import org.chromium.base.CalledByNative; |
| 21 import org.chromium.base.JNINamespace; |
| 22 import org.chromium.ui.gfx.NativeWindow; |
| 23 |
| 24 /** |
| 25 * A dialog that is triggered from a file input field that allows a user to sele
ct a file based on |
| 26 * a set of accepted file types. The path of the selected file is passed to the
native dialog. |
| 27 */ |
| 28 @JNINamespace("ui") |
| 29 class SelectFileDialog implements NativeWindow.IntentCallback{ |
| 30 // TODO (aurimas): Swap these constants with AppResources when it gets moved
to base to support |
| 31 // internationalization. |
| 32 private static final String LOW_MEMORY_ERROR = |
| 33 "Unable to complete previous operation due to low memory"; |
| 34 private static final String OPENING_FILE_ERROR = "Failed to open selected fi
le"; |
| 35 |
| 36 private static final String IMAGE_TYPE = "image/"; |
| 37 private static final String VIDEO_TYPE = "video/"; |
| 38 private static final String AUDIO_TYPE = "audio/"; |
| 39 private static final String ALL_IMAGE_TYPES = IMAGE_TYPE + "*"; |
| 40 private static final String ALL_VIDEO_TYPES = VIDEO_TYPE + "*"; |
| 41 private static final String ALL_AUDIO_TYPES = AUDIO_TYPE + "*"; |
| 42 private static final String ANY_TYPES = "*/*"; |
| 43 private static final String CAPTURE_CAMERA = "camera"; |
| 44 private static final String CAPTURE_CAMCORDER = "camcorder"; |
| 45 private static final String CAPTURE_MICROPHONE = "microphone"; |
| 46 private static final String CAPTURE_FILESYSTEM = "filesystem"; |
| 47 private static final String CAPTURE_IMAGE_DIRECTORY = "browser-photos"; |
| 48 |
| 49 private final int mNativeSelectFileDialog; |
| 50 private List<String> mFileTypes; |
| 51 private String mCapture; // May be null if no capture parameter was set. |
| 52 private Uri mCameraOutputUri; |
| 53 |
| 54 private SelectFileDialog(int nativeSelectFileDialog) { |
| 55 mNativeSelectFileDialog = nativeSelectFileDialog; |
| 56 } |
| 57 |
| 58 /** |
| 59 * Creates and starts an intent based on the passed fileTypes and capture va
lue. |
| 60 * @param fileTypes MIME types requested (i.e. "image/*") |
| 61 * @param capture The capture value as described in http://www.w3.org/TR/htm
l-media-capture/ |
| 62 * @param window The NativeWindow that can show intents |
| 63 */ |
| 64 @CalledByNative |
| 65 private void selectFile(String[] fileTypes, String capture, NativeWindow win
dow) { |
| 66 mFileTypes = new ArrayList<String>(Arrays.asList(fileTypes)); |
| 67 mCapture = capture; |
| 68 |
| 69 Intent chooser = new Intent(Intent.ACTION_CHOOSER); |
| 70 Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
| 71 mCameraOutputUri = Uri.fromFile(getFileForImageCapture()); |
| 72 camera.putExtra(MediaStore.EXTRA_OUTPUT, mCameraOutputUri); |
| 73 Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); |
| 74 Intent soundRecorder = new Intent( |
| 75 MediaStore.Audio.Media.RECORD_SOUND_ACTION); |
| 76 |
| 77 // Quick check - if a capture parameter other than filesystem (the defau
lt) is specified we |
| 78 // should just launch the appropriate intent. Otherwise build up a choos
er based on the |
| 79 // accept type and then display that to the user. |
| 80 if (captureCamera()) { |
| 81 if (window.showIntent(camera, this, LOW_MEMORY_ERROR)) return; |
| 82 } else if (captureCamcorder()) { |
| 83 if (window.showIntent(camcorder, this, LOW_MEMORY_ERROR)) return; |
| 84 } else if (captureMicrophone()) { |
| 85 if (window.showIntent(soundRecorder, this, LOW_MEMORY_ERROR)) return
; |
| 86 } |
| 87 |
| 88 Intent getContentIntent = new Intent(Intent.ACTION_GET_CONTENT); |
| 89 getContentIntent.addCategory(Intent.CATEGORY_OPENABLE); |
| 90 ArrayList<Intent> extraIntents = new ArrayList<Intent>(); |
| 91 if (!noSpecificType()) { |
| 92 // Create a chooser based on the accept type that was specified in t
he webpage. Note |
| 93 // that if the web page specified multiple accept types, we will hav
e built a generic |
| 94 // chooser above. |
| 95 if (shouldShowImageTypes()) { |
| 96 extraIntents.add(camera); |
| 97 getContentIntent.setType("image/*"); |
| 98 } else if (shouldShowVideoTypes()) { |
| 99 extraIntents.add(camcorder); |
| 100 getContentIntent.setType("video/*"); |
| 101 } else if (shouldShowAudioTypes()) { |
| 102 extraIntents.add(soundRecorder); |
| 103 getContentIntent.setType("audio/*"); |
| 104 } |
| 105 } |
| 106 |
| 107 if (extraIntents.isEmpty()) { |
| 108 // We couldn't resolve an accept type, so fallback to a generic choo
ser. |
| 109 getContentIntent.setType("*/*"); |
| 110 extraIntents.add(camera); |
| 111 extraIntents.add(camcorder); |
| 112 extraIntents.add(soundRecorder); |
| 113 } |
| 114 |
| 115 chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, |
| 116 extraIntents.toArray(new Intent[] { })); |
| 117 |
| 118 chooser.putExtra(Intent.EXTRA_INTENT, getContentIntent); |
| 119 |
| 120 if (!window.showIntent(chooser, this, LOW_MEMORY_ERROR)) onFileNotSelect
ed(); |
| 121 } |
| 122 |
| 123 /** |
| 124 * Get a file for the image capture in the CAPTURE_IMAGE_DIRECTORY directory
. |
| 125 */ |
| 126 private File getFileForImageCapture() { |
| 127 File externalDataDir = Environment.getExternalStoragePublicDirectory( |
| 128 Environment.DIRECTORY_DCIM); |
| 129 File cameraDataDir = new File(externalDataDir.getAbsolutePath() + |
| 130 File.separator + CAPTURE_IMAGE_DIRECTORY); |
| 131 if (!cameraDataDir.exists() && !cameraDataDir.mkdirs()) { |
| 132 cameraDataDir = externalDataDir; |
| 133 } |
| 134 File photoFile = new File(cameraDataDir.getAbsolutePath() + |
| 135 File.separator + System.currentTimeMillis() + ".jpg"); |
| 136 return photoFile; |
| 137 } |
| 138 |
| 139 /** |
| 140 * Callback method to handle the intent results and pass on the path to the
native |
| 141 * SelectFileDialog. |
| 142 * @param window The window that has access to the application activity. |
| 143 * @param resultCode The result code whether the intent returned successfull
y. |
| 144 * @param contentResolver The content resolver used to extract the path of t
he selected file. |
| 145 * @param results The results of the requested intent. |
| 146 */ |
| 147 @Override |
| 148 public void onIntentCompleted(NativeWindow window, int resultCode, |
| 149 ContentResolver contentResolver, Intent results) { |
| 150 if (resultCode != Activity.RESULT_OK) { |
| 151 onFileNotSelected(); |
| 152 return; |
| 153 } |
| 154 boolean success = false; |
| 155 if (results == null) { |
| 156 // If we have a successful return but no data, then assume this is t
he camera returning |
| 157 // the photo that we requested. |
| 158 nativeOnFileSelected(mNativeSelectFileDialog, mCameraOutputUri.getPa
th()); |
| 159 success = true; |
| 160 |
| 161 // Broadcast to the media scanner that there's a new photo on the de
vice so it will |
| 162 // show up right away in the gallery (rather than waiting until the
next time the media |
| 163 // scanner runs). |
| 164 window.getActivity().sendBroadcast(new Intent( |
| 165 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mCameraOutputUri)); |
| 166 } else { |
| 167 // We get back a content:// URI from the system if the user picked a
file from the |
| 168 // gallery. The ContentView has functionality that will convert that
content:// URI to |
| 169 // a file path on disk that Chromium understands. |
| 170 Cursor c = contentResolver.query(results.getData(), |
| 171 new String[] { MediaStore.MediaColumns.DATA }, null, null, n
ull); |
| 172 if (c != null) { |
| 173 if (c.getCount() == 1) { |
| 174 c.moveToFirst(); |
| 175 String path = c.getString(0); |
| 176 if (path != null) { |
| 177 // Not all providers support the MediaStore.DATA column.
For example, |
| 178 // Gallery3D (com.android.gallery3d.provider) does not s
upport it for |
| 179 // Picasa Web Album images. |
| 180 nativeOnFileSelected(mNativeSelectFileDialog, path); |
| 181 success = true; |
| 182 } |
| 183 } |
| 184 c.close(); |
| 185 } |
| 186 } |
| 187 if (!success) { |
| 188 onFileNotSelected(); |
| 189 window.showError(OPENING_FILE_ERROR); |
| 190 } |
| 191 } |
| 192 |
| 193 private void onFileNotSelected() { |
| 194 nativeOnFileNotSelected(mNativeSelectFileDialog); |
| 195 } |
| 196 |
| 197 private boolean noSpecificType() { |
| 198 // We use a single Intent to decide the type of the file chooser we disp
lay to the user, |
| 199 // which means we can only give it a single type. If there are multiple
accept types |
| 200 // specified, we will fallback to a generic chooser (unless a capture pa
rameter has been |
| 201 // specified, in which case we'll try to satisfy that first. |
| 202 return mFileTypes.size() != 1 || mFileTypes.contains(ANY_TYPES); |
| 203 } |
| 204 |
| 205 private boolean shouldShowTypes(String allTypes, String specificType) { |
| 206 if (noSpecificType() || mFileTypes.contains(allTypes)) return true; |
| 207 return acceptSpecificType(specificType); |
| 208 } |
| 209 |
| 210 private boolean shouldShowImageTypes() { |
| 211 return shouldShowTypes(ALL_IMAGE_TYPES,IMAGE_TYPE); |
| 212 } |
| 213 |
| 214 private boolean shouldShowVideoTypes() { |
| 215 return shouldShowTypes(ALL_VIDEO_TYPES, VIDEO_TYPE); |
| 216 } |
| 217 |
| 218 private boolean shouldShowAudioTypes() { |
| 219 return shouldShowTypes(ALL_AUDIO_TYPES, AUDIO_TYPE); |
| 220 } |
| 221 |
| 222 private boolean captureCamera() { |
| 223 return shouldShowImageTypes() && mCapture != null && mCapture.startsWith
(CAPTURE_CAMERA); |
| 224 } |
| 225 |
| 226 private boolean captureCamcorder() { |
| 227 return shouldShowVideoTypes() && mCapture != null && |
| 228 mCapture.startsWith(CAPTURE_CAMCORDER); |
| 229 } |
| 230 |
| 231 private boolean captureMicrophone() { |
| 232 return shouldShowAudioTypes() && mCapture != null && |
| 233 mCapture.startsWith(CAPTURE_MICROPHONE); |
| 234 } |
| 235 |
| 236 private boolean captureFilesystem() { |
| 237 return mCapture != null && mCapture.startsWith(CAPTURE_FILESYSTEM); |
| 238 } |
| 239 |
| 240 private boolean acceptSpecificType(String accept) { |
| 241 for (String type : mFileTypes) { |
| 242 if (type.startsWith(accept)) { |
| 243 return true; |
| 244 } |
| 245 } |
| 246 return false; |
| 247 } |
| 248 |
| 249 @CalledByNative |
| 250 private static SelectFileDialog create(int nativeSelectFileDialog) { |
| 251 return new SelectFileDialog(nativeSelectFileDialog); |
| 252 } |
| 253 |
| 254 private native void nativeOnFileSelected(int nativeSelectFileDialogImpl, |
| 255 String filePath); |
| 256 private native void nativeOnFileNotSelected(int nativeSelectFileDialogImpl); |
| 257 } |
OLD | NEW |