AlbumSetPage.java revision 4105e22d72d7a44e67cba4a58ad88018ed664d10
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.ui.SynchronizedHandler; 44import com.android.gallery3d.util.GalleryUtils; 45 46import android.app.Activity; 47import android.content.Context; 48import android.content.Intent; 49import android.graphics.Rect; 50import android.os.Bundle; 51import android.os.Message; 52import android.provider.MediaStore; 53import android.view.ActionMode; 54import android.view.Menu; 55import android.view.MenuInflater; 56import android.view.MenuItem; 57import android.view.View.MeasureSpec; 58import android.widget.Toast; 59 60public class AlbumSetPage extends ActivityState implements 61 SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner, 62 EyePosition.EyePositionListener { 63 @SuppressWarnings("unused") 64 private static final String TAG = "AlbumSetPage"; 65 66 public static final String KEY_MEDIA_PATH = "media-path"; 67 public static final String KEY_SET_TITLE = "set-title"; 68 public static final String KEY_SET_SUBTITLE = "set-subtitle"; 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 mShowClusterTabs; 81 82 protected SelectionManager mSelectionManager; 83 private AlbumSetDataAdapter mAlbumSetDataAdapter; 84 private GridDrawer mGridDrawer; 85 private HighlightDrawer mHighlightDrawer; 86 87 private boolean mGetContent; 88 private boolean mGetAlbum; 89 private ActionMode mActionMode; 90 private ActionModeHandler mActionModeHandler; 91 private DetailsHelper mDetailsHelper; 92 private MyDetailsSource mDetailsSource; 93 private boolean mShowDetails; 94 private EyePosition mEyePosition; 95 96 // The eyes' position of the user, the origin is at the center of the 97 // device and the unit is in pixels. 98 private float mX; 99 private float mY; 100 private float mZ; 101 102 private SynchronizedHandler mHandler; 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 public void onLongTap(int slotIndex) { 216 if (mGetContent || mGetAlbum) return; 217 if (mShowDetails) { 218 onSingleTapUp(slotIndex); 219 } else { 220 MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex); 221 if (set == null) return; 222 mSelectionManager.setAutoLeaveSelectionMode(true); 223 mSelectionManager.toggle(set.getPath()); 224 mDetailsSource.findIndex(slotIndex); 225 mAlbumSetView.invalidate(); 226 } 227 } 228 229 public void doCluster(int clusterType) { 230 String basePath = mMediaSet.getPath().toString(); 231 String newPath = FilterUtils.switchClusterPath(basePath, clusterType); 232 Bundle data = new Bundle(getData()); 233 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath); 234 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity)); 235 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data); 236 } 237 238 public void doFilter(int filterType) { 239 String basePath = mMediaSet.getPath().toString(); 240 String newPath = FilterUtils.switchFilterPath(basePath, filterType); 241 Bundle data = new Bundle(getData()); 242 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath); 243 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity)); 244 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data); 245 } 246 247 public void onOperationComplete() { 248 mAlbumSetView.invalidate(); 249 // TODO: enable animation 250 } 251 252 @Override 253 public void onCreate(Bundle data, Bundle restoreState) { 254 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { 255 @Override 256 public void handleMessage(Message message) { 257 Utils.assertTrue(message.what == MSG_GOTO_MANAGE_CACHE_PAGE); 258 Bundle data = new Bundle(); 259 String mediaPath = mActivity.getDataManager().getTopSetPath( 260 DataManager.INCLUDE_ALL); 261 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath); 262 mActivity.getStateManager().startState(ManageCachePage.class, data); 263 } 264 }; 265 266 initializeViews(); 267 initializeData(data); 268 mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false); 269 mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false); 270 mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE); 271 mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE); 272 mEyePosition = new EyePosition(mActivity.getAndroidContext(), this); 273 mDetailsSource = new MyDetailsSource(); 274 275 startTransition(); 276 } 277 278 @Override 279 public void onPause() { 280 super.onPause(); 281 mIsActive = false; 282 mActionModeHandler.pause(); 283 mAlbumSetDataAdapter.pause(); 284 mAlbumSetView.pause(); 285 mEyePosition.pause(); 286 DetailsHelper.pause(); 287 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 288 if (actionBar != null) actionBar.hideClusterTabs(); 289 } 290 291 @Override 292 public void onResume() { 293 super.onResume(); 294 mIsActive = true; 295 setContentPane(mRootPane); 296 mAlbumSetDataAdapter.resume(); 297 mAlbumSetView.resume(); 298 mEyePosition.resume(); 299 mActionModeHandler.resume(); 300 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 301 if (mShowClusterTabs && actionBar != null) actionBar.showClusterTabs(this); 302 } 303 304 private void initializeData(Bundle data) { 305 String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH); 306 mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath); 307 mSelectionManager.setSourceMediaSet(mMediaSet); 308 mAlbumSetDataAdapter = new AlbumSetDataAdapter( 309 mActivity, mMediaSet, DATA_CACHE_SIZE); 310 mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener()); 311 mAlbumSetView.setModel(mAlbumSetDataAdapter); 312 } 313 314 private void initializeViews() { 315 mSelectionManager = new SelectionManager(mActivity, true); 316 mSelectionManager.setSelectionListener(this); 317 mStaticBackground = new StaticBackground(mActivity.getAndroidContext()); 318 mRootPane.addComponent(mStaticBackground); 319 320 mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager); 321 Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity); 322 mAlbumSetView = new AlbumSetView(mActivity, mGridDrawer, 323 config.slotWidth, config.slotHeight, 324 config.displayItemSize, config.labelFontSize, 325 config.labelOffsetY, config.labelMargin); 326 mAlbumSetView.setListener(new SlotView.SimpleListener() { 327 @Override 328 public void onSingleTapUp(int slotIndex) { 329 AlbumSetPage.this.onSingleTapUp(slotIndex); 330 } 331 @Override 332 public void onLongTap(int slotIndex) { 333 AlbumSetPage.this.onLongTap(slotIndex); 334 } 335 }); 336 337 mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager); 338 mActionModeHandler.setActionModeListener(new ActionModeListener() { 339 public boolean onActionItemClicked(MenuItem item) { 340 return onItemSelected(item); 341 } 342 }); 343 mRootPane.addComponent(mAlbumSetView); 344 345 mStaticBackground.setImage(R.drawable.background, 346 R.drawable.background_portrait); 347 } 348 349 @Override 350 protected boolean onCreateActionBar(Menu menu) { 351 Activity activity = (Activity) mActivity; 352 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 353 MenuInflater inflater = activity.getMenuInflater(); 354 355 final boolean inAlbum = mActivity.getStateManager().hasStateClass( 356 AlbumPage.class); 357 358 if (mGetContent) { 359 inflater.inflate(R.menu.pickup, menu); 360 int typeBits = mData.getInt( 361 Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE); 362 int id = R.string.select_image; 363 if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) { 364 id = (typeBits & DataManager.INCLUDE_IMAGE) == 0 365 ? R.string.select_video 366 : R.string.select_item; 367 } 368 actionBar.setTitle(id); 369 } else if (mGetAlbum) { 370 inflater.inflate(R.menu.pickup, menu); 371 actionBar.setTitle(R.string.select_album); 372 } else { 373 mShowClusterTabs = !inAlbum; 374 inflater.inflate(R.menu.albumset, menu); 375 if (mTitle != null) { 376 actionBar.setTitle(mTitle); 377 } else { 378 actionBar.setTitle(activity.getApplicationInfo().labelRes); 379 } 380 MenuItem selectItem = menu.findItem(R.id.action_select); 381 382 if (selectItem != null) { 383 boolean selectAlbums = !inAlbum && 384 actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM; 385 if (selectAlbums) { 386 selectItem.setTitle(R.string.select_album); 387 } else { 388 selectItem.setTitle(R.string.select_group); 389 } 390 } 391 392 MenuItem switchCamera = menu.findItem(R.id.action_camera); 393 if (switchCamera != null) { 394 switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity)); 395 } 396 397 actionBar.setSubtitle(mSubtitle); 398 } 399 return true; 400 } 401 402 @Override 403 protected boolean onItemSelected(MenuItem item) { 404 Activity activity = (Activity) mActivity; 405 switch (item.getItemId()) { 406 case R.id.action_cancel: 407 activity.setResult(Activity.RESULT_CANCELED); 408 activity.finish(); 409 return true; 410 case R.id.action_select: 411 mSelectionManager.setAutoLeaveSelectionMode(false); 412 mSelectionManager.enterSelectionMode(); 413 return true; 414 case R.id.action_details: 415 if (mAlbumSetDataAdapter.size() != 0) { 416 if (mShowDetails) { 417 hideDetails(); 418 } else { 419 showDetails(); 420 } 421 } else { 422 Toast.makeText(activity, 423 activity.getText(R.string.no_albums_alert), 424 Toast.LENGTH_SHORT).show(); 425 } 426 return true; 427 case R.id.action_camera: { 428 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA) 429 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP 430 | Intent.FLAG_ACTIVITY_NEW_TASK); 431 activity.startActivity(intent); 432 return true; 433 } 434 case R.id.action_manage_offline: { 435 mHandler.sendEmptyMessage(MSG_GOTO_MANAGE_CACHE_PAGE); 436 return true; 437 } 438 case R.id.action_sync_picasa_albums: { 439 PicasaSource.requestSync(activity); 440 return true; 441 } 442 case R.id.action_settings: { 443 activity.startActivity(new Intent(activity, GallerySettings.class)); 444 return true; 445 } 446 default: 447 return false; 448 } 449 } 450 451 @Override 452 protected void onStateResult(int requestCode, int resultCode, Intent data) { 453 switch (requestCode) { 454 case REQUEST_DO_ANIMATION: { 455 startTransition(); 456 } 457 } 458 } 459 460 private void startTransition() { 461 final PositionRepository repository = 462 PositionRepository.getInstance(mActivity); 463 mAlbumSetView.startTransition(new PositionProvider() { 464 private Position mTempPosition = new Position(); 465 public Position getPosition(long identity, Position target) { 466 Position p = repository.get(identity); 467 if (p == null) { 468 p = mTempPosition; 469 p.set(target.x, target.y, 128, target.theta, 1); 470 } 471 return p; 472 } 473 }); 474 } 475 476 private String getSelectedString() { 477 GalleryActionBar actionBar = mActivity.getGalleryActionBar(); 478 int count = mSelectionManager.getSelectedCount(); 479 int action = actionBar.getClusterTypeAction(); 480 int string = action == FilterUtils.CLUSTER_BY_ALBUM 481 ? R.plurals.number_of_albums_selected 482 : R.plurals.number_of_groups_selected; 483 String format = mActivity.getResources().getQuantityString(string, count); 484 return String.format(format, count); 485 } 486 487 public void onSelectionModeChange(int mode) { 488 489 switch (mode) { 490 case SelectionManager.ENTER_SELECTION_MODE: { 491 mActivity.getGalleryActionBar().hideClusterTabs(); 492 mActionMode = mActionModeHandler.startActionMode(); 493 break; 494 } 495 case SelectionManager.LEAVE_SELECTION_MODE: { 496 mActionMode.finish(); 497 mActivity.getGalleryActionBar().showClusterTabs(this); 498 mRootPane.invalidate(); 499 break; 500 } 501 case SelectionManager.SELECT_ALL_MODE: { 502 mActionModeHandler.setTitle(getSelectedString()); 503 mRootPane.invalidate(); 504 break; 505 } 506 } 507 } 508 509 public void onSelectionChange(Path path, boolean selected) { 510 Utils.assertTrue(mActionMode != null); 511 mActionModeHandler.setTitle(getSelectedString()); 512 mActionModeHandler.updateSupportedOperation(path, selected); 513 } 514 515 private void hideDetails() { 516 mShowDetails = false; 517 mAlbumSetView.setSelectionDrawer(mGridDrawer); 518 mDetailsHelper.hide(); 519 } 520 521 private void showDetails() { 522 mShowDetails = true; 523 if (mDetailsHelper == null) { 524 mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext()); 525 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource); 526 mDetailsHelper.setCloseListener(new CloseListener() { 527 public void onClose() { 528 hideDetails(); 529 } 530 }); 531 } 532 mAlbumSetView.setSelectionDrawer(mHighlightDrawer); 533 mDetailsHelper.show(); 534 } 535 536 private class MyLoadingListener implements LoadingListener { 537 public void onLoadingStarted() { 538 GalleryUtils.setSpinnerVisibility((Activity) mActivity, true); 539 } 540 541 public void onLoadingFinished() { 542 if (!mIsActive) return; 543 GalleryUtils.setSpinnerVisibility((Activity) mActivity, false); 544 if (mAlbumSetDataAdapter.size() == 0) { 545 Toast.makeText((Context) mActivity, 546 R.string.empty_album, Toast.LENGTH_LONG).show(); 547 if (mActivity.getStateManager().getStateCount() > 1) { 548 mActivity.getStateManager().finishState(AlbumSetPage.this); 549 } 550 } 551 } 552 } 553 554 private class MyDetailsSource implements DetailsHelper.DetailsSource { 555 private int mIndex; 556 public int size() { 557 return mAlbumSetDataAdapter.size(); 558 } 559 560 public int getIndex() { 561 return mIndex; 562 } 563 564 // If requested index is out of active window, suggest a valid index. 565 // If there is no valid index available, return -1. 566 public int findIndex(int indexHint) { 567 if (mAlbumSetDataAdapter.isActive(indexHint)) { 568 mIndex = indexHint; 569 } else { 570 mIndex = mAlbumSetDataAdapter.getActiveStart(); 571 if (!mAlbumSetDataAdapter.isActive(mIndex)) { 572 return -1; 573 } 574 } 575 return mIndex; 576 } 577 578 public MediaDetails getDetails() { 579 MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex); 580 if (item != null) { 581 mHighlightDrawer.setHighlightItem(item.getPath()); 582 return item.getDetails(); 583 } else { 584 return null; 585 } 586 } 587 } 588} 589