OSDN Git Service

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