blob: 10cc03f3bc0444d1146c61a6cc7db3a036d74403 [file] [log] [blame]
The Android Open Source Projected217d92008-12-17 18:05:52 -08001/*
2 * Copyright (C) 2008 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;
20import android.content.Context;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -080021import android.database.Cursor;
The Android Open Source Projected217d92008-12-17 18:05:52 -080022import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.graphics.Color;
25import android.net.Uri;
26import android.os.Environment;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.Message;
30import android.provider.MediaStore;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -080031import android.provider.MediaStore.Images.Media;
The Android Open Source Projected217d92008-12-17 18:05:52 -080032import android.util.Log;
33import android.view.LayoutInflater;
34import android.view.MotionEvent;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.AdapterView;
38import android.widget.BaseAdapter;
39import android.widget.GridView;
40import android.widget.ImageView;
41import android.widget.TextView;
42
43import java.io.File;
44import java.io.FileOutputStream;
45import java.io.IOException;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -080046import java.io.RandomAccessFile;
The Android Open Source Projected217d92008-12-17 18:05:52 -080047import java.util.Collections;
48import java.util.HashMap;
49import java.util.Map;
50import java.util.Vector;
51
52import org.json.JSONArray;
53import org.json.JSONException;
The Android Open Source Project765e7c92009-01-09 17:51:25 -080054import org.json.JSONObject;
The Android Open Source Projected217d92008-12-17 18:05:52 -080055
56/**
57 * Gears FilePicker dialog
58 */
59class GearsFilePickerDialog extends GearsBaseDialog
60 implements View.OnTouchListener {
61
62 private static final String TAG = "Gears FilePicker";
63 private static Bitmap mDirectoryIcon;
64 private static Bitmap mDefaultIcon;
65 private static Bitmap mImageIcon;
66 private static Bitmap mBackIcon;
The Android Open Source Project765e7c92009-01-09 17:51:25 -080067
68 private static String MULTIPLE_FILES = "MULTIPLE_FILES";
69 private static String SINGLE_FILE = "SINGLE_FILE";
70
The Android Open Source Projected217d92008-12-17 18:05:52 -080071 private static ImagesLoad mImagesLoader;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -080072 private static SystemThumbnails mSystemThumbnails;
The Android Open Source Projected217d92008-12-17 18:05:52 -080073 private FilePickerAdapter mAdapter;
The Android Open Source Project765e7c92009-01-09 17:51:25 -080074 private String mSelectionMode;
75 private boolean mMultipleSelection;
76 private String mCurrentPath;
77
78 // Disable saving thumbnails until this is refactored to fit into
79 // existing schemes.
80 private static final boolean enableSavedThumbnails = false;
The Android Open Source Projected217d92008-12-17 18:05:52 -080081
82 public GearsFilePickerDialog(Activity activity,
83 Handler handler,
84 String arguments) {
85 super (activity, handler, arguments);
86 mAdapter = new FilePickerAdapter(activity);
The Android Open Source Project765e7c92009-01-09 17:51:25 -080087 parseArguments();
88 }
89
90 public void parseArguments() {
91 mSelectionMode = MULTIPLE_FILES;
92 try {
93 JSONObject json = new JSONObject(mDialogArguments);
94
95 if (json.has("mode")) {
96 mSelectionMode = json.getString("mode");
97 }
98 } catch (JSONException e) {
99 Log.e(TAG, "exc: " + e);
100 }
101 if (mSelectionMode.equalsIgnoreCase(SINGLE_FILE)) {
102 mMultipleSelection = false;
103 } else {
104 mMultipleSelection = true;
105 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800106 }
107
108 public void setup() {
109 inflate(R.layout.gears_dialog_filepicker, R.id.panel_content);
110 setupButtons(0,
111 R.string.filepicker_button_allow,
112 R.string.filepicker_button_deny);
113 setupDialog();
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800114
115 TextView textViewPath = (TextView) findViewById(R.id.path_name);
116 if (textViewPath != null) {
117 textViewPath.setText(R.string.filepicker_path);
118 }
119
The Android Open Source Projected217d92008-12-17 18:05:52 -0800120 GridView view = (GridView) findViewById(R.id.files_list);
121 view.setAdapter(mAdapter);
122 view.setOnTouchListener(this);
123
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800124 showView(null, R.id.selection);
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800125 setSelectionText();
126
The Android Open Source Projected217d92008-12-17 18:05:52 -0800127 mImagesLoader = new ImagesLoad(mAdapter);
128 mImagesLoader.setAdapterView(view);
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800129 Thread imagesLoaderThread = new Thread(mImagesLoader);
130 imagesLoaderThread.setPriority(Thread.MIN_PRIORITY);
131 imagesLoaderThread.start();
132
133 mSystemThumbnails = new SystemThumbnails();
134 Thread systemThumbnailsThread = new Thread(mSystemThumbnails);
135 systemThumbnailsThread.setPriority(Thread.MIN_PRIORITY);
136 systemThumbnailsThread.start();
The Android Open Source Projected217d92008-12-17 18:05:52 -0800137 }
138
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800139 public void setSelectionText() {
140 Vector elements = mAdapter.selectedElements();
141 if (elements == null)
142 return;
143 TextView info = (TextView) findViewById(R.id.selection);
144 int nbElements = elements.size();
145 if (nbElements == 0) {
146 info.setText(R.string.filepicker_no_files_selected);
147 } else if (nbElements == 1) {
148 info.setText(R.string.filepicker_one_file_selected);
149 } else {
150 info.setText(nbElements + " " +
151 mActivity.getString(
152 R.string.filepicker_some_files_selected));
153 }
154 }
155
156 public void setCurrentPath(String path) {
157 if (path != null) {
158 mCurrentPath = path;
159 TextView textViewPath = (TextView) findViewById(R.id.current_path);
160 if (textViewPath != null) {
161 textViewPath.setText(path);
162 }
163 }
164 }
165
The Android Open Source Projected217d92008-12-17 18:05:52 -0800166 public void setupDialog(TextView message, ImageView icon) {
167 message.setText(R.string.filepicker_message);
168 message.setTextSize(24);
The Android Open Source Projectfbb2a3a2009-02-13 12:57:52 -0800169 icon.setImageResource(R.drawable.ic_dialog_menu_generic);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800170 }
171
172 public boolean onTouch(View v, MotionEvent event) {
173 mImagesLoader.pauseIconRequest();
174 return false;
175 }
176
177 /**
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800178 * Utility class encapsulating thumbnails information
179 * for a file (file image id and magic number)
180 */
181 class SystemThumbnailInfo {
182 private long mID;
183 private long mMagicNumber;
184 SystemThumbnailInfo(long anID, long magicNumber) {
185 mID = anID;
186 mMagicNumber = magicNumber;
187 }
188 public long getID() {
189 return mID;
190 }
191 public long getMagicNumber() {
192 return mMagicNumber;
193 }
194 }
195
196 /**
197 * Utility class to pre-fetch the thumbnails information
198 */
199 class SystemThumbnails implements Runnable {
200 private Map<String, SystemThumbnailInfo> mThumbnails;
201
202 SystemThumbnails() {
203 mThumbnails = Collections.synchronizedMap(new HashMap());
204 }
205
206 public void run() {
207 Uri query = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
208 Cursor cursor = mActivity.managedQuery(query,
209 new String[] { "_id", "mini_thumb_magic", "_data" },
210 null, null, null);
211
212 if (cursor != null) {
213 int count = cursor.getCount();
214 for (int i = 0; i < count; i++) {
215 cursor.moveToPosition(i);
216 SystemThumbnailInfo info = new SystemThumbnailInfo(cursor.getLong(0),
217 cursor.getLong(1));
218 mThumbnails.put(cursor.getString(2), info);
219 }
220 }
221 }
222
223 public SystemThumbnailInfo getThumb(String path) {
224 SystemThumbnailInfo ret = mThumbnails.get(path);
225 if (ret == null) {
226 Uri query = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
227 Cursor cursor = mActivity.managedQuery(query,
228 new String[] { "_id", "mini_thumb_magic", "_data" },
229 "_data = ?", new String[] { path }, null);
230 if (cursor != null && cursor.moveToFirst()) {
231 long longid = cursor.getLong(0);
232 long miniThumbMagic = cursor.getLong(1);
233 ret = new SystemThumbnailInfo(longid, miniThumbMagic);
234 mThumbnails.put(path, ret);
235 }
236 }
237 return ret;
238 }
239 }
240
241 /**
The Android Open Source Projected217d92008-12-17 18:05:52 -0800242 * Utility class to load and generate thumbnails
243 * for image files
244 */
245 class ImagesLoad implements Runnable {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800246 private Vector mImagesPath;
247 private BaseAdapter mAdapter;
248 private AdapterView mAdapterView;
249 private Vector<FilePickerElement> mElements;
250 private Handler mLoaderHandler;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800251 // We use the same value as in Camera.app's ImageManager.java
252 private static final int BYTES_PER_MINI_THUMB = 10000;
253 private final byte[] mMiniThumbData = new byte[BYTES_PER_MINI_THUMB];
254 private final int MINI_THUMB_DATA_FILE_VERSION = 3;
255 private final int THUMBNAIL_SIZE = 128;
256 private Map<Uri, RandomAccessFile> mThumbFiles;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800257
258 ImagesLoad(BaseAdapter adapter) {
259 mAdapter = adapter;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800260 mThumbFiles = Collections.synchronizedMap(new HashMap());
The Android Open Source Projected217d92008-12-17 18:05:52 -0800261 }
262
263 public void signalChanges() {
264 Message message = mHandler.obtainMessage(GearsBaseDialog.NEW_ICON,
265 mAdapter);
266 mHandler.sendMessage(message);
267 }
268
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800269 private String getMiniThumbFileFromUri(Uri uri) {
270 if (uri == null) {
271 return null;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800272 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800273 String directoryName =
274 Environment.getExternalStorageDirectory().toString() +
275 "/dcim/.thumbnails";
276 String path = directoryName + "/.thumbdata" +
277 MINI_THUMB_DATA_FILE_VERSION + "-" + uri.hashCode();
278 return path;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800279 }
280
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800281 private Bitmap getMiniThumbFor(Uri uri, long longid, long magic) {
282 RandomAccessFile thumbFile = mThumbFiles.get(uri);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800283 try {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800284 if (thumbFile == null) {
285 String path = getMiniThumbFileFromUri(uri);
286 File f = new File(path);
287 if (f.exists()) {
288 thumbFile = new RandomAccessFile(f, "rw");
289 mThumbFiles.put(uri, thumbFile);
290 }
291 }
292 } catch (IOException ex) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800293 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800294 if (thumbFile == null) {
295 return null;
296 }
297 byte[] data = getMiniThumbFromFile(thumbFile, longid,
298 mMiniThumbData, magic);
299 if (data != null) {
300 return BitmapFactory.decodeByteArray(data, 0, data.length);
301 }
302 return null;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800303 }
304
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800305 private byte [] getMiniThumbFromFile(RandomAccessFile r,
306 long id,
307 byte [] data,
308 long magicCheck) {
309 if (r == null)
310 return null;
311 long pos = id * BYTES_PER_MINI_THUMB;
312 RandomAccessFile f = r;
313 synchronized (f) {
314 try {
315 f.seek(pos);
316 if (f.readByte() == 1) {
317 long magic = f.readLong();
318 if (magic != magicCheck) {
319 return null;
320 }
321 int length = f.readInt();
322 f.read(data, 0, length);
323 return data;
324 } else {
325 return null;
326 }
327 } catch (IOException ex) {
328 long fileLength;
329 try {
330 fileLength = f.length();
331 } catch (IOException ex1) {
332 fileLength = -1;
333 }
334 return null;
335 }
336 }
337 }
338
339 /*
340 * Returns a thumbnail saved by the Camera application
341 * We pre-cached the information (image id and magic number)
342 * when starting the filepicker.
343 */
344 public Bitmap getSystemThumbnail(FilePickerElement elem) {
345 if (elem.askedForSystemThumbnail() == false) {
346 elem.setAskedForSystemThumbnail(true);
347 String path = elem.getPath();
348 SystemThumbnailInfo thumbInfo = mSystemThumbnails.getThumb(path);
349 if (thumbInfo != null) {
350 Uri query = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
351 Bitmap bmp = getMiniThumbFor(query, thumbInfo.getID(),
352 thumbInfo.getMagicNumber());
353 if (bmp != null) {
354 return bmp;
355 }
356 }
357 }
358 return null;
359 }
360
361 /*
362 * Generate a thumbnail for a given element
363 */
The Android Open Source Projected217d92008-12-17 18:05:52 -0800364 public Bitmap generateImage(FilePickerElement elem) {
365 String path = elem.getPath();
366 Bitmap finalImage = null;
367 try {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800368
369 // First we try to get the thumbnail from the system
370 // (created by the Camera application)
371
372 finalImage = getSystemThumbnail(elem);
373 if (finalImage != null) {
374 return finalImage;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800375 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800376
377 // No thumbnail was found, so we have to create one
378 //
379 // First we get the image information and
380 // determine the sampleSize
381
The Android Open Source Projected217d92008-12-17 18:05:52 -0800382 BitmapFactory.Options options = new BitmapFactory.Options();
383 options.inJustDecodeBounds = true;
384 BitmapFactory.decodeFile(path, options);
385
386 int width = options.outWidth;
387 int height = options.outHeight;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800388 int sampleSize = 1;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800389 if (width > THUMBNAIL_SIZE || height > THUMBNAIL_SIZE) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800390 sampleSize = 2;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800391 while ((width / sampleSize > 2*THUMBNAIL_SIZE)
392 || (height / sampleSize > 2*THUMBNAIL_SIZE)) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800393 sampleSize += 2;
394 }
395 }
396 options.inJustDecodeBounds = false;
397 options.inSampleSize = sampleSize;
398 Bitmap originalImage = BitmapFactory.decodeFile(path, options);
399 if (originalImage == null) {
400 return null;
401 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800402
403 // Let's rescale the image to a THUMBNAIL_SIZE
404
405 width = originalImage.getWidth();
406 height = originalImage.getHeight();
407
408 if (width > height) {
409 width = (int) (width * (THUMBNAIL_SIZE / (double) height));
410 height = THUMBNAIL_SIZE;
411 } else {
412 height = (int) (height * (THUMBNAIL_SIZE / (double) width));
413 width = THUMBNAIL_SIZE;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800414 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800415 originalImage = Bitmap.createScaledBitmap(originalImage,
416 width, height, true);
417
418 // We can now crop the image to a THUMBNAIL_SIZE rectangle
419
420 width = originalImage.getWidth();
421 height = originalImage.getHeight();
422 int d = 0;
423 if (width > height) {
424 d = (width - height) / 2;
425 finalImage = Bitmap.createBitmap(originalImage, d, 0,
426 THUMBNAIL_SIZE, THUMBNAIL_SIZE);
427 } else {
428 d = (height - width) / 2;
429 finalImage = Bitmap.createBitmap(originalImage, 0, d,
430 THUMBNAIL_SIZE, THUMBNAIL_SIZE);
431 }
432
The Android Open Source Projected217d92008-12-17 18:05:52 -0800433 originalImage.recycle();
434 } catch (java.lang.OutOfMemoryError e) {
435 Log.e(TAG, "Intercepted OOM ", e);
436 }
437 return finalImage;
438 }
439
440 public void pauseIconRequest() {
441 Message message = Message.obtain(mLoaderHandler,
442 GearsBaseDialog.PAUSE_REQUEST_ICON);
443 mLoaderHandler.sendMessageAtFrontOfQueue(message);
444 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800445
446 public void clearIconRequests() {
447 Message message = Message.obtain(mLoaderHandler,
448 GearsBaseDialog.CLEAR_REQUEST_ICON);
449 mLoaderHandler.sendMessageAtFrontOfQueue(message);
450 }
451
452 public void postIconRequest(FilePickerElement item,
453 int position,
454 boolean front) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800455 if (item == null) {
456 return;
457 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800458 if (item.isImage() && (item.getThumbnail() == null)) {
459 Message message = mLoaderHandler.obtainMessage(
460 GearsBaseDialog.REQUEST_ICON, position, 0, item);
461 if (front) {
462 mLoaderHandler.sendMessageAtFrontOfQueue(message);
463 } else {
464 mLoaderHandler.sendMessage(message);
465 }
466 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800467 }
468
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800469 public boolean generateIcon(FilePickerElement elem) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800470 if (elem.isImage()) {
471 if (elem.getThumbnail() == null) {
472 Bitmap image = generateImage(elem);
473 if (image != null) {
474 elem.setThumbnail(image);
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800475 return true;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800476 }
477 }
478 }
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800479 return false;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800480 }
481
482 public void setAdapterView(AdapterView view) {
483 mAdapterView = view;
484 }
485
486 public void run() {
487 Looper.prepare();
488 mLoaderHandler = new Handler() {
489 public void handleMessage(Message msg) {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800490 if (msg.what == GearsBaseDialog.CLEAR_REQUEST_ICON) {
491 mLoaderHandler.removeMessages(
492 GearsBaseDialog.PAUSE_REQUEST_ICON);
493 mLoaderHandler.removeMessages(
494 GearsBaseDialog.REQUEST_ICON);
495 } else if (msg.what == GearsBaseDialog.PAUSE_REQUEST_ICON) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800496 try {
497 // We are busy (likely) scrolling the view,
498 // so we just pause the loading.
499 Thread.sleep(1000);
500 mLoaderHandler.removeMessages(
501 GearsBaseDialog.PAUSE_REQUEST_ICON);
502 } catch (InterruptedException e) {
503 Log.e(TAG, "InterruptedException ", e);
504 }
505 } else if (msg.what == GearsBaseDialog.REQUEST_ICON) {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800506 FilePickerElement elem = (FilePickerElement) msg.obj;
507 if (generateIcon(elem)) {
508 signalChanges();
509 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800510 try {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800511 Thread.sleep(50);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800512 } catch (InterruptedException e) {
513 Log.e(TAG, "InterruptedException ", e);
514 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800515 }
516 }
517 };
518 Looper.loop();
519 }
520 }
521
522 /**
523 * Utility class representing an element displayed in the
524 * file picker, associated with an icon and/or thumbnail
525 */
526 class FilePickerElement {
527 private File mPath;
528 private String mName;
529 private Bitmap mIcon;
530 private boolean mIsSelected;
531 private Vector mChildren;
532 private FilePickerElement mParent;
533 private boolean mIsParent;
534 private BaseAdapter mAdapter;
535 private String mExtension;
536 private Bitmap mThumbnail;
537 private boolean mIsImage;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800538 private boolean mAskedForSystemThumbnail;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800539
540 public FilePickerElement(String name, BaseAdapter adapter) {
541 this(name, adapter, null);
542 }
543
544 public FilePickerElement(String path, String name, BaseAdapter adapter) {
545 this(path, name, adapter, null);
546 }
547
548 public FilePickerElement(String name,
549 BaseAdapter adapter,
550 FilePickerElement parent) {
551 mName = name;
552 mAdapter = adapter;
553 mParent = parent;
554 mIsSelected = false;
555 mChildren = null;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800556 mAskedForSystemThumbnail = false;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800557 }
558
559 public FilePickerElement(String path,
560 String name,
561 BaseAdapter adapter,
562 FilePickerElement parent) {
563 mPath = new File(path);
564 mName = name;
565 mIsSelected = false;
566 mChildren = null;
567 mParent = parent;
568 mAdapter = adapter;
569 mExtension = null;
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800570 mAskedForSystemThumbnail = false;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800571
572 setIcons();
573 }
574
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800575 public void setAskedForSystemThumbnail(boolean value) {
576 mAskedForSystemThumbnail = value;
577 }
578
579 public boolean askedForSystemThumbnail() {
580 return mAskedForSystemThumbnail;
581 }
582
The Android Open Source Projected217d92008-12-17 18:05:52 -0800583 public void setIcons() {
584 if (mPath.isDirectory()) {
585 if (mDirectoryIcon == null) {
586 mDirectoryIcon = BitmapFactory.decodeResource(
587 getResources(), R.drawable.gears_folder);
588 }
589 mIcon = mDirectoryIcon;
590
591 } else {
592 if (isImage()) {
593 if (mImageIcon == null) {
594 mImageIcon = BitmapFactory.decodeResource(
595 getResources(), R.drawable.gears_file_image);
596 }
597 mIcon = mImageIcon;
598 } else if (isAudio()) {
599 mIcon = BitmapFactory.decodeResource(
600 getResources(), R.drawable.gears_file_audio);
601 } else if (isVideo()) {
602 mIcon = BitmapFactory.decodeResource(
603 getResources(), R.drawable.gears_file_video);
604 } else {
605 if (mDefaultIcon == null) {
606 mDefaultIcon = BitmapFactory.decodeResource(
607 getResources(), R.drawable.gears_file_unknown);
608 }
609 mIcon = mDefaultIcon;
610 }
611 }
612 if (mBackIcon == null) {
613 mBackIcon = BitmapFactory.decodeResource(getResources(),
The Android Open Source Project066e9082009-01-20 14:03:59 -0800614 com.android.internal.R.drawable.ic_menu_back);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800615 }
616 }
617
618 public boolean isImage() {
619 if (mIsImage) return mIsImage;
620 String extension = getExtension();
621 if (extension != null) {
622 if (extension.equalsIgnoreCase("jpg") ||
623 extension.equalsIgnoreCase("jpeg") ||
624 extension.equalsIgnoreCase("png") ||
625 extension.equalsIgnoreCase("gif")) {
626 mIsImage = true;
627 return true;
628 }
629 }
630 return false;
631 }
632
633 public boolean isAudio() {
634 String extension = getExtension();
635 if (extension != null) {
636 if (extension.equalsIgnoreCase("mp3") ||
637 extension.equalsIgnoreCase("wav") ||
638 extension.equalsIgnoreCase("aac")) {
639 return true;
640 }
641 }
642 return false;
643 }
644
645 public boolean isVideo() {
646 String extension = getExtension();
647 if (extension != null) {
648 if (extension.equalsIgnoreCase("mpg") ||
649 extension.equalsIgnoreCase("mpeg") ||
650 extension.equalsIgnoreCase("mpe") ||
651 extension.equalsIgnoreCase("divx") ||
652 extension.equalsIgnoreCase("3gpp") ||
653 extension.equalsIgnoreCase("avi")) {
654 return true;
655 }
656 }
657 return false;
658 }
659
660 public void setParent(boolean isParent) {
661 mIsParent = isParent;
662 }
663
664 public boolean isDirectory() {
665 return mPath.isDirectory();
666 }
667
668 public String getExtension() {
669 if (isDirectory()) {
670 return null;
671 }
672 if (mExtension == null) {
673 String path = getPath();
674 int index = path.lastIndexOf(".");
675 if ((index != -1) && (index != path.length() - 1)){
676 // if we find a dot that is not the last character
677 mExtension = path.substring(index+1);
678 return mExtension;
679 }
680 }
681 return mExtension;
682 }
683
684 public void refresh() {
685 mChildren = null;
686 Vector children = getChildren();
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800687 mImagesLoader.clearIconRequests();
The Android Open Source Projected217d92008-12-17 18:05:52 -0800688 }
689
690 public Vector getChildren() {
691 if (isDirectory()) {
692 if (mChildren == null) {
693 mChildren = new Vector();
694 File[] files = mPath.listFiles();
695 if (mParent != null) {
696 mChildren.add(mParent);
697 mParent.setParent(true);
698 }
699 for (int i = 0; i < files.length; i++) {
700 String name = files[i].getName();
701 String fpath = files[i].getPath();
702 if (!name.startsWith(".")) { // hide dotfiles
703 FilePickerElement elem = new FilePickerElement(fpath, name,
704 mAdapter, this);
705 elem.setParent(false);
706 mChildren.add(elem);
707 }
708 }
709 }
710 }
711 return mChildren;
712 }
713
714 public FilePickerElement getChild(int position) {
715 Vector children = getChildren();
716 if (children != null) {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800717 FilePickerElement elem = (FilePickerElement) children.get(position);
718 return elem;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800719 }
720 return null;
721 }
722
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800723 /*
724 * Depending on the type, we return either
725 * the icon (mIcon) or the back icon (mBackIcon).
726 * If we can load a system thumbnail we do this
727 * synchronously and return it, else we ask the
728 * mImagesLoader to generate a thumbnail for us.
729 */
The Android Open Source Projected217d92008-12-17 18:05:52 -0800730 public Bitmap getIcon(int position) {
731 if (mIsParent) {
732 return mBackIcon;
733 }
734 if (isImage()) {
735 if (mThumbnail != null) {
736 return mThumbnail;
737 } else {
The Android Open Source Project7561d4f2009-02-19 10:57:35 -0800738 Bitmap image = mImagesLoader.getSystemThumbnail(this);
739 if (image != null) {
740 mThumbnail = image;
741 return mThumbnail;
742 }
743 mImagesLoader.postIconRequest(this, position, true);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800744 }
745 }
746 return mIcon;
747 }
748
749 public Bitmap getThumbnail() {
750 return mThumbnail;
751 }
752
753 public void setThumbnail(Bitmap icon) {
754 mThumbnail = icon;
755 }
756
757 public String getName() {
758 return mName;
759 }
760
761 public String getPath() {
762 return mPath.getPath();
763 }
764
765 public void toggleSelection() {
766 mIsSelected = !mIsSelected;
767 }
768
769 public boolean isSelected() {
770 return mIsSelected;
771 }
772
773 }
774
775 /**
776 * Adapter for the GridView
777 */
778 class FilePickerAdapter extends BaseAdapter {
779 private Context mContext;
780 private Map mImagesMap;
781 private Map mImagesSelected;
782
783 private Vector mImages;
784 private Vector<FilePickerElement> mFiles;
785
786 private FilePickerElement mRootElement;
787 private FilePickerElement mCurrentElement;
788
789 public FilePickerAdapter(Context context) {
790 mContext = context;
791 mImages = new Vector();
792 mFiles = new Vector();
793
794 mImagesMap = Collections.synchronizedMap(new HashMap());
795 mImagesSelected = new HashMap();
796
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800797 String startingPath = Environment.getExternalStorageDirectory().getPath();
798 mRootElement = new FilePickerElement(startingPath, "SD Card", this);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800799 mCurrentElement = mRootElement;
800 }
801
802 public void addImage(String path) {
803 mImages.add(path);
804 Bitmap image = BitmapFactory.decodeResource(
805 getResources(), R.drawable.gears_file_unknown);
806 mImagesMap.put(path, image);
807 mImagesSelected.put(path, Boolean.FALSE);
808 }
809
810 public int getCount() {
811 Vector elems = mCurrentElement.getChildren();
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800812 setCurrentPath(mCurrentElement.getPath());
The Android Open Source Projected217d92008-12-17 18:05:52 -0800813 return elems.size();
814 }
815
816 public Object getItem(int position) {
817 return position;
818 }
819
820 public long getItemId(int position) {
821 return position;
822 }
823
824 public Vector selectedElements() {
825 if (mCurrentElement == null) {
826 return null;
827 }
828 Vector children = mCurrentElement.getChildren();
829 Vector ret = new Vector();
830 for (int i = 0; i < children.size(); i++) {
831 FilePickerElement elem = (FilePickerElement) children.get(i);
832 if (elem.isSelected()) {
833 ret.add(elem);
834 }
835 }
836 return ret;
837 }
838
839 public View getView(int position, View convertView, ViewGroup parent) {
840 View cell = convertView;
841 if (cell == null) {
842 LayoutInflater inflater = (LayoutInflater) getSystemService(
843 Context.LAYOUT_INFLATER_SERVICE);
844 cell = inflater.inflate(R.layout.gears_dialog_filepicker_cell, null);
845 }
846 ImageView imageView = (ImageView) cell.findViewById(R.id.icon);
847 TextView textView = (TextView) cell.findViewById(R.id.name);
848 FilePickerElement elem = mCurrentElement.getChild(position);
849 if (elem == null) {
850 String message = "Could not get elem " + position;
851 message += " for " + mCurrentElement.getPath();
852 Log.e(TAG, message);
853 return null;
854 }
855 String path = elem.getPath();
856 textView.setText(elem.getName());
857
858 View.OnClickListener listener = new View.OnClickListener() {
859 public void onClick(View view) {
860 int pos = (Integer) view.getTag();
861 FilePickerElement elem = mCurrentElement.getChild(pos);
862 if (elem.isDirectory()) {
863 mCurrentElement = elem;
864 mCurrentElement.refresh();
865 } else {
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800866 if (mMultipleSelection) {
867 elem.toggleSelection();
868 } else {
869 Vector elems = selectedElements();
870 if (elems != null) {
871 if (elems.size() == 0) {
872 elem.toggleSelection();
873 } else if ((elems.size() == 1)
874 && elem.isSelected()) {
875 elem.toggleSelection();
876 }
877 }
878 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800879 }
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800880 setSelectionText();
The Android Open Source Projected217d92008-12-17 18:05:52 -0800881 notifyDataSetChanged();
882 }
883 };
The Android Open Source Projected217d92008-12-17 18:05:52 -0800884 cell.setLayoutParams(new GridView.LayoutParams(96, 96));
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800885 cell.setOnClickListener(listener);
886 cell.setOnTouchListener(new View.OnTouchListener() {
887 public boolean onTouch(View v, MotionEvent event) {
888 if (event.getAction() == MotionEvent.ACTION_DOWN) {
889 int color = getResources().getColor(R.color.icon_selection);
890 v.setBackgroundColor(color);
891 } else {
The Android Open Source Projectfbb2a3a2009-02-13 12:57:52 -0800892 v.setBackgroundColor(android.R.color.background_dark);
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800893 }
894 return false;
895 }
896 });
The Android Open Source Projected217d92008-12-17 18:05:52 -0800897
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800898 cell.setTag(position);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800899
900 if (elem.isSelected()) {
The Android Open Source Project765e7c92009-01-09 17:51:25 -0800901 int color = getResources().getColor(R.color.icon_selection);
902 cell.setBackgroundColor(color);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800903 } else {
The Android Open Source Projectfbb2a3a2009-02-13 12:57:52 -0800904 cell.setBackgroundColor(android.R.color.background_dark);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800905 }
906 Bitmap bmp = elem.getIcon(position);
907 if (bmp != null) {
908 imageView.setImageBitmap(bmp);
909 }
910
911 return cell;
912 }
913 }
914
915 private String selectedFiles() {
916 Vector selection = mAdapter.selectedElements();
917 JSONArray jsonSelection = new JSONArray();
918 if (selection != null) {
919 for (int i = 0; i < selection.size(); i++) {
920 FilePickerElement elem = (FilePickerElement) selection.get(i);
921 jsonSelection.put(elem.getPath());
922 }
923 }
924 return jsonSelection.toString();
925 }
926
927 public String closeDialog(int closingType) {
928 return selectedFiles();
929 }
930}