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