AlbumPage.java revision 2daced6baacd59850e7c64ea02f2479530654ed8
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        if (mSelectionManager.inSelectionMode()) {
440            mSelectionManager.leaveSelectionMode();
441        }
442        mAlbumView.setSlotFilter(null);
443
444        mAlbumDataAdapter.pause();
445        mAlbumView.pause();
446        DetailsHelper.pause();
447        if (!mGetContent) {
448            mActivity.getGalleryActionBar().disableAlbumModeMenu(true);
449        }
450
451        if (mSyncTask != null) {
452            mSyncTask.cancel();
453            mSyncTask = null;
454            clearLoadingBit(BIT_LOADING_SYNC);
455        }
456        mActionModeHandler.pause();
457
458        // The camera app should always launch in capture mode when
459        // resumed, so make the next resume faster by closing the grid
460        // view now
461        if (mInCameraAndWantQuitOnPause) {
462            if (mActivity.getStateManager().getTopState() == this)
463                mActivity.getStateManager().finishState(this, false);
464        }
465    }
466
467    @Override
468    protected void onDestroy() {
469        super.onDestroy();
470        if (mAlbumDataAdapter != null) {
471            mAlbumDataAdapter.setLoadingListener(null);
472        }
473    }
474
475    private void initializeViews() {
476        mSelectionManager = new SelectionManager(mActivity, false);
477        mSelectionManager.setSelectionListener(this);
478        Config.AlbumPage config = Config.AlbumPage.get(mActivity);
479        mSlotView = new SlotView(mActivity, config.slotViewSpec);
480        mAlbumView = new AlbumSlotRenderer(mActivity, mSlotView,
481                mSelectionManager, config.placeholderColor);
482        mSlotView.setSlotRenderer(mAlbumView);
483        mRootPane.addComponent(mSlotView);
484        mSlotView.setListener(new SlotView.SimpleListener() {
485            @Override
486            public void onDown(int index) {
487                AlbumPage.this.onDown(index);
488            }
489
490            @Override
491            public void onUp(boolean followedByLongPress) {
492                AlbumPage.this.onUp(followedByLongPress);
493            }
494
495            @Override
496            public void onSingleTapUp(int slotIndex) {
497                AlbumPage.this.onSingleTapUp(slotIndex);
498            }
499
500            @Override
501            public void onLongTap(int slotIndex) {
502                AlbumPage.this.onLongTap(slotIndex);
503            }
504        });
505        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
506        mActionModeHandler.setActionModeListener(new ActionModeListener() {
507            @Override
508            public boolean onActionItemClicked(MenuItem item) {
509                return onItemSelected(item);
510            }
511        });
512    }
513
514    private void initializeData(Bundle data) {
515        mMediaSetPath = Path.fromString(data.getString(KEY_MEDIA_PATH));
516        mParentMediaSetString = data.getString(KEY_PARENT_MEDIA_PATH);
517        mMediaSet = mActivity.getDataManager().getMediaSet(mMediaSetPath);
518        if (mMediaSet == null) {
519            Utils.fail("MediaSet is null. Path = %s", mMediaSetPath);
520        }
521        mSelectionManager.setSourceMediaSet(mMediaSet);
522        mAlbumDataAdapter = new AlbumDataLoader(mActivity, mMediaSet);
523        mAlbumDataAdapter.setLoadingListener(new MyLoadingListener());
524        mAlbumView.setModel(mAlbumDataAdapter);
525    }
526
527    private void showDetails() {
528        mShowDetails = true;
529        if (mDetailsHelper == null) {
530            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
531            mDetailsHelper.setCloseListener(new CloseListener() {
532                @Override
533                public void onClose() {
534                    hideDetails();
535                }
536            });
537        }
538        mDetailsHelper.show();
539    }
540
541    private void hideDetails() {
542        mShowDetails = false;
543        mDetailsHelper.hide();
544        mAlbumView.setHighlightItemPath(null);
545        mSlotView.invalidate();
546    }
547
548    @Override
549    protected boolean onCreateActionBar(Menu menu) {
550        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
551        MenuInflater inflator = getSupportMenuInflater();
552        if (mGetContent) {
553            inflator.inflate(R.menu.pickup, menu);
554            int typeBits = mData.getInt(Gallery.KEY_TYPE_BITS,
555                    DataManager.INCLUDE_IMAGE);
556            actionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
557        } else {
558            inflator.inflate(R.menu.album, menu);
559            actionBar.setTitle(mMediaSet.getName());
560
561            menu.findItem(R.id.action_slideshow)
562                    .setVisible(!(mMediaSet instanceof MtpDevice));
563
564            FilterUtils.setupMenuItems(actionBar, mMediaSetPath, true);
565
566            menu.findItem(R.id.action_group_by).setVisible(mShowClusterMenu);
567            menu.findItem(R.id.action_camera).setVisible(
568                    MediaSetUtils.isCameraSource(mMediaSetPath)
569                    && GalleryUtils.isCameraAvailable(mActivity));
570
571        }
572        actionBar.setSubtitle(null);
573        return true;
574    }
575
576    private void prepareAnimationBackToFilmstrip(int slotIndex) {
577        if (mAlbumDataAdapter == null || !mAlbumDataAdapter.isActive(slotIndex)) return;
578        MediaItem item = mAlbumDataAdapter.get(slotIndex);
579        if (item == null) return;
580        TransitionStore transitions = mActivity.getTransitionStore();
581        transitions.put(PhotoPage.KEY_INDEX_HINT, slotIndex);
582        transitions.put(PhotoPage.KEY_OPEN_ANIMATION_RECT,
583                mSlotView.getSlotRect(slotIndex, mRootPane));
584    }
585
586    private void switchToFilmstrip() {
587        if (mAlbumDataAdapter.size() < 1) return;
588        int targetPhoto = mSlotView.getVisibleStart();
589        prepareAnimationBackToFilmstrip(targetPhoto);
590        if(mLaunchedFromPhotoPage) {
591            onBackPressed();
592        } else {
593            pickPhoto(targetPhoto, true);
594        }
595    }
596
597    @Override
598    protected boolean onItemSelected(MenuItem item) {
599        switch (item.getItemId()) {
600            case android.R.id.home: {
601                onUpPressed();
602                return true;
603            }
604            case R.id.action_cancel:
605                mActivity.getStateManager().finishState(this);
606                return true;
607            case R.id.action_select:
608                mSelectionManager.setAutoLeaveSelectionMode(false);
609                mSelectionManager.enterSelectionMode();
610                return true;
611            case R.id.action_group_by: {
612                mActivity.getGalleryActionBar().showClusterDialog(this);
613                return true;
614            }
615            case R.id.action_slideshow: {
616                mInCameraAndWantQuitOnPause = false;
617                Bundle data = new Bundle();
618                data.putString(SlideshowPage.KEY_SET_PATH,
619                        mMediaSetPath.toString());
620                data.putBoolean(SlideshowPage.KEY_REPEAT, true);
621                mActivity.getStateManager().startStateForResult(
622                        SlideshowPage.class, REQUEST_SLIDESHOW, data);
623                return true;
624            }
625            case R.id.action_details: {
626                if (mShowDetails) {
627                    hideDetails();
628                } else {
629                    showDetails();
630                }
631                return true;
632            }
633            case R.id.action_camera: {
634                GalleryUtils.startCameraActivity(mActivity);
635                return true;
636            }
637            default:
638                return false;
639        }
640    }
641
642    @Override
643    protected void onStateResult(int request, int result, Intent data) {
644        switch (request) {
645            case REQUEST_SLIDESHOW: {
646                // data could be null, if there is no images in the album
647                if (data == null) return;
648                mFocusIndex = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
649                mSlotView.setCenterIndex(mFocusIndex);
650                break;
651            }
652            case REQUEST_PHOTO: {
653                if (data == null) return;
654                mFocusIndex = data.getIntExtra(PhotoPage.KEY_RETURN_INDEX_HINT, 0);
655                mSlotView.makeSlotVisible(mFocusIndex);
656                break;
657            }
658            case REQUEST_DO_ANIMATION: {
659                mSlotView.startRisingAnimation();
660                break;
661            }
662        }
663    }
664
665    @Override
666    public void onSelectionModeChange(int mode) {
667        switch (mode) {
668            case SelectionManager.ENTER_SELECTION_MODE: {
669                mActionModeHandler.startActionMode();
670                if (mHapticsEnabled) mVibrator.vibrate(100);
671                break;
672            }
673            case SelectionManager.LEAVE_SELECTION_MODE: {
674                mActionModeHandler.finishActionMode();
675                mRootPane.invalidate();
676                break;
677            }
678            case SelectionManager.SELECT_ALL_MODE: {
679                mActionModeHandler.updateSupportedOperation();
680                mRootPane.invalidate();
681                break;
682            }
683        }
684    }
685
686    @Override
687    public void onSelectionChange(Path path, boolean selected) {
688        int count = mSelectionManager.getSelectedCount();
689        String format = mActivity.getResources().getQuantityString(
690                R.plurals.number_of_items_selected, count);
691        mActionModeHandler.setTitle(String.format(format, count));
692        mActionModeHandler.updateSupportedOperation(path, selected);
693    }
694
695    @Override
696    public void onSyncDone(final MediaSet mediaSet, final int resultCode) {
697        Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result="
698                + resultCode);
699        ((Activity) mActivity).runOnUiThread(new Runnable() {
700            @Override
701            public void run() {
702                GLRoot root = mActivity.getGLRoot();
703                root.lockRenderThread();
704                try {
705                    if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) {
706                        mInitialSynced = true;
707                    }
708                    clearLoadingBit(BIT_LOADING_SYNC);
709                    if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive
710                            && (mAlbumDataAdapter.size() == 0)) {
711                        // show error toast only if the album is empty
712                        Toast.makeText(mActivity, R.string.sync_album_error,
713                                Toast.LENGTH_LONG).show();
714                    }
715                } finally {
716                    root.unlockRenderThread();
717                }
718            }
719        });
720    }
721
722    private void setLoadingBit(int loadTaskBit) {
723        mLoadingBits |= loadTaskBit;
724    }
725
726    private void clearLoadingBit(int loadTaskBit) {
727        mLoadingBits &= ~loadTaskBit;
728        if (mLoadingBits == 0 && mIsActive) {
729            if (mAlbumDataAdapter.size() == 0) {
730                Intent result = new Intent();
731                result.putExtra(KEY_EMPTY_ALBUM, true);
732                setStateResult(Activity.RESULT_OK, result);
733                mActivity.getStateManager().finishState(this);
734            }
735        }
736    }
737
738    private class MyLoadingListener implements LoadingListener {
739        @Override
740        public void onLoadingStarted() {
741            setLoadingBit(BIT_LOADING_RELOAD);
742        }
743
744        @Override
745        public void onLoadingFinished() {
746            clearLoadingBit(BIT_LOADING_RELOAD);
747        }
748    }
749
750    private class MyDetailsSource implements DetailsHelper.DetailsSource {
751        private int mIndex;
752
753        @Override
754        public int size() {
755            return mAlbumDataAdapter.size();
756        }
757
758        @Override
759        public int setIndex() {
760            Path id = mSelectionManager.getSelected(false).get(0);
761            mIndex = mAlbumDataAdapter.findItem(id);
762            return mIndex;
763        }
764
765        @Override
766        public MediaDetails getDetails() {
767            // this relies on setIndex() being called beforehand
768            MediaObject item = mAlbumDataAdapter.get(mIndex);
769            if (item != null) {
770                mAlbumView.setHighlightItemPath(item.getPath());
771                return item.getDetails();
772            } else {
773                return null;
774            }
775        }
776    }
777
778    @Override
779    public void onAlbumModeSelected(int mode) {
780        if (mode == GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED) {
781            switchToFilmstrip();
782        }
783    }
784}
785