• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onBackPressed()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 
onUpPressed()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 
onDown(int index)223     private void onDown(int index) {
224         mAlbumView.setPressedIndex(index);
225     }
226 
onUp(boolean followedByLongPress)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 
onSingleTapUp(int slotIndex)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 
pickPhoto(int slotIndex)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 
getSlotRect(int slotIndex)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 
onGetContent(final MediaItem item)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 
onLongTap(int slotIndex)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
doCluster(int clusterType)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
onCreate(Bundle data, Bundle restoreState)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
onResume()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
onPause()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
onDestroy()426     protected void onDestroy() {
427         super.onDestroy();
428         if (mAlbumDataAdapter != null) {
429             mAlbumDataAdapter.setLoadingListener(null);
430         }
431     }
432 
initializeViews()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 
initializeData(Bundle data)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 
showDetails()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 
hideDetails()496     private void hideDetails() {
497         mShowDetails = false;
498         mDetailsHelper.hide();
499         mAlbumView.setHighlightItemPath(null);
500         mSlotView.invalidate();
501     }
502 
503     @Override
onCreateActionBar(Menu menu)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
onItemSelected(MenuItem item)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
onStateResult(int request, int result, Intent data)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 
onSelectionModeChange(int mode)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 
onSelectionChange(Path path, boolean selected)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
onSyncDone(final MediaSet mediaSet, final int resultCode)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 
setLoadingBit(int loadTaskBit)668     private void setLoadingBit(int loadTaskBit) {
669         mLoadingBits |= loadTaskBit;
670     }
671 
clearLoadingBit(int loadTaskBit)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
onLoadingStarted()685         public void onLoadingStarted() {
686             setLoadingBit(BIT_LOADING_RELOAD);
687         }
688 
689         @Override
onLoadingFinished()690         public void onLoadingFinished() {
691             clearLoadingBit(BIT_LOADING_RELOAD);
692         }
693     }
694 
695     private class MyDetailsSource implements DetailsHelper.DetailsSource {
696         private int mIndex;
697 
size()698         public int size() {
699             return mAlbumDataAdapter.size();
700         }
701 
getIndex()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.
findIndex(int indexHint)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 
getDetails()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