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