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