1 /* 2 * Copyright (C) 2009 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.cooliris.media; 18 19 import com.cooliris.app.App; 20 import com.cooliris.app.Res; 21 import com.cooliris.cache.CacheService; 22 import com.cooliris.wallpaper.RandomDataSource; 23 import com.cooliris.wallpaper.Slideshow; 24 25 import android.app.Activity; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.res.Configuration; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.os.Environment; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Message; 35 import android.os.PowerManager; 36 import android.os.PowerManager.WakeLock; 37 import android.provider.MediaStore.Images; 38 import android.util.Log; 39 import android.view.KeyEvent; 40 import android.widget.Toast; 41 42 import java.util.HashMap; 43 44 public final class Gallery extends Activity { 45 public static final String REVIEW_ACTION = "com.cooliris.media.action.REVIEW"; 46 private static final String TAG = "Gallery"; 47 48 private App mApp = null; 49 private RenderView mRenderView = null; 50 private GridLayer mGridLayer; 51 private WakeLock mWakeLock; 52 private HashMap<String, Boolean> mAccountsEnabled = new HashMap<String, Boolean>(); 53 private boolean mDockSlideshow = false; 54 private int mNumRetries; 55 private boolean mImageManagerHasStorageAfterDelay = false; 56 private HandlerThread mPicasaAccountThread = new HandlerThread("PicasaAccountMonitor"); 57 private Handler mPicasaHandler = null; 58 59 private static final int GET_PICASA_ACCOUNT_STATUS = 1; 60 private static final int UPDATE_PICASA_ACCOUNT_STATUS = 2; 61 62 private static final int CHECK_STORAGE = 0; 63 private static final int HANDLE_INTENT = 1; 64 private static final int NUM_STORAGE_CHECKS = 25; 65 66 private final Handler handler = new Handler() { 67 @Override 68 public void handleMessage(Message msg) { 69 switch (msg.what) { 70 case CHECK_STORAGE: 71 checkStorage(); 72 break; 73 case HANDLE_INTENT: 74 initializeDataSource(); 75 break; 76 } 77 } 78 }; 79 80 private void checkStorage() { 81 mNumRetries++; 82 mImageManagerHasStorageAfterDelay = ImageManager.hasStorage(); 83 if (!mImageManagerHasStorageAfterDelay && mNumRetries < NUM_STORAGE_CHECKS) { 84 if (mNumRetries == 1) { 85 int res; 86 87 if (Environment.isExternalStorageRemovable()) { 88 res = Res.string.no_sd_card; 89 } else { 90 res = Res.string.no_usb_storage; 91 } 92 93 mApp.showToast(getResources().getString(res), Toast.LENGTH_LONG); 94 } 95 handler.sendEmptyMessageDelayed(CHECK_STORAGE, 200); 96 } else { 97 handler.sendEmptyMessage(HANDLE_INTENT); 98 } 99 } 100 101 @Override 102 public void onCreate(Bundle savedInstanceState) { 103 super.onCreate(savedInstanceState); 104 mApp = new App(Gallery.this); 105 final boolean imageManagerHasStorage = ImageManager.hasStorage(); 106 boolean slideshowIntent = false; 107 if (isViewIntent()) { 108 Bundle extras = getIntent().getExtras(); 109 if (extras != null) { 110 slideshowIntent = extras.getBoolean("slideshow", false); 111 } 112 } 113 if (isViewIntent() && getIntent().getData().equals(Images.Media.EXTERNAL_CONTENT_URI) && slideshowIntent) { 114 if (!imageManagerHasStorage) { 115 int res; 116 117 if (Environment.isExternalStorageRemovable()) { 118 res = Res.string.no_sd_card; 119 } else { 120 res = Res.string.no_usb_storage; 121 } 122 123 Toast.makeText(this, getResources().getString(res), Toast.LENGTH_LONG).show(); 124 finish(); 125 } else { 126 Slideshow slideshow = new Slideshow(this); 127 slideshow.setDataSource(new RandomDataSource()); 128 setContentView(slideshow); 129 mDockSlideshow = true; 130 } 131 return; 132 } 133 mRenderView = new RenderView(this); 134 mGridLayer = new GridLayer(this, (int) (96.0f * App.PIXEL_DENSITY), (int) (72.0f * App.PIXEL_DENSITY), new GridLayoutInterface(4), 135 mRenderView); 136 mRenderView.setRootLayer(mGridLayer); 137 setContentView(mRenderView); 138 139 mPicasaAccountThread.start(); 140 mPicasaHandler = new Handler(mPicasaAccountThread.getLooper()) { 141 142 @Override 143 public void handleMessage(Message msg) { 144 switch (msg.what) { 145 case GET_PICASA_ACCOUNT_STATUS: 146 mAccountsEnabled = PicasaDataSource.getAccountStatus(Gallery.this); 147 break; 148 case UPDATE_PICASA_ACCOUNT_STATUS: 149 updatePicasaAccountStatus(); 150 break; 151 } 152 } 153 }; 154 155 sendInitialMessage(); 156 157 Log.i(TAG, "onCreate"); 158 } 159 160 private void sendInitialMessage() { 161 mNumRetries = 0; 162 Message checkStorage = new Message(); 163 checkStorage.what = CHECK_STORAGE; 164 handler.sendMessage(checkStorage); 165 } 166 167 @Override 168 protected void onNewIntent(Intent intent) { 169 setIntent(intent); 170 handler.removeMessages(CHECK_STORAGE); 171 handler.removeMessages(HANDLE_INTENT); 172 173 sendInitialMessage(); 174 } 175 176 @Override 177 public void onRestart() { 178 super.onRestart(); 179 } 180 181 @Override 182 public void onStart() { 183 super.onStart(); 184 } 185 186 @Override 187 public void onResume() { 188 super.onResume(); 189 if (mDockSlideshow) { 190 if (mWakeLock != null) { 191 if (mWakeLock.isHeld()) { 192 mWakeLock.release(); 193 } 194 } 195 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 196 mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GridView.Slideshow.All"); 197 mWakeLock.acquire(); 198 return; 199 } 200 if (mRenderView != null) { 201 mRenderView.onResume(); 202 } 203 if (mApp.isPaused()) { 204 if (mPicasaHandler != null) { 205 mPicasaHandler.removeMessages(GET_PICASA_ACCOUNT_STATUS); 206 mPicasaHandler.sendEmptyMessage(UPDATE_PICASA_ACCOUNT_STATUS); 207 } 208 mApp.onResume(); 209 } 210 } 211 212 void updatePicasaAccountStatus() { 213 // We check to see if the authenticated accounts have 214 // changed, if so, reload the datasource. 215 216 // TODO: This should be done in PicasaDataFeed 217 if (mGridLayer != null) { 218 HashMap<String, Boolean> accountsEnabled = PicasaDataSource.getAccountStatus(this); 219 if (!accountsEnabled.equals(mAccountsEnabled)) { 220 mGridLayer.setDataSource(mGridLayer.getDataSource()); 221 mAccountsEnabled = accountsEnabled; 222 } 223 } 224 } 225 226 @Override 227 public void onPause() { 228 super.onPause(); 229 if (mRenderView != null) 230 mRenderView.onPause(); 231 if (mWakeLock != null) { 232 if (mWakeLock.isHeld()) { 233 mWakeLock.release(); 234 } 235 mWakeLock = null; 236 } 237 238 LocalDataSource.sThumbnailCache.flush(); 239 LocalDataSource.sThumbnailCacheVideo.flush(); 240 PicasaDataSource.sThumbnailCache.flush(); 241 242 if (mPicasaHandler != null) { 243 mPicasaHandler.removeMessages(GET_PICASA_ACCOUNT_STATUS); 244 mPicasaHandler.removeMessages(UPDATE_PICASA_ACCOUNT_STATUS); 245 } 246 mApp.onPause(); 247 } 248 249 @Override 250 public void onStop() { 251 super.onStop(); 252 if (mGridLayer != null) 253 mGridLayer.stop(); 254 255 // Start the thumbnailer. 256 CacheService.startCache(this, true); 257 } 258 259 @Override 260 public void onDestroy() { 261 // Force GLThread to exit. 262 setContentView(Res.layout.main); 263 264 // Remove any post messages. 265 handler.removeMessages(CHECK_STORAGE); 266 handler.removeMessages(HANDLE_INTENT); 267 268 mPicasaAccountThread.quit(); 269 mPicasaAccountThread = null; 270 mPicasaHandler = null; 271 272 if (mGridLayer != null) { 273 DataSource dataSource = mGridLayer.getDataSource(); 274 if (dataSource != null) { 275 dataSource.shutdown(); 276 } 277 mGridLayer.shutdown(); 278 } 279 if (mRenderView != null) { 280 mRenderView.shutdown(); 281 mRenderView = null; 282 } 283 mGridLayer = null; 284 mApp.shutdown(); 285 super.onDestroy(); 286 Log.i(TAG, "onDestroy"); 287 } 288 289 @Override 290 public void onConfigurationChanged(Configuration newConfig) { 291 super.onConfigurationChanged(newConfig); 292 if (mGridLayer != null) { 293 mGridLayer.markDirty(30); 294 } 295 if (mRenderView != null) 296 mRenderView.requestRender(); 297 Log.i(TAG, "onConfigurationChanged"); 298 } 299 300 @Override 301 public boolean onKeyDown(int keyCode, KeyEvent event) { 302 if (mRenderView != null) { 303 return mRenderView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); 304 } else { 305 return super.onKeyDown(keyCode, event); 306 } 307 } 308 309 private boolean isPickIntent() { 310 String action = getIntent().getAction(); 311 return (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)); 312 } 313 314 private boolean isViewIntent() { 315 String action = getIntent().getAction(); 316 return Intent.ACTION_VIEW.equals(action); 317 } 318 319 private boolean isReviewIntent() { 320 String action = getIntent().getAction(); 321 return REVIEW_ACTION.equals(action); 322 } 323 324 private boolean isImageType(String type) { 325 return type.contains("*/") || type.equals("vnd.android.cursor.dir/image") || type.equals("image/*"); 326 } 327 328 private boolean isVideoType(String type) { 329 return type.contains("*/") || type.equals("vnd.android.cursor.dir/video") || type.equals("video/*"); 330 } 331 332 @Override 333 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 334 switch (requestCode) { 335 case CropImage.CROP_MSG: { 336 if (resultCode == RESULT_OK) { 337 setResult(resultCode, data); 338 finish(); 339 } 340 break; 341 } 342 case CropImage.CROP_MSG_INTERNAL: { 343 // We cropped an image, we must try to set the focus of the camera 344 // to that image. 345 if (resultCode == RESULT_OK) { 346 String contentUri = data.getAction(); 347 if (mGridLayer != null && contentUri != null) { 348 mGridLayer.focusItem(contentUri); 349 } 350 } 351 break; 352 } 353 } 354 } 355 356 @Override 357 public void onLowMemory() { 358 if (mRenderView != null) { 359 mRenderView.handleLowMemory(); 360 } 361 } 362 363 private void initializeDataSource() { 364 final boolean hasStorage = mImageManagerHasStorageAfterDelay; 365 // Creating the DataSource objects. 366 final PicasaDataSource picasaDataSource = new PicasaDataSource(Gallery.this); 367 final LocalDataSource localDataSource = new LocalDataSource(Gallery.this, LocalDataSource.URI_ALL_MEDIA, false); 368 final ConcatenatedDataSource combinedDataSource = new ConcatenatedDataSource(localDataSource, picasaDataSource); 369 370 // Depending upon the intent, we assign the right dataSource. 371 if (!isPickIntent() && !isViewIntent() && !isReviewIntent()) { 372 localDataSource.setMimeFilter(true, true); 373 if (hasStorage) { 374 mGridLayer.setDataSource(combinedDataSource); 375 } else { 376 mGridLayer.setDataSource(picasaDataSource); 377 } 378 } else if (isPickIntent()) { 379 final Intent intent = getIntent(); 380 if (intent != null) { 381 String type = intent.resolveType(Gallery.this); 382 if (type == null) { 383 // By default, we include images 384 type = "image/*"; 385 } 386 boolean includeImages = isImageType(type); 387 boolean includeVideos = isVideoType(type); 388 localDataSource.setMimeFilter(includeImages, includeVideos); 389 if (includeImages) { 390 if (hasStorage) { 391 mGridLayer.setDataSource(combinedDataSource); 392 } else { 393 mGridLayer.setDataSource(picasaDataSource); 394 } 395 } else { 396 mGridLayer.setDataSource(localDataSource); 397 } 398 mGridLayer.setPickIntent(true); 399 if (hasStorage) { 400 mApp.showToast(getResources().getString(Res.string.pick_prompt), Toast.LENGTH_LONG); 401 } 402 } 403 } else { // view intent for images and review intent for images and videos 404 final Intent intent = getIntent(); 405 Uri uri = intent.getData(); 406 boolean slideshow = intent.getBooleanExtra("slideshow", false); 407 final LocalDataSource singleDataSource = new LocalDataSource(Gallery.this, uri.toString(), true); 408 // Display both image and video. 409 singleDataSource.setMimeFilter(true, true); 410 411 if (hasStorage) { 412 ConcatenatedDataSource singleCombinedDataSource = new ConcatenatedDataSource(singleDataSource, 413 picasaDataSource); 414 mGridLayer.setDataSource(singleCombinedDataSource); 415 } else { 416 mGridLayer.setDataSource(picasaDataSource); 417 } 418 mGridLayer.setViewIntent(true, Utils.getBucketNameFromUri(getContentResolver(), uri)); 419 420 if (isReviewIntent()) { 421 mGridLayer.setEnterSelectionMode(true); 422 } 423 424 if (singleDataSource.isSingleImage()) { 425 mGridLayer.setSingleImage(false); 426 } else if (slideshow) { 427 mGridLayer.setSingleImage(true); 428 mGridLayer.startSlideshow(); 429 } 430 } 431 // We record the set of enabled accounts for picasa. 432 mPicasaHandler.sendEmptyMessage(GET_PICASA_ACCOUNT_STATUS); 433 } 434 } 435