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