OSDN Git Service

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