| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.cooliris.media; |
| |
| import java.util.ArrayList; |
| import javax.microedition.khronos.opengles.GL11; |
| |
| import android.hardware.SensorEvent; |
| import android.opengl.GLU; |
| import android.os.PowerManager; |
| import android.os.PowerManager.WakeLock; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.content.Context; |
| |
| import com.cooliris.app.App; |
| import com.cooliris.app.Res; |
| |
| public final class GridLayer extends RootLayer implements MediaFeed.Listener, TimeBar.Listener { |
| private static final String TAG = "GridLayer"; |
| public static final int STATE_MEDIA_SETS = 0; |
| public static final int STATE_GRID_VIEW = 1; |
| public static final int STATE_FULL_SCREEN = 2; |
| public static final int STATE_TIMELINE = 3; |
| |
| public static final int ANCHOR_LEFT = 0; |
| public static final int ANCHOR_RIGHT = 1; |
| public static final int ANCHOR_CENTER = 2; |
| |
| public static final int MAX_ITEMS_PER_SLOT = 32; |
| public static final int MAX_DISPLAYED_ITEMS_PER_SLOT = 4; |
| public static final int MAX_DISPLAYED_ITEMS_PER_FOCUSED_SLOT = 32; |
| public static final int MAX_DISPLAY_SLOTS = 96; |
| public static final int MAX_ITEMS_DRAWABLE = MAX_ITEMS_PER_SLOT * MAX_DISPLAY_SLOTS; |
| |
| private static final float SLIDESHOW_TRANSITION_TIME = 3.5f; |
| |
| private HudLayer mHud; |
| private int mState; |
| private final IndexRange mBufferedVisibleRange = new IndexRange(); |
| private final IndexRange mVisibleRange = new IndexRange(); |
| private final IndexRange mPreviousDataRange = new IndexRange(); |
| private final IndexRange mCompleteRange = new IndexRange(); |
| |
| private final Pool<Vector3f> mTempVec; |
| private final Pool<Vector3f> mTempVecAlt; |
| |
| private final ArrayList<MediaItem> mTempList = new ArrayList<MediaItem>(); |
| private final MediaItem[] mTempHash = new MediaItem[64]; |
| |
| private final Vector3f mDeltaAnchorPositionUncommited = new Vector3f(); |
| private Vector3f mDeltaAnchorPosition = new Vector3f(); |
| |
| // The display primitives. |
| final private GridDrawables mDrawables; |
| private float mSelectedAlpha = 0.0f; |
| private float mTargetAlpha = 0.0f; |
| |
| final private GridCamera mCamera; |
| final private GridCameraManager mCameraManager; |
| final private GridDrawManager mDrawManager; |
| final private GridInputProcessor mInputProcessor; |
| |
| private boolean mFeedAboutToChange; |
| private boolean mPerformingLayoutChange; |
| private boolean mFeedChanged; |
| |
| private final LayoutInterface mLayoutInterface; |
| private static final LayoutInterface sfullScreenLayoutInterface = new GridLayoutInterface(1); |
| private static final float DEPTH_POSITION = 0.5f; |
| |
| private MediaFeed mMediaFeed; |
| private boolean mInAlbum = false; |
| private int mCurrentExpandedSlot; |
| |
| private final DisplayList mDisplayList = new DisplayList(); |
| private final DisplayItem[] mDisplayItems = new DisplayItem[MAX_ITEMS_DRAWABLE]; |
| private final DisplaySlot[] mDisplaySlots = new DisplaySlot[MAX_DISPLAY_SLOTS]; |
| private ArrayList<MediaItem> mVisibleItems; |
| |
| private final BackgroundLayer mBackground; |
| private boolean mLocationFilter; |
| private float mZoomValue = 1.0f; |
| private float mCurrentFocusItemWidth = 1.0f; |
| private float mCurrentFocusItemHeight = 1.0f; |
| private float mTimeElapsedSinceGridViewReady = 0.0f; |
| |
| private boolean mSlideshowMode; |
| private boolean mNoDeleteMode = false; |
| private float mTimeElapsedSinceView; |
| private final MediaBucketList mSelectedBucketList = new MediaBucketList(); |
| private final MediaBucketList mMarkedBucketList = new MediaBucketList(); |
| private float mTimeElapsedSinceStackViewReady; |
| |
| private Context mContext; |
| private RenderView mView; |
| private boolean mPickIntent; |
| private boolean mViewIntent; |
| private WakeLock mWakeLock; |
| private int mStartMemoryRange; |
| private int mFramesDirty; |
| private String mRequestFocusContentUri; |
| private int mFrameCount; |
| private boolean mRequestToEnterSelection; |
| |
| // private ArrayList<Integer> mBreakSlots = new ArrayList<Integer>(); |
| // private ArrayList<Integer> mOldBreakSlots; |
| // private LongSparseArray<Integer> mBreakSlots = new |
| // LongSparseArray<Integer>(); |
| // private LongSparseArray<Integer> mOldBreakSlots; |
| |
| public GridLayer(Context context, int itemWidth, int itemHeight, LayoutInterface layoutInterface, RenderView view) { |
| mBackground = new BackgroundLayer(this); |
| mContext = context; |
| mView = view; |
| |
| Vector3f[] vectorPool = new Vector3f[128]; |
| int length = vectorPool.length; |
| for (int i = 0; i < length; ++i) { |
| vectorPool[i] = new Vector3f(); |
| } |
| Vector3f[] vectorPoolRenderThread = new Vector3f[128]; |
| length = vectorPoolRenderThread.length; |
| for (int i = 0; i < length; ++i) { |
| vectorPoolRenderThread[i] = new Vector3f(); |
| } |
| mTempVec = new Pool<Vector3f>(vectorPool); |
| mTempVecAlt = new Pool<Vector3f>(vectorPoolRenderThread); |
| |
| DisplaySlot[] displaySlots = mDisplaySlots; |
| for (int i = 0; i < MAX_DISPLAY_SLOTS; ++i) { |
| DisplaySlot slot = new DisplaySlot(); |
| displaySlots[i] = slot; |
| } |
| mLayoutInterface = layoutInterface; |
| mCamera = new GridCamera(0, 0, itemWidth, itemHeight); |
| mDrawables = new GridDrawables(itemWidth, itemHeight); |
| mBufferedVisibleRange.set(Shared.INVALID, Shared.INVALID); |
| mVisibleRange.set(Shared.INVALID, Shared.INVALID); |
| mCompleteRange.set(Shared.INVALID, Shared.INVALID); |
| mPreviousDataRange.set(Shared.INVALID, Shared.INVALID); |
| mDeltaAnchorPosition.set(0, 0, 0); |
| mDeltaAnchorPositionUncommited.set(0, 0, 0); |
| mSelectedBucketList.clear(); |
| |
| mVisibleItems = new ArrayList<MediaItem>(); |
| mHud = new HudLayer(context); |
| mHud.setContext(context); |
| mHud.setGridLayer(this); |
| mHud.getPathBar().clear(); |
| mHud.setGridLayer(this); |
| mHud.getTimeBar().setListener(this); |
| mHud.getPathBar().pushLabel(Res.drawable.icon_home_small, context.getResources().getString(Res.string.app_name), |
| new Runnable() { |
| public void run() { |
| if (mHud.getAlpha() == 1.0f) { |
| if (!mFeedAboutToChange) { |
| setState(STATE_MEDIA_SETS); |
| } |
| } else { |
| mHud.setAlpha(1.0f); |
| } |
| } |
| }); |
| mCameraManager = new GridCameraManager(mCamera); |
| mDrawManager = new GridDrawManager(context, mCamera, mDrawables, mDisplayList, mDisplayItems, mDisplaySlots); |
| mInputProcessor = new GridInputProcessor(context, mCamera, this, mView, mTempVec, mDisplayItems); |
| setState(STATE_MEDIA_SETS); |
| } |
| |
| public HudLayer getHud() { |
| return mHud; |
| } |
| |
| public void shutdown() { |
| if (mMediaFeed != null) { |
| mMediaFeed.shutdown(); |
| } |
| mContext = null; |
| mSelectedBucketList.clear(); |
| mView = null; |
| } |
| |
| public void stop() { |
| endSlideshow(); |
| mBackground.clear(); |
| handleLowMemory(); |
| } |
| |
| @Override |
| public void generate(RenderView view, RenderView.Lists lists) { |
| lists.updateList.add(this); |
| lists.opaqueList.add(this); |
| mBackground.generate(view, lists); |
| lists.blendedList.add(this); |
| lists.hitTestList.add(this); |
| mHud.generate(view, lists); |
| } |
| |
| @Override |
| protected void onSizeChanged() { |
| mHud.setSize(mWidth, mHeight); |
| mHud.setAlpha(1.0f); |
| mBackground.setSize(mWidth, mHeight); |
| if (mView != null) { |
| mView.requestRender(); |
| } |
| } |
| |
| public int getState() { |
| return mState; |
| } |
| |
| public void setState(int state) { |
| boolean feedUnchanged = false; |
| mCamera.mFriction = 0.0f; |
| if (mState == state) { |
| feedUnchanged = true; |
| } |
| GridLayoutInterface layoutInterface = (GridLayoutInterface) mLayoutInterface; |
| GridLayoutInterface oldLayout = (GridLayoutInterface) sfullScreenLayoutInterface; |
| oldLayout.mNumRows = layoutInterface.mNumRows; |
| oldLayout.mSpacingX = layoutInterface.mSpacingX; |
| oldLayout.mSpacingY = layoutInterface.mSpacingY; |
| GridCamera camera = mCamera; |
| int numMaxRows = (camera.mHeight >= camera.mWidth) ? 4 : 3; |
| MediaFeed feed = mMediaFeed; |
| boolean performLayout = true; |
| mZoomValue = 1.0f; |
| float yStretch = camera.mDefaultAspectRatio / camera.mAspectRatio; |
| if (yStretch < 1.0f) { |
| yStretch = 1.0f; |
| } |
| switch (state) { |
| case STATE_GRID_VIEW: |
| mTimeElapsedSinceGridViewReady = 0.0f; |
| if (feed != null && feedUnchanged == false) { |
| boolean updatedData = feed.restorePreviousClusteringState(); |
| if (updatedData) { |
| performLayout = false; |
| } |
| } |
| layoutInterface.mNumRows = numMaxRows; |
| layoutInterface.mSpacingX = (int) (10 * App.PIXEL_DENSITY); |
| layoutInterface.mSpacingY = (int) (10 * App.PIXEL_DENSITY); |
| if (mState == STATE_MEDIA_SETS) { |
| // Entering album. |
| mInAlbum = true; |
| MediaSet set = feed.getCurrentSet(); |
| int icon = mDrawables.getIconForSet(set, true); |
| if (set != null) { |
| mHud.getPathBar().pushLabel(icon, set.mNoCountTitleString, new Runnable() { |
| public void run() { |
| if (mFeedAboutToChange) { |
| return; |
| } |
| if (mHud.getAlpha() == 1.0f) { |
| disableLocationFiltering(); |
| mInputProcessor.clearSelection(); |
| setState(STATE_GRID_VIEW); |
| } else { |
| mHud.setAlpha(1.0f); |
| } |
| } |
| }); |
| } |
| } |
| if (mState == STATE_FULL_SCREEN) { |
| mHud.getPathBar().popLabel(); |
| } |
| break; |
| case STATE_TIMELINE: |
| mTimeElapsedSinceStackViewReady = 0.0f; |
| if (feed != null && feedUnchanged == false) { |
| feed.performClustering(); |
| performLayout = false; |
| } |
| disableLocationFiltering(); |
| layoutInterface.mNumRows = numMaxRows - 1; |
| layoutInterface.mSpacingX = (int) (100 * App.PIXEL_DENSITY); |
| layoutInterface.mSpacingY = (int) (70 * App.PIXEL_DENSITY * yStretch); |
| break; |
| case STATE_FULL_SCREEN: |
| layoutInterface.mNumRows = 1; |
| layoutInterface.mSpacingX = (int) (40 * App.PIXEL_DENSITY); |
| layoutInterface.mSpacingY = (int) (40 * App.PIXEL_DENSITY); |
| if (mState != STATE_FULL_SCREEN) { |
| mHud.getPathBar().pushLabel(Res.drawable.ic_fs_details, "", new Runnable() { |
| public void run() { |
| if (mHud.getAlpha() == 1.0f) { |
| mHud.swapFullscreenLabel(); |
| } |
| mHud.setAlpha(1.0f); |
| } |
| }); |
| } |
| break; |
| case STATE_MEDIA_SETS: |
| mTimeElapsedSinceStackViewReady = 0.0f; |
| if (feed != null && feedUnchanged == false) { |
| feed.restorePreviousClusteringState(); |
| mMarkedBucketList.clear(); |
| feed.expandMediaSet(Shared.INVALID); |
| performLayout = false; |
| } |
| disableLocationFiltering(); |
| mInputProcessor.clearSelection(); |
| layoutInterface.mNumRows = numMaxRows - 1; |
| layoutInterface.mSpacingX = (int) (100 * App.PIXEL_DENSITY); |
| layoutInterface.mSpacingY = (int) (70 * App.PIXEL_DENSITY * yStretch); |
| if (mInAlbum) { |
| if (mState == STATE_FULL_SCREEN) { |
| mHud.getPathBar().popLabel(); |
| } |
| mHud.getPathBar().popLabel(); |
| mInAlbum = false; |
| } |
| break; |
| } |
| mState = state; |
| mHud.onGridStateChanged(); |
| if (performLayout && mFeedAboutToChange == false) { |
| onLayout(Shared.INVALID, Shared.INVALID, oldLayout); |
| } |
| if (state != STATE_FULL_SCREEN) { |
| mCamera.moveYTo(0); |
| mCamera.moveZTo(0); |
| } |
| } |
| |
| protected void enableLocationFiltering(String label) { |
| if (mLocationFilter == false) { |
| mLocationFilter = true; |
| mHud.getPathBar().pushLabel(Res.drawable.icon_location_small, label, new Runnable() { |
| public void run() { |
| if (mHud.getAlpha() == 1.0f) { |
| if (mState == STATE_FULL_SCREEN) { |
| mInputProcessor.clearSelection(); |
| setState(STATE_GRID_VIEW); |
| } else { |
| disableLocationFiltering(); |
| } |
| } else { |
| mHud.setAlpha(1.0f); |
| } |
| } |
| }); |
| } |
| } |
| |
| protected void disableLocationFiltering() { |
| if (mLocationFilter) { |
| mLocationFilter = false; |
| mMediaFeed.removeFilter(); |
| mHud.getPathBar().popLabel(); |
| } |
| } |
| |
| boolean goBack() { |
| if (mFeedAboutToChange) { |
| return false; |
| } |
| int state = mState; |
| if (mInputProcessor.getCurrentSelectedSlot() == Shared.INVALID) { |
| if (mLocationFilter) { |
| disableLocationFiltering(); |
| setState(STATE_TIMELINE); |
| return true; |
| } |
| } |
| switch (state) { |
| case STATE_GRID_VIEW: |
| setState(STATE_MEDIA_SETS); |
| break; |
| case STATE_TIMELINE: |
| setState(STATE_GRID_VIEW); |
| break; |
| case STATE_FULL_SCREEN: |
| setState(STATE_GRID_VIEW); |
| mInputProcessor.clearSelection(); |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| public void endSlideshow() { |
| mSlideshowMode = false; |
| if (mWakeLock != null) { |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| mWakeLock = null; |
| } |
| mHud.setAlpha(1.0f); |
| } |
| |
| @Override |
| public void onSensorChanged(RenderView view, SensorEvent event) { |
| mInputProcessor.onSensorChanged(view, event, mState); |
| } |
| |
| public DataSource getDataSource() { |
| if (mMediaFeed != null) |
| return mMediaFeed.getDataSource(); |
| return null; |
| } |
| |
| public void setDataSource(DataSource dataSource) { |
| MediaFeed feed = mMediaFeed; |
| mMediaFeed = new MediaFeed(mContext, dataSource, this); |
| if (feed != null) { |
| // Restore the slot state in the original feed before shutting it down. |
| mMediaFeed.copySlotStateFrom(feed); |
| |
| feed.shutdown(); |
| mDisplayList.clear(); |
| mBackground.clear(); |
| } |
| |
| mMediaFeed.start(); |
| } |
| |
| public IndexRange getVisibleRange() { |
| return mVisibleRange; |
| } |
| |
| public IndexRange getBufferedVisibleRange() { |
| return mBufferedVisibleRange; |
| } |
| |
| public IndexRange getCompleteRange() { |
| return mCompleteRange; |
| } |
| |
| private int hitTest(Vector3f worldPos, int itemWidth, int itemHeight) { |
| int retVal = Shared.INVALID; |
| int firstSlotIndex = 0; |
| int lastSlotIndex = 0; |
| IndexRange rangeToUse = mVisibleRange; |
| synchronized (rangeToUse) { |
| firstSlotIndex = rangeToUse.begin; |
| lastSlotIndex = rangeToUse.end; |
| } |
| Pool<Vector3f> pool = mTempVec; |
| float itemWidthBy2 = itemWidth * 0.5f; |
| float itemHeightBy2 = itemHeight * 0.5f; |
| Vector3f position = pool.create(); |
| Vector3f deltaAnchorPosition = pool.create(); |
| try { |
| deltaAnchorPosition.set(mDeltaAnchorPosition); |
| for (int i = firstSlotIndex; i <= lastSlotIndex; ++i) { |
| GridCameraManager.getSlotPositionForSlotIndex(i, mCamera, mLayoutInterface, deltaAnchorPosition, position); |
| if (FloatUtils.boundsContainsPoint(position.x - itemWidthBy2, position.x + itemWidthBy2, |
| position.y - itemHeightBy2, position.y + itemHeightBy2, worldPos.x, worldPos.y)) { |
| retVal = i; |
| break; |
| } |
| } |
| } finally { |
| pool.delete(deltaAnchorPosition); |
| pool.delete(position); |
| } |
| return retVal; |
| } |
| |
| void centerCameraForSlot(int slotIndex, float baseConvergence) { |
| float imageTheta = 0.0f; |
| DisplayItem displayItem = getDisplayItemForSlotId(slotIndex); |
| if (displayItem != null) { |
| imageTheta = displayItem.getImageTheta(); |
| } |
| mCameraManager.centerCameraForSlot(mLayoutInterface, slotIndex, baseConvergence, mDeltaAnchorPositionUncommited, |
| mInputProcessor.getCurrentSelectedSlot(), mZoomValue, imageTheta, mState); |
| } |
| |
| boolean constrainCameraForSlot(int slotIndex) { |
| return mCameraManager.constrainCameraForSlot(mLayoutInterface, slotIndex, mDeltaAnchorPosition, mCurrentFocusItemWidth, |
| mCurrentFocusItemHeight); |
| } |
| |
| // Called on render thread before rendering. |
| @Override |
| public boolean update(RenderView view, float timeElapsed) { |
| if (mFeedAboutToChange == false) { |
| mTimeElapsedSinceGridViewReady += timeElapsed; |
| if (mTimeElapsedSinceGridViewReady >= 1.0f) { |
| mTimeElapsedSinceGridViewReady = 1.0f; |
| } |
| mTimeElapsedSinceStackViewReady += timeElapsed; |
| if (mTimeElapsedSinceStackViewReady >= 1.0f) { |
| mTimeElapsedSinceStackViewReady = 1.0f; |
| } |
| } |
| if (mRequestToEnterSelection) { |
| HudLayer hud = getHud(); |
| if (hud != null) { |
| hud.enterSelectionMode(); |
| if (hud.getMode() == HudLayer.MODE_SELECT) { |
| mRequestToEnterSelection = false; |
| addSlotToSelectedItems(mInputProcessor.getCurrentSelectedSlot(), true, true); |
| } |
| } |
| } |
| |
| // isSingleImageMode() returns true only when the item is not found in DB. |
| // In that case, we won't enter into the selection mode. |
| if (mMediaFeed != null && mMediaFeed.isSingleImageMode()) { |
| HudLayer hud = getHud(); |
| hud.getPathBar().setHidden(true); |
| hud.getMenuBar().setHidden(true); |
| if (hud.getMode() != HudLayer.MODE_NORMAL) |
| hud.setMode(HudLayer.MODE_NORMAL); |
| } |
| if (view.elapsedLoadingExpensiveTextures() > 150 || (mMediaFeed != null && mMediaFeed.getWaitingForMediaScanner())) { |
| mHud.getPathBar().setAnimatedIcons(GridDrawables.TEXTURE_SPINNER); |
| } else { |
| mHud.getPathBar().setAnimatedIcons(null); |
| } |
| |
| // In that case, we need to commit the respective Display Items when the |
| // feed was updated. |
| GridCamera camera = mCamera; |
| camera.update(timeElapsed); |
| DisplayItem anchorDisplayItem = getAnchorDisplayItem(ANCHOR_CENTER); |
| if (anchorDisplayItem != null && !mHud.getTimeBar().isDragged()) { |
| mHud.getTimeBar().setItem(anchorDisplayItem.mItemRef); |
| } |
| mDisplayList.update(timeElapsed); |
| mInputProcessor.update(timeElapsed); |
| mSelectedAlpha = FloatUtils.animate(mSelectedAlpha, mTargetAlpha, timeElapsed * 0.5f); |
| if (mState == STATE_FULL_SCREEN) { |
| mHud.autoHide(true); |
| } else { |
| mHud.autoHide(false); |
| mHud.setAlpha(1.0f); |
| } |
| GridQuad[] fullscreenQuads = GridDrawables.sFullscreenGrid; |
| int numFullScreenQuads = fullscreenQuads.length; |
| for (int i = 0; i < numFullScreenQuads; ++i) { |
| fullscreenQuads[i].update(timeElapsed); |
| } |
| if (mSlideshowMode && mState == STATE_FULL_SCREEN) { |
| mTimeElapsedSinceView += timeElapsed; |
| if (mTimeElapsedSinceView > SLIDESHOW_TRANSITION_TIME) { |
| // time to go to the next slide |
| mTimeElapsedSinceView = 0.0f; |
| changeFocusToNextSlot(0.5f); |
| mCamera.commitMoveInX(); |
| mCamera.commitMoveInY(); |
| } |
| } |
| if (mState == STATE_MEDIA_SETS || mState == STATE_TIMELINE) { |
| mCamera.moveYTo(-0.1f); |
| mCamera.commitMoveInY(); |
| } |
| boolean dirty = mDrawManager.update(timeElapsed); |
| dirty |= mSlideshowMode; |
| dirty |= mFramesDirty > 0; |
| ++mFrameCount; |
| if (mFramesDirty > 0) { |
| --mFramesDirty; |
| } |
| if (mDisplayList.getNumAnimatables() != 0 || mCamera.isAnimating() |
| || mSelectedAlpha != mTargetAlpha |
| // || (mAnimatedFov != mTargetFov) |
| || dirty) |
| return true; |
| else |
| return false; |
| } |
| |
| private void computeVisibleRange() { |
| if (mPerformingLayoutChange) |
| return; |
| if (mDeltaAnchorPosition.equals(mDeltaAnchorPositionUncommited) == false) { |
| mDeltaAnchorPosition.set(mDeltaAnchorPositionUncommited); |
| } |
| mCameraManager.computeVisibleRange(mMediaFeed, mLayoutInterface, mDeltaAnchorPosition, mVisibleRange, |
| mBufferedVisibleRange, mCompleteRange, mState); |
| } |
| |
| private void computeVisibleItems() { |
| if (mFeedAboutToChange == true || mPerformingLayoutChange == true) { |
| return; |
| } |
| computeVisibleRange(); |
| int deltaBegin = mBufferedVisibleRange.begin - mPreviousDataRange.begin; |
| int deltaEnd = mBufferedVisibleRange.end - mPreviousDataRange.end; |
| if (deltaBegin != 0 || deltaEnd != 0) { |
| // The delta has changed, we have to compute the display items |
| // again. |
| // We find the intersection range, these slots have not changed at |
| // all. |
| int firstVisibleSlotIndex = mBufferedVisibleRange.begin; |
| int lastVisibleSlotIndex = mBufferedVisibleRange.end; |
| mPreviousDataRange.begin = firstVisibleSlotIndex; |
| mPreviousDataRange.end = lastVisibleSlotIndex; |
| |
| Pool<Vector3f> pool = mTempVec; |
| Vector3f position = pool.create(); |
| Vector3f deltaAnchorPosition = pool.create(); |
| try { |
| MediaFeed feed = mMediaFeed; |
| DisplayList displayList = mDisplayList; |
| DisplayItem[] displayItems = mDisplayItems; |
| DisplaySlot[] displaySlots = mDisplaySlots; |
| int numDisplayItems = displayItems.length; |
| int numDisplaySlots = displaySlots.length; |
| ArrayList<MediaItem> visibleItems = mVisibleItems; |
| deltaAnchorPosition.set(mDeltaAnchorPosition); |
| LayoutInterface layout = mLayoutInterface; |
| GridCamera camera = mCamera; |
| for (int i = firstVisibleSlotIndex; i <= lastVisibleSlotIndex; ++i) { |
| GridCameraManager.getSlotPositionForSlotIndex(i, camera, layout, deltaAnchorPosition, position); |
| MediaSet set = feed.getSetForSlot(i); |
| int indexIntoSlots = i - firstVisibleSlotIndex; |
| |
| if (set != null && indexIntoSlots >= 0 && indexIntoSlots < numDisplaySlots) { |
| ArrayList<MediaItem> items = set.getItems(); |
| displaySlots[indexIntoSlots].setMediaSet(set); |
| ArrayList<MediaItem> bestItems = mTempList; |
| { |
| // We always show the same top thumbnails for a |
| // stack of albums |
| // if (mState == STATE_MEDIA_SETS) |
| // ArrayUtils.computeSortedIntersection(items, |
| // visibleItems, MAX_ITEMS_PER_SLOT, bestItems, |
| // sTempHash); |
| // else |
| ArrayUtils.computeSortedIntersection(visibleItems, items, MAX_ITEMS_PER_SLOT, bestItems, mTempHash); |
| } |
| |
| int numItemsInSet = set.getNumItems(); |
| int numBestItems = bestItems.size(); |
| int originallyFoundItems = numBestItems; |
| if (numBestItems < MAX_ITEMS_PER_SLOT) { |
| int itemsRemaining = MAX_ITEMS_PER_SLOT - numBestItems; |
| for (int currItemPos = 0; currItemPos < numItemsInSet; currItemPos++) { |
| MediaItem item = items.get(currItemPos); |
| if (!bestItems.contains(item)) { |
| bestItems.add(item); |
| if (--itemsRemaining == 0) { |
| break; |
| } |
| } |
| } |
| } |
| numBestItems = bestItems.size(); |
| int baseIndex = (i - firstVisibleSlotIndex) * MAX_ITEMS_PER_SLOT; |
| for (int j = 0; j < numBestItems; ++j) { |
| if (baseIndex + j >= numDisplayItems) { |
| break; |
| } |
| if (j >= numItemsInSet) { |
| displayItems[baseIndex + j] = null; |
| } else { |
| MediaItem item = bestItems.get(j); |
| if (item != null) { |
| DisplayItem displayItem = displayList.get(item); |
| if ((mState == STATE_FULL_SCREEN && i != mInputProcessor.getCurrentSelectedSlot()) |
| || (mState == STATE_GRID_VIEW && j >= originallyFoundItems)) { |
| displayItem.set(position, j, false); |
| displayItem.commit(); |
| } else { |
| boolean isTouchPressed = mInputProcessor.touchPressed(); |
| boolean isBarDragged = mHud.getTimeBar().isDragged(); |
| if (mState == STATE_GRID_VIEW |
| && !isTouchPressed |
| && !isBarDragged) { |
| displayItem.mAnimatedPosition.add( |
| 0.0f, 0.0f, i * DEPTH_POSITION); |
| } |
| displayList.setPositionAndStackIndex(displayItem, position, j, true); |
| } |
| displayItems[baseIndex + j] = displayItem; |
| } |
| } |
| } |
| for (int j = numBestItems; j < MAX_ITEMS_PER_SLOT; ++j) { |
| displayItems[baseIndex + j] = null; |
| } |
| bestItems.clear(); |
| } |
| } |
| if (mFeedChanged) { |
| mFeedChanged = false; |
| if (mInputProcessor != null && mState == STATE_FULL_SCREEN && mRequestFocusContentUri == null) { |
| int currentSelectedSlot = mInputProcessor.getCurrentSelectedSlot(); |
| if (currentSelectedSlot > mCompleteRange.end) |
| currentSelectedSlot = mCompleteRange.end; |
| mInputProcessor.setCurrentSelectedSlot(currentSelectedSlot); |
| } |
| if (mState == STATE_GRID_VIEW) { |
| MediaSet expandedSet = mMediaFeed.getExpandedMediaSet(); |
| if (expandedSet != null) { |
| if (mHud != null) { |
| final PathBarLayer pathBar = mHud.getPathBar(); |
| if (pathBar != null) { |
| final String currentLabel = pathBar.getCurrentLabel(); |
| if (currentLabel == null || !currentLabel.equals(expandedSet.mNoCountTitleString)) { |
| pathBar.changeLabel(expandedSet.mNoCountTitleString); |
| } |
| } |
| } |
| } |
| } |
| if (mRequestFocusContentUri != null) { |
| // We have to find the item that has this contentUri |
| int numSlots = mCompleteRange.end + 1; |
| for (int i = 0; i < numSlots; ++i) { |
| MediaSet set = feed.getSetForSlot(i); |
| ArrayList<MediaItem> items = set.getItems(); |
| int numItems = items.size(); |
| for (int j = 0; j < numItems; ++j) { |
| String itemUri = items.get(j).mContentUri; |
| if (itemUri != null && mRequestFocusContentUri != null) { |
| if (itemUri.equals(mRequestFocusContentUri)) { |
| if (mState == STATE_FULL_SCREEN) { |
| mInputProcessor.setCurrentSelectedSlot(i); |
| } else { |
| centerCameraForSlot(i, 1.0f); |
| } |
| break; |
| } |
| } |
| } |
| } |
| mRequestFocusContentUri = null; |
| } |
| } |
| } finally { |
| pool.delete(position); |
| pool.delete(deltaAnchorPosition); |
| } |
| // We keep upto 400 thumbnails in memory. |
| int numThumbnailsToKeepInMemory = (mState == STATE_MEDIA_SETS || mState == STATE_TIMELINE) ? 100 : 400; |
| int startMemoryRange = (mBufferedVisibleRange.begin / numThumbnailsToKeepInMemory) * numThumbnailsToKeepInMemory; |
| if (mStartMemoryRange != startMemoryRange) { |
| mStartMemoryRange = startMemoryRange; |
| clearUnusedThumbnails(); |
| } |
| } |
| } |
| |
| @Override |
| public void handleLowMemory() { |
| clearUnusedThumbnails(); |
| GridDrawables.sStringTextureTable.clear(); |
| mBackground.clearCache(); |
| } |
| |
| // This method can be potentially expensive |
| public void clearUnusedThumbnails() { |
| mDisplayList.clearExcept(mDisplayItems); |
| } |
| |
| @Override |
| public void onSurfaceCreated(RenderView view, GL11 gl) { |
| mDisplayList.clear(); |
| mHud.clear(); |
| mHud.reset(); |
| GridDrawables.sStringTextureTable.clear(); |
| mDrawables.onSurfaceCreated(view, gl); |
| mBackground.clear(); |
| } |
| |
| @Override |
| public void onSurfaceChanged(RenderView view, int width, int height) { |
| mCamera.viewportChanged(width, height, mCamera.mItemWidth, mCamera.mItemHeight); |
| view.setFov(mCamera.mFov); |
| setState(mState); |
| } |
| |
| // Renders the node in a given pass. |
| public void renderOpaque(RenderView view, GL11 gl) { |
| GridCamera camera = mCamera; |
| int selectedSlotIndex = mInputProcessor.getCurrentSelectedSlot(); |
| computeVisibleItems(); |
| |
| gl.glMatrixMode(GL11.GL_MODELVIEW); |
| gl.glLoadIdentity(); |
| GLU.gluLookAt(gl, -camera.mEyeX, -camera.mEyeY, -camera.mEyeZ, -camera.mLookAtX, -camera.mLookAtY, -camera.mLookAtZ, |
| camera.mUpX, camera.mUpY, camera.mUpZ); |
| view.setAlpha(1.0f); |
| if (mSelectedAlpha != 1.0f) { |
| gl.glEnable(GL11.GL_BLEND); |
| gl.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); |
| view.setAlpha(mSelectedAlpha); |
| } |
| if (selectedSlotIndex != Shared.INVALID) { |
| mTargetAlpha = 0.0f; |
| } else { |
| mTargetAlpha = 1.0f; |
| } |
| mDrawManager.prepareDraw(mBufferedVisibleRange, mVisibleRange, selectedSlotIndex, mInputProcessor.getCurrentFocusSlot(), |
| mInputProcessor.getCurrentScaledSlot(), mInputProcessor.isFocusItemPressed(), mInputProcessor.getScale(), |
| mInputProcessor.getScaleGestureDetector(), mFeedAboutToChange); |
| if (mSelectedAlpha != 0.0f) { |
| mDrawManager.drawThumbnails(view, gl, mState); |
| } |
| if (mSelectedAlpha != 1.0f) { |
| gl.glDisable(GL11.GL_BLEND); |
| } |
| // We draw the selected slotIndex. |
| if (selectedSlotIndex != Shared.INVALID) { |
| mDrawManager.drawFocusItems(view, gl, mZoomValue, mSlideshowMode, mTimeElapsedSinceView); |
| mCurrentFocusItemWidth = mDrawManager.getFocusQuadWidth(); |
| mCurrentFocusItemHeight = mDrawManager.getFocusQuadHeight(); |
| } |
| view.setAlpha(mSelectedAlpha); |
| } |
| |
| public void renderBlended(RenderView view, GL11 gl) { |
| // We draw the placeholder for all visible slots. |
| if (mHud != null && mDrawManager != null) { |
| if (mMediaFeed != null) { |
| mDrawManager.drawBlendedComponents(view, gl, mSelectedAlpha, mState, mHud.getMode(), |
| mTimeElapsedSinceStackViewReady, mTimeElapsedSinceGridViewReady, mSelectedBucketList, mMarkedBucketList, |
| mMediaFeed.getWaitingForMediaScanner() || mFeedAboutToChange || mMediaFeed.isLoading()); |
| } |
| } |
| } |
| |
| public synchronized void onLayout(int newAnchorSlotIndex, int currentAnchorSlotIndex, LayoutInterface oldLayout) { |
| if (mPerformingLayoutChange || !mDeltaAnchorPosition.equals(mDeltaAnchorPositionUncommited)) { |
| return; |
| } |
| // mOldBreakSlots = mBreakSlots; |
| if (mState == STATE_GRID_VIEW) { |
| final ArrayList<Integer> breaks = mMediaFeed.getBreaks(); |
| } else { |
| // mBreakSlots = null; |
| } |
| mPerformingLayoutChange = true; |
| LayoutInterface layout = mLayoutInterface; |
| if (oldLayout == null) { |
| oldLayout = sfullScreenLayoutInterface; |
| } |
| GridCamera camera = mCamera; |
| if (currentAnchorSlotIndex == Shared.INVALID) { |
| currentAnchorSlotIndex = getAnchorSlotIndex(ANCHOR_CENTER); |
| if (mCurrentExpandedSlot != Shared.INVALID) { |
| currentAnchorSlotIndex = mCurrentExpandedSlot; |
| } |
| int selectedSlotIndex = mInputProcessor.getCurrentSelectedSlot(); |
| if (selectedSlotIndex != Shared.INVALID) { |
| currentAnchorSlotIndex = selectedSlotIndex; |
| } |
| } |
| if (newAnchorSlotIndex == Shared.INVALID) { |
| newAnchorSlotIndex = currentAnchorSlotIndex; |
| } |
| int itemHeight = camera.mItemHeight; |
| int itemWidth = camera.mItemWidth; |
| Pool<Vector3f> pool = mTempVec; |
| Vector3f deltaAnchorPosition = pool.create(); |
| Vector3f currentSlotPosition = pool.create(); |
| try { |
| deltaAnchorPosition.set(0, 0, 0); |
| if (currentAnchorSlotIndex != Shared.INVALID && newAnchorSlotIndex != Shared.INVALID) { |
| layout.getPositionForSlotIndex(newAnchorSlotIndex, itemWidth, itemHeight, deltaAnchorPosition); |
| oldLayout.getPositionForSlotIndex(currentAnchorSlotIndex, itemWidth, itemHeight, currentSlotPosition); |
| currentSlotPosition.subtract(mDeltaAnchorPosition); |
| deltaAnchorPosition.subtract(currentSlotPosition); |
| deltaAnchorPosition.y = 0; |
| deltaAnchorPosition.z = 0; |
| } |
| mDeltaAnchorPositionUncommited.set(deltaAnchorPosition); |
| } finally { |
| pool.delete(deltaAnchorPosition); |
| pool.delete(currentSlotPosition); |
| } |
| centerCameraForSlot(newAnchorSlotIndex, 1.0f); |
| mCurrentExpandedSlot = Shared.INVALID; |
| |
| // Force recompute of visible items and their positions. |
| ((GridLayoutInterface) oldLayout).mNumRows = ((GridLayoutInterface) layout).mNumRows; |
| ((GridLayoutInterface) oldLayout).mSpacingX = ((GridLayoutInterface) layout).mSpacingX; |
| ((GridLayoutInterface) oldLayout).mSpacingY = ((GridLayoutInterface) layout).mSpacingY; |
| forceRecomputeVisibleRange(); |
| mPerformingLayoutChange = false; |
| } |
| |
| private void forceRecomputeVisibleRange() { |
| mPreviousDataRange.begin = Shared.INVALID; |
| mPreviousDataRange.end = Shared.INVALID; |
| if (mView != null) { |
| mView.requestRender(); |
| } |
| } |
| |
| // called on background thread |
| public synchronized void onFeedChanged(MediaFeed feed, boolean needsLayout) { |
| if (!needsLayout && !mFeedAboutToChange) { |
| mFeedChanged = true; |
| forceRecomputeVisibleRange(); |
| if (mState == STATE_GRID_VIEW || mState == STATE_FULL_SCREEN) |
| mHud.setFeed(feed, mState, needsLayout); |
| return; |
| } |
| |
| while (mPerformingLayoutChange == true) { |
| Thread.yield(); |
| } |
| if (mState == STATE_GRID_VIEW) { |
| if (mHud != null) { |
| MediaSet set = feed.getCurrentSet(); |
| if (set != null && !mLocationFilter) |
| mHud.getPathBar().changeLabel(set.mNoCountTitleString); |
| } |
| } |
| DisplayItem[] displayItems = mDisplayItems; |
| int firstBufferedVisibleSlotIndex = mBufferedVisibleRange.begin; |
| int lastBufferedVisibleSlotIndex = mBufferedVisibleRange.end; |
| int currentlyVisibleSlotIndex = getAnchorSlotIndex(ANCHOR_CENTER); |
| int numVisibleItems = mVisibleRange.end - mVisibleRange.begin + 1; |
| if (mState == STATE_MEDIA_SETS && currentlyVisibleSlotIndex < numVisibleItems) { |
| currentlyVisibleSlotIndex = getAnchorSlotIndex(ANCHOR_LEFT); |
| } |
| if (mCurrentExpandedSlot != Shared.INVALID) { |
| currentlyVisibleSlotIndex = mCurrentExpandedSlot; |
| } |
| MediaItem anchorItem = null; |
| ArrayList<MediaItem> visibleItems = mVisibleItems; |
| visibleItems.clear(); |
| visibleItems.ensureCapacity(lastBufferedVisibleSlotIndex - firstBufferedVisibleSlotIndex); |
| if (currentlyVisibleSlotIndex != Shared.INVALID && currentlyVisibleSlotIndex >= firstBufferedVisibleSlotIndex |
| && currentlyVisibleSlotIndex <= lastBufferedVisibleSlotIndex) { |
| int baseIndex = (currentlyVisibleSlotIndex - firstBufferedVisibleSlotIndex) * MAX_ITEMS_PER_SLOT; |
| for (int i = 0; i < MAX_ITEMS_PER_SLOT; ++i) { |
| DisplayItem displayItem = displayItems[baseIndex + i]; |
| if (displayItem != null) { |
| if (anchorItem == null) { |
| anchorItem = displayItem.mItemRef; |
| } |
| visibleItems.add(displayItem.mItemRef); |
| } |
| } |
| } |
| // We want to add items from the middle. |
| int numItems = lastBufferedVisibleSlotIndex - firstBufferedVisibleSlotIndex + 1; |
| int midPoint = currentlyVisibleSlotIndex; |
| int length = displayItems.length; |
| for (int i = 0; i < numItems; ++i) { |
| int index = midPoint + Shared.midPointIterator(i); |
| int indexIntoDisplayItem = (index - firstBufferedVisibleSlotIndex) * MAX_ITEMS_PER_SLOT; |
| if (indexIntoDisplayItem >= 0 && indexIntoDisplayItem < length) { |
| for (int j = 0; j < MAX_ITEMS_PER_SLOT; ++j) { |
| DisplayItem displayItem = displayItems[indexIntoDisplayItem + j]; |
| if (displayItem != null) { |
| MediaItem item = displayItem.mItemRef; |
| if (!visibleItems.contains(item)) { |
| visibleItems.add(item); |
| } |
| } |
| } |
| } |
| } |
| int newSlotIndex = Shared.INVALID; |
| if (anchorItem != null) { |
| // We try to find the anchor item in the new feed. |
| int numSlots = feed.getNumSlots(); |
| for (int i = 0; i < numSlots; ++i) { |
| MediaSet set = feed.getSetForSlot(i); |
| if (set != null && ArrayUtils.contains(set.getItems(), anchorItem)) { |
| newSlotIndex = i; |
| break; |
| } |
| } |
| } |
| |
| if (anchorItem != null && newSlotIndex == Shared.INVALID) { |
| int numSlots = feed.getNumSlots(); |
| MediaSet parentSet = anchorItem.mParentMediaSet; |
| for (int i = 0; i < numSlots; ++i) { |
| MediaSet set = feed.getSetForSlot(i); |
| if (set != null && set.mId == parentSet.mId) { |
| newSlotIndex = i; |
| break; |
| } |
| } |
| } |
| // We must create a new display store now since the data has changed. |
| Log.i(TAG, "Slot changing from " + currentlyVisibleSlotIndex + " to " + newSlotIndex); |
| if (newSlotIndex != Shared.INVALID) { |
| if (mState == STATE_MEDIA_SETS) { |
| mDisplayList.clearExcept(displayItems); |
| } |
| onLayout(newSlotIndex, currentlyVisibleSlotIndex, null); |
| } else { |
| forceRecomputeVisibleRange(); |
| } |
| mCurrentExpandedSlot = Shared.INVALID; |
| mFeedAboutToChange = false; |
| mFeedChanged = true; |
| if (feed != null) { |
| if (mState == STATE_GRID_VIEW || mState == STATE_FULL_SCREEN) |
| mHud.setFeed(feed, mState, needsLayout); |
| } |
| if (mView != null) { |
| mView.requestRender(); |
| } |
| } |
| |
| public DisplayItem getRepresentativeDisplayItem() { |
| int slotIndex = Shared.INVALID; |
| if (mInputProcessor != null) { |
| slotIndex = mInputProcessor.getCurrentFocusSlot(); |
| } |
| if (slotIndex == Shared.INVALID) { |
| slotIndex = getAnchorSlotIndex(ANCHOR_CENTER); |
| } |
| int index = (slotIndex - mBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT; |
| if (index >= 0 && index < MAX_ITEMS_DRAWABLE) { |
| return mDisplayItems[index]; |
| } else { |
| return null; |
| } |
| } |
| |
| public DisplayItem getAnchorDisplayItem(int type) { |
| int slotIndex = getAnchorSlotIndex(type); |
| int index = (slotIndex - mBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT; |
| if (index >= 0 && index < MAX_ITEMS_DRAWABLE) { |
| return mDisplayItems[index]; |
| } else { |
| return null; |
| } |
| } |
| |
| public float getScrollPosition() { |
| return (mCamera.mLookAtX * mCamera.mScale + mDeltaAnchorPosition.x); // in |
| // pixels |
| } |
| |
| public DisplayItem getDisplayItemForScrollPosition(float posX) { |
| Pool<Vector3f> pool = mTempVecAlt; |
| MediaFeed feed = mMediaFeed; |
| int itemWidth = mCamera.mItemWidth; |
| int itemHeight = mCamera.mItemHeight; |
| GridLayoutInterface gridInterface = (GridLayoutInterface) mLayoutInterface; |
| float absolutePosX = posX; |
| int left = (int) ((absolutePosX / itemWidth) * gridInterface.mNumRows); |
| int right = feed == null ? 0 : (int) (feed.getNumSlots()); |
| int retSlot = left; |
| Vector3f position = pool.create(); |
| try { |
| for (int i = left; i < right; ++i) { |
| gridInterface.getPositionForSlotIndex(i, itemWidth, itemHeight, position); |
| retSlot = i; |
| if (position.x >= absolutePosX) { |
| break; |
| } |
| } |
| } finally { |
| pool.delete(position); |
| } |
| if (mFeedAboutToChange) { |
| return null; |
| } |
| right = feed == null ? 0 : feed.getNumSlots(); |
| if (right == 0) { |
| return null; |
| } |
| |
| if (retSlot >= right) |
| retSlot = right - 1; |
| MediaSet set = feed.getSetForSlot(retSlot); |
| if (set != null) { |
| ArrayList<MediaItem> items = set.getItems(); |
| if (items != null && set.getNumItems() > 0) { |
| return (mDisplayList.get(items.get(0))); |
| } |
| } |
| return null; |
| } |
| |
| // Returns the top left-most item. |
| public int getAnchorSlotIndex(int anchorType) { |
| int retVal = 0; |
| switch (anchorType) { |
| case ANCHOR_LEFT: |
| retVal = mVisibleRange.begin; |
| break; |
| case ANCHOR_RIGHT: |
| retVal = mVisibleRange.end; |
| break; |
| case ANCHOR_CENTER: |
| retVal = (mVisibleRange.begin + mVisibleRange.end) / 2; |
| break; |
| } |
| return retVal; |
| } |
| |
| DisplayItem getDisplayItemForSlotId(int slotId) { |
| int index = slotId - mBufferedVisibleRange.begin; |
| if (index >= 0 && slotId <= mBufferedVisibleRange.end) { |
| return mDisplayItems[index * MAX_ITEMS_PER_SLOT]; |
| } |
| return null; |
| } |
| |
| boolean changeFocusToNextSlot(float convergence) { |
| int currentSelectedSlot = mInputProcessor.getCurrentSelectedSlot(); |
| boolean retVal = changeFocusToSlot(currentSelectedSlot + 1, convergence); |
| if (mInputProcessor.getCurrentSelectedSlot() == currentSelectedSlot) { |
| endSlideshow(); |
| mHud.setAlpha(1.0f); |
| } |
| return retVal; |
| } |
| |
| boolean changeFocusToSlot(int slotId, float convergence) { |
| mZoomValue = 1.0f; |
| int index = slotId - mBufferedVisibleRange.begin; |
| if (index >= 0 && slotId <= mBufferedVisibleRange.end) { |
| DisplayItem displayItem = mDisplayItems[index * MAX_ITEMS_PER_SLOT]; |
| if (displayItem != null) { |
| MediaItem item = displayItem.mItemRef; |
| mHud.fullscreenSelectionChanged(item, slotId + 1, mCompleteRange.end + 1); |
| if (slotId != Shared.INVALID && slotId <= mCompleteRange.end) { |
| mInputProcessor.setCurrentFocusSlot(slotId); |
| centerCameraForSlot(slotId, convergence); |
| return true; |
| } else { |
| centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), convergence); |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| |
| boolean changeFocusToPreviousSlot(float convergence) { |
| return changeFocusToSlot(mInputProcessor.getCurrentSelectedSlot() - 1, convergence); |
| } |
| |
| public ArrayList<MediaBucket> getSelectedBuckets() { |
| return mSelectedBucketList.get(); |
| } |
| |
| public void selectAll() { |
| if (mState != STATE_FULL_SCREEN) { |
| int numSlots = mCompleteRange.end + 1; |
| for (int i = 0; i < numSlots; ++i) { |
| addSlotToSelectedItems(i, false, false); |
| } |
| updateCountOfSelectedItems(); |
| } else { |
| addSlotToSelectedItems(mInputProcessor.getCurrentFocusSlot(), false, true); |
| } |
| } |
| |
| public void deselectOrCancelSelectMode() { |
| if (mSelectedBucketList.size() == 0) { |
| mHud.cancelSelection(); |
| } else { |
| mSelectedBucketList.clear(); |
| updateCountOfSelectedItems(); |
| } |
| } |
| |
| public void deselectAll() { |
| mHud.cancelSelection(); |
| mSelectedBucketList.clear(); |
| updateCountOfSelectedItems(); |
| } |
| |
| public void deleteSelection() { |
| // Delete the selection and exit selection mode. |
| mMediaFeed.performOperation(MediaFeed.OPERATION_DELETE, getSelectedBuckets(), null); |
| deselectAll(); |
| |
| // If the current set is now empty, return to the parent set. |
| if (mCompleteRange.isEmpty()) { |
| goBack(); // TODO(venkat): This does not work most of the time, can |
| // you take a look? |
| } |
| } |
| |
| void addSlotToSelectedItems(int slotId, boolean removeIfAlreadyAdded, boolean updateCount) { |
| // mMediaFeed may be null because setDataSource() may not be called yet. |
| if (mFeedAboutToChange == false && mMediaFeed != null) { |
| MediaFeed feed = mMediaFeed; |
| mSelectedBucketList.add(slotId, feed, removeIfAlreadyAdded); |
| if (updateCount) { |
| updateCountOfSelectedItems(); |
| if (mSelectedBucketList.size() == 0) |
| deselectAll(); |
| } |
| } |
| mHud.computeBottomMenu(); |
| } |
| |
| private void updateCountOfSelectedItems() { |
| mHud.updateNumItemsSelected(mSelectedBucketList.size()); |
| } |
| |
| public int getMetadataSlotIndexForScreenPosition(int posX, int posY) { |
| return getSlotForScreenPosition(posX, posY, mCamera.mItemWidth + (int) (100 * App.PIXEL_DENSITY), mCamera.mItemHeight |
| + (int) (100 * App.PIXEL_DENSITY)); |
| } |
| |
| public int getSlotIndexForScreenPosition(int posX, int posY) { |
| return getSlotForScreenPosition(posX, posY, mCamera.mItemWidth, mCamera.mItemHeight); |
| } |
| |
| private int getSlotForScreenPosition(int posX, int posY, int itemWidth, int itemHeight) { |
| Pool<Vector3f> pool = mTempVec; |
| int retVal = 0; |
| Vector3f worldPos = pool.create(); |
| try { |
| GridCamera camera = mCamera; |
| camera.convertToCameraSpace(posX, posY, 0, worldPos); |
| // slots are expressed in pixels as well |
| worldPos.x *= camera.mScale; |
| worldPos.y *= camera.mScale; |
| // we ignore z |
| retVal = hitTest(worldPos, itemWidth, itemHeight); |
| } finally { |
| pool.delete(worldPos); |
| } |
| return retVal; |
| } |
| |
| public boolean tapGesture(int slotIndex, boolean metadata) { |
| MediaFeed feed = mMediaFeed; |
| if (feed == null) |
| return false; |
| if (!feed.isClustered()) { |
| // It is not clustering. |
| if (!feed.hasExpandedMediaSet()) { |
| if (feed.canExpandSet(slotIndex)) { |
| mCurrentExpandedSlot = slotIndex; |
| feed.expandMediaSet(slotIndex); |
| setState(STATE_GRID_VIEW); |
| } |
| return false; |
| } else { |
| return true; |
| } |
| } else { |
| // Select a cluster, and recompute a new cluster within this |
| // cluster. |
| mCurrentExpandedSlot = slotIndex; |
| mMarkedBucketList.clear(); |
| mMarkedBucketList.add(slotIndex, feed, false); |
| goBack(); |
| if (metadata) { |
| DisplaySlot slot = mDisplaySlots[slotIndex - mBufferedVisibleRange.begin]; |
| if (slot.hasValidLocation()) { |
| MediaSet set = slot.getMediaSet(); |
| if (set.mReverseGeocodedLocation != null) { |
| enableLocationFiltering(set.mReverseGeocodedLocation); |
| } |
| feed.setFilter(new LocationMediaFilter(set.mMinLatLatitude, set.mMinLonLongitude, set.mMaxLatLatitude, |
| set.mMaxLonLongitude)); |
| } |
| } |
| return false; |
| } |
| } |
| |
| public void onTimeChanged(TimeBar timebar) { |
| if (mFeedAboutToChange) { |
| return; |
| } |
| // TODO lot of optimization possible here |
| MediaItem item = timebar.getItem(); |
| if (item == null) |
| return; |
| MediaFeed feed = mMediaFeed; |
| if (feed == null) |
| return; |
| int numSlots = feed.getNumSlots(); |
| for (int i = 0; i < numSlots; ++i) { |
| MediaSet set = feed.getSetForSlot(i); |
| if (set == null) { |
| return; |
| } |
| ArrayList<MediaItem> items = set.getItems(); |
| if (items == null || set.getNumItems() == 0) { |
| return; |
| } |
| if (ArrayUtils.contains(items, item)) { |
| centerCameraForSlot(i, 1.0f); |
| break; |
| } |
| } |
| } |
| |
| public void onFeedAboutToChange(MediaFeed feed) { |
| mFeedAboutToChange = true; |
| } |
| |
| public void startSlideshow() { |
| endSlideshow(); |
| mSlideshowMode = true; |
| mZoomValue = 1.0f; |
| centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); |
| mTimeElapsedSinceView = SLIDESHOW_TRANSITION_TIME - 1.0f; |
| mHud.setAlpha(0); |
| PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); |
| mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GridView.Slideshow"); |
| mWakeLock.acquire(); |
| } |
| |
| public void enterSelectionMode() { |
| mSlideshowMode = false; |
| mHud.enterSelectionMode(); |
| int currentSlot = mInputProcessor.getCurrentSelectedSlot(); |
| if (currentSlot == Shared.INVALID) { |
| currentSlot = mInputProcessor.getCurrentFocusSlot(); |
| } |
| addSlotToSelectedItems(currentSlot, false, true); |
| } |
| |
| private float getFillScreenZoomValue() { |
| return GridCameraManager.getFillScreenZoomValue(mCamera, mTempVec, mCurrentFocusItemWidth, mCurrentFocusItemHeight); |
| } |
| |
| public void zoomInToSelectedItem() { |
| mSlideshowMode = false; |
| float potentialZoomValue = getFillScreenZoomValue(); |
| if (mZoomValue < potentialZoomValue) { |
| mZoomValue = potentialZoomValue; |
| } else { |
| mZoomValue *= 3.0f; |
| } |
| if (mZoomValue > 6.0f) { |
| mZoomValue = 6.0f; |
| } |
| mHud.setAlpha(1.0f); |
| centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); |
| } |
| |
| public void zoomOutFromSelectedItem() { |
| mSlideshowMode = false; |
| if (mZoomValue == getFillScreenZoomValue()) { |
| mZoomValue = 1.0f; |
| } else { |
| mZoomValue /= 3.0f; |
| } |
| if (mZoomValue < 1.0f) { |
| mZoomValue = 1.0f; |
| } |
| mHud.setAlpha(1.0f); |
| centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); |
| } |
| |
| public void rotateSelectedItems(float f) { |
| MediaBucketList bucketList = mSelectedBucketList; |
| ArrayList<MediaBucket> mediaBuckets = bucketList.get(); |
| DisplayList displayList = mDisplayList; |
| int numBuckets = mediaBuckets.size(); |
| for (int i = 0; i < numBuckets; ++i) { |
| MediaBucket bucket = mediaBuckets.get(i); |
| ArrayList<MediaItem> mediaItems = bucket.mediaItems; |
| if (mediaItems != null) { |
| int numMediaItems = mediaItems.size(); |
| for (int j = 0; j < numMediaItems; ++j) { |
| MediaItem item = mediaItems.get(j); |
| DisplayItem displayItem = displayList.get(item); |
| displayItem.rotateImageBy(f); |
| displayList.addToAnimatables(displayItem); |
| } |
| } |
| } |
| if (mState == STATE_FULL_SCREEN) { |
| centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); |
| } |
| mMediaFeed.performOperation(MediaFeed.OPERATION_ROTATE, mediaBuckets, new Float(f)); |
| // we recreate these displayitems from the cache |
| } |
| |
| public void cropSelectedItem() { |
| |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent event) { |
| return mInputProcessor.onTouchEvent(event); |
| } |
| |
| @Override |
| public boolean onKeyDown(int keyCode, KeyEvent event) { |
| if (mInputProcessor != null) |
| return mInputProcessor.onKeyDown(keyCode, event, mState); |
| return false; |
| } |
| |
| public boolean inSlideShowMode() { |
| return mSlideshowMode; |
| } |
| |
| public boolean noDeleteMode() { |
| return mNoDeleteMode || (mMediaFeed != null && mMediaFeed.isSingleImageMode()); |
| } |
| |
| public float getZoomValue() { |
| return mZoomValue; |
| } |
| |
| public boolean feedAboutToChange() { |
| return mFeedAboutToChange; |
| } |
| |
| public boolean isInAlbumMode() { |
| return mInAlbum; |
| } |
| |
| public Vector3f getDeltaAnchorPosition() { |
| return mDeltaAnchorPosition; |
| } |
| |
| public int getExpandedSlot() { |
| return mCurrentExpandedSlot; |
| } |
| |
| public GridLayoutInterface getLayoutInterface() { |
| return (GridLayoutInterface) mLayoutInterface; |
| } |
| |
| public void setZoomValue(float f) { |
| mZoomValue = f; |
| centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f); |
| } |
| |
| public void setPickIntent(boolean b) { |
| mPickIntent = b; |
| mHud.getPathBar().popLabel(); |
| mHud.getPathBar().pushLabel(Res.drawable.icon_location_small, mContext.getResources().getString(Res.string.pick), |
| new Runnable() { |
| public void run() { |
| if (mHud.getAlpha() == 1.0f) { |
| if (!mFeedAboutToChange) { |
| setState(STATE_MEDIA_SETS); |
| } |
| } else { |
| mHud.setAlpha(1.0f); |
| } |
| } |
| }); |
| } |
| |
| public boolean getPickIntent() { |
| return mPickIntent; |
| } |
| |
| public void setViewIntent(boolean b, final String setName) { |
| mViewIntent = b; |
| if (b) { |
| mMediaFeed.expandMediaSet(0); |
| setState(STATE_GRID_VIEW); |
| // We need to make sure we haven't pushed the same label twice |
| if (mHud.getPathBar().getNumLevels() == 1) { |
| mHud.getPathBar().pushLabel(Res.drawable.icon_folder_small, setName, new Runnable() { |
| public void run() { |
| if (mFeedAboutToChange) { |
| return; |
| } |
| if (mHud.getAlpha() == 1.0f) { |
| disableLocationFiltering(); |
| if (mInputProcessor != null) |
| mInputProcessor.clearSelection(); |
| setState(STATE_GRID_VIEW); |
| } else { |
| mHud.setAlpha(1.0f); |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| public boolean getViewIntent() { |
| return mViewIntent; |
| } |
| |
| public void setSingleImage(boolean noDeleteMode) { |
| mNoDeleteMode = noDeleteMode; |
| mInputProcessor.setCurrentSelectedSlot(0); |
| } |
| |
| public MediaFeed getFeed() { |
| return mMediaFeed; |
| } |
| |
| public void markDirty(int numFrames) { |
| mFramesDirty = numFrames; |
| } |
| |
| public void focusItem(String contentUri) { |
| mRequestFocusContentUri = contentUri; |
| if (mMediaFeed != null) { |
| mMediaFeed.updateListener(false); |
| } |
| } |
| |
| public void onResume() { |
| if (mMediaFeed != null) { |
| mMediaFeed.onResume(); |
| } |
| } |
| |
| public void onPause() { |
| if (mMediaFeed != null) { |
| mMediaFeed.onPause(); |
| } |
| } |
| |
| public void setEnterSelectionMode(boolean enterSelection) { |
| mRequestToEnterSelection = enterSelection; |
| } |
| } |