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.Handler;
25import android.os.Message;
26import android.view.HapticFeedbackConstants;
27import android.view.Menu;
28import android.view.MenuInflater;
29import android.view.MenuItem;
30import android.view.View;
31import android.view.View.OnClickListener;
32import android.widget.Button;
33import android.widget.RelativeLayout;
34import android.widget.Toast;
35
36import com.android.gallery3d.R;
37import com.android.gallery3d.common.Utils;
38import com.android.gallery3d.data.DataManager;
39import com.android.gallery3d.data.MediaDetails;
40import com.android.gallery3d.data.MediaItem;
41import com.android.gallery3d.data.MediaObject;
42import com.android.gallery3d.data.MediaSet;
43import com.android.gallery3d.data.Path;
44import com.android.gallery3d.glrenderer.FadeTexture;
45import com.android.gallery3d.glrenderer.GLCanvas;
46import com.android.gallery3d.picasasource.PicasaSource;
47import com.android.gallery3d.settings.GallerySettings;
48import com.android.gallery3d.ui.ActionModeHandler;
49import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
50import com.android.gallery3d.ui.AlbumSetSlotRenderer;
51import com.android.gallery3d.ui.DetailsHelper;
52import com.android.gallery3d.ui.DetailsHelper.CloseListener;
53import com.android.gallery3d.ui.GLRoot;
54import com.android.gallery3d.ui.GLView;
55import com.android.gallery3d.ui.SelectionManager;
56import com.android.gallery3d.ui.SlotView;
57import com.android.gallery3d.ui.SynchronizedHandler;
58import com.android.gallery3d.util.Future;
59import com.android.gallery3d.util.GalleryUtils;
60import com.android.gallery3d.util.HelpUtils;
61
62import java.lang.ref.WeakReference;
63import java.util.ArrayList;
64
65public class AlbumSetPage extends ActivityState implements
66        SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner,
67        EyePosition.EyePositionListener, MediaSet.SyncListener {
68    @SuppressWarnings("unused")
69    private static final String TAG = "AlbumSetPage";
70
71    private static final int MSG_PICK_ALBUM = 1;
72
73    public static final String KEY_MEDIA_PATH = "media-path";
74    public static final String KEY_SET_TITLE = "set-title";
75    public static final String KEY_SET_SUBTITLE = "set-subtitle";
76    public static final String KEY_SELECTED_CLUSTER_TYPE = "selected-cluster";
77
78    private static final int DATA_CACHE_SIZE = 256;
79    private static final int REQUEST_DO_ANIMATION = 1;
80
81    private static final int BIT_LOADING_RELOAD = 1;
82    private static final int BIT_LOADING_SYNC = 2;
83
84    private boolean mIsActive = false;
85    private SlotView mSlotView;
86    private AlbumSetSlotRenderer mAlbumSetView;
87    private Config.AlbumSetPage mConfig;
88
89    private MediaSet mMediaSet;
90    private String mTitle;
91    private String mSubtitle;
92    private boolean mShowClusterMenu;
93    private GalleryActionBar mActionBar;
94    private int mSelectedAction;
95
96    protected SelectionManager mSelectionManager;
97    private AlbumSetDataLoader mAlbumSetDataAdapter;
98
99    private boolean mGetContent;
100    private boolean mGetAlbum;
101    private ActionModeHandler mActionModeHandler;
102    private DetailsHelper mDetailsHelper;
103    private MyDetailsSource mDetailsSource;
104    private boolean mShowDetails;
105    private EyePosition mEyePosition;
106    private Handler mHandler;
107
108    // The eyes' position of the user, the origin is at the center of the
109    // device and the unit is in pixels.
110    private float mX;
111    private float mY;
112    private float mZ;
113
114    private Future<Integer> mSyncTask = null;
115
116    private int mLoadingBits = 0;
117    private boolean mInitialSynced = false;
118
119    private Button mCameraButton;
120    private boolean mShowedEmptyToastForSelf = false;
121
122    @Override
123    protected int getBackgroundColorId() {
124        return R.color.albumset_background;
125    }
126
127    private final GLView mRootPane = new GLView() {
128        private final float mMatrix[] = new float[16];
129
130        @Override
131        protected void onLayout(
132                boolean changed, int left, int top, int right, int bottom) {
133            mEyePosition.resetPosition();
134
135            int slotViewTop = mActionBar.getHeight() + mConfig.paddingTop;
136            int slotViewBottom = bottom - top - mConfig.paddingBottom;
137            int slotViewRight = right - left;
138
139            if (mShowDetails) {
140                mDetailsHelper.layout(left, slotViewTop, right, bottom);
141            } else {
142                mAlbumSetView.setHighlightItemPath(null);
143            }
144
145            mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
146        }
147
148        @Override
149        protected void render(GLCanvas canvas) {
150            canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
151            GalleryUtils.setViewPointMatrix(mMatrix,
152                    getWidth() / 2 + mX, getHeight() / 2 + mY, mZ);
153            canvas.multiplyMatrix(mMatrix, 0);
154            super.render(canvas);
155            canvas.restore();
156        }
157    };
158
159    @Override
160    public void onEyePositionChanged(float x, float y, float z) {
161        mRootPane.lockRendering();
162        mX = x;
163        mY = y;
164        mZ = z;
165        mRootPane.unlockRendering();
166        mRootPane.invalidate();
167    }
168
169    @Override
170    public void onBackPressed() {
171        if (mShowDetails) {
172            hideDetails();
173        } else if (mSelectionManager.inSelectionMode()) {
174            mSelectionManager.leaveSelectionMode();
175        } else {
176            super.onBackPressed();
177        }
178    }
179
180    private void getSlotCenter(int slotIndex, int center[]) {
181        Rect offset = new Rect();
182        mRootPane.getBoundsOf(mSlotView, offset);
183        Rect r = mSlotView.getSlotRect(slotIndex);
184        int scrollX = mSlotView.getScrollX();
185        int scrollY = mSlotView.getScrollY();
186        center[0] = offset.left + (r.left + r.right) / 2 - scrollX;
187        center[1] = offset.top + (r.top + r.bottom) / 2 - scrollY;
188    }
189
190    public void onSingleTapUp(int slotIndex) {
191        if (!mIsActive) return;
192
193        if (mSelectionManager.inSelectionMode()) {
194            MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
195            if (targetSet == null) return; // Content is dirty, we shall reload soon
196            mSelectionManager.toggle(targetSet.getPath());
197            mSlotView.invalidate();
198        } else {
199            // Show pressed-up animation for the single-tap.
200            mAlbumSetView.setPressedIndex(slotIndex);
201            mAlbumSetView.setPressedUp();
202            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PICK_ALBUM, slotIndex, 0),
203                    FadeTexture.DURATION);
204        }
205    }
206
207    private static boolean albumShouldOpenInFilmstrip(MediaSet album) {
208        int itemCount = album.getMediaItemCount();
209        ArrayList<MediaItem> list = (itemCount == 1) ? album.getMediaItem(0, 1) : null;
210        // open in film strip only if there's one item in the album and the item exists
211        return (list != null && !list.isEmpty());
212    }
213
214    WeakReference<Toast> mEmptyAlbumToast = null;
215
216    private void showEmptyAlbumToast(int toastLength) {
217        Toast toast;
218        if (mEmptyAlbumToast != null) {
219            toast = mEmptyAlbumToast.get();
220            if (toast != null) {
221                toast.show();
222                return;
223            }
224        }
225        toast = Toast.makeText(mActivity, R.string.empty_album, toastLength);
226        mEmptyAlbumToast = new WeakReference<Toast>(toast);
227        toast.show();
228    }
229
230    private void hideEmptyAlbumToast() {
231        if (mEmptyAlbumToast != null) {
232            Toast toast = mEmptyAlbumToast.get();
233            if (toast != null) toast.cancel();
234        }
235    }
236
237    private void pickAlbum(int slotIndex) {
238        if (!mIsActive) return;
239
240        MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
241        if (targetSet == null) return; // Content is dirty, we shall reload soon
242        if (targetSet.getTotalMediaItemCount() == 0) {
243            showEmptyAlbumToast(Toast.LENGTH_SHORT);
244            return;
245        }
246        hideEmptyAlbumToast();
247
248        String mediaPath = targetSet.getPath().toString();
249
250        Bundle data = new Bundle(getData());
251        int[] center = new int[2];
252        getSlotCenter(slotIndex, center);
253        data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
254        if (mGetAlbum && targetSet.isLeafAlbum()) {
255            Activity activity = mActivity;
256            Intent result = new Intent()
257                    .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString());
258            activity.setResult(Activity.RESULT_OK, result);
259            activity.finish();
260        } else if (targetSet.getSubMediaSetCount() > 0) {
261            data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
262            mActivity.getStateManager().startStateForResult(
263                    AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
264        } else {
265            if (!mGetContent && albumShouldOpenInFilmstrip(targetSet)) {
266                data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
267                        mSlotView.getSlotRect(slotIndex, mRootPane));
268                data.putInt(PhotoPage.KEY_INDEX_HINT, 0);
269                data.putString(PhotoPage.KEY_MEDIA_SET_PATH,
270                        mediaPath);
271                data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP, true);
272                data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, targetSet.isCameraRoll());
273                mActivity.getStateManager().startStateForResult(
274                        FilmstripPage.class, AlbumPage.REQUEST_PHOTO, data);
275                return;
276            }
277            data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
278
279            // We only show cluster menu in the first AlbumPage in stack
280            boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
281            data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum);
282            mActivity.getStateManager().startStateForResult(
283                    AlbumPage.class, REQUEST_DO_ANIMATION, data);
284        }
285    }
286
287    private void onDown(int index) {
288        mAlbumSetView.setPressedIndex(index);
289    }
290
291    private void onUp(boolean followedByLongPress) {
292        if (followedByLongPress) {
293            // Avoid showing press-up animations for long-press.
294            mAlbumSetView.setPressedIndex(-1);
295        } else {
296            mAlbumSetView.setPressedUp();
297        }
298    }
299
300    public void onLongTap(int slotIndex) {
301        if (mGetContent || mGetAlbum) return;
302        MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex);
303        if (set == null) return;
304        mSelectionManager.setAutoLeaveSelectionMode(true);
305        mSelectionManager.toggle(set.getPath());
306        mSlotView.invalidate();
307    }
308
309    @Override
310    public void doCluster(int clusterType) {
311        String basePath = mMediaSet.getPath().toString();
312        String newPath = FilterUtils.switchClusterPath(basePath, clusterType);
313        Bundle data = new Bundle(getData());
314        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
315        data.putInt(KEY_SELECTED_CLUSTER_TYPE, clusterType);
316        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
317    }
318
319    @Override
320    public void onCreate(Bundle data, Bundle restoreState) {
321        super.onCreate(data, restoreState);
322        initializeViews();
323        initializeData(data);
324        Context context = mActivity.getAndroidContext();
325        mGetContent = data.getBoolean(GalleryActivity.KEY_GET_CONTENT, false);
326        mGetAlbum = data.getBoolean(GalleryActivity.KEY_GET_ALBUM, false);
327        mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE);
328        mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
329        mEyePosition = new EyePosition(context, this);
330        mDetailsSource = new MyDetailsSource();
331        mActionBar = mActivity.getGalleryActionBar();
332        mSelectedAction = data.getInt(AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE,
333                FilterUtils.CLUSTER_BY_ALBUM);
334
335        mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
336            @Override
337            public void handleMessage(Message message) {
338                switch (message.what) {
339                    case MSG_PICK_ALBUM: {
340                        pickAlbum(message.arg1);
341                        break;
342                    }
343                    default: throw new AssertionError(message.what);
344                }
345            }
346        };
347    }
348
349    @Override
350    public void onDestroy() {
351        super.onDestroy();
352        cleanupCameraButton();
353        mActionModeHandler.destroy();
354    }
355
356    private boolean setupCameraButton() {
357        if (!GalleryUtils.isCameraAvailable(mActivity)) return false;
358        RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity)
359                .findViewById(R.id.gallery_root);
360        if (galleryRoot == null) return false;
361
362        mCameraButton = new Button(mActivity);
363        mCameraButton.setText(R.string.camera_label);
364        mCameraButton.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.frame_overlay_gallery_camera, 0, 0);
365        mCameraButton.setOnClickListener(new OnClickListener() {
366            @Override
367            public void onClick(View arg0) {
368                GalleryUtils.startCameraActivity(mActivity);
369            }
370        });
371        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
372                RelativeLayout.LayoutParams.WRAP_CONTENT,
373                RelativeLayout.LayoutParams.WRAP_CONTENT);
374        lp.addRule(RelativeLayout.CENTER_IN_PARENT);
375        galleryRoot.addView(mCameraButton, lp);
376        return true;
377    }
378
379    private void cleanupCameraButton() {
380        if (mCameraButton == null) return;
381        RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity)
382                .findViewById(R.id.gallery_root);
383        if (galleryRoot == null) return;
384        galleryRoot.removeView(mCameraButton);
385        mCameraButton = null;
386    }
387
388    private void showCameraButton() {
389        if (mCameraButton == null && !setupCameraButton()) return;
390        mCameraButton.setVisibility(View.VISIBLE);
391    }
392
393    private void hideCameraButton() {
394        if (mCameraButton == null) return;
395        mCameraButton.setVisibility(View.GONE);
396    }
397
398    private void clearLoadingBit(int loadingBit) {
399        mLoadingBits &= ~loadingBit;
400        if (mLoadingBits == 0 && mIsActive) {
401            if (mAlbumSetDataAdapter.size() == 0) {
402                // If this is not the top of the gallery folder hierarchy,
403                // tell the parent AlbumSetPage instance to handle displaying
404                // the empty album toast, otherwise show it within this
405                // instance
406                if (mActivity.getStateManager().getStateCount() > 1) {
407                    Intent result = new Intent();
408                    result.putExtra(AlbumPage.KEY_EMPTY_ALBUM, true);
409                    setStateResult(Activity.RESULT_OK, result);
410                    mActivity.getStateManager().finishState(this);
411                } else {
412                    mShowedEmptyToastForSelf = true;
413                    showEmptyAlbumToast(Toast.LENGTH_LONG);
414                    mSlotView.invalidate();
415                    showCameraButton();
416                }
417                return;
418            }
419        }
420        // Hide the empty album toast if we are in the root instance of
421        // AlbumSetPage and the album is no longer empty (for instance,
422        // after a sync is completed and web albums have been synced)
423        if (mShowedEmptyToastForSelf) {
424            mShowedEmptyToastForSelf = false;
425            hideEmptyAlbumToast();
426            hideCameraButton();
427        }
428    }
429
430    private void setLoadingBit(int loadingBit) {
431        mLoadingBits |= loadingBit;
432    }
433
434    @Override
435    public void onPause() {
436        super.onPause();
437        mIsActive = false;
438        mAlbumSetDataAdapter.pause();
439        mAlbumSetView.pause();
440        mActionModeHandler.pause();
441        mEyePosition.pause();
442        DetailsHelper.pause();
443        // Call disableClusterMenu to avoid receiving callback after paused.
444        // Don't hide menu here otherwise the list menu will disappear earlier than
445        // the action bar, which is janky and unwanted behavior.
446        mActionBar.disableClusterMenu(false);
447        if (mSyncTask != null) {
448            mSyncTask.cancel();
449            mSyncTask = null;
450            clearLoadingBit(BIT_LOADING_SYNC);
451        }
452    }
453
454    @Override
455    public void onResume() {
456        super.onResume();
457        mIsActive = true;
458        setContentPane(mRootPane);
459
460        // Set the reload bit here to prevent it exit this page in clearLoadingBit().
461        setLoadingBit(BIT_LOADING_RELOAD);
462        mAlbumSetDataAdapter.resume();
463
464        mAlbumSetView.resume();
465        mEyePosition.resume();
466        mActionModeHandler.resume();
467        if (mShowClusterMenu) {
468            mActionBar.enableClusterMenu(mSelectedAction, this);
469        }
470        if (!mInitialSynced) {
471            setLoadingBit(BIT_LOADING_SYNC);
472            mSyncTask = mMediaSet.requestSync(AlbumSetPage.this);
473        }
474    }
475
476    private void initializeData(Bundle data) {
477        String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
478        mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
479        mSelectionManager.setSourceMediaSet(mMediaSet);
480        mAlbumSetDataAdapter = new AlbumSetDataLoader(
481                mActivity, mMediaSet, DATA_CACHE_SIZE);
482        mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
483        mAlbumSetView.setModel(mAlbumSetDataAdapter);
484    }
485
486    private void initializeViews() {
487        mSelectionManager = new SelectionManager(mActivity, true);
488        mSelectionManager.setSelectionListener(this);
489
490        mConfig = Config.AlbumSetPage.get(mActivity);
491        mSlotView = new SlotView(mActivity, mConfig.slotViewSpec);
492        mAlbumSetView = new AlbumSetSlotRenderer(
493                mActivity, mSelectionManager, mSlotView, mConfig.labelSpec,
494                mConfig.placeholderColor);
495        mSlotView.setSlotRenderer(mAlbumSetView);
496        mSlotView.setListener(new SlotView.SimpleListener() {
497            @Override
498            public void onDown(int index) {
499                AlbumSetPage.this.onDown(index);
500            }
501
502            @Override
503            public void onUp(boolean followedByLongPress) {
504                AlbumSetPage.this.onUp(followedByLongPress);
505            }
506
507            @Override
508            public void onSingleTapUp(int slotIndex) {
509                AlbumSetPage.this.onSingleTapUp(slotIndex);
510            }
511
512            @Override
513            public void onLongTap(int slotIndex) {
514                AlbumSetPage.this.onLongTap(slotIndex);
515            }
516        });
517
518        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
519        mActionModeHandler.setActionModeListener(new ActionModeListener() {
520            @Override
521            public boolean onActionItemClicked(MenuItem item) {
522                return onItemSelected(item);
523            }
524        });
525        mRootPane.addComponent(mSlotView);
526    }
527
528    @Override
529    protected boolean onCreateActionBar(Menu menu) {
530        Activity activity = mActivity;
531        final boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
532        MenuInflater inflater = getSupportMenuInflater();
533
534        if (mGetContent) {
535            inflater.inflate(R.menu.pickup, menu);
536            int typeBits = mData.getInt(
537                    GalleryActivity.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
538            mActionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
539        } else  if (mGetAlbum) {
540            inflater.inflate(R.menu.pickup, menu);
541            mActionBar.setTitle(R.string.select_album);
542        } else {
543            inflater.inflate(R.menu.albumset, menu);
544            boolean wasShowingClusterMenu = mShowClusterMenu;
545            mShowClusterMenu = !inAlbum;
546            boolean selectAlbums = !inAlbum &&
547                    mActionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM;
548            MenuItem selectItem = menu.findItem(R.id.action_select);
549            selectItem.setTitle(activity.getString(
550                    selectAlbums ? R.string.select_album : R.string.select_group));
551
552            MenuItem cameraItem = menu.findItem(R.id.action_camera);
553            cameraItem.setVisible(GalleryUtils.isCameraAvailable(activity));
554
555            FilterUtils.setupMenuItems(mActionBar, mMediaSet.getPath(), false);
556
557            Intent helpIntent = HelpUtils.getHelpIntent(activity);
558
559            MenuItem helpItem = menu.findItem(R.id.action_general_help);
560            helpItem.setVisible(helpIntent != null);
561            if (helpIntent != null) helpItem.setIntent(helpIntent);
562
563            mActionBar.setTitle(mTitle);
564            mActionBar.setSubtitle(mSubtitle);
565            if (mShowClusterMenu != wasShowingClusterMenu) {
566                if (mShowClusterMenu) {
567                    mActionBar.enableClusterMenu(mSelectedAction, this);
568                } else {
569                    mActionBar.disableClusterMenu(true);
570                }
571            }
572        }
573        return true;
574    }
575
576    @Override
577    protected boolean onItemSelected(MenuItem item) {
578        Activity activity = mActivity;
579        switch (item.getItemId()) {
580            case R.id.action_cancel:
581                activity.setResult(Activity.RESULT_CANCELED);
582                activity.finish();
583                return true;
584            case R.id.action_select:
585                mSelectionManager.setAutoLeaveSelectionMode(false);
586                mSelectionManager.enterSelectionMode();
587                return true;
588            case R.id.action_details:
589                if (mAlbumSetDataAdapter.size() != 0) {
590                    if (mShowDetails) {
591                        hideDetails();
592                    } else {
593                        showDetails();
594                    }
595                } else {
596                    Toast.makeText(activity,
597                            activity.getText(R.string.no_albums_alert),
598                            Toast.LENGTH_SHORT).show();
599                }
600                return true;
601            case R.id.action_camera: {
602                GalleryUtils.startCameraActivity(activity);
603                return true;
604            }
605            case R.id.action_manage_offline: {
606                Bundle data = new Bundle();
607                String mediaPath = mActivity.getDataManager().getTopSetPath(
608                    DataManager.INCLUDE_ALL);
609                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
610                mActivity.getStateManager().startState(ManageCachePage.class, data);
611                return true;
612            }
613            case R.id.action_sync_picasa_albums: {
614                PicasaSource.requestSync(activity);
615                return true;
616            }
617            case R.id.action_settings: {
618                activity.startActivity(new Intent(activity, GallerySettings.class));
619                return true;
620            }
621            default:
622                return false;
623        }
624    }
625
626    @Override
627    protected void onStateResult(int requestCode, int resultCode, Intent data) {
628        if (data != null && data.getBooleanExtra(AlbumPage.KEY_EMPTY_ALBUM, false)) {
629            showEmptyAlbumToast(Toast.LENGTH_SHORT);
630        }
631        switch (requestCode) {
632            case REQUEST_DO_ANIMATION: {
633                mSlotView.startRisingAnimation();
634            }
635        }
636    }
637
638    private String getSelectedString() {
639        int count = mSelectionManager.getSelectedCount();
640        int action = mActionBar.getClusterTypeAction();
641        int string = action == FilterUtils.CLUSTER_BY_ALBUM
642                ? R.plurals.number_of_albums_selected
643                : R.plurals.number_of_groups_selected;
644        String format = mActivity.getResources().getQuantityString(string, count);
645        return String.format(format, count);
646    }
647
648    @Override
649    public void onSelectionModeChange(int mode) {
650        switch (mode) {
651            case SelectionManager.ENTER_SELECTION_MODE: {
652                mActionBar.disableClusterMenu(true);
653                mActionModeHandler.startActionMode();
654                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
655                break;
656            }
657            case SelectionManager.LEAVE_SELECTION_MODE: {
658                mActionModeHandler.finishActionMode();
659                if (mShowClusterMenu) {
660                    mActionBar.enableClusterMenu(mSelectedAction, this);
661                }
662                mRootPane.invalidate();
663                break;
664            }
665            case SelectionManager.SELECT_ALL_MODE: {
666                mActionModeHandler.updateSupportedOperation();
667                mRootPane.invalidate();
668                break;
669            }
670        }
671    }
672
673    @Override
674    public void onSelectionChange(Path path, boolean selected) {
675        mActionModeHandler.setTitle(getSelectedString());
676        mActionModeHandler.updateSupportedOperation(path, selected);
677    }
678
679    private void hideDetails() {
680        mShowDetails = false;
681        mDetailsHelper.hide();
682        mAlbumSetView.setHighlightItemPath(null);
683        mSlotView.invalidate();
684    }
685
686    private void showDetails() {
687        mShowDetails = true;
688        if (mDetailsHelper == null) {
689            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
690            mDetailsHelper.setCloseListener(new CloseListener() {
691                @Override
692                public void onClose() {
693                    hideDetails();
694                }
695            });
696        }
697        mDetailsHelper.show();
698    }
699
700    @Override
701    public void onSyncDone(final MediaSet mediaSet, final int resultCode) {
702        if (resultCode == MediaSet.SYNC_RESULT_ERROR) {
703            Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result="
704                    + resultCode);
705        }
706        ((Activity) mActivity).runOnUiThread(new Runnable() {
707            @Override
708            public void run() {
709                GLRoot root = mActivity.getGLRoot();
710                root.lockRenderThread();
711                try {
712                    if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) {
713                        mInitialSynced = true;
714                    }
715                    clearLoadingBit(BIT_LOADING_SYNC);
716                    if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) {
717                        Log.w(TAG, "failed to load album set");
718                    }
719                } finally {
720                    root.unlockRenderThread();
721                }
722            }
723        });
724    }
725
726    private class MyLoadingListener implements LoadingListener {
727        @Override
728        public void onLoadingStarted() {
729            setLoadingBit(BIT_LOADING_RELOAD);
730        }
731
732        @Override
733        public void onLoadingFinished(boolean loadingFailed) {
734            clearLoadingBit(BIT_LOADING_RELOAD);
735        }
736    }
737
738    private class MyDetailsSource implements DetailsHelper.DetailsSource {
739        private int mIndex;
740
741        @Override
742        public int size() {
743            return mAlbumSetDataAdapter.size();
744        }
745
746        @Override
747        public int setIndex() {
748            Path id = mSelectionManager.getSelected(false).get(0);
749            mIndex = mAlbumSetDataAdapter.findSet(id);
750            return mIndex;
751        }
752
753        @Override
754        public MediaDetails getDetails() {
755            MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
756            if (item != null) {
757                mAlbumSetView.setHighlightItemPath(item.getPath());
758                return item.getDetails();
759            } else {
760                return null;
761            }
762        }
763    }
764}
765