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