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.net.Uri;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.os.Vibrator;
     28 import android.provider.MediaStore;
     29 import android.view.ActionMode;
     30 import android.view.Menu;
     31 import android.view.MenuInflater;
     32 import android.view.MenuItem;
     33 import android.widget.Toast;
     34 
     35 import com.android.gallery3d.R;
     36 import com.android.gallery3d.common.Utils;
     37 import com.android.gallery3d.data.DataManager;
     38 import com.android.gallery3d.data.MediaDetails;
     39 import com.android.gallery3d.data.MediaItem;
     40 import com.android.gallery3d.data.MediaObject;
     41 import com.android.gallery3d.data.MediaSet;
     42 import com.android.gallery3d.data.MtpDevice;
     43 import com.android.gallery3d.data.Path;
     44 import com.android.gallery3d.ui.ActionModeHandler;
     45 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
     46 import com.android.gallery3d.ui.AlbumSlotRenderer;
     47 import com.android.gallery3d.ui.DetailsHelper;
     48 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
     49 import com.android.gallery3d.ui.FadeTexture;
     50 import com.android.gallery3d.ui.GLCanvas;
     51 import com.android.gallery3d.ui.GLRoot;
     52 import com.android.gallery3d.ui.GLView;
     53 import com.android.gallery3d.ui.PhotoFallbackEffect;
     54 import com.android.gallery3d.ui.RelativePosition;
     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.MediaSetUtils;
     61 
     62 public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner,
     63         SelectionManager.SelectionListener, MediaSet.SyncListener {
     64     @SuppressWarnings("unused")
     65     private static final String TAG = "AlbumPage";
     66 
     67     private static final int MSG_PICK_PHOTO = 1;
     68 
     69     public static final String KEY_MEDIA_PATH = "media-path";
     70     public static final String KEY_PARENT_MEDIA_PATH = "parent-media-path";
     71     public static final String KEY_SET_CENTER = "set-center";
     72     public static final String KEY_AUTO_SELECT_ALL = "auto-select-all";
     73     public static final String KEY_SHOW_CLUSTER_MENU = "cluster-menu";
     74     public static final String KEY_RESUME_ANIMATION = "resume_animation";
     75 
     76     private static final int REQUEST_SLIDESHOW = 1;
     77     private static final int REQUEST_PHOTO = 2;
     78     private static final int REQUEST_DO_ANIMATION = 3;
     79 
     80     private static final int BIT_LOADING_RELOAD = 1;
     81     private static final int BIT_LOADING_SYNC = 2;
     82 
     83     private static final float USER_DISTANCE_METER = 0.3f;
     84 
     85     private boolean mIsActive = false;
     86     private AlbumSlotRenderer mAlbumView;
     87     private Path mMediaSetPath;
     88     private String mParentMediaSetString;
     89     private SlotView mSlotView;
     90 
     91     private AlbumDataLoader mAlbumDataAdapter;
     92 
     93     protected SelectionManager mSelectionManager;
     94     private Vibrator mVibrator;
     95 
     96     private boolean mGetContent;
     97     private boolean mShowClusterMenu;
     98 
     99     private ActionMode mActionMode;
    100     private ActionModeHandler mActionModeHandler;
    101     private int mFocusIndex = 0;
    102     private DetailsHelper mDetailsHelper;
    103     private MyDetailsSource mDetailsSource;
    104     private MediaSet mMediaSet;
    105     private boolean mShowDetails;
    106     private float mUserDistance; // in pixel
    107     private Handler mHandler;
    108 
    109     private Future<Integer> mSyncTask = null;
    110 
    111     private int mLoadingBits = 0;
    112     private boolean mInitialSynced = false;
    113     private RelativePosition mOpenCenter = new RelativePosition();
    114 
    115     private PhotoFallbackEffect mResumeEffect;
    116     private PhotoFallbackEffect.PositionProvider mPositionProvider =
    117             new PhotoFallbackEffect.PositionProvider() {
    118         @Override
    119         public Rect getPosition(int index) {
    120             Rect rect = mSlotView.getSlotRect(index);
    121             Rect bounds = mSlotView.bounds();
    122             rect.offset(bounds.left - mSlotView.getScrollX(),
    123                     bounds.top - mSlotView.getScrollY());
    124             return rect;
    125         }
    126 
    127         @Override
    128         public int getItemIndex(Path path) {
    129             int start = mSlotView.getVisibleStart();
    130             int end = mSlotView.getVisibleEnd();
    131             for (int i = start; i < end; ++i) {
    132                 MediaItem item = mAlbumDataAdapter.get(i);
    133                 if (item != null && item.getPath() == path) return i;
    134             }
    135             return -1;
    136         }
    137     };
    138 
    139     private final GLView mRootPane = new GLView() {
    140         private final float mMatrix[] = new float[16];
    141 
    142         @Override
    143         protected void renderBackground(GLCanvas view) {
    144             view.clearBuffer();
    145         }
    146 
    147         @Override
    148         protected void onLayout(
    149                 boolean changed, int left, int top, int right, int bottom) {
    150 
    151             int slotViewTop = mActivity.getGalleryActionBar().getHeight();
    152             int slotViewBottom = bottom - top;
    153             int slotViewRight = right - left;
    154 
    155             if (mShowDetails) {
    156                 mDetailsHelper.layout(left, slotViewTop, right, bottom);
    157             } else {
    158                 mAlbumView.setHighlightItemPath(null);
    159             }
    160 
    161             // Set the mSlotView as a reference point to the open animation
    162             mOpenCenter.setReferencePosition(0, slotViewTop);
    163             mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
    164             GalleryUtils.setViewPointMatrix(mMatrix,
    165                     (right - left) / 2, (bottom - top) / 2, -mUserDistance);
    166         }
    167 
    168         @Override
    169         protected void render(GLCanvas canvas) {
    170             canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
    171             canvas.multiplyMatrix(mMatrix, 0);
    172             super.render(canvas);
    173 
    174             if (mResumeEffect != null) {
    175                 boolean more = mResumeEffect.draw(canvas);
    176                 if (!more) {
    177                     mResumeEffect = null;
    178                     mAlbumView.setSlotFilter(null);
    179                 }
    180                 // We want to render one more time even when no more effect
    181                 // required. So that the animated thumbnails could be draw
    182                 // with declarations in super.render().
    183                 invalidate();
    184             }
    185             canvas.restore();
    186         }
    187     };
    188 
    189     // This are the transitions we want:
    190     //
    191     // +--------+           +------------+    +-------+    +----------+
    192     // | Camera |---------->| Fullscreen |--->| Album |--->| AlbumSet |
    193     // |  View  | thumbnail |   Photo    | up | Page  | up |   Page   |
    194     // +--------+           +------------+    +-------+    +----------+
    195     //     ^                      |               |            ^  |
    196     //     |                      |               |            |  |         close
    197     //     +----------back--------+               +----back----+  +--back->  app
    198     //
    199     @Override
    200     protected void onBackPressed() {
    201         if (mShowDetails) {
    202             hideDetails();
    203         } else if (mSelectionManager.inSelectionMode()) {
    204             mSelectionManager.leaveSelectionMode();
    205         } else {
    206             // TODO: fix this regression
    207             // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
    208             onUpPressed();
    209         }
    210     }
    211 
    212     private void onUpPressed() {
    213         if (mActivity.getStateManager().getStateCount() > 1) {
    214             super.onBackPressed();
    215         } else if (mParentMediaSetString != null) {
    216             Bundle data = new Bundle(getData());
    217             data.putString(AlbumSetPage.KEY_MEDIA_PATH, mParentMediaSetString);
    218             mActivity.getStateManager().switchState(
    219                     this, AlbumSetPage.class, data);
    220         }
    221     }
    222 
    223     private void onDown(int index) {
    224         mAlbumView.setPressedIndex(index);
    225     }
    226 
    227     private void onUp(boolean followedByLongPress) {
    228         if (followedByLongPress) {
    229             // Avoid showing press-up animations for long-press.
    230             mAlbumView.setPressedIndex(-1);
    231         } else {
    232             mAlbumView.setPressedUp();
    233         }
    234     }
    235 
    236     private void onSingleTapUp(int slotIndex) {
    237         if (!mIsActive) return;
    238 
    239         if (mSelectionManager.inSelectionMode()) {
    240             MediaItem item = mAlbumDataAdapter.get(slotIndex);
    241             if (item == null) return; // Item not ready yet, ignore the click
    242             mSelectionManager.toggle(item.getPath());
    243             mDetailsSource.findIndex(slotIndex);
    244             mSlotView.invalidate();
    245         } else {
    246             // Show pressed-up animation for the single-tap.
    247             mAlbumView.setPressedIndex(slotIndex);
    248             mAlbumView.setPressedUp();
    249             mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PICK_PHOTO, slotIndex, 0),
    250                     FadeTexture.DURATION);
    251         }
    252     }
    253 
    254     private void pickPhoto(int slotIndex) {
    255         if (!mIsActive) return;
    256 
    257         MediaItem item = mAlbumDataAdapter.get(slotIndex);
    258         if (item == null) return; // Item not ready yet, ignore the click
    259         if (mGetContent) {
    260             onGetContent(item);
    261         } else {
    262             // Get into the PhotoPage.
    263             // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
    264             Bundle data = new Bundle();
    265             data.putInt(PhotoPage.KEY_INDEX_HINT, slotIndex);
    266             data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
    267                     getSlotRect(slotIndex));
    268             data.putString(PhotoPage.KEY_MEDIA_SET_PATH,
    269                     mMediaSetPath.toString());
    270             data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH,
    271                     item.getPath().toString());
    272             mActivity.getStateManager().startStateForResult(
    273                     PhotoPage.class, REQUEST_PHOTO, data);
    274         }
    275     }
    276 
    277     private Rect getSlotRect(int slotIndex) {
    278         // Get slot rectangle relative to this root pane.
    279         Rect offset = new Rect();
    280         mRootPane.getBoundsOf(mSlotView, offset);
    281         Rect r = mSlotView.getSlotRect(slotIndex);
    282         r.offset(offset.left - mSlotView.getScrollX(),
    283                 offset.top - mSlotView.getScrollY());
    284         return r;
    285     }
    286 
    287     private void onGetContent(final MediaItem item) {
    288         DataManager dm = mActivity.getDataManager();
    289         Activity activity = (Activity) mActivity;
    290         if (mData.getString(Gallery.EXTRA_CROP) != null) {
    291             // TODO: Handle MtpImagew
    292             Uri uri = dm.getContentUri(item.getPath());
    293             Intent intent = new Intent(CropImage.ACTION_CROP, uri)
    294                     .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
    295                     .putExtras(getData());
    296             if (mData.getParcelable(MediaStore.EXTRA_OUTPUT) == null) {
    297                 intent.putExtra(CropImage.KEY_RETURN_DATA, true);
    298             }
    299             activity.startActivity(intent);
    300             activity.finish();
    301         } else {
    302             activity.setResult(Activity.RESULT_OK,
    303                     new Intent(null, item.getContentUri()));
    304             activity.finish();
    305         }
    306     }
    307 
    308     public void onLongTap(int slotIndex) {
    309         if (mGetContent) return;
    310         MediaItem item = mAlbumDataAdapter.get(slotIndex);
    311         if (item == null) return;
    312         mSelectionManager.setAutoLeaveSelectionMode(true);
    313         mSelectionManager.toggle(item.getPath());
    314         mDetailsSource.findIndex(slotIndex);
    315         mSlotView.invalidate();
    316     }
    317 
    318     @Override
    319     public void doCluster(int clusterType) {
    320         String basePath = mMediaSet.getPath().toString();
    321         String newPath = FilterUtils.newClusterPath(basePath, clusterType);
    322         Bundle data = new Bundle(getData());
    323         data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
    324         if (mShowClusterMenu) {
    325             Context context = mActivity.getAndroidContext();
    326             data.putString(AlbumSetPage.KEY_SET_TITLE, mMediaSet.getName());
    327             data.putString(AlbumSetPage.KEY_SET_SUBTITLE,
    328                     GalleryActionBar.getClusterByTypeString(context, clusterType));
    329         }
    330 
    331         // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
    332         mActivity.getStateManager().startStateForResult(
    333                 AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
    334     }
    335 
    336     @Override
    337     protected void onCreate(Bundle data, Bundle restoreState) {
    338         mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
    339         initializeViews();
    340         initializeData(data);
    341         mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
    342         mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
    343         mDetailsSource = new MyDetailsSource();
    344         Context context = mActivity.getAndroidContext();
    345         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    346 
    347         // Enable auto-select-all for mtp album
    348         if (data.getBoolean(KEY_AUTO_SELECT_ALL)) {
    349             mSelectionManager.selectAll();
    350         }
    351 
    352         // Don't show animation if it is restored
    353         if (restoreState == null && data != null) {
    354             int[] center = data.getIntArray(KEY_SET_CENTER);
    355             if (center != null) {
    356                 mOpenCenter.setAbsolutePosition(center[0], center[1]);
    357                 mSlotView.startScatteringAnimation(mOpenCenter);
    358             }
    359         }
    360 
    361         mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
    362             @Override
    363             public void handleMessage(Message message) {
    364                 switch (message.what) {
    365                     case MSG_PICK_PHOTO: {
    366                         pickPhoto(message.arg1);
    367                         break;
    368                     }
    369                     default: throw new AssertionError(message.what);
    370                 }
    371             }
    372         };
    373     }
    374 
    375     @Override
    376     protected void onResume() {
    377         super.onResume();
    378         mIsActive = true;
    379 
    380         mResumeEffect = mActivity.getTransitionStore().get(KEY_RESUME_ANIMATION);
    381         if (mResumeEffect != null) {
    382             mAlbumView.setSlotFilter(mResumeEffect);
    383             mResumeEffect.setPositionProvider(mPositionProvider);
    384             mResumeEffect.start();
    385         }
    386 
    387         setContentPane(mRootPane);
    388 
    389         Path path = mMediaSet.getPath();
    390         boolean enableHomeButton = (mActivity.getStateManager().getStateCount() > 1) |
    391                 mParentMediaSetString != null;
    392         mActivity.getGalleryActionBar().setDisplayOptions(enableHomeButton, true);
    393 
    394         // Set the reload bit here to prevent it exit this page in clearLoadingBit().
    395         setLoadingBit(BIT_LOADING_RELOAD);
    396         mAlbumDataAdapter.resume();
    397 
    398         mAlbumView.resume();
    399         mActionModeHandler.resume();
    400         if (!mInitialSynced) {
    401             setLoadingBit(BIT_LOADING_SYNC);
    402             mSyncTask = mMediaSet.requestSync(this);
    403         }
    404     }
    405 
    406     @Override
    407     protected void onPause() {
    408         super.onPause();
    409         mIsActive = false;
    410 
    411         mAlbumView.setSlotFilter(null);
    412 
    413         mAlbumDataAdapter.pause();
    414         mAlbumView.pause();
    415         DetailsHelper.pause();
    416 
    417         if (mSyncTask != null) {
    418             mSyncTask.cancel();
    419             mSyncTask = null;
    420             clearLoadingBit(BIT_LOADING_SYNC);
    421         }
    422         mActionModeHandler.pause();
    423     }
    424 
    425     @Override
    426     protected void onDestroy() {
    427         super.onDestroy();
    428         if (mAlbumDataAdapter != null) {
    429             mAlbumDataAdapter.setLoadingListener(null);
    430         }
    431     }
    432 
    433     private void initializeViews() {
    434         mSelectionManager = new SelectionManager(mActivity, false);
    435         mSelectionManager.setSelectionListener(this);
    436         Config.AlbumPage config = Config.AlbumPage.get((Context) mActivity);
    437         mSlotView = new SlotView(mActivity, config.slotViewSpec);
    438         mAlbumView = new AlbumSlotRenderer(mActivity, mSlotView, mSelectionManager);
    439         mSlotView.setSlotRenderer(mAlbumView);
    440         mRootPane.addComponent(mSlotView);
    441         mSlotView.setListener(new SlotView.SimpleListener() {
    442             @Override
    443             public void onDown(int index) {
    444                 AlbumPage.this.onDown(index);
    445             }
    446 
    447             @Override
    448             public void onUp(boolean followedByLongPress) {
    449                 AlbumPage.this.onUp(followedByLongPress);
    450             }
    451 
    452             @Override
    453             public void onSingleTapUp(int slotIndex) {
    454                 AlbumPage.this.onSingleTapUp(slotIndex);
    455             }
    456 
    457             @Override
    458             public void onLongTap(int slotIndex) {
    459                 AlbumPage.this.onLongTap(slotIndex);
    460             }
    461         });
    462         mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
    463         mActionModeHandler.setActionModeListener(new ActionModeListener() {
    464             public boolean onActionItemClicked(MenuItem item) {
    465                 return onItemSelected(item);
    466             }
    467         });
    468     }
    469 
    470     private void initializeData(Bundle data) {
    471         mMediaSetPath = Path.fromString(data.getString(KEY_MEDIA_PATH));
    472         mParentMediaSetString = data.getString(KEY_PARENT_MEDIA_PATH);
    473         mMediaSet = mActivity.getDataManager().getMediaSet(mMediaSetPath);
    474         if (mMediaSet == null) {
    475             Utils.fail("MediaSet is null. Path = %s", mMediaSetPath);
    476         }
    477         mSelectionManager.setSourceMediaSet(mMediaSet);
    478         mAlbumDataAdapter = new AlbumDataLoader(mActivity, mMediaSet);
    479         mAlbumDataAdapter.setLoadingListener(new MyLoadingListener());
    480         mAlbumView.setModel(mAlbumDataAdapter);
    481     }
    482 
    483     private void showDetails() {
    484         mShowDetails = true;
    485         if (mDetailsHelper == null) {
    486             mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
    487             mDetailsHelper.setCloseListener(new CloseListener() {
    488                 public void onClose() {
    489                     hideDetails();
    490                 }
    491             });
    492         }
    493         mDetailsHelper.show();
    494     }
    495 
    496     private void hideDetails() {
    497         mShowDetails = false;
    498         mDetailsHelper.hide();
    499         mAlbumView.setHighlightItemPath(null);
    500         mSlotView.invalidate();
    501     }
    502 
    503     @Override
    504     protected boolean onCreateActionBar(Menu menu) {
    505         Activity activity = (Activity) mActivity;
    506         GalleryActionBar actionBar = mActivity.getGalleryActionBar();
    507         MenuInflater inflater = activity.getMenuInflater();
    508 
    509         if (mGetContent) {
    510             inflater.inflate(R.menu.pickup, menu);
    511             int typeBits = mData.getInt(Gallery.KEY_TYPE_BITS,
    512                     DataManager.INCLUDE_IMAGE);
    513 
    514             actionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
    515         } else {
    516             inflater.inflate(R.menu.album, menu);
    517             actionBar.setTitle(mMediaSet.getName());
    518             if (mMediaSet instanceof MtpDevice) {
    519                 menu.findItem(R.id.action_slideshow).setVisible(false);
    520             } else {
    521                 menu.findItem(R.id.action_slideshow).setVisible(true);
    522             }
    523 
    524             FilterUtils.setupMenuItems(actionBar, mMediaSetPath, true);
    525 
    526             MenuItem groupBy = menu.findItem(R.id.action_group_by);
    527             if (groupBy != null) {
    528                 groupBy.setVisible(mShowClusterMenu);
    529             }
    530 
    531             MenuItem switchCamera = menu.findItem(R.id.action_camera);
    532             if (switchCamera != null) {
    533                 switchCamera.setVisible(
    534                         MediaSetUtils.isCameraSource(mMediaSetPath)
    535                         && GalleryUtils.isCameraAvailable(activity));
    536             }
    537 
    538             actionBar.setTitle(mMediaSet.getName());
    539         }
    540         actionBar.setSubtitle(null);
    541 
    542         return true;
    543     }
    544 
    545     @Override
    546     protected boolean onItemSelected(MenuItem item) {
    547         switch (item.getItemId()) {
    548             case android.R.id.home: {
    549                 onUpPressed();
    550                 return true;
    551             }
    552             case R.id.action_cancel:
    553                 mActivity.getStateManager().finishState(this);
    554                 return true;
    555             case R.id.action_select:
    556                 mSelectionManager.setAutoLeaveSelectionMode(false);
    557                 mSelectionManager.enterSelectionMode();
    558                 return true;
    559             case R.id.action_group_by: {
    560                 mActivity.getGalleryActionBar().showClusterDialog(this);
    561                 return true;
    562             }
    563             case R.id.action_slideshow: {
    564                 Bundle data = new Bundle();
    565                 data.putString(SlideshowPage.KEY_SET_PATH,
    566                         mMediaSetPath.toString());
    567                 data.putBoolean(SlideshowPage.KEY_REPEAT, true);
    568                 mActivity.getStateManager().startStateForResult(
    569                         SlideshowPage.class, REQUEST_SLIDESHOW, data);
    570                 return true;
    571             }
    572             case R.id.action_details: {
    573                 if (mShowDetails) {
    574                     hideDetails();
    575                 } else {
    576                     showDetails();
    577                 }
    578                 return true;
    579             }
    580             case R.id.action_camera: {
    581                 GalleryUtils.startCameraActivity((Activity) mActivity);
    582                 return true;
    583             }
    584             default:
    585                 return false;
    586         }
    587     }
    588 
    589     @Override
    590     protected void onStateResult(int request, int result, Intent data) {
    591         switch (request) {
    592             case REQUEST_SLIDESHOW: {
    593                 // data could be null, if there is no images in the album
    594                 if (data == null) return;
    595                 mFocusIndex = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
    596                 mSlotView.setCenterIndex(mFocusIndex);
    597                 break;
    598             }
    599             case REQUEST_PHOTO: {
    600                 if (data == null) return;
    601                 mFocusIndex = data.getIntExtra(PhotoPage.KEY_RETURN_INDEX_HINT, 0);
    602                 mSlotView.makeSlotVisible(mFocusIndex);
    603                 break;
    604             }
    605             case REQUEST_DO_ANIMATION: {
    606                 mSlotView.startRisingAnimation();
    607                 break;
    608             }
    609         }
    610     }
    611 
    612     public void onSelectionModeChange(int mode) {
    613         switch (mode) {
    614             case SelectionManager.ENTER_SELECTION_MODE: {
    615                 mActionMode = mActionModeHandler.startActionMode();
    616                 mVibrator.vibrate(100);
    617                 break;
    618             }
    619             case SelectionManager.LEAVE_SELECTION_MODE: {
    620                 mActionMode.finish();
    621                 mRootPane.invalidate();
    622                 break;
    623             }
    624             case SelectionManager.SELECT_ALL_MODE: {
    625                 mActionModeHandler.updateSupportedOperation();
    626                 mRootPane.invalidate();
    627                 break;
    628             }
    629         }
    630     }
    631 
    632     public void onSelectionChange(Path path, boolean selected) {
    633         Utils.assertTrue(mActionMode != null);
    634         int count = mSelectionManager.getSelectedCount();
    635         String format = mActivity.getResources().getQuantityString(
    636                 R.plurals.number_of_items_selected, count);
    637         mActionModeHandler.setTitle(String.format(format, count));
    638         mActionModeHandler.updateSupportedOperation(path, selected);
    639     }
    640 
    641     @Override
    642     public void onSyncDone(final MediaSet mediaSet, final int resultCode) {
    643         Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result="
    644                 + resultCode);
    645         ((Activity) mActivity).runOnUiThread(new Runnable() {
    646             @Override
    647             public void run() {
    648                 GLRoot root = mActivity.getGLRoot();
    649                 root.lockRenderThread();
    650                 try {
    651                     if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) {
    652                         mInitialSynced = true;
    653                     }
    654                     clearLoadingBit(BIT_LOADING_SYNC);
    655                     if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive
    656                             && (mAlbumDataAdapter.size() == 0)) {
    657                         // show error toast only if the album is empty
    658                         Toast.makeText((Context) mActivity, R.string.sync_album_error,
    659                                 Toast.LENGTH_LONG).show();
    660                     }
    661                 } finally {
    662                     root.unlockRenderThread();
    663                 }
    664             }
    665         });
    666     }
    667 
    668     private void setLoadingBit(int loadTaskBit) {
    669         mLoadingBits |= loadTaskBit;
    670     }
    671 
    672     private void clearLoadingBit(int loadTaskBit) {
    673         mLoadingBits &= ~loadTaskBit;
    674         if (mLoadingBits == 0 && mIsActive) {
    675             if (mAlbumDataAdapter.size() == 0) {
    676                 Toast.makeText((Context) mActivity,
    677                         R.string.empty_album, Toast.LENGTH_LONG).show();
    678                 mActivity.getStateManager().finishState(AlbumPage.this);
    679             }
    680         }
    681     }
    682 
    683     private class MyLoadingListener implements LoadingListener {
    684         @Override
    685         public void onLoadingStarted() {
    686             setLoadingBit(BIT_LOADING_RELOAD);
    687         }
    688 
    689         @Override
    690         public void onLoadingFinished() {
    691             clearLoadingBit(BIT_LOADING_RELOAD);
    692         }
    693     }
    694 
    695     private class MyDetailsSource implements DetailsHelper.DetailsSource {
    696         private int mIndex;
    697 
    698         public int size() {
    699             return mAlbumDataAdapter.size();
    700         }
    701 
    702         public int getIndex() {
    703             return mIndex;
    704         }
    705 
    706         // If requested index is out of active window, suggest a valid index.
    707         // If there is no valid index available, return -1.
    708         public int findIndex(int indexHint) {
    709             if (mAlbumDataAdapter.isActive(indexHint)) {
    710                 mIndex = indexHint;
    711             } else {
    712                 mIndex = mAlbumDataAdapter.getActiveStart();
    713                 if (!mAlbumDataAdapter.isActive(mIndex)) {
    714                     return -1;
    715                 }
    716             }
    717             return mIndex;
    718         }
    719 
    720         public MediaDetails getDetails() {
    721             MediaObject item = mAlbumDataAdapter.get(mIndex);
    722             if (item != null) {
    723                 mAlbumView.setHighlightItemPath(item.getPath());
    724                 return item.getDetails();
    725             } else {
    726                 return null;
    727             }
    728         }
    729     }
    730 }
    731