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.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, R.string.help_url_gallery_main);
    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