1/* 2 * Copyright (C) 2009 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.cooliris.media; 18 19import java.util.ArrayList; 20import javax.microedition.khronos.opengles.GL11; 21 22import android.hardware.SensorEvent; 23import android.opengl.GLU; 24import android.os.PowerManager; 25import android.os.PowerManager.WakeLock; 26import android.util.Log; 27import android.view.KeyEvent; 28import android.view.MotionEvent; 29import android.content.Context; 30 31import com.cooliris.app.App; 32import com.cooliris.app.Res; 33 34public final class GridLayer extends RootLayer implements MediaFeed.Listener, TimeBar.Listener { 35 private static final String TAG = "GridLayer"; 36 public static final int STATE_MEDIA_SETS = 0; 37 public static final int STATE_GRID_VIEW = 1; 38 public static final int STATE_FULL_SCREEN = 2; 39 public static final int STATE_TIMELINE = 3; 40 41 public static final int ANCHOR_LEFT = 0; 42 public static final int ANCHOR_RIGHT = 1; 43 public static final int ANCHOR_CENTER = 2; 44 45 public static final int MAX_ITEMS_PER_SLOT = 32; 46 public static final int MAX_DISPLAYED_ITEMS_PER_SLOT = 4; 47 public static final int MAX_DISPLAYED_ITEMS_PER_FOCUSED_SLOT = 32; 48 public static final int MAX_DISPLAY_SLOTS = 96; 49 public static final int MAX_ITEMS_DRAWABLE = MAX_ITEMS_PER_SLOT * MAX_DISPLAY_SLOTS; 50 51 private static final float SLIDESHOW_TRANSITION_TIME = 3.5f; 52 53 private HudLayer mHud; 54 private int mState; 55 private final IndexRange mBufferedVisibleRange = new IndexRange(); 56 private final IndexRange mVisibleRange = new IndexRange(); 57 private final IndexRange mPreviousDataRange = new IndexRange(); 58 private final IndexRange mCompleteRange = new IndexRange(); 59 60 private final Pool<Vector3f> mTempVec; 61 private final Pool<Vector3f> mTempVecAlt; 62 63 private final ArrayList<MediaItem> mTempList = new ArrayList<MediaItem>(); 64 private final MediaItem[] mTempHash = new MediaItem[64]; 65 66 private final Vector3f mDeltaAnchorPositionUncommited = new Vector3f(); 67 private Vector3f mDeltaAnchorPosition = new Vector3f(); 68 69 // The display primitives. 70 final private GridDrawables mDrawables; 71 private float mSelectedAlpha = 0.0f; 72 private float mTargetAlpha = 0.0f; 73 74 final private GridCamera mCamera; 75 final private GridCameraManager mCameraManager; 76 final private GridDrawManager mDrawManager; 77 final private GridInputProcessor mInputProcessor; 78 79 private boolean mFeedAboutToChange; 80 private boolean mPerformingLayoutChange; 81 private boolean mFeedChanged; 82 83 private final LayoutInterface mLayoutInterface; 84 private static final LayoutInterface sfullScreenLayoutInterface = new GridLayoutInterface(1); 85 86 private MediaFeed mMediaFeed; 87 private boolean mInAlbum = false; 88 private int mCurrentExpandedSlot; 89 90 private final DisplayList mDisplayList = new DisplayList(); 91 private final DisplayItem[] mDisplayItems = new DisplayItem[MAX_ITEMS_DRAWABLE]; 92 private final DisplaySlot[] mDisplaySlots = new DisplaySlot[MAX_DISPLAY_SLOTS]; 93 private ArrayList<MediaItem> mVisibleItems; 94 95 private final BackgroundLayer mBackground; 96 private boolean mLocationFilter; 97 private float mZoomValue = 1.0f; 98 private float mCurrentFocusItemWidth = 1.0f; 99 private float mCurrentFocusItemHeight = 1.0f; 100 private float mTimeElapsedSinceGridViewReady = 0.0f; 101 102 private boolean mSlideshowMode; 103 private boolean mNoDeleteMode = false; 104 private float mTimeElapsedSinceView; 105 private final MediaBucketList mSelectedBucketList = new MediaBucketList(); 106 private final MediaBucketList mMarkedBucketList = new MediaBucketList(); 107 private float mTimeElapsedSinceStackViewReady; 108 109 private Context mContext; 110 private RenderView mView; 111 private boolean mPickIntent; 112 private boolean mViewIntent; 113 private WakeLock mWakeLock; 114 private int mStartMemoryRange; 115 private int mFramesDirty; 116 private String mRequestFocusContentUri; 117 private int mFrameCount; 118 private boolean mRequestToEnterSelection; 119 120 // private ArrayList<Integer> mBreakSlots = new ArrayList<Integer>(); 121 // private ArrayList<Integer> mOldBreakSlots; 122 // private LongSparseArray<Integer> mBreakSlots = new 123 // LongSparseArray<Integer>(); 124 // private LongSparseArray<Integer> mOldBreakSlots; 125 126 public GridLayer(Context context, int itemWidth, int itemHeight, LayoutInterface layoutInterface, RenderView view) { 127 mBackground = new BackgroundLayer(this); 128 mContext = context; 129 mView = view; 130 131 Vector3f[] vectorPool = new Vector3f[128]; 132 int length = vectorPool.length; 133 for (int i = 0; i < length; ++i) { 134 vectorPool[i] = new Vector3f(); 135 } 136 Vector3f[] vectorPoolRenderThread = new Vector3f[128]; 137 length = vectorPoolRenderThread.length; 138 for (int i = 0; i < length; ++i) { 139 vectorPoolRenderThread[i] = new Vector3f(); 140 } 141 mTempVec = new Pool<Vector3f>(vectorPool); 142 mTempVecAlt = new Pool<Vector3f>(vectorPoolRenderThread); 143 144 DisplaySlot[] displaySlots = mDisplaySlots; 145 for (int i = 0; i < MAX_DISPLAY_SLOTS; ++i) { 146 DisplaySlot slot = new DisplaySlot(); 147 displaySlots[i] = slot; 148 } 149 mLayoutInterface = layoutInterface; 150 mCamera = new GridCamera(0, 0, itemWidth, itemHeight); 151 mDrawables = new GridDrawables(itemWidth, itemHeight); 152 mBufferedVisibleRange.set(Shared.INVALID, Shared.INVALID); 153 mVisibleRange.set(Shared.INVALID, Shared.INVALID); 154 mCompleteRange.set(Shared.INVALID, Shared.INVALID); 155 mPreviousDataRange.set(Shared.INVALID, Shared.INVALID); 156 mDeltaAnchorPosition.set(0, 0, 0); 157 mDeltaAnchorPositionUncommited.set(0, 0, 0); 158 mSelectedBucketList.clear(); 159 160 mVisibleItems = new ArrayList<MediaItem>(); 161 mHud = new HudLayer(context); 162 mHud.setContext(context); 163 mHud.setGridLayer(this); 164 mHud.getPathBar().clear(); 165 mHud.setGridLayer(this); 166 mHud.getTimeBar().setListener(this); 167 mHud.getPathBar().pushLabel(Res.drawable.icon_home_small, context.getResources().getString(Res.string.app_name), 168 new Runnable() { 169 public void run() { 170 if (mHud.getAlpha() == 1.0f) { 171 if (!mFeedAboutToChange) { 172 setState(STATE_MEDIA_SETS); 173 } 174 } else { 175 mHud.setAlpha(1.0f); 176 } 177 } 178 }); 179 mCameraManager = new GridCameraManager(mCamera); 180 mDrawManager = new GridDrawManager(context, mCamera, mDrawables, mDisplayList, mDisplayItems, mDisplaySlots); 181 mInputProcessor = new GridInputProcessor(context, mCamera, this, mView, mTempVec, mDisplayItems); 182 setState(STATE_MEDIA_SETS); 183 } 184 185 public HudLayer getHud() { 186 return mHud; 187 } 188 189 public void shutdown() { 190 if (mMediaFeed != null) { 191 mMediaFeed.shutdown(); 192 } 193 mContext = null; 194 mSelectedBucketList.clear(); 195 mView = null; 196 } 197 198 public void stop() { 199 endSlideshow(); 200 mBackground.clear(); 201 handleLowMemory(); 202 } 203 204 @Override 205 public void generate(RenderView view, RenderView.Lists lists) { 206 lists.updateList.add(this); 207 lists.opaqueList.add(this); 208 mBackground.generate(view, lists); 209 lists.blendedList.add(this); 210 lists.hitTestList.add(this); 211 mHud.generate(view, lists); 212 } 213 214 @Override 215 protected void onSizeChanged() { 216 mHud.setSize(mWidth, mHeight); 217 mHud.setAlpha(1.0f); 218 mBackground.setSize(mWidth, mHeight); 219 if (mView != null) { 220 mView.requestRender(); 221 } 222 } 223 224 public int getState() { 225 return mState; 226 } 227 228 public void setState(int state) { 229 boolean feedUnchanged = false; 230 mCamera.mFriction = 0.0f; 231 if (mState == state) { 232 feedUnchanged = true; 233 } 234 GridLayoutInterface layoutInterface = (GridLayoutInterface) mLayoutInterface; 235 GridLayoutInterface oldLayout = (GridLayoutInterface) sfullScreenLayoutInterface; 236 oldLayout.mNumRows = layoutInterface.mNumRows; 237 oldLayout.mSpacingX = layoutInterface.mSpacingX; 238 oldLayout.mSpacingY = layoutInterface.mSpacingY; 239 GridCamera camera = mCamera; 240 int numMaxRows = (camera.mHeight >= camera.mWidth) ? 4 : 3; 241 MediaFeed feed = mMediaFeed; 242 boolean performLayout = true; 243 mZoomValue = 1.0f; 244 float yStretch = camera.mDefaultAspectRatio / camera.mAspectRatio; 245 if (yStretch < 1.0f) { 246 yStretch = 1.0f; 247 } 248 switch (state) { 249 case STATE_GRID_VIEW: 250 mTimeElapsedSinceGridViewReady = 0.0f; 251 if (feed != null && feedUnchanged == false) { 252 boolean updatedData = feed.restorePreviousClusteringState(); 253 if (updatedData) { 254 performLayout = false; 255 } 256 } 257 layoutInterface.mNumRows = numMaxRows; 258 layoutInterface.mSpacingX = (int) (10 * App.PIXEL_DENSITY); 259 layoutInterface.mSpacingY = (int) (10 * App.PIXEL_DENSITY); 260 if (mState == STATE_MEDIA_SETS) { 261 // Entering album. 262 mInAlbum = true; 263 MediaSet set = feed.getCurrentSet(); 264 int icon = mDrawables.getIconForSet(set, true); 265 if (set != null) { 266 mHud.getPathBar().pushLabel(icon, set.mNoCountTitleString, new Runnable() { 267 public void run() { 268 if (mFeedAboutToChange) { 269 return; 270 } 271 if (mHud.getAlpha() == 1.0f) { 272 disableLocationFiltering(); 273 mInputProcessor.clearSelection(); 274 setState(STATE_GRID_VIEW); 275 } else { 276 mHud.setAlpha(1.0f); 277 } 278 } 279 }); 280 } 281 } 282 if (mState == STATE_FULL_SCREEN) { 283 mHud.getPathBar().popLabel(); 284 } 285 break; 286 case STATE_TIMELINE: 287 mTimeElapsedSinceStackViewReady = 0.0f; 288 if (feed != null && feedUnchanged == false) { 289 feed.performClustering(); 290 performLayout = false; 291 } 292 disableLocationFiltering(); 293 layoutInterface.mNumRows = numMaxRows - 1; 294 layoutInterface.mSpacingX = (int) (100 * App.PIXEL_DENSITY); 295 layoutInterface.mSpacingY = (int) (70 * App.PIXEL_DENSITY * yStretch); 296 break; 297 case STATE_FULL_SCREEN: 298 layoutInterface.mNumRows = 1; 299 layoutInterface.mSpacingX = (int) (40 * App.PIXEL_DENSITY); 300 layoutInterface.mSpacingY = (int) (40 * App.PIXEL_DENSITY); 301 if (mState != STATE_FULL_SCREEN) { 302 mHud.getPathBar().pushLabel(Res.drawable.ic_fs_details, "", new Runnable() { 303 public void run() { 304 if (mHud.getAlpha() == 1.0f) { 305 mHud.swapFullscreenLabel(); 306 } 307 mHud.setAlpha(1.0f); 308 } 309 }); 310 } 311 break; 312 case STATE_MEDIA_SETS: 313 mTimeElapsedSinceStackViewReady = 0.0f; 314 if (feed != null && feedUnchanged == false) { 315 feed.restorePreviousClusteringState(); 316 mMarkedBucketList.clear(); 317 feed.expandMediaSet(Shared.INVALID); 318 performLayout = false; 319 } 320 disableLocationFiltering(); 321 mInputProcessor.clearSelection(); 322 layoutInterface.mNumRows = numMaxRows - 1; 323 layoutInterface.mSpacingX = (int) (100 * App.PIXEL_DENSITY); 324 layoutInterface.mSpacingY = (int) (70 * App.PIXEL_DENSITY * yStretch); 325 if (mInAlbum) { 326 if (mState == STATE_FULL_SCREEN) { 327 mHud.getPathBar().popLabel(); 328 } 329 mHud.getPathBar().popLabel(); 330 mInAlbum = false; 331 } 332 break; 333 } 334 mState = state; 335 mHud.onGridStateChanged(); 336 if (performLayout && mFeedAboutToChange == false) { 337 onLayout(Shared.INVALID, Shared.INVALID, oldLayout); 338 } 339 if (state != STATE_FULL_SCREEN) { 340 mCamera.moveYTo(0); 341 mCamera.moveZTo(0); 342 } 343 } 344 345 protected void enableLocationFiltering(String label) { 346 if (mLocationFilter == false) { 347 mLocationFilter = true; 348 mHud.getPathBar().pushLabel(Res.drawable.icon_location_small, label, new Runnable() { 349 public void run() { 350 if (mHud.getAlpha() == 1.0f) { 351 if (mState == STATE_FULL_SCREEN) { 352 mInputProcessor.clearSelection(); 353 setState(STATE_GRID_VIEW); 354 } else { 355 disableLocationFiltering(); 356 } 357 } else { 358 mHud.setAlpha(1.0f); 359 } 360 } 361 }); 362 } 363 } 364 365 protected void disableLocationFiltering() { 366 if (mLocationFilter) { 367 mLocationFilter = false; 368 mMediaFeed.removeFilter(); 369 mHud.getPathBar().popLabel(); 370 } 371 } 372 373 boolean goBack() { 374 if (mFeedAboutToChange) { 375 return false; 376 } 377 int state = mState; 378 if (mInputProcessor.getCurrentSelectedSlot() == Shared.INVALID) { 379 if (mLocationFilter) { 380 disableLocationFiltering(); 381 setState(STATE_TIMELINE); 382 return true; 383 } 384 } 385 switch (state) { 386 case STATE_GRID_VIEW: 387 setState(STATE_MEDIA_SETS); 388 break; 389 case STATE_TIMELINE: 390 setState(STATE_GRID_VIEW); 391 break; 392 case STATE_FULL_SCREEN: 393 setState(STATE_GRID_VIEW); 394 mInputProcessor.clearSelection(); 395 break; 396 default: 397 return false; 398 } 399 return true; 400 } 401 402 public void endSlideshow() { 403 mSlideshowMode = false; 404 if (mWakeLock != null) { 405 if (mWakeLock.isHeld()) { 406 mWakeLock.release(); 407 } 408 mWakeLock = null; 409 } 410 mHud.setAlpha(1.0f); 411 } 412 413 @Override 414 public void onSensorChanged(RenderView view, SensorEvent event) { 415 mInputProcessor.onSensorChanged(view, event, mState); 416 } 417 418 public DataSource getDataSource() { 419 if (mMediaFeed != null) 420 return mMediaFeed.getDataSource(); 421 return null; 422 } 423 424 public void setDataSource(DataSource dataSource) { 425 MediaFeed feed = mMediaFeed; 426 mMediaFeed = new MediaFeed(mContext, dataSource, this); 427 if (feed != null) { 428 // Restore the slot state in the original feed before shutting it down. 429 mMediaFeed.copySlotStateFrom(feed); 430 431 feed.shutdown(); 432 mDisplayList.clear(); 433 mBackground.clear(); 434 } 435 436 mMediaFeed.start(); 437 } 438 439 public IndexRange getVisibleRange() { 440 return mVisibleRange; 441 } 442 443 public IndexRange getBufferedVisibleRange() { 444 return mBufferedVisibleRange; 445 } 446 447 public IndexRange getCompleteRange() { 448 return mCompleteRange; 449 } 450 451 private int hitTest(Vector3f worldPos, int itemWidth, int itemHeight) { 452 int retVal = Shared.INVALID; 453 int firstSlotIndex = 0; 454 int lastSlotIndex = 0; 455 IndexRange rangeToUse = mVisibleRange; 456 synchronized (rangeToUse) { 457 firstSlotIndex = rangeToUse.begin; 458 lastSlotIndex = rangeToUse.end; 459 } 460 Pool<Vector3f> pool = mTempVec; 461 float itemWidthBy2 = itemWidth * 0.5f; 462 float itemHeightBy2 = itemHeight * 0.5f; 463 Vector3f position = pool.create(); 464 Vector3f deltaAnchorPosition = pool.create(); 465 try { 466 deltaAnchorPosition.set(mDeltaAnchorPosition); 467 for (int i = firstSlotIndex; i <= lastSlotIndex; ++i) { 468 GridCameraManager.getSlotPositionForSlotIndex(i, mCamera, mLayoutInterface, deltaAnchorPosition, position); 469 if (FloatUtils.boundsContainsPoint(position.x - itemWidthBy2, position.x + itemWidthBy2, 470 position.y - itemHeightBy2, position.y + itemHeightBy2, worldPos.x, worldPos.y)) { 471 retVal = i; 472 break; 473 } 474 } 475 } finally { 476 pool.delete(deltaAnchorPosition); 477 pool.delete(position); 478 } 479 return retVal; 480 } 481 482 void centerCameraForSlot(int slotIndex, float baseConvergence) { 483 float imageTheta = 0.0f; 484 DisplayItem displayItem = getDisplayItemForSlotId(slotIndex); 485 if (displayItem != null) { 486 imageTheta = displayItem.getImageTheta(); 487 } 488 mCameraManager.centerCameraForSlot(mLayoutInterface, slotIndex, baseConvergence, mDeltaAnchorPositionUncommited, 489 mInputProcessor.getCurrentSelectedSlot(), mZoomValue, imageTheta, mState); 490 } 491 492 boolean constrainCameraForSlot(int slotIndex) { 493 return mCameraManager.constrainCameraForSlot(mLayoutInterface, slotIndex, mDeltaAnchorPosition, mCurrentFocusItemWidth, 494 mCurrentFocusItemHeight); 495 } 496 497 // Called on render thread before rendering. 498 @Override 499 public boolean update(RenderView view, float timeElapsed) { 500 if (mFeedAboutToChange == false) { 501 mTimeElapsedSinceGridViewReady += timeElapsed; 502 if (mTimeElapsedSinceGridViewReady >= 1.0f) { 503 mTimeElapsedSinceGridViewReady = 1.0f; 504 } 505 mTimeElapsedSinceStackViewReady += timeElapsed; 506 if (mTimeElapsedSinceStackViewReady >= 1.0f) { 507 mTimeElapsedSinceStackViewReady = 1.0f; 508 } 509 } 510 if (mRequestToEnterSelection) { 511 HudLayer hud = getHud(); 512 if (hud != null) { 513 hud.enterSelectionMode(); 514 if (hud.getMode() == HudLayer.MODE_SELECT) { 515 mRequestToEnterSelection = false; 516 addSlotToSelectedItems(mInputProcessor.getCurrentSelectedSlot(), true, true); 517 } 518 } 519 } 520 521 // isSingleImageMode() returns true only when the item is not found in DB. 522 // In that case, we won't enter into the selection mode. 523 if (mMediaFeed != null && mMediaFeed.isSingleImageMode()) { 524 HudLayer hud = getHud(); 525 hud.getPathBar().setHidden(true); 526 hud.getMenuBar().setHidden(true); 527 if (hud.getMode() != HudLayer.MODE_NORMAL) 528 hud.setMode(HudLayer.MODE_NORMAL); 529 } 530 if (view.elapsedLoadingExpensiveTextures() > 150 || (mMediaFeed != null && mMediaFeed.getWaitingForMediaScanner())) { 531 mHud.getPathBar().setAnimatedIcons(GridDrawables.TEXTURE_SPINNER); 532 } else { 533 mHud.getPathBar().setAnimatedIcons(null); 534 } 535 536 // In that case, we need to commit the respective Display Items when the 537 // feed was updated. 538 GridCamera camera = mCamera; 539 camera.update(timeElapsed); 540 DisplayItem anchorDisplayItem = getAnchorDisplayItem(ANCHOR_CENTER); 541 if (anchorDisplayItem != null && !mHud.getTimeBar().isDragged()) { 542 mHud.getTimeBar().setItem(anchorDisplayItem.mItemRef); 543 } 544 mDisplayList.update(timeElapsed); 545 mInputProcessor.update(timeElapsed); 546 mSelectedAlpha = FloatUtils.animate(mSelectedAlpha, mTargetAlpha, timeElapsed * 0.5f); 547 if (mState == STATE_FULL_SCREEN) { 548 mHud.autoHide(true); 549 } else { 550 mHud.autoHide(false); 551 mHud.setAlpha(1.0f); 552 } 553 GridQuad[] fullscreenQuads = GridDrawables.sFullscreenGrid; 554 int numFullScreenQuads = fullscreenQuads.length; 555 for (int i = 0; i < numFullScreenQuads; ++i) { 556 fullscreenQuads[i].update(timeElapsed); 557 } 558 if (mSlideshowMode && mState == STATE_FULL_SCREEN) { 559 mTimeElapsedSinceView += timeElapsed; 560 if (mTimeElapsedSinceView > SLIDESHOW_TRANSITION_TIME) { 561 // time to go to the next slide 562 mTimeElapsedSinceView = 0.0f; 563 changeFocusToNextSlot(0.5f); 564 mCamera.commitMoveInX(); 565 mCamera.commitMoveInY(); 566 } 567 } 568 if (mState == STATE_MEDIA_SETS || mState == STATE_TIMELINE) { 569 mCamera.moveYTo(-0.1f); 570 mCamera.commitMoveInY(); 571 } 572 boolean dirty = mDrawManager.update(timeElapsed); 573 dirty |= mSlideshowMode; 574 dirty |= mFramesDirty > 0; 575 ++mFrameCount; 576 if (mFramesDirty > 0) { 577 --mFramesDirty; 578 } 579 if (mDisplayList.getNumAnimatables() != 0 || mCamera.isAnimating() 580 || mSelectedAlpha != mTargetAlpha 581 // || (mAnimatedFov != mTargetFov) 582 || dirty) 583 return true; 584 else 585 return false; 586 } 587 588 private void computeVisibleRange() { 589 if (mPerformingLayoutChange) 590 return; 591 if (mDeltaAnchorPosition.equals(mDeltaAnchorPositionUncommited) == false) { 592 mDeltaAnchorPosition.set(mDeltaAnchorPositionUncommited); 593 } 594 mCameraManager.computeVisibleRange(mMediaFeed, mLayoutInterface, mDeltaAnchorPosition, mVisibleRange, 595 mBufferedVisibleRange, mCompleteRange, mState); 596 } 597 598 private void computeVisibleItems() { 599 if (mFeedAboutToChange == true || mPerformingLayoutChange == true) { 600 return; 601 } 602 computeVisibleRange(); 603 int deltaBegin = mBufferedVisibleRange.begin - mPreviousDataRange.begin; 604 int deltaEnd = mBufferedVisibleRange.end - mPreviousDataRange.end; 605 if (deltaBegin != 0 || deltaEnd != 0) { 606 // The delta has changed, we have to compute the display items 607 // again. 608 // We find the intersection range, these slots have not changed at 609 // all. 610 int firstVisibleSlotIndex = mBufferedVisibleRange.begin; 611 int lastVisibleSlotIndex = mBufferedVisibleRange.end; 612 mPreviousDataRange.begin = firstVisibleSlotIndex; 613 mPreviousDataRange.end = lastVisibleSlotIndex; 614 615 Pool<Vector3f> pool = mTempVec; 616 Vector3f position = pool.create(); 617 Vector3f deltaAnchorPosition = pool.create(); 618 try { 619 MediaFeed feed = mMediaFeed; 620 DisplayList displayList = mDisplayList; 621 DisplayItem[] displayItems = mDisplayItems; 622 DisplaySlot[] displaySlots = mDisplaySlots; 623 int numDisplayItems = displayItems.length; 624 int numDisplaySlots = displaySlots.length; 625 ArrayList<MediaItem> visibleItems = mVisibleItems; 626 deltaAnchorPosition.set(mDeltaAnchorPosition); 627 LayoutInterface layout = mLayoutInterface; 628 GridCamera camera = mCamera; 629 for (int i = firstVisibleSlotIndex; i <= lastVisibleSlotIndex; ++i) { 630 GridCameraManager.getSlotPositionForSlotIndex(i, camera, layout, deltaAnchorPosition, position); 631 MediaSet set = feed.getSetForSlot(i); 632 int indexIntoSlots = i - firstVisibleSlotIndex; 633 634 if (set != null && indexIntoSlots >= 0 && indexIntoSlots < numDisplaySlots) { 635 ArrayList<MediaItem> items = set.getItems(); 636 displaySlots[indexIntoSlots].setMediaSet(set); 637 ArrayList<MediaItem> bestItems = mTempList; 638 { 639 // We always show the same top thumbnails for a 640 // stack of albums 641 // if (mState == STATE_MEDIA_SETS) 642 // ArrayUtils.computeSortedIntersection(items, 643 // visibleItems, MAX_ITEMS_PER_SLOT, bestItems, 644 // sTempHash); 645 // else 646 ArrayUtils.computeSortedIntersection(visibleItems, items, MAX_ITEMS_PER_SLOT, bestItems, mTempHash); 647 } 648 649 int numItemsInSet = set.getNumItems(); 650 int numBestItems = bestItems.size(); 651 int originallyFoundItems = numBestItems; 652 if (numBestItems < MAX_ITEMS_PER_SLOT) { 653 int itemsRemaining = MAX_ITEMS_PER_SLOT - numBestItems; 654 for (int currItemPos = 0; currItemPos < numItemsInSet; currItemPos++) { 655 MediaItem item = items.get(currItemPos); 656 if (!bestItems.contains(item)) { 657 bestItems.add(item); 658 if (--itemsRemaining == 0) { 659 break; 660 } 661 } 662 } 663 } 664 numBestItems = bestItems.size(); 665 int baseIndex = (i - firstVisibleSlotIndex) * MAX_ITEMS_PER_SLOT; 666 for (int j = 0; j < numBestItems; ++j) { 667 if (baseIndex + j >= numDisplayItems) { 668 break; 669 } 670 if (j >= numItemsInSet) { 671 displayItems[baseIndex + j] = null; 672 } else { 673 MediaItem item = bestItems.get(j); 674 if (item != null) { 675 DisplayItem displayItem = displayList.get(item); 676 if ((mState == STATE_FULL_SCREEN && i != mInputProcessor.getCurrentSelectedSlot()) 677 || (mState == STATE_GRID_VIEW && j >= originallyFoundItems)) { 678 displayItem.set(position, j, false); 679 displayItem.commit(); 680 } else { 681 displayList.setPositionAndStackIndex(displayItem, position, j, true); 682 } 683 displayItems[baseIndex + j] = displayItem; 684 } 685 } 686 } 687 for (int j = numBestItems; j < MAX_ITEMS_PER_SLOT; ++j) { 688 displayItems[baseIndex + j] = null; 689 } 690 bestItems.clear(); 691 } 692 } 693 if (mFeedChanged) { 694 mFeedChanged = false; 695 if (mInputProcessor != null && mState == STATE_FULL_SCREEN && mRequestFocusContentUri == null) { 696 int currentSelectedSlot = mInputProcessor.getCurrentSelectedSlot(); 697 if (currentSelectedSlot > mCompleteRange.end) 698 currentSelectedSlot = mCompleteRange.end; 699 mInputProcessor.setCurrentSelectedSlot(currentSelectedSlot); 700 } 701 if (mState == STATE_GRID_VIEW) { 702 MediaSet expandedSet = mMediaFeed.getExpandedMediaSet(); 703 if (expandedSet != null) { 704 if (mHud != null) { 705 final PathBarLayer pathBar = mHud.getPathBar(); 706 if (pathBar != null) { 707 final String currentLabel = pathBar.getCurrentLabel(); 708 if (currentLabel == null || !currentLabel.equals(expandedSet.mNoCountTitleString)) { 709 pathBar.changeLabel(expandedSet.mNoCountTitleString); 710 } 711 } 712 } 713 } 714 } 715 if (mRequestFocusContentUri != null) { 716 // We have to find the item that has this contentUri 717 int numSlots = mCompleteRange.end + 1; 718 for (int i = 0; i < numSlots; ++i) { 719 MediaSet set = feed.getSetForSlot(i); 720 ArrayList<MediaItem> items = set.getItems(); 721 int numItems = items.size(); 722 for (int j = 0; j < numItems; ++j) { 723 String itemUri = items.get(j).mContentUri; 724 if (itemUri != null && mRequestFocusContentUri != null) { 725 if (itemUri.equals(mRequestFocusContentUri)) { 726 if (mState == STATE_FULL_SCREEN) { 727 mInputProcessor.setCurrentSelectedSlot(i); 728 } else { 729 centerCameraForSlot(i, 1.0f); 730 } 731 break; 732 } 733 } 734 } 735 } 736 mRequestFocusContentUri = null; 737 } 738 } 739 } finally { 740 pool.delete(position); 741 pool.delete(deltaAnchorPosition); 742 } 743 // We keep upto 400 thumbnails in memory. 744 int numThumbnailsToKeepInMemory = (mState == STATE_MEDIA_SETS || mState == STATE_TIMELINE) ? 100 : 400; 745 int startMemoryRange = (mBufferedVisibleRange.begin / numThumbnailsToKeepInMemory) * numThumbnailsToKeepInMemory; 746 if (mStartMemoryRange != startMemoryRange) { 747 mStartMemoryRange = startMemoryRange; 748 clearUnusedThumbnails(); 749 } 750 } 751 } 752 753 @Override 754 public void handleLowMemory() { 755 clearUnusedThumbnails(); 756 GridDrawables.sStringTextureTable.clear(); 757 mBackground.clearCache(); 758 } 759 760 // This method can be potentially expensive 761 public void clearUnusedThumbnails() { 762 mDisplayList.clearExcept(mDisplayItems); 763 } 764 765 @Override 766 public void onSurfaceCreated(RenderView view, GL11 gl) { 767 mDisplayList.clear(); 768 mHud.clear(); 769 mHud.reset(); 770 GridDrawables.sStringTextureTable.clear(); 771 mDrawables.onSurfaceCreated(view, gl); 772 mBackground.clear(); 773 } 774 775 @Override 776 public void onSurfaceChanged(RenderView view, int width, int height) { 777 mCamera.viewportChanged(width, height, mCamera.mItemWidth, mCamera.mItemHeight); 778 view.setFov(mCamera.mFov); 779 setState(mState); 780 } 781 782 // Renders the node in a given pass. 783 public void renderOpaque(RenderView view, GL11 gl) { 784 GridCamera camera = mCamera; 785 int selectedSlotIndex = mInputProcessor.getCurrentSelectedSlot(); 786 computeVisibleItems(); 787 788 gl.glMatrixMode(GL11.GL_MODELVIEW); 789 gl.glLoadIdentity(); 790 GLU.gluLookAt(gl, -camera.mEyeX, -camera.mEyeY, -camera.mEyeZ, -camera.mLookAtX, -camera.mLookAtY, -camera.mLookAtZ, 791 camera.mUpX, camera.mUpY, camera.mUpZ); 792 view.setAlpha(1.0f); 793 if (mSelectedAlpha != 1.0f) { 794 gl.glEnable(GL11.GL_BLEND); 795 gl.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 796 view.setAlpha(mSelectedAlpha); 797 } 798 if (selectedSlotIndex != Shared.INVALID) { 799 mTargetAlpha = 0.0f; 800 } else { 801 mTargetAlpha = 1.0f; 802 } 803 mDrawManager.prepareDraw(mBufferedVisibleRange, mVisibleRange, selectedSlotIndex, mInputProcessor.getCurrentFocusSlot(), 804 mInputProcessor.getCurrentScaledSlot(), mInputProcessor.isFocusItemPressed(), mInputProcessor.getScale(), 805 mInputProcessor.getScaleGestureDetector(), mFeedAboutToChange); 806 if (mSelectedAlpha != 0.0f) { 807 mDrawManager.drawThumbnails(view, gl, mState); 808 } 809 if (mSelectedAlpha != 1.0f) { 810 gl.glDisable(GL11.GL_BLEND); 811 } 812 // We draw the selected slotIndex. 813 if (selectedSlotIndex != Shared.INVALID) { 814 mDrawManager.drawFocusItems(view, gl, mZoomValue, mSlideshowMode, mTimeElapsedSinceView); 815 mCurrentFocusItemWidth = mDrawManager.getFocusQuadWidth(); 816 mCurrentFocusItemHeight = mDrawManager.getFocusQuadHeight(); 817 } 818 view.setAlpha(mSelectedAlpha); 819 } 820 821 public void renderBlended(RenderView view, GL11 gl) { 822 // We draw the placeholder for all visible slots. 823 if (mHud != null && mDrawManager != null) { 824 if (mMediaFeed != null) { 825 mDrawManager.drawBlendedComponents(view, gl, mSelectedAlpha, mState, mHud.getMode(), 826 mTimeElapsedSinceStackViewReady, mTimeElapsedSinceGridViewReady, mSelectedBucketList, mMarkedBucketList, 827 mMediaFeed.getWaitingForMediaScanner() || mFeedAboutToChange || mMediaFeed.isLoading()); 828 } 829 } 830 } 831 832 public synchronized void onLayout(int newAnchorSlotIndex, int currentAnchorSlotIndex, LayoutInterface oldLayout) { 833 if (mPerformingLayoutChange || !mDeltaAnchorPosition.equals(mDeltaAnchorPositionUncommited)) { 834 return; 835 } 836 // mOldBreakSlots = mBreakSlots; 837 if (mState == STATE_GRID_VIEW) { 838 final ArrayList<Integer> breaks = mMediaFeed.getBreaks(); 839 } else { 840 // mBreakSlots = null; 841 } 842 mPerformingLayoutChange = true; 843 LayoutInterface layout = mLayoutInterface; 844 if (oldLayout == null) { 845 oldLayout = sfullScreenLayoutInterface; 846 } 847 GridCamera camera = mCamera; 848 if (currentAnchorSlotIndex == Shared.INVALID) { 849 currentAnchorSlotIndex = getAnchorSlotIndex(ANCHOR_CENTER); 850 if (mCurrentExpandedSlot != Shared.INVALID) { 851 currentAnchorSlotIndex = mCurrentExpandedSlot; 852 } 853 int selectedSlotIndex = mInputProcessor.getCurrentSelectedSlot(); 854 if (selectedSlotIndex != Shared.INVALID) { 855 currentAnchorSlotIndex = selectedSlotIndex; 856 } 857 } 858 if (newAnchorSlotIndex == Shared.INVALID) { 859 newAnchorSlotIndex = currentAnchorSlotIndex; 860 } 861 int itemHeight = camera.mItemHeight; 862 int itemWidth = camera.mItemWidth; 863 Pool<Vector3f> pool = mTempVec; 864 Vector3f deltaAnchorPosition = pool.create(); 865 Vector3f currentSlotPosition = pool.create(); 866 try { 867 deltaAnchorPosition.set(0, 0, 0); 868 if (currentAnchorSlotIndex != Shared.INVALID && newAnchorSlotIndex != Shared.INVALID) { 869 layout.getPositionForSlotIndex(newAnchorSlotIndex, itemWidth, itemHeight, deltaAnchorPosition); 870 oldLayout.getPositionForSlotIndex(currentAnchorSlotIndex, itemWidth, itemHeight, currentSlotPosition); 871 currentSlotPosition.subtract(mDeltaAnchorPosition); 872 deltaAnchorPosition.subtract(currentSlotPosition); 873 deltaAnchorPosition.y = 0; 874 deltaAnchorPosition.z = 0; 875 } 876 mDeltaAnchorPositionUncommited.set(deltaAnchorPosition); 877 } finally { 878 pool.delete(deltaAnchorPosition); 879 pool.delete(currentSlotPosition); 880 } 881 centerCameraForSlot(newAnchorSlotIndex, 1.0f); 882 mCurrentExpandedSlot = Shared.INVALID; 883 884 // Force recompute of visible items and their positions. 885 ((GridLayoutInterface) oldLayout).mNumRows = ((GridLayoutInterface) layout).mNumRows; 886 ((GridLayoutInterface) oldLayout).mSpacingX = ((GridLayoutInterface) layout).mSpacingX; 887 ((GridLayoutInterface) oldLayout).mSpacingY = ((GridLayoutInterface) layout).mSpacingY; 888 forceRecomputeVisibleRange(); 889 mPerformingLayoutChange = false; 890 } 891 892 private void forceRecomputeVisibleRange() { 893 mPreviousDataRange.begin = Shared.INVALID; 894 mPreviousDataRange.end = Shared.INVALID; 895 if (mView != null) { 896 mView.requestRender(); 897 } 898 } 899 900 // called on background thread 901 public synchronized void onFeedChanged(MediaFeed feed, boolean needsLayout) { 902 if (!needsLayout && !mFeedAboutToChange) { 903 mFeedChanged = true; 904 forceRecomputeVisibleRange(); 905 if (mState == STATE_GRID_VIEW || mState == STATE_FULL_SCREEN) 906 mHud.setFeed(feed, mState, needsLayout); 907 return; 908 } 909 910 while (mPerformingLayoutChange == true) { 911 Thread.yield(); 912 } 913 if (mState == STATE_GRID_VIEW) { 914 if (mHud != null) { 915 MediaSet set = feed.getCurrentSet(); 916 if (set != null && !mLocationFilter) 917 mHud.getPathBar().changeLabel(set.mNoCountTitleString); 918 } 919 } 920 DisplayItem[] displayItems = mDisplayItems; 921 int firstBufferedVisibleSlotIndex = mBufferedVisibleRange.begin; 922 int lastBufferedVisibleSlotIndex = mBufferedVisibleRange.end; 923 int currentlyVisibleSlotIndex = getAnchorSlotIndex(ANCHOR_CENTER); 924 int numVisibleItems = mVisibleRange.end - mVisibleRange.begin + 1; 925 if (mState == STATE_MEDIA_SETS && currentlyVisibleSlotIndex < numVisibleItems) { 926 currentlyVisibleSlotIndex = getAnchorSlotIndex(ANCHOR_LEFT); 927 } 928 if (mCurrentExpandedSlot != Shared.INVALID) { 929 currentlyVisibleSlotIndex = mCurrentExpandedSlot; 930 } 931 MediaItem anchorItem = null; 932 ArrayList<MediaItem> visibleItems = mVisibleItems; 933 visibleItems.clear(); 934 visibleItems.ensureCapacity(lastBufferedVisibleSlotIndex - firstBufferedVisibleSlotIndex); 935 if (currentlyVisibleSlotIndex != Shared.INVALID && currentlyVisibleSlotIndex >= firstBufferedVisibleSlotIndex 936 && currentlyVisibleSlotIndex <= lastBufferedVisibleSlotIndex) { 937 int baseIndex = (currentlyVisibleSlotIndex - firstBufferedVisibleSlotIndex) * MAX_ITEMS_PER_SLOT; 938 for (int i = 0; i < MAX_ITEMS_PER_SLOT; ++i) { 939 DisplayItem displayItem = displayItems[baseIndex + i]; 940 if (displayItem != null) { 941 if (anchorItem == null) { 942 anchorItem = displayItem.mItemRef; 943 } 944 visibleItems.add(displayItem.mItemRef); 945 } 946 } 947 } 948 // We want to add items from the middle. 949 int numItems = lastBufferedVisibleSlotIndex - firstBufferedVisibleSlotIndex + 1; 950 int midPoint = currentlyVisibleSlotIndex; 951 int length = displayItems.length; 952 for (int i = 0; i < numItems; ++i) { 953 int index = midPoint + Shared.midPointIterator(i); 954 int indexIntoDisplayItem = (index - firstBufferedVisibleSlotIndex) * MAX_ITEMS_PER_SLOT; 955 if (indexIntoDisplayItem >= 0 && indexIntoDisplayItem < length) { 956 for (int j = 0; j < MAX_ITEMS_PER_SLOT; ++j) { 957 DisplayItem displayItem = displayItems[indexIntoDisplayItem + j]; 958 if (displayItem != null) { 959 MediaItem item = displayItem.mItemRef; 960 if (!visibleItems.contains(item)) { 961 visibleItems.add(item); 962 } 963 } 964 } 965 } 966 } 967 int newSlotIndex = Shared.INVALID; 968 if (anchorItem != null) { 969 // We try to find the anchor item in the new feed. 970 int numSlots = feed.getNumSlots(); 971 for (int i = 0; i < numSlots; ++i) { 972 MediaSet set = feed.getSetForSlot(i); 973 if (set != null && ArrayUtils.contains(set.getItems(), anchorItem)) { 974 newSlotIndex = i; 975 break; 976 } 977 } 978 } 979 980 if (anchorItem != null && newSlotIndex == Shared.INVALID) { 981 int numSlots = feed.getNumSlots(); 982 MediaSet parentSet = anchorItem.mParentMediaSet; 983 for (int i = 0; i < numSlots; ++i) { 984 MediaSet set = feed.getSetForSlot(i); 985 if (set != null && set.mId == parentSet.mId) { 986 newSlotIndex = i; 987 break; 988 } 989 } 990 } 991 // We must create a new display store now since the data has changed. 992 Log.i(TAG, "Slot changing from " + currentlyVisibleSlotIndex + " to " + newSlotIndex); 993 if (newSlotIndex != Shared.INVALID) { 994 if (mState == STATE_MEDIA_SETS) { 995 mDisplayList.clearExcept(displayItems); 996 } 997 onLayout(newSlotIndex, currentlyVisibleSlotIndex, null); 998 } else { 999 forceRecomputeVisibleRange(); 1000 } 1001 mCurrentExpandedSlot = Shared.INVALID; 1002 mFeedAboutToChange = false; 1003 mFeedChanged = true; 1004 if (feed != null) { 1005 if (mState == STATE_GRID_VIEW || mState == STATE_FULL_SCREEN) 1006 mHud.setFeed(feed, mState, needsLayout); 1007 } 1008 if (mView != null) { 1009 mView.requestRender(); 1010 } 1011 } 1012 1013 public DisplayItem getRepresentativeDisplayItem() { 1014 int slotIndex = Shared.INVALID; 1015 if (mInputProcessor != null) { 1016 slotIndex = mInputProcessor.getCurrentFocusSlot(); 1017 } 1018 if (slotIndex == Shared.INVALID) { 1019 slotIndex = getAnchorSlotIndex(ANCHOR_CENTER); 1020 } 1021 int index = (slotIndex - mBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT; 1022 if (index >= 0 && index < MAX_ITEMS_DRAWABLE) { 1023 return mDisplayItems[index]; 1024 } else { 1025 return null; 1026 } 1027 } 1028 1029 public DisplayItem getAnchorDisplayItem(int type) { 1030 int slotIndex = getAnchorSlotIndex(type); 1031 int index = (slotIndex - mBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT; 1032 if (index >= 0 && index < MAX_ITEMS_DRAWABLE) { 1033 return mDisplayItems[index]; 1034 } else { 1035 return null; 1036 } 1037 } 1038 1039 public float getScrollPosition() { 1040 return (mCamera.mLookAtX * mCamera.mScale + mDeltaAnchorPosition.x); // in 1041 // pixels 1042 } 1043 1044 public DisplayItem getDisplayItemForScrollPosition(float posX) { 1045 Pool<Vector3f> pool = mTempVecAlt; 1046 MediaFeed feed = mMediaFeed; 1047 int itemWidth = mCamera.mItemWidth; 1048 int itemHeight = mCamera.mItemHeight; 1049 GridLayoutInterface gridInterface = (GridLayoutInterface) mLayoutInterface; 1050 float absolutePosX = posX; 1051 int left = (int) ((absolutePosX / itemWidth) * gridInterface.mNumRows); 1052 int right = feed == null ? 0 : (int) (feed.getNumSlots()); 1053 int retSlot = left; 1054 Vector3f position = pool.create(); 1055 try { 1056 for (int i = left; i < right; ++i) { 1057 gridInterface.getPositionForSlotIndex(i, itemWidth, itemHeight, position); 1058 retSlot = i; 1059 if (position.x >= absolutePosX) { 1060 break; 1061 } 1062 } 1063 } finally { 1064 pool.delete(position); 1065 } 1066 if (mFeedAboutToChange) { 1067 return null; 1068 } 1069 right = feed == null ? 0 : feed.getNumSlots(); 1070 if (right == 0) { 1071 return null; 1072 } 1073 1074 if (retSlot >= right) 1075 retSlot = right - 1; 1076 MediaSet set = feed.getSetForSlot(retSlot); 1077 if (set != null) { 1078 ArrayList<MediaItem> items = set.getItems(); 1079 if (items != null && set.getNumItems() > 0) { 1080 return (mDisplayList.get(items.get(0))); 1081 } 1082 } 1083 return null; 1084 } 1085 1086 // Returns the top left-most item. 1087 public int getAnchorSlotIndex(int anchorType) { 1088 int retVal = 0; 1089 switch (anchorType) { 1090 case ANCHOR_LEFT: 1091 retVal = mVisibleRange.begin; 1092 break; 1093 case ANCHOR_RIGHT: 1094 retVal = mVisibleRange.end; 1095 break; 1096 case ANCHOR_CENTER: 1097 retVal = (mVisibleRange.begin + mVisibleRange.end) / 2; 1098 break; 1099 } 1100 return retVal; 1101 } 1102 1103 DisplayItem getDisplayItemForSlotId(int slotId) { 1104 int index = slotId - mBufferedVisibleRange.begin; 1105 if (index >= 0 && slotId <= mBufferedVisibleRange.end) { 1106 return mDisplayItems[index * MAX_ITEMS_PER_SLOT]; 1107 } 1108 return null; 1109 } 1110 1111 boolean changeFocusToNextSlot(float convergence) { 1112 int currentSelectedSlot = mInputProcessor.getCurrentSelectedSlot(); 1113 boolean retVal = changeFocusToSlot(currentSelectedSlot + 1, convergence); 1114 if (mInputProcessor.getCurrentSelectedSlot() == currentSelectedSlot) { 1115 endSlideshow(); 1116 mHud.setAlpha(1.0f); 1117 } 1118 return retVal; 1119 } 1120 1121 boolean changeFocusToSlot(int slotId, float convergence) { 1122 mZoomValue = 1.0f; 1123 int index = slotId - mBufferedVisibleRange.begin; 1124 if (index >= 0 && slotId <= mBufferedVisibleRange.end) { 1125 DisplayItem displayItem = mDisplayItems[index * MAX_ITEMS_PER_SLOT]; 1126 if (displayItem != null) { 1127 MediaItem item = displayItem.mItemRef; 1128 mHud.fullscreenSelectionChanged(item, slotId + 1, mCompleteRange.end + 1); 1129 if (slotId != Shared.INVALID && slotId <= mCompleteRange.end) { 1130 mInputProcessor.setCurrentFocusSlot(slotId); 1131 centerCameraForSlot(slotId, convergence); 1132 return true; 1133 } else { 1134 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), convergence); 1135 return false; 1136 } 1137 } 1138 } 1139 return false; 1140 } 1141 1142 boolean changeFocusToPreviousSlot(float convergence) { 1143 return changeFocusToSlot(mInputProcessor.getCurrentSelectedSlot() - 1, convergence); 1144 } 1145 1146 public ArrayList<MediaBucket> getSelectedBuckets() { 1147 return mSelectedBucketList.get(); 1148 } 1149 1150 public void selectAll() { 1151 if (mState != STATE_FULL_SCREEN) { 1152 int numSlots = mCompleteRange.end + 1; 1153 for (int i = 0; i < numSlots; ++i) { 1154 addSlotToSelectedItems(i, false, false); 1155 } 1156 updateCountOfSelectedItems(); 1157 } else { 1158 addSlotToSelectedItems(mInputProcessor.getCurrentFocusSlot(), false, true); 1159 } 1160 } 1161 1162 public void deselectOrCancelSelectMode() { 1163 if (mSelectedBucketList.size() == 0) { 1164 mHud.cancelSelection(); 1165 } else { 1166 mSelectedBucketList.clear(); 1167 updateCountOfSelectedItems(); 1168 } 1169 } 1170 1171 public void deselectAll() { 1172 mHud.cancelSelection(); 1173 mSelectedBucketList.clear(); 1174 updateCountOfSelectedItems(); 1175 } 1176 1177 public void deleteSelection() { 1178 // Delete the selection and exit selection mode. 1179 mMediaFeed.performOperation(MediaFeed.OPERATION_DELETE, getSelectedBuckets(), null); 1180 deselectAll(); 1181 1182 // If the current set is now empty, return to the parent set. 1183 if (mCompleteRange.isEmpty()) { 1184 goBack(); // TODO(venkat): This does not work most of the time, can 1185 // you take a look? 1186 } 1187 } 1188 1189 void addSlotToSelectedItems(int slotId, boolean removeIfAlreadyAdded, boolean updateCount) { 1190 // mMediaFeed may be null because setDataSource() may not be called yet. 1191 if (mFeedAboutToChange == false && mMediaFeed != null) { 1192 MediaFeed feed = mMediaFeed; 1193 mSelectedBucketList.add(slotId, feed, removeIfAlreadyAdded); 1194 if (updateCount) { 1195 updateCountOfSelectedItems(); 1196 if (mSelectedBucketList.size() == 0) 1197 deselectAll(); 1198 } 1199 } 1200 mHud.computeBottomMenu(); 1201 } 1202 1203 private void updateCountOfSelectedItems() { 1204 mHud.updateNumItemsSelected(mSelectedBucketList.size()); 1205 } 1206 1207 public int getMetadataSlotIndexForScreenPosition(int posX, int posY) { 1208 return getSlotForScreenPosition(posX, posY, mCamera.mItemWidth + (int) (100 * App.PIXEL_DENSITY), mCamera.mItemHeight 1209 + (int) (100 * App.PIXEL_DENSITY)); 1210 } 1211 1212 public int getSlotIndexForScreenPosition(int posX, int posY) { 1213 return getSlotForScreenPosition(posX, posY, mCamera.mItemWidth, mCamera.mItemHeight); 1214 } 1215 1216 private int getSlotForScreenPosition(int posX, int posY, int itemWidth, int itemHeight) { 1217 Pool<Vector3f> pool = mTempVec; 1218 int retVal = 0; 1219 Vector3f worldPos = pool.create(); 1220 try { 1221 GridCamera camera = mCamera; 1222 camera.convertToCameraSpace(posX, posY, 0, worldPos); 1223 // slots are expressed in pixels as well 1224 worldPos.x *= camera.mScale; 1225 worldPos.y *= camera.mScale; 1226 // we ignore z 1227 retVal = hitTest(worldPos, itemWidth, itemHeight); 1228 } finally { 1229 pool.delete(worldPos); 1230 } 1231 return retVal; 1232 } 1233 1234 public boolean tapGesture(int slotIndex, boolean metadata) { 1235 MediaFeed feed = mMediaFeed; 1236 if (feed == null) 1237 return false; 1238 if (!feed.isClustered()) { 1239 // It is not clustering. 1240 if (!feed.hasExpandedMediaSet()) { 1241 if (feed.canExpandSet(slotIndex)) { 1242 mCurrentExpandedSlot = slotIndex; 1243 feed.expandMediaSet(slotIndex); 1244 setState(STATE_GRID_VIEW); 1245 } 1246 return false; 1247 } else { 1248 return true; 1249 } 1250 } else { 1251 // Select a cluster, and recompute a new cluster within this 1252 // cluster. 1253 mCurrentExpandedSlot = slotIndex; 1254 mMarkedBucketList.clear(); 1255 mMarkedBucketList.add(slotIndex, feed, false); 1256 goBack(); 1257 if (metadata) { 1258 DisplaySlot slot = mDisplaySlots[slotIndex - mBufferedVisibleRange.begin]; 1259 if (slot.hasValidLocation()) { 1260 MediaSet set = slot.getMediaSet(); 1261 if (set.mReverseGeocodedLocation != null) { 1262 enableLocationFiltering(set.mReverseGeocodedLocation); 1263 } 1264 feed.setFilter(new LocationMediaFilter(set.mMinLatLatitude, set.mMinLonLongitude, set.mMaxLatLatitude, 1265 set.mMaxLonLongitude)); 1266 } 1267 } 1268 return false; 1269 } 1270 } 1271 1272 public void onTimeChanged(TimeBar timebar) { 1273 if (mFeedAboutToChange) { 1274 return; 1275 } 1276 // TODO lot of optimization possible here 1277 MediaItem item = timebar.getItem(); 1278 if (item == null) 1279 return; 1280 MediaFeed feed = mMediaFeed; 1281 if (feed == null) 1282 return; 1283 int numSlots = feed.getNumSlots(); 1284 for (int i = 0; i < numSlots; ++i) { 1285 MediaSet set = feed.getSetForSlot(i); 1286 if (set == null) { 1287 return; 1288 } 1289 ArrayList<MediaItem> items = set.getItems(); 1290 if (items == null || set.getNumItems() == 0) { 1291 return; 1292 } 1293 if (ArrayUtils.contains(items, item)) { 1294 centerCameraForSlot(i, 1.0f); 1295 break; 1296 } 1297 } 1298 } 1299 1300 public void onFeedAboutToChange(MediaFeed feed) { 1301 mFeedAboutToChange = true; 1302 } 1303 1304 public void startSlideshow() { 1305 endSlideshow(); 1306 mSlideshowMode = true; 1307 mZoomValue = 1.0f; 1308 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); 1309 mTimeElapsedSinceView = SLIDESHOW_TRANSITION_TIME - 1.0f; 1310 mHud.setAlpha(0); 1311 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 1312 mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GridView.Slideshow"); 1313 mWakeLock.acquire(); 1314 } 1315 1316 public void enterSelectionMode() { 1317 mSlideshowMode = false; 1318 mHud.enterSelectionMode(); 1319 int currentSlot = mInputProcessor.getCurrentSelectedSlot(); 1320 if (currentSlot == Shared.INVALID) { 1321 currentSlot = mInputProcessor.getCurrentFocusSlot(); 1322 } 1323 addSlotToSelectedItems(currentSlot, false, true); 1324 } 1325 1326 private float getFillScreenZoomValue() { 1327 return GridCameraManager.getFillScreenZoomValue(mCamera, mTempVec, mCurrentFocusItemWidth, mCurrentFocusItemHeight); 1328 } 1329 1330 public void zoomInToSelectedItem() { 1331 mSlideshowMode = false; 1332 float potentialZoomValue = getFillScreenZoomValue(); 1333 if (mZoomValue < potentialZoomValue) { 1334 mZoomValue = potentialZoomValue; 1335 } else { 1336 mZoomValue *= 3.0f; 1337 } 1338 if (mZoomValue > 6.0f) { 1339 mZoomValue = 6.0f; 1340 } 1341 mHud.setAlpha(1.0f); 1342 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); 1343 } 1344 1345 public void zoomOutFromSelectedItem() { 1346 mSlideshowMode = false; 1347 if (mZoomValue == getFillScreenZoomValue()) { 1348 mZoomValue = 1.0f; 1349 } else { 1350 mZoomValue /= 3.0f; 1351 } 1352 if (mZoomValue < 1.0f) { 1353 mZoomValue = 1.0f; 1354 } 1355 mHud.setAlpha(1.0f); 1356 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); 1357 } 1358 1359 public void rotateSelectedItems(float f) { 1360 MediaBucketList bucketList = mSelectedBucketList; 1361 ArrayList<MediaBucket> mediaBuckets = bucketList.get(); 1362 DisplayList displayList = mDisplayList; 1363 int numBuckets = mediaBuckets.size(); 1364 for (int i = 0; i < numBuckets; ++i) { 1365 MediaBucket bucket = mediaBuckets.get(i); 1366 ArrayList<MediaItem> mediaItems = bucket.mediaItems; 1367 if (mediaItems != null) { 1368 int numMediaItems = mediaItems.size(); 1369 for (int j = 0; j < numMediaItems; ++j) { 1370 MediaItem item = mediaItems.get(j); 1371 DisplayItem displayItem = displayList.get(item); 1372 displayItem.rotateImageBy(f); 1373 displayList.addToAnimatables(displayItem); 1374 } 1375 } 1376 } 1377 if (mState == STATE_FULL_SCREEN) { 1378 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); 1379 } 1380 mMediaFeed.performOperation(MediaFeed.OPERATION_ROTATE, mediaBuckets, new Float(f)); 1381 // we recreate these displayitems from the cache 1382 } 1383 1384 public void cropSelectedItem() { 1385 1386 } 1387 1388 @Override 1389 public boolean onTouchEvent(MotionEvent event) { 1390 return mInputProcessor.onTouchEvent(event); 1391 } 1392 1393 @Override 1394 public boolean onKeyDown(int keyCode, KeyEvent event) { 1395 if (mInputProcessor != null) 1396 return mInputProcessor.onKeyDown(keyCode, event, mState); 1397 return false; 1398 } 1399 1400 public boolean inSlideShowMode() { 1401 return mSlideshowMode; 1402 } 1403 1404 public boolean noDeleteMode() { 1405 return mNoDeleteMode || (mMediaFeed != null && mMediaFeed.isSingleImageMode()); 1406 } 1407 1408 public float getZoomValue() { 1409 return mZoomValue; 1410 } 1411 1412 public boolean feedAboutToChange() { 1413 return mFeedAboutToChange; 1414 } 1415 1416 public boolean isInAlbumMode() { 1417 return mInAlbum; 1418 } 1419 1420 public Vector3f getDeltaAnchorPosition() { 1421 return mDeltaAnchorPosition; 1422 } 1423 1424 public int getExpandedSlot() { 1425 return mCurrentExpandedSlot; 1426 } 1427 1428 public GridLayoutInterface getLayoutInterface() { 1429 return (GridLayoutInterface) mLayoutInterface; 1430 } 1431 1432 public void setZoomValue(float f) { 1433 mZoomValue = f; 1434 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); 1435 } 1436 1437 public void setPickIntent(boolean b) { 1438 mPickIntent = b; 1439 mHud.getPathBar().popLabel(); 1440 mHud.getPathBar().pushLabel(Res.drawable.icon_location_small, mContext.getResources().getString(Res.string.pick), 1441 new Runnable() { 1442 public void run() { 1443 if (mHud.getAlpha() == 1.0f) { 1444 if (!mFeedAboutToChange) { 1445 setState(STATE_MEDIA_SETS); 1446 } 1447 } else { 1448 mHud.setAlpha(1.0f); 1449 } 1450 } 1451 }); 1452 } 1453 1454 public boolean getPickIntent() { 1455 return mPickIntent; 1456 } 1457 1458 public void setViewIntent(boolean b, final String setName) { 1459 mViewIntent = b; 1460 if (b) { 1461 mMediaFeed.expandMediaSet(0); 1462 setState(STATE_GRID_VIEW); 1463 // We need to make sure we haven't pushed the same label twice 1464 if (mHud.getPathBar().getNumLevels() == 1) { 1465 mHud.getPathBar().pushLabel(Res.drawable.icon_folder_small, setName, new Runnable() { 1466 public void run() { 1467 if (mFeedAboutToChange) { 1468 return; 1469 } 1470 if (mHud.getAlpha() == 1.0f) { 1471 disableLocationFiltering(); 1472 if (mInputProcessor != null) 1473 mInputProcessor.clearSelection(); 1474 setState(STATE_GRID_VIEW); 1475 } else { 1476 mHud.setAlpha(1.0f); 1477 } 1478 } 1479 }); 1480 } 1481 } 1482 } 1483 1484 public boolean getViewIntent() { 1485 return mViewIntent; 1486 } 1487 1488 public void setSingleImage(boolean noDeleteMode) { 1489 mNoDeleteMode = noDeleteMode; 1490 mInputProcessor.setCurrentSelectedSlot(0); 1491 } 1492 1493 public MediaFeed getFeed() { 1494 return mMediaFeed; 1495 } 1496 1497 public void markDirty(int numFrames) { 1498 mFramesDirty = numFrames; 1499 } 1500 1501 public void focusItem(String contentUri) { 1502 mRequestFocusContentUri = contentUri; 1503 if (mMediaFeed != null) { 1504 mMediaFeed.updateListener(false); 1505 } 1506 } 1507 1508 public void onResume() { 1509 if (mMediaFeed != null) { 1510 mMediaFeed.onResume(); 1511 } 1512 } 1513 1514 public void onPause() { 1515 if (mMediaFeed != null) { 1516 mMediaFeed.onPause(); 1517 } 1518 } 1519 1520 public void setEnterSelectionMode(boolean enterSelection) { 1521 mRequestToEnterSelection = enterSelection; 1522 } 1523} 1524