AlbumSetPage.java revision 20cb268c47ce382859c6b3245e71a73e05a629d4
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.os.Bundle; 24import android.os.Vibrator; 25import android.provider.MediaStore; 26import android.view.ActionMode; 27import android.view.Menu; 28import android.view.MenuInflater; 29import android.view.MenuItem; 30import android.widget.Toast; 31 32import com.android.gallery3d.R; 33import com.android.gallery3d.common.Utils; 34import com.android.gallery3d.data.DataManager; 35import com.android.gallery3d.data.MediaDetails; 36import com.android.gallery3d.data.MediaObject; 37import com.android.gallery3d.data.MediaSet; 38import com.android.gallery3d.data.Path; 39import com.android.gallery3d.picasasource.PicasaSource; 40import com.android.gallery3d.settings.GallerySettings; 41import com.android.gallery3d.ui.ActionModeHandler; 42import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener; 43import com.android.gallery3d.ui.AlbumSetView; 44import com.android.gallery3d.ui.DetailsHelper; 45import com.android.gallery3d.ui.DetailsHelper.CloseListener; 46import com.android.gallery3d.ui.GLCanvas; 47import com.android.gallery3d.ui.GLView; 48import com.android.gallery3d.ui.GridDrawer; 49import com.android.gallery3d.ui.HighlightDrawer; 50import com.android.gallery3d.ui.PositionProvider; 51import com.android.gallery3d.ui.PositionRepository; 52import com.android.gallery3d.ui.PositionRepository.Position; 53import com.android.gallery3d.ui.SelectionManager; 54import com.android.gallery3d.ui.SlotView; 55import com.android.gallery3d.ui.StaticBackground; 56import com.android.gallery3d.util.GalleryUtils; 57 58public class AlbumSetPage extends ActivityState implements 59 SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner, 60 EyePosition.EyePositionListener { 61 @SuppressWarnings("unused") 62 private static final String TAG = "AlbumSetPage"; 63 64 public static final String KEY_MEDIA_PATH = "media-path"; 65 public static final String KEY_SET_TITLE = "set-title"; 66 public static final String KEY_SET_SUBTITLE = "set-subtitle"; 67 public static final String KEY_SELECTED_CLUSTER_TYPE = "selected-cluster"; 68 69 private static final int DATA_CACHE_SIZE = 256; 70 private static final int REQUEST_DO_ANIMATION = 1; 71 private static final int MSG_GOTO_MANAGE_CACHE_PAGE = 1; 72 73 private boolean mIsActive = false; 74 private StaticBackground mStaticBackground; 75 private AlbumSetView mAlbumSetView; 76 77 private MediaSet mMediaSet; 78 private String mSubtitle; 79 private boolean mShowClusterMenu; 80 private int mSelectedAction; 81 private Vibrator mVibrator; 82 83 protected SelectionManager mSelectionManager; 84 private AlbumSetDataAdapter mAlbumSetDataAdapter; 85 private GridDrawer mGridDrawer; 86 private HighlightDrawer mHighlightDrawer; 87 88 private boolean mGetContent; 89 private boolean mGetAlbum; 90 private ActionMode mActionMode; 91 private ActionModeHandler mActionModeHandler; 92 private DetailsHelper mDetailsHelper; 93 private MyDetailsSource mDetailsSource; 94 private boolean mShowDetails; 95 private EyePosition mEyePosition; 96 97 // The eyes' position of the user, the origin is at the center of the 98 // device and the unit is in pixels. 99 private float mX; 100 private float mY; 101 private float mZ; 102 103 private GLView mRootPane = new GLView() { 104 private float mMatrix[] = new float[16]; 105 106 @Override 107 protected void onLayout( 108 boolean changed, int left, int top, int right, int bottom) { 109 mStaticBackground.layout(0, 0, right - left, bottom - top); 110 mEyePosition.resetPosition(); 111 112 int slotViewTop = GalleryActionBar.getHeight((Activity) mActivity); 113 int slotViewBottom = bottom - top; 114 int slotViewRight = right - left; 115 116 if (mShowDetails) { 117 mDetailsHelper.layout(left, slotViewTop, right, bottom); 118 } else { 119 mAlbumSetView.setSelectionDrawer(mGridDrawer); 120 } 121 122 mAlbumSetView.layout(0, slotViewTop, slotViewRight, slotViewBottom); 123 PositionRepository.getInstance(mActivity).setOffset( 124 0, slotViewTop); 125 } 126 127 @Override 128 protected void render(GLCanvas canvas) { 129 canvas.save(GLCanvas.SAVE_FLAG_MATRIX); 130 GalleryUtils.setViewPointMatrix(mMatrix, 131 getWidth() / 2 + mX, getHeight() / 2 + mY, mZ); 132 canvas.multiplyMatrix(mMatrix, 0); 133 super.render(canvas); 134 canvas.restore(); 135 } 136 }; 137 138 @Override 139 public void onEyePositionChanged(float x, float y, float z) { 140 mRootPane.lockRendering(); 141 mX = x; 142 mY = y; 143 mZ = z; 144 mRootPane.unlockRendering(); 145 mRootPane.invalidate(); 146 } 147 148 @Override 149 public void onBackPressed() { 150 if (mShowDetails) { 151 hideDetails(); 152 } else if (mSelectionManager.inSelectionMode()) { 153 mSelectionManager.leaveSelectionMode(); 154 } else { 155 mAlbumSetView.savePositions( 156 PositionRepository.getInstance(mActivity)); 157 super.onBackPressed(); 158 } 159 } 160 161 private void savePositions(int slotIndex, int center[]) { 162 Rect offset = new Rect(); 163 mRootPane.getBoundsOf(mAlbumSetView, offset); 164 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity)); 165 Rect r = mAlbumSetView.getSlotRect(slotIndex); 166 int scrollX = mAlbumSetView.getScrollX(); 167 int scrollY = mAlbumSetView.getScrollY(); 168 center[0] = offset.left + (r.left + r.right) / 2 - scrollX; 169 center[1] = offset.top + (r.top + r.bottom) / 2 - scrollY; 170 } 171 172 public void onSingleTapUp(int slotIndex) { 173 MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex); 174 if (targetSet == null) return; // Content is dirty, we shall reload soon 175 176 if (mShowDetails) { 177 Path path = targetSet.getPath(); 178 mHighlightDrawer.setHighlightItem(path); 179 mDetailsHelper.reloadDetails(slotIndex); 180 } else if (!mSelectionManager.inSelectionMode()) { 181 Bundle data = new Bundle(getData()); 182 String mediaPath = targetSet.getPath().toString(); 183 int[] center = new int[2]; 184 savePositions(slotIndex, center); 185 data.putIntArray(AlbumPage.KEY_SET_CENTER, center); 186 if (mGetAlbum && targetSet.isLeafAlbum()) { 187 Activity activity = (Activity) mActivity; 188 Intent result = new Intent() 189 .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString()); 190 activity.setResult(Activity.RESULT_OK, result); 191 activity.finish(); 192 } else if (targetSet.getSubMediaSetCount() > 0) { 193 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath); 194 mActivity.getStateManager().startStateForResult( 195 AlbumSetPage.class, REQUEST_DO_ANIMATION, data); 196 } else { 197 if (!mGetContent && (targetSet.getSupportedOperations() 198 & MediaObject.SUPPORT_IMPORT) != 0) { 199 data.putBoolean(AlbumPage.KEY_AUTO_SELECT_ALL, true); 200 } 201 data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath); 202 boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class); 203 // We only show cluster menu in the first AlbumPage in stack 204 data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum); 205 mActivity.getStateManager().startStateForResult( 206 AlbumPage.class, REQUEST_DO_ANIMATION, data); 207 } 208 } else { 209 mSelectionManager.toggle(targetSet.getPath()); 210 mAlbumSetView.invalidate(); 211 } 212 } 213 214 private void onDown(int index) { 215 MediaSet set = mAlbumSetDataAdapter.getMediaSet(index); 216 Path path = (set == null) ? null : set.getPath(); 217 mSelectionManager.setPressedPath(path); 218 mAlbumSetView.invalidate(); 219 } 220 221 private void onUp() { 222 mSelectionManager.setPressedPath(null); 223 mAlbumSetView.invalidate(); 224 } 225 226 public void onLongTap(int slotIndex) { 227 if (mGetContent || mGetAlbum) return; 228 if (mShowDetails) { 229 onSingleTapUp(slotIndex); 230 } else { 231 MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex); 232 if (set == null) return; 233 mSelectionManager.setAutoLeaveSelectionMode(true); 234 mSelectionManager.toggle(set.getPath()); 235 mDetailsSource.findIndex(slotIndex); 236 mAlbumSetView.invalidate(); 237 } 238 } 239 240 public void doCluster(int clusterType) { 241 Log.v(TAG, "doCluster: " + clusterType); 242 String basePath = mMediaSet.getPath().toString(); 243 String newPath = FilterUtils.switchClusterPath(basePath, clusterType); 244 Bundle data = new Bundle(getData()); 245 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath); 246 data.putInt(KEY_SELECTED_CLUSTER_TYPE, clusterType); 247 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity)); 248 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data); 249 } 250 251 public void doFilter(int filterType) { 252 String basePath = mMediaSet.getPath().toString(); 253 String newPath = FilterUtils.switchFilterPath(basePath, filterType); 254 Bundle data = new Bundle(getData()); 255 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath); 256 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity)); 257 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data); 258 } 259 260 public void onOperationComplete() { 261 mAlbumSetView.invalidate(); 262 // TODO: enable animation 263 } 264 265 @Override 266 public void onCreate(Bundle data, Bundle restoreState) { 267 initializeViews(); 268 initializeData(data); 269 Context context = mActivity.getAndroidContext(); 270 mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false); 271 mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false); 272 mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE); 273 mEyePosition = new EyePosition(context, this); 274 mDetailsSource = new MyDetailsSource(); 275 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); 276 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 277 if (actionBar != null) { 278 mSelectedAction = data.getInt( 279 AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE, FilterUtils.CLUSTER_BY_ALBUM); 280 } 281 startTransition(); 282 } 283 284 @Override 285 public void onPause() { 286 super.onPause(); 287 Log.v(TAG, "onPause"); 288 mIsActive = false; 289 mActionModeHandler.pause(); 290 mAlbumSetDataAdapter.pause(); 291 mAlbumSetView.pause(); 292 mEyePosition.pause(); 293 DetailsHelper.pause(); 294 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 295 if (actionBar != null) actionBar.hideClusterMenu(); 296 } 297 298 @Override 299 public void onResume() { 300 super.onResume(); 301 Log.v(TAG, "onResume"); 302 mIsActive = true; 303 setContentPane(mRootPane); 304 mAlbumSetDataAdapter.resume(); 305 mAlbumSetView.resume(); 306 mEyePosition.resume(); 307 mActionModeHandler.resume(); 308 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 309 if (mShowClusterMenu && actionBar != null) actionBar.showClusterMenu(mSelectedAction, this); 310 } 311 312 private void initializeData(Bundle data) { 313 String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH); 314 mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath); 315 mSelectionManager.setSourceMediaSet(mMediaSet); 316 mAlbumSetDataAdapter = new AlbumSetDataAdapter( 317 mActivity, mMediaSet, DATA_CACHE_SIZE); 318 mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener()); 319 mAlbumSetView.setModel(mAlbumSetDataAdapter); 320 } 321 322 private void initializeViews() { 323 mSelectionManager = new SelectionManager(mActivity, true); 324 mSelectionManager.setSelectionListener(this); 325 mStaticBackground = new StaticBackground(mActivity.getAndroidContext()); 326 mRootPane.addComponent(mStaticBackground); 327 328 mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager); 329 Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity); 330 mAlbumSetView = new AlbumSetView(mActivity, mGridDrawer, 331 config.slotViewSpec, config.labelSpec); 332 mAlbumSetView.setListener(new SlotView.SimpleListener() { 333 @Override 334 public void onDown(int index) { 335 AlbumSetPage.this.onDown(index); 336 } 337 338 @Override 339 public void onUp() { 340 AlbumSetPage.this.onUp(); 341 } 342 343 @Override 344 public void onSingleTapUp(int slotIndex) { 345 AlbumSetPage.this.onSingleTapUp(slotIndex); 346 } 347 348 @Override 349 public void onLongTap(int slotIndex) { 350 AlbumSetPage.this.onLongTap(slotIndex); 351 } 352 }); 353 354 mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager); 355 mActionModeHandler.setActionModeListener(new ActionModeListener() { 356 public boolean onActionItemClicked(MenuItem item) { 357 return onItemSelected(item); 358 } 359 }); 360 mRootPane.addComponent(mAlbumSetView); 361 362 mStaticBackground.setImage(R.drawable.background, 363 R.drawable.background_portrait); 364 } 365 366 @Override 367 protected boolean onCreateActionBar(Menu menu) { 368 Activity activity = (Activity) mActivity; 369 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 370 MenuInflater inflater = activity.getMenuInflater(); 371 372 final boolean inAlbum = mActivity.getStateManager().hasStateClass( 373 AlbumPage.class); 374 375 if (mGetContent) { 376 inflater.inflate(R.menu.pickup, menu); 377 int typeBits = mData.getInt( 378 Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE); 379 int id = R.string.select_image; 380 if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) { 381 id = (typeBits & DataManager.INCLUDE_IMAGE) == 0 382 ? R.string.select_video 383 : R.string.select_item; 384 } 385 actionBar.setTitle(id); 386 } else if (mGetAlbum) { 387 inflater.inflate(R.menu.pickup, menu); 388 actionBar.setTitle(R.string.select_album); 389 } else { 390 mShowClusterMenu = !inAlbum; 391 inflater.inflate(R.menu.albumset, menu); 392 actionBar.setTitle(null); 393 MenuItem selectItem = menu.findItem(R.id.action_select); 394 395 if (selectItem != null) { 396 boolean selectAlbums = !inAlbum && 397 actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM; 398 if (selectAlbums) { 399 selectItem.setTitle(R.string.select_album); 400 } else { 401 selectItem.setTitle(R.string.select_group); 402 } 403 } 404 405 MenuItem switchCamera = menu.findItem(R.id.action_camera); 406 if (switchCamera != null) { 407 switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity)); 408 } 409 410 actionBar.setSubtitle(mSubtitle); 411 } 412 return true; 413 } 414 415 @Override 416 protected boolean onItemSelected(MenuItem item) { 417 Activity activity = (Activity) mActivity; 418 switch (item.getItemId()) { 419 case R.id.action_cancel: 420 activity.setResult(Activity.RESULT_CANCELED); 421 activity.finish(); 422 return true; 423 case R.id.action_select: 424 mSelectionManager.setAutoLeaveSelectionMode(false); 425 mSelectionManager.enterSelectionMode(); 426 return true; 427 case R.id.action_details: 428 if (mAlbumSetDataAdapter.size() != 0) { 429 if (mShowDetails) { 430 hideDetails(); 431 } else { 432 showDetails(); 433 } 434 } else { 435 Toast.makeText(activity, 436 activity.getText(R.string.no_albums_alert), 437 Toast.LENGTH_SHORT).show(); 438 } 439 return true; 440 case R.id.action_camera: { 441 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA) 442 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP 443 | Intent.FLAG_ACTIVITY_NEW_TASK); 444 activity.startActivity(intent); 445 return true; 446 } 447 case R.id.action_manage_offline: { 448 Bundle data = new Bundle(); 449 String mediaPath = mActivity.getDataManager().getTopSetPath( 450 DataManager.INCLUDE_ALL); 451 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath); 452 mActivity.getStateManager().startState(ManageCachePage.class, data); 453 return true; 454 } 455 case R.id.action_sync_picasa_albums: { 456 PicasaSource.requestSync(activity); 457 return true; 458 } 459 case R.id.action_settings: { 460 activity.startActivity(new Intent(activity, GallerySettings.class)); 461 return true; 462 } 463 default: 464 return false; 465 } 466 } 467 468 @Override 469 protected void onStateResult(int requestCode, int resultCode, Intent data) { 470 switch (requestCode) { 471 case REQUEST_DO_ANIMATION: { 472 startTransition(); 473 } 474 } 475 } 476 477 private void startTransition() { 478 final PositionRepository repository = 479 PositionRepository.getInstance(mActivity); 480 mAlbumSetView.startTransition(new PositionProvider() { 481 private Position mTempPosition = new Position(); 482 public Position getPosition(long identity, Position target) { 483 Position p = repository.get(identity); 484 if (p == null) { 485 p = mTempPosition; 486 p.set(target.x, target.y, 128, target.theta, 1); 487 } 488 return p; 489 } 490 }); 491 } 492 493 private String getSelectedString() { 494 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 495 int count = mSelectionManager.getSelectedCount(); 496 int action = actionBar.getClusterTypeAction(); 497 int string = action == FilterUtils.CLUSTER_BY_ALBUM 498 ? R.plurals.number_of_albums_selected 499 : R.plurals.number_of_groups_selected; 500 String format = mActivity.getResources().getQuantityString(string, count); 501 return String.format(format, count); 502 } 503 504 public void onSelectionModeChange(int mode) { 505 506 switch (mode) { 507 case SelectionManager.ENTER_SELECTION_MODE: { 508 mActivity.getGalleryActionBar().hideClusterMenu(); 509 mActionMode = mActionModeHandler.startActionMode(); 510 mVibrator.vibrate(100); 511 break; 512 } 513 case SelectionManager.LEAVE_SELECTION_MODE: { 514 mActionMode.finish(); 515 mActivity.getGalleryActionBar().showClusterMenu(mSelectedAction, this); 516 mRootPane.invalidate(); 517 break; 518 } 519 case SelectionManager.SELECT_ALL_MODE: { 520 mActionModeHandler.setTitle(getSelectedString()); 521 mRootPane.invalidate(); 522 break; 523 } 524 } 525 } 526 527 public void onSelectionChange(Path path, boolean selected) { 528 Utils.assertTrue(mActionMode != null); 529 mActionModeHandler.setTitle(getSelectedString()); 530 mActionModeHandler.updateSupportedOperation(path, selected); 531 } 532 533 private void hideDetails() { 534 mShowDetails = false; 535 mDetailsHelper.hide(); 536 mAlbumSetView.setSelectionDrawer(mGridDrawer); 537 mAlbumSetView.invalidate(); 538 } 539 540 private void showDetails() { 541 mShowDetails = true; 542 if (mDetailsHelper == null) { 543 mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(), 544 mSelectionManager); 545 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource); 546 mDetailsHelper.setCloseListener(new CloseListener() { 547 public void onClose() { 548 hideDetails(); 549 } 550 }); 551 } 552 mAlbumSetView.setSelectionDrawer(mHighlightDrawer); 553 mDetailsHelper.show(); 554 } 555 556 private class MyLoadingListener implements LoadingListener { 557 public void onLoadingStarted() { 558 GalleryUtils.setSpinnerVisibility((Activity) mActivity, true); 559 } 560 561 public void onLoadingFinished() { 562 if (!mIsActive) return; 563 GalleryUtils.setSpinnerVisibility((Activity) mActivity, false); 564 if (mAlbumSetDataAdapter.size() == 0) { 565 Toast.makeText((Context) mActivity, 566 R.string.empty_album, Toast.LENGTH_LONG).show(); 567 if (mActivity.getStateManager().getStateCount() > 1) { 568 mActivity.getStateManager().finishState(AlbumSetPage.this); 569 } 570 } 571 } 572 } 573 574 private class MyDetailsSource implements DetailsHelper.DetailsSource { 575 private int mIndex; 576 public int size() { 577 return mAlbumSetDataAdapter.size(); 578 } 579 580 public int getIndex() { 581 return mIndex; 582 } 583 584 // If requested index is out of active window, suggest a valid index. 585 // If there is no valid index available, return -1. 586 public int findIndex(int indexHint) { 587 if (mAlbumSetDataAdapter.isActive(indexHint)) { 588 mIndex = indexHint; 589 } else { 590 mIndex = mAlbumSetDataAdapter.getActiveStart(); 591 if (!mAlbumSetDataAdapter.isActive(mIndex)) { 592 return -1; 593 } 594 } 595 return mIndex; 596 } 597 598 public MediaDetails getDetails() { 599 MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex); 600 if (item != null) { 601 mHighlightDrawer.setHighlightItem(item.getPath()); 602 return item.getDetails(); 603 } else { 604 return null; 605 } 606 } 607 } 608} 609