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