The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2007 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.camera; |
| 18 | |
Owen Lin | 101d528 | 2009-04-03 16:20:08 -0700 | [diff] [blame] | 19 | import com.android.camera.gallery.IImage; |
| 20 | import com.android.camera.gallery.IImageList; |
| 21 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 22 | import android.app.Activity; |
| 23 | import android.app.Dialog; |
| 24 | import android.app.ProgressDialog; |
| 25 | import android.content.BroadcastReceiver; |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 26 | import android.content.ContentResolver; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 27 | import android.content.Context; |
| 28 | import android.content.Intent; |
| 29 | import android.content.IntentFilter; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 30 | import android.content.res.Resources; |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 31 | import android.database.ContentObserver; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 32 | import android.graphics.Bitmap; |
| 33 | import android.graphics.Canvas; |
| 34 | import android.graphics.Matrix; |
| 35 | import android.graphics.Paint; |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 36 | import android.graphics.PorterDuff; |
| 37 | import android.graphics.PorterDuffXfermode; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 38 | import android.graphics.Rect; |
| 39 | import android.graphics.drawable.Drawable; |
| 40 | import android.net.Uri; |
| 41 | import android.os.Bundle; |
| 42 | import android.os.Environment; |
| 43 | import android.os.Handler; |
| 44 | import android.os.StatFs; |
Owen Lin | 588e29b | 2009-07-07 11:37:12 -0700 | [diff] [blame] | 45 | import android.provider.MediaStore; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 46 | import android.provider.MediaStore.Images; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 47 | import android.util.Log; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 48 | import android.view.ContextMenu; |
| 49 | import android.view.LayoutInflater; |
| 50 | import android.view.Menu; |
| 51 | import android.view.MenuItem; |
| 52 | import android.view.View; |
| 53 | import android.view.ViewGroup; |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 54 | import android.view.ContextMenu.ContextMenuInfo; |
| 55 | import android.view.MenuItem.OnMenuItemClickListener; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 56 | import android.widget.AdapterView; |
| 57 | import android.widget.BaseAdapter; |
| 58 | import android.widget.GridView; |
| 59 | import android.widget.TextView; |
| 60 | import android.widget.Toast; |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 61 | import android.widget.AdapterView.AdapterContextMenuInfo; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 62 | |
| 63 | import java.util.ArrayList; |
| 64 | import java.util.HashMap; |
| 65 | import java.util.Map; |
| 66 | |
Ray Chen | 993105a | 2009-04-10 02:11:35 -0700 | [diff] [blame] | 67 | /** |
| 68 | * The GalleryPicker activity. |
| 69 | */ |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 70 | public class GalleryPicker extends Activity { |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 71 | private static final String TAG = "GalleryPicker"; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 72 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 73 | Handler mHandler = new Handler(); // handler for the main thread |
| 74 | Thread mWorkerThread; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 75 | BroadcastReceiver mReceiver; |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 76 | ContentObserver mDbObserver; |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 77 | GridView mGridView; |
| 78 | GalleryPickerAdapter mAdapter; // mAdapter is only accessed in main thread. |
| 79 | boolean mScanning; |
| 80 | boolean mUnmounted; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 81 | |
| 82 | @Override |
| 83 | public void onCreate(Bundle icicle) { |
| 84 | super.onCreate(icicle); |
| 85 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 86 | setContentView(R.layout.gallerypicker); |
| 87 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 88 | mGridView = (GridView) findViewById(R.id.albums); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 89 | |
| 90 | mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 91 | public void onItemClick(AdapterView<?> parent, View view, |
| 92 | int position, long id) { |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 93 | launchFolderGallery(position); |
| 94 | } |
| 95 | }); |
Owen Lin | 937fc48 | 2009-04-14 02:02:51 -0700 | [diff] [blame] | 96 | |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 97 | mGridView.setOnCreateContextMenuListener( |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 98 | new View.OnCreateContextMenuListener() { |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 99 | public void onCreateContextMenu(ContextMenu menu, View v, |
| 100 | final ContextMenuInfo menuInfo) { |
| 101 | onCreateGalleryPickerContextMenu(menu, menuInfo); |
| 102 | } |
| 103 | }); |
Owen Lin | 937fc48 | 2009-04-14 02:02:51 -0700 | [diff] [blame] | 104 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 105 | mReceiver = new BroadcastReceiver() { |
| 106 | @Override |
| 107 | public void onReceive(Context context, Intent intent) { |
| 108 | onReceiveMediaBroadcast(intent); |
| 109 | } |
| 110 | }; |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 111 | |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 112 | mDbObserver = new ContentObserver(mHandler) { |
| 113 | @Override |
| 114 | public void onChange(boolean selfChange) { |
| 115 | rebake(false, ImageManager.isMediaScannerScanning( |
| 116 | getContentResolver())); |
| 117 | } |
| 118 | }; |
| 119 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 120 | ImageManager.ensureOSXCompatibleFolder(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | Dialog mMediaScanningDialog; |
| 124 | |
| 125 | // Display a dialog if the storage is being scanned now. |
| 126 | public void updateScanningDialog(boolean scanning) { |
| 127 | boolean prevScanning = (mMediaScanningDialog != null); |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 128 | if (prevScanning == scanning && mAdapter.mItems.size() == 0) return; |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 129 | // Now we are certain the state is changed. |
| 130 | if (prevScanning) { |
| 131 | mMediaScanningDialog.cancel(); |
| 132 | mMediaScanningDialog = null; |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 133 | } else if (scanning && mAdapter.mItems.size() == 0) { |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 134 | mMediaScanningDialog = ProgressDialog.show( |
| 135 | this, |
| 136 | null, |
| 137 | getResources().getString(R.string.wait), |
| 138 | true, |
| 139 | true); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | private View mNoImagesView; |
| 144 | |
| 145 | // Show/Hide the "no images" icon and text. Load resources on demand. |
| 146 | private void showNoImagesView() { |
| 147 | if (mNoImagesView == null) { |
| 148 | ViewGroup root = (ViewGroup) findViewById(R.id.root); |
| 149 | getLayoutInflater().inflate(R.layout.gallerypicker_no_images, root); |
| 150 | mNoImagesView = findViewById(R.id.no_images); |
| 151 | } |
| 152 | mNoImagesView.setVisibility(View.VISIBLE); |
| 153 | } |
| 154 | |
| 155 | private void hideNoImagesView() { |
| 156 | if (mNoImagesView != null) { |
| 157 | mNoImagesView.setVisibility(View.GONE); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | // The storage status is changed, restart the worker or show "no images". |
| 162 | private void rebake(boolean unmounted, boolean scanning) { |
| 163 | if (unmounted == mUnmounted && scanning == mScanning) return; |
| 164 | abortWorker(); |
| 165 | mUnmounted = unmounted; |
| 166 | mScanning = scanning; |
| 167 | updateScanningDialog(mScanning); |
| 168 | if (mUnmounted) { |
| 169 | showNoImagesView(); |
| 170 | } else { |
| 171 | hideNoImagesView(); |
| 172 | startWorker(); |
| 173 | } |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 174 | } |
Owen Lin | 937fc48 | 2009-04-14 02:02:51 -0700 | [diff] [blame] | 175 | |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 176 | // This is called when we receive media-related broadcast. |
| 177 | private void onReceiveMediaBroadcast(Intent intent) { |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 178 | String action = intent.getAction(); |
| 179 | if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) { |
| 180 | // SD card available |
| 181 | // TODO put up a "please wait" message |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 182 | } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) { |
| 183 | // SD card unavailable |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 184 | rebake(true, false); |
| 185 | } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) { |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 186 | rebake(false, true); |
| 187 | } else if (action.equals( |
| 188 | Intent.ACTION_MEDIA_SCANNER_FINISHED)) { |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 189 | rebake(false, false); |
| 190 | } else if (action.equals(Intent.ACTION_MEDIA_EJECT)) { |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 191 | rebake(true, false); |
| 192 | } |
| 193 | } |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 194 | |
| 195 | private void launchFolderGallery(int position) { |
| 196 | mAdapter.mItems.get(position).launch(this); |
| 197 | } |
| 198 | |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 199 | private void onCreateGalleryPickerContextMenu(ContextMenu menu, |
| 200 | final ContextMenuInfo menuInfo) { |
| 201 | int position = ((AdapterContextMenuInfo) menuInfo).position; |
| 202 | menu.setHeaderTitle(mAdapter.baseTitleForPosition(position)); |
| 203 | // "Slide Show" |
| 204 | if ((mAdapter.getIncludeMediaTypes(position) |
| 205 | & ImageManager.INCLUDE_IMAGES) != 0) { |
Chih-Chung Chang | d189083 | 2009-09-08 13:32:52 +0800 | [diff] [blame] | 206 | menu.add(R.string.slide_show) |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 207 | .setOnMenuItemClickListener(new OnMenuItemClickListener() { |
| 208 | public boolean onMenuItemClick(MenuItem item) { |
| 209 | return onSlideShowClicked(menuInfo); |
| 210 | } |
| 211 | }); |
| 212 | } |
| 213 | // "View" |
Chih-Chung Chang | d189083 | 2009-09-08 13:32:52 +0800 | [diff] [blame] | 214 | menu.add(R.string.view) |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 215 | .setOnMenuItemClickListener(new OnMenuItemClickListener() { |
| 216 | public boolean onMenuItemClick(MenuItem item) { |
| 217 | return onViewClicked(menuInfo); |
| 218 | } |
| 219 | }); |
| 220 | } |
| 221 | |
| 222 | // This is called when the user clicks "Slideshow" from the context menu. |
| 223 | private boolean onSlideShowClicked(ContextMenuInfo menuInfo) { |
| 224 | AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; |
| 225 | int position = info.position; |
| 226 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 227 | if (position < 0 || position >= mAdapter.mItems.size()) { |
| 228 | return true; |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 229 | } |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 230 | // Slide show starts from the first image on the list. |
| 231 | Item item = mAdapter.mItems.get(position); |
| 232 | Uri targetUri = item.mFirstImageUri; |
| 233 | |
| 234 | if (targetUri != null && item.mBucketId != null) { |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 235 | targetUri = targetUri.buildUpon() |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 236 | .appendQueryParameter("bucketId", item.mBucketId) |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 237 | .build(); |
| 238 | } |
| 239 | Intent intent = new Intent(Intent.ACTION_VIEW, targetUri); |
| 240 | intent.putExtra("slideshow", true); |
| 241 | startActivity(intent); |
| 242 | return true; |
| 243 | } |
| 244 | |
| 245 | // This is called when the user clicks "View" from the context menu. |
| 246 | private boolean onViewClicked(ContextMenuInfo menuInfo) { |
| 247 | AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; |
| 248 | launchFolderGallery(info.position); |
| 249 | return true; |
| 250 | } |
| 251 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 252 | @Override |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 253 | public void onStop() { |
| 254 | super.onStop(); |
Owen Lin | 937fc48 | 2009-04-14 02:02:51 -0700 | [diff] [blame] | 255 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 256 | abortWorker(); |
| 257 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 258 | unregisterReceiver(mReceiver); |
Owen Lin | 588e29b | 2009-07-07 11:37:12 -0700 | [diff] [blame] | 259 | getContentResolver().unregisterContentObserver(mDbObserver); |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 260 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 261 | // free up some ram |
| 262 | mAdapter = null; |
| 263 | mGridView.setAdapter(null); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 264 | unloadDrawable(); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 265 | } |
| 266 | |
| 267 | @Override |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 268 | public void onStart() { |
| 269 | super.onStart(); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 270 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 271 | mAdapter = new GalleryPickerAdapter(getLayoutInflater()); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 272 | mGridView.setAdapter(mAdapter); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 273 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 274 | // install an intent filter to receive SD card related events. |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 275 | IntentFilter intentFilter = new IntentFilter(); |
| 276 | intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 277 | intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); |
| 278 | intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); |
| 279 | intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); |
| 280 | intentFilter.addAction(Intent.ACTION_MEDIA_EJECT); |
| 281 | intentFilter.addDataScheme("file"); |
| 282 | |
| 283 | registerReceiver(mReceiver, intentFilter); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 284 | |
repo sync | 62ca191 | 2009-07-01 18:37:14 +0800 | [diff] [blame] | 285 | getContentResolver().registerContentObserver( |
| 286 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI, |
| 287 | true, mDbObserver); |
| 288 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 289 | // Assume the storage is mounted and not scanning. |
| 290 | mUnmounted = false; |
| 291 | mScanning = false; |
| 292 | startWorker(); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 293 | } |
| 294 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 295 | // This is used to stop the worker thread. |
| 296 | volatile boolean mAbort = false; |
| 297 | |
| 298 | // Create the worker thread. |
| 299 | private void startWorker() { |
| 300 | mAbort = false; |
| 301 | mWorkerThread = new Thread("GalleryPicker Worker") { |
Owen Lin | deb5725 | 2009-05-27 14:55:45 -0700 | [diff] [blame] | 302 | @Override |
| 303 | public void run() { |
| 304 | workerRun(); |
| 305 | } |
| 306 | }; |
Owen Lin | 33f86b8 | 2009-07-13 16:45:23 -0700 | [diff] [blame] | 307 | BitmapManager.instance().allowThreadDecoding(mWorkerThread); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 308 | mWorkerThread.start(); |
| 309 | } |
| 310 | |
| 311 | private void abortWorker() { |
| 312 | if (mWorkerThread != null) { |
Ray Chen | 28f3595 | 2009-10-05 14:34:24 -0700 | [diff] [blame] | 313 | BitmapManager.instance().cancelThreadDecoding(mWorkerThread, getContentResolver()); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 314 | mAbort = true; |
| 315 | try { |
| 316 | mWorkerThread.join(); |
| 317 | } catch (InterruptedException ex) { |
| 318 | Log.e(TAG, "join interrupted"); |
| 319 | } |
| 320 | mWorkerThread = null; |
Chih-Chung Chang | a61075a | 2009-05-07 11:07:06 +0800 | [diff] [blame] | 321 | // Remove all runnables in mHandler. |
| 322 | // (We assume that the "what" field in the messages are 0 |
| 323 | // for runnables). |
| 324 | mHandler.removeMessages(0); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 325 | mAdapter.clear(); |
| 326 | mAdapter.updateDisplay(); |
| 327 | clearImageLists(); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | // This is run in the worker thread. |
| 332 | private void workerRun() { |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 333 | // We collect items from checkImageList() and checkBucketIds() and |
| 334 | // put them in allItems. Later we give allItems to checkThumbBitmap() |
| 335 | // and generated thumbnail bitmaps for each item. We do this instead of |
| 336 | // generating thumbnail bitmaps in checkImageList() and checkBucketIds() |
| 337 | // because we want to show all the folders first, then update them with |
| 338 | // the thumb bitmaps. (Generating thumbnail bitmaps takes some time.) |
| 339 | ArrayList<Item> allItems = new ArrayList<Item>(); |
| 340 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 341 | checkScanning(); |
| 342 | if (mAbort) return; |
| 343 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 344 | checkImageList(allItems); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 345 | if (mAbort) return; |
| 346 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 347 | checkBucketIds(allItems); |
| 348 | if (mAbort) return; |
| 349 | |
| 350 | checkThumbBitmap(allItems); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 351 | if (mAbort) return; |
| 352 | |
| 353 | checkLowStorage(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 354 | } |
| 355 | |
| 356 | // This is run in the worker thread. |
| 357 | private void checkScanning() { |
| 358 | ContentResolver cr = getContentResolver(); |
| 359 | final boolean scanning = |
| 360 | ImageManager.isMediaScannerScanning(cr); |
| 361 | mHandler.post(new Runnable() { |
| 362 | public void run() { |
| 363 | checkScanningFinished(scanning); |
| 364 | } |
| 365 | }); |
| 366 | } |
| 367 | |
| 368 | // This is run in the main thread. |
| 369 | private void checkScanningFinished(boolean scanning) { |
| 370 | updateScanningDialog(scanning); |
| 371 | } |
| 372 | |
| 373 | // This is run in the worker thread. |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 374 | private void checkImageList(ArrayList<Item> allItems) { |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 375 | int length = IMAGE_LIST_DATA.length; |
| 376 | IImageList[] lists = new IImageList[length]; |
| 377 | for (int i = 0; i < length; i++) { |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 378 | ImageListData data = IMAGE_LIST_DATA[i]; |
| 379 | lists[i] = createImageList(data.mInclude, data.mBucketId, |
| 380 | getContentResolver()); |
| 381 | if (mAbort) return; |
| 382 | Item item = null; |
| 383 | |
| 384 | if (lists[i].isEmpty()) continue; |
| 385 | |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 386 | // i >= 3 means we are looking at All Images/All Videos. |
| 387 | // lists[i-3] is the corresponding Camera Images/Camera Videos. |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 388 | // We want to add the "All" list only if it's different from |
| 389 | // the "Camera" list. |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 390 | if (i >= 3 && lists[i].getCount() == lists[i - 3].getCount()) { |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 391 | continue; |
| 392 | } |
| 393 | |
| 394 | item = new Item(data.mType, |
| 395 | data.mBucketId, |
| 396 | getResources().getString(data.mStringId), |
| 397 | lists[i]); |
| 398 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 399 | allItems.add(item); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 400 | |
| 401 | final Item finalItem = item; |
| 402 | mHandler.post(new Runnable() { |
| 403 | public void run() { |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 404 | updateItem(finalItem); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 405 | } |
| 406 | }); |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | // This is run in the main thread. |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 411 | private void updateItem(Item item) { |
Owen Lin | f68887f | 2009-07-16 17:09:24 -0700 | [diff] [blame] | 412 | // Hide NoImageView if we are going to add the first item |
| 413 | if (mAdapter.getCount() == 0) { |
| 414 | hideNoImagesView(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 415 | } |
Owen Lin | f68887f | 2009-07-16 17:09:24 -0700 | [diff] [blame] | 416 | mAdapter.addItem(item); |
| 417 | mAdapter.updateDisplay(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 418 | } |
| 419 | |
| 420 | private static final String CAMERA_BUCKET = |
| 421 | ImageManager.CAMERA_IMAGE_BUCKET_ID; |
| 422 | |
| 423 | // This is run in the worker thread. |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 424 | private void checkBucketIds(ArrayList<Item> allItems) { |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 425 | final IImageList allImages; |
| 426 | if (!mScanning && !mUnmounted) { |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 427 | allImages = ImageManager.makeImageList( |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 428 | getContentResolver(), |
| 429 | ImageManager.DataLocation.ALL, |
Owen Lin | f271893 | 2009-06-03 17:07:33 -0700 | [diff] [blame] | 430 | ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS, |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 431 | ImageManager.SORT_DESCENDING, |
| 432 | null); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 433 | } else { |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 434 | allImages = ImageManager.makeEmptyImageList(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 435 | } |
| 436 | |
| 437 | if (mAbort) { |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 438 | allImages.close(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 439 | return; |
| 440 | } |
| 441 | |
| 442 | HashMap<String, String> hashMap = allImages.getBucketIds(); |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 443 | allImages.close(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 444 | if (mAbort) return; |
| 445 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 446 | for (Map.Entry<String, String> entry : hashMap.entrySet()) { |
| 447 | String key = entry.getKey(); |
| 448 | if (key == null) { |
| 449 | continue; |
| 450 | } |
| 451 | if (!key.equals(CAMERA_BUCKET)) { |
| 452 | IImageList list = createImageList( |
| 453 | ImageManager.INCLUDE_IMAGES |
| 454 | | ImageManager.INCLUDE_VIDEOS, key, |
| 455 | getContentResolver()); |
| 456 | if (mAbort) return; |
| 457 | |
| 458 | Item item = new Item(Item.TYPE_NORMAL_FOLDERS, key, |
| 459 | entry.getValue(), list); |
| 460 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 461 | allItems.add(item); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 462 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 463 | final Item finalItem = item; |
| 464 | mHandler.post(new Runnable() { |
| 465 | public void run() { |
| 466 | updateItem(finalItem); |
| 467 | } |
| 468 | }); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 469 | } |
| 470 | } |
| 471 | |
| 472 | mHandler.post(new Runnable() { |
| 473 | public void run() { |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 474 | checkBucketIdsFinished(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 475 | } |
| 476 | }); |
| 477 | } |
| 478 | |
| 479 | // This is run in the main thread. |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 480 | private void checkBucketIdsFinished() { |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 481 | |
| 482 | // If we just have one folder, open it. |
| 483 | // If we have zero folder, show the "no images" icon. |
| 484 | if (!mScanning) { |
| 485 | int numItems = mAdapter.mItems.size(); |
| 486 | if (numItems == 0) { |
| 487 | showNoImagesView(); |
| 488 | } else if (numItems == 1) { |
| 489 | mAdapter.mItems.get(0).launch(this); |
| 490 | finish(); |
| 491 | return; |
| 492 | } |
| 493 | } |
| 494 | } |
| 495 | |
Chih-Chung Chang | dfedc5b | 2009-05-07 11:49:24 +0800 | [diff] [blame] | 496 | private static final int THUMB_SIZE = 142; |
| 497 | // This is run in the worker thread. |
| 498 | private void checkThumbBitmap(ArrayList<Item> allItems) { |
| 499 | for (Item item : allItems) { |
| 500 | final Bitmap b = makeMiniThumbBitmap(THUMB_SIZE, THUMB_SIZE, |
| 501 | item.mImageList); |
| 502 | if (mAbort) { |
| 503 | if (b != null) b.recycle(); |
| 504 | return; |
| 505 | } |
| 506 | |
| 507 | final Item finalItem = item; |
| 508 | mHandler.post(new Runnable() { |
| 509 | public void run() { |
| 510 | updateThumbBitmap(finalItem, b); |
| 511 | } |
| 512 | }); |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | // This is run in the main thread. |
| 517 | private void updateThumbBitmap(Item item, Bitmap b) { |
| 518 | item.setThumbBitmap(b); |
| 519 | mAdapter.updateDisplay(); |
| 520 | } |
| 521 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 522 | private static final long LOW_STORAGE_THRESHOLD = 1024 * 1024 * 2; |
| 523 | |
| 524 | // This is run in the worker thread. |
| 525 | private void checkLowStorage() { |
| 526 | // Check available space only if we are writable |
| 527 | if (ImageManager.hasStorage()) { |
| 528 | String storageDirectory = Environment |
| 529 | .getExternalStorageDirectory().toString(); |
| 530 | StatFs stat = new StatFs(storageDirectory); |
| 531 | long remaining = (long) stat.getAvailableBlocks() |
| 532 | * (long) stat.getBlockSize(); |
| 533 | if (remaining < LOW_STORAGE_THRESHOLD) { |
| 534 | mHandler.post(new Runnable() { |
| 535 | public void run() { |
| 536 | checkLowStorageFinished(); |
| 537 | } |
| 538 | }); |
| 539 | } |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | // This is run in the main thread. |
| 544 | // This is called only if the storage is low. |
| 545 | private void checkLowStorageFinished() { |
| 546 | Toast.makeText(GalleryPicker.this, R.string.not_enough_space, 5000) |
| 547 | .show(); |
| 548 | } |
| 549 | |
| 550 | // IMAGE_LIST_DATA stores the parameters for the four image lists |
| 551 | // we are interested in. The order of the IMAGE_LIST_DATA array is |
| 552 | // significant (See the implementation of GalleryPickerAdapter.init). |
| 553 | private static final class ImageListData { |
| 554 | ImageListData(int type, int include, String bucketId, int stringId) { |
| 555 | mType = type; |
| 556 | mInclude = include; |
| 557 | mBucketId = bucketId; |
| 558 | mStringId = stringId; |
| 559 | } |
| 560 | int mType; |
| 561 | int mInclude; |
| 562 | String mBucketId; |
| 563 | int mStringId; |
| 564 | } |
| 565 | |
| 566 | private static final ImageListData[] IMAGE_LIST_DATA = { |
| 567 | // Camera Images |
| 568 | new ImageListData(Item.TYPE_CAMERA_IMAGES, |
| 569 | ImageManager.INCLUDE_IMAGES, |
| 570 | ImageManager.CAMERA_IMAGE_BUCKET_ID, |
| 571 | R.string.gallery_camera_bucket_name), |
| 572 | // Camera Videos |
| 573 | new ImageListData(Item.TYPE_CAMERA_VIDEOS, |
| 574 | ImageManager.INCLUDE_VIDEOS, |
| 575 | ImageManager.CAMERA_IMAGE_BUCKET_ID, |
| 576 | R.string.gallery_camera_videos_bucket_name), |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 577 | |
| 578 | // Camera Medias |
| 579 | new ImageListData(Item.TYPE_CAMERA_MEDIAS, |
| 580 | ImageManager.INCLUDE_VIDEOS | ImageManager.INCLUDE_IMAGES, |
| 581 | ImageManager.CAMERA_IMAGE_BUCKET_ID, |
| 582 | R.string.gallery_camera_media_bucket_name), |
| 583 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 584 | // All Images |
| 585 | new ImageListData(Item.TYPE_ALL_IMAGES, |
| 586 | ImageManager.INCLUDE_IMAGES, |
| 587 | null, |
| 588 | R.string.all_images), |
| 589 | |
| 590 | // All Videos |
| 591 | new ImageListData(Item.TYPE_ALL_VIDEOS, |
| 592 | ImageManager.INCLUDE_VIDEOS, |
| 593 | null, |
| 594 | R.string.all_videos), |
| 595 | }; |
| 596 | |
| 597 | |
| 598 | // These drawables are loaded on-demand. |
| 599 | Drawable mFrameGalleryMask; |
| 600 | Drawable mCellOutline; |
| 601 | Drawable mVideoOverlay; |
| 602 | |
| 603 | private void loadDrawableIfNeeded() { |
| 604 | if (mFrameGalleryMask != null) return; // already loaded |
| 605 | Resources r = getResources(); |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 606 | mFrameGalleryMask = r.getDrawable( |
| 607 | R.drawable.frame_gallery_preview_album_mask); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 608 | mCellOutline = r.getDrawable(android.R.drawable.gallery_thumb); |
| 609 | mVideoOverlay = r.getDrawable(R.drawable.ic_gallery_video_overlay); |
| 610 | } |
| 611 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 612 | private void unloadDrawable() { |
| 613 | mFrameGalleryMask = null; |
| 614 | mCellOutline = null; |
| 615 | mVideoOverlay = null; |
| 616 | } |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 617 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 618 | private static void placeImage(Bitmap image, Canvas c, Paint paint, |
| 619 | int imageWidth, int widthPadding, int imageHeight, |
| 620 | int heightPadding, int offsetX, int offsetY, |
| 621 | int pos) { |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 622 | int row = pos / 2; |
| 623 | int col = pos - (row * 2); |
| 624 | |
| 625 | int xPos = (col * (imageWidth + widthPadding)) - offsetX; |
| 626 | int yPos = (row * (imageHeight + heightPadding)) - offsetY; |
| 627 | |
| 628 | c.drawBitmap(image, xPos, yPos, paint); |
| 629 | } |
| 630 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 631 | // This is run in worker thread. |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 632 | private Bitmap makeMiniThumbBitmap(int width, int height, |
| 633 | IImageList images) { |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 634 | int count = images.getCount(); |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 635 | // We draw three different version of the folder image depending on the |
| 636 | // number of images in the folder. |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 637 | // For a single image, that image draws over the whole folder. |
| 638 | // For two or three images, we draw the two most recent photos. |
| 639 | // For four or more images, we draw four photos. |
| 640 | final int padding = 4; |
| 641 | int imageWidth = width; |
| 642 | int imageHeight = height; |
| 643 | int offsetWidth = 0; |
| 644 | int offsetHeight = 0; |
| 645 | |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 646 | imageWidth = (imageWidth - padding) / 2; // 2 here because we show two |
| 647 | // images |
| 648 | imageHeight = (imageHeight - padding) / 2; // per row and column |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 649 | |
Chih-Chung Chang | 6b27050 | 2009-04-29 11:57:06 +0800 | [diff] [blame] | 650 | final Paint p = new Paint(); |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 651 | final Bitmap b = Bitmap.createBitmap(width, height, |
| 652 | Bitmap.Config.ARGB_8888); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 653 | final Canvas c = new Canvas(b); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 654 | final Matrix m = new Matrix(); |
| 655 | |
| 656 | // draw the whole canvas as transparent |
| 657 | p.setColor(0x00000000); |
| 658 | c.drawPaint(p); |
| 659 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 660 | // load the drawables |
| 661 | loadDrawableIfNeeded(); |
| 662 | |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 663 | // draw the mask normally |
| 664 | p.setColor(0xFFFFFFFF); |
| 665 | mFrameGalleryMask.setBounds(0, 0, width, height); |
| 666 | mFrameGalleryMask.draw(c); |
| 667 | |
| 668 | Paint pdpaint = new Paint(); |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 669 | pdpaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 670 | |
| 671 | pdpaint.setStyle(Paint.Style.FILL); |
| 672 | c.drawRect(0, 0, width, height, pdpaint); |
| 673 | |
| 674 | for (int i = 0; i < 4; i++) { |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 675 | if (mAbort) { |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 676 | return null; |
| 677 | } |
| 678 | |
| 679 | Bitmap temp = null; |
Owen Lin | 101d528 | 2009-04-03 16:20:08 -0700 | [diff] [blame] | 680 | IImage image = i < count ? images.getImageAt(i) : null; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 681 | |
| 682 | if (image != null) { |
| 683 | temp = image.miniThumbBitmap(); |
| 684 | } |
| 685 | |
| 686 | if (temp != null) { |
| 687 | if (ImageManager.isVideo(image)) { |
| 688 | Bitmap newMap = temp.copy(temp.getConfig(), true); |
| 689 | Canvas overlayCanvas = new Canvas(newMap); |
| 690 | int overlayWidth = mVideoOverlay.getIntrinsicWidth(); |
| 691 | int overlayHeight = mVideoOverlay.getIntrinsicHeight(); |
| 692 | int left = (newMap.getWidth() - overlayWidth) / 2; |
| 693 | int top = (newMap.getHeight() - overlayHeight) / 2; |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 694 | Rect newBounds = new Rect(left, top, left + overlayWidth, |
| 695 | top + overlayHeight); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 696 | mVideoOverlay.setBounds(newBounds); |
| 697 | mVideoOverlay.draw(overlayCanvas); |
| 698 | temp.recycle(); |
| 699 | temp = newMap; |
| 700 | } |
| 701 | |
Chih-Chung Chang | 34fe2a9 | 2009-08-19 15:52:32 +0800 | [diff] [blame] | 702 | temp = Util.transform(m, temp, imageWidth, |
| 703 | imageHeight, true, Util.RECYCLE_INPUT); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 704 | } |
| 705 | |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 706 | Bitmap thumb = Bitmap.createBitmap(imageWidth, imageHeight, |
| 707 | Bitmap.Config.ARGB_8888); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 708 | Canvas tempCanvas = new Canvas(thumb); |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 709 | if (temp != null) { |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 710 | tempCanvas.drawBitmap(temp, new Matrix(), new Paint()); |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 711 | } |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 712 | mCellOutline.setBounds(0, 0, imageWidth, imageHeight); |
| 713 | mCellOutline.draw(tempCanvas); |
| 714 | |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 715 | placeImage(thumb, c, pdpaint, imageWidth, padding, imageHeight, |
| 716 | padding, offsetWidth, offsetHeight, i); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 717 | |
| 718 | thumb.recycle(); |
| 719 | |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 720 | if (temp != null) { |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 721 | temp.recycle(); |
Chih-Chung Chang | d8209aa | 2009-04-07 11:45:12 -0700 | [diff] [blame] | 722 | } |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 723 | } |
| 724 | |
| 725 | return b; |
| 726 | } |
| 727 | |
| 728 | @Override |
| 729 | public boolean onCreateOptionsMenu(Menu menu) { |
| 730 | super.onCreateOptionsMenu(menu); |
| 731 | |
| 732 | MenuHelper.addCaptureMenuItems(menu, this); |
| 733 | |
Chih-Chung Chang | d189083 | 2009-09-08 13:32:52 +0800 | [diff] [blame] | 734 | menu.add(Menu.NONE, Menu.NONE, MenuHelper.POSITION_GALLERY_SETTING, |
| 735 | R.string.camerasettings) |
Chih-Chung Chang | 885e558 | 2009-04-08 03:24:13 -0700 | [diff] [blame] | 736 | .setOnMenuItemClickListener(new OnMenuItemClickListener() { |
| 737 | public boolean onMenuItemClick(MenuItem item) { |
| 738 | Intent preferences = new Intent(); |
| 739 | preferences.setClass(GalleryPicker.this, |
| 740 | GallerySettings.class); |
| 741 | startActivity(preferences); |
| 742 | return true; |
| 743 | } |
| 744 | }) |
| 745 | .setAlphabeticShortcut('p') |
| 746 | .setIcon(android.R.drawable.ic_menu_preferences); |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 747 | |
| 748 | return true; |
| 749 | } |
| 750 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 751 | // image lists created by createImageList() are collected in mAllLists. |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 752 | // They will be closed in clearImageList, so they don't hold open files |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 753 | // on SD card. We will be killed if we don't close files when the SD card |
| 754 | // is unmounted. |
| 755 | ArrayList<IImageList> mAllLists = new ArrayList<IImageList>(); |
| 756 | |
| 757 | private IImageList createImageList(int mediaTypes, String bucketId, |
| 758 | ContentResolver cr) { |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 759 | IImageList list = ImageManager.makeImageList( |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 760 | cr, |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 761 | ImageManager.DataLocation.ALL, |
| 762 | mediaTypes, |
| 763 | ImageManager.SORT_DESCENDING, |
| 764 | bucketId); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 765 | mAllLists.add(list); |
| 766 | return list; |
| 767 | } |
| 768 | |
| 769 | private void clearImageLists() { |
| 770 | for (IImageList list : mAllLists) { |
Chih-Chung Chang | f5bf8ca | 2009-08-25 18:28:29 +0800 | [diff] [blame] | 771 | list.close(); |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 772 | } |
| 773 | mAllLists.clear(); |
| 774 | } |
| 775 | } |
| 776 | |
| 777 | // Item is the underlying data for GalleryPickerAdapter. |
| 778 | // It is passed from the activity to the adapter. |
| 779 | class Item { |
| 780 | public static final int TYPE_NONE = -1; |
| 781 | public static final int TYPE_ALL_IMAGES = 0; |
| 782 | public static final int TYPE_ALL_VIDEOS = 1; |
| 783 | public static final int TYPE_CAMERA_IMAGES = 2; |
| 784 | public static final int TYPE_CAMERA_VIDEOS = 3; |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 785 | public static final int TYPE_CAMERA_MEDIAS = 4; |
| 786 | public static final int TYPE_NORMAL_FOLDERS = 5; |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 787 | |
| 788 | public final int mType; |
| 789 | public final String mBucketId; |
| 790 | public final String mName; |
| 791 | public final IImageList mImageList; |
| 792 | public final int mCount; |
| 793 | public final Uri mFirstImageUri; // could be null if the list is empty |
| 794 | |
| 795 | // The thumbnail bitmap is set by setThumbBitmap() later because we want |
| 796 | // to let the user sees the folder icon as soon as possible (and possibly |
| 797 | // select them), then present more detailed information when we have it. |
| 798 | public Bitmap mThumbBitmap; // the thumbnail bitmap for the image list |
| 799 | |
| 800 | public Item(int type, String bucketId, String name, IImageList list) { |
| 801 | mType = type; |
| 802 | mBucketId = bucketId; |
| 803 | mName = name; |
| 804 | mImageList = list; |
| 805 | mCount = list.getCount(); |
| 806 | if (mCount > 0) { |
| 807 | mFirstImageUri = list.getImageAt(0).fullSizeImageUri(); |
| 808 | } else { |
| 809 | mFirstImageUri = null; |
| 810 | } |
| 811 | } |
| 812 | |
| 813 | public void setThumbBitmap(Bitmap thumbBitmap) { |
| 814 | mThumbBitmap = thumbBitmap; |
| 815 | } |
| 816 | |
| 817 | public boolean needsBucketId() { |
| 818 | return mType >= TYPE_CAMERA_IMAGES; |
| 819 | } |
| 820 | |
| 821 | public void launch(Activity activity) { |
| 822 | Uri uri = Images.Media.INTERNAL_CONTENT_URI; |
| 823 | if (needsBucketId()) { |
| 824 | uri = uri.buildUpon() |
| 825 | .appendQueryParameter("bucketId", mBucketId).build(); |
| 826 | } |
| 827 | Intent intent = new Intent(Intent.ACTION_VIEW, uri); |
| 828 | intent.putExtra("windowTitle", mName); |
| 829 | intent.putExtra("mediaTypes", getIncludeMediaTypes()); |
| 830 | activity.startActivity(intent); |
| 831 | } |
| 832 | |
| 833 | public int getIncludeMediaTypes() { |
| 834 | return convertItemTypeToIncludedMediaType(mType); |
| 835 | } |
| 836 | |
| 837 | public static int convertItemTypeToIncludedMediaType(int itemType) { |
| 838 | switch (itemType) { |
| 839 | case TYPE_ALL_IMAGES: |
| 840 | case TYPE_CAMERA_IMAGES: |
| 841 | return ImageManager.INCLUDE_IMAGES; |
| 842 | case TYPE_ALL_VIDEOS: |
| 843 | case TYPE_CAMERA_VIDEOS: |
| 844 | return ImageManager.INCLUDE_VIDEOS; |
| 845 | case TYPE_NORMAL_FOLDERS: |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 846 | case TYPE_CAMERA_MEDIAS: |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 847 | default: |
| 848 | return ImageManager.INCLUDE_IMAGES |
| 849 | | ImageManager.INCLUDE_VIDEOS; |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | public int getOverlay() { |
| 854 | switch (mType) { |
| 855 | case TYPE_ALL_IMAGES: |
| 856 | case TYPE_CAMERA_IMAGES: |
| 857 | return R.drawable.frame_overlay_gallery_camera; |
| 858 | case TYPE_ALL_VIDEOS: |
| 859 | case TYPE_CAMERA_VIDEOS: |
Owen Lin | 59b09db | 2009-05-28 20:21:39 -0700 | [diff] [blame] | 860 | case TYPE_CAMERA_MEDIAS: |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 861 | return R.drawable.frame_overlay_gallery_video; |
| 862 | case TYPE_NORMAL_FOLDERS: |
| 863 | default: |
| 864 | return R.drawable.frame_overlay_gallery_folder; |
| 865 | } |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | class GalleryPickerAdapter extends BaseAdapter { |
| 870 | ArrayList<Item> mItems = new ArrayList<Item>(); |
| 871 | LayoutInflater mInflater; |
| 872 | |
| 873 | GalleryPickerAdapter(LayoutInflater inflater) { |
| 874 | mInflater = inflater; |
| 875 | } |
| 876 | |
| 877 | public void addItem(Item item) { |
| 878 | mItems.add(item); |
| 879 | } |
| 880 | |
| 881 | public void updateDisplay() { |
| 882 | notifyDataSetChanged(); |
| 883 | } |
| 884 | |
| 885 | public void clear() { |
| 886 | mItems.clear(); |
| 887 | } |
| 888 | |
| 889 | public int getCount() { |
| 890 | return mItems.size(); |
| 891 | } |
| 892 | |
| 893 | public Object getItem(int position) { |
| 894 | return null; |
| 895 | } |
| 896 | |
| 897 | public long getItemId(int position) { |
| 898 | return position; |
| 899 | } |
| 900 | |
| 901 | public String baseTitleForPosition(int position) { |
| 902 | return mItems.get(position).mName; |
| 903 | } |
| 904 | |
| 905 | public int getIncludeMediaTypes(int position) { |
| 906 | return mItems.get(position).getIncludeMediaTypes(); |
| 907 | } |
| 908 | |
| 909 | public View getView(final int position, View convertView, |
| 910 | ViewGroup parent) { |
| 911 | View v; |
| 912 | |
| 913 | if (convertView == null) { |
| 914 | v = mInflater.inflate(R.layout.gallery_picker_item, null); |
| 915 | } else { |
| 916 | v = convertView; |
| 917 | } |
| 918 | |
| 919 | TextView titleView = (TextView) v.findViewById(R.id.title); |
| 920 | |
| 921 | GalleryPickerItem iv = |
| 922 | (GalleryPickerItem) v.findViewById(R.id.thumbnail); |
| 923 | Item item = mItems.get(position); |
| 924 | iv.setOverlay(item.getOverlay()); |
| 925 | if (item.mThumbBitmap != null) { |
| 926 | iv.setImageBitmap(item.mThumbBitmap); |
| 927 | String title = item.mName + " (" + item.mCount + ")"; |
| 928 | titleView.setText(title); |
| 929 | } else { |
| 930 | iv.setImageResource(android.R.color.transparent); |
| 931 | titleView.setText(item.mName); |
| 932 | } |
| 933 | |
Owen Lin | d10dda2 | 2009-10-13 12:15:29 +0800 | [diff] [blame] | 934 | // An workaround due to a bug in TextView. If the length of text is |
| 935 | // different from the previous in convertView, the layout would be |
| 936 | // wrong. |
| 937 | titleView.requestLayout(); |
| 938 | |
Chih-Chung Chang | eb99d4e | 2009-05-06 15:21:04 +0800 | [diff] [blame] | 939 | return v; |
The Android Open Source Project | b64d345 | 2009-03-03 19:32:20 -0800 | [diff] [blame] | 940 | } |
| 941 | } |