AlbumPage.java revision 35e7b4ae727394aace466ebcf33d89adf01b690c
1/*
2 * Copyright (C) 2010 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.android.gallery3d.app;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.Rect;
23import android.net.Uri;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Message;
27import android.os.Vibrator;
28import android.provider.MediaStore;
29import android.view.Menu;
30import android.view.MenuInflater;
31import android.view.MenuItem;
32import android.widget.Toast;
33
34import com.android.gallery3d.R;
35import com.android.gallery3d.common.Utils;
36import com.android.gallery3d.data.DataManager;
37import com.android.gallery3d.data.MediaDetails;
38import com.android.gallery3d.data.MediaItem;
39import com.android.gallery3d.data.MediaObject;
40import com.android.gallery3d.data.MediaSet;
41import com.android.gallery3d.data.MtpDevice;
42import com.android.gallery3d.data.Path;
43import com.android.gallery3d.ui.ActionModeHandler;
44import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
45import com.android.gallery3d.ui.AlbumSlotRenderer;
46import com.android.gallery3d.ui.DetailsHelper;
47import com.android.gallery3d.ui.DetailsHelper.CloseListener;
48import com.android.gallery3d.ui.FadeTexture;
49import com.android.gallery3d.ui.GLCanvas;
50import com.android.gallery3d.ui.GLRoot;
51import com.android.gallery3d.ui.GLView;
52import com.android.gallery3d.ui.PhotoFallbackEffect;
53import com.android.gallery3d.ui.RelativePosition;
54import com.android.gallery3d.ui.SelectionManager;
55import com.android.gallery3d.ui.SlotView;
56import com.android.gallery3d.ui.SynchronizedHandler;
57import com.android.gallery3d.util.Future;
58import com.android.gallery3d.util.GalleryUtils;
59import com.android.gallery3d.util.MediaSetUtils;
60
61public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner,
62        SelectionManager.SelectionListener, MediaSet.SyncListener, GalleryActionBar.OnAlbumModeSelectedListener {
63    @SuppressWarnings("unused")
64    private static final String TAG = "AlbumPage";
65
66    public static final String KEY_MEDIA_PATH = "media-path";
67    public static final String KEY_PARENT_MEDIA_PATH = "parent-media-path";
68    public static final String KEY_SET_CENTER = "set-center";
69    public static final String KEY_AUTO_SELECT_ALL = "auto-select-all";
70    public static final String KEY_SHOW_CLUSTER_MENU = "cluster-menu";
71    public static final String KEY_EMPTY_ALBUM = "empty-album";
72    public static final String KEY_RESUME_ANIMATION = "resume_animation";
73
74    private static final int REQUEST_SLIDESHOW = 1;
75    public static final int REQUEST_PHOTO = 2;
76    private static final int REQUEST_DO_ANIMATION = 3;
77
78    private static final int BIT_LOADING_RELOAD = 1;
79    private static final int BIT_LOADING_SYNC = 2;
80
81    private static final float USER_DISTANCE_METER = 0.3f;
82
83    private boolean mIsActive = false;
84    private AlbumSlotRenderer mAlbumView;
85    private Path mMediaSetPath;
86    private String mParentMediaSetString;
87    private SlotView mSlotView;
88
89    private AlbumDataLoader mAlbumDataAdapter;
90
91    protected SelectionManager mSelectionManager;
92    private Vibrator mVibrator;
93
94    private boolean mGetContent;
95    private boolean mShowClusterMenu;
96
97    private ActionModeHandler mActionModeHandler;
98    private int mFocusIndex = 0;
99    private DetailsHelper mDetailsHelper;
100    private MyDetailsSource mDetailsSource;
101    private MediaSet mMediaSet;
102    private boolean mShowDetails;
103    private float mUserDistance; // in pixel
104    private Future<Integer> mSyncTask = null;
105    private boolean mLaunchedFromPhotoPage;
106    private boolean mInCameraApp;
107    private boolean mInCameraAndWantQuitOnPause;
108
109    private int mLoadingBits = 0;
110    private boolean mInitialSynced = false;
111    private RelativePosition mOpenCenter = new RelativePosition();
112
113    private Handler mHandler;
114    private static final int MSG_PICK_PHOTO = 0;
115
116    private PhotoFallbackEffect mResumeEffect;
117    private PhotoFallbackEffect.PositionProvider mPositionProvider =
118            new PhotoFallbackEffect.PositionProvider() {
119        @Override
120        public Rect getPosition(int index) {
121            Rect rect = mSlotView.getSlotRect(index);
122            Rect bounds = mSlotView.bounds();
123            rect.offset(bounds.left - mSlotView.getScrollX(),
124                    bounds.top - mSlotView.getScrollY());
125            return rect;
126        }
127
128        @Override
129        public int getItemIndex(Path path) {
130            int start = mSlotView.getVisibleStart();
131            int end = mSlotView.getVisibleEnd();
132            for (int i = start; i < end; ++i) {
133                MediaItem item = mAlbumDataAdapter.get(i);
134                if (item != null && item.getPath() == path) return i;
135            }
136            return -1;
137        }
138    };
139
140    @Override
141    protected int getBackgroundColorId() {
142        return R.color.album_background;
143    }
144
145    private final GLView mRootPane = new GLView() {
146        private final float mMatrix[] = new float[16];
147
148        @Override
149        protected void onLayout(
150                boolean changed, int left, int top, int right, int bottom) {
151
152            int slotViewTop = mActivity.getGalleryActionBar().getHeight();
153            int slotViewBottom = bottom - top;
154            int slotViewRight = right - left;
155
156            if (mShowDetails) {
157                mDetailsHelper.layout(left, slotViewTop, right, bottom);
158            } else {
159                mAlbumView.setHighlightItemPath(null);
160            }
161
162            // Set the mSlotView as a reference point to the open animation
163            mOpenCenter.setReferencePosition(0, slotViewTop);
164            mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
165            GalleryUtils.setViewPointMatrix(mMatrix,
166                    (right - left) / 2, (bottom - top) / 2, -mUserDistance);
167        }
168
169        @Override
170        protected void render(GLCanvas canvas) {
171            canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
172            canvas.multiplyMatrix(mMatrix, 0);
173            super.render(canvas);
174
175            if (mResumeEffect != null) {
176                boolean more = mResumeEffect.draw(canvas);
177                if (!more) {
178                    mResumeEffect = null;
179                    mAlbumView.setSlotFilter(null);
180                }
181                // We want to render one more time even when no more effect
182                // required. So that the animated thumbnails could be draw
183                // with declarations in super.render().
184                invalidate();
185            }
186            canvas.restore();
187        }
188    };
189
190    // This are the transitions we want:
191    //
192    // +--------+           +------------+    +-------+    +----------+
193    // | Camera |---------->| Fullscreen |--->| Album |--->| AlbumSet |
194    // |  View  | thumbnail |   Photo    | up | Page  | up |   Page   |
195    // +--------+           +------------+    +-------+    +----------+
196    //     ^                      |               |            ^  |
197    //     |                      |               |            |  |         close
198    //     +----------back--------+               +----back----+  +--back->  app
199    //
200    @Override
201    protected void onBackPressed() {
202        if (mShowDetails) {
203            hideDetails();
204        } else if (mSelectionManager.inSelectionMode()) {
205            mSelectionManager.leaveSelectionMode();
206        } else {
207            if(mLaunchedFromPhotoPage) {
208                mActivity.getTransitionStore().putIfNotPresent(
209                        PhotoPage.KEY_ALBUMPAGE_TRANSITION,
210                        PhotoPage.MSG_ALBUMPAGE_RESUMED);
211            }
212            // TODO: fix this regression
213            // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
214            if (mInCameraApp) {
215                super.onBackPressed();
216            } else {
217                onUpPressed();
218            }
219        }
220    }
221
222    private void onUpPressed() {
223        if (mInCameraApp) {
224            GalleryUtils.startGalleryActivity(mActivity);
225        } else if (mActivity.getStateManager().getStateCount() > 1) {
226            super.onBackPressed();
227        } else if (mParentMediaSetString != null) {
228            Bundle data = new Bundle(getData());
229            data.putString(AlbumSetPage.KEY_MEDIA_PATH, mParentMediaSetString);
230            mActivity.getStateManager().switchState(
231                    this, AlbumSetPage.class, data);
232        }
233    }
234
235    private void onDown(int index) {
236        mAlbumView.setPressedIndex(index);
237    }
238
239    private void onUp(boolean followedByLongPress) {
240        if (followedByLongPress) {
241            // Avoid showing press-up animations for long-press.
242            mAlbumView.setPressedIndex(-1);
243        } else {
244            mAlbumView.setPressedUp();
245        }
246    }
247
248    private void onSingleTapUp(int slotIndex) {
249        if (!mIsActive) return;
250
251        if (mSelectionManager.inSelectionMode()) {
252            MediaItem item = mAlbumDataAdapter.get(slotIndex);
253            if (item == null) return; // Item not ready yet, ignore the click
254            mSelectionManager.toggle(item.getPath());
255            mSlotView.invalidate();
256        } else {
257            // Render transition in pressed state
258            mAlbumView.setPressedIndex(slotIndex);
259            mAlbumView.setPressedUp();
260            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PICK_PHOTO, slotIndex, 0),
261                    FadeTexture.DURATION);
262        }
263    }
264
265    private void pickPhoto(int slotIndex) {
266        pickPhoto(slotIndex, false);
267    }
268
269    private void pickPhoto(int slotIndex, boolean startInFilmstrip) {
270        if (!mIsActive) return;
271
272        if (!startInFilmstrip) {
273            // Launch photos in lights out mode
274            mActivity.getGLRoot().setLightsOutMode(true);
275        }
276
277        MediaItem item = mAlbumDataAdapter.get(slotIndex);
278        if (item == null) return; // Item not ready yet, ignore the click
279        if (mGetContent) {
280            onGetContent(item);
281        } else if (mLaunchedFromPhotoPage) {
282            TransitionStore transitions = mActivity.getTransitionStore();
283            transitions.put(
284                    PhotoPage.KEY_ALBUMPAGE_TRANSITION,
285                    PhotoPage.MSG_ALBUMPAGE_PICKED);
286            transitions.put(PhotoPage.KEY_INDEX_HINT, slotIndex);
287            onBackPressed();
288        } else {
289            // Get into the PhotoPage.
290            // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
291            Bundle data = new Bundle();
292            data.putInt(PhotoPage.KEY_INDEX_HINT, slotIndex);
293            data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
294                    mSlotView.getSlotRect(slotIndex, mRootPane));
295            data.putString(PhotoPage.KEY_MEDIA_SET_PATH,
296                    mMediaSetPath.toString());
297            data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH,
298                    item.getPath().toString());
299            data.putInt(PhotoPage.KEY_ALBUMPAGE_TRANSITION,
300                    PhotoPage.MSG_ALBUMPAGE_STARTED);
301            data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP,
302                    startInFilmstrip);
303            data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, mMediaSet.isCameraRoll());
304            if (startInFilmstrip) {
305                mActivity.getStateManager().switchState(this, PhotoPage.class, data);
306            } else {
307                mActivity.getStateManager().startStateForResult(
308                            PhotoPage.class, REQUEST_PHOTO, data);
309            }
310        }
311    }
312
313    private void onGetContent(final MediaItem item) {
314        DataManager dm = mActivity.getDataManager();
315        Activity activity = mActivity;
316        if (mData.getString(Gallery.EXTRA_CROP) != null) {
317            // TODO: Handle MtpImagew
318            Uri uri = dm.getContentUri(item.getPath());
319            Intent intent = new Intent(CropImage.ACTION_CROP, uri)
320                    .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
321                    .putExtras(getData());
322            if (mData.getParcelable(MediaStore.EXTRA_OUTPUT) == null) {
323                intent.putExtra(CropImage.KEY_RETURN_DATA, true);
324            }
325            activity.startActivity(intent);
326            activity.finish();
327        } else {
328            Intent intent = new Intent(null, item.getContentUri())
329                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
330            activity.setResult(Activity.RESULT_OK, intent);
331            activity.finish();
332        }
333    }
334
335    public void onLongTap(int slotIndex) {
336        if (mGetContent) return;
337        MediaItem item = mAlbumDataAdapter.get(slotIndex);
338        if (item == null) return;
339        mSelectionManager.setAutoLeaveSelectionMode(true);
340        mSelectionManager.toggle(item.getPath());
341        mSlotView.invalidate();
342    }
343
344    @Override
345    public void doCluster(int clusterType) {
346        String basePath = mMediaSet.getPath().toString();
347        String newPath = FilterUtils.newClusterPath(basePath, clusterType);
348        Bundle data = new Bundle(getData());
349        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
350        if (mShowClusterMenu) {
351            Context context = mActivity.getAndroidContext();
352            data.putString(AlbumSetPage.KEY_SET_TITLE, mMediaSet.getName());
353            data.putString(AlbumSetPage.KEY_SET_SUBTITLE,
354                    GalleryActionBar.getClusterByTypeString(context, clusterType));
355        }
356
357        // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
358        mActivity.getStateManager().startStateForResult(
359                AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
360    }
361
362    @Override
363    protected void onCreate(Bundle data, Bundle restoreState) {
364        super.onCreate(data, restoreState);
365        mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
366        initializeViews();
367        initializeData(data);
368        mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
369        mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
370        mDetailsSource = new MyDetailsSource();
371        Context context = mActivity.getAndroidContext();
372        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
373
374        // Enable auto-select-all for mtp album
375        if (data.getBoolean(KEY_AUTO_SELECT_ALL)) {
376            mSelectionManager.selectAll();
377        }
378
379        mLaunchedFromPhotoPage =
380                mActivity.getStateManager().hasStateClass(PhotoPage.class);
381        mInCameraApp = data.getBoolean(PhotoPage.KEY_APP_BRIDGE, false);
382
383        mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
384            @Override
385            public void handleMessage(Message message) {
386                switch (message.what) {
387                    case MSG_PICK_PHOTO: {
388                        pickPhoto(message.arg1);
389                        break;
390                    }
391                    default:
392                        throw new AssertionError(message.what);
393                }
394            }
395        };
396    }
397
398    @Override
399    protected void onResume() {
400        super.onResume();
401        mIsActive = true;
402
403        mResumeEffect = mActivity.getTransitionStore().get(KEY_RESUME_ANIMATION);
404        if (mResumeEffect != null) {
405            mAlbumView.setSlotFilter(mResumeEffect);
406            mResumeEffect.setPositionProvider(mPositionProvider);
407            mResumeEffect.start();
408        }
409
410        setContentPane(mRootPane);
411
412        boolean enableHomeButton = (mActivity.getStateManager().getStateCount() > 1) |
413                mParentMediaSetString != null;
414        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
415        actionBar.setDisplayOptions(enableHomeButton, false);
416        if (!mGetContent) {
417            actionBar.enableAlbumModeMenu(GalleryActionBar.ALBUM_GRID_MODE_SELECTED, this);
418        }
419
420        // Set the reload bit here to prevent it exit this page in clearLoadingBit().
421        setLoadingBit(BIT_LOADING_RELOAD);
422        mAlbumDataAdapter.resume();
423
424        mAlbumView.resume();
425        mAlbumView.setPressedIndex(-1);
426        mActionModeHandler.resume();
427        if (!mInitialSynced) {
428            setLoadingBit(BIT_LOADING_SYNC);
429            mSyncTask = mMediaSet.requestSync(this);
430        }
431        mInCameraAndWantQuitOnPause = mInCameraApp;
432    }
433
434    @Override
435    protected void onPause() {
436        super.onPause();
437        mIsActive = false;
438
439        mAlbumView.setSlotFilter(null);
440
441        mAlbumDataAdapter.pause();
442        mAlbumView.pause();
443        DetailsHelper.pause();
444        if (!mGetContent) {
445            mActivity.getGalleryActionBar().disableAlbumModeMenu(true);
446        }
447
448        if (mSyncTask != null) {
449            mSyncTask.cancel();
450            mSyncTask = null;
451            clearLoadingBit(BIT_LOADING_SYNC);
452        }
453        mActionModeHandler.pause();
454
455        // The camera app should always launch in capture mode when
456        // resumed, so make the next resume faster by closing the grid
457        // view now
458        if (mInCameraAndWantQuitOnPause) {
459            if (mActivity.getStateManager().getTopState() == this)
460                mActivity.getStateManager().finishState(this, false);
461        }
462    }
463
464    @Override
465    protected void onDestroy() {
466        super.onDestroy();
467        if (mAlbumDataAdapter != null) {
468            mAlbumDataAdapter.setLoadingListener(null);
469        }
470    }
471
472    private void initializeViews() {
473        mSelectionManager = new SelectionManager(mActivity, false);
474        mSelectionManager.setSelectionListener(this);
475        Config.AlbumPage config = Config.AlbumPage.get(mActivity);
476        mSlotView = new SlotView(mActivity, config.slotViewSpec);
477        mAlbumView = new AlbumSlotRenderer(mActivity, mSlotView,
478                mSelectionManager, config.placeholderColor);
479        mSlotView.setSlotRenderer(mAlbumView);
480        mRootPane.addComponent(mSlotView);
481        mSlotView.setListener(new SlotView.SimpleListener() {
482            @Override
483            public void onDown(int index) {
484                AlbumPage.this.onDown(index);
485            }
486
487            @Override
488            public void onUp(boolean followedByLongPress) {
489                AlbumPage.this.onUp(followedByLongPress);
490            }
491
492            @Override
493            public void onSingleTapUp(int slotIndex) {
494                AlbumPage.this.onSingleTapUp(slotIndex);
495            }
496
497            @Override
498            public void onLongTap(int slotIndex) {
499                AlbumPage.this.onLongTap(slotIndex);
500            }
501        });
502        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
503        mActionModeHandler.setActionModeListener(new ActionModeListener() {
504            @Override
505            public boolean onActionItemClicked(MenuItem item) {
506                return onItemSelected(item);
507            }
508        });
509    }
510
511    private void initializeData(Bundle data) {
512        mMediaSetPath = Path.fromString(data.getString(KEY_MEDIA_PATH));
513        mParentMediaSetString = data.getString(KEY_PARENT_MEDIA_PATH);
514        mMediaSet = mActivity.getDataManager().getMediaSet(mMediaSetPath);
515        if (mMediaSet == null) {
516            Utils.fail("MediaSet is null. Path = %s", mMediaSetPath);
517        }
518        mSelectionManager.setSourceMediaSet(mMediaSet);
519        mAlbumDataAdapter = new AlbumDataLoader(mActivity, mMediaSet);
520        mAlbumDataAdapter.setLoadingListener(new MyLoadingListener());
521        mAlbumView.setModel(mAlbumDataAdapter);
522    }
523
524    private void showDetails() {
525        mShowDetails = true;
526        if (mDetailsHelper == null) {
527            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
528            mDetailsHelper.setCloseListener(new CloseListener() {
529                @Override
530                public void onClose() {
531                    hideDetails();
532                }
533            });
534        }
535        mDetailsHelper.show();
536    }
537
538    private void hideDetails() {
539        mShowDetails = false;
540        mDetailsHelper.hide();
541        mAlbumView.setHighlightItemPath(null);
542        mSlotView.invalidate();
543    }
544
545    @Override
546    protected boolean onCreateActionBar(Menu menu) {
547        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
548        MenuInflater inflator = getSupportMenuInflater();
549        if (mGetContent) {
550            inflator.inflate(R.menu.pickup, menu);
551            int typeBits = mData.getInt(Gallery.KEY_TYPE_BITS,
552                    DataManager.INCLUDE_IMAGE);
553            actionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
554        } else {
555            inflator.inflate(R.menu.album, menu);
556            actionBar.setTitle(mMediaSet.getName());
557
558            menu.findItem(R.id.action_slideshow)
559                    .setVisible(!(mMediaSet instanceof MtpDevice));
560
561            FilterUtils.setupMenuItems(actionBar, mMediaSetPath, true);
562
563            menu.findItem(R.id.action_group_by).setVisible(mShowClusterMenu);
564            menu.findItem(R.id.action_camera).setVisible(
565                    MediaSetUtils.isCameraSource(mMediaSetPath)
566                    && GalleryUtils.isCameraAvailable(mActivity));
567
568        }
569        actionBar.setSubtitle(null);
570        return true;
571    }
572
573    private void prepareAnimationBackToFilmstrip(int slotIndex) {
574        if (mAlbumDataAdapter == null || !mAlbumDataAdapter.isActive(slotIndex)) return;
575        MediaItem item = mAlbumDataAdapter.get(slotIndex);
576        if (item == null) return;
577        TransitionStore transitions = mActivity.getTransitionStore();
578        transitions.put(PhotoPage.KEY_INDEX_HINT, slotIndex);
579        transitions.put(PhotoPage.KEY_OPEN_ANIMATION_RECT,
580                mSlotView.getSlotRect(slotIndex, mRootPane));
581    }
582
583    private void switchToFilmstrip() {
584        if (mAlbumDataAdapter.size() < 1) return;
585        int targetPhoto = mSlotView.getVisibleStart();
586        prepareAnimationBackToFilmstrip(targetPhoto);
587        if(mLaunchedFromPhotoPage) {
588            onBackPressed();
589        } else {
590            pickPhoto(targetPhoto, true);
591        }
592    }
593
594    @Override
595    protected boolean onItemSelected(MenuItem item) {
596        switch (item.getItemId()) {
597            case android.R.id.home: {
598                onUpPressed();
599                return true;
600            }
601            case R.id.action_cancel:
602                mActivity.getStateManager().finishState(this);
603                return true;
604            case R.id.action_select:
605                mSelectionManager.setAutoLeaveSelectionMode(false);
606                mSelectionManager.enterSelectionMode();
607                return true;
608            case R.id.action_group_by: {
609                mActivity.getGalleryActionBar().showClusterDialog(this);
610                return true;
611            }
612            case R.id.action_slideshow: {
613                mInCameraAndWantQuitOnPause = false;
614                Bundle data = new Bundle();
615                data.putString(SlideshowPage.KEY_SET_PATH,
616                        mMediaSetPath.toString());
617                data.putBoolean(SlideshowPage.KEY_REPEAT, true);
618                mActivity.getStateManager().startStateForResult(
619                        SlideshowPage.class, REQUEST_SLIDESHOW, data);
620                return true;
621            }
622            case R.id.action_details: {
623                if (mShowDetails) {
624                    hideDetails();
625                } else {
626                    showDetails();
627                }
628                return true;
629            }
630            case R.id.action_camera: {
631                GalleryUtils.startCameraActivity(mActivity);
632                return true;
633            }
634            default:
635                return false;
636        }
637    }
638
639    @Override
640    protected void onStateResult(int request, int result, Intent data) {
641        switch (request) {
642            case REQUEST_SLIDESHOW: {
643                // data could be null, if there is no images in the album
644                if (data == null) return;
645                mFocusIndex = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
646                mSlotView.setCenterIndex(mFocusIndex);
647                break;
648            }
649            case REQUEST_PHOTO: {
650                if (data == null) return;
651                mFocusIndex = data.getIntExtra(PhotoPage.KEY_RETURN_INDEX_HINT, 0);
652                mSlotView.makeSlotVisible(mFocusIndex);
653                break;
654            }
655            case REQUEST_DO_ANIMATION: {
656                mSlotView.startRisingAnimation();
657                break;
658            }
659        }
660    }
661
662    @Override
663    public void onSelectionModeChange(int mode) {
664        switch (mode) {
665            case SelectionManager.ENTER_SELECTION_MODE: {
666                mActionModeHandler.startActionMode();
667                if (mHapticsEnabled) mVibrator.vibrate(100);
668                break;
669            }
670            case SelectionManager.LEAVE_SELECTION_MODE: {
671                mActionModeHandler.finishActionMode();
672                mRootPane.invalidate();
673                break;
674            }
675            case SelectionManager.SELECT_ALL_MODE: {
676                mActionModeHandler.updateSupportedOperation();
677                mRootPane.invalidate();
678                break;
679            }
680        }
681    }
682
683    @Override
684    public void onSelectionChange(Path path, boolean selected) {
685        int count = mSelectionManager.getSelectedCount();
686        String format = mActivity.getResources().getQuantityString(
687                R.plurals.number_of_items_selected, count);
688        mActionModeHandler.setTitle(String.format(format, count));
689        mActionModeHandler.updateSupportedOperation(path, selected);
690    }
691
692    @Override
693    public void onSyncDone(final MediaSet mediaSet, final int resultCode) {
694        Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result="
695                + resultCode);
696        ((Activity) mActivity).runOnUiThread(new Runnable() {
697            @Override
698            public void run() {
699                GLRoot root = mActivity.getGLRoot();
700                root.lockRenderThread();
701                try {
702                    if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) {
703                        mInitialSynced = true;
704                    }
705                    clearLoadingBit(BIT_LOADING_SYNC);
706                    if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive
707                            && (mAlbumDataAdapter.size() == 0)) {
708                        // show error toast only if the album is empty
709                        Toast.makeText(mActivity, R.string.sync_album_error,
710                                Toast.LENGTH_LONG).show();
711                    }
712                } finally {
713                    root.unlockRenderThread();
714                }
715            }
716        });
717    }
718
719    private void setLoadingBit(int loadTaskBit) {
720        mLoadingBits |= loadTaskBit;
721    }
722
723    private void clearLoadingBit(int loadTaskBit) {
724        mLoadingBits &= ~loadTaskBit;
725        if (mLoadingBits == 0 && mIsActive) {
726            if (mAlbumDataAdapter.size() == 0) {
727                Intent result = new Intent();
728                result.putExtra(KEY_EMPTY_ALBUM, true);
729                setStateResult(Activity.RESULT_OK, result);
730                mActivity.getStateManager().finishState(this);
731            }
732        }
733    }
734
735    private class MyLoadingListener implements LoadingListener {
736        @Override
737        public void onLoadingStarted() {
738            setLoadingBit(BIT_LOADING_RELOAD);
739        }
740
741        @Override
742        public void onLoadingFinished() {
743            clearLoadingBit(BIT_LOADING_RELOAD);
744        }
745    }
746
747    private class MyDetailsSource implements DetailsHelper.DetailsSource {
748        private int mIndex;
749
750        @Override
751        public int size() {
752            return mAlbumDataAdapter.size();
753        }
754
755        @Override
756        public int setIndex() {
757            Path id = mSelectionManager.getSelected(false).get(0);
758            mIndex = mAlbumDataAdapter.findItem(id);
759            return mIndex;
760        }
761
762        @Override
763        public MediaDetails getDetails() {
764            // this relies on setIndex() being called beforehand
765            MediaObject item = mAlbumDataAdapter.get(mIndex);
766            if (item != null) {
767                mAlbumView.setHighlightItemPath(item.getPath());
768                return item.getDetails();
769            } else {
770                return null;
771            }
772        }
773    }
774
775    @Override
776    public void onAlbumModeSelected(int mode) {
777        if (mode == GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED) {
778            switchToFilmstrip();
779        }
780    }
781}
782