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.os.Bundle;
24import android.os.Vibrator;
25import android.provider.MediaStore;
26import android.view.ActionMode;
27import android.view.Menu;
28import android.view.MenuInflater;
29import android.view.MenuItem;
30import android.widget.Toast;
31
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.MediaObject;
37import com.android.gallery3d.data.MediaSet;
38import com.android.gallery3d.data.Path;
39import com.android.gallery3d.picasasource.PicasaSource;
40import com.android.gallery3d.settings.GallerySettings;
41import com.android.gallery3d.ui.ActionModeHandler;
42import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
43import com.android.gallery3d.ui.AlbumSetView;
44import com.android.gallery3d.ui.DetailsHelper;
45import com.android.gallery3d.ui.DetailsHelper.CloseListener;
46import com.android.gallery3d.ui.GLCanvas;
47import com.android.gallery3d.ui.GLView;
48import com.android.gallery3d.ui.GridDrawer;
49import com.android.gallery3d.ui.HighlightDrawer;
50import com.android.gallery3d.ui.PositionProvider;
51import com.android.gallery3d.ui.PositionRepository;
52import com.android.gallery3d.ui.PositionRepository.Position;
53import com.android.gallery3d.ui.SelectionManager;
54import com.android.gallery3d.ui.SlotView;
55import com.android.gallery3d.ui.StaticBackground;
56import com.android.gallery3d.util.Future;
57import com.android.gallery3d.util.GalleryUtils;
58
59public class AlbumSetPage extends ActivityState implements
60        SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner,
61        EyePosition.EyePositionListener, MediaSet.SyncListener {
62    @SuppressWarnings("unused")
63    private static final String TAG = "AlbumSetPage";
64
65    public static final String KEY_MEDIA_PATH = "media-path";
66    public static final String KEY_SET_TITLE = "set-title";
67    public static final String KEY_SET_SUBTITLE = "set-subtitle";
68    public static final String KEY_SELECTED_CLUSTER_TYPE = "selected-cluster";
69
70    private static final int DATA_CACHE_SIZE = 256;
71    private static final int REQUEST_DO_ANIMATION = 1;
72
73    private static final int BIT_LOADING_RELOAD = 1;
74    private static final int BIT_LOADING_SYNC = 2;
75
76    private boolean mIsActive = false;
77    private StaticBackground mStaticBackground;
78    private AlbumSetView mAlbumSetView;
79
80    private MediaSet mMediaSet;
81    private String mTitle;
82    private String mSubtitle;
83    private boolean mShowClusterMenu;
84    private int mSelectedAction;
85    private Vibrator mVibrator;
86
87    protected SelectionManager mSelectionManager;
88    private AlbumSetDataAdapter mAlbumSetDataAdapter;
89    private GridDrawer mGridDrawer;
90    private HighlightDrawer mHighlightDrawer;
91
92    private boolean mGetContent;
93    private boolean mGetAlbum;
94    private ActionMode mActionMode;
95    private ActionModeHandler mActionModeHandler;
96    private DetailsHelper mDetailsHelper;
97    private MyDetailsSource mDetailsSource;
98    private boolean mShowDetails;
99    private EyePosition mEyePosition;
100
101    // The eyes' position of the user, the origin is at the center of the
102    // device and the unit is in pixels.
103    private float mX;
104    private float mY;
105    private float mZ;
106
107    private Future<Integer> mSyncTask = null;
108
109    private int mLoadingBits = 0;
110    private boolean mInitialSynced = false;
111
112    private final GLView mRootPane = new GLView() {
113        private final float mMatrix[] = new float[16];
114
115        @Override
116        protected void onLayout(
117                boolean changed, int left, int top, int right, int bottom) {
118            mStaticBackground.layout(0, 0, right - left, bottom - top);
119            mEyePosition.resetPosition();
120
121            int slotViewTop = GalleryActionBar.getHeight((Activity) mActivity);
122            int slotViewBottom = bottom - top;
123            int slotViewRight = right - left;
124
125            if (mShowDetails) {
126                mDetailsHelper.layout(left, slotViewTop, right, bottom);
127            } else {
128                mAlbumSetView.setSelectionDrawer(mGridDrawer);
129            }
130
131            mAlbumSetView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
132            PositionRepository.getInstance(mActivity).setOffset(
133                    0, slotViewTop);
134        }
135
136        @Override
137        protected void render(GLCanvas canvas) {
138            canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
139            GalleryUtils.setViewPointMatrix(mMatrix,
140                    getWidth() / 2 + mX, getHeight() / 2 + mY, mZ);
141            canvas.multiplyMatrix(mMatrix, 0);
142            super.render(canvas);
143            canvas.restore();
144        }
145    };
146
147    @Override
148    public void onEyePositionChanged(float x, float y, float z) {
149        mRootPane.lockRendering();
150        mX = x;
151        mY = y;
152        mZ = z;
153        mRootPane.unlockRendering();
154        mRootPane.invalidate();
155    }
156
157    @Override
158    public void onBackPressed() {
159        if (mShowDetails) {
160            hideDetails();
161        } else if (mSelectionManager.inSelectionMode()) {
162            mSelectionManager.leaveSelectionMode();
163        } else {
164            mAlbumSetView.savePositions(
165                    PositionRepository.getInstance(mActivity));
166            super.onBackPressed();
167        }
168    }
169
170    private void savePositions(int slotIndex, int center[]) {
171        Rect offset = new Rect();
172        mRootPane.getBoundsOf(mAlbumSetView, offset);
173        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
174        Rect r = mAlbumSetView.getSlotRect(slotIndex);
175        int scrollX = mAlbumSetView.getScrollX();
176        int scrollY = mAlbumSetView.getScrollY();
177        center[0] = offset.left + (r.left + r.right) / 2 - scrollX;
178        center[1] = offset.top + (r.top + r.bottom) / 2 - scrollY;
179    }
180
181    public void onSingleTapUp(int slotIndex) {
182        MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
183        if (targetSet == null) return; // Content is dirty, we shall reload soon
184
185        if (mShowDetails) {
186            Path path = targetSet.getPath();
187            mHighlightDrawer.setHighlightItem(path);
188            mDetailsHelper.reloadDetails(slotIndex);
189        } else if (!mSelectionManager.inSelectionMode()) {
190            Bundle data = new Bundle(getData());
191            String mediaPath = targetSet.getPath().toString();
192            int[] center = new int[2];
193            savePositions(slotIndex, center);
194            data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
195            if (mGetAlbum && targetSet.isLeafAlbum()) {
196                Activity activity = (Activity) mActivity;
197                Intent result = new Intent()
198                        .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString());
199                activity.setResult(Activity.RESULT_OK, result);
200                activity.finish();
201            } else if (targetSet.getSubMediaSetCount() > 0) {
202                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
203                mActivity.getStateManager().startStateForResult(
204                        AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
205            } else {
206                if (!mGetContent && (targetSet.getSupportedOperations()
207                        & MediaObject.SUPPORT_IMPORT) != 0) {
208                    data.putBoolean(AlbumPage.KEY_AUTO_SELECT_ALL, true);
209                }
210                data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
211                boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
212                // We only show cluster menu in the first AlbumPage in stack
213                data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum);
214                mActivity.getStateManager().startStateForResult(
215                        AlbumPage.class, REQUEST_DO_ANIMATION, data);
216            }
217        } else {
218            mSelectionManager.toggle(targetSet.getPath());
219            mAlbumSetView.invalidate();
220        }
221    }
222
223    private void onDown(int index) {
224        MediaSet set = mAlbumSetDataAdapter.getMediaSet(index);
225        Path path = (set == null) ? null : set.getPath();
226        mSelectionManager.setPressedPath(path);
227        mAlbumSetView.invalidate();
228    }
229
230    private void onUp() {
231        mSelectionManager.setPressedPath(null);
232        mAlbumSetView.invalidate();
233    }
234
235    public void onLongTap(int slotIndex) {
236        if (mGetContent || mGetAlbum) return;
237        if (mShowDetails) {
238            onSingleTapUp(slotIndex);
239        } else {
240            MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex);
241            if (set == null) return;
242            mSelectionManager.setAutoLeaveSelectionMode(true);
243            mSelectionManager.toggle(set.getPath());
244            mDetailsSource.findIndex(slotIndex);
245            mAlbumSetView.invalidate();
246        }
247    }
248
249    public void doCluster(int clusterType) {
250        String basePath = mMediaSet.getPath().toString();
251        String newPath = FilterUtils.switchClusterPath(basePath, clusterType);
252        Bundle data = new Bundle(getData());
253        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
254        data.putInt(KEY_SELECTED_CLUSTER_TYPE, clusterType);
255        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
256        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
257    }
258
259    public void doFilter(int filterType) {
260        String basePath = mMediaSet.getPath().toString();
261        String newPath = FilterUtils.switchFilterPath(basePath, filterType);
262        Bundle data = new Bundle(getData());
263        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
264        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
265        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
266    }
267
268    public void onOperationComplete() {
269        mAlbumSetView.invalidate();
270        // TODO: enable animation
271    }
272
273    @Override
274    public void onCreate(Bundle data, Bundle restoreState) {
275        initializeViews();
276        initializeData(data);
277        Context context = mActivity.getAndroidContext();
278        mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
279        mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false);
280        mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE);
281        mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
282        mEyePosition = new EyePosition(context, this);
283        mDetailsSource = new MyDetailsSource();
284        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
285        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
286        if (actionBar != null) {
287            mSelectedAction = data.getInt(
288                    AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE, FilterUtils.CLUSTER_BY_ALBUM);
289        }
290        startTransition();
291    }
292
293    private void clearLoadingBit(int loadingBit) {
294        mLoadingBits &= ~loadingBit;
295        if (mLoadingBits == 0) {
296            GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
297
298            // Only show toast when there's no album and we are going to finish
299            // the page. Toast is redundant if we are going to stay on this page.
300            if ((mAlbumSetDataAdapter.size() == 0)) {
301                Toast.makeText((Context) mActivity,
302                        R.string.empty_album, Toast.LENGTH_LONG).show();
303                if (mActivity.getStateManager().getStateCount() > 1) {
304                    mActivity.getStateManager().finishState(this);
305                }
306            }
307        }
308    }
309
310    private void setLoadingBit(int loadingBit) {
311        if (mLoadingBits == 0) {
312            GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
313        }
314        mLoadingBits |= loadingBit;
315    }
316
317    @Override
318    public void onPause() {
319        super.onPause();
320        mIsActive = false;
321        mActionModeHandler.pause();
322        mAlbumSetDataAdapter.pause();
323        mAlbumSetView.pause();
324        mEyePosition.pause();
325        DetailsHelper.pause();
326        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
327        if (actionBar != null) actionBar.hideClusterMenu();
328        if (mSyncTask != null) {
329            mSyncTask.cancel();
330            mSyncTask = null;
331            clearLoadingBit(BIT_LOADING_SYNC);
332        }
333    }
334
335    @Override
336    public void onResume() {
337        super.onResume();
338        mIsActive = true;
339        setContentPane(mRootPane);
340
341        // Set the reload bit here to prevent it exit this page in clearLoadingBit().
342        setLoadingBit(BIT_LOADING_RELOAD);
343        mAlbumSetDataAdapter.resume();
344
345        mAlbumSetView.resume();
346        mEyePosition.resume();
347        mActionModeHandler.resume();
348        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
349        if (mShowClusterMenu && actionBar != null) {
350            actionBar.showClusterMenu(mSelectedAction, this);
351        }
352        if (!mInitialSynced) {
353            setLoadingBit(BIT_LOADING_SYNC);
354            mSyncTask = mMediaSet.requestSync(AlbumSetPage.this);
355        }
356    }
357
358    private void initializeData(Bundle data) {
359        String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
360        mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
361        mSelectionManager.setSourceMediaSet(mMediaSet);
362        mAlbumSetDataAdapter = new AlbumSetDataAdapter(
363                mActivity, mMediaSet, DATA_CACHE_SIZE);
364        mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
365        mAlbumSetView.setModel(mAlbumSetDataAdapter);
366    }
367
368    private void initializeViews() {
369        mSelectionManager = new SelectionManager(mActivity, true);
370        mSelectionManager.setSelectionListener(this);
371        mStaticBackground = new StaticBackground(mActivity.getAndroidContext());
372        mRootPane.addComponent(mStaticBackground);
373
374        mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager);
375        Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity);
376        mAlbumSetView = new AlbumSetView(mActivity, mGridDrawer,
377                config.slotViewSpec, config.labelSpec);
378        mAlbumSetView.setListener(new SlotView.SimpleListener() {
379            @Override
380            public void onDown(int index) {
381                AlbumSetPage.this.onDown(index);
382            }
383
384            @Override
385            public void onUp() {
386                AlbumSetPage.this.onUp();
387            }
388
389            @Override
390            public void onSingleTapUp(int slotIndex) {
391                AlbumSetPage.this.onSingleTapUp(slotIndex);
392            }
393
394            @Override
395            public void onLongTap(int slotIndex) {
396                AlbumSetPage.this.onLongTap(slotIndex);
397            }
398        });
399
400        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
401        mActionModeHandler.setActionModeListener(new ActionModeListener() {
402            public boolean onActionItemClicked(MenuItem item) {
403                return onItemSelected(item);
404            }
405        });
406        mRootPane.addComponent(mAlbumSetView);
407
408        mStaticBackground.setImage(R.drawable.background,
409                R.drawable.background_portrait);
410    }
411
412    @Override
413    protected boolean onCreateActionBar(Menu menu) {
414        Activity activity = (Activity) mActivity;
415        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
416        MenuInflater inflater = activity.getMenuInflater();
417
418        final boolean inAlbum = mActivity.getStateManager().hasStateClass(
419                AlbumPage.class);
420
421        if (mGetContent) {
422            inflater.inflate(R.menu.pickup, menu);
423            int typeBits = mData.getInt(
424                    Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
425            int id = R.string.select_image;
426            if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) {
427                id = (typeBits & DataManager.INCLUDE_IMAGE) == 0
428                        ? R.string.select_video
429                        : R.string.select_item;
430            }
431            actionBar.setTitle(id);
432        } else  if (mGetAlbum) {
433            inflater.inflate(R.menu.pickup, menu);
434            actionBar.setTitle(R.string.select_album);
435        } else {
436            mShowClusterMenu = !inAlbum;
437            inflater.inflate(R.menu.albumset, menu);
438            MenuItem selectItem = menu.findItem(R.id.action_select);
439
440            if (selectItem != null) {
441                boolean selectAlbums = !inAlbum &&
442                        actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM;
443                if (selectAlbums) {
444                    selectItem.setTitle(R.string.select_album);
445                } else {
446                    selectItem.setTitle(R.string.select_group);
447                }
448            }
449
450            FilterUtils.setupMenuItems(actionBar, mMediaSet.getPath(), false);
451            MenuItem switchCamera = menu.findItem(R.id.action_camera);
452            if (switchCamera != null) {
453                switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity));
454            }
455
456            actionBar.setTitle(mTitle);
457            actionBar.setSubtitle(mSubtitle);
458        }
459        return true;
460    }
461
462    @Override
463    protected boolean onItemSelected(MenuItem item) {
464        Activity activity = (Activity) mActivity;
465        switch (item.getItemId()) {
466            case R.id.action_cancel:
467                activity.setResult(Activity.RESULT_CANCELED);
468                activity.finish();
469                return true;
470            case R.id.action_select:
471                mSelectionManager.setAutoLeaveSelectionMode(false);
472                mSelectionManager.enterSelectionMode();
473                return true;
474            case R.id.action_details:
475                if (mAlbumSetDataAdapter.size() != 0) {
476                    if (mShowDetails) {
477                        hideDetails();
478                    } else {
479                        showDetails();
480                    }
481                } else {
482                    Toast.makeText(activity,
483                            activity.getText(R.string.no_albums_alert),
484                            Toast.LENGTH_SHORT).show();
485                }
486                return true;
487            case R.id.action_camera: {
488                Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
489                        .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
490                        | Intent.FLAG_ACTIVITY_NEW_TASK);
491                activity.startActivity(intent);
492                return true;
493            }
494            case R.id.action_manage_offline: {
495                Bundle data = new Bundle();
496                String mediaPath = mActivity.getDataManager().getTopSetPath(
497                    DataManager.INCLUDE_ALL);
498                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
499                mActivity.getStateManager().startState(ManageCachePage.class, data);
500                return true;
501            }
502            case R.id.action_sync_picasa_albums: {
503                PicasaSource.requestSync(activity);
504                return true;
505            }
506            case R.id.action_settings: {
507                activity.startActivity(new Intent(activity, GallerySettings.class));
508                return true;
509            }
510            default:
511                return false;
512        }
513    }
514
515    @Override
516    protected void onStateResult(int requestCode, int resultCode, Intent data) {
517        switch (requestCode) {
518            case REQUEST_DO_ANIMATION: {
519                startTransition();
520            }
521        }
522    }
523
524    private void startTransition() {
525        final PositionRepository repository =
526                PositionRepository.getInstance(mActivity);
527        mAlbumSetView.startTransition(new PositionProvider() {
528            private final Position mTempPosition = new Position();
529            public Position getPosition(long identity, Position target) {
530                Position p = repository.get(identity);
531                if (p == null) {
532                    p = mTempPosition;
533                    p.set(target.x, target.y, 128, target.theta, 1);
534                }
535                return p;
536            }
537        });
538    }
539
540    private String getSelectedString() {
541        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
542        int count = mSelectionManager.getSelectedCount();
543        int action = actionBar.getClusterTypeAction();
544        int string = action == FilterUtils.CLUSTER_BY_ALBUM
545                ? R.plurals.number_of_albums_selected
546                : R.plurals.number_of_groups_selected;
547        String format = mActivity.getResources().getQuantityString(string, count);
548        return String.format(format, count);
549    }
550
551    public void onSelectionModeChange(int mode) {
552
553        switch (mode) {
554            case SelectionManager.ENTER_SELECTION_MODE: {
555                mActivity.getGalleryActionBar().hideClusterMenu();
556                mActionMode = mActionModeHandler.startActionMode();
557                mVibrator.vibrate(100);
558                break;
559            }
560            case SelectionManager.LEAVE_SELECTION_MODE: {
561                mActionMode.finish();
562                if (mShowClusterMenu) {
563                    mActivity.getGalleryActionBar().showClusterMenu(mSelectedAction, this);
564                }
565                mRootPane.invalidate();
566                break;
567            }
568            case SelectionManager.SELECT_ALL_MODE: {
569                mActionModeHandler.setTitle(getSelectedString());
570                mRootPane.invalidate();
571                break;
572            }
573        }
574    }
575
576    public void onSelectionChange(Path path, boolean selected) {
577        Utils.assertTrue(mActionMode != null);
578        mActionModeHandler.setTitle(getSelectedString());
579        mActionModeHandler.updateSupportedOperation(path, selected);
580    }
581
582    private void hideDetails() {
583        mShowDetails = false;
584        mDetailsHelper.hide();
585        mAlbumSetView.setSelectionDrawer(mGridDrawer);
586        mAlbumSetView.invalidate();
587    }
588
589    private void showDetails() {
590        mShowDetails = true;
591        if (mDetailsHelper == null) {
592            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(),
593                    mSelectionManager);
594            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
595            mDetailsHelper.setCloseListener(new CloseListener() {
596                public void onClose() {
597                    hideDetails();
598                }
599            });
600        }
601        mAlbumSetView.setSelectionDrawer(mHighlightDrawer);
602        mDetailsHelper.show();
603    }
604
605    @Override
606    public void onSyncDone(final MediaSet mediaSet, final int resultCode) {
607        if (resultCode == MediaSet.SYNC_RESULT_ERROR) {
608            Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result="
609                    + resultCode);
610        }
611        ((Activity) mActivity).runOnUiThread(new Runnable() {
612            @Override
613            public void run() {
614                if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) {
615                    mInitialSynced = true;
616                }
617                if (!mIsActive) return;
618                clearLoadingBit(BIT_LOADING_SYNC);
619                if (resultCode == MediaSet.SYNC_RESULT_ERROR) {
620                    Toast.makeText((Context) mActivity, R.string.sync_album_set_error,
621                            Toast.LENGTH_LONG).show();
622                }
623            }
624        });
625    }
626
627    private class MyLoadingListener implements LoadingListener {
628        public void onLoadingStarted() {
629            setLoadingBit(BIT_LOADING_RELOAD);
630        }
631
632        public void onLoadingFinished() {
633            if (!mIsActive) return;
634            clearLoadingBit(BIT_LOADING_RELOAD);
635        }
636    }
637
638    private class MyDetailsSource implements DetailsHelper.DetailsSource {
639        private int mIndex;
640        public int size() {
641            return mAlbumSetDataAdapter.size();
642        }
643
644        public int getIndex() {
645            return mIndex;
646        }
647
648        // If requested index is out of active window, suggest a valid index.
649        // If there is no valid index available, return -1.
650        public int findIndex(int indexHint) {
651            if (mAlbumSetDataAdapter.isActive(indexHint)) {
652                mIndex = indexHint;
653            } else {
654                mIndex = mAlbumSetDataAdapter.getActiveStart();
655                if (!mAlbumSetDataAdapter.isActive(mIndex)) {
656                    return -1;
657                }
658            }
659            return mIndex;
660        }
661
662        public MediaDetails getDetails() {
663            MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
664            if (item != null) {
665                mHighlightDrawer.setHighlightItem(item.getPath());
666                return item.getDetails();
667            } else {
668                return null;
669            }
670        }
671    }
672}
673