• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javaandroidc++linuxc#windowsobjective-ccocoaqt誰得pythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

packages/apps/Gallery2


Commit MetaInfo

修订版d8a6d17db8aaef2ea06c78fe643f06601d8aec5d (tree)
时间2009-12-01 05:11:59
作者Dave Sparks <davidsparks@andr...>
CommiterDave Sparks

Log Message

Updates to 3D Gallery.

更改概述

差异

--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -139,7 +139,8 @@
139139 <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
140140 </receiver>
141141
142- <!-- We configure a widget by asking to pick a photo, then crop it, and store the config internally -->
142+ <!-- We configure a widget by asking to pick a photo, then crop it, and store the config internally
143+-->
143144 <activity android:name="PhotoAppWidgetConfigure">
144145 <intent-filter>
145146 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
@@ -148,7 +149,8 @@
148149
149150 <!-- We also allow direct binding where the caller provides a bitmap and
150151 appWidgetId to bind. We require the permission because this changes our
151- internal database without user confirmation. -->
152+ internal database without user confirmation.
153+-->
152154 <activity android:name="PhotoAppWidgetBind" android:exported="true"
153155 android:theme="@android:style/Theme.NoDisplay"
154156 android:permission="android.permission.BIND_APPWIDGET" />
Binary files a/res/drawable/default_background.png and b/res/drawable/default_background.png differ
--- a/src/com/cooliris/cache/BootReceiver.java
+++ b/src/com/cooliris/cache/BootReceiver.java
@@ -10,11 +10,11 @@ import android.net.Uri;
1010 import android.util.Log;
1111
1212 public class BootReceiver extends BroadcastReceiver {
13- private final String TAG = "BootReceiver";
13+ private static final String TAG = "BootReceiver";
1414
1515 @Override
1616 public void onReceive(Context context, Intent intent) {
17- String action = intent.getAction();
17+ final String action = intent.getAction();
1818 Log.i(TAG, "Got intent with action " + action);
1919 if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)) {
2020 CacheService.markDirty(context);
@@ -23,8 +23,8 @@ public class BootReceiver extends BroadcastReceiver {
2323 // Do nothing, wait for the mediascanner to be done after mounting.
2424 ;
2525 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)) {
26- Uri fileUri = intent.getData();
27- long bucketId = SingleDataSource.parseBucketIdFromFileUri(fileUri.toString());
26+ final Uri fileUri = intent.getData();
27+ final long bucketId = SingleDataSource.parseBucketIdFromFileUri(fileUri.toString());
2828 if (!CacheService.isPresentInCache(bucketId)) {
2929 CacheService.markDirty(context);
3030 }
--- a/src/com/cooliris/cache/CacheService.java
+++ b/src/com/cooliris/cache/CacheService.java
@@ -40,6 +40,7 @@ import android.util.Log;
4040
4141 import com.cooliris.media.DataSource;
4242 import com.cooliris.media.DiskCache;
43+import com.cooliris.media.Gallery;
4344 import com.cooliris.media.LocalDataSource;
4445 import com.cooliris.media.LongSparseArray;
4546 import com.cooliris.media.MediaFeed;
@@ -52,10 +53,12 @@ import com.cooliris.media.UriTexture;
5253 import com.cooliris.media.Utils;
5354
5455 public final class CacheService extends IntentService {
55- private static final String TAG = "CacheService";
5656 public static final String ACTION_CACHE = "com.cooliris.cache.action.CACHE";
5757 public static final DiskCache sAlbumCache = new DiskCache("local-album-cache");
5858
59+ private static final String TAG = "CacheService";
60+ private static ImageList sList = null;
61+
5962 // Wait 2 seconds to start the thumbnailer so that the application can load without any overheads.
6063 private static final int THUMBNAILER_WAIT_IN_MS = 2000;
6164 private static final int DEFAULT_THUMBNAIL_WIDTH = 128;
@@ -81,8 +84,9 @@ public final class CacheService extends IntentService {
8184 public static final int THUMBNAIL_ID_INDEX = 0;
8285 public static final int THUMBNAIL_DATE_MODIFIED_INDEX = 1;
8386 public static final int THUMBNAIL_DATA_INDEX = 2;
87+ public static final int THUMBNAIL_ORIENTATION_INDEX = 2;
8488 public static final String[] THUMBNAIL_PROJECTION = new String[] { Images.ImageColumns._ID, Images.ImageColumns.DATE_MODIFIED,
85- Images.ImageColumns.DATA };
89+ Images.ImageColumns.DATA, Images.ImageColumns.ORIENTATION };
8690
8791 // Must preserve order between these indices and the order of the terms in INITIAL_PROJECTION_IMAGES and
8892 // INITIAL_PROJECTION_VIDEOS.
@@ -121,26 +125,27 @@ public final class CacheService extends IntentService {
121125
122126 private static final DateFormat mDateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
123127 private static final DateFormat mAltDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
128+ private static final byte[] sDummyData = new byte[] { 1 };
124129 private static boolean QUEUE_DIRTY_SET;
125130 private static boolean QUEUE_DIRTY_ALL;
126131
127- public static String getCachePath(String subFolderName) {
132+ public static final String getCachePath(final String subFolderName) {
128133 return Environment.getExternalStorageDirectory() + "/Android/data/com.cooliris.media/cache/" + subFolderName;
129134 }
130135
131- public static void startCache(final Context context, boolean checkthumbnails) {
132- Locale locale = getLocaleForAlbumCache();
133- Locale defaultLocale = Locale.getDefault();
136+ public static final void startCache(final Context context, final boolean checkthumbnails) {
137+ final Locale locale = getLocaleForAlbumCache();
138+ final Locale defaultLocale = Locale.getDefault();
134139 if (locale == null || !locale.equals(defaultLocale)) {
135140 sAlbumCache.deleteAll();
136- putLocaleForAlbumCache(Locale.getDefault());
141+ putLocaleForAlbumCache(defaultLocale);
137142 }
138143 final Intent intent = new Intent(ACTION_CACHE, null, context, CacheService.class);
139144 intent.putExtra("checkthumbnails", checkthumbnails);
140145 context.startService(intent);
141146 }
142147
143- public static boolean isCacheReady(final boolean onlyMediaSets) {
148+ public static final boolean isCacheReady(final boolean onlyMediaSets) {
144149 if (onlyMediaSets) {
145150 return (sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0) != null && sAlbumCache.get(ALBUM_CACHE_DIRTY_INDEX, 0) == null);
146151 } else {
@@ -149,17 +154,17 @@ public final class CacheService extends IntentService {
149154 }
150155 }
151156
152- public static boolean isCacheReady(long setId) {
153- boolean isReady = (sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0) != null
157+ public static final boolean isCacheReady(final long setId) {
158+ final boolean isReady = (sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0) != null
154159 && sAlbumCache.get(ALBUM_CACHE_DIRTY_INDEX, 0) == null && sAlbumCache.get(ALBUM_CACHE_INCOMPLETE_INDEX, 0) == null);
155160 if (!isReady) {
156161 return isReady;
157162 }
158163 // Also, we need to check if this setId is dirty.
159- byte[] existingData = sAlbumCache.get(ALBUM_CACHE_DIRTY_BUCKET_INDEX, 0);
164+ final byte[] existingData = sAlbumCache.get(ALBUM_CACHE_DIRTY_BUCKET_INDEX, 0);
160165 if (existingData != null && existingData.length > 0) {
161- long[] ids = toLongArray(existingData);
162- int numIds = ids.length;
166+ final long[] ids = toLongArray(existingData);
167+ final int numIds = ids.length;
163168 for (int i = 0; i < numIds; ++i) {
164169 if (ids[i] == setId) {
165170 return false;
@@ -169,13 +174,13 @@ public final class CacheService extends IntentService {
169174 return true;
170175 }
171176
172- public static boolean isPresentInCache(long setId) {
177+ public static final boolean isPresentInCache(final long setId) {
173178 return sAlbumCache.get(setId, 0) != null;
174179 }
175180
176- public final static void markDirty(final Context context) {
177- byte[] data = new byte[] { 1 };
178- sAlbumCache.put(ALBUM_CACHE_DIRTY_INDEX, data);
181+ public static final void markDirty(final Context context) {
182+ sList = null;
183+ sAlbumCache.put(ALBUM_CACHE_DIRTY_INDEX, sDummyData);
179184 if (CACHE_THREAD.get() == null) {
180185 restartThread(CACHE_THREAD, "CacheRefresh", new Runnable() {
181186 public void run() {
@@ -187,12 +192,13 @@ public final class CacheService extends IntentService {
187192 }
188193 }
189194
190- public final static void markDirtyImmediate(long id) {
195+ public static final void markDirtyImmediate(final long id) {
196+ sList = null;
191197 byte[] data = longToByteArray(id);
192- byte[] existingData = sAlbumCache.get(ALBUM_CACHE_DIRTY_BUCKET_INDEX, 0);
198+ final byte[] existingData = sAlbumCache.get(ALBUM_CACHE_DIRTY_BUCKET_INDEX, 0);
193199 if (existingData != null && existingData.length > 0) {
194- long[] ids = toLongArray(existingData);
195- int numIds = ids.length;
200+ final long[] ids = toLongArray(existingData);
201+ final int numIds = ids.length;
196202 for (int i = 0; i < numIds; ++i) {
197203 if (ids[i] == id) {
198204 return;
@@ -204,7 +210,7 @@ public final class CacheService extends IntentService {
204210 sAlbumCache.put(ALBUM_CACHE_DIRTY_BUCKET_INDEX, data);
205211 }
206212
207- public final static void markDirty(final Context context, long id) {
213+ public static final void markDirty(final Context context, final long id) {
208214 markDirtyImmediate(id);
209215 if (CACHE_THREAD.get() == null) {
210216 restartThread(CACHE_THREAD, "CacheRefreshDirtySets", new Runnable() {
@@ -217,10 +223,10 @@ public final class CacheService extends IntentService {
217223 }
218224 }
219225
220- public static boolean setHasItems(ContentResolver cr, long setId) {
226+ public static final boolean setHasItems(final ContentResolver cr, final long setId) {
221227 final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI;
222228 final Uri uriVideos = Video.Media.EXTERNAL_CONTENT_URI;
223- StringBuffer whereString = new StringBuffer(Images.ImageColumns.BUCKET_ID + "=" + setId);
229+ final StringBuffer whereString = new StringBuffer(Images.ImageColumns.BUCKET_ID + "=" + setId);
224230 final Cursor cursorImages = cr.query(uriImages, BUCKET_PROJECTION_IMAGES, whereString.toString(), null, null);
225231 if (cursorImages != null && cursorImages.getCount() > 0) {
226232 cursorImages.close();
@@ -234,7 +240,7 @@ public final class CacheService extends IntentService {
234240 return false;
235241 }
236242
237- public static void loadMediaSets(final MediaFeed feed, final DataSource source, final boolean includeImages,
243+ public static final void loadMediaSets(final MediaFeed feed, final DataSource source, final boolean includeImages,
238244 final boolean includeVideos) {
239245 int timeElapsed = 0;
240246 while (!isCacheReady(true) && timeElapsed < 10000) {
@@ -245,7 +251,7 @@ public final class CacheService extends IntentService {
245251 }
246252 timeElapsed += 300;
247253 }
248- byte[] albumData = sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0);
254+ final byte[] albumData = sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0);
249255 if (albumData != null && albumData.length > 0) {
250256 final DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(albumData), 256));
251257 try {
@@ -278,7 +284,7 @@ public final class CacheService extends IntentService {
278284 }
279285 }
280286
281- public static void loadMediaSet(final MediaFeed feed, final DataSource source, final long bucketId) {
287+ public static final void loadMediaSet(final MediaFeed feed, final DataSource source, final long bucketId) {
282288 int timeElapsed = 0;
283289 while (!isCacheReady(false) && timeElapsed < 10000) {
284290 try {
@@ -288,13 +294,13 @@ public final class CacheService extends IntentService {
288294 }
289295 timeElapsed += 300;
290296 }
291- byte[] albumData = sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0);
297+ final byte[] albumData = sAlbumCache.get(ALBUM_CACHE_METADATA_INDEX, 0);
292298 if (albumData != null && albumData.length > 0) {
293299 DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(albumData), 256));
294300 try {
295- int numAlbums = dis.readInt();
301+ final int numAlbums = dis.readInt();
296302 for (int i = 0; i < numAlbums; ++i) {
297- long setId = dis.readLong();
303+ final long setId = dis.readLong();
298304 MediaSet mediaSet = null;
299305 if (setId == bucketId) {
300306 mediaSet = feed.getMediaSet(setId);
@@ -321,7 +327,7 @@ public final class CacheService extends IntentService {
321327 }
322328 }
323329
324- public static void loadMediaItemsIntoMediaFeed(final MediaFeed feed, final MediaSet set, final int rangeStart,
330+ public static final void loadMediaItemsIntoMediaFeed(final MediaFeed feed, final MediaSet set, final int rangeStart,
325331 final int rangeEnd, final boolean includeImages, final boolean includeVideos) {
326332 int timeElapsed = 0;
327333 byte[] albumData = null;
@@ -337,13 +343,13 @@ public final class CacheService extends IntentService {
337343 if (albumData != null && set.mNumItemsLoaded < set.getNumExpectedItems()) {
338344 final DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(albumData), 256));
339345 try {
340- int numItems = dis.readInt();
346+ final int numItems = dis.readInt();
341347 Log.i(TAG, "Loading Set Id " + set.mId + " with " + numItems + " items.");
342348 set.setNumExpectedItems(numItems);
343349 set.mMinTimestamp = dis.readLong();
344350 set.mMaxTimestamp = dis.readLong();
345351 for (int i = 0; i < numItems; ++i) {
346- MediaItem item = new MediaItem();
352+ final MediaItem item = new MediaItem();
347353 // Must preserve order with method that writes to cache.
348354 item.mId = dis.readLong();
349355 item.mCaption = Utils.readUTF(dis);
@@ -380,61 +386,65 @@ public final class CacheService extends IntentService {
380386 set.generateTitle(true);
381387 }
382388
383- public static void populateVideoItemFromCursor(MediaItem item, ContentResolver cr, Cursor cursor, String baseUri) {
389+ public static final void populateVideoItemFromCursor(final MediaItem item, final ContentResolver cr, final Cursor cursor,
390+ final String baseUri) {
384391 item.setMediaType(MediaItem.MEDIA_TYPE_VIDEO);
385392 populateMediaItemFromCursor(item, cr, cursor, baseUri);
386393 }
387394
388- public static void populateMediaItemFromCursor(MediaItem item, ContentResolver cr, Cursor cursor, String baseUri) {
395+ public static final void populateMediaItemFromCursor(final MediaItem item, final ContentResolver cr, final Cursor cursor,
396+ final String baseUri) {
389397 item.mId = cursor.getLong(CacheService.MEDIA_ID_INDEX);
390- item.mCaption = cursor.getString(CacheService.MEDIA_CAPTION_INDEX);
398+ //item.mCaption = cursor.getString(CacheService.MEDIA_CAPTION_INDEX);
391399 item.mMimeType = cursor.getString(CacheService.MEDIA_MIME_TYPE_INDEX);
392400 item.mLatitude = cursor.getDouble(CacheService.MEDIA_LATITUDE_INDEX);
393401 item.mLongitude = cursor.getDouble(CacheService.MEDIA_LONGITUDE_INDEX);
394402 item.mDateTakenInMs = cursor.getLong(CacheService.MEDIA_DATE_TAKEN_INDEX);
395403 item.mDateAddedInSec = cursor.getLong(CacheService.MEDIA_DATE_ADDED_INDEX);
396404 item.mDateModifiedInSec = cursor.getLong(CacheService.MEDIA_DATE_MODIFIED_INDEX);
405+ if (item.mDateTakenInMs == item.mDateModifiedInSec) {
406+ item.mDateTakenInMs = item.mDateModifiedInSec * 1000;
407+ }
397408 item.mFilePath = cursor.getString(CacheService.MEDIA_DATA_INDEX);
398-
399- int itemMediaType = item.getMediaType();
409+ if (baseUri != null)
410+ item.mContentUri = baseUri + item.mId;
411+ final int itemMediaType = item.getMediaType();
400412 // Check to see if a new date taken is available.
401- long dateTaken = fetchDateTaken(item);
402- if (dateTaken != -1L) {
413+ final long dateTaken = fetchDateTaken(item);
414+ if (dateTaken != -1L && item.mContentUri != null) {
403415 item.mDateTakenInMs = dateTaken;
404- ContentValues values = new ContentValues();
416+ final ContentValues values = new ContentValues();
405417 if (itemMediaType == MediaItem.MEDIA_TYPE_VIDEO) {
406418 values.put(Video.VideoColumns.DATE_TAKEN, item.mDateTakenInMs);
407419 } else {
408420 values.put(Images.ImageColumns.DATE_TAKEN, item.mDateTakenInMs);
409421 }
410- if (item.mContentUri != null) {
411- cr.update(Uri.parse(item.mContentUri), values, null, null);
412- }
422+ cr.update(Uri.parse(item.mContentUri), values, null, null);
413423 }
414424
415- int orientationDurationValue = cursor.getInt(CacheService.MEDIA_ORIENTATION_OR_DURATION_INDEX);
425+ final int orientationDurationValue = cursor.getInt(CacheService.MEDIA_ORIENTATION_OR_DURATION_INDEX);
416426 if (itemMediaType == MediaItem.MEDIA_TYPE_IMAGE) {
417427 item.mRotation = orientationDurationValue;
418428 } else {
419429 item.mDurationInSec = orientationDurationValue;
420430 }
421- item.mContentUri = baseUri + item.mId;
422431 }
423432
424433 // Returns -1 if we failed to examine EXIF information or EXIF parsing failed.
425- public static long fetchDateTaken(MediaItem item) {
434+ public static final long fetchDateTaken(final MediaItem item) {
426435 if (!item.isDateTakenValid() && !item.mTriedRetrievingExifDateTaken
427436 && (item.mFilePath.endsWith(".jpg") || item.mFilePath.endsWith(".jpeg"))) {
428437 try {
429- ExifInterface exif = new ExifInterface(item.mFilePath);
430- String dateTakenStr = exif.getAttribute(ExifInterface.TAG_DATETIME);
438+ Log.i(TAG, "Parsing date taken from exif");
439+ final ExifInterface exif = new ExifInterface(item.mFilePath);
440+ final String dateTakenStr = exif.getAttribute(ExifInterface.TAG_DATETIME);
431441 if (dateTakenStr != null) {
432442 try {
433- Date dateTaken = mDateFormat.parse(dateTakenStr);
443+ final Date dateTaken = mDateFormat.parse(dateTakenStr);
434444 return dateTaken.getTime();
435445 } catch (ParseException pe) {
436446 try {
437- Date dateTaken = mAltDateFormat.parse(dateTakenStr);
447+ final Date dateTaken = mAltDateFormat.parse(dateTakenStr);
438448 return dateTaken.getTime();
439449 } catch (ParseException pe2) {
440450 Log.i(TAG, "Unable to parse date out of string - " + dateTakenStr);
@@ -451,73 +461,92 @@ public final class CacheService extends IntentService {
451461 return -1L;
452462 }
453463
454- public static byte[] queryThumbnail(final Context context, final long thumbId, final long origId, final boolean isVideo,
464+ public static final byte[] queryThumbnail(final Context context, final long thumbId, final long origId, final boolean isVideo,
455465 final long timestamp) {
456466 final DiskCache thumbnailCache = (isVideo) ? LocalDataSource.sThumbnailCacheVideo : LocalDataSource.sThumbnailCache;
457467 return queryThumbnail(context, thumbId, origId, isVideo, thumbnailCache, timestamp);
458468 }
459469
460- private static byte[] queryThumbnail(final Context context, final long thumbId, final long origId, final boolean isVideo,
461- final DiskCache thumbnailCache, final long timestamp) {
462- Thread thumbnailThread = THUMBNAIL_THREAD.getAndSet(null);
463- if (thumbnailThread != null) {
464- thumbnailThread.interrupt();
465- }
466- byte[] bitmap = thumbnailCache.get(thumbId, timestamp);
467- if (bitmap == null) {
468- Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
469- long time = SystemClock.uptimeMillis();
470- bitmap = buildThumbnailForId(context, thumbnailCache, thumbId, origId, isVideo, DEFAULT_THUMBNAIL_WIDTH,
471- DEFAULT_THUMBNAIL_HEIGHT);
472- Log.i(TAG, "Built thumbnail and screennail for " + origId + " in " + (SystemClock.uptimeMillis() - time));
473- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
474- }
475- return bitmap;
476- }
477-
478- private static void buildThumbnails(final Context context) {
479- Log.i(TAG, "Preparing DiskCache for all thumbnails.");
470+ public static final ImageList getImageList(final Context context) {
471+ if (sList != null)
472+ return sList;
473+ ImageList list = new ImageList();
480474 final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI;
481475 final ContentResolver cr = context.getContentResolver();
482476 final Cursor cursorImages = cr.query(uriImages, THUMBNAIL_PROJECTION, null, null, null);
483- final DiskCache thumbnailCache = LocalDataSource.sThumbnailCache;
484-
485477 if (cursorImages != null && cursorImages.moveToFirst()) {
486478 final int size = cursorImages.getCount();
487479 final long[] ids = new long[size];
488480 final long[] thumbnailIds = new long[size];
489481 final long[] timestamp = new long[size];
482+ final int[] orientation = new int[size];
490483 int ctr = 0;
491484 do {
492485 if (Thread.interrupted()) {
493- return;
494- }
495- synchronized (thumbnailCache) {
496- ids[ctr] = cursorImages.getLong(THUMBNAIL_ID_INDEX);
497- timestamp[ctr] = cursorImages.getLong(THUMBNAIL_DATE_MODIFIED_INDEX);
498- thumbnailIds[ctr] = Utils.Crc64Long(cursorImages.getString(THUMBNAIL_DATA_INDEX));
499- ++ctr;
486+ break;
500487 }
488+ ids[ctr] = cursorImages.getLong(THUMBNAIL_ID_INDEX);
489+ timestamp[ctr] = cursorImages.getLong(THUMBNAIL_DATE_MODIFIED_INDEX);
490+ thumbnailIds[ctr] = Utils.Crc64Long(cursorImages.getString(THUMBNAIL_DATA_INDEX));
491+ orientation[ctr] = cursorImages.getInt(THUMBNAIL_ORIENTATION_INDEX);
492+ ++ctr;
501493 } while (cursorImages.moveToNext());
502494 cursorImages.close();
495+ list.ids = ids;
496+ list.thumbids = thumbnailIds;
497+ list.timestamp = timestamp;
498+ list.orientation = orientation;
499+ }
500+ if (sList == null) {
501+ sList = list;
502+ }
503+ return list;
504+ }
503505
504- for (int i = 0; i < size; ++i) {
505- if (Thread.interrupted()) {
506- return;
507- }
508- final long id = ids[i];
509- final long timeModifiedInSec = timestamp[i];
510- final long thumbnailId = thumbnailIds[i];
511- if (!thumbnailCache.isDataAvailable(thumbnailId, timeModifiedInSec * 1000)) {
512- buildThumbnailForId(context, thumbnailCache, thumbnailId, id, false, DEFAULT_THUMBNAIL_WIDTH,
513- DEFAULT_THUMBNAIL_HEIGHT);
514- }
506+ private static final byte[] queryThumbnail(final Context context, final long thumbId, final long origId, final boolean isVideo,
507+ final DiskCache thumbnailCache, final long timestamp) {
508+ if (!((Gallery) context).isPaused()) {
509+ final Thread thumbnailThread = THUMBNAIL_THREAD.getAndSet(null);
510+ if (thumbnailThread != null) {
511+ thumbnailThread.interrupt();
512+ }
513+ }
514+ byte[] bitmap = thumbnailCache.get(thumbId, timestamp);
515+ if (bitmap == null) {
516+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
517+ final long time = SystemClock.uptimeMillis();
518+ bitmap = buildThumbnailForId(context, thumbnailCache, thumbId, origId, isVideo, DEFAULT_THUMBNAIL_WIDTH,
519+ DEFAULT_THUMBNAIL_HEIGHT);
520+ Log.i(TAG, "Built thumbnail and screennail for " + origId + " in " + (SystemClock.uptimeMillis() - time));
521+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
522+ }
523+ return bitmap;
524+ }
525+
526+ private static final void buildThumbnails(final Context context) {
527+ Log.i(TAG, "Preparing DiskCache for all thumbnails.");
528+ ImageList list = getImageList(context);
529+ final int size = (list.ids == null) ? 0 : list.ids.length;
530+ final long[] ids = list.ids;
531+ final long[] timestamp = list.timestamp;
532+ final long[] thumbnailIds = list.thumbids;
533+ final DiskCache thumbnailCache = LocalDataSource.sThumbnailCache;
534+ for (int i = 0; i < size; ++i) {
535+ if (Thread.interrupted()) {
536+ return;
537+ }
538+ final long id = ids[i];
539+ final long timeModifiedInSec = timestamp[i];
540+ final long thumbnailId = thumbnailIds[i];
541+ if (!thumbnailCache.isDataAvailable(thumbnailId, timeModifiedInSec * 1000)) {
542+ buildThumbnailForId(context, thumbnailCache, thumbnailId, id, false, DEFAULT_THUMBNAIL_WIDTH,
543+ DEFAULT_THUMBNAIL_HEIGHT);
515544 }
516545 }
517546 Log.i(TAG, "DiskCache ready for all thumbnails.");
518547 }
519548
520- private static byte[] buildThumbnailForId(final Context context, final DiskCache thumbnailCache, final long thumbId,
549+ private static final byte[] buildThumbnailForId(final Context context, final DiskCache thumbnailCache, final long thumbId,
521550 final long origId, final boolean isVideo, final int thumbnailWidth, final int thumbnailHeight) {
522551 if (origId == Shared.INVALID) {
523552 return null;
@@ -537,7 +566,21 @@ public final class CacheService extends IntentService {
537566 return null;
538567 }
539568 } else {
540- // TODO: We need to generate video thumbnails if it is not eclair
569+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
570+ new Thread() {
571+ public void run() {
572+ try {
573+ Thread.sleep(5000);
574+ } catch (InterruptedException e) {
575+ ;
576+ }
577+ try {
578+ MediaStore.Video.Thumbnails.cancelThumbnailRequest(context.getContentResolver(), origId);
579+ } catch (Exception e) {
580+ ;
581+ }
582+ }
583+ }.start();
541584 bitmap = MediaStore.Video.Thumbnails.getThumbnail(context.getContentResolver(), origId,
542585 MediaStore.Video.Thumbnails.MICRO_KIND, null);
543586 }
@@ -552,7 +595,7 @@ public final class CacheService extends IntentService {
552595 }
553596 }
554597
555- public static byte[] writeBitmapToCache(final DiskCache thumbnailCache, final long thumbId, final long origId,
598+ public static final byte[] writeBitmapToCache(final DiskCache thumbnailCache, final long thumbId, final long origId,
556599 final Bitmap bitmap, final int thumbnailWidth, final int thumbnailHeight) {
557600 final int width = bitmap.getWidth();
558601 final int height = bitmap.getHeight();
@@ -651,23 +694,23 @@ public final class CacheService extends IntentService {
651694 if (intent.getBooleanExtra("checkthumbnails", false)) {
652695 startNewThumbnailThread(this);
653696 } else {
654- Thread existingThread = THUMBNAIL_THREAD.getAndSet(null);
697+ final Thread existingThread = THUMBNAIL_THREAD.getAndSet(null);
655698 if (existingThread != null) {
656699 existingThread.interrupt();
657700 }
658701 }
659702 }
660703
661- private static void putLocaleForAlbumCache(Locale locale) {
662- ByteArrayOutputStream bos = new ByteArrayOutputStream();
663- DataOutputStream dos = new DataOutputStream(bos);
704+ private static final void putLocaleForAlbumCache(final Locale locale) {
705+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
706+ final DataOutputStream dos = new DataOutputStream(bos);
664707 try {
665708 Utils.writeUTF(dos, locale.getCountry());
666709 Utils.writeUTF(dos, locale.getLanguage());
667710 Utils.writeUTF(dos, locale.getVariant());
668711 dos.flush();
669712 bos.flush();
670- byte[] data = bos.toByteArray();
713+ final byte[] data = bos.toByteArray();
671714 sAlbumCache.put(ALBUM_CACHE_LOCALE_INDEX, data);
672715 sAlbumCache.flush();
673716 dos.close();
@@ -679,8 +722,8 @@ public final class CacheService extends IntentService {
679722 }
680723 }
681724
682- private static Locale getLocaleForAlbumCache() {
683- byte[] data = sAlbumCache.get(ALBUM_CACHE_LOCALE_INDEX, 0);
725+ private static final Locale getLocaleForAlbumCache() {
726+ final byte[] data = sAlbumCache.get(ALBUM_CACHE_LOCALE_INDEX, 0);
684727 if (data != null && data.length > 0) {
685728 ByteArrayInputStream bis = new ByteArrayInputStream(data);
686729 DataInputStream dis = new DataInputStream(bis);
@@ -694,7 +737,7 @@ public final class CacheService extends IntentService {
694737 String variant = Utils.readUTF(dis);
695738 if (variant == null)
696739 variant = "";
697- Locale locale = new Locale(language, country, variant);
740+ final Locale locale = new Locale(language, country, variant);
698741 dis.close();
699742 bis.close();
700743 return locale;
@@ -707,9 +750,9 @@ public final class CacheService extends IntentService {
707750 return null;
708751 }
709752
710- private static void restartThread(final AtomicReference<Thread> threadRef, String name, final Runnable action) {
753+ private static final void restartThread(final AtomicReference<Thread> threadRef, final String name, final Runnable action) {
711754 // Create a new thread.
712- Thread newThread = new Thread() {
755+ final Thread newThread = new Thread() {
713756 public void run() {
714757 try {
715758 action.run();
@@ -722,13 +765,13 @@ public final class CacheService extends IntentService {
722765 newThread.start();
723766
724767 // Interrupt any existing thread.
725- Thread existingThread = threadRef.getAndSet(newThread);
768+ final Thread existingThread = threadRef.getAndSet(newThread);
726769 if (existingThread != null) {
727770 existingThread.interrupt();
728771 }
729772 }
730773
731- public static void startNewThumbnailThread(final Context context) {
774+ public static final void startNewThumbnailThread(final Context context) {
732775 restartThread(THUMBNAIL_THREAD, "ThumbnailRefresh", new Runnable() {
733776 public void run() {
734777 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -759,28 +802,28 @@ public final class CacheService extends IntentService {
759802 });
760803 }
761804
762- private static final byte[] concat(byte[] A, byte[] B) {
763- byte[] C = (byte[]) new byte[A.length + B.length];
805+ private static final byte[] concat(final byte[] A, final byte[] B) {
806+ final byte[] C = (byte[]) new byte[A.length + B.length];
764807 System.arraycopy(A, 0, C, 0, A.length);
765808 System.arraycopy(B, 0, C, A.length, B.length);
766809 return C;
767810 }
768811
769- private static final long[] toLongArray(byte[] data) {
770- ByteBuffer bBuffer = ByteBuffer.wrap(data);
771- LongBuffer lBuffer = bBuffer.asLongBuffer();
772- int numLongs = lBuffer.capacity();
773- long[] retVal = new long[numLongs];
812+ private static final long[] toLongArray(final byte[] data) {
813+ final ByteBuffer bBuffer = ByteBuffer.wrap(data);
814+ final LongBuffer lBuffer = bBuffer.asLongBuffer();
815+ final int numLongs = lBuffer.capacity();
816+ final long[] retVal = new long[numLongs];
774817 for (int i = 0; i < numLongs; ++i) {
775818 retVal[i] = lBuffer.get(i);
776819 }
777820 return retVal;
778821 }
779822
780- private static byte[] longToByteArray(long l) {
781- byte[] bArray = new byte[8];
782- ByteBuffer bBuffer = ByteBuffer.wrap(bArray);
783- LongBuffer lBuffer = bBuffer.asLongBuffer();
823+ private static final byte[] longToByteArray(final long l) {
824+ final byte[] bArray = new byte[8];
825+ final ByteBuffer bBuffer = ByteBuffer.wrap(bArray);
826+ final LongBuffer lBuffer = bBuffer.asLongBuffer();
784827 lBuffer.put(0, l);
785828 return bArray;
786829 }
@@ -789,10 +832,12 @@ public final class CacheService extends IntentService {
789832 // First we build the album cache.
790833 // This is the meta-data about the albums / buckets on the SD card.
791834 Log.i(TAG, "Refreshing cache.");
835+ int priority = Process.getThreadPriority(Process.myTid());
836+ Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
792837 sAlbumCache.deleteAll();
793838 putLocaleForAlbumCache(Locale.getDefault());
794-
795- ArrayList<MediaSet> sets = new ArrayList<MediaSet>();
839+
840+ final ArrayList<MediaSet> sets = new ArrayList<MediaSet>();
796841 LongSparseArray<MediaSet> acceleratedSets = new LongSparseArray<MediaSet>();
797842 Log.i(TAG, "Building albums.");
798843 final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI.buildUpon().appendQueryParameter("distinct", "true").build();
@@ -833,13 +878,13 @@ public final class CacheService extends IntentService {
833878 sortCursor.close();
834879 }
835880
836- byte[] data = new byte[] { 1 };
837- sAlbumCache.put(ALBUM_CACHE_INCOMPLETE_INDEX, data);
881+ sAlbumCache.put(ALBUM_CACHE_INCOMPLETE_INDEX, sDummyData);
838882 writeSetsToCache(sets);
839883 Log.i(TAG, "Done building albums.");
840884 // Now we must cache the items contained in every album / bucket.
841885 populateMediaItemsForSets(context, sets, acceleratedSets, false);
842886 sAlbumCache.delete(ALBUM_CACHE_INCOMPLETE_INDEX);
887+ Process.setThreadPriority(priority);
843888 if (QUEUE_DIRTY_ALL) {
844889 QUEUE_DIRTY_ALL = false;
845890 refresh(context);
@@ -847,15 +892,15 @@ public final class CacheService extends IntentService {
847892 }
848893
849894 private final static void refreshDirtySets(final Context context) {
850- byte[] existingData = sAlbumCache.get(ALBUM_CACHE_DIRTY_BUCKET_INDEX, 0);
895+ final byte[] existingData = sAlbumCache.get(ALBUM_CACHE_DIRTY_BUCKET_INDEX, 0);
851896 if (existingData != null && existingData.length > 0) {
852- long[] ids = toLongArray(existingData);
853- int numIds = ids.length;
897+ final long[] ids = toLongArray(existingData);
898+ final int numIds = ids.length;
854899 if (numIds > 0) {
855- ArrayList<MediaSet> sets = new ArrayList<MediaSet>(numIds);
856- LongSparseArray<MediaSet> acceleratedSets = new LongSparseArray<MediaSet>(numIds);
900+ final ArrayList<MediaSet> sets = new ArrayList<MediaSet>(numIds);
901+ final LongSparseArray<MediaSet> acceleratedSets = new LongSparseArray<MediaSet>(numIds);
857902 for (int i = 0; i < numIds; ++i) {
858- MediaSet set = new MediaSet();
903+ final MediaSet set = new MediaSet();
859904 set.mId = ids[i];
860905 sets.add(set);
861906 acceleratedSets.put(set.mId, set);
@@ -899,37 +944,38 @@ public final class CacheService extends IntentService {
899944
900945 final Cursor cursorImages = cr.query(uriImages, PROJECTION_IMAGES, whereClause, null, DEFAULT_IMAGE_SORT_ORDER);
901946 final Cursor cursorVideos = cr.query(uriVideos, PROJECTION_VIDEOS, whereClause, null, DEFAULT_VIDEO_SORT_ORDER);
902- Cursor[] cursors = new Cursor[2];
947+ final Cursor[] cursors = new Cursor[2];
903948 cursors[0] = cursorImages;
904949 cursors[1] = cursorVideos;
905950 final SortCursor sortCursor = new SortCursor(cursors, Images.ImageColumns.DATE_TAKEN, SortCursor.TYPE_NUMERIC, true);
906951 if (Thread.interrupted()) {
907952 return;
908953 }
909-
954+
910955 if (sortCursor != null && sortCursor.moveToFirst()) {
911- int count = sortCursor.getCount();
912- int numSets = sets.size();
913- int approximateCountPerSet = count / numSets;
956+ final int count = sortCursor.getCount();
957+ final int numSets = sets.size();
958+ final int approximateCountPerSet = count / numSets;
914959 for (int i = 0; i < numSets; ++i) {
915- MediaSet set = sets.get(i);
960+ final MediaSet set = sets.get(i);
961+ set.getItems().clear();
916962 set.setNumExpectedItems(approximateCountPerSet);
917963 }
918964 do {
919965 if (Thread.interrupted()) {
920966 return;
921967 }
922- MediaItem item = new MediaItem();
923- boolean isVideo = (sortCursor.getCurrentCursorIndex() == 1);
968+ final MediaItem item = new MediaItem();
969+ final boolean isVideo = (sortCursor.getCurrentCursorIndex() == 1);
924970 if (isVideo) {
925- populateVideoItemFromCursor(item, cr, sortCursor, BASE_CONTENT_STRING_VIDEOS);
971+ populateVideoItemFromCursor(item, cr, sortCursor, CacheService.BASE_CONTENT_STRING_VIDEOS);
926972 } else {
927- populateMediaItemFromCursor(item, cr, sortCursor, BASE_CONTENT_STRING_IMAGES);
973+ populateMediaItemFromCursor(item, cr, sortCursor, CacheService.BASE_CONTENT_STRING_IMAGES);
928974 }
929- long setId = sortCursor.getLong(MEDIA_BUCKET_ID_INDEX);
930- MediaSet set = findSet(setId, acceleratedSets);
975+ final long setId = sortCursor.getLong(MEDIA_BUCKET_ID_INDEX);
976+ final MediaSet set = findSet(setId, acceleratedSets);
931977 if (set != null) {
932- set.addItem(item);
978+ set.getItems().add(item);
933979 }
934980 } while (sortCursor.moveToNext());
935981 }
@@ -985,8 +1031,8 @@ public final class CacheService extends IntentService {
9851031 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
9861032 final DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(bos, 256));
9871033 try {
988- ArrayList<MediaItem> items = set.getItems();
989- int numItems = set.getNumItems();
1034+ final ArrayList<MediaItem> items = set.getItems();
1035+ final int numItems = items.size();
9901036 dos.writeInt(numItems);
9911037 dos.writeLong(set.mMinTimestamp);
9921038 dos.writeLong(set.mMaxTimestamp);
@@ -1020,7 +1066,7 @@ public final class CacheService extends IntentService {
10201066 }
10211067 }
10221068
1023- private static final MediaSet findSet(long id, LongSparseArray<MediaSet> acceleratedTable) {
1069+ private static final MediaSet findSet(final long id, final LongSparseArray<MediaSet> acceleratedTable) {
10241070 // This is the accelerated lookup table for the MediaSet based on set id.
10251071 return acceleratedTable.get(id);
10261072 }
--- /dev/null
+++ b/src/com/cooliris/cache/ImageList.java
@@ -0,0 +1,8 @@
1+package com.cooliris.cache;
2+
3+public class ImageList {
4+ public long ids[];
5+ public long thumbids[];
6+ public long timestamp[];
7+ public int orientation[];
8+}
--- /dev/null
+++ b/src/com/cooliris/media/ActiveWallpaper.java
@@ -0,0 +1,17 @@
1+package com.cooliris.media;
2+
3+import com.cooliris.wallpaper.RandomDataSource;
4+import com.cooliris.wallpaper.Slideshow;
5+
6+import android.app.*;
7+import android.os.Bundle;
8+
9+public class ActiveWallpaper extends Activity {
10+ @Override
11+ public void onCreate(Bundle savedInstanceState) {
12+ super.onCreate(savedInstanceState);
13+ Slideshow slideshow = new Slideshow(this);
14+ slideshow.setDataSource(new RandomDataSource());
15+ setContentView(slideshow);
16+ }
17+}
--- a/src/com/cooliris/media/AdaptiveBackgroundTexture.java
+++ b/src/com/cooliris/media/AdaptiveBackgroundTexture.java
@@ -76,8 +76,8 @@ public final class AdaptiveBackgroundTexture extends Texture {
7676 int sourceHeight = source.getHeight();
7777 int destWidth = mWidth;
7878 int destHeight = mHeight;
79- float fitX = (float) sourceWidth / destWidth; // CR: cast destWidth to float as well to be clean; no space after the cast.
80- float fitY = (float) sourceHeight / destHeight;
79+ float fitX = (float)sourceWidth / (float)destWidth;
80+ float fitY = (float)sourceHeight / (float)destHeight;
8181 float scale;
8282 int cropX;
8383 int cropY;
@@ -86,10 +86,10 @@ public final class AdaptiveBackgroundTexture extends Texture {
8686 if (fitX < fitY) {
8787 // Full width, partial height.
8888 cropWidth = sourceWidth;
89- cropHeight = (int) (destHeight * fitX); // CR: no space after the cast.
89+ cropHeight = (int)(destHeight * fitX);
9090 cropX = 0;
9191 cropY = (sourceHeight - cropHeight) / 2;
92- scale = 1f / fitX; // CR: i think 1.0f is clearer than 1f.
92+ scale = 1.0f / fitX;
9393 } else {
9494 // Full height, partial or full width.
9595 cropWidth = (int) (destHeight * fitY);
--- a/src/com/cooliris/media/ArrayUtils.java
+++ b/src/com/cooliris/media/ArrayUtils.java
@@ -21,7 +21,7 @@ public final class ArrayUtils {
2121 int firstListSize = firstList.size();
2222 for (int i = 0; i < firstListSize; ++i) {
2323 MediaItem firstListItem = firstList.get(i);
24- MediaItem hashItem = hash[firstListItem.hashCode() & mask];
24+ MediaItem hashItem = (hash != null) ? hash[firstListItem.hashCode() & mask] : null;
2525 if (hashItem != null && ((hashItem.mId != Shared.INVALID && hashItem.mId == firstListItem.mId) || contains(secondList, firstListItem))) {
2626 intersectionList.add(firstListItem);
2727 if (--maxSize == 0) {
--- a/src/com/cooliris/media/BackgroundLayer.java
+++ b/src/com/cooliris/media/BackgroundLayer.java
@@ -9,7 +9,7 @@ public class BackgroundLayer extends Layer {
99 private static final String TAG = "AdaptiveBackground";
1010 private final GridLayer mGridLayer;
1111 private CrossFadingTexture mBackground;
12- private static final int MAX_ADAPTIVES_TO_KEEP_IN_MEMORY = 8;
12+ private static final int MAX_ADAPTIVES_TO_KEEP_IN_MEMORY = 16;
1313
1414 private final HashMap<Texture, AdaptiveBackgroundTexture> mCacheAdaptiveTexture = new HashMap<Texture, AdaptiveBackgroundTexture>();
1515 private int mCount;
@@ -43,7 +43,7 @@ public class BackgroundLayer extends Layer {
4343 int cameraPosition = (int) mGridLayer.getScrollPosition();
4444 final int backgroundSpacing = mBackgroundBlitWidth - mBackgroundOverlap;
4545 cameraPosition = (int) ((cameraPosition / backgroundSpacing) * backgroundSpacing);
46- final DisplayItem displayItem = mGridLayer.getDisplayItemForScrollPosition(cameraPosition);
46+ final DisplayItem displayItem = mGridLayer.getRepresentativeDisplayItem();
4747 if (displayItem != null) {
4848 mBackground.setTexture(getAdaptive(view, displayItem));
4949 }
@@ -76,13 +76,15 @@ public class BackgroundLayer extends Layer {
7676 @Override
7777 public void renderOpaque(RenderView view, GL11 gl) {
7878 gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
79- mFallbackBackground = view.getResource(R.drawable.default_background, false);
80- view.loadTexture(mFallbackBackground);
79+ if (mFallbackBackground == null) {
80+ mFallbackBackground = view.getResource(R.drawable.default_background, false);
81+ view.loadTexture(mFallbackBackground);
82+ }
8183 }
8284
8385 @Override
8486 public void renderBlended(RenderView view, GL11 gl) {
85- if (mBackground == null)
87+ if (mBackground == null || mFallbackBackground == null)
8688 return;
8789 gl.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
8890 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
@@ -90,6 +92,11 @@ public class BackgroundLayer extends Layer {
9092 boolean bind = anchorTexture.bind(view, gl);
9193 if (!bind) {
9294 view.bind(mFallbackBackground);
95+ } else {
96+ Texture texture = anchorTexture.getTexture();
97+ if (texture.isLoaded()) {
98+ mFallbackBackground = texture;
99+ }
93100 }
94101
95102 // We stitch this crossfading texture, and to cover all cases of overlap we need to perform 3 draws.
@@ -122,6 +129,7 @@ public class BackgroundLayer extends Layer {
122129 public void clear() {
123130 clearCache();
124131 mBackground = null;
132+ mFallbackBackground = null;
125133 }
126134
127135 public void clearCache() {
--- a/src/com/cooliris/media/DiskCache.java
+++ b/src/com/cooliris/media/DiskCache.java
@@ -45,10 +45,6 @@ public final class DiskCache {
4545 shutdown();
4646 }
4747
48- public void load() {
49- loadIndex();
50- }
51-
5248 public byte[] get(long key, long timestamp) {
5349 // Look up the record for the given key.
5450 Record record = null;
@@ -58,7 +54,6 @@ public final class DiskCache {
5854 if (record != null) {
5955 // Read the chunk from the file.
6056 if (record.timestamp < timestamp && timestamp < System.currentTimeMillis()) {
61- Log.i(TAG, "Old copy: CacheTimestamp " + record.timestamp + " new timestamp " + timestamp);
6257 return null;
6358 }
6459 try {
@@ -85,9 +80,10 @@ public final class DiskCache {
8580 return false;
8681 }
8782 if (record.timestamp < timestamp && timestamp < System.currentTimeMillis()) {
88- Log.i(TAG, "Old copy: CacheTimestamp " + record.timestamp + " new timestamp " + timestamp);
8983 return false;
9084 }
85+ if (record.size == 0)
86+ return false;
9187 return true;
9288 }
9389
--- a/src/com/cooliris/media/FloatUtils.java
+++ b/src/com/cooliris/media/FloatUtils.java
@@ -127,4 +127,11 @@ public class FloatUtils {
127127 }
128128 }
129129 }
130+
131+ public static final float max(float scaleX, float scaleY) {
132+ if (scaleX > scaleY)
133+ return scaleX;
134+ else
135+ return scaleY;
136+ }
130137 }
--- a/src/com/cooliris/media/Gallery.java
+++ b/src/com/cooliris/media/Gallery.java
@@ -12,6 +12,7 @@ import android.graphics.Bitmap;
1212 import android.net.Uri;
1313 import android.os.Bundle;
1414 import android.os.Handler;
15+import android.provider.MediaStore.Images;
1516 import android.util.DisplayMetrics;
1617 import android.util.Log;
1718 import android.view.KeyEvent;
@@ -19,14 +20,16 @@ import android.widget.Toast;
1920 import android.media.MediaScannerConnection;
2021
2122 import com.cooliris.cache.CacheService;
23+import com.cooliris.wallpaper.RandomDataSource;
24+import com.cooliris.wallpaper.Slideshow;
2225
2326 public final class Gallery extends Activity {
2427 public static final TimeZone CURRENT_TIME_ZONE = TimeZone.getDefault();
25- public static float PIXEL_DENSITY = 1.0f;
26- public static int DENSITY_DPI = DisplayMetrics.DENSITY_DEFAULT;
28+ public static float PIXEL_DENSITY = 0.0f;
2729 public static boolean NEEDS_REFRESH = true;
28- public static final int CROP_MSG_INTERNAL = 100;
30+
2931 private static final String TAG = "Gallery";
32+ public static final int CROP_MSG_INTERNAL = 100;
3033 private static final int CROP_MSG = 10;
3134 private RenderView mRenderView = null;
3235 private GridLayer mGridLayer;
@@ -34,16 +37,25 @@ public final class Gallery extends Activity {
3437 private ReverseGeocoder mReverseGeocoder;
3538 private boolean mPause;
3639 private MediaScannerConnection mConnection;
40+ private static final boolean TEST_WALLPAPER = false;
3741
3842 @Override
3943 public void onCreate(Bundle savedInstanceState) {
4044 super.onCreate(savedInstanceState);
45+ if (TEST_WALLPAPER || (isViewIntent() && getIntent().getData().equals(Images.Media.EXTERNAL_CONTENT_URI))) {
46+ Slideshow slideshow = new Slideshow(this);
47+ slideshow.setDataSource(new RandomDataSource());
48+ setContentView(slideshow);
49+ return;
50+ }
4151 boolean isCacheReady = CacheService.isCacheReady(false);
4252 CacheService.startCache(this, false);
53+ if (PIXEL_DENSITY == 0.0f) {
54+ DisplayMetrics metrics = new DisplayMetrics();
55+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
56+ PIXEL_DENSITY = metrics.density;
57+ }
4358 mReverseGeocoder = new ReverseGeocoder(this);
44- DisplayMetrics metrics = new DisplayMetrics();
45- getWindowManager().getDefaultDisplay().getMetrics(metrics);
46- PIXEL_DENSITY = metrics.density;
4759 mRenderView = new RenderView(this);
4860 mGridLayer = new GridLayer(this, (int) (96.0f * PIXEL_DENSITY), (int) (72.0f * PIXEL_DENSITY), new GridLayoutInterface(4),
4961 mRenderView);
@@ -99,7 +111,9 @@ public final class Gallery extends Activity {
99111 Uri uri = getIntent().getData();
100112 boolean slideshow = getIntent().getBooleanExtra("slideshow", false);
101113 SingleDataSource localDataSource = new SingleDataSource(this, uri.toString(), slideshow);
102- mGridLayer.setDataSource(localDataSource);
114+ PicasaDataSource picasaDataSource = new PicasaDataSource(this);
115+ ConcatenatedDataSource combinedDataSource = new ConcatenatedDataSource(localDataSource, picasaDataSource);
116+ mGridLayer.setDataSource(combinedDataSource);
103117 mGridLayer.setViewIntent(true, Utils.getBucketNameFromUri(uri));
104118 if (SingleDataSource.isSingleImageMode(uri.toString())) {
105119 mGridLayer.setSingleImage(false);
@@ -133,7 +147,8 @@ public final class Gallery extends Activity {
133147 @Override
134148 public void onResume() {
135149 super.onResume();
136- mRenderView.onResume();
150+ if (mRenderView != null)
151+ mRenderView.onResume();
137152 if (NEEDS_REFRESH) {
138153 NEEDS_REFRESH = false;
139154 CacheService.markDirtyImmediate(LocalDataSource.CAMERA_BUCKET_ID);
@@ -146,7 +161,8 @@ public final class Gallery extends Activity {
146161 @Override
147162 public void onPause() {
148163 super.onPause();
149- mRenderView.onPause();
164+ if (mRenderView != null)
165+ mRenderView.onPause();
150166 mPause = true;
151167 }
152168
@@ -172,8 +188,8 @@ public final class Gallery extends Activity {
172188 public void onDestroy() {
173189 // Force GLThread to exit.
174190 setContentView(R.layout.main);
175- DataSource dataSource = mGridLayer.getDataSource();
176191 if (mGridLayer != null) {
192+ DataSource dataSource = mGridLayer.getDataSource();
177193 if (dataSource != null) {
178194 dataSource.shutdown();
179195 }
@@ -181,8 +197,10 @@ public final class Gallery extends Activity {
181197 }
182198 if (mReverseGeocoder != null)
183199 mReverseGeocoder.shutdown();
184- mRenderView.shutdown();
185- mRenderView = null;
200+ if (mRenderView != null) {
201+ mRenderView.shutdown();
202+ mRenderView = null;
203+ }
186204 mGridLayer = null;
187205 super.onDestroy();
188206 Log.i(TAG, "onDestroy");
@@ -194,13 +212,18 @@ public final class Gallery extends Activity {
194212 if (mGridLayer != null) {
195213 mGridLayer.markDirty(30);
196214 }
197- mRenderView.requestRender();
215+ if (mRenderView != null)
216+ mRenderView.requestRender();
198217 Log.i(TAG, "onConfigurationChanged");
199218 }
200219
201220 @Override
202221 public boolean onKeyDown(int keyCode, KeyEvent event) {
203- return mRenderView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
222+ if (mRenderView != null) {
223+ return mRenderView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
224+ } else {
225+ return super.onKeyDown(keyCode, event);
226+ }
204227 }
205228
206229 private boolean isPickIntent() {
@@ -293,7 +316,7 @@ public final class Gallery extends Activity {
293316 public void onScanCompleted(String path, Uri uri) {
294317 shutdown(uri.toString());
295318 }
296-
319+
297320 public void shutdown(String uri) {
298321 dialog.dismiss();
299322 performReturn(myExtras, uri.toString());
@@ -319,7 +342,7 @@ public final class Gallery extends Activity {
319342 Bitmap bitmap = null;
320343 try {
321344 bitmap = UriTexture.createFromUri(this, contentUri, 1024, 1024, 0, null);
322- } catch (IOException e) {
345+ } catch (IOException e) {
323346 ;
324347 } catch (URISyntaxException e) {
325348 ;
--- a/src/com/cooliris/media/GridCameraManager.java
+++ b/src/com/cooliris/media/GridCameraManager.java
@@ -1,23 +1,25 @@
11 package com.cooliris.media;
22
33 public final class GridCameraManager {
4- private GridCamera mCamera;
5- private ConcurrentPool<Vector3f> mPool;
6-
7- public GridCameraManager(GridCamera camera) {
8- mCamera = camera;
9- Vector3f[] vectorPool = new Vector3f[128];
4+ private final GridCamera mCamera;
5+ private static final Pool<Vector3f> sPool;
6+ static {
7+ final Vector3f[] vectorPool = new Vector3f[128];
108 int length = vectorPool.length;
119 for (int i = 0; i < length; ++i) {
1210 vectorPool[i] = new Vector3f();
1311 }
14- mPool = new ConcurrentPool<Vector3f>(vectorPool);
12+ sPool = new Pool<Vector3f>(vectorPool);
13+ }
14+
15+ public GridCameraManager(final GridCamera camera) {
16+ mCamera = camera;
1517 }
1618
1719 public void centerCameraForSlot(LayoutInterface layout, int slotIndex, float baseConvergence, Vector3f deltaAnchorPositionIn,
1820 int selectedSlotIndex, float zoomValue, float imageTheta, int state) {
1921 final GridCamera camera = mCamera;
20- final ConcurrentPool<Vector3f> pool = mPool;
22+ final Pool<Vector3f> pool = sPool;
2123 synchronized (camera) {
2224 final boolean zoomin = (selectedSlotIndex != Shared.INVALID);
2325 final int theta = (int) imageTheta;
@@ -61,16 +63,16 @@ public final class GridCameraManager {
6163 */
6264 public boolean constrainCameraForSlot(LayoutInterface layout, int slotIndex, Vector3f deltaAnchorPositionIn,
6365 float currentFocusItemWidth, float currentFocusItemHeight) {
64- GridCamera camera = mCamera;
65- ConcurrentPool<Vector3f> pool = mPool;
66+ final GridCamera camera = mCamera;
67+ final Pool<Vector3f> pool = sPool;
6668 boolean retVal = false;
6769 synchronized (camera) {
68- Vector3f position = pool.create();
69- Vector3f deltaAnchorPosition = pool.create();
70- Vector3f topLeft = pool.create();
71- Vector3f bottomRight = pool.create();
72- Vector3f imgTopLeft = pool.create();
73- Vector3f imgBottomRight = pool.create();
70+ final Vector3f position = pool.create();
71+ final Vector3f deltaAnchorPosition = pool.create();
72+ final Vector3f topLeft = pool.create();
73+ final Vector3f bottomRight = pool.create();
74+ final Vector3f imgTopLeft = pool.create();
75+ final Vector3f imgBottomRight = pool.create();
7476
7577 try {
7678 if (slotIndex >= 0) {
@@ -120,7 +122,7 @@ public final class GridCameraManager {
120122 public void computeVisibleRange(MediaFeed feed, LayoutInterface layout, Vector3f deltaAnchorPositionIn,
121123 IndexRange outVisibleRange, IndexRange outBufferedVisibleRange, IndexRange outCompleteRange, int state) {
122124 GridCamera camera = mCamera;
123- ConcurrentPool<Vector3f> pool = mPool;
125+ Pool<Vector3f> pool = sPool;
124126 float offset = (camera.mLookAtX * camera.mScale);
125127 int itemWidth = camera.mItemWidth;
126128 float maxIncrement = camera.mWidth * 0.5f + itemWidth;
@@ -223,8 +225,8 @@ public final class GridCameraManager {
223225
224226 public static final float getFillScreenZoomValue(GridCamera camera, Pool<Vector3f> pool, float currentFocusItemWidth,
225227 float currentFocusItemHeight) {
226- Vector3f topLeft = pool.create();
227- Vector3f bottomRight = pool.create();
228+ final Vector3f topLeft = pool.create();
229+ final Vector3f bottomRight = pool.create();
228230 float potentialZoomValue = 1.0f;
229231 try {
230232 camera.convertToCameraSpace(0, 0, 0, topLeft);
--- a/src/com/cooliris/media/GridDrawManager.java
+++ b/src/com/cooliris/media/GridDrawManager.java
@@ -21,16 +21,16 @@ public final class GridDrawManager {
2121 public static final int PASS_LOCATION_LABEL = 8;
2222 public static final int PASS_MEDIASET_SOURCE_LABEL = 9;
2323
24- private MediaItemTexture.Config mThumbnailConfig = new MediaItemTexture.Config();
25- private DisplayItem[] mDisplayItems;
26- private DisplaySlot[] mDisplaySlots;
27- private DisplayList mDisplayList;
28- private GridCamera mCamera;
24+ private static final MediaItemTexture.Config sThumbnailConfig = new MediaItemTexture.Config();
25+ private final DisplayItem[] mDisplayItems;
26+ private final DisplaySlot[] mDisplaySlots;
27+ private final DisplayList mDisplayList;
28+ private final GridCamera mCamera;
29+ private final GridDrawables mDrawables;
2930 private IndexRange mBufferedVisibleRange;
3031 private IndexRange mVisibleRange;
3132 private int mSelectedSlot;
3233 private int mCurrentFocusSlot;
33- private GridDrawables mDrawables;
3434 private DisplayItem[] mItemsDrawn;
3535 private int mDrawnCounter;
3636 private float mTargetFocusMixRatio = 0.0f;
@@ -41,7 +41,7 @@ public final class GridDrawManager {
4141 private boolean mCurrentFocusIsPressed;
4242 private final Texture mNoItemsTexture;
4343
44- private Comparator<DisplayItem> mDisplayItemComparator = new Comparator<DisplayItem>() {
44+ private static final Comparator<DisplayItem> sDisplayItemComparator = new Comparator<DisplayItem>() {
4545 public int compare(DisplayItem a, DisplayItem b) {
4646 if (a == null || b == null) {
4747 return 0;
@@ -59,8 +59,8 @@ public final class GridDrawManager {
5959
6060 public GridDrawManager(Context context, GridCamera camera, GridDrawables drawables, DisplayList displayList,
6161 DisplayItem[] displayItems, DisplaySlot[] displaySlots) {
62- mThumbnailConfig.thumbnailWidth = 128;
63- mThumbnailConfig.thumbnailHeight = 96;
62+ sThumbnailConfig.thumbnailWidth = 128;
63+ sThumbnailConfig.thumbnailHeight = 96;
6464 mDisplayItems = displayItems;
6565 mDisplaySlots = displaySlots;
6666 mDisplayList = displayList;
@@ -96,21 +96,22 @@ public final class GridDrawManager {
9696 }
9797
9898 public void drawThumbnails(RenderView view, GL11 gl, int state) {
99- GridDrawables drawables = mDrawables;
100- DisplayList displayList = mDisplayList;
101- DisplayItem[] displayItems = mDisplayItems;
102- int firstBufferedVisibleSlot = mBufferedVisibleRange.begin;
103- int lastBufferedVisibleSlot = mBufferedVisibleRange.end;
104- int firstVisibleSlot = mVisibleRange.begin;
105- int lastVisibleSlot = mVisibleRange.end;
106- int selectedSlotIndex = mSelectedSlot;
107- int currentFocusSlot = mCurrentFocusSlot;
108- DisplayItem[] itemsDrawn = mItemsDrawn;
99+ final GridDrawables drawables = mDrawables;
100+ final DisplayList displayList = mDisplayList;
101+ final DisplayItem[] displayItems = mDisplayItems;
102+ final int firstBufferedVisibleSlot = mBufferedVisibleRange.begin;
103+ final int lastBufferedVisibleSlot = mBufferedVisibleRange.end;
104+ final int firstVisibleSlot = mVisibleRange.begin;
105+ final int lastVisibleSlot = mVisibleRange.end;
106+ final int selectedSlotIndex = mSelectedSlot;
107+ final int currentFocusSlot = mCurrentFocusSlot;
108+ final DisplayItem[] itemsDrawn = mItemsDrawn;
109109 itemsDrawn[0] = null; // No items drawn yet.
110110 int drawnCounter = 0;
111- GridQuad grid = drawables.mGrid;
111+ final GridQuad grid = GridDrawables.sGrid;
112112 grid.bindArrays(gl);
113113 int numTexturesQueued = 0;
114+ Context context = view.getContext();
114115 for (int itrSlotIndex = firstBufferedVisibleSlot; itrSlotIndex <= lastBufferedVisibleSlot; ++itrSlotIndex) {
115116 int index = itrSlotIndex;
116117 boolean priority = !(index < firstVisibleSlot || index > lastVisibleSlot);
@@ -120,7 +121,7 @@ public final class GridDrawManager {
120121 if (displayItem == null) {
121122 continue;
122123 } else {
123- Texture texture = displayItem.getThumbnailImage(view.getContext(), mThumbnailConfig);
124+ Texture texture = displayItem.getThumbnailImage(context, sThumbnailConfig);
124125 if (texture != null && texture.isLoaded() == false) {
125126 startSlotIndex = j;
126127 break;
@@ -136,13 +137,13 @@ public final class GridDrawManager {
136137 if (selectedSlotIndex != Shared.INVALID && (index <= selectedSlotIndex - 2 || index >= selectedSlotIndex + 2)) {
137138 displayItem.clearScreennailImage();
138139 }
139- Texture texture = displayItem.getThumbnailImage(view.getContext(), mThumbnailConfig);
140+ Texture texture = displayItem.getThumbnailImage(context, sThumbnailConfig);
140141 if (texture != null && !texture.isLoaded() && numTexturesQueued <= 6) {
141- boolean isUncachedVideo = texture.isUncachedVideo();
142- if (!isUncachedVideo)
142+ boolean isCached = texture.isCached();
143+ if (isCached)
143144 view.prime(texture, priority);
144145 view.bind(texture);
145- if (priority && !isUncachedVideo)
146+ if (priority && isCached && texture.mState != Texture.STATE_ERROR)
146147 ++numTexturesQueued;
147148 }
148149 }
@@ -164,7 +165,7 @@ public final class GridDrawManager {
164165 } else {
165166 displayList.setHasFocus(displayItem, false, pushDown);
166167 }
167- Texture texture = displayItem.getThumbnailImage(view.getContext(), mThumbnailConfig);
168+ Texture texture = displayItem.getThumbnailImage(view.getContext(), sThumbnailConfig);
168169 if (texture != null) {
169170 if ((!displayItem.isAnimating() || !texture.isLoaded())
170171 && displayItem.getStackIndex() > GridLayer.MAX_ITEMS_PER_SLOT) {
@@ -194,7 +195,6 @@ public final class GridDrawManager {
194195 }
195196 mDrawnCounter = drawnCounter;
196197 grid.unbindArrays(gl);
197- drawables.swapGridQuads(gl);
198198 }
199199
200200 public float getFocusQuadWidth() {
@@ -255,7 +255,7 @@ public final class GridDrawManager {
255255 }
256256 DisplayItem displayItem = displayItems[indexInDrawnArray];
257257 MediaItem item = displayItem.mItemRef;
258- final Texture thumbnailTexture = displayItem.getThumbnailImage(view.getContext(), mThumbnailConfig);
258+ final Texture thumbnailTexture = displayItem.getThumbnailImage(view.getContext(), sThumbnailConfig);
259259 Texture texture = displayItem.getScreennailImage(view.getContext());
260260 if (isCameraZAnimating && (texture == null || !texture.isLoaded())) {
261261 texture = thumbnailTexture;
@@ -296,11 +296,13 @@ public final class GridDrawManager {
296296 mSelectedMixRatio.animateValue(1f, 0.75f, view.getFrameTime());
297297 }
298298 }
299- if (!slideshowMode && skipPrevious && i == -1) {
300- continue;
301- }
302- if (!skipPrevious && i == 1) {
303- continue;
299+ if (mCamera.isAnimating()) {
300+ if (!slideshowMode && skipPrevious && i == -1) {
301+ continue;
302+ }
303+ if (!skipPrevious && i == 1) {
304+ continue;
305+ }
304306 }
305307 int theta = (int) displayItem.getImageTheta();
306308 // If it is in slideshow mode, we draw the previous item in
@@ -332,7 +334,7 @@ public final class GridDrawManager {
332334 texture = thumbnailTexture;
333335 view.setAlpha(alpha * (1.0f - selectedMixRatio));
334336 }
335- GridQuad quad = drawables.mFullscreenGrid[vboIndex];
337+ GridQuad quad = GridDrawables.sFullscreenGrid[vboIndex];
336338 float u = texture.getNormalizedWidth();
337339 float v = texture.getNormalizedHeight();
338340 float imageWidth = texture.getWidth();
@@ -373,9 +375,9 @@ public final class GridDrawManager {
373375 view.setAlpha(alpha);
374376 if (item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO) {
375377 // The play graphic overlay.
376- drawables.mVideoGrid.bindArrays(gl);
378+ GridDrawables.sVideoGrid.bindArrays(gl);
377379 drawDisplayItem(view, gl, displayItem, drawables.mTextureVideo, PASS_VIDEO_LABEL, null, 0);
378- drawables.mVideoGrid.unbindArrays(gl);
380+ GridDrawables.sVideoGrid.unbindArrays(gl);
379381 }
380382 }
381383 }
@@ -395,7 +397,7 @@ public final class GridDrawManager {
395397 // We draw the frames around the drawn items.
396398 boolean currentFocusIsPressed = mCurrentFocusIsPressed;
397399 if (state != GridLayer.STATE_FULL_SCREEN) {
398- drawables.mFrame.bindArrays(gl);
400+ GridDrawables.sFrame.bindArrays(gl);
399401 Texture texturePlaceHolder = (state == GridLayer.STATE_GRID_VIEW) ? drawables.mTextureGridFrame
400402 : drawables.mTextureFrame;
401403 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
@@ -427,7 +429,7 @@ public final class GridDrawManager {
427429 DisplayItem[] itemsDrawn = mItemsDrawn;
428430 if (texture != null) {
429431 if (drawnCounter > 0) {
430- Arrays.sort(itemsDrawn, 0, drawnCounter, mDisplayItemComparator);
432+ Arrays.sort(itemsDrawn, 0, drawnCounter, sDisplayItemComparator);
431433 float timeElapsedSinceGridView = gridMixRatio;
432434 float timeElapsedSinceStackView = stackMixRatio;
433435 for (int i = drawnCounter - 1; i >= 0; --i) {
@@ -457,14 +459,14 @@ public final class GridDrawManager {
457459 }
458460 }
459461 }
460- drawables.mFrame.unbindArrays(gl);
462+ GridDrawables.sFrame.unbindArrays(gl);
461463 gl.glDepthFunc(GL10.GL_ALWAYS);
462464 if (state == GridLayer.STATE_MEDIA_SETS || state == GridLayer.STATE_TIMELINE) {
463465 DisplaySlot[] displaySlots = mDisplaySlots;
464- drawables.mTextGrid.bindArrays(gl);
466+ GridDrawables.sTextGrid.bindArrays(gl);
465467 final float textOffsetY = 0.82f;
466468 gl.glTranslatef(0.0f, -textOffsetY, 0.0f);
467- HashMap<String, StringTexture> stringTextureTable = drawables.mStringTextureTable;
469+ HashMap<String, StringTexture> stringTextureTable = GridDrawables.sStringTextureTable;
468470 ReverseGeocoder reverseGeocoder = ((Gallery) view.getContext()).getReverseGeocoder();
469471
470472 boolean itemsPresent = false;
@@ -512,7 +514,7 @@ public final class GridDrawManager {
512514 }
513515 }
514516 if (state == GridLayer.STATE_TIMELINE) {
515- drawables.mLocationGrid.bindArrays(gl);
517+ GridDrawables.sLocationGrid.bindArrays(gl);
516518 Texture locationTexture = drawables.mTextureLocation;
517519 final float yLocationLabelOffset = 0.19f;
518520 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
@@ -536,9 +538,9 @@ public final class GridDrawManager {
536538 }
537539 }
538540
539- drawables.mLocationGrid.unbindArrays(gl);
541+ GridDrawables.sLocationGrid.unbindArrays(gl);
540542 } else if (state == GridLayer.STATE_MEDIA_SETS && stackMixRatio > 0.0f) {
541- drawables.mSourceIconGrid.bindArrays(gl);
543+ GridDrawables.sSourceIconGrid.bindArrays(gl);
542544 Texture transparentTexture = drawables.mTextureTransparent;
543545 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
544546 DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
@@ -556,18 +558,18 @@ public final class GridDrawManager {
556558 }
557559 }
558560 }
559- drawables.mSourceIconGrid.unbindArrays(gl);
561+ GridDrawables.sSourceIconGrid.unbindArrays(gl);
560562 }
561563 gl.glTranslatef(0.0f, yLocOffset, 0.0f);
562564 gl.glTranslatef(0.0f, textOffsetY, 0.0f);
563- drawables.mTextGrid.unbindArrays(gl);
565+ GridDrawables.sTextGrid.unbindArrays(gl);
564566 }
565567 if (hudMode == HudLayer.MODE_SELECT && state != GridLayer.STATE_FULL_SCREEN) {
566568 Texture textureSelectedOn = drawables.mTextureCheckmarkOn;
567569 Texture textureSelectedOff = drawables.mTextureCheckmarkOff;
568570 view.prime(textureSelectedOn, true);
569571 view.prime(textureSelectedOff, true);
570- drawables.mSelectedGrid.bindArrays(gl);
572+ GridDrawables.sSelectedGrid.bindArrays(gl);
571573 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
572574 DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
573575 if (displayItem != null) {
@@ -575,9 +577,9 @@ public final class GridDrawManager {
575577 drawDisplayItem(view, gl, displayItem, textureToUse, PASS_SELECTION_LABEL, null, 0);
576578 }
577579 }
578- drawables.mSelectedGrid.unbindArrays(gl);
580+ GridDrawables.sSelectedGrid.unbindArrays(gl);
579581 }
580- drawables.mVideoGrid.bindArrays(gl);
582+ GridDrawables.sVideoGrid.bindArrays(gl);
581583 Texture videoTexture = drawables.mTextureVideo;
582584 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
583585 DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
@@ -587,7 +589,7 @@ public final class GridDrawManager {
587589 }
588590 }
589591 }
590- drawables.mVideoGrid.unbindArrays(gl);
592+ GridDrawables.sVideoGrid.unbindArrays(gl);
591593 gl.glDepthFunc(GL10.GL_LEQUAL);
592594 }
593595 }
@@ -656,7 +658,9 @@ public final class GridDrawManager {
656658 gl.glTranslatef(-translateXf, -translateYf, -translateZf);
657659 float theta = (pass == PASS_FOCUS_CONTENT) ? displayItem.mAnimatedImageTheta + displayItem.mAnimatedTheta
658660 : displayItem.mAnimatedTheta;
659- gl.glRotatef(theta, 0.0f, 0.0f, 1.0f);
661+ if (theta != 0.0f) {
662+ gl.glRotatef(theta, 0.0f, 0.0f, 1.0f);
663+ }
660664 float orientation = 0.0f;
661665 if (pass == PASS_THUMBNAIL_CONTENT && displayItem.mAnimatedImageTheta != 0.0f) {
662666 orientation = displayItem.mAnimatedImageTheta;
@@ -666,7 +670,9 @@ public final class GridDrawManager {
666670 } else {
667671 GridQuad.draw(gl, orientation);
668672 }
669- gl.glRotatef(-theta, 0.0f, 0.0f, 1.0f);
673+ if (theta != 0.0f) {
674+ gl.glRotatef(-theta, 0.0f, 0.0f, 1.0f);
675+ }
670676 gl.glTranslatef(translateXf, translateYf, translateZf);
671677 if (usingMixedTextures) {
672678 view.unbindMixed();
--- a/src/com/cooliris/media/GridDrawables.java
+++ b/src/com/cooliris/media/GridDrawables.java
@@ -3,19 +3,17 @@ package com.cooliris.media;
33 import java.util.HashMap;
44
55 import javax.microedition.khronos.opengles.GL11;
6-import android.os.Process;
76
87 public final class GridDrawables {
98 // The display primitives.
10- public GridQuad mGrid;
11- public final GridQuadFrame mFrame;
12- public final GridQuad mTextGrid;
13- public final GridQuad mSelectedGrid;
14- public final GridQuad mVideoGrid;
15- public final GridQuad mLocationGrid;
16- public final GridQuad mSourceIconGrid;
17- public final GridQuad[] mFullscreenGrid = new GridQuad[3];
18- private GridQuad mGridNew;
9+ public static GridQuad sGrid;
10+ public static GridQuadFrame sFrame;
11+ public static GridQuad sTextGrid;
12+ public static GridQuad sSelectedGrid;
13+ public static GridQuad sVideoGrid;
14+ public static GridQuad sLocationGrid;
15+ public static GridQuad sSourceIconGrid;
16+ public static final GridQuad[] sFullscreenGrid = new GridQuad[3];
1917
2018 // All the resource Textures.
2119 private static final int TEXTURE_FRAME = R.drawable.stack_frame;
@@ -31,7 +29,7 @@ public final class GridDrawables {
3129 public static final int[] TEXTURE_SPINNER = new int[8];
3230 private static final int TEXTURE_TRANSPARENT = R.drawable.transparent;
3331 private static final int TEXTURE_PLACEHOLDER = R.drawable.grid_placeholder;
34-
32+
3533 public Texture mTextureFrame;
3634 public Texture mTextureGridFrame;
3735 public Texture mTextureFrameFocus;
@@ -47,9 +45,9 @@ public final class GridDrawables {
4745 public Texture mTexturePlaceholder;
4846
4947 // The textures generated from strings.
50- public final HashMap<String, StringTexture> mStringTextureTable = new HashMap<String, StringTexture>(128);
48+ public static final HashMap<String, StringTexture> sStringTextureTable = new HashMap<String, StringTexture>(128);
5149
52- public GridDrawables(final int itemWidth, final int itemHeight) {
50+ static {
5351 // We first populate the spinner textures.
5452 final int[] textureSpinner = TEXTURE_SPINNER;
5553 textureSpinner[0] = R.drawable.ic_spinner1;
@@ -60,107 +58,88 @@ public final class GridDrawables {
6058 textureSpinner[5] = R.drawable.ic_spinner6;
6159 textureSpinner[6] = R.drawable.ic_spinner7;
6260 textureSpinner[7] = R.drawable.ic_spinner8;
63-
64- final float height = 1.0f;
65- final float width = (float) (height * itemWidth) / (float) itemHeight;
66- final float aspectRatio = (float) itemWidth / (float) itemHeight;
67- final float oneByAspect = 1.0f / aspectRatio;
68-
69- // We create the grid quad.
70- mGrid = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
71-
72- // We create the quads used in fullscreen.
73- mFullscreenGrid[0] = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
74- mFullscreenGrid[0].setDynamic(true);
75- mFullscreenGrid[1] = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
76- mFullscreenGrid[1].setDynamic(true);
77- mFullscreenGrid[2] = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
78- mFullscreenGrid[2].setDynamic(true);
79-
80- // We create supplementary quads for the checkmarks, video overlay and location button
81- float sizeOfSelectedIcon = 32 * Gallery.PIXEL_DENSITY; // In pixels.
82- sizeOfSelectedIcon /= itemHeight;
83- float sizeOfLocationIcon = 52 * Gallery.PIXEL_DENSITY; // In pixels.
84- sizeOfLocationIcon /= itemHeight;
85- float sizeOfSourceIcon = 76 * Gallery.PIXEL_DENSITY; // In pixels.
86- sizeOfSourceIcon /= itemHeight;
87- mSelectedGrid = GridQuad.createGridQuad(sizeOfSelectedIcon, sizeOfSelectedIcon, -0.5f, 0.25f, 1.0f, 1.0f, false);
88- mVideoGrid = GridQuad.createGridQuad(sizeOfSelectedIcon, sizeOfSelectedIcon, -0.08f, -0.09f, 1.0f, 1.0f, false);
89- mLocationGrid = GridQuad.createGridQuad(sizeOfLocationIcon, sizeOfLocationIcon, 0, 0, 1.0f, 1.0f, false);
90- mSourceIconGrid = GridQuad.createGridQuad(sizeOfSourceIcon, sizeOfSourceIcon, 0, 0, 1.0f, 1.0f, false);
91-
92- // We create the quad for the text label.
93- float seedTextWidth = (Gallery.PIXEL_DENSITY < 1.5f) ? 128.0f : 256.0f;
94- float textWidth = (seedTextWidth / (float) itemWidth) * width;
95- float textHeightPow2 = (Gallery.PIXEL_DENSITY < 1.5f) ? 32.0f : 64.0f;
96- float textHeight = (textHeightPow2 / (float) itemHeight) * height;
97- float textOffsetY = 0.0f;
98- mTextGrid = GridQuad.createGridQuad(textWidth, textHeight, 0, textOffsetY, 1.0f, 1.0f, false);
99-
100- // We finally create the frame around every grid item
101- mFrame = GridQuadFrame.createFrame(width, height, itemWidth, itemHeight);
102-
103- Thread creationThread = new Thread() {
104- public void run() {
105- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
106- try {
107- Thread.sleep(2000);
108- } catch (InterruptedException e) {
109- ;
110- }
111- mGridNew = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, true);
112- }
113- };
114- creationThread.start();
11561 }
116-
117- public void swapGridQuads(GL11 gl) {
118- if (mGridNew != null) {
119- mGrid.freeHardwareBuffers(gl);
120- mGrid = mGridNew;
121- mGrid.generateHardwareBuffers(gl);
122- mGridNew = null;
62+
63+ public GridDrawables(final int itemWidth, final int itemHeight) {
64+ if (sGrid == null) {
65+ final float height = 1.0f;
66+ final float width = (float) (height * itemWidth) / (float) itemHeight;
67+ final float aspectRatio = (float) itemWidth / (float) itemHeight;
68+ final float oneByAspect = 1.0f / aspectRatio;
69+
70+ // We create the grid quad.
71+ sGrid = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, true);
72+
73+ // We create the quads used in fullscreen.
74+ sFullscreenGrid[0] = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
75+ sFullscreenGrid[0].setDynamic(true);
76+ sFullscreenGrid[1] = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
77+ sFullscreenGrid[1].setDynamic(true);
78+ sFullscreenGrid[2] = GridQuad.createGridQuad(width, height, 0, 0, 1.0f, oneByAspect, false);
79+ sFullscreenGrid[2].setDynamic(true);
80+
81+ // We create supplementary quads for the checkmarks, video overlay and location button
82+ float sizeOfSelectedIcon = 32 * Gallery.PIXEL_DENSITY; // In pixels.
83+ sizeOfSelectedIcon /= itemHeight;
84+ float sizeOfLocationIcon = 52 * Gallery.PIXEL_DENSITY; // In pixels.
85+ sizeOfLocationIcon /= itemHeight;
86+ float sizeOfSourceIcon = 76 * Gallery.PIXEL_DENSITY; // In pixels.
87+ sizeOfSourceIcon /= itemHeight;
88+ sSelectedGrid = GridQuad.createGridQuad(sizeOfSelectedIcon, sizeOfSelectedIcon, -0.5f, 0.25f, 1.0f, 1.0f, false);
89+ sVideoGrid = GridQuad.createGridQuad(sizeOfSelectedIcon, sizeOfSelectedIcon, -0.08f, -0.09f, 1.0f, 1.0f, false);
90+ sLocationGrid = GridQuad.createGridQuad(sizeOfLocationIcon, sizeOfLocationIcon, 0, 0, 1.0f, 1.0f, false);
91+ sSourceIconGrid = GridQuad.createGridQuad(sizeOfSourceIcon, sizeOfSourceIcon, 0, 0, 1.0f, 1.0f, false);
92+
93+ // We create the quad for the text label.
94+ float seedTextWidth = (Gallery.PIXEL_DENSITY < 1.5f) ? 128.0f : 256.0f;
95+ float textWidth = (seedTextWidth / (float) itemWidth) * width;
96+ float textHeightPow2 = (Gallery.PIXEL_DENSITY < 1.5f) ? 32.0f : 64.0f;
97+ float textHeight = (textHeightPow2 / (float) itemHeight) * height;
98+ float textOffsetY = 0.0f;
99+ sTextGrid = GridQuad.createGridQuad(textWidth, textHeight, 0, textOffsetY, 1.0f, 1.0f, false);
100+
101+ // We finally create the frame around every grid item
102+ sFrame = GridQuadFrame.createFrame(width, height, itemWidth, itemHeight);
123103 }
124104 }
125105
126106 public void onSurfaceCreated(RenderView view, GL11 gl) {
127107 // The grid quad.
128- mGrid.freeHardwareBuffers(gl);
129- mGrid.generateHardwareBuffers(gl);
108+ sGrid.freeHardwareBuffers(gl);
109+ sGrid.generateHardwareBuffers(gl);
130110
131111 // The fullscreen quads.
132- mFullscreenGrid[0].freeHardwareBuffers(gl);
133- mFullscreenGrid[1].freeHardwareBuffers(gl);
134- mFullscreenGrid[2].freeHardwareBuffers(gl);
135- mFullscreenGrid[0].generateHardwareBuffers(gl);
136- mFullscreenGrid[1].generateHardwareBuffers(gl);
137- mFullscreenGrid[2].generateHardwareBuffers(gl);
112+ sFullscreenGrid[0].freeHardwareBuffers(gl);
113+ sFullscreenGrid[1].freeHardwareBuffers(gl);
114+ sFullscreenGrid[2].freeHardwareBuffers(gl);
115+ sFullscreenGrid[0].generateHardwareBuffers(gl);
116+ sFullscreenGrid[1].generateHardwareBuffers(gl);
117+ sFullscreenGrid[2].generateHardwareBuffers(gl);
138118
139119 // Supplementary quads.
140- mSelectedGrid.freeHardwareBuffers(gl);
141- mVideoGrid.freeHardwareBuffers(gl);
142- mLocationGrid.freeHardwareBuffers(gl);
143- mSourceIconGrid.freeHardwareBuffers(gl);
144- mSelectedGrid.generateHardwareBuffers(gl);
145- mVideoGrid.generateHardwareBuffers(gl);
146- mLocationGrid.generateHardwareBuffers(gl);
147- mSourceIconGrid.generateHardwareBuffers(gl);
120+ sSelectedGrid.freeHardwareBuffers(gl);
121+ sVideoGrid.freeHardwareBuffers(gl);
122+ sLocationGrid.freeHardwareBuffers(gl);
123+ sSourceIconGrid.freeHardwareBuffers(gl);
124+ sSelectedGrid.generateHardwareBuffers(gl);
125+ sVideoGrid.generateHardwareBuffers(gl);
126+ sLocationGrid.generateHardwareBuffers(gl);
127+ sSourceIconGrid.generateHardwareBuffers(gl);
148128
149129 // Text quads.
150- mTextGrid.freeHardwareBuffers(gl);
151- mTextGrid.generateHardwareBuffers(gl);
130+ sTextGrid.freeHardwareBuffers(gl);
131+ sTextGrid.generateHardwareBuffers(gl);
152132
153133 // Frame mesh.
154- mFrame.freeHardwareBuffers(gl);
155- mFrame.generateHardwareBuffers(gl);
134+ sFrame.freeHardwareBuffers(gl);
135+ sFrame.generateHardwareBuffers(gl);
156136
157137 // Clear the string table.
158- mStringTextureTable.clear();
159-
138+ sStringTextureTable.clear();
139+
160140 // Regenerate all the textures.
161141 mTextureFrame = view.getResource(TEXTURE_FRAME, false);
162142 mTextureGridFrame = view.getResource(TEXTURE_GRID_FRAME, false);
163- view.loadTexture(mTextureGridFrame);
164143 mTextureFrameFocus = view.getResource(TEXTURE_FRAME_FOCUS, false);
165144 mTextureFramePressed = view.getResource(TEXTURE_FRAME_PRESSED, false);
166145 mTextureLocation = view.getResource(TEXTURE_LOCATION, false);
@@ -171,7 +150,11 @@ public final class GridDrawables {
171150 mTexturePicasaSmall = view.getResource(TEXTURE_PICASA_SMALL, false);
172151 mTextureTransparent = view.getResource(TEXTURE_TRANSPARENT, false);
173152 mTexturePlaceholder = view.getResource(TEXTURE_PLACEHOLDER, false);
174-
153+ view.loadTexture(mTextureFrame);
154+ view.loadTexture(mTextureGridFrame);
155+ view.loadTexture(mTextureFrameFocus);
156+ view.loadTexture(mTextureFramePressed);
157+
175158 mTextureSpinner[0] = view.getResource(R.drawable.ic_spinner1);
176159 mTextureSpinner[1] = view.getResource(R.drawable.ic_spinner2);
177160 mTextureSpinner[2] = view.getResource(R.drawable.ic_spinner3);
--- a/src/com/cooliris/media/GridInputProcessor.java
+++ b/src/com/cooliris/media/GridInputProcessor.java
@@ -438,14 +438,6 @@ public final class GridInputProcessor implements GestureDetector.OnGestureListen
438438 }
439439 }
440440
441- public int getSelectedSlot() {
442- return mCurrentSelectedSlot;
443- }
444-
445- public int getFocusSlot() {
446- return mCurrentFocusSlot;
447- }
448-
449441 public void clearSelection() {
450442 mCurrentSelectedSlot = Shared.INVALID;
451443 }
@@ -489,10 +481,10 @@ public final class GridInputProcessor implements GestureDetector.OnGestureListen
489481 slotsToSkip = maxSlots;
490482 if (slotsToSkip < -maxSlots)
491483 slotsToSkip = -maxSlots;
492- if (slotsToSkip <= 1) {
484+ if (Math.abs(slotsToSkip) <= 1) {
493485 if (velocityX > 0)
494486 slotsToSkip = -2;
495- else
487+ else if (velocityX < 0)
496488 slotsToSkip = 2;
497489 }
498490 int slotToGetTo = mLayer.getAnchorSlotIndex(GridLayer.ANCHOR_CENTER) + slotsToSkip;
--- a/src/com/cooliris/media/GridLayer.java
+++ b/src/com/cooliris/media/GridLayer.java
@@ -21,24 +21,42 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
2121 public static final int ANCHOR_RIGHT = 1;
2222 public static final int ANCHOR_CENTER = 2;
2323
24- public static final int MAX_ITEMS_PER_SLOT = 24;
24+ public static final int MAX_ITEMS_PER_SLOT = 12;
2525 public static final int MAX_DISPLAYED_ITEMS_PER_SLOT = 4;
2626 public static final int MAX_DISPLAY_SLOTS = 96;
2727 public static final int MAX_ITEMS_DRAWABLE = MAX_ITEMS_PER_SLOT * MAX_DISPLAY_SLOTS;
2828
2929 private static final float SLIDESHOW_TRANSITION_TIME = 3.5f;
3030
31- private HudLayer mHud;
31+ private static HudLayer sHud;
3232 private int mState;
33- private IndexRange mBufferedVisibleRange;
34- private IndexRange mVisibleRange;
35- private IndexRange mPreviousDataRange;
36- private IndexRange mCompleteRange;
37- private Pool<Vector3f> mTempVec;
38- private final ArrayList<MediaItem> mTempList = new ArrayList<MediaItem>();
39- private final MediaItem[] mTempHash = new MediaItem[64];
40- private Vector3f mDeltaAnchorPositionUncommited;
41- private Vector3f mDeltaAnchorPosition;
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();
4260
4361 // The display primitives.
4462 private GridDrawables mDrawables;
@@ -54,16 +72,18 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
5472 private boolean mPerformingLayoutChange;
5573 private boolean mFeedChanged;
5674
57- private LayoutInterface mLayoutInterface;
58- private LayoutInterface mPrevLayoutInterface;
75+ private final LayoutInterface mLayoutInterface;
76+ private static final LayoutInterface sfullScreenLayoutInterface = new GridLayoutInterface(1);
77+
5978 private MediaFeed mMediaFeed;
6079 private boolean mInAlbum = false;
6180 private int mCurrentExpandedSlot;
6281
63- private ArrayList<MediaItem> mVisibleItems;
64- private DisplayList mDisplayList = new DisplayList();
65- private DisplayItem[] mDisplayItems = new DisplayItem[MAX_ITEMS_DRAWABLE];
66- private DisplaySlot[] mDisplaySlots = new DisplaySlot[MAX_DISPLAY_SLOTS];
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+
6787 private float mTimeElapsedSinceTransition;
6888 private BackgroundLayer mBackground;
6989 private boolean mLocationFilter;
@@ -75,9 +95,9 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
7595 private boolean mSlideshowMode;
7696 private boolean mNoDeleteMode = false;
7797 private float mTimeElapsedSinceView;
78- private MediaBucketList mBucketList = new MediaBucketList();
98+ private static final MediaBucketList sBucketList = new MediaBucketList();
7999 private float mTimeElapsedSinceStackViewReady;
80- private Pool<Vector3f> mTempVecAlt;
100+
81101 private Context mContext;
82102 private RenderView mView;
83103 private boolean mPickIntent;
@@ -91,19 +111,8 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
91111 mBackground = new BackgroundLayer(this);
92112 mContext = context;
93113 mView = view;
94- Vector3f[] vectorPool = new Vector3f[128];
95- int length = vectorPool.length;
96- for (int i = 0; i < length; ++i) {
97- vectorPool[i] = new Vector3f();
98- }
99- Vector3f[] vectorPoolRenderThread = new Vector3f[128];
100- length = vectorPoolRenderThread.length;
101- for (int i = 0; i < length; ++i) {
102- vectorPoolRenderThread[i] = new Vector3f();
103- }
104- mTempVec = new Pool<Vector3f>(vectorPool);
105- mTempVecAlt = new Pool<Vector3f>(vectorPoolRenderThread);
106- DisplaySlot[] displaySlots = mDisplaySlots;
114+
115+ DisplaySlot[] displaySlots = sDisplaySlots;
107116 for (int i = 0; i < MAX_DISPLAY_SLOTS; ++i) {
108117 DisplaySlot slot = new DisplaySlot();
109118 displaySlots[i] = slot;
@@ -111,39 +120,43 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
111120 mLayoutInterface = layoutInterface;
112121 mCamera = new GridCamera(0, 0, itemWidth, itemHeight);
113122 mDrawables = new GridDrawables(itemWidth, itemHeight);
114- mBufferedVisibleRange = new IndexRange();
115- mVisibleRange = new IndexRange();
116- mCompleteRange = new IndexRange();
117- mPreviousDataRange = new IndexRange();
118- mPreviousDataRange.begin = Shared.INVALID;
119- mPreviousDataRange.end = Shared.INVALID;
120- mDeltaAnchorPosition = new Vector3f();
121- mDeltaAnchorPositionUncommited = new Vector3f();
122- mPrevLayoutInterface = new GridLayoutInterface(1);
123- mVisibleItems = new ArrayList<MediaItem>();
124- mHud = new HudLayer(context);
125- mHud.setGridLayer(this);
126- mHud.getTimeBar().setListener(this);
127- mHud.getPathBar().pushLabel(R.drawable.icon_home_small, context.getResources().getString(R.string.app_name),
123+ sBufferedVisibleRange.set(Shared.INVALID, Shared.INVALID);
124+ sVisibleRange.set(Shared.INVALID, Shared.INVALID);
125+ sCompleteRange.set(Shared.INVALID, Shared.INVALID);
126+ sPreviousDataRange.set(Shared.INVALID, Shared.INVALID);
127+ sDeltaAnchorPosition.set(0, 0, 0);
128+ sDeltaAnchorPositionUncommited.set(0, 0, 0);
129+ sBucketList.clear();
130+
131+ sVisibleItems = new ArrayList<MediaItem>();
132+ if (sHud == null) {
133+ sHud = new HudLayer(context);
134+ }
135+ sHud.setContext(context);
136+ sHud.setGridLayer(this);
137+ sHud.getPathBar().clear();
138+ sHud.setGridLayer(this);
139+ sHud.getTimeBar().setListener(this);
140+ sHud.getPathBar().pushLabel(R.drawable.icon_home_small, context.getResources().getString(R.string.app_name),
128141 new Runnable() {
129142 public void run() {
130- if (mHud.getAlpha() == 1.0f) {
143+ if (sHud.getAlpha() == 1.0f) {
131144 if (!mFeedAboutToChange) {
132145 setState(STATE_MEDIA_SETS);
133146 }
134147 } else {
135- mHud.setAlpha(1.0f);
148+ sHud.setAlpha(1.0f);
136149 }
137150 }
138151 });
139152 mCameraManager = new GridCameraManager(mCamera);
140- mDrawManager = new GridDrawManager(context, mCamera, mDrawables, mDisplayList, mDisplayItems, mDisplaySlots);
141- mInputProcessor = new GridInputProcessor(context, mCamera, this, mView, mTempVec, mDisplayItems);
153+ mDrawManager = new GridDrawManager(context, mCamera, mDrawables, sDisplayList, sDisplayItems, sDisplaySlots);
154+ mInputProcessor = new GridInputProcessor(context, mCamera, this, mView, sTempVec, sDisplayItems);
142155 setState(STATE_MEDIA_SETS);
143156 }
144157
145158 public HudLayer getHud() {
146- return mHud;
159+ return sHud;
147160 }
148161
149162 public void shutdown() {
@@ -153,16 +166,11 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
153166 mContext = null;
154167 mInputProcessor = null;
155168 mBackground = null;
156- mBucketList = null;
169+ sBucketList.clear();
157170 mCameraManager = null;
158171 mDrawManager = null;
159- mHud.shutDown();
160- mHud = null;
172+ sHud.shutDown();
161173 mView = null;
162-
163- mDisplayItems = null;
164- mDisplayList = null;
165- mDisplaySlots = null;
166174 }
167175
168176 public void stop() {
@@ -178,13 +186,13 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
178186 mBackground.generate(view, lists);
179187 lists.blendedList.add(this);
180188 lists.hitTestList.add(this);
181- mHud.generate(view, lists);
189+ sHud.generate(view, lists);
182190 }
183191
184192 @Override
185193 protected void onSizeChanged() {
186- mHud.setSize(mWidth, mHeight);
187- mHud.setAlpha(1.0f);
194+ sHud.setSize(mWidth, mHeight);
195+ sHud.setAlpha(1.0f);
188196 mBackground.setSize(mWidth, mHeight);
189197 mTimeElapsedSinceTransition = 0.0f;
190198 if (mView != null) {
@@ -202,7 +210,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
202210 feedUnchanged = true;
203211 }
204212 GridLayoutInterface layoutInterface = (GridLayoutInterface) mLayoutInterface;
205- GridLayoutInterface oldLayout = (GridLayoutInterface) mPrevLayoutInterface;
213+ GridLayoutInterface oldLayout = (GridLayoutInterface) sfullScreenLayoutInterface;
206214 oldLayout.mNumRows = layoutInterface.mNumRows;
207215 oldLayout.mSpacingX = layoutInterface.mSpacingX;
208216 oldLayout.mSpacingY = layoutInterface.mSpacingY;
@@ -233,24 +241,24 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
233241 MediaSet set = feed.getCurrentSet();
234242 int icon = mDrawables.getIconForSet(set, true);
235243 if (set != null) {
236- mHud.getPathBar().pushLabel(icon, set.mNoCountTitleString, new Runnable() {
244+ sHud.getPathBar().pushLabel(icon, set.mNoCountTitleString, new Runnable() {
237245 public void run() {
238246 if (mFeedAboutToChange) {
239247 return;
240248 }
241- if (mHud.getAlpha() == 1.0f) {
249+ if (sHud.getAlpha() == 1.0f) {
242250 disableLocationFiltering();
243251 mInputProcessor.clearSelection();
244252 setState(STATE_GRID_VIEW);
245253 } else {
246- mHud.setAlpha(1.0f);
254+ sHud.setAlpha(1.0f);
247255 }
248256 }
249257 });
250258 }
251259 }
252260 if (mState == STATE_FULL_SCREEN) {
253- mHud.getPathBar().popLabel();
261+ sHud.getPathBar().popLabel();
254262 }
255263 break;
256264 case STATE_TIMELINE:
@@ -269,12 +277,12 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
269277 layoutInterface.mSpacingX = (int) (40 * Gallery.PIXEL_DENSITY);
270278 layoutInterface.mSpacingY = (int) (40 * Gallery.PIXEL_DENSITY);
271279 if (mState != STATE_FULL_SCREEN) {
272- mHud.getPathBar().pushLabel(R.drawable.ic_fs_details, "", new Runnable() {
280+ sHud.getPathBar().pushLabel(R.drawable.ic_fs_details, "", new Runnable() {
273281 public void run() {
274- if (mHud.getAlpha() == 1.0f) {
275- mHud.swapFullscreenLabel();
282+ if (sHud.getAlpha() == 1.0f) {
283+ sHud.swapFullscreenLabel();
276284 }
277- mHud.setAlpha(1.0f);
285+ sHud.setAlpha(1.0f);
278286 }
279287 });
280288 }
@@ -293,15 +301,15 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
293301 layoutInterface.mSpacingY = (int) (70 * Gallery.PIXEL_DENSITY * yStretch);
294302 if (mInAlbum) {
295303 if (mState == STATE_FULL_SCREEN) {
296- mHud.getPathBar().popLabel();
304+ sHud.getPathBar().popLabel();
297305 }
298- mHud.getPathBar().popLabel();
306+ sHud.getPathBar().popLabel();
299307 mInAlbum = false;
300308 }
301309 break;
302310 }
303311 mState = state;
304- mHud.onGridStateChanged();
312+ sHud.onGridStateChanged();
305313 if (performLayout && mFeedAboutToChange == false) {
306314 onLayout(Shared.INVALID, Shared.INVALID, oldLayout);
307315 }
@@ -314,9 +322,9 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
314322 protected void enableLocationFiltering(String label) {
315323 if (mLocationFilter == false) {
316324 mLocationFilter = true;
317- mHud.getPathBar().pushLabel(R.drawable.icon_location_small, label, new Runnable() {
325+ sHud.getPathBar().pushLabel(R.drawable.icon_location_small, label, new Runnable() {
318326 public void run() {
319- if (mHud.getAlpha() == 1.0f) {
327+ if (sHud.getAlpha() == 1.0f) {
320328 if (mState == STATE_FULL_SCREEN) {
321329 mInputProcessor.clearSelection();
322330 setState(STATE_GRID_VIEW);
@@ -324,7 +332,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
324332 disableLocationFiltering();
325333 }
326334 } else {
327- mHud.setAlpha(1.0f);
335+ sHud.setAlpha(1.0f);
328336 }
329337 }
330338 });
@@ -335,7 +343,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
335343 if (mLocationFilter) {
336344 mLocationFilter = false;
337345 mMediaFeed.removeFilter();
338- mHud.getPathBar().popLabel();
346+ sHud.getPathBar().popLabel();
339347 }
340348 }
341349
@@ -344,7 +352,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
344352 return false;
345353 }
346354 int state = mState;
347- if (mInputProcessor.getSelectedSlot() == Shared.INVALID) {
355+ if (mInputProcessor.getCurrentSelectedSlot() == Shared.INVALID) {
348356 if (mLocationFilter) {
349357 disableLocationFiltering();
350358 setState(STATE_TIMELINE);
@@ -376,7 +384,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
376384 }
377385 mWakeLock = null;
378386 }
379- mHud.setAlpha(1.0f);
387+ sHud.setAlpha(1.0f);
380388 }
381389
382390 @Override
@@ -394,7 +402,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
394402 MediaFeed feed = mMediaFeed;
395403 if (feed != null) {
396404 feed.shutdown();
397- mDisplayList.clear();
405+ sDisplayList.clear();
398406 mBackground.clear();
399407 }
400408 mMediaFeed = new MediaFeed(mContext, dataSource, this);
@@ -402,33 +410,33 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
402410 }
403411
404412 public IndexRange getVisibleRange() {
405- return mVisibleRange;
413+ return sVisibleRange;
406414 }
407415
408416 public IndexRange getBufferedVisibleRange() {
409- return mBufferedVisibleRange;
417+ return sBufferedVisibleRange;
410418 }
411419
412420 public IndexRange getCompleteRange() {
413- return mCompleteRange;
421+ return sCompleteRange;
414422 }
415423
416424 private int hitTest(Vector3f worldPos, int itemWidth, int itemHeight) {
417425 int retVal = Shared.INVALID;
418426 int firstSlotIndex = 0;
419427 int lastSlotIndex = 0;
420- IndexRange rangeToUse = mVisibleRange;
428+ IndexRange rangeToUse = sVisibleRange;
421429 synchronized (rangeToUse) {
422430 firstSlotIndex = rangeToUse.begin;
423431 lastSlotIndex = rangeToUse.end;
424432 }
425- Pool<Vector3f> pool = mTempVec;
433+ Pool<Vector3f> pool = sTempVec;
426434 float itemWidthBy2 = itemWidth * 0.5f;
427435 float itemHeightBy2 = itemHeight * 0.5f;
428436 Vector3f position = pool.create();
429437 Vector3f deltaAnchorPosition = pool.create();
430438 try {
431- deltaAnchorPosition.set(mDeltaAnchorPosition);
439+ deltaAnchorPosition.set(sDeltaAnchorPosition);
432440 for (int i = firstSlotIndex; i <= lastSlotIndex; ++i) {
433441 GridCameraManager.getSlotPositionForSlotIndex(i, mCamera, mLayoutInterface, deltaAnchorPosition, position);
434442 if (FloatUtils.boundsContainsPoint(position.x - itemWidthBy2, position.x + itemWidthBy2,
@@ -450,12 +458,12 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
450458 if (displayItem != null) {
451459 imageTheta = displayItem.getImageTheta();
452460 }
453- mCameraManager.centerCameraForSlot(mLayoutInterface, slotIndex, baseConvergence, mDeltaAnchorPositionUncommited,
461+ mCameraManager.centerCameraForSlot(mLayoutInterface, slotIndex, baseConvergence, sDeltaAnchorPositionUncommited,
454462 mInputProcessor.getCurrentSelectedSlot(), mZoomValue, imageTheta, mState);
455463 }
456464
457465 boolean constrainCameraForSlot(int slotIndex) {
458- return mCameraManager.constrainCameraForSlot(mLayoutInterface, slotIndex, mDeltaAnchorPosition, mCurrentFocusItemWidth,
466+ return mCameraManager.constrainCameraForSlot(mLayoutInterface, slotIndex, sDeltaAnchorPosition, mCurrentFocusItemWidth,
459467 mCurrentFocusItemHeight);
460468 }
461469
@@ -475,10 +483,17 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
475483 } else {
476484 mTimeElapsedSinceTransition = 0;
477485 }
486+ if (mMediaFeed != null && mMediaFeed.isSingleImageMode()) {
487+ HudLayer hud = getHud();
488+ hud.getPathBar().setHidden(true);
489+ hud.getMenuBar().setHidden(true);
490+ if (hud.getMode() != HudLayer.MODE_NORMAL)
491+ hud.setMode(HudLayer.MODE_NORMAL);
492+ }
478493 if (view.elapsedLoadingExpensiveTextures() > 150 || (mMediaFeed != null && mMediaFeed.getWaitingForMediaScanner())) {
479- mHud.getPathBar().setAnimatedIcons(GridDrawables.TEXTURE_SPINNER);
494+ sHud.getPathBar().setAnimatedIcons(GridDrawables.TEXTURE_SPINNER);
480495 } else {
481- mHud.getPathBar().setAnimatedIcons(null);
496+ sHud.getPathBar().setAnimatedIcons(null);
482497 }
483498
484499 // In that case, we need to commit the respective Display Items when the
@@ -486,19 +501,19 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
486501 GridCamera camera = mCamera;
487502 camera.update(timeElapsed);
488503 DisplayItem anchorDisplayItem = getAnchorDisplayItem(ANCHOR_CENTER);
489- if (anchorDisplayItem != null && !mHud.getTimeBar().isDragged()) {
490- mHud.getTimeBar().setItem(anchorDisplayItem.mItemRef);
504+ if (anchorDisplayItem != null && !sHud.getTimeBar().isDragged()) {
505+ sHud.getTimeBar().setItem(anchorDisplayItem.mItemRef);
491506 }
492- mDisplayList.update(timeElapsed);
507+ sDisplayList.update(timeElapsed);
493508 mInputProcessor.update(timeElapsed);
494509 mSelectedAlpha = FloatUtils.animate(mSelectedAlpha, mTargetAlpha, timeElapsed * 0.5f);
495510 if (mState == STATE_FULL_SCREEN) {
496- mHud.autoHide(true);
511+ sHud.autoHide(true);
497512 } else {
498- mHud.autoHide(false);
499- mHud.setAlpha(1.0f);
513+ sHud.autoHide(false);
514+ sHud.setAlpha(1.0f);
500515 }
501- GridQuad[] fullscreenQuads = mDrawables.mFullscreenGrid;
516+ GridQuad[] fullscreenQuads = GridDrawables.sFullscreenGrid;
502517 int numFullScreenQuads = fullscreenQuads.length;
503518 for (int i = 0; i < numFullScreenQuads; ++i) {
504519 fullscreenQuads[i].update(timeElapsed);
@@ -531,7 +546,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
531546 } catch (InterruptedException e) {
532547
533548 }
534- if (mDisplayList.getNumAnimatables() != 0 || mCamera.isAnimating()
549+ if (sDisplayList.getNumAnimatables() != 0 || mCamera.isAnimating()
535550 || (mTimeElapsedSinceTransition > 0.0f && mTimeElapsedSinceTransition < 1.0f) || mSelectedAlpha != mTargetAlpha
536551 // || (mAnimatedFov != mTargetFov)
537552 || dirty)
@@ -543,41 +558,40 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
543558 private void computeVisibleRange() {
544559 if (mPerformingLayoutChange)
545560 return;
546- if (mDeltaAnchorPosition.equals(mDeltaAnchorPositionUncommited) == false) {
547- mDeltaAnchorPosition.set(mDeltaAnchorPositionUncommited);
561+ if (sDeltaAnchorPosition.equals(sDeltaAnchorPositionUncommited) == false) {
562+ sDeltaAnchorPosition.set(sDeltaAnchorPositionUncommited);
548563 }
549- mCameraManager.computeVisibleRange(mMediaFeed, mLayoutInterface, mDeltaAnchorPosition, mVisibleRange,
550- mBufferedVisibleRange, mCompleteRange, mState);
564+ mCameraManager.computeVisibleRange(mMediaFeed, mLayoutInterface, sDeltaAnchorPosition, sVisibleRange,
565+ sBufferedVisibleRange, sCompleteRange, mState);
551566 }
552567
553568 private void computeVisibleItems() {
554569 if (mFeedAboutToChange == true || mPerformingLayoutChange == true) {
555570 return;
556571 }
557-
558572 computeVisibleRange();
559- int deltaBegin = mBufferedVisibleRange.begin - mPreviousDataRange.begin;
560- int deltaEnd = mBufferedVisibleRange.end - mPreviousDataRange.end;
573+ int deltaBegin = sBufferedVisibleRange.begin - sPreviousDataRange.begin;
574+ int deltaEnd = sBufferedVisibleRange.end - sPreviousDataRange.end;
561575 if (deltaBegin != 0 || deltaEnd != 0) {
562576 // The delta has changed, we have to compute the display items again.
563577 // We find the intersection range, these slots have not changed at all.
564- int firstVisibleSlotIndex = mBufferedVisibleRange.begin;
565- int lastVisibleSlotIndex = mBufferedVisibleRange.end;
566- mPreviousDataRange.begin = firstVisibleSlotIndex;
567- mPreviousDataRange.end = lastVisibleSlotIndex;
578+ int firstVisibleSlotIndex = sBufferedVisibleRange.begin;
579+ int lastVisibleSlotIndex = sBufferedVisibleRange.end;
580+ sPreviousDataRange.begin = firstVisibleSlotIndex;
581+ sPreviousDataRange.end = lastVisibleSlotIndex;
568582
569- Pool<Vector3f> pool = mTempVec;
583+ Pool<Vector3f> pool = sTempVec;
570584 Vector3f position = pool.create();
571585 Vector3f deltaAnchorPosition = pool.create();
572586 try {
573587 MediaFeed feed = mMediaFeed;
574- DisplayList displayList = mDisplayList;
575- DisplayItem[] displayItems = mDisplayItems;
576- DisplaySlot[] displaySlots = mDisplaySlots;
588+ DisplayList displayList = sDisplayList;
589+ DisplayItem[] displayItems = sDisplayItems;
590+ DisplaySlot[] displaySlots = sDisplaySlots;
577591 int numDisplayItems = displayItems.length;
578592 int numDisplaySlots = displaySlots.length;
579- ArrayList<MediaItem> visibleItems = mVisibleItems;
580- deltaAnchorPosition.set(mDeltaAnchorPosition);
593+ ArrayList<MediaItem> visibleItems = sVisibleItems;
594+ deltaAnchorPosition.set(sDeltaAnchorPosition);
581595 LayoutInterface layout = mLayoutInterface;
582596 GridCamera camera = mCamera;
583597 for (int i = firstVisibleSlotIndex; i <= lastVisibleSlotIndex; ++i) {
@@ -588,12 +602,15 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
588602 if (set != null && indexIntoSlots >= 0 && indexIntoSlots < numDisplaySlots) {
589603 ArrayList<MediaItem> items = set.getItems();
590604 displaySlots[indexIntoSlots].setMediaSet(set);
591- ArrayList<MediaItem> bestItems = mTempList;
605+ ArrayList<MediaItem> bestItems = sTempList;
592606 if (mTimeElapsedSinceTransition < 1.0f) {
593- ArrayUtils.computeSortedIntersection(visibleItems, items, MAX_ITEMS_PER_SLOT, bestItems, mTempHash);
607+ // We always show the same top thumbnails for a stack of albums
608+ if (mState == STATE_MEDIA_SETS)
609+ ArrayUtils.computeSortedIntersection(items, visibleItems, MAX_ITEMS_PER_SLOT, bestItems, sTempHash);
610+ else
611+ ArrayUtils.computeSortedIntersection(visibleItems, items, MAX_ITEMS_PER_SLOT, bestItems, sTempHash);
594612 }
595-
596- // TODO: Could be problematic with dummy items in set.
613+
597614 int numItemsInSet = set.getNumItems();
598615 int numBestItems = bestItems.size();
599616 int originallyFoundItems = numBestItems;
@@ -621,7 +638,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
621638 MediaItem item = bestItems.get(j);
622639 if (item != null) {
623640 DisplayItem displayItem = displayList.get(item);
624- if (mState == STATE_FULL_SCREEN
641+ if ((mState == STATE_FULL_SCREEN && i != mInputProcessor.getCurrentSelectedSlot())
625642 || (mState == STATE_GRID_VIEW && (mTimeElapsedSinceTransition > 1.0f || j >= originallyFoundItems))) {
626643 displayItem.set(position, j, false);
627644 displayItem.commit();
@@ -642,22 +659,22 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
642659 mFeedChanged = false;
643660 if (mInputProcessor != null && mState == STATE_FULL_SCREEN) {
644661 int currentSelectedSlot = mInputProcessor.getCurrentSelectedSlot();
645- if (currentSelectedSlot > mCompleteRange.end)
646- currentSelectedSlot = mCompleteRange.end;
662+ if (currentSelectedSlot > sCompleteRange.end)
663+ currentSelectedSlot = sCompleteRange.end;
647664 mInputProcessor.setCurrentSelectedSlot(currentSelectedSlot);
648665 }
649666 if (mState == STATE_GRID_VIEW) {
650667 MediaSet expandedSet = mMediaFeed.getExpandedMediaSet();
651668 if (expandedSet != null) {
652- if (!mHud.getPathBar().getCurrentLabel().equals(expandedSet.mNoCountTitleString)) {
653- mHud.getPathBar().changeLabel(expandedSet.mNoCountTitleString);
669+ if (!sHud.getPathBar().getCurrentLabel().equals(expandedSet.mNoCountTitleString)) {
670+ sHud.getPathBar().changeLabel(expandedSet.mNoCountTitleString);
654671 }
655672 }
656673 }
657674 if (mRequestFocusContentUri != null) {
658675 // We have to find the item that has this contentUri
659676 if (mState == STATE_FULL_SCREEN) {
660- int numSlots = mCompleteRange.end;
677+ int numSlots = sCompleteRange.end;
661678 for (int i = 0; i < numSlots; ++i) {
662679 MediaSet set = feed.getSetForSlot(i);
663680 ArrayList<MediaItem> items = set.getItems();
@@ -679,7 +696,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
679696 }
680697 // We keep upto 400 thumbnails in memory.
681698 int numThumbnailsToKeepInMemory = (mState == STATE_MEDIA_SETS || mState == STATE_TIMELINE) ? 100 : 400;
682- int startMemoryRange = (mBufferedVisibleRange.begin / numThumbnailsToKeepInMemory) * numThumbnailsToKeepInMemory;
699+ int startMemoryRange = (sBufferedVisibleRange.begin / numThumbnailsToKeepInMemory) * numThumbnailsToKeepInMemory;
683700 if (mStartMemoryRange != startMemoryRange) {
684701 mStartMemoryRange = startMemoryRange;
685702 clearUnusedThumbnails();
@@ -690,22 +707,23 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
690707 @Override
691708 public void handleLowMemory() {
692709 clearUnusedThumbnails();
693- mDrawables.mStringTextureTable.clear();
710+ GridDrawables.sStringTextureTable.clear();
694711 mBackground.clearCache();
695712 }
696713
697714 // This method can be potentially expensive
698715 public void clearUnusedThumbnails() {
699- mDisplayList.clearExcept(mDisplayItems);
716+ sDisplayList.clearExcept(sDisplayItems);
700717 }
701718
702719 @Override
703720 public void onSurfaceCreated(RenderView view, GL11 gl) {
704- mDisplayList.clear();
705- mHud.clear();
706- mHud.reset();
707- mDrawables.mStringTextureTable.clear();
721+ sDisplayList.clear();
722+ sHud.clear();
723+ sHud.reset();
724+ GridDrawables.sStringTextureTable.clear();
708725 mDrawables.onSurfaceCreated(view, gl);
726+ mBackground.clear();
709727 }
710728
711729 @Override
@@ -720,7 +738,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
720738 GridCamera camera = mCamera;
721739 int selectedSlotIndex = mInputProcessor.getCurrentSelectedSlot();
722740 computeVisibleItems();
723-
741+
724742 gl.glMatrixMode(GL11.GL_MODELVIEW);
725743 gl.glLoadIdentity();
726744 GLU.gluLookAt(gl, -camera.mEyeX, -camera.mEyeY, -camera.mEyeZ, -camera.mLookAtX, -camera.mLookAtY, -camera.mLookAtZ,
@@ -736,12 +754,14 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
736754 } else {
737755 mTargetAlpha = 1.0f;
738756 }
739- mDrawManager.prepareDraw(mBufferedVisibleRange, mVisibleRange, selectedSlotIndex, mInputProcessor.getCurrentFocusSlot(),
757+ mDrawManager.prepareDraw(sBufferedVisibleRange, sVisibleRange, selectedSlotIndex, mInputProcessor.getCurrentFocusSlot(),
740758 mInputProcessor.isFocusItemPressed());
741759 if (mSelectedAlpha != 0.0f) {
742760 mDrawManager.drawThumbnails(view, gl, mState);
743761 }
744- gl.glDisable(GL11.GL_BLEND);
762+ if (mSelectedAlpha != 1.0f) {
763+ gl.glDisable(GL11.GL_BLEND);
764+ }
745765 // We draw the selected slotIndex.
746766 if (selectedSlotIndex != Shared.INVALID) {
747767 mDrawManager.drawFocusItems(view, gl, mZoomValue, mSlideshowMode, mTimeElapsedSinceView);
@@ -753,15 +773,15 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
753773
754774 public void renderBlended(RenderView view, GL11 gl) {
755775 // We draw the placeholder for all visible slots.
756- if (mHud != null && mDrawManager != null) {
757- mDrawManager.drawBlendedComponents(view, gl, mSelectedAlpha, mState, mHud.getMode(), mTimeElapsedSinceStackViewReady,
758- mTimeElapsedSinceGridViewReady, mBucketList, mMediaFeed.getWaitingForMediaScanner() || mFeedAboutToChange
776+ if (sHud != null && mDrawManager != null) {
777+ mDrawManager.drawBlendedComponents(view, gl, mSelectedAlpha, mState, sHud.getMode(), mTimeElapsedSinceStackViewReady,
778+ mTimeElapsedSinceGridViewReady, sBucketList, mMediaFeed.getWaitingForMediaScanner() || mFeedAboutToChange
759779 || mMediaFeed.isLoading());
760780 }
761781 }
762782
763783 public synchronized void onLayout(int newAnchorSlotIndex, int currentAnchorSlotIndex, LayoutInterface oldLayout) {
764- if (mPerformingLayoutChange || !mDeltaAnchorPosition.equals(mDeltaAnchorPositionUncommited)) {
784+ if (mPerformingLayoutChange || !sDeltaAnchorPosition.equals(sDeltaAnchorPositionUncommited)) {
765785 return;
766786 }
767787
@@ -769,7 +789,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
769789 mPerformingLayoutChange = true;
770790 LayoutInterface layout = mLayoutInterface;
771791 if (oldLayout == null) {
772- oldLayout = mPrevLayoutInterface;
792+ oldLayout = sfullScreenLayoutInterface;
773793 }
774794 GridCamera camera = mCamera;
775795 if (currentAnchorSlotIndex == Shared.INVALID) {
@@ -787,7 +807,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
787807 }
788808 int itemHeight = camera.mItemHeight;
789809 int itemWidth = camera.mItemWidth;
790- Pool<Vector3f> pool = mTempVec;
810+ Pool<Vector3f> pool = sTempVec;
791811 Vector3f deltaAnchorPosition = pool.create();
792812 Vector3f currentSlotPosition = pool.create();
793813 try {
@@ -795,12 +815,12 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
795815 if (currentAnchorSlotIndex != Shared.INVALID && newAnchorSlotIndex != Shared.INVALID) {
796816 layout.getPositionForSlotIndex(newAnchorSlotIndex, itemWidth, itemHeight, deltaAnchorPosition);
797817 oldLayout.getPositionForSlotIndex(currentAnchorSlotIndex, itemWidth, itemHeight, currentSlotPosition);
798- currentSlotPosition.subtract(mDeltaAnchorPosition);
818+ currentSlotPosition.subtract(sDeltaAnchorPosition);
799819 deltaAnchorPosition.subtract(currentSlotPosition);
800820 deltaAnchorPosition.y = 0;
801821 deltaAnchorPosition.z = 0;
802822 }
803- mDeltaAnchorPositionUncommited.set(deltaAnchorPosition);
823+ sDeltaAnchorPositionUncommited.set(deltaAnchorPosition);
804824 } finally {
805825 pool.delete(deltaAnchorPosition);
806826 pool.delete(currentSlotPosition);
@@ -817,8 +837,8 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
817837 }
818838
819839 private void forceRecomputeVisibleRange() {
820- mPreviousDataRange.begin = Shared.INVALID;
821- mPreviousDataRange.end = Shared.INVALID;
840+ sPreviousDataRange.begin = Shared.INVALID;
841+ sPreviousDataRange.end = Shared.INVALID;
822842 if (mView != null) {
823843 mView.requestRender();
824844 }
@@ -830,7 +850,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
830850 mFeedChanged = true;
831851 forceRecomputeVisibleRange();
832852 if (mState == STATE_GRID_VIEW || mState == STATE_FULL_SCREEN)
833- mHud.setFeed(feed, mState, needsLayout);
853+ sHud.setFeed(feed, mState, needsLayout);
834854 return;
835855 }
836856
@@ -838,21 +858,21 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
838858 Thread.yield();
839859 }
840860 if (mState == STATE_GRID_VIEW) {
841- if (mHud != null) {
861+ if (sHud != null) {
842862 MediaSet set = feed.getCurrentSet();
843863 if (set != null && !mLocationFilter)
844- mHud.getPathBar().changeLabel(set.mNoCountTitleString);
864+ sHud.getPathBar().changeLabel(set.mNoCountTitleString);
845865 }
846866 }
847- DisplayItem[] displayItems = mDisplayItems;
848- int firstBufferedVisibleSlotIndex = mBufferedVisibleRange.begin;
849- int lastBufferedVisibleSlotIndex = mBufferedVisibleRange.end;
867+ DisplayItem[] displayItems = sDisplayItems;
868+ int firstBufferedVisibleSlotIndex = sBufferedVisibleRange.begin;
869+ int lastBufferedVisibleSlotIndex = sBufferedVisibleRange.end;
850870 int currentlyVisibleSlotIndex = getAnchorSlotIndex(ANCHOR_CENTER);
851871 if (mCurrentExpandedSlot != Shared.INVALID) {
852872 currentlyVisibleSlotIndex = mCurrentExpandedSlot;
853873 }
854874 MediaItem anchorItem = null;
855- ArrayList<MediaItem> visibleItems = mVisibleItems;
875+ ArrayList<MediaItem> visibleItems = sVisibleItems;
856876 visibleItems.clear();
857877 visibleItems.ensureCapacity(lastBufferedVisibleSlotIndex - firstBufferedVisibleSlotIndex);
858878 if (currentlyVisibleSlotIndex != Shared.INVALID && currentlyVisibleSlotIndex >= firstBufferedVisibleSlotIndex
@@ -915,7 +935,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
915935 // We must create a new display store now since the data has changed.
916936 if (newSlotIndex != Shared.INVALID) {
917937 if (mState == STATE_MEDIA_SETS) {
918- mDisplayList.clearExcept(displayItems);
938+ sDisplayList.clearExcept(displayItems);
919939 }
920940 onLayout(newSlotIndex, currentlyVisibleSlotIndex, null);
921941 } else {
@@ -925,25 +945,37 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
925945 mFeedAboutToChange = false;
926946 mFeedChanged = true;
927947 if (feed != null) {
928- mHud.setFeed(feed, mState, needsLayout);
948+ if (mState == STATE_GRID_VIEW || mState == STATE_FULL_SCREEN)
949+ sHud.setFeed(feed, mState, needsLayout);
929950 }
930951 if (mView != null) {
931952 mView.requestRender();
932953 }
933954 }
955+
956+ public DisplayItem getRepresentativeDisplayItem() {
957+ int slotIndex = Shared.INVALID;
958+ if (mInputProcessor != null) {
959+ slotIndex = mInputProcessor.getCurrentFocusSlot();
960+ }
961+ if (slotIndex == Shared.INVALID) {
962+ slotIndex = getAnchorSlotIndex(ANCHOR_CENTER);
963+ }
964+ return sDisplayItems[(slotIndex - sBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT];
965+ }
934966
935967 public DisplayItem getAnchorDisplayItem(int type) {
936968 int slotIndex = getAnchorSlotIndex(type);
937- return mDisplayItems[(slotIndex - mBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT];
969+ return sDisplayItems[(slotIndex - sBufferedVisibleRange.begin) * MAX_ITEMS_PER_SLOT];
938970 }
939971
940972 public float getScrollPosition() {
941- return (mCamera.mLookAtX * mCamera.mScale + mDeltaAnchorPosition.x); // in
973+ return (mCamera.mLookAtX * mCamera.mScale + sDeltaAnchorPosition.x); // in
942974 // pixels
943975 }
944976
945977 public DisplayItem getDisplayItemForScrollPosition(float posX) {
946- Pool<Vector3f> pool = mTempVecAlt;
978+ Pool<Vector3f> pool = sTempVecAlt;
947979 MediaFeed feed = mMediaFeed;
948980 int itemWidth = mCamera.mItemWidth;
949981 int itemHeight = mCamera.mItemHeight;
@@ -978,7 +1010,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
9781010 if (set != null) {
9791011 ArrayList<MediaItem> items = set.getItems();
9801012 if (items != null && set.getNumItems() > 0) {
981- return (mDisplayList.get(items.get(0)));
1013+ return (sDisplayList.get(items.get(0)));
9821014 }
9831015 }
9841016 return null;
@@ -989,22 +1021,22 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
9891021 int retVal = 0;
9901022 switch (anchorType) {
9911023 case ANCHOR_LEFT:
992- retVal = mVisibleRange.begin;
1024+ retVal = sVisibleRange.begin;
9931025 break;
9941026 case ANCHOR_RIGHT:
995- retVal = mVisibleRange.end;
1027+ retVal = sVisibleRange.end;
9961028 break;
9971029 case ANCHOR_CENTER:
998- retVal = (mVisibleRange.begin + mVisibleRange.end) / 2;
1030+ retVal = (sVisibleRange.begin + sVisibleRange.end) / 2;
9991031 break;
10001032 }
10011033 return retVal;
10021034 }
10031035
10041036 DisplayItem getDisplayItemForSlotId(int slotId) {
1005- int index = slotId - mBufferedVisibleRange.begin;
1006- if (index >= 0 && slotId <= mBufferedVisibleRange.end) {
1007- return mDisplayItems[index * MAX_ITEMS_PER_SLOT];
1037+ int index = slotId - sBufferedVisibleRange.begin;
1038+ if (index >= 0 && slotId <= sBufferedVisibleRange.end) {
1039+ return sDisplayItems[index * MAX_ITEMS_PER_SLOT];
10081040 }
10091041 return null;
10101042 }
@@ -1014,20 +1046,20 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
10141046 boolean retVal = changeFocusToSlot(currentSelectedSlot + 1, convergence);
10151047 if (mInputProcessor.getCurrentSelectedSlot() == currentSelectedSlot) {
10161048 endSlideshow();
1017- mHud.setAlpha(1.0f);
1049+ sHud.setAlpha(1.0f);
10181050 }
10191051 return retVal;
10201052 }
10211053
10221054 boolean changeFocusToSlot(int slotId, float convergence) {
10231055 mZoomValue = 1.0f;
1024- int index = slotId - mBufferedVisibleRange.begin;
1025- if (index >= 0 && slotId <= mBufferedVisibleRange.end) {
1026- DisplayItem displayItem = mDisplayItems[index * MAX_ITEMS_PER_SLOT];
1056+ int index = slotId - sBufferedVisibleRange.begin;
1057+ if (index >= 0 && slotId <= sBufferedVisibleRange.end) {
1058+ DisplayItem displayItem = sDisplayItems[index * MAX_ITEMS_PER_SLOT];
10271059 if (displayItem != null) {
10281060 MediaItem item = displayItem.mItemRef;
1029- mHud.fullscreenSelectionChanged(item, slotId + 1, mCompleteRange.end + 1);
1030- if (slotId != Shared.INVALID && slotId <= mCompleteRange.end) {
1061+ sHud.fullscreenSelectionChanged(item, slotId + 1, sCompleteRange.end + 1);
1062+ if (slotId != Shared.INVALID && slotId <= sCompleteRange.end) {
10311063 mInputProcessor.setCurrentFocusSlot(slotId);
10321064 centerCameraForSlot(slotId, convergence);
10331065 return true;
@@ -1045,12 +1077,12 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
10451077 }
10461078
10471079 public ArrayList<MediaBucket> getSelectedBuckets() {
1048- return mBucketList.get();
1080+ return sBucketList.get();
10491081 }
10501082
10511083 public void selectAll() {
10521084 if (mState != STATE_FULL_SCREEN) {
1053- int numSlots = mCompleteRange.end + 1;
1085+ int numSlots = sCompleteRange.end + 1;
10541086 for (int i = 0; i < numSlots; ++i) {
10551087 addSlotToSelectedItems(i, false, false);
10561088 }
@@ -1061,17 +1093,17 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
10611093 }
10621094
10631095 public void deselectOrCancelSelectMode() {
1064- if (mBucketList.size() == 0) {
1065- mHud.cancelSelection();
1096+ if (sBucketList.size() == 0) {
1097+ sHud.cancelSelection();
10661098 } else {
1067- mBucketList.clear();
1099+ sBucketList.clear();
10681100 updateCountOfSelectedItems();
10691101 }
10701102 }
10711103
10721104 public void deselectAll() {
1073- mHud.cancelSelection();
1074- mBucketList.clear();
1105+ sHud.cancelSelection();
1106+ sBucketList.clear();
10751107 updateCountOfSelectedItems();
10761108 }
10771109
@@ -1081,7 +1113,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
10811113 deselectAll();
10821114
10831115 // If the current set is now empty, return to the parent set.
1084- if (mCompleteRange.isEmpty()) {
1116+ if (sCompleteRange.isEmpty()) {
10851117 goBack(); // TODO(venkat): This does not work most of the time, can you take a look?
10861118 }
10871119 }
@@ -1089,17 +1121,17 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
10891121 void addSlotToSelectedItems(int slotId, boolean removeIfAlreadyAdded, boolean updateCount) {
10901122 if (mFeedAboutToChange == false) {
10911123 MediaFeed feed = mMediaFeed;
1092- mBucketList.add(slotId, feed, removeIfAlreadyAdded);
1124+ sBucketList.add(slotId, feed, removeIfAlreadyAdded);
10931125 if (updateCount) {
10941126 updateCountOfSelectedItems();
1095- if (mBucketList.size() == 0)
1127+ if (sBucketList.size() == 0)
10961128 deselectAll();
10971129 }
10981130 }
10991131 }
11001132
11011133 private void updateCountOfSelectedItems() {
1102- mHud.updateNumItemsSelected(mBucketList.size());
1134+ sHud.updateNumItemsSelected(sBucketList.size());
11031135 }
11041136
11051137 public int getMetadataSlotIndexForScreenPosition(int posX, int posY) {
@@ -1112,7 +1144,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
11121144 }
11131145
11141146 private int getSlotForScreenPosition(int posX, int posY, int itemWidth, int itemHeight) {
1115- Pool<Vector3f> pool = mTempVec;
1147+ Pool<Vector3f> pool = sTempVec;
11161148 int retVal = 0;
11171149 Vector3f worldPos = pool.create();
11181150 try {
@@ -1148,7 +1180,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
11481180 mCurrentExpandedSlot = slotIndex;
11491181 goBack();
11501182 if (metadata) {
1151- DisplaySlot slot = mDisplaySlots[slotIndex - mBufferedVisibleRange.begin];
1183+ DisplaySlot slot = sDisplaySlots[slotIndex - sBufferedVisibleRange.begin];
11521184 if (slot.hasValidLocation()) {
11531185 MediaSet set = slot.getMediaSet();
11541186 if (set.mReverseGeocodedLocation != null) {
@@ -1196,7 +1228,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
11961228 mZoomValue = 1.0f;
11971229 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f);
11981230 mTimeElapsedSinceView = SLIDESHOW_TRANSITION_TIME - 1.0f;
1199- mHud.setAlpha(0);
1231+ sHud.setAlpha(0);
12001232 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
12011233 mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GridView.Slideshow");
12021234 mWakeLock.acquire();
@@ -1204,7 +1236,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
12041236
12051237 public void enterSelectionMode() {
12061238 mSlideshowMode = false;
1207- mHud.enterSelectionMode();
1239+ sHud.enterSelectionMode();
12081240 int currentSlot = mInputProcessor.getCurrentSelectedSlot();
12091241 if (currentSlot == Shared.INVALID) {
12101242 currentSlot = mInputProcessor.getCurrentFocusSlot();
@@ -1213,7 +1245,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
12131245 }
12141246
12151247 private float getFillScreenZoomValue() {
1216- return GridCameraManager.getFillScreenZoomValue(mCamera, mTempVec, mCurrentFocusItemWidth, mCurrentFocusItemHeight);
1248+ return GridCameraManager.getFillScreenZoomValue(mCamera, sTempVec, mCurrentFocusItemWidth, mCurrentFocusItemHeight);
12171249 }
12181250
12191251 public void zoomInToSelectedItem() {
@@ -1227,7 +1259,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
12271259 if (mZoomValue > 6.0f) {
12281260 mZoomValue = 6.0f;
12291261 }
1230- mHud.setAlpha(1.0f);
1262+ sHud.setAlpha(1.0f);
12311263 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f);
12321264 }
12331265
@@ -1241,14 +1273,14 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
12411273 if (mZoomValue < 1.0f) {
12421274 mZoomValue = 1.0f;
12431275 }
1244- mHud.setAlpha(1.0f);
1276+ sHud.setAlpha(1.0f);
12451277 centerCameraForSlot(mInputProcessor.getCurrentSelectedSlot(), 1.0f);
12461278 }
12471279
12481280 public void rotateSelectedItems(float f) {
1249- MediaBucketList bucketList = mBucketList;
1281+ MediaBucketList bucketList = sBucketList;
12501282 ArrayList<MediaBucket> mediaBuckets = bucketList.get();
1251- DisplayList displayList = mDisplayList;
1283+ DisplayList displayList = sDisplayList;
12521284 int numBuckets = mediaBuckets.size();
12531285 for (int i = 0; i < numBuckets; ++i) {
12541286 MediaBucket bucket = mediaBuckets.get(i);
@@ -1307,7 +1339,7 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
13071339 }
13081340
13091341 public Vector3f getDeltaAnchorPosition() {
1310- return mDeltaAnchorPosition;
1342+ return sDeltaAnchorPosition;
13111343 }
13121344
13131345 public int getExpandedSlot() {
@@ -1325,16 +1357,16 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
13251357
13261358 public void setPickIntent(boolean b) {
13271359 mPickIntent = b;
1328- mHud.getPathBar().popLabel();
1329- mHud.getPathBar().pushLabel(R.drawable.icon_location_small, mContext.getResources().getString(R.string.pick),
1360+ sHud.getPathBar().popLabel();
1361+ sHud.getPathBar().pushLabel(R.drawable.icon_location_small, mContext.getResources().getString(R.string.pick),
13301362 new Runnable() {
13311363 public void run() {
1332- if (mHud.getAlpha() == 1.0f) {
1364+ if (sHud.getAlpha() == 1.0f) {
13331365 if (!mFeedAboutToChange) {
13341366 setState(STATE_MEDIA_SETS);
13351367 }
13361368 } else {
1337- mHud.setAlpha(1.0f);
1369+ sHud.setAlpha(1.0f);
13381370 }
13391371 }
13401372 });
@@ -1350,18 +1382,18 @@ public final class GridLayer extends RootLayer implements MediaFeed.Listener, Ti
13501382 mMediaFeed.expandMediaSet(0);
13511383 setState(STATE_GRID_VIEW);
13521384 // We need to make sure we haven't pushed the same label twice
1353- if (mHud.getPathBar().getNumLevels() == 1) {
1354- mHud.getPathBar().pushLabel(R.drawable.icon_folder_small, setName, new Runnable() {
1385+ if (sHud.getPathBar().getNumLevels() == 1) {
1386+ sHud.getPathBar().pushLabel(R.drawable.icon_folder_small, setName, new Runnable() {
13551387 public void run() {
13561388 if (mFeedAboutToChange) {
13571389 return;
13581390 }
1359- if (mHud.getAlpha() == 1.0f) {
1391+ if (sHud.getAlpha() == 1.0f) {
13601392 disableLocationFiltering();
13611393 mInputProcessor.clearSelection();
13621394 setState(STATE_GRID_VIEW);
13631395 } else {
1364- mHud.setAlpha(1.0f);
1396+ sHud.setAlpha(1.0f);
13651397 }
13661398 }
13671399 });
--- a/src/com/cooliris/media/HudLayer.java
+++ b/src/com/cooliris/media/HudLayer.java
@@ -15,6 +15,7 @@ import android.content.pm.ResolveInfo;
1515 import android.content.res.Resources;
1616 import android.net.Uri;
1717 import android.util.FloatMath;
18+import android.util.Log;
1819 import android.view.MotionEvent;
1920
2021 import com.cooliris.media.MenuBar.Menu;
@@ -24,13 +25,13 @@ public final class HudLayer extends Layer {
2425 public static final int MODE_NORMAL = 0;
2526 public static final int MODE_SELECT = 1;
2627
27- private final Context mContext;
28+ private Context mContext;
2829 private GridLayer mGridLayer;
2930 private final ImageButton mTopRightButton = new ImageButton();
3031 private final ImageButton mZoomInButton = new ImageButton();
3132 private final ImageButton mZoomOutButton = new ImageButton();
32- private final PathBarLayer mPathBar;
33- private final TimeBar mTimeBar;
33+ private static PathBarLayer sPathBar;
34+ private static TimeBar sTimeBar;
3435 private MenuBar.Menu[] mNormalBottomMenu = null;
3536 private MenuBar.Menu[] mSingleViewIntentBottomMenu = null;
3637 private final MenuBar mSelectionMenuBottom;
@@ -103,9 +104,10 @@ public final class HudLayer extends Layer {
103104
104105 HudLayer(Context context) {
105106 mAlpha = 1.0f;
106- mContext = context;
107- mTimeBar = new TimeBar(context);
108- mPathBar = new PathBarLayer();
107+ if (sTimeBar == null) {
108+ sTimeBar = new TimeBar(context);
109+ sPathBar = new PathBarLayer();
110+ }
109111 mTopRightButton.setSize((int) (100 * Gallery.PIXEL_DENSITY), (int) (94 * Gallery.PIXEL_DENSITY));
110112
111113 mZoomInButton.setSize(43 * Gallery.PIXEL_DENSITY, 43 * Gallery.PIXEL_DENSITY);
@@ -118,13 +120,13 @@ public final class HudLayer extends Layer {
118120 // The Share submenu is populated dynamically when opened.
119121 Resources resources = context.getResources();
120122 PopupMenu.Option[] deleteOptions = {
121- new PopupMenu.Option(mContext.getResources().getString(R.string.confirm_delete), resources
123+ new PopupMenu.Option(context.getResources().getString(R.string.confirm_delete), resources
122124 .getDrawable(R.drawable.icon_delete), new Runnable() {
123125 public void run() {
124126 deleteSelection();
125127 }
126128 }),
127- new PopupMenu.Option(mContext.getResources().getString(R.string.cancel), resources
129+ new PopupMenu.Option(context.getResources().getString(R.string.cancel), resources
128130 .getDrawable(R.drawable.icon_cancel), new Runnable() {
129131 public void run() {
130132
@@ -132,17 +134,17 @@ public final class HudLayer extends Layer {
132134 }), };
133135 mSelectionMenuBottom = new MenuBar(context);
134136
135- MenuBar.Menu shareMenu = new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.share)).icon(
137+ MenuBar.Menu shareMenu = new MenuBar.Menu.Builder(context.getResources().getString(R.string.share)).icon(
136138 R.drawable.icon_share).onSelect(new Runnable() {
137139 public void run() {
138140 updateShareMenu();
139141 }
140142 }).build();
141143
142- MenuBar.Menu deleteMenu = new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.delete)).icon(
144+ MenuBar.Menu deleteMenu = new MenuBar.Menu.Builder(context.getResources().getString(R.string.delete)).icon(
143145 R.drawable.icon_delete).options(deleteOptions).build();
144146
145- MenuBar.Menu moreMenu = new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.more)).icon(
147+ MenuBar.Menu moreMenu = new MenuBar.Menu.Builder(context.getResources().getString(R.string.more)).icon(
146148 R.drawable.icon_more).onSelect(new Runnable() {
147149 public void run() {
148150 buildMoreOptions();
@@ -155,19 +157,19 @@ public final class HudLayer extends Layer {
155157 mSelectionMenuBottom.setMenus(mNormalBottomMenu);
156158 mSelectionMenuTop = new MenuBar(context);
157159 mSelectionMenuTop.setMenus(new MenuBar.Menu[] {
158- new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.select_all)).onSelect(new Runnable() {
160+ new MenuBar.Menu.Builder(context.getResources().getString(R.string.select_all)).onSelect(new Runnable() {
159161 public void run() {
160162 mGridLayer.selectAll();
161163 }
162164 }).build(), new MenuBar.Menu.Builder("").build(),
163- new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.deselect_all)).onSelect(new Runnable() {
165+ new MenuBar.Menu.Builder(context.getResources().getString(R.string.deselect_all)).onSelect(new Runnable() {
164166 public void run() {
165167 mGridLayer.deselectOrCancelSelectMode();
166168 }
167169 }).build() });
168170 mFullscreenMenu = new MenuBar(context);
169171 mFullscreenMenu.setMenus(new MenuBar.Menu[] {
170- new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.slideshow)).icon(R.drawable.icon_play)
172+ new MenuBar.Menu.Builder(context.getResources().getString(R.string.slideshow)).icon(R.drawable.icon_play)
171173 .onSingleTapUp(new Runnable() {
172174 public void run() {
173175 if (getAlpha() == 1.0f)
@@ -176,7 +178,7 @@ public final class HudLayer extends Layer {
176178 setAlpha(1.0f);
177179 }
178180 }).build(), /* new MenuBar.Menu.Builder("").build(), */
179- new MenuBar.Menu.Builder(mContext.getResources().getString(R.string.menu)).icon(R.drawable.icon_more)
181+ new MenuBar.Menu.Builder(context.getResources().getString(R.string.menu)).icon(R.drawable.icon_more)
180182 .onSingleTapUp(new Runnable() {
181183 public void run() {
182184 if (getAlpha() == 1.0f)
@@ -186,6 +188,13 @@ public final class HudLayer extends Layer {
186188 }
187189 }).build() });
188190 }
191+
192+ public void setContext(Context context) {
193+ if (mContext != context) {
194+ mContext = context;
195+ sTimeBar.regenerateStringsForContext(context);
196+ }
197+ }
189198
190199 private void buildMoreOptions() {
191200 ArrayList<MediaBucket> buckets = mGridLayer.getSelectedBuckets();
@@ -407,8 +416,8 @@ public final class HudLayer extends Layer {
407416 final float height = mHeight;
408417 closeSelectionMenu();
409418
410- mTimeBar.setPosition(0f, height - TimeBar.HEIGHT * Gallery.PIXEL_DENSITY);
411- mTimeBar.setSize(width, TimeBar.HEIGHT * Gallery.PIXEL_DENSITY);
419+ sTimeBar.setPosition(0f, height - TimeBar.HEIGHT * Gallery.PIXEL_DENSITY);
420+ sTimeBar.setSize(width, TimeBar.HEIGHT * Gallery.PIXEL_DENSITY);
412421 mSelectionMenuTop.setPosition(0f, 0);
413422 mSelectionMenuTop.setSize(width, MenuBar.HEIGHT * Gallery.PIXEL_DENSITY);
414423 mSelectionMenuBottom.setPosition(0f, height - MenuBar.HEIGHT * Gallery.PIXEL_DENSITY);
@@ -417,7 +426,7 @@ public final class HudLayer extends Layer {
417426 mFullscreenMenu.setPosition(0f, height - MenuBar.HEIGHT * Gallery.PIXEL_DENSITY);
418427 mFullscreenMenu.setSize(width, MenuBar.HEIGHT * Gallery.PIXEL_DENSITY);
419428
420- mPathBar.setPosition(0f, -4f * Gallery.PIXEL_DENSITY);
429+ sPathBar.setPosition(0f, -4f * Gallery.PIXEL_DENSITY);
421430 computeSizeForPathbar();
422431
423432 mTopRightButton.setPosition(width - mTopRightButton.getWidth(), 0f);
@@ -429,12 +438,12 @@ public final class HudLayer extends Layer {
429438 float pathBarWidth = mWidth
430439 - ((mGridLayer.getState() == GridLayer.STATE_FULL_SCREEN) ? 32 * Gallery.PIXEL_DENSITY
431440 : 120 * Gallery.PIXEL_DENSITY);
432- mPathBar.setSize(pathBarWidth, FloatMath.ceil(39 * Gallery.PIXEL_DENSITY));
433- mPathBar.recomputeComponents();
441+ sPathBar.setSize(pathBarWidth, FloatMath.ceil(39 * Gallery.PIXEL_DENSITY));
442+ sPathBar.recomputeComponents();
434443 }
435444
436445 public void setFeed(MediaFeed feed, int state, boolean needsLayout) {
437- mTimeBar.setFeed(feed, state, needsLayout);
446+ sTimeBar.setFeed(feed, state, needsLayout);
438447 }
439448
440449 public void onGridStateChanged() {
@@ -442,8 +451,9 @@ public final class HudLayer extends Layer {
442451 }
443452
444453 private void updateViews() {
454+ if (mGridLayer == null)
455+ return;
445456 final int state = mGridLayer.getState();
446-
447457 // Show the selection menu in selection mode.
448458 final boolean selectionMode = mMode == MODE_SELECT;
449459 final boolean fullscreenMode = state == GridLayer.STATE_FULL_SCREEN;
@@ -455,11 +465,11 @@ public final class HudLayer extends Layer {
455465 mZoomOutButton.setHidden(mFullscreenMenu.isHidden());
456466
457467 // Show the time bar in stack and grid states, except in selection mode.
458- mTimeBar.setHidden(fullscreenMode || selectionMode || stackMode);
468+ sTimeBar.setHidden(fullscreenMode || selectionMode || stackMode);
459469 // mTimeBar.setHidden(selectionMode || (state != GridLayer.STATE_TIMELINE && state != GridLayer.STATE_GRID_VIEW));
460470
461471 // Hide the path bar and top-right button in selection mode.
462- mPathBar.setHidden(selectionMode);
472+ sPathBar.setHidden(selectionMode);
463473 mTopRightButton.setHidden(selectionMode || fullscreenMode);
464474 computeSizeForPathbar();
465475
@@ -495,11 +505,11 @@ public final class HudLayer extends Layer {
495505 }
496506
497507 public TimeBar getTimeBar() {
498- return mTimeBar;
508+ return sTimeBar;
499509 }
500510
501511 public PathBarLayer getPathBar() {
502- return mPathBar;
512+ return sPathBar;
503513 }
504514
505515 public GridLayer getGridLayer() {
@@ -566,12 +576,12 @@ public final class HudLayer extends Layer {
566576 mTopRightButton.generate(view, lists);
567577 mZoomInButton.generate(view, lists);
568578 mZoomOutButton.generate(view, lists);
569- mTimeBar.generate(view, lists);
579+ sTimeBar.generate(view, lists);
570580 mSelectionMenuTop.generate(view, lists);
571581 mSelectionMenuBottom.generate(view, lists);
572582 mFullscreenMenu.generate(view, lists);
573- mPathBar.generate(view, lists);
574- //mLoadingLayer.generate(view, lists);
583+ sPathBar.generate(view, lists);
584+ // mLoadingLayer.generate(view, lists);
575585 mView = view;
576586 }
577587
@@ -609,6 +619,7 @@ public final class HudLayer extends Layer {
609619
610620 void reset() {
611621 mLoadingLayer.reset();
622+ sTimeBar.regenerateStringsForContext(mContext);
612623 }
613624
614625 public void fullscreenSelectionChanged(MediaItem item, int index, int count) {
@@ -622,12 +633,7 @@ public final class HudLayer extends Layer {
622633 mCachedCaption = item.mCaption;
623634 mCachedPosition = location;
624635 mCachedCurrentLabel = location;
625- mPathBar.changeLabel(location);
626- // String displayString = DateFormat.format("h:mmaa MMM dd yyyy", item.dateTaken).toString();
627- // Menu menu = new
628- // MenuBar.Menu.Builder(displayString).StringTexture.Config(MenuBar.MENU_TITLE_STYLE_TEXT).resizeToAccomodate()
629- // .build();
630- // mFullscreenMenu.updateMenu(menu, 1);
636+ sPathBar.changeLabel(location);
631637 }
632638
633639 private void updateShareMenu() {
@@ -739,7 +745,7 @@ public final class HudLayer extends Layer {
739745
740746 public void swapFullscreenLabel() {
741747 mCachedCurrentLabel = (mCachedCurrentLabel == mCachedCaption || mCachedCaption == null) ? mCachedPosition : mCachedCaption;
742- mPathBar.changeLabel(mCachedCurrentLabel);
748+ sPathBar.changeLabel(mCachedCurrentLabel);
743749 }
744750
745751 public void clear() {
@@ -747,7 +753,7 @@ public final class HudLayer extends Layer {
747753 }
748754
749755 public void shutDown() {
750- mGridLayer = null;
756+
751757 }
752758
753759 public void enterSelectionMode() {
--- a/src/com/cooliris/media/ImageManager.java
+++ b/src/com/cooliris/media/ImageManager.java
@@ -280,9 +280,9 @@ public class ImageManager {
280280 return false;
281281 }
282282
283- private static Cursor query(ContentResolver resolver, Uri uri,
284- String[] projection, String selection, String[] selectionArgs,
285- String sortOrder) {
283+ private static final Cursor query(final ContentResolver resolver, final Uri uri,
284+ final String[] projection, final String selection, final String[] selectionArgs,
285+ final String sortOrder) {
286286 try {
287287 if (resolver == null) {
288288 return null;
@@ -295,9 +295,9 @@ public class ImageManager {
295295
296296 }
297297
298- public static boolean isMediaScannerScanning(ContentResolver cr) {
298+ public static final boolean isMediaScannerScanning(final ContentResolver cr) {
299299 boolean result = false;
300- Cursor cursor = query(cr, MediaStore.getMediaScannerUri(),
300+ final Cursor cursor = query(cr, MediaStore.getMediaScannerUri(),
301301 new String [] {MediaStore.MEDIA_SCANNER_VOLUME},
302302 null, null, null);
303303 if (cursor != null) {
--- a/src/com/cooliris/media/LocalDataSource.java
+++ b/src/com/cooliris/media/LocalDataSource.java
@@ -94,6 +94,9 @@ public final class LocalDataSource implements DataSource {
9494 }
9595
9696 public void shutdown() {
97+ if (ImageManager.isMediaScannerScanning(mContext.getContentResolver())) {
98+ stopListeners();
99+ }
97100 }
98101
99102 private void stopListeners() {
@@ -113,7 +116,6 @@ public final class LocalDataSource implements DataSource {
113116 Log.i(TAG, "Refreshing local data source");
114117 Gallery.NEEDS_REFRESH = true;
115118 if (feed.getMediaSet(setIdToUse) == null) {
116- Log.i(TAG, "We check to see if there are any items with this bucket id in the database.");
117119 if (!CacheService.setHasItems(mContext.getContentResolver(), setIdToUse))
118120 return;
119121 MediaSet mediaSet = feed.addMediaSet(setIdToUse, this);
--- a/src/com/cooliris/media/MediaFeed.java
+++ b/src/com/cooliris/media/MediaFeed.java
@@ -23,6 +23,7 @@ public final class MediaFeed implements Runnable {
2323 private Listener mListener;
2424 private DataSource mDataSource;
2525 private boolean mListenerNeedsUpdate = false;
26+ private boolean mMediaFeedNeedsToRun = false;
2627 private MediaSet mSingleWrapper = new MediaSet();
2728 private boolean mInClusteringMode = false;
2829 private HashMap<MediaSet, MediaClustering> mClusterSets = new HashMap<MediaSet, MediaClustering>(32);
@@ -50,7 +51,7 @@ public final class MediaFeed implements Runnable {
5051 mSingleWrapper.setNumExpectedItems(1);
5152 mLoading = true;
5253 }
53-
54+
5455 public void shutdown() {
5556 if (mDataSourceThread != null) {
5657 mDataSource.shutdown();
@@ -83,13 +84,16 @@ public final class MediaFeed implements Runnable {
8384 }
8485
8586 public void setVisibleRange(int begin, int end) {
86- mVisibleRange.begin = begin;
87- mVisibleRange.end = end;
88- int numItems = 96;
89- int numItemsBy2 = numItems / 2;
90- int numItemsBy4 = numItems / 4;
91- mBufferedRange.begin = (begin / numItemsBy2) * numItemsBy2 - numItemsBy4;
92- mBufferedRange.end = mBufferedRange.begin + numItems;
87+ if (begin != mVisibleRange.begin || end != mVisibleRange.end) {
88+ mVisibleRange.begin = begin;
89+ mVisibleRange.end = end;
90+ int numItems = 96;
91+ int numItemsBy2 = numItems / 2;
92+ int numItemsBy4 = numItems / 4;
93+ mBufferedRange.begin = (begin / numItemsBy2) * numItemsBy2 - numItemsBy4;
94+ mBufferedRange.end = mBufferedRange.begin + numItems;
95+ mMediaFeedNeedsToRun = true;
96+ }
9397 }
9498
9599 public void setFilter(MediaFilter filter) {
@@ -98,6 +102,7 @@ public final class MediaFeed implements Runnable {
98102 if (mListener != null) {
99103 mListener.onFeedAboutToChange(this);
100104 }
105+ mMediaFeedNeedsToRun = true;
101106 }
102107
103108 public void removeFilter() {
@@ -107,19 +112,24 @@ public final class MediaFeed implements Runnable {
107112 mListener.onFeedAboutToChange(this);
108113 updateListener(true);
109114 }
115+ mMediaFeedNeedsToRun = true;
110116 }
111117
112118 public ArrayList<MediaSet> getMediaSets() {
113119 return mMediaSets;
114120 }
115121
116- public synchronized MediaSet getMediaSet(final long setId) {
122+ public MediaSet getMediaSet(final long setId) {
117123 if (setId != Shared.INVALID) {
118- int mMediaSetsSize = mMediaSets.size();
119- for (int i = 0; i < mMediaSetsSize; i++) {
120- if (mMediaSets.get(i).mId == setId) {
121- return mMediaSets.get(i);
124+ try {
125+ int mMediaSetsSize = mMediaSets.size();
126+ for (int i = 0; i < mMediaSetsSize; i++) {
127+ if (mMediaSets.get(i).mId == setId) {
128+ return mMediaSets.get(i);
129+ }
122130 }
131+ } catch (Exception e) {
132+ return null;
123133 }
124134 }
125135 return null;
@@ -133,6 +143,10 @@ public final class MediaFeed implements Runnable {
133143 MediaSet mediaSet = new MediaSet(dataSource);
134144 mediaSet.mId = setId;
135145 mMediaSets.add(mediaSet);
146+ if (mDataSourceThread != null && !mDataSourceThread.isAlive()) {
147+ mDataSourceThread.start();
148+ }
149+ mMediaFeedNeedsToRun = true;
136150 return mediaSet;
137151 }
138152
@@ -161,7 +175,7 @@ public final class MediaFeed implements Runnable {
161175 public void addItemToMediaSet(MediaItem item, MediaSet mediaSet) {
162176 item.mParentMediaSet = mediaSet;
163177 mediaSet.addItem(item);
164- synchronized (this) {
178+ synchronized (mClusterSets) {
165179 if (item.mClusteringState == MediaItem.NOT_CLUSTERED) {
166180 MediaClustering clustering = mClusterSets.get(mediaSet);
167181 if (clustering == null) {
@@ -173,6 +187,7 @@ public final class MediaFeed implements Runnable {
173187 item.mClusteringState = MediaItem.CLUSTERED;
174188 }
175189 }
190+ mMediaFeedNeedsToRun = true;
176191 }
177192
178193 public void performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) {
@@ -183,7 +198,6 @@ public final class MediaFeed implements Runnable {
183198 }
184199 if (operation == OPERATION_DELETE && mListener != null) {
185200 mListener.onFeedAboutToChange(this);
186-
187201 }
188202 Thread operationThread = new Thread(new Runnable() {
189203 public void run() {
@@ -214,6 +228,7 @@ public final class MediaFeed implements Runnable {
214228 }
215229 }
216230 updateListener(true);
231+ mMediaFeedNeedsToRun = true;
217232 if (mDataSource != null) {
218233 mDataSource.performOperation(OPERATION_DELETE, mediaBuckets, null);
219234 }
@@ -228,16 +243,18 @@ public final class MediaFeed implements Runnable {
228243
229244 public void removeMediaSet(MediaSet set) {
230245 mMediaSets.remove(set);
246+ mMediaFeedNeedsToRun = true;
231247 }
232248
233249 private void removeItemFromMediaSet(MediaItem item, MediaSet mediaSet) {
234250 mediaSet.removeItem(item);
235- synchronized (this) {
251+ synchronized (mClusterSets) {
236252 MediaClustering clustering = mClusterSets.get(mediaSet);
237253 if (clustering != null) {
238254 clustering.removeItemFromClustering(item);
239255 }
240256 }
257+ mMediaFeedNeedsToRun = true;
241258 }
242259
243260 public void updateListener(boolean needsLayout) {
@@ -311,7 +328,7 @@ public final class MediaFeed implements Runnable {
311328 public boolean getWaitingForMediaScanner() {
312329 return mWaitingForMediaScanner;
313330 }
314-
331+
315332 public boolean isLoading() {
316333 return mLoading;
317334 }
@@ -319,6 +336,8 @@ public final class MediaFeed implements Runnable {
319336 public void start() {
320337 final MediaFeed feed = this;
321338 mLoading = true;
339+ mDataSourceThread = new Thread(this);
340+ mDataSourceThread.setName("MediaFeed");
322341 mAlbumSourceThread = new Thread(new Runnable() {
323342 public void run() {
324343 if (mContext == null)
@@ -326,6 +345,9 @@ public final class MediaFeed implements Runnable {
326345 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
327346 DataSource dataSource = mDataSource;
328347 // We must wait while the SD card is mounted or the MediaScanner is running.
348+ if (dataSource != null) {
349+ dataSource.loadMediaSets(feed);
350+ }
329351 mWaitingForMediaScanner = false;
330352 while (ImageManager.isMediaScannerScanning(mContext.getContentResolver())) {
331353 // MediaScanner is still running, wait
@@ -341,19 +363,16 @@ public final class MediaFeed implements Runnable {
341363 }
342364 if (mWaitingForMediaScanner) {
343365 showToast(mContext.getResources().getString(R.string.loading_new), Toast.LENGTH_LONG);
344- }
345- mWaitingForMediaScanner = false;
346- if (dataSource != null) {
347- dataSource.loadMediaSets(feed);
366+ mWaitingForMediaScanner = false;
367+ if (dataSource != null) {
368+ dataSource.loadMediaSets(feed);
369+ }
348370 }
349371 mLoading = false;
350372 }
351373 });
352374 mAlbumSourceThread.setName("MediaSets");
353375 mAlbumSourceThread.start();
354- mDataSourceThread = new Thread(this);
355- mDataSourceThread.setName("MediaFeed");
356- mDataSourceThread.start();
357376 }
358377
359378 private void showToast(final String string, final int duration) {
@@ -361,7 +380,7 @@ public final class MediaFeed implements Runnable {
361380 }
362381
363382 private void showToast(final String string, final int duration, final boolean centered) {
364- if (mContext != null && !((Gallery)mContext).isPaused()) {
383+ if (mContext != null && !((Gallery) mContext).isPaused()) {
365384 ((Gallery) mContext).getHandler().post(new Runnable() {
366385 public void run() {
367386 if (mContext != null) {
@@ -378,10 +397,10 @@ public final class MediaFeed implements Runnable {
378397
379398 public void run() {
380399 DataSource dataSource = mDataSource;
381- int sleepMs = 100;
400+ int sleepMs = 10;
382401 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
383402 if (dataSource != null) {
384- while (Thread.interrupted() == false) {
403+ while (!Thread.interrupted()) {
385404 if (mListenerNeedsUpdate) {
386405 mListenerNeedsUpdate = false;
387406 if (mListener != null)
@@ -392,13 +411,21 @@ public final class MediaFeed implements Runnable {
392411 return;
393412 }
394413 } else {
414+ if (mWaitingForMediaScanner) {
415+ synchronized (mMediaSets) {
416+ mMediaSets.clear();
417+ }
418+ }
395419 try {
396420 Thread.sleep(sleepMs);
397421 } catch (InterruptedException e) {
398422 return;
399423 }
400424 }
401- sleepMs = 100;
425+ sleepMs = 300;
426+ if (!mMediaFeedNeedsToRun)
427+ continue;
428+ mMediaFeedNeedsToRun = false;
402429 ArrayList<MediaSet> mediaSets = mMediaSets;
403430 synchronized (mediaSets) {
404431 int expandedSetIndex = mExpandedMediaSetIndex;
@@ -415,13 +442,6 @@ public final class MediaFeed implements Runnable {
415442 if (i >= visibleRange.begin && i <= visibleRange.end && scanMediaSets) {
416443 MediaSet set = mediaSets.get(i);
417444 int numItemsLoaded = set.mNumItemsLoaded;
418- if (!set.setContainsValidItems()) {
419- mediaSets.remove(set);
420- if (mListener != null) {
421- mListener.onFeedChanged(this, false);
422- }
423- break;
424- }
425445 if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
426446 dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
427447 if (set.getNumExpectedItems() == 0) {
@@ -434,6 +454,13 @@ public final class MediaFeed implements Runnable {
434454 sleepMs = 100;
435455 scanMediaSets = false;
436456 }
457+ if (!set.setContainsValidItems()) {
458+ mediaSets.remove(set);
459+ if (mListener != null) {
460+ mListener.onFeedChanged(this, false);
461+ }
462+ break;
463+ }
437464 }
438465 }
439466 numSets = mMediaSets.size();
@@ -455,7 +482,7 @@ public final class MediaFeed implements Runnable {
455482 scanMediaSets = false;
456483 }
457484 }
458- } else if (i < bufferedRange.begin || i > bufferedRange.end){
485+ } else if (i < bufferedRange.begin || i > bufferedRange.end) {
459486 // Purge this set to its initial status.
460487 MediaClustering clustering = mClusterSets.get(set);
461488 if (clustering != null) {
@@ -555,6 +582,7 @@ public final class MediaFeed implements Runnable {
555582 // PicasaService.requestSync(mContext, PicasaService.TYPE_ALBUM_PHOTOS, set.mPicasaAlbumId);
556583 }
557584 updateListener(true);
585+ mMediaFeedNeedsToRun = true;
558586 }
559587
560588 public boolean canExpandSet(int slotIndex) {
@@ -583,6 +611,7 @@ public final class MediaFeed implements Runnable {
583611 mListener.onFeedAboutToChange(this);
584612 }
585613 updateListener(true);
614+ mMediaFeedNeedsToRun = true;
586615 }
587616 return retVal;
588617 }
@@ -591,6 +620,7 @@ public final class MediaFeed implements Runnable {
591620 if (mInClusteringMode) {
592621 // Disable clustering.
593622 mInClusteringMode = false;
623+ mMediaFeedNeedsToRun = true;
594624 return true;
595625 }
596626 return false;
@@ -617,7 +647,7 @@ public final class MediaFeed implements Runnable {
617647 }
618648 if (setToUse != null) {
619649 MediaClustering clustering = null;
620- synchronized (this) {
650+ synchronized (mClusterSets) {
621651 // Make sure the computation is completed to the end.
622652 clustering = mClusterSets.get(setToUse);
623653 if (clustering != null) {
@@ -627,6 +657,7 @@ public final class MediaFeed implements Runnable {
627657 }
628658 }
629659 mInClusteringMode = true;
660+ mMediaFeedNeedsToRun = true;
630661 updateListener(true);
631662 }
632663 }
@@ -660,6 +691,7 @@ public final class MediaFeed implements Runnable {
660691 mediaSets.set(i - 1, setEnd);
661692 }
662693 }
694+ mMediaFeedNeedsToRun = true;
663695 }
664696
665697 public MediaSet replaceMediaSet(long setId, DataSource dataSource) {
@@ -675,9 +707,10 @@ public final class MediaFeed implements Runnable {
675707 break;
676708 }
677709 }
710+ mMediaFeedNeedsToRun = true;
678711 return mediaSet;
679712 }
680-
713+
681714 public void setSingleImageMode(boolean singleImageMode) {
682715 mSingleImageMode = singleImageMode;
683716 }
--- a/src/com/cooliris/media/MediaItem.java
+++ b/src/com/cooliris/media/MediaItem.java
@@ -91,11 +91,13 @@ public final class MediaItem {
9191 public boolean isPicassaItem() {
9292 return (mParentMediaSet != null && mParentMediaSet.isPicassaAlbum());
9393 }
94+
95+ private static final String VIDEO = "video/";
9496
9597 public int getMediaType() {
9698 if (mMediaType == -1) {
9799 // Default to image if mMimetype is null or not video.
98- mMediaType = (mMimeType != null && mMimeType.startsWith("video/")) ? MediaItem.MEDIA_TYPE_VIDEO : MediaItem.MEDIA_TYPE_IMAGE;
100+ mMediaType = (mMimeType != null && mMimeType.startsWith(VIDEO)) ? MediaItem.MEDIA_TYPE_VIDEO : MediaItem.MEDIA_TYPE_IMAGE;
99101 }
100102 return mMediaType;
101103 }
--- a/src/com/cooliris/media/MediaItemTexture.java
+++ b/src/com/cooliris/media/MediaItemTexture.java
@@ -24,6 +24,7 @@ public final class MediaItemTexture extends Texture {
2424 private final MediaItem mItem;
2525 private Context mContext;
2626 private boolean mIsRetrying;
27+ private boolean mCached;
2728
2829 public static final class Config {
2930 public int thumbnailWidth;
@@ -34,23 +35,10 @@ public final class MediaItemTexture extends Texture {
3435 mConfig = config;
3536 mContext = context;
3637 mItem = item;
38+ mCached = computeCache();
3739 }
3840
39- @Override
40- public boolean isUncachedVideo() {
41- if (isCached())
42- return false;
43- if (mItem.mParentMediaSet == null || mItem.mMimeType == null)
44- return false;
45- if (mItem.mParentMediaSet.mPicasaAlbumId == Shared.INVALID && mItem.mMimeType.contains("video")) {
46- return true;
47- } else {
48- return false;
49- }
50- }
51-
52- @Override
53- public boolean isCached() {
41+ private boolean computeCache() {
5442 final Config config = mConfig;
5543 final MediaItem item = mItem;
5644 DiskCache cache = null;
@@ -61,7 +49,7 @@ public final class MediaItemTexture extends Texture {
6149 if (item.mMimeType.contains("video")) {
6250 cache = LocalDataSource.sThumbnailCacheVideo;
6351 }
64- }
52+ }
6553 }
6654 if (cache == null) {
6755 return false;
@@ -72,6 +60,24 @@ public final class MediaItemTexture extends Texture {
7260 }
7361 }
7462
63+ @Override
64+ public boolean isUncachedVideo() {
65+ if (isCached())
66+ return false;
67+ if (mItem.mParentMediaSet == null || mItem.mMimeType == null)
68+ return false;
69+ if (mItem.mParentMediaSet.mPicasaAlbumId == Shared.INVALID && mItem.mMimeType.contains("video")) {
70+ return true;
71+ } else {
72+ return false;
73+ }
74+ }
75+
76+ @Override
77+ public boolean isCached() {
78+ return mCached;
79+ }
80+
7581 protected Bitmap load(RenderView view) {
7682
7783 final Config config = mConfig;
@@ -112,11 +118,15 @@ public final class MediaItemTexture extends Texture {
112118 new Thread() {
113119 public void run() {
114120 try {
115- Thread.sleep(5000);
121+ Thread.sleep(5000);
116122 } catch (InterruptedException e) {
117123 ;
118124 }
119- MediaStore.Video.Thumbnails.cancelThumbnailRequest(mContext.getContentResolver(), mItem.mId);
125+ try {
126+ MediaStore.Video.Thumbnails.cancelThumbnailRequest(mContext.getContentResolver(), mItem.mId);
127+ } catch (Exception e) {
128+ ;
129+ }
120130 }
121131 }.start();
122132 retVal = MediaStore.Video.Thumbnails.getThumbnail(mContext.getContentResolver(), mItem.mId,
@@ -169,7 +179,7 @@ public final class MediaItemTexture extends Texture {
169179 item.mThumbnailFocusY = dataInput.readShort();
170180 // Decode the thumbnail.
171181 final BitmapFactory.Options options = new BitmapFactory.Options();
172- options.inDither = true;
182+ options.inDither = false;
173183 options.inScaled = false;
174184 options.inPreferredConfig = Bitmap.Config.RGB_565;
175185 final Bitmap bitmap = BitmapFactory.decodeByteArray(data, CACHE_HEADER_SIZE, data.length - CACHE_HEADER_SIZE,
--- a/src/com/cooliris/media/MenuBar.java
+++ b/src/com/cooliris/media/MenuBar.java
@@ -142,7 +142,6 @@ public final class MenuBar extends Layer implements PopupMenu.Listener {
142142 view.draw2D(icon, menu.x + offset, iconY);
143143 }
144144 float titleY = y + (height - MENU_TITLE_STYLE.height) / 2 + 1;
145- //Log.i("MENUBAR", "Drawing label (" + title.getWidth() + ", " + title.getHeight() + ", " + title.mNormalizedWidth + ")");
146145 view.draw2D(titleTexture, menu.x + offset + iconWidth, titleY);
147146 }
148147 }
--- a/src/com/cooliris/media/PathBarLayer.java
+++ b/src/com/cooliris/media/PathBarLayer.java
@@ -260,4 +260,8 @@ public final class PathBarLayer extends Layer {
260260 public int getNumLevels() {
261261 return mComponents.size();
262262 }
263+
264+ public void clear() {
265+ mComponents.clear();
266+ }
263267 }
--- a/src/com/cooliris/media/PicasaDataSource.java
+++ b/src/com/cooliris/media/PicasaDataSource.java
@@ -189,7 +189,6 @@ public final class PicasaDataSource implements DataSource {
189189 for (int j = 0, numItems = items.size(); j != numItems; ++j) {
190190 MediaItem item = items.get(j);
191191 if (item != null) {
192- Log.i(TAG, "Deleting picasa photo " + item.mContentUri);
193192 String itemUri = PicasaContentProvider.PHOTOS_URI + "/" + item.mId;
194193 client.delete(Uri.parse(itemUri), null, null);
195194 }
--- a/src/com/cooliris/media/RenderView.java
+++ b/src/com/cooliris/media/RenderView.java
@@ -31,7 +31,7 @@ import android.view.SurfaceHolder;
3131 public final class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer, SensorEventListener {
3232 private static final String TAG = "RenderView";
3333 private static final int NUM_TEXTURE_LOAD_THREADS = 4;
34- private static final int MAX_LOADING_COUNT = 8;
34+ private static final int MAX_LOADING_COUNT = 128;
3535
3636 private static final int EVENT_NONE = 0;
3737 // private static final int EVENT_TOUCH = 1;
@@ -46,7 +46,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
4646
4747 private RootLayer mRootLayer = null;
4848 private boolean mListsDirty = false;
49- private final Lists mLists = new Lists();
49+ private static final Lists sLists = new Lists();
5050
5151 private Layer mTouchEventTarget = null;
5252
@@ -56,16 +56,18 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
5656 private volatile boolean mPendingSensorEvent = false;
5757
5858 private int mLoadingCount = 0;
59- private final Deque<Texture> mLoadInputQueue = new Deque<Texture>();
60- private final Deque<Texture> mLoadInputQueueCached = new Deque<Texture>();
61- private final Deque<Texture> mLoadInputQueueVideo = new Deque<Texture>();
62- private final Deque<Texture> mLoadOutputQueue = new Deque<Texture>();
59+ private static final Deque<Texture> sLoadInputQueue = new Deque<Texture>();
60+ private static final Deque<Texture> sLoadInputQueueCached = new Deque<Texture>();
61+ private static final Deque<Texture> sLoadInputQueueVideo = new Deque<Texture>();
62+ private static final Deque<Texture> sLoadOutputQueue = new Deque<Texture>();
63+ private static TextureLoadThread sCachedTextureLoadThread = null;
64+ private static TextureLoadThread sVideoTextureLoadThread = null;
65+ private static final TextureLoadThread[] sTextureLoadThreads = new TextureLoadThread[NUM_TEXTURE_LOAD_THREADS];
66+
6367 private final Deque<MotionEvent> mTouchEventQueue = new Deque<MotionEvent>();
6468 private final DirectLinkedList<TextureReference> mActiveTextureList = new DirectLinkedList<TextureReference>();
69+ @SuppressWarnings("unchecked")
6570 private final ReferenceQueue mUnreferencedTextureQueue = new ReferenceQueue();
66- private final TextureLoadThread[] mTextureLoadThreads = new TextureLoadThread[NUM_TEXTURE_LOAD_THREADS];
67- private TextureLoadThread mCachedTextureLoadThread = null;
68- private TextureLoadThread mVideoTextureLoadThread = null;
6971
7072 // Frame time in milliseconds and delta since last frame in seconds. Uses SystemClock.getUptimeMillis().
7173 private long mFrameTime = 0;
@@ -77,10 +79,14 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
7779 private final SparseArray<ResourceTexture> sCacheUnscaled = new SparseArray<ResourceTexture>();
7880
7981 private boolean mFirstDraw;
82+ // The cached texture that is bound to Texture Unit 0.
83+ // We need to reset this to null whenever the active texture unit changes.
84+ private Texture mBoundTexture;
8085
8186 // Weak reference to a texture that stores the associated texture ID.
8287 private static final class TextureReference extends WeakReference<Texture> {
83- public TextureReference(Texture texture, GL11 gl, ReferenceQueue<Texture> referenceQueue, int textureId) {
88+ @SuppressWarnings("unchecked")
89+ public TextureReference(Texture texture, GL11 gl, ReferenceQueue referenceQueue, int textureId) {
8490 super(texture, referenceQueue);
8591 this.textureId = textureId;
8692 this.gl = gl;
@@ -107,24 +113,25 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
107113 }
108114 }
109115
110- public RenderView(Context context) {
116+ public RenderView(final Context context) {
111117 super(context);
112118 setBackgroundDrawable(null);
113119 setFocusable(true);
114120 setEGLConfigChooser(true);
115121 setRenderer(this);
116122 mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
117-
118- for (int i = 0; i != NUM_TEXTURE_LOAD_THREADS; ++i) {
119- TextureLoadThread thread = new TextureLoadThread();
120- if (i == 0) {
121- mCachedTextureLoadThread = thread;
122- }
123- if (i == 1) {
124- mVideoTextureLoadThread = thread;
123+ if (sCachedTextureLoadThread == null) {
124+ for (int i = 0; i != NUM_TEXTURE_LOAD_THREADS; ++i) {
125+ TextureLoadThread thread = new TextureLoadThread();
126+ if (i == 0) {
127+ sCachedTextureLoadThread = thread;
128+ }
129+ if (i == 1) {
130+ sVideoTextureLoadThread = thread;
131+ }
132+ sTextureLoadThreads[i] = thread;
133+ thread.start();
125134 }
126- mTextureLoadThreads[i] = thread;
127- thread.start();
128135 }
129136 }
130137
@@ -241,11 +248,13 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
241248
242249 public boolean bind(Texture texture) {
243250 if (texture != null) {
251+ if (texture == mBoundTexture)
252+ return true;
244253 switch (texture.mState) {
245254 case Texture.STATE_UNLOADED:
246255 if (texture.getClass().equals(ResourceTexture.class)) {
247256 loadTexture(texture);
248- return true;
257+ return false;
249258 }
250259 if (mLoadingCount < MAX_LOADING_COUNT) {
251260 queueLoad(texture, false);
@@ -253,6 +262,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
253262 break;
254263 case Texture.STATE_LOADED:
255264 mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.mId);
265+ mBoundTexture = texture;
256266 return true;
257267 default:
258268 break;
@@ -308,8 +318,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
308318 texture.mState = Texture.STATE_LOADING;
309319
310320 // Push the texture onto the load input queue.
311- Deque<Texture> inputQueue = (texture.isCached()) ? mLoadInputQueueCached : mLoadInputQueue;
312- inputQueue = (texture.isUncachedVideo()) ? mLoadInputQueueVideo : mLoadInputQueue;
321+ Deque<Texture> inputQueue = (texture.isUncachedVideo()) ? sLoadInputQueueVideo : (texture.isCached()) ? sLoadInputQueueCached : sLoadInputQueue;;
313322 synchronized (inputQueue) {
314323 if (highPriority) {
315324 inputQueue.addFirst(texture);
@@ -351,6 +360,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
351360 boolean bind = true;
352361 bind &= bind(from);
353362 gl.glActiveTexture(GL11.GL_TEXTURE1);
363+ mBoundTexture = null;
354364 bind &= bind(to);
355365 if (!bind) {
356366 return false;
@@ -385,6 +395,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
385395
386396 // Switch back to the default texture unit.
387397 gl.glActiveTexture(GL11.GL_TEXTURE0);
398+ mBoundTexture = null;
388399 }
389400
390401 public void drawMixed2D(Texture from, Texture to, float ratio, float x, float y, float z, float width, float height) {
@@ -393,6 +404,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
393404 // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
394405 if (bind(from)) {
395406 gl.glActiveTexture(GL11.GL_TEXTURE1);
407+ mBoundTexture = null;
396408 if (bind(to)) {
397409 // Enable TEXTURE1.
398410 gl.glEnable(GL11.GL_TEXTURE_2D);
@@ -423,6 +435,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
423435
424436 // Switch back to the default texture unit.
425437 gl.glActiveTexture(GL11.GL_TEXTURE0);
438+ mBoundTexture = null;
426439 }
427440 }
428441
@@ -445,7 +458,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
445458 }
446459 mActiveTextureList.remove(textureReference.activeListEntry);
447460 }
448- Deque<Texture> outputQueue = mLoadOutputQueue;
461+ Deque<Texture> outputQueue = sLoadOutputQueue;
449462 Texture texture;
450463 do {
451464 // Upload loaded textures to the GPU one frame at a time.
@@ -467,6 +480,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
467480 private void uploadTexture(Texture texture, int[] textureId) {
468481 Bitmap bitmap = texture.mBitmap;
469482 GL11 gl = mGL;
483+ int glError = GL11.GL_NO_ERROR;
470484 if (bitmap != null) {
471485 final int width = texture.mWidth;
472486 final int height = texture.mHeight;
@@ -474,41 +488,38 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
474488 // Define a vertically flipped crop rectangle for OES_draw_texture.
475489 int[] cropRect = { 0, height, width, -height };
476490
477- // Handle texture upload failures
478- int numTextureFails = 0;
479- boolean textureFail = false;
480- do {
481- textureFail = false;
482- // Upload the bitmap to a new texture.
483- gl.glGenTextures(1, textureId, 0);
484- gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
485- gl.glTexParameteriv(GL11.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
486- gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
487- gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
488- gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
489- gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
490- GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
491- int glError = gl.glGetError();
492- if (glError == GL11.GL_OUT_OF_MEMORY) {
493- Log.i(TAG, "Texture creation fail, glError " + glError + " retry id " + numTextureFails);
494- ++numTextureFails;
495- textureFail = true;
496- handleLowMemory();
497- // TODO: Retry logic
498- numTextureFails = 3;
499- }
500- } while (textureFail && numTextureFails < 3);
491+ // Upload the bitmap to a new texture.
492+ gl.glGenTextures(1, textureId, 0);
493+ gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
494+ gl.glTexParameteriv(GL11.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
495+ gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
496+ gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
497+ gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
498+ gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
499+ GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
500+ glError = gl.glGetError();
501+
501502 bitmap.recycle();
502-
503- // Update texture state.
504- texture.mBitmap = null;
505- texture.mId = textureId[0];
506- texture.mState = Texture.STATE_LOADED;
507-
508- // Add to the active list.
509- final TextureReference textureRef = new TextureReference(texture, gl, mUnreferencedTextureQueue, textureId[0]);
510- mActiveTextureList.add(textureRef.activeListEntry);
511- requestRender();
503+ if (glError == GL11.GL_OUT_OF_MEMORY) {
504+ handleLowMemory();
505+ }
506+ if (glError != GL11.GL_NO_ERROR) {
507+ // There was an error, we need to retry this texture at some later time
508+ Log.i(TAG, "Texture creation fail, glError " + glError);
509+ texture.mId = 0;
510+ texture.mBitmap = null;
511+ texture.mState = Texture.STATE_UNLOADED;
512+ } else {
513+ // Update texture state.
514+ texture.mBitmap = null;
515+ texture.mId = textureId[0];
516+ texture.mState = Texture.STATE_LOADED;
517+
518+ // Add to the active list.
519+ final TextureReference textureRef = new TextureReference(texture, gl, mUnreferencedTextureQueue, textureId[0]);
520+ mActiveTextureList.add(textureRef.activeListEntry);
521+ requestRender();
522+ }
512523 } else {
513524 texture.mState = Texture.STATE_ERROR;
514525 }
@@ -551,9 +562,9 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
551562
552563 boolean wasLoadingExpensiveTextures = isLoadingExpensiveTextures();
553564 boolean loadingExpensiveTextures = false;
554- int numTextureThreads = mTextureLoadThreads.length;
565+ int numTextureThreads = sTextureLoadThreads.length;
555566 for (int i = 2; i < numTextureThreads; ++i) {
556- if (mTextureLoadThreads[i].mIsLoading) {
567+ if (sTextureLoadThreads[i].mIsLoading) {
557568 loadingExpensiveTextures = true;
558569 break;
559570 }
@@ -566,7 +577,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
566577 processTextures(false);
567578
568579 // Update the current time and frame time interval.
569- final long now = SystemClock.uptimeMillis();
580+ long now = SystemClock.uptimeMillis();
570581 final float dt = 0.001f * Math.min(50, now - mFrameTime);
571582 mFrameInterval = dt;
572583 mFrameTime = now;
@@ -575,7 +586,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
575586 processCurrentEvent();
576587 processTouchEvent();
577588 // Run the update pass.
578- final Lists lists = mLists;
589+ final Lists lists = sLists;
579590 synchronized (lists) {
580591 final ArrayList<Layer> updateList = lists.updateList;
581592 boolean isDirty = false;
@@ -586,7 +597,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
586597 if (isDirty) {
587598 requestRender();
588599 }
589-
600+
590601 // Clear the depth buffer.
591602 gl.glClear(GL11.GL_DEPTH_BUFFER_BIT);
592603 gl.glEnable(GL11.GL_SCISSOR_TEST);
@@ -601,7 +612,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
601612 layer.renderOpaque(this, gl);
602613 }
603614 }
604-
615+
605616 // Run the blended pass.
606617 gl.glEnable(GL11.GL_BLEND);
607618 final ArrayList<Layer> blendedList = lists.blendedList;
@@ -611,10 +622,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
611622 layer.renderBlended(this, gl);
612623 }
613624 }
614- }
615- int priority = Process.getThreadPriority(Process.myTid());
616- if (priority != Process.THREAD_PRIORITY_URGENT_DISPLAY) {
617- Log.e(TAG, "The display thread should never be lowered to priority level " + priority);
625+ gl.glDisable(GL11.GL_BLEND);
618626 }
619627 }
620628
@@ -638,6 +646,8 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
638646
639647 private void processTouchEvent() {
640648 MotionEvent event = null;
649+ int numEvents = mTouchEventQueue.size();
650+ int i = 0;
641651 do {
642652 // We look at the touch event queue and process one event at a time
643653 synchronized (mTouchEventQueue) {
@@ -666,7 +676,8 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
666676 mTouchEventTarget = null;
667677 }
668678 event.recycle();
669- } while (event != null);
679+ ++i;
680+ } while (event != null && i < numEvents);
670681 synchronized (this) {
671682 this.notify();
672683 }
@@ -697,7 +708,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
697708 }
698709
699710 private Layer hitTest(float x, float y) {
700- final ArrayList<Layer> hitTestList = mLists.hitTestList;
711+ final ArrayList<Layer> hitTestList = sLists.hitTestList;
701712 for (int i = hitTestList.size() - 1; i >= 0; --i) {
702713 final Layer layer = hitTestList.get(i);
703714 if (layer != null && !layer.mHidden) {
@@ -714,9 +725,9 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
714725
715726 private void updateLists() {
716727 if (mRootLayer != null) {
717- synchronized (mLists) {
718- mLists.clear();
719- mRootLayer.generate(this, mLists);
728+ synchronized (sLists) {
729+ sLists.clear();
730+ mRootLayer.generate(this, sLists);
720731 }
721732 }
722733 }
@@ -759,7 +770,6 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
759770 // Clear the resource texture cache.
760771 clearCache();
761772
762- // Log.i(TAG, "Surface Created for " + this);
763773 GL11 gl = (GL11) gl1;
764774 if (mGL == null) {
765775 mGL = gl;
@@ -770,14 +780,14 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
770780 }
771781 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
772782 // Increase the priority of the render thread.
773- Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
783+ Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
774784
775785 // Disable unused state.
776786 gl.glEnable(GL11.GL_DITHER);
777787 gl.glDisable(GL11.GL_LIGHTING);
778788
779789 // Set global state.
780- gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
790+ // gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
781791
782792 // Enable textures.
783793 gl.glEnable(GL11.GL_TEXTURE_2D);
@@ -821,8 +831,8 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
821831 if (mRootLayer != null) {
822832 mRootLayer.onSurfaceCreated(this, gl);
823833 }
824- synchronized (mLists) {
825- ArrayList<Layer> systemList = mLists.systemList;
834+ synchronized (sLists) {
835+ ArrayList<Layer> systemList = sLists.systemList;
826836 for (int i = systemList.size() - 1; i >= 0; --i) {
827837 systemList.get(i).onSurfaceCreated(this, gl);
828838 }
@@ -915,19 +925,16 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
915925 @Override
916926 public void surfaceDestroyed(SurfaceHolder holder) {
917927 super.surfaceDestroyed(holder);
918- // Log.i(TAG, "Surface destroyed for " + this);
919928 }
920929
921930 @Override
922931 protected void onAttachedToWindow() {
923932 super.onAttachedToWindow();
924- // Log.i(TAG, "Attaching to window for " + this);
925933 }
926934
927935 @Override
928936 protected void onDetachedFromWindow() {
929937 super.onDetachedFromWindow();
930- // Log.i(TAG, "Detaching from Window for " + this);
931938 }
932939
933940 private final class TextureLoadThread extends Thread {
@@ -935,14 +942,12 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
935942
936943 public TextureLoadThread() {
937944 super("TextureLoad");
938- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
939945 }
940946
941947 public void run() {
942- RenderView view = RenderView.this;
943- Deque<Texture> inputQueue = (mCachedTextureLoadThread == this) ? view.mLoadInputQueueCached : view.mLoadInputQueue;
944- inputQueue = (mVideoTextureLoadThread == this) ? view.mLoadInputQueueVideo : view.mLoadInputQueue;
945- Deque<Texture> outputQueue = view.mLoadOutputQueue;
948+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
949+ Deque<Texture> inputQueue = (sVideoTextureLoadThread == this) ? sLoadInputQueueVideo : ((sCachedTextureLoadThread == this) ? sLoadInputQueueCached : sLoadInputQueue);
950+ Deque<Texture> outputQueue = sLoadOutputQueue;
946951 try {
947952 for (;;) {
948953 // Pop the next texture from the input queue.
@@ -952,7 +957,7 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
952957 inputQueue.wait();
953958 }
954959 }
955- if (mCachedTextureLoadThread != this)
960+ if (sCachedTextureLoadThread != this)
956961 mIsLoading = true;
957962 // Load the texture bitmap.
958963 load(texture);
@@ -977,14 +982,9 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
977982 }
978983
979984 public void shutdown() {
980- // stop all the threads
981- for (int i = 0; i < NUM_TEXTURE_LOAD_THREADS; ++i) {
982- mTextureLoadThreads[i].interrupt();
983- }
984- ArrayUtils.clear(mTextureLoadThreads);
985985 mRootLayer = null;
986- synchronized (mLists) {
987- mLists.clear();
986+ synchronized (sLists) {
987+ sLists.clear();
988988 }
989989 }
990990
@@ -996,6 +996,6 @@ public final class RenderView extends GLSurfaceView implements GLSurfaceView.Ren
996996 }
997997
998998 public Lists getLists() {
999- return mLists;
999+ return sLists;
10001000 }
10011001 }
--- a/src/com/cooliris/media/ReverseGeocoder.java
+++ b/src/com/cooliris/media/ReverseGeocoder.java
@@ -20,25 +20,21 @@ public final class ReverseGeocoder extends Thread {
2020 // If two points are within 50 miles of each other, use "Around Palo Alto, CA" or "Around Mountain View, CA".
2121 // instead of directly jumping to the next level and saying "California, US".
2222 private static final int MAX_LOCALITY_MILE_RANGE = 50;
23-
23+ private static final Deque<MediaSet> sQueue = new Deque<MediaSet>();
24+ private static final DiskCache sGeoCache = new DiskCache("geocoder-cache");
2425 private static final String TAG = "ReverseGeocoder";
2526
26- private final Geocoder mGeocoder;
27+ private Geocoder mGeocoder;
2728 private final Context mContext;
28- private final Deque<MediaSet> mQueue = new Deque<MediaSet>();
29- private final DiskCache mGeoCache = new DiskCache("geocoder-cache");
3029
3130 public ReverseGeocoder(Context context) {
3231 super(TAG);
3332 mContext = context;
34- mGeocoder = new Geocoder(mContext);
35- // Loading the addresses in the GeoCache.
36- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
3733 start();
3834 }
3935
4036 public void enqueue(MediaSet set) {
41- Deque<MediaSet> inQueue = mQueue;
37+ Deque<MediaSet> inQueue = sQueue;
4238 synchronized (inQueue) {
4339 inQueue.addLast(set);
4440 inQueue.notify();
@@ -47,7 +43,10 @@ public final class ReverseGeocoder extends Thread {
4743
4844 @Override
4945 public void run() {
50- Deque<MediaSet> queue = mQueue;
46+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
47+ Deque<MediaSet> queue = sQueue;
48+ mGeocoder = new Geocoder(mContext);
49+ queue.clear();
5150 try {
5251 for (;;) {
5352 // Wait for the next request.
@@ -66,12 +65,11 @@ public final class ReverseGeocoder extends Thread {
6665 }
6766
6867 public void flushCache() {
69- mGeoCache.flush();
68+ sGeoCache.flush();
7069 }
7170
7271 public void shutdown() {
7372 flushCache();
74- mGeoCache.close();
7573 this.interrupt();
7674 }
7775
@@ -134,8 +132,8 @@ public final class ReverseGeocoder extends Thread {
134132 }
135133
136134 // Just choose one of the localities if within a 50 mile radius.
137- int distance = (int) LocationMediaFilter.toMile(
138- LocationMediaFilter.distanceBetween(setMinLatitude, setMinLongitude, setMaxLatitude, setMaxLongitude));
135+ int distance = (int) LocationMediaFilter.toMile(LocationMediaFilter.distanceBetween(setMinLatitude, setMinLongitude,
136+ setMaxLatitude, setMaxLongitude));
139137 if (distance < MAX_LOCALITY_MILE_RANGE) {
140138 // Try each of the points and just return the first one to have a valid address.
141139 Address minLatAddress = lookupAddress(setMinLatitude, set.mMinLatLongitude);
@@ -207,11 +205,11 @@ public final class ReverseGeocoder extends Thread {
207205 }
208206 }
209207 }
210-
208+
211209 if (numDetails == desiredNumDetails) {
212210 return location;
213211 }
214-
212+
215213 String locality = addr.getLocality();
216214 if (locality != null && !("null".equals(locality))) {
217215 if (location != null && location.length() > 0) {
@@ -221,11 +219,11 @@ public final class ReverseGeocoder extends Thread {
221219 }
222220 numDetails++;
223221 }
224-
222+
225223 if (numDetails == desiredNumDetails) {
226224 return location;
227225 }
228-
226+
229227 String adminArea = addr.getAdminArea();
230228 if (adminArea != null && !("null".equals(adminArea))) {
231229 if (location != null && location.length() > 0) {
@@ -274,7 +272,7 @@ public final class ReverseGeocoder extends Thread {
274272 private Address lookupAddress(final double latitude, final double longitude) {
275273 try {
276274 long locationKey = (long) (((latitude + LocationMediaFilter.LAT_MAX) * 2 * LocationMediaFilter.LAT_MAX + (longitude + LocationMediaFilter.LON_MAX)) * LocationMediaFilter.EARTH_RADIUS_METERS);
277- byte[] cachedLocation = mGeoCache.get(locationKey, 0);
275+ byte[] cachedLocation = sGeoCache.get(locationKey, 0);
278276 Address address = null;
279277 if (cachedLocation == null || cachedLocation.length == 0) {
280278 List<Address> addresses = mGeocoder.getFromLocation(latitude, longitude, 1);
@@ -305,7 +303,7 @@ public final class ReverseGeocoder extends Thread {
305303 Utils.writeUTF(dos, address.getUrl());
306304
307305 dos.flush();
308- mGeoCache.put(locationKey, bos.toByteArray());
306+ sGeoCache.put(locationKey, bos.toByteArray());
309307 dos.close();
310308 }
311309 } else {
@@ -325,7 +323,7 @@ public final class ReverseGeocoder extends Thread {
325323 }
326324 }
327325 if (!locale.getLanguage().equals(Locale.getDefault().getLanguage())) {
328- mGeoCache.delete(locationKey);
326+ sGeoCache.delete(locationKey);
329327 dis.close();
330328 return lookupAddress(latitude, longitude);
331329 }
--- a/src/com/cooliris/media/TimeBar.java
+++ b/src/com/cooliris/media/TimeBar.java
@@ -25,7 +25,6 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
2525 private static final int MARKER_SPACING_PIXELS = 50;
2626 private static final float AUTO_SCROLL_MARGIN = 100f;
2727 private static final Paint SRC_PAINT = new Paint();
28- private final Context mContext;
2928 private Listener mListener = null;
3029 private MediaFeed mFeed = null;
3130 private float mTotalWidth = 0f;
@@ -44,7 +43,7 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
4443 private final StringTexture.Config mMonthYearFormat = new StringTexture.Config();
4544 private final StringTexture.Config mDayFormat = new StringTexture.Config();
4645 private final SparseArray<StringTexture> mYearLabels = new SparseArray<StringTexture>();
47- private final StringTexture mDateUnknown;
46+ private StringTexture mDateUnknown;
4847 private final StringTexture[] mMonthLabels = new StringTexture[12];
4948 private final StringTexture[] mDayLabels = new StringTexture[32];
5049 private final StringTexture[] mOpaqueDayLabels = new StringTexture[32];
@@ -63,18 +62,22 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
6362 }
6463
6564 TimeBar(Context context) {
66- // Save the context.
67- mContext = context;
68-
6965 // Setup formatting for text labels.
7066 mMonthYearFormat.fontSize = 17f * Gallery.PIXEL_DENSITY;
7167 mMonthYearFormat.bold = true;
7268 mMonthYearFormat.a = 0.85f;
7369 mDayFormat.fontSize = 17f * Gallery.PIXEL_DENSITY;
7470 mDayFormat.a = 0.61f;
75-
71+ regenerateStringsForContext(context);
72+ Bitmap background = BitmapFactory.decodeResource(context.getResources(), R.drawable.popup);
73+ mBackground = new NinePatch(background, background.getNinePatchChunk(), null);
74+ mBackgroundRect = new Rect();
75+ SRC_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
76+ }
77+
78+ public void regenerateStringsForContext(Context context) {
7679 // Create textures for month names.
77- String[] months = mContext.getResources().getStringArray(R.array.months_abbreviated);
80+ String[] months = context.getResources().getStringArray(R.array.months_abbreviated);
7881 for (int i = 0; i < months.length; ++i) {
7982 mMonthLabels[i] = new StringTexture(months[i], mMonthYearFormat);
8083 }
@@ -84,10 +87,6 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
8487 mOpaqueDayLabels[i] = new StringTexture(Integer.toString(i), mMonthYearFormat);
8588 }
8689 mDateUnknown = new StringTexture(context.getResources().getString(R.string.date_unknown), mMonthYearFormat);
87- Bitmap background = BitmapFactory.decodeResource(context.getResources(), R.drawable.popup);
88- mBackground = new NinePatch(background, background.getNinePatchChunk(), null);
89- mBackgroundRect = new Rect();
90- SRC_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
9190 }
9291
9392 public void setListener(Listener listener) {
@@ -109,30 +108,6 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
109108 mScroll = getScrollForPosition(mPosition);
110109 }
111110
112- // public long getTime() {
113- // final float x = mPosition * mTotalWidth;
114- // for (int i = 0, size = mMarkers.size(); i < size; ++i) {
115- // Marker marker = mMarkers.get(i);
116- // if (x <= marker.x) {
117- // return marker.time;
118- // }
119- // }
120- // return 0;
121- // }
122- //
123- // public void setTime(long time) {
124- // synchronized (mMarkers) {
125- // for (int i = 0, size = mMarkers.size(); i < size; ++i) {
126- // Marker marker = mMarkers.get(i);
127- // if (time <= marker.time) {
128- // mPosition = Math.max(0.0f, Math.min(1.0f, marker.x / mTotalWidth));
129- // mScroll = getScrollForPosition(mPosition);
130- // break;
131- // }
132- // }
133- // }
134- // }
135-
136111 public MediaItem getItem() {
137112 synchronized (mMarkers) {
138113 // x is between 0 and 1.0f
@@ -257,18 +232,18 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
257232 if (month != lastMonth) {
258233 lastMonth = month;
259234 lastDayBlock = -1;
260- marker = new Marker(mMonthLabels[month], dx, time.getTimeInMillis(), year, month, dayBlock,
235+ marker = new Marker(dx, time.getTimeInMillis(), year, month, dayBlock,
261236 Marker.TYPE_MONTH, increment);
262237 dx = addMarker(marker);
263238 } else if (dayBlock != lastDayBlock) {
264239 lastDayBlock = dayBlock;
265240 if (dayBlock != 0) {
266- marker = new Marker(mDayLabels[dayBlock], dx, time.getTimeInMillis(), year, month, dayBlock,
241+ marker = new Marker(dx, time.getTimeInMillis(), year, month, dayBlock,
267242 Marker.TYPE_DAY, increment);
268243 dx = addMarker(marker);
269244 }
270245 } else {
271- marker = new Marker(mDot, dx, time.getTimeInMillis(), year, month, dayBlock, Marker.TYPE_DOT, increment);
246+ marker = new Marker(dx, time.getTimeInMillis(), year, month, dayBlock, Marker.TYPE_DOT, increment);
272247 dx = addMarker(marker);
273248 }
274249 for (int k = 0; k < increment; ++k) {
@@ -496,7 +471,7 @@ public final class TimeBar extends Layer implements MediaFeed.Listener {
496471 }
497472
498473 private static final class Marker {
499- Marker(StringTexture texture, float x, long time, int year, int month, int day, int type, int expectedCapacity) {
474+ Marker(float x, long time, int year, int month, int day, int type, int expectedCapacity) {
500475 this.x = x;
501476 this.year = year;
502477 this.month = month;
--- a/src/com/cooliris/media/UriTexture.java
+++ b/src/com/cooliris/media/UriTexture.java
@@ -85,7 +85,7 @@ public class UriTexture extends Texture {
8585 final BitmapFactory.Options options = new BitmapFactory.Options();
8686 options.inScaled = false;
8787 options.inPreferredConfig = Bitmap.Config.RGB_565;
88- options.inDither = true;
88+ options.inDither = false;
8989 long crc64 = 0;
9090 Bitmap bitmap = null;
9191 if (uri.startsWith(ContentResolver.SCHEME_CONTENT)) {
@@ -121,7 +121,7 @@ public class UriTexture extends Texture {
121121 int ratio = Math.max(ratioX, ratioY);
122122 ratio = Shared.nextPowerOf2(ratio);
123123 sampleSize = ratio;
124- options.inDither = true;
124+ options.inDither = false;
125125 options.inJustDecodeBounds = false;
126126 options.inSampleSize = ratio;
127127 Thread timeoutThread = new Thread("BitmapTimeoutThread") {
@@ -207,7 +207,7 @@ public class UriTexture extends Texture {
207207 final BitmapFactory.Options options = new BitmapFactory.Options();
208208 options.inScaled = false;
209209 options.inPreferredConfig = Bitmap.Config.RGB_565;
210- options.inDither = true;
210+ options.inDither = false;
211211 if (crc64 != 0) {
212212 file = createFilePathFromCrc64(crc64, maxResolution);
213213 try {
@@ -227,7 +227,7 @@ public class UriTexture extends Texture {
227227 final BitmapFactory.Options options = new BitmapFactory.Options();
228228 options.inScaled = false;
229229 options.inPreferredConfig = Bitmap.Config.RGB_565;
230- options.inDither = true;
230+ options.inDither = false;
231231 if (crc64 != 0) {
232232 file = createFilePathFromCrc64(crc64, maxResolution);
233233 bitmap = BitmapFactory.decodeFile(file, options);
--- a/src/com/cooliris/picasa/PicasaApi.java
+++ b/src/com/cooliris/picasa/PicasaApi.java
@@ -15,7 +15,6 @@ import android.content.Context;
1515 import android.content.SyncResult;
1616 import android.net.Uri;
1717 import android.os.Bundle;
18-import android.util.DisplayMetrics;
1918 import android.util.Log;
2019 import android.util.Xml;
2120
@@ -30,11 +29,9 @@ public final class PicasaApi {
3029
3130 static {
3231 // Build the base query string using screen dimensions.
33- DisplayMetrics metrics = new DisplayMetrics();
34- int maxDimension = Math.max(metrics.widthPixels, metrics.heightPixels);
35- StringBuilder query = new StringBuilder("?imgmax=1024&max-results=1000&thumbsize=");
36- String thumbnailSize = metrics.density <= 1 ? "144u," : "144u,";
37- String screennailSize = maxDimension <= 512 ? "512u" : "800u";
32+ final StringBuilder query = new StringBuilder("?imgmax=1024&max-results=1000&thumbsize=");
33+ final String thumbnailSize = "144u,";
34+ final String screennailSize = "1024u";
3835 query.append(thumbnailSize);
3936 query.append(screennailSize);
4037 BASE_QUERY_STRING = query.toString() + "&visibility=visible";
--- /dev/null
+++ b/src/com/cooliris/wallpaper/RandomDataSource.java
@@ -0,0 +1,43 @@
1+package com.cooliris.wallpaper;
2+
3+import java.io.IOException;
4+import java.net.URISyntaxException;
5+
6+import com.cooliris.cache.CacheService;
7+import com.cooliris.cache.ImageList;
8+import com.cooliris.media.UriTexture;
9+import com.cooliris.media.Util;
10+
11+import android.content.Context;
12+import android.graphics.Bitmap;
13+
14+public class RandomDataSource implements Slideshow.DataSource {
15+
16+ public Bitmap getBitmapForIndex(Context context, int currentSlideshowCounter) {
17+ ImageList list = CacheService.getImageList(context);
18+ // Once we have the id and the thumbid, we can return a bitmap
19+ // First we select a random numbers
20+ if (list.ids == null)
21+ return null;
22+ double random = Math.random();
23+ random *= list.ids.length;
24+ int index = (int) random;
25+ long cacheId = list.thumbids[index];
26+ final String uri = CacheService.BASE_CONTENT_STRING_IMAGES + list.ids[index];
27+ Bitmap retVal = null;
28+ try {
29+ retVal = UriTexture.createFromUri(context, uri, UriTexture.MAX_RESOLUTION, UriTexture.MAX_RESOLUTION, cacheId, null);
30+ if (retVal != null) {
31+ retVal = Util.rotate(retVal, list.orientation[index]);
32+ }
33+ } catch (OutOfMemoryError e) {
34+ ;
35+ } catch (IOException e) {
36+ ;
37+ } catch (URISyntaxException e) {
38+ ;
39+ }
40+ return retVal;
41+ }
42+
43+}
--- /dev/null
+++ b/src/com/cooliris/wallpaper/Slideshow.java
@@ -0,0 +1,225 @@
1+package com.cooliris.wallpaper;
2+
3+//import android.app.Service;
4+import com.cooliris.media.Vector3f;
5+
6+import android.content.Context;
7+import android.graphics.Bitmap;
8+import android.graphics.Canvas;
9+import android.graphics.Color;
10+import android.graphics.Paint;
11+import android.graphics.PorterDuffColorFilter;
12+import android.graphics.Rect;
13+import android.graphics.RectF;
14+import android.os.Handler;
15+import android.os.SystemClock;
16+import android.view.SurfaceHolder;
17+import android.view.SurfaceView;
18+import android.graphics.PorterDuff.Mode;
19+
20+public class Slideshow extends SurfaceView implements SurfaceHolder.Callback {
21+ public Slideshow(Context context) {
22+ super(context);
23+ SurfaceHolder holder = getHolder();
24+ holder.addCallback(this);
25+ }
26+
27+ public static final int SLIDESHOW_DURATION = 5000;
28+
29+ public interface DataSource {
30+ /**
31+ *
32+ * @param currentSlideshowCounter
33+ * @return
34+ */
35+ Bitmap getBitmapForIndex(Context context, int currentSlideshowCounter);
36+ }
37+
38+ private final Handler mHandler = new Handler();
39+ private final Runnable mDrawFrame = new Runnable() {
40+ public void run() {
41+ drawFrame();
42+ }
43+ };
44+ private static final Paint sPaint = new Paint();
45+ static {
46+ sPaint.setFilterBitmap(true);
47+ sPaint.setDither(true);
48+ }
49+ private boolean mVisible = true;
50+ private DataSource mSource;
51+ private int mCurrentSlideshowCounter;
52+ private Bitmap mBitmap;
53+ private Rect mRect;
54+ private RectF mFrameRect;
55+ private static final Vector3f sGrow = new Vector3f();
56+ private Bitmap mQueuedBitmap;
57+ private Rect mQueuedRect;
58+ private RectF mQueuedFrameRect;
59+ private static final Vector3f sQueuedGrow = new Vector3f();
60+
61+ private long mPrevTime;
62+ private long mTimeElapsed;
63+
64+ public void setDataSource(DataSource source) {
65+ mSource = source;
66+ }
67+
68+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
69+ mHandler.post(mDrawFrame);
70+ if (mBitmap != null) {
71+ mRect = getRectToFitBitmap(mBitmap.getWidth(), mBitmap.getHeight(), width, height);
72+ mFrameRect.right = width;
73+ mFrameRect.bottom = height;
74+ }
75+ if (mQueuedBitmap != null) {
76+ mQueuedRect = getRectToFitBitmap(mQueuedBitmap.getWidth(), mQueuedBitmap.getHeight(), width, height);
77+ mQueuedFrameRect.right = width;
78+ mQueuedFrameRect.bottom = height;
79+ }
80+ }
81+
82+ public void surfaceCreated(SurfaceHolder holder) {
83+ // We may need to make calls to super once this is a subclass of WallpaperService.
84+ mHandler.post(mDrawFrame);
85+ }
86+
87+ public void surfaceDestroyed(SurfaceHolder holder) {
88+
89+ }
90+
91+ public void drawFrame() {
92+ final SurfaceHolder holder = getHolder();
93+ Rect frame = holder.getSurfaceFrame();
94+ final Paint paint = sPaint;
95+ Canvas c = null;
96+ try {
97+ c = holder.lockCanvas();
98+ if (c != null) {
99+ long now = SystemClock.uptimeMillis();
100+ long delta = now - mPrevTime;
101+ if (delta > 50)
102+ delta = 50;
103+ mTimeElapsed += delta;
104+ mPrevTime = now;
105+ performSetup(frame.width(), frame.height());
106+ // We draw the source bitmap
107+ if (mBitmap != null) {
108+ if (mTimeElapsed > SLIDESHOW_DURATION) {
109+ float alpha = ((float)(mTimeElapsed - SLIDESHOW_DURATION)) / 2000.0f;
110+ paint.setColorFilter(null);
111+ if (alpha < 1.0f) {
112+ //int val = (int)(255 * (1.0f - alpha));
113+ //int srcColor = Color.argb(val, 0, 0, 0);
114+ //PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(srcColor, Mode.SRC_IN);
115+ //paint.setColorFilter(null);
116+ }
117+ c.drawBitmap(mBitmap, mRect, mFrameRect, paint);
118+ if (alpha < 1.0f) {
119+ int val = (int)(255 * alpha);
120+ int srcColor = Color.argb(val, 0, 0, 0);
121+ PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(srcColor, Mode.DST_IN);
122+ paint.setColorFilter(colorFilter);
123+ }
124+
125+ c.drawBitmap(mQueuedBitmap, mQueuedRect, mQueuedFrameRect, paint);
126+ performUpdate(mQueuedFrameRect, sQueuedGrow, delta);
127+ if (alpha >= 1.0f) {
128+ // We switch the image.
129+ mBitmap.recycle();
130+ mRect = mQueuedRect;
131+ mBitmap = mQueuedBitmap;
132+ mFrameRect = mQueuedFrameRect;
133+ sGrow.set(sQueuedGrow);
134+ mQueuedBitmap = null;
135+ mQueuedRect = null;
136+ mQueuedFrameRect = null;
137+ mTimeElapsed = 0;
138+ }
139+ } else {
140+ paint.setColorFilter(null);
141+ c.drawBitmap(mBitmap, mRect, mFrameRect, paint);
142+ }
143+ performUpdate(mFrameRect, sGrow, delta);
144+ }
145+
146+ }
147+ } finally {
148+ if (c != null)
149+ holder.unlockCanvasAndPost(c);
150+ }
151+ mHandler.removeCallbacks(mDrawFrame);
152+ if (mVisible) {
153+ mHandler.postDelayed(mDrawFrame, 20);
154+ }
155+ }
156+
157+ private void performUpdate(RectF rect, Vector3f grow, long delta) {
158+ float timeElapsed = ((float)(delta)) / 1000.0f;
159+ float amountToGrowX = timeElapsed * (rect.width() / 30.0f);
160+ float amountToGrowY = amountToGrowX * (rect.height() / rect.width());
161+ rect.top -= amountToGrowY * grow.x;
162+ rect.left -= amountToGrowX * grow.y;
163+
164+ rect.bottom += amountToGrowY * (1 - grow.x);
165+ rect.right += amountToGrowX * (1 - grow.y);
166+ }
167+
168+ private void performSetup(int viewWidth, int viewHeight) {
169+ if (mBitmap == null) {
170+ mBitmap = getRandomBitmap();
171+ if (mBitmap != null) {
172+ mRect = getRectToFitBitmap(mBitmap.getWidth(), mBitmap.getHeight(), viewWidth, viewHeight);
173+ mFrameRect = new RectF();
174+ mFrameRect.right = viewWidth;
175+ mFrameRect.bottom = viewHeight;
176+ sGrow.set((float)Math.random(), (float)Math.random(), 0);
177+ }
178+ }
179+ if (mQueuedBitmap == null) {
180+ mQueuedBitmap = getRandomBitmap();
181+ if (mQueuedBitmap == null) {
182+ mQueuedBitmap = mBitmap;
183+ }
184+ if (mQueuedBitmap != null) {
185+ mQueuedRect = getRectToFitBitmap(mQueuedBitmap.getWidth(), mQueuedBitmap.getHeight(), viewWidth, viewHeight);
186+ mQueuedFrameRect = new RectF();
187+ mQueuedFrameRect.right = viewWidth;
188+ mQueuedFrameRect.bottom = viewHeight;
189+ sQueuedGrow.set((float)Math.random(), (float)Math.random(), 0);
190+ }
191+ }
192+ }
193+
194+ private Rect getRectToFitBitmap(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) {
195+ Rect rect = new Rect();
196+ float viewAspect = (float)viewHeight / viewWidth;
197+ float newWidth = bitmapWidth * viewAspect;
198+ if (bitmapHeight < newWidth) {
199+ // Vertically constrained.
200+ newWidth = bitmapHeight / viewAspect;
201+ rect.set((int)(bitmapWidth/2 - newWidth/2), 0, (int)(bitmapWidth/2 + newWidth/2), bitmapHeight);
202+ } else {
203+ // Horizontally constrained
204+ float newHeight = bitmapWidth * viewAspect;
205+ rect.set(0, (int)(bitmapHeight/2 - newHeight/2), bitmapWidth, (int)(bitmapHeight/2 + newHeight/2));
206+ }
207+ return rect;
208+ }
209+
210+ private Bitmap getRandomBitmap() {
211+ if (mSource != null) {
212+ return mSource.getBitmapForIndex(getContext(), mCurrentSlideshowCounter++);
213+ }
214+ return null;
215+ }
216+
217+ public void onVisibilityChanged(boolean visible) {
218+ mVisible = visible;
219+ if (!visible) {
220+ mHandler.removeCallbacks(mDrawFrame);
221+ }
222+ drawFrame();
223+ }
224+
225+}