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