OSDN Git Service

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