2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.cooliris.media;
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;
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;
42 import java.util.HashMap;
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";
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;
59 private static final int GET_PICASA_ACCOUNT_STATUS = 1;
60 private static final int UPDATE_PICASA_ACCOUNT_STATUS = 2;
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;
66 private final Handler handler = new Handler() {
68 public void handleMessage(Message msg) {
74 initializeDataSource();
80 private void checkStorage() {
82 mImageManagerHasStorageAfterDelay = ImageManager.hasStorage();
83 if (!mImageManagerHasStorageAfterDelay && mNumRetries < NUM_STORAGE_CHECKS) {
84 if (mNumRetries == 1) {
87 if (Environment.isExternalStorageRemovable()) {
88 res = Res.string.no_sd_card;
90 res = Res.string.no_usb_storage;
93 mApp.showToast(getResources().getString(res), Toast.LENGTH_LONG);
95 handler.sendEmptyMessageDelayed(CHECK_STORAGE, 200);
97 handler.sendEmptyMessage(HANDLE_INTENT);
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);
113 if (isViewIntent() && getIntent().getData().equals(Images.Media.EXTERNAL_CONTENT_URI) && slideshowIntent) {
114 if (!imageManagerHasStorage) {
117 if (Environment.isExternalStorageRemovable()) {
118 res = Res.string.no_sd_card;
120 res = Res.string.no_usb_storage;
123 Toast.makeText(this, getResources().getString(res), Toast.LENGTH_LONG).show();
126 Slideshow slideshow = new Slideshow(this);
127 slideshow.setDataSource(new RandomDataSource());
128 setContentView(slideshow);
129 mDockSlideshow = true;
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),
136 mRenderView.setRootLayer(mGridLayer);
137 setContentView(mRenderView);
139 mPicasaAccountThread.start();
140 mPicasaHandler = new Handler(mPicasaAccountThread.getLooper()) {
143 public void handleMessage(Message msg) {
145 case GET_PICASA_ACCOUNT_STATUS:
146 mAccountsEnabled = PicasaDataSource.getAccountStatus(Gallery.this);
148 case UPDATE_PICASA_ACCOUNT_STATUS:
149 updatePicasaAccountStatus();
155 sendInitialMessage();
157 Log.i(TAG, "onCreate");
160 private void sendInitialMessage() {
162 Message checkStorage = new Message();
163 checkStorage.what = CHECK_STORAGE;
164 handler.sendMessage(checkStorage);
168 protected void onNewIntent(Intent intent) {
170 handler.removeMessages(CHECK_STORAGE);
171 handler.removeMessages(HANDLE_INTENT);
173 sendInitialMessage();
177 public void onRestart() {
182 public void onStart() {
187 public void onResume() {
189 if (mDockSlideshow) {
190 if (mWakeLock != null) {
191 if (mWakeLock.isHeld()) {
195 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
196 mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GridView.Slideshow.All");
200 if (mRenderView != null) {
201 mRenderView.onResume();
203 if (mApp.isPaused()) {
204 if (mPicasaHandler != null) {
205 mPicasaHandler.removeMessages(GET_PICASA_ACCOUNT_STATUS);
206 mPicasaHandler.sendEmptyMessage(UPDATE_PICASA_ACCOUNT_STATUS);
212 void updatePicasaAccountStatus() {
213 // We check to see if the authenticated accounts have
214 // changed, if so, reload the datasource.
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;
227 public void onPause() {
229 if (mRenderView != null)
230 mRenderView.onPause();
231 if (mWakeLock != null) {
232 if (mWakeLock.isHeld()) {
238 LocalDataSource.sThumbnailCache.flush();
239 LocalDataSource.sThumbnailCacheVideo.flush();
240 PicasaDataSource.sThumbnailCache.flush();
242 if (mPicasaHandler != null) {
243 mPicasaHandler.removeMessages(GET_PICASA_ACCOUNT_STATUS);
244 mPicasaHandler.removeMessages(UPDATE_PICASA_ACCOUNT_STATUS);
250 public void onStop() {
252 if (mGridLayer != null)
255 // Start the thumbnailer.
256 CacheService.startCache(this, true);
260 public void onDestroy() {
261 // Force GLThread to exit.
262 setContentView(Res.layout.main);
264 // Remove any post messages.
265 handler.removeMessages(CHECK_STORAGE);
266 handler.removeMessages(HANDLE_INTENT);
268 mPicasaAccountThread.quit();
269 mPicasaAccountThread = null;
270 mPicasaHandler = null;
272 if (mGridLayer != null) {
273 DataSource dataSource = mGridLayer.getDataSource();
274 if (dataSource != null) {
275 dataSource.shutdown();
277 mGridLayer.shutdown();
279 if (mRenderView != null) {
280 mRenderView.shutdown();
286 Log.i(TAG, "onDestroy");
290 public void onConfigurationChanged(Configuration newConfig) {
291 super.onConfigurationChanged(newConfig);
292 if (mGridLayer != null) {
293 mGridLayer.markDirty(30);
295 if (mRenderView != null)
296 mRenderView.requestRender();
297 Log.i(TAG, "onConfigurationChanged");
301 public boolean onKeyDown(int keyCode, KeyEvent event) {
302 if (mRenderView != null) {
303 return mRenderView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
305 return super.onKeyDown(keyCode, event);
309 private boolean isPickIntent() {
310 String action = getIntent().getAction();
311 return (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action));
314 private boolean isViewIntent() {
315 String action = getIntent().getAction();
316 return Intent.ACTION_VIEW.equals(action);
319 private boolean isReviewIntent() {
320 String action = getIntent().getAction();
321 return REVIEW_ACTION.equals(action);
324 private boolean isImageType(String type) {
325 return type.contains("*/") || type.equals("vnd.android.cursor.dir/image") || type.equals("image/*");
328 private boolean isVideoType(String type) {
329 return type.contains("*/") || type.equals("vnd.android.cursor.dir/video") || type.equals("video/*");
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);
342 case CropImage.CROP_MSG_INTERNAL: {
343 // We cropped an image, we must try to set the focus of the camera
345 if (resultCode == RESULT_OK) {
346 String contentUri = data.getAction();
347 if (mGridLayer != null && contentUri != null) {
348 mGridLayer.focusItem(contentUri);
357 public void onLowMemory() {
358 if (mRenderView != null) {
359 mRenderView.handleLowMemory();
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);
370 // Depending upon the intent, we assign the right dataSource.
371 if (!isPickIntent() && !isViewIntent() && !isReviewIntent()) {
372 localDataSource.setMimeFilter(true, true);
374 mGridLayer.setDataSource(combinedDataSource);
376 mGridLayer.setDataSource(picasaDataSource);
378 } else if (isPickIntent()) {
379 final Intent intent = getIntent();
380 if (intent != null) {
381 String type = intent.resolveType(Gallery.this);
383 // By default, we include images
386 boolean includeImages = isImageType(type);
387 boolean includeVideos = isVideoType(type);
388 localDataSource.setMimeFilter(includeImages, includeVideos);
391 mGridLayer.setDataSource(combinedDataSource);
393 mGridLayer.setDataSource(picasaDataSource);
396 mGridLayer.setDataSource(localDataSource);
398 mGridLayer.setPickIntent(true);
400 mApp.showToast(getResources().getString(Res.string.pick_prompt), Toast.LENGTH_LONG);
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);
412 ConcatenatedDataSource singleCombinedDataSource = new ConcatenatedDataSource(singleDataSource,
414 mGridLayer.setDataSource(singleCombinedDataSource);
416 mGridLayer.setDataSource(picasaDataSource);
418 mGridLayer.setViewIntent(true, Utils.getBucketNameFromUri(getContentResolver(), uri));
420 if (isReviewIntent()) {
421 mGridLayer.setEnterSelectionMode(true);
424 if (singleDataSource.isSingleImage()) {
425 mGridLayer.setSingleImage(false);
426 } else if (slideshow) {
427 mGridLayer.setSingleImage(true);
428 mGridLayer.startSlideshow();
431 // We record the set of enabled accounts for picasa.
432 mPicasaHandler.sendEmptyMessage(GET_PICASA_ACCOUNT_STATUS);