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