OSDN Git Service

Fix hardcoded date/time formats in 3D Gallery.
[android-x86/packages-apps-Gallery2.git] / src / com / cooliris / media / MediaFeed.java
1 package com.cooliris.media;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5
6 import android.content.Context;
7 import android.view.Gravity;
8 import android.widget.Toast;
9 import android.os.Process;
10
11 import com.cooliris.media.MediaClustering.Cluster;
12
13 public final class MediaFeed implements Runnable {
14     public static final int OPERATION_DELETE = 0;
15     public static final int OPERATION_ROTATE = 1;
16     public static final int OPERATION_CROP = 2;
17
18     private static final int NUM_ITEMS_LOOKAHEAD = 60;
19
20     private IndexRange mVisibleRange = new IndexRange();
21     private IndexRange mBufferedRange = new IndexRange();
22     private ArrayList<MediaSet> mMediaSets = new ArrayList<MediaSet>();
23     private Listener mListener;
24     private DataSource mDataSource;
25     private boolean mListenerNeedsUpdate = false;
26     private boolean mMediaFeedNeedsToRun = false;
27     private MediaSet mSingleWrapper = new MediaSet();
28     private boolean mInClusteringMode = false;
29     private HashMap<MediaSet, MediaClustering> mClusterSets = new HashMap<MediaSet, MediaClustering>(32);
30     private int mExpandedMediaSetIndex = Shared.INVALID;
31     private MediaFilter mMediaFilter;
32     private MediaSet mMediaFilteredSet;
33     private Context mContext;
34     private Thread mDataSourceThread = null;
35     private Thread mAlbumSourceThread = null;
36     private boolean mListenerNeedsLayout;
37     private boolean mWaitingForMediaScanner;
38     private boolean mSingleImageMode;
39     private boolean mLoading;
40
41     public interface Listener {
42         public abstract void onFeedAboutToChange(MediaFeed feed);
43
44         public abstract void onFeedChanged(MediaFeed feed, boolean needsLayout);
45     }
46
47     public MediaFeed(Context context, DataSource dataSource, Listener listener) {
48         mContext = context;
49         mListener = listener;
50         mDataSource = dataSource;
51         mSingleWrapper.setNumExpectedItems(1);
52         mLoading = true;
53     }
54
55     public void shutdown() {
56         if (mDataSourceThread != null) {
57             mDataSource.shutdown();
58             mDataSourceThread.interrupt();
59             mDataSourceThread = null;
60         }
61         if (mAlbumSourceThread != null) {
62             mAlbumSourceThread.interrupt();
63             mAlbumSourceThread = null;
64         }
65         int numSets = mMediaSets.size();
66         for (int i = 0; i < numSets; ++i) {
67             MediaSet set = mMediaSets.get(i);
68             set.clear();
69         }
70         synchronized (mMediaSets) {
71             mMediaSets.clear();
72         }
73         int numClusters = mClusterSets.size();
74         for (int i = 0; i < numClusters; ++i) {
75             MediaClustering mc = mClusterSets.get(i);
76             if (mc != null) {
77                 mc.clear();
78             }
79         }
80         mClusterSets.clear();
81         mListener = null;
82         mDataSource = null;
83         mSingleWrapper = null;
84     }
85
86     public void setVisibleRange(int begin, int end) {
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         }
97     }
98
99     public void setFilter(MediaFilter filter) {
100         mMediaFilter = filter;
101         mMediaFilteredSet = null;
102         if (mListener != null) {
103             mListener.onFeedAboutToChange(this);
104         }
105         mMediaFeedNeedsToRun = true;
106     }
107
108     public void removeFilter() {
109         mMediaFilter = null;
110         mMediaFilteredSet = null;
111         if (mListener != null) {
112             mListener.onFeedAboutToChange(this);
113             updateListener(true);
114         }
115         mMediaFeedNeedsToRun = true;
116     }
117
118     public ArrayList<MediaSet> getMediaSets() {
119         return mMediaSets;
120     }
121
122     public MediaSet getMediaSet(final long setId) {
123         if (setId != Shared.INVALID) {
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                     }
130                 }
131             } catch (Exception e) {
132                 return null;
133             }
134         }
135         return null;
136     }
137
138     public MediaSet getFilteredSet() {
139         return mMediaFilteredSet;
140     }
141
142     public MediaSet addMediaSet(final long setId, DataSource dataSource) {
143         MediaSet mediaSet = new MediaSet(dataSource);
144         mediaSet.mId = setId;
145         mMediaSets.add(mediaSet);
146         if (mDataSourceThread != null && !mDataSourceThread.isAlive()) {
147             mDataSourceThread.start();
148         }
149         mMediaFeedNeedsToRun = true;
150         return mediaSet;
151     }
152
153     public DataSource getDataSource() {
154         return mDataSource;
155     }
156
157     public MediaClustering getClustering() {
158         if (mExpandedMediaSetIndex != Shared.INVALID && mExpandedMediaSetIndex < mMediaSets.size()) {
159             return mClusterSets.get(mMediaSets.get(mExpandedMediaSetIndex));
160         }
161         return null;
162     }
163
164     public ArrayList<Cluster> getClustersForSet(final MediaSet set) {
165         ArrayList<Cluster> clusters = null;
166         if (mClusterSets != null && mClusterSets.containsKey(set)) {
167             MediaClustering mediaClustering = mClusterSets.get(set);
168             if (mediaClustering != null) {
169                 clusters = mediaClustering.getClusters();
170             }
171         }
172         return clusters;
173     }
174
175     public void addItemToMediaSet(MediaItem item, MediaSet mediaSet) {
176         item.mParentMediaSet = mediaSet;
177         mediaSet.addItem(item);
178         synchronized (mClusterSets) {
179             if (item.mClusteringState == MediaItem.NOT_CLUSTERED) {
180                 MediaClustering clustering = mClusterSets.get(mediaSet);
181                 if (clustering == null) {
182                     clustering = new MediaClustering(mediaSet.isPicassaAlbum());
183                     mClusterSets.put(mediaSet, clustering);
184                 }
185                 clustering.setTimeRange(mediaSet.mMaxTimestamp - mediaSet.mMinTimestamp, mediaSet.getNumExpectedItems());
186                 clustering.addItemForClustering(item);
187                 item.mClusteringState = MediaItem.CLUSTERED;
188             }
189         }
190         mMediaFeedNeedsToRun = true;
191     }
192
193     public void performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) {
194         int numBuckets = mediaBuckets.size();
195         final ArrayList<MediaBucket> copyMediaBuckets = new ArrayList<MediaBucket>(numBuckets);
196         for (int i = 0; i < numBuckets; ++i) {
197             copyMediaBuckets.add(mediaBuckets.get(i));
198         }
199         if (operation == OPERATION_DELETE && mListener != null) {
200             mListener.onFeedAboutToChange(this);
201         }
202         Thread operationThread = new Thread(new Runnable() {
203             public void run() {
204                 ArrayList<MediaBucket> mediaBuckets = copyMediaBuckets;
205                 if (operation == OPERATION_DELETE) {
206                     int numBuckets = mediaBuckets.size();
207                     for (int i = 0; i < numBuckets; ++i) {
208                         MediaBucket bucket = mediaBuckets.get(i);
209                         MediaSet set = bucket.mediaSet;
210                         ArrayList<MediaItem> items = bucket.mediaItems;
211                         if (set != null && items == null) {
212                             // Remove the entire bucket.
213                             removeMediaSet(set);
214                         } else if (set != null && items != null) {
215                             // We need to remove these items from the set.
216                             int numItems = items.size();
217                             // We also need to delete the items from the cluster.
218                             MediaClustering clustering = mClusterSets.get(set);
219                             for (int j = 0; j < numItems; ++j) {
220                                 MediaItem item = items.get(j);
221                                 removeItemFromMediaSet(item, set);
222                                 if (clustering != null) {
223                                     clustering.removeItemFromClustering(item);
224                                 }
225                             }
226                             set.updateNumExpectedItems();
227                             set.generateTitle(true);
228                         }
229                     }
230                     updateListener(true);
231                     mMediaFeedNeedsToRun = true;
232                     if (mDataSource != null) {
233                         mDataSource.performOperation(OPERATION_DELETE, mediaBuckets, null);
234                     }
235                 } else {
236                     mDataSource.performOperation(operation, mediaBuckets, data);
237                 }
238             }
239         });
240         operationThread.setName("Operation " + operation);
241         operationThread.start();
242     }
243
244     public void removeMediaSet(MediaSet set) {
245         mMediaSets.remove(set);
246         mMediaFeedNeedsToRun = true;
247     }
248
249     private void removeItemFromMediaSet(MediaItem item, MediaSet mediaSet) {
250         mediaSet.removeItem(item);
251         synchronized (mClusterSets) {
252             MediaClustering clustering = mClusterSets.get(mediaSet);
253             if (clustering != null) {
254                 clustering.removeItemFromClustering(item);
255             }
256         }
257         mMediaFeedNeedsToRun = true;
258     }
259
260     public void updateListener(boolean needsLayout) {
261         mListenerNeedsUpdate = true;
262         mListenerNeedsLayout = needsLayout;
263     }
264
265     public int getNumSlots() {
266         int currentMediaSetIndex = mExpandedMediaSetIndex;
267         ArrayList<MediaSet> mediaSets = mMediaSets;
268         int mediaSetsSize = mediaSets.size();
269
270         if (mInClusteringMode == false) {
271             if (currentMediaSetIndex == Shared.INVALID || currentMediaSetIndex >= mediaSetsSize) {
272                 return mediaSetsSize;
273             } else {
274                 MediaSet setToUse = (mMediaFilteredSet == null) ? mediaSets.get(currentMediaSetIndex) : mMediaFilteredSet;
275                 return setToUse.getNumItems();
276             }
277         } else if (currentMediaSetIndex != Shared.INVALID && currentMediaSetIndex < mediaSetsSize) {
278             MediaSet set = mediaSets.get(currentMediaSetIndex);
279             MediaClustering clustering = mClusterSets.get(set);
280             if (clustering != null) {
281                 return clustering.getClustersForDisplay().size();
282             }
283         }
284         return 0;
285     }
286
287     public MediaSet getSetForSlot(int slotIndex) {
288         if (slotIndex < 0) {
289             return null;
290         }
291
292         ArrayList<MediaSet> mediaSets = mMediaSets;
293         int mediaSetsSize = mediaSets.size();
294         int currentMediaSetIndex = mExpandedMediaSetIndex;
295
296         if (mInClusteringMode == false) {
297             if (currentMediaSetIndex == Shared.INVALID || currentMediaSetIndex >= mediaSetsSize) {
298                 if (slotIndex >= mediaSetsSize) {
299                     return null;
300                 }
301                 return mMediaSets.get(slotIndex);
302             }
303             if (mSingleWrapper.getNumItems() == 0) {
304                 mSingleWrapper.addItem(null);
305             }
306             MediaSet setToUse = (mMediaFilteredSet == null) ? mMediaSets.get(currentMediaSetIndex) : mMediaFilteredSet;
307             ArrayList<MediaItem> items = setToUse.getItems();
308             if (slotIndex >= setToUse.getNumItems()) {
309                 return null;
310             }
311             mSingleWrapper.getItems().set(0, items.get(slotIndex));
312             return mSingleWrapper;
313         } else if (currentMediaSetIndex != Shared.INVALID && currentMediaSetIndex < mediaSetsSize) {
314             MediaSet set = mediaSets.get(currentMediaSetIndex);
315             MediaClustering clustering = mClusterSets.get(set);
316             if (clustering != null) {
317                 ArrayList<MediaClustering.Cluster> clusters = clustering.getClustersForDisplay();
318                 if (clusters.size() > slotIndex) {
319                     MediaClustering.Cluster cluster = clusters.get(slotIndex);
320                     cluster.generateCaption(mContext);
321                     return cluster;
322                 }
323             }
324         }
325         return null;
326     }
327
328     public boolean getWaitingForMediaScanner() {
329         return mWaitingForMediaScanner;
330     }
331
332     public boolean isLoading() {
333         return mLoading;
334     }
335
336     public void start() {
337         final MediaFeed feed = this;
338         mLoading = true;
339         mDataSourceThread = new Thread(this);
340         mDataSourceThread.setName("MediaFeed");
341         mAlbumSourceThread = new Thread(new Runnable() {
342             public void run() {
343                 if (mContext == null)
344                     return;
345                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
346                 DataSource dataSource = mDataSource;
347                 // We must wait while the SD card is mounted or the MediaScanner is running.
348                 if (dataSource != null) {
349                     dataSource.loadMediaSets(feed);
350                 }
351                 mWaitingForMediaScanner = false;
352                 while (ImageManager.isMediaScannerScanning(mContext.getContentResolver())) {
353                     // MediaScanner is still running, wait
354                     mWaitingForMediaScanner = true;
355                     try {
356                         if (mContext == null)
357                             return;
358                         showToast(mContext.getResources().getString(R.string.initializing), Toast.LENGTH_LONG);
359                         Thread.sleep(6000);
360                     } catch (InterruptedException e) {
361
362                     }
363                 }
364                 if (mWaitingForMediaScanner) {
365                     showToast(mContext.getResources().getString(R.string.loading_new), Toast.LENGTH_LONG);
366                     mWaitingForMediaScanner = false;
367                     if (dataSource != null) {
368                         dataSource.loadMediaSets(feed);
369                     }
370                 }
371                 mLoading = false;
372             }
373         });
374         mAlbumSourceThread.setName("MediaSets");
375         mAlbumSourceThread.start();
376     }
377
378     private void showToast(final String string, final int duration) {
379         showToast(string, duration, false);
380     }
381
382     private void showToast(final String string, final int duration, final boolean centered) {
383         if (mContext != null && !((Gallery) mContext).isPaused()) {
384             ((Gallery) mContext).getHandler().post(new Runnable() {
385                 public void run() {
386                     if (mContext != null) {
387                         Toast toast = Toast.makeText(mContext, string, duration);
388                         if (centered) {
389                             toast.setGravity(Gravity.CENTER, 0, 0);
390                         }
391                         toast.show();
392                     }
393                 }
394             });
395         }
396     }
397
398     public void run() {
399         DataSource dataSource = mDataSource;
400         int sleepMs = 10;
401         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
402         if (dataSource != null) {
403             while (!Thread.interrupted()) {
404                 if (mListenerNeedsUpdate) {
405                     mListenerNeedsUpdate = false;
406                     if (mListener != null)
407                         mListener.onFeedChanged(this, mListenerNeedsLayout);
408                     try {
409                         Thread.sleep(sleepMs);
410                     } catch (InterruptedException e) {
411                         return;
412                     }
413                 } else {
414                     if (mWaitingForMediaScanner) {
415                         synchronized (mMediaSets) {
416                             mMediaSets.clear();
417                         }
418                     }
419                     try {
420                         Thread.sleep(sleepMs);
421                     } catch (InterruptedException e) {
422                         return;
423                     }
424                 }
425                 sleepMs = 300;
426                 if (!mMediaFeedNeedsToRun)
427                     continue;
428                 mMediaFeedNeedsToRun = false;
429                 ArrayList<MediaSet> mediaSets = mMediaSets;
430                 synchronized (mediaSets) {
431                     int expandedSetIndex = mExpandedMediaSetIndex;
432                     if (expandedSetIndex >= mMediaSets.size()) {
433                         expandedSetIndex = Shared.INVALID;
434                     }
435                     if (expandedSetIndex == Shared.INVALID) {
436                         // We purge the sets outside this visibleRange.
437                         int numSets = mMediaSets.size();
438                         IndexRange visibleRange = mVisibleRange;
439                         IndexRange bufferedRange = mBufferedRange;
440                         boolean scanMediaSets = true;
441                         for (int i = 0; i < numSets; ++i) {
442                             if (i >= visibleRange.begin && i <= visibleRange.end && scanMediaSets) {
443                                 MediaSet set = mediaSets.get(i);
444                                 int numItemsLoaded = set.mNumItemsLoaded;
445                                 if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
446                                     dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
447                                     if (set.getNumExpectedItems() == 0) {
448                                         mediaSets.remove(set);
449                                         break;
450                                     }
451                                     if (mListener != null) {
452                                         mListener.onFeedChanged(this, false);
453                                     }
454                                     sleepMs = 100;
455                                     scanMediaSets = false;
456                                 }
457                                 if (!set.setContainsValidItems()) {
458                                     mediaSets.remove(set);
459                                     if (mListener != null) {
460                                         mListener.onFeedChanged(this, false);
461                                     }
462                                     break;
463                                 }
464                             }
465                         }
466                         numSets = mMediaSets.size();
467                         for (int i = 0; i < numSets; ++i) {
468                             MediaSet set = mediaSets.get(i);
469                             if (i >= bufferedRange.begin && i <= bufferedRange.end) {
470                                 if (scanMediaSets) {
471                                     int numItemsLoaded = set.mNumItemsLoaded;
472                                     if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
473                                         dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
474                                         if (set.getNumExpectedItems() == 0) {
475                                             mediaSets.remove(set);
476                                             break;
477                                         }
478                                         if (mListener != null) {
479                                             mListener.onFeedChanged(this, false);
480                                         }
481                                         sleepMs = 100;
482                                         scanMediaSets = false;
483                                     }
484                                 }
485                             } else if (i < bufferedRange.begin || i > bufferedRange.end) {
486                                 // Purge this set to its initial status.
487                                 MediaClustering clustering = mClusterSets.get(set);
488                                 if (clustering != null) {
489                                     clustering.clear();
490                                     mClusterSets.remove(set);
491                                 }
492                                 if (set.getNumItems() != 0)
493                                     set.clear();
494                             }
495                         }
496                     }
497                     if (expandedSetIndex != Shared.INVALID) {
498                         int numSets = mMediaSets.size();
499                         for (int i = 0; i < numSets; ++i) {
500                             // Purge other sets.
501                             if (i != expandedSetIndex) {
502                                 MediaSet set = mediaSets.get(i);
503                                 MediaClustering clustering = mClusterSets.get(set);
504                                 if (clustering != null) {
505                                     clustering.clear();
506                                     mClusterSets.remove(set);
507                                 }
508                                 if (set.getNumItems() != 0)
509                                     set.clear();
510                             }
511                         }
512                         // Make sure all the items are loaded for the album.
513                         int numItemsLoaded = mediaSets.get(expandedSetIndex).mNumItemsLoaded;
514                         int requestedItems = mVisibleRange.end;
515                         // requestedItems count changes in clustering mode.
516                         if (mInClusteringMode && mClusterSets != null) {
517                             requestedItems = 0;
518                             MediaClustering clustering = mClusterSets.get(mediaSets.get(expandedSetIndex));
519                             ArrayList<Cluster> clusters = clustering.getClustersForDisplay();
520                             int numClusters = clusters.size();
521                             for (int i = 0; i < numClusters; i++) {
522                                 requestedItems += clusters.get(i).getNumExpectedItems();
523                             }
524                         }
525                         MediaSet set = mediaSets.get(expandedSetIndex);
526                         if (numItemsLoaded < set.getNumExpectedItems()) {
527                             // TODO(Venkat) Why are we doing 4th param calculations like this?
528                             dataSource.loadItemsForSet(this, set, numItemsLoaded, (requestedItems / NUM_ITEMS_LOOKAHEAD)
529                                     * NUM_ITEMS_LOOKAHEAD + NUM_ITEMS_LOOKAHEAD);
530                             if (set.getNumExpectedItems() == 0) {
531                                 mediaSets.remove(set);
532                                 mListener.onFeedChanged(this, false);
533                             }
534                             if (numItemsLoaded != set.mNumItemsLoaded && mListener != null) {
535                                 mListener.onFeedChanged(this, false);
536                             }
537                         }
538                     }
539                     MediaFilter filter = mMediaFilter;
540                     if (filter != null && mMediaFilteredSet == null) {
541                         if (expandedSetIndex != Shared.INVALID) {
542                             MediaSet set = mediaSets.get(expandedSetIndex);
543                             ArrayList<MediaItem> items = set.getItems();
544                             int numItems = set.getNumItems();
545                             MediaSet filteredSet = new MediaSet();
546                             filteredSet.setNumExpectedItems(numItems);
547                             mMediaFilteredSet = filteredSet;
548                             for (int i = 0; i < numItems; ++i) {
549                                 MediaItem item = items.get(i);
550                                 if (filter.pass(item)) {
551                                     filteredSet.addItem(item);
552                                 }
553                             }
554                             filteredSet.updateNumExpectedItems();
555                             filteredSet.generateTitle(true);
556                         }
557                         updateListener(true);
558                     }
559                 }
560             }
561         }
562     }
563
564     public void expandMediaSet(int mediaSetIndex) {
565         // We need to check if this slot can be focused or not.
566         if (mListener != null) {
567             mListener.onFeedAboutToChange(this);
568         }
569         if (mExpandedMediaSetIndex > 0 && mediaSetIndex == Shared.INVALID) {
570             // We are collapsing a previously expanded media set
571             if (mediaSetIndex < mMediaSets.size() && mExpandedMediaSetIndex >= 0) {
572                 MediaSet set = mMediaSets.get(mExpandedMediaSetIndex);
573                 if (set.getNumItems() == 0) {
574                     set.clear();
575                 }
576             }
577         }
578         mExpandedMediaSetIndex = mediaSetIndex;
579         if (mediaSetIndex < mMediaSets.size() && mediaSetIndex >= 0) {
580             // Notify Picasa that the user entered the album.
581             // MediaSet set = mMediaSets.get(mediaSetIndex);
582             // PicasaService.requestSync(mContext, PicasaService.TYPE_ALBUM_PHOTOS, set.mPicasaAlbumId);
583         }
584         updateListener(true);
585         mMediaFeedNeedsToRun = true;
586     }
587
588     public boolean canExpandSet(int slotIndex) {
589         int mediaSetIndex = slotIndex;
590         if (mediaSetIndex < mMediaSets.size() && mediaSetIndex >= 0) {
591             MediaSet set = mMediaSets.get(mediaSetIndex);
592             if (set.getNumItems() > 0) {
593                 MediaItem item = set.getItems().get(0);
594                 if (item.mId == Shared.INVALID) {
595                     return false;
596                 }
597                 return true;
598             }
599         }
600         return false;
601     }
602
603     public boolean hasExpandedMediaSet() {
604         return (mExpandedMediaSetIndex != Shared.INVALID);
605     }
606
607     public boolean restorePreviousClusteringState() {
608         boolean retVal = disableClusteringIfNecessary();
609         if (retVal) {
610             if (mListener != null) {
611                 mListener.onFeedAboutToChange(this);
612             }
613             updateListener(true);
614             mMediaFeedNeedsToRun = true;
615         }
616         return retVal;
617     }
618
619     private boolean disableClusteringIfNecessary() {
620         if (mInClusteringMode) {
621             // Disable clustering.
622             mInClusteringMode = false;
623             mMediaFeedNeedsToRun = true;
624             return true;
625         }
626         return false;
627     }
628
629     public boolean isClustered() {
630         return mInClusteringMode;
631     }
632
633     public MediaSet getCurrentSet() {
634         if (mExpandedMediaSetIndex != Shared.INVALID && mExpandedMediaSetIndex < mMediaSets.size()) {
635             return mMediaSets.get(mExpandedMediaSetIndex);
636         }
637         return null;
638     }
639
640     public void performClustering() {
641         if (mListener != null) {
642             mListener.onFeedAboutToChange(this);
643         }
644         MediaSet setToUse = null;
645         if (mExpandedMediaSetIndex != Shared.INVALID || mExpandedMediaSetIndex < mMediaSets.size()) {
646             setToUse = mMediaSets.get(mExpandedMediaSetIndex);
647         }
648         if (setToUse != null) {
649             MediaClustering clustering = null;
650             synchronized (mClusterSets) {
651                 // Make sure the computation is completed to the end.
652                 clustering = mClusterSets.get(setToUse);
653                 if (clustering != null) {
654                     clustering.compute(null, true);
655                 } else {
656                     return;
657                 }
658             }
659             mInClusteringMode = true;
660             mMediaFeedNeedsToRun = true;
661             updateListener(true);
662         }
663     }
664
665     public void moveSetToFront(MediaSet mediaSet) {
666         ArrayList<MediaSet> mediaSets = mMediaSets;
667         int numSets = mediaSets.size();
668         if (numSets == 0) {
669             mediaSets.add(mediaSet);
670             return;
671         }
672         MediaSet setToFind = mediaSets.get(0);
673         if (setToFind == mediaSet) {
674             return;
675         }
676         mediaSets.set(0, mediaSet);
677         int indexToSwapTill = -1;
678         for (int i = 1; i < numSets; ++i) {
679             MediaSet set = mediaSets.get(i);
680             if (set == mediaSet) {
681                 mediaSets.set(i, setToFind);
682                 indexToSwapTill = i;
683                 break;
684             }
685         }
686         if (indexToSwapTill != Shared.INVALID) {
687             for (int i = indexToSwapTill; i > 1; --i) {
688                 MediaSet setEnd = mediaSets.get(i);
689                 MediaSet setPrev = mediaSets.get(i - 1);
690                 mediaSets.set(i, setPrev);
691                 mediaSets.set(i - 1, setEnd);
692             }
693         }
694         mMediaFeedNeedsToRun = true;
695     }
696
697     public MediaSet replaceMediaSet(long setId, DataSource dataSource) {
698         MediaSet mediaSet = new MediaSet(dataSource);
699         mediaSet.mId = setId;
700         ArrayList<MediaSet> mediaSets = mMediaSets;
701         int numSets = mediaSets.size();
702         for (int i = 0; i < numSets; ++i) {
703             if (mediaSets.get(i).mId == setId) {
704                 MediaSet thisSet = mediaSets.get(i);
705                 mediaSet.mName = thisSet.mName;
706                 mediaSets.set(i, mediaSet);
707                 break;
708             }
709         }
710         mMediaFeedNeedsToRun = true;
711         return mediaSet;
712     }
713
714     public void setSingleImageMode(boolean singleImageMode) {
715         mSingleImageMode = singleImageMode;
716     }
717
718     public boolean isSingleImageMode() {
719         return mSingleImageMode;
720     }
721
722     public MediaSet getExpandedMediaSet() {
723         if (mExpandedMediaSetIndex == Shared.INVALID)
724             return null;
725         if (mExpandedMediaSetIndex >= mMediaSets.size())
726             return null;
727         return mMediaSets.get(mExpandedMediaSetIndex);
728     }
729 }