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