Home | History | Annotate | Download | only in media
      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 
     17 package com.cooliris.media;
     18 
     19 import java.util.ArrayList;
     20 import javax.microedition.khronos.opengles.GL11;
     21 
     22 import android.hardware.SensorEvent;
     23 import android.opengl.GLU;
     24 import android.os.PowerManager;
     25 import android.os.PowerManager.WakeLock;
     26 import android.util.Log;
     27 import android.view.KeyEvent;
     28 import android.view.MotionEvent;
     29 import android.content.Context;
     30 
     31 import com.cooliris.app.App;
     32 import com.cooliris.app.Res;
     33 
     34 public 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