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