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