OSDN Git Service

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