OSDN Git Service

dd9d8ec410714305961ccb7c8c00e426dbe7b633
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / app / AlbumSetPage.java
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
17 package com.android.gallery3d.app;
18
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.graphics.Rect;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.view.HapticFeedbackConstants;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.view.View;
31 import android.view.View.OnClickListener;
32 import android.widget.Button;
33 import android.widget.RelativeLayout;
34 import android.widget.Toast;
35
36 import com.android.gallery3d.R;
37 import com.android.gallery3d.common.Utils;
38 import com.android.gallery3d.data.DataManager;
39 import com.android.gallery3d.data.MediaDetails;
40 import com.android.gallery3d.data.MediaItem;
41 import com.android.gallery3d.data.MediaObject;
42 import com.android.gallery3d.data.MediaSet;
43 import com.android.gallery3d.data.Path;
44 import com.android.gallery3d.glrenderer.FadeTexture;
45 import com.android.gallery3d.glrenderer.GLCanvas;
46 import com.android.gallery3d.picasasource.PicasaSource;
47 import com.android.gallery3d.settings.GallerySettings;
48 import com.android.gallery3d.ui.ActionModeHandler;
49 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
50 import com.android.gallery3d.ui.AlbumSetSlotRenderer;
51 import com.android.gallery3d.ui.DetailsHelper;
52 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
53 import com.android.gallery3d.ui.GLRoot;
54 import com.android.gallery3d.ui.GLView;
55 import com.android.gallery3d.ui.SelectionManager;
56 import com.android.gallery3d.ui.SlotView;
57 import com.android.gallery3d.ui.SynchronizedHandler;
58 import com.android.gallery3d.util.Future;
59 import com.android.gallery3d.util.GalleryUtils;
60 import com.android.gallery3d.util.HelpUtils;
61
62 import java.lang.ref.WeakReference;
63 import java.util.ArrayList;
64
65 public 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(Gallery.KEY_GET_CONTENT, false);
326         mGetAlbum = data.getBoolean(Gallery.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                     Gallery.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 }