Home | History | Annotate | Download | only in media
      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