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