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