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