blob: c190ef9ac73969f5b4bb995835e43d6c93fc050d [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cooliris.media;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import com.cooliris.app.App;
import com.cooliris.app.Res;
import com.cooliris.media.MediaClustering.Cluster;
public final class MediaFeed implements Runnable {
private final String TAG = "MediaFeed";
public static final int OPERATION_DELETE = 0;
public static final int OPERATION_ROTATE = 1;
public static final int OPERATION_CROP = 2;
private static final int NUM_ITEMS_LOOKAHEAD = 60;
private static final int NUM_INTERRUPT_RETRIES = 30;
private static final int JOIN_TIMEOUT = 50;
private IndexRange mVisibleRange = new IndexRange();
private IndexRange mBufferedRange = new IndexRange();
private ArrayList<MediaSet> mMediaSets = new ArrayList<MediaSet>();
private Listener mListener;
private DataSource mDataSource;
private boolean mListenerNeedsUpdate = false;
private boolean mMediaFeedNeedsToRun = false;
private MediaSet mSingleWrapper = new MediaSet();
private boolean mInClusteringMode = false;
private HashMap<MediaSet, MediaClustering> mClusterSets = new HashMap<MediaSet, MediaClustering>(32);
private int mExpandedMediaSetIndex = Shared.INVALID;
private MediaFilter mMediaFilter;
private MediaSet mMediaFilteredSet;
private Context mContext;
private Thread mDataSourceThread = null;
private Thread mAlbumSourceThread = null;
private boolean mListenerNeedsLayout;
private boolean mWaitingForMediaScanner;
private boolean mSingleImageMode;
private boolean mLoading;
private HashMap<String, ContentObserver> mContentObservers = new HashMap<String, ContentObserver>();
private ArrayList<String[]> mRequestedRefresh = new ArrayList<String[]>();
private volatile boolean mIsShutdown = false;
public interface Listener {
public abstract void onFeedAboutToChange(MediaFeed feed);
public abstract void onFeedChanged(MediaFeed feed, boolean needsLayout);
}
public MediaFeed(Context context, DataSource dataSource, Listener listener) {
mContext = context;
mListener = listener;
mDataSource = dataSource;
mSingleWrapper.setNumExpectedItems(1);
mLoading = true;
}
public void shutdown() {
mIsShutdown = true;
if (mDataSourceThread != null) {
mDataSource.shutdown();
repeatShuttingDownThread(mDataSourceThread);
mDataSourceThread = null;
}
if (mAlbumSourceThread != null) {
repeatShuttingDownThread(mAlbumSourceThread);
mAlbumSourceThread = null;
}
int numSets = mMediaSets.size();
for (int i = 0; i < numSets; ++i) {
MediaSet set = mMediaSets.get(i);
set.clear();
}
synchronized (mMediaSets) {
mMediaSets.clear();
}
int numClusters = mClusterSets.size();
for (int i = 0; i < numClusters; ++i) {
MediaClustering mc = mClusterSets.get(i);
if (mc != null) {
mc.clear();
}
}
mClusterSets.clear();
mListener = null;
mDataSource = null;
mSingleWrapper = null;
}
private void repeatShuttingDownThread(Thread targetThread) {
for (int i = 0; i < NUM_INTERRUPT_RETRIES && targetThread.isAlive(); ++i) {
targetThread.interrupt();
try {
targetThread.join(JOIN_TIMEOUT);
} catch (InterruptedException e) {
Log.w(TAG, "Cannot stop the thread: " + targetThread.getName(), e);
Thread.currentThread().interrupt();
return;
}
}
if (targetThread.isAlive()) {
Log.w(TAG, "Cannot stop the thread: " + targetThread.getName());
}
}
public void setVisibleRange(int begin, int end) {
if (begin != mVisibleRange.begin || end != mVisibleRange.end) {
mVisibleRange.begin = begin;
mVisibleRange.end = end;
int numItems = 96;
int numItemsBy2 = numItems / 2;
int numItemsBy4 = numItems / 4;
mBufferedRange.begin = (begin / numItemsBy2) * numItemsBy2 - numItemsBy4;
mBufferedRange.end = mBufferedRange.begin + numItems;
mMediaFeedNeedsToRun = true;
}
}
public void setFilter(MediaFilter filter) {
mMediaFilter = filter;
mMediaFilteredSet = null;
if (mListener != null) {
mListener.onFeedAboutToChange(this);
}
mMediaFeedNeedsToRun = true;
}
public void removeFilter() {
mMediaFilter = null;
mMediaFilteredSet = null;
if (mListener != null) {
mListener.onFeedAboutToChange(this);
updateListener(true);
}
mMediaFeedNeedsToRun = true;
}
public ArrayList<MediaSet> getMediaSets() {
return mMediaSets;
}
public MediaSet getMediaSet(final long setId) {
if (setId != Shared.INVALID) {
try {
int mMediaSetsSize = mMediaSets.size();
for (int i = 0; i < mMediaSetsSize; i++) {
final MediaSet set = mMediaSets.get(i);
if (set.mId == setId) {
set.mFlagForDelete = false;
return set;
}
}
} catch (Exception e) {
return null;
}
}
return null;
}
public MediaSet getFilteredSet() {
return mMediaFilteredSet;
}
public MediaSet addMediaSet(final long setId, DataSource dataSource) {
MediaSet mediaSet = new MediaSet(dataSource);
mediaSet.mId = setId;
mMediaSets.add(mediaSet);
if (mDataSourceThread != null && !mDataSourceThread.isAlive()) {
mDataSourceThread.start();
}
mMediaFeedNeedsToRun = true;
return mediaSet;
}
public DataSource getDataSource() {
return mDataSource;
}
public MediaClustering getClustering() {
if (mExpandedMediaSetIndex != Shared.INVALID && mExpandedMediaSetIndex < mMediaSets.size()) {
return mClusterSets.get(mMediaSets.get(mExpandedMediaSetIndex));
}
return null;
}
public ArrayList<Cluster> getClustersForSet(final MediaSet set) {
ArrayList<Cluster> clusters = null;
if (mClusterSets != null && mClusterSets.containsKey(set)) {
MediaClustering mediaClustering = mClusterSets.get(set);
if (mediaClustering != null) {
clusters = mediaClustering.getClusters();
}
}
return clusters;
}
public void addItemToMediaSet(MediaItem item, MediaSet mediaSet) {
item.mParentMediaSet = mediaSet;
mediaSet.addItem(item);
synchronized (mClusterSets) {
if (item.mClusteringState == MediaItem.NOT_CLUSTERED) {
MediaClustering clustering = mClusterSets.get(mediaSet);
if (clustering == null) {
clustering = new MediaClustering(mediaSet.isPicassaAlbum());
mClusterSets.put(mediaSet, clustering);
}
clustering.setTimeRange(mediaSet.mMaxTimestamp - mediaSet.mMinTimestamp, mediaSet.getNumExpectedItems());
clustering.addItemForClustering(item);
item.mClusteringState = MediaItem.CLUSTERED;
}
}
mMediaFeedNeedsToRun = true;
}
public void performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) {
int numBuckets = mediaBuckets.size();
final ArrayList<MediaBucket> copyMediaBuckets = new ArrayList<MediaBucket>(numBuckets);
for (int i = 0; i < numBuckets; ++i) {
copyMediaBuckets.add(mediaBuckets.get(i));
}
if (operation == OPERATION_DELETE && mListener != null) {
mListener.onFeedAboutToChange(this);
}
Thread operationThread = new Thread(new Runnable() {
public void run() {
ArrayList<MediaBucket> mediaBuckets = copyMediaBuckets;
if (operation == OPERATION_DELETE) {
int numBuckets = mediaBuckets.size();
for (int i = 0; i < numBuckets; ++i) {
MediaBucket bucket = mediaBuckets.get(i);
MediaSet set = bucket.mediaSet;
ArrayList<MediaItem> items = bucket.mediaItems;
if (set != null && items == null) {
// Remove the entire bucket.
removeMediaSet(set);
} else if (set != null && items != null) {
// We need to remove these items from the set.
int numItems = items.size();
// We also need to delete the items from the
// cluster.
MediaClustering clustering = mClusterSets.get(set);
for (int j = 0; j < numItems; ++j) {
MediaItem item = items.get(j);
removeItemFromMediaSet(item, set);
if (clustering != null) {
clustering.removeItemFromClustering(item);
}
}
set.updateNumExpectedItems();
set.generateTitle(true);
}
}
updateListener(true);
mMediaFeedNeedsToRun = true;
if (mDataSource != null) {
mDataSource.performOperation(OPERATION_DELETE, mediaBuckets, null);
}
} else {
mDataSource.performOperation(operation, mediaBuckets, data);
}
}
});
operationThread.setName("Operation " + operation);
operationThread.start();
}
public void removeMediaSet(MediaSet set) {
synchronized (mMediaSets) {
mMediaSets.remove(set);
}
mMediaFeedNeedsToRun = true;
}
private void removeItemFromMediaSet(MediaItem item, MediaSet mediaSet) {
mediaSet.removeItem(item);
synchronized (mClusterSets) {
MediaClustering clustering = mClusterSets.get(mediaSet);
if (clustering != null) {
clustering.removeItemFromClustering(item);
}
}
mMediaFeedNeedsToRun = true;
}
public void updateListener(boolean needsLayout) {
mListenerNeedsUpdate = true;
mListenerNeedsLayout = needsLayout;
}
public int getNumSlots() {
int currentMediaSetIndex = mExpandedMediaSetIndex;
ArrayList<MediaSet> mediaSets = mMediaSets;
int mediaSetsSize = mediaSets.size();
if (mInClusteringMode == false) {
if (currentMediaSetIndex == Shared.INVALID || currentMediaSetIndex >= mediaSetsSize) {
return mediaSetsSize;
} else {
MediaSet setToUse = (mMediaFilteredSet == null) ? mediaSets.get(currentMediaSetIndex) : mMediaFilteredSet;
return setToUse.getNumExpectedItems();
}
} else if (currentMediaSetIndex != Shared.INVALID && currentMediaSetIndex < mediaSetsSize) {
MediaSet set = mediaSets.get(currentMediaSetIndex);
MediaClustering clustering = mClusterSets.get(set);
if (clustering != null) {
return clustering.getClustersForDisplay().size();
}
}
return 0;
}
public void copySlotStateFrom(MediaFeed another) {
mExpandedMediaSetIndex = another.mExpandedMediaSetIndex;
mInClusteringMode = another.mInClusteringMode;
}
public ArrayList<Integer> getBreaks() {
if (true)
return null;
int currentMediaSetIndex = mExpandedMediaSetIndex;
ArrayList<MediaSet> mediaSets = mMediaSets;
int mediaSetsSize = mediaSets.size();
if (currentMediaSetIndex == Shared.INVALID || currentMediaSetIndex >= mediaSetsSize)
return null;
MediaSet set = mediaSets.get(currentMediaSetIndex);
MediaClustering clustering = mClusterSets.get(set);
if (clustering != null) {
clustering.compute(null, true);
final ArrayList<Cluster> clusters = clustering.getClustersForDisplay();
int numClusters = clusters.size();
final ArrayList<Integer> retVal = new ArrayList<Integer>(numClusters);
int size = 0;
for (int i = 0; i < numClusters; ++i) {
size += clusters.get(i).getItems().size();
retVal.add(size);
}
return retVal;
} else {
return null;
}
}
public MediaSet getSetForSlot(int slotIndex) {
if (slotIndex < 0) {
return null;
}
ArrayList<MediaSet> mediaSets = mMediaSets;
int mediaSetsSize = mediaSets.size();
int currentMediaSetIndex = mExpandedMediaSetIndex;
if (mInClusteringMode == false) {
if (currentMediaSetIndex == Shared.INVALID || currentMediaSetIndex >= mediaSetsSize) {
if (slotIndex >= mediaSetsSize) {
return null;
}
return mMediaSets.get(slotIndex);
}
if (mSingleWrapper.getNumItems() == 0) {
mSingleWrapper.addItem(null);
}
MediaSet setToUse = (mMediaFilteredSet == null) ? mMediaSets.get(currentMediaSetIndex) : mMediaFilteredSet;
ArrayList<MediaItem> items = setToUse.getItems();
if (slotIndex >= setToUse.getNumItems()) {
return null;
}
mSingleWrapper.getItems().set(0, items.get(slotIndex));
return mSingleWrapper;
} else if (currentMediaSetIndex != Shared.INVALID && currentMediaSetIndex < mediaSetsSize) {
MediaSet set = mediaSets.get(currentMediaSetIndex);
MediaClustering clustering = mClusterSets.get(set);
if (clustering != null) {
ArrayList<MediaClustering.Cluster> clusters = clustering.getClustersForDisplay();
if (clusters.size() > slotIndex) {
MediaClustering.Cluster cluster = clusters.get(slotIndex);
cluster.generateCaption(mContext);
return cluster;
}
}
}
return null;
}
public boolean getWaitingForMediaScanner() {
return mWaitingForMediaScanner;
}
public boolean isLoading() {
return mLoading;
}
public void start() {
final MediaFeed feed = this;
onResume();
mLoading = true;
mDataSourceThread = new Thread(this);
mDataSourceThread.setName("MediaFeed");
mIsShutdown = false;
mAlbumSourceThread = new Thread(new Runnable() {
public void run() {
if (mContext == null)
return;
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
DataSource dataSource = mDataSource;
// We must wait while the SD card is mounted or the MediaScanner
// is running.
if (dataSource != null) {
loadMediaSets();
}
mWaitingForMediaScanner = false;
while (ImageManager.isMediaScannerScanning(mContext.getContentResolver())) {
// MediaScanner is still running, wait
if (Thread.interrupted())
return;
mWaitingForMediaScanner = true;
try {
if (mContext == null)
return;
showToast(mContext.getResources().getString(Res.string.initializing), Toast.LENGTH_LONG);
if (dataSource != null) {
loadMediaSets();
}
Thread.sleep(10000);
} catch (InterruptedException e) {
return;
}
}
if (mWaitingForMediaScanner) {
showToast(mContext.getResources().getString(Res.string.loading_new), Toast.LENGTH_LONG);
mWaitingForMediaScanner = false;
loadMediaSets();
}
mLoading = false;
}
});
mAlbumSourceThread.setName("MediaSets");
mAlbumSourceThread.start();
}
private void loadMediaSets() {
if (mDataSource == null)
return;
final ArrayList<MediaSet> sets = mMediaSets;
synchronized (sets) {
final int numSets = sets.size();
for (int i = 0; i < numSets; ++i) {
final MediaSet set = sets.get(i);
set.mFlagForDelete = true;
}
mDataSource.refresh(MediaFeed.this, mDataSource.getDatabaseUris());
mDataSource.loadMediaSets(MediaFeed.this);
final ArrayList<MediaSet> setsToRemove = new ArrayList<MediaSet>();
for (int i = 0; i < numSets; ++i) {
final MediaSet set = sets.get(i);
if (set.mFlagForDelete) {
setsToRemove.add(set);
}
}
int numSetsToRemove = setsToRemove.size();
for (int i = 0; i < numSetsToRemove; ++i) {
sets.remove(setsToRemove.get(i));
}
setsToRemove.clear();
}
mMediaFeedNeedsToRun = true;
updateListener(false);
}
private void showToast(final String string, final int duration) {
showToast(string, duration, false);
}
private void showToast(final String string, final int duration, final boolean centered) {
if (mContext != null && !App.get(mContext).isPaused()) {
App.get(mContext).getHandler().post(new Runnable() {
public void run() {
if (mContext != null) {
Toast toast = Toast.makeText(mContext, string, duration);
if (centered) {
toast.setGravity(Gravity.CENTER, 0, 0);
}
toast.show();
}
}
});
}
}
public void run() {
DataSource dataSource = mDataSource;
int sleepMs = 10;
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
if (dataSource != null) {
while (!Thread.interrupted() && !mIsShutdown) {
String[] databaseUris = null;
boolean performRefresh = false;
synchronized (mRequestedRefresh) {
if (mRequestedRefresh.size() > 0) {
// We prune this first.
int numRequests = mRequestedRefresh.size();
for (int i = 0; i < numRequests; ++i) {
databaseUris = ArrayUtils.addAll(databaseUris, mRequestedRefresh.get(i));
}
mRequestedRefresh.clear();
performRefresh = true;
// We need to eliminate duplicate uris in this array
final HashMap<String, String> uris = new HashMap<String, String>();
if (databaseUris != null) {
int numUris = databaseUris.length;
for (int i = 0; i < numUris; ++i) {
final String uri = databaseUris[i];
if (uri != null)
uris.put(uri, uri);
}
}
databaseUris = new String[0];
databaseUris = (String[]) uris.keySet().toArray(databaseUris);
}
}
boolean settingFeedAboutToChange = false;
if (performRefresh) {
if (dataSource != null) {
if (mListener != null) {
settingFeedAboutToChange = true;
mListener.onFeedAboutToChange(this);
}
dataSource.refresh(this, databaseUris);
mMediaFeedNeedsToRun = true;
}
}
if (mListenerNeedsUpdate && !mMediaFeedNeedsToRun) {
mListenerNeedsUpdate = false;
if (mListener != null)
synchronized (mMediaSets) {
mListener.onFeedChanged(this, mListenerNeedsLayout);
}
try {
Thread.sleep(sleepMs);
} catch (InterruptedException e) {
return;
}
} else {
try {
Thread.sleep(sleepMs);
} catch (InterruptedException e) {
return;
}
}
sleepMs = 300;
if (!mMediaFeedNeedsToRun)
continue;
App app = App.get(mContext);
if (app == null || app.isPaused())
continue;
if (settingFeedAboutToChange) {
updateListener(true);
}
mMediaFeedNeedsToRun = false;
ArrayList<MediaSet> mediaSets = mMediaSets;
synchronized (mediaSets) {
int expandedSetIndex = mExpandedMediaSetIndex;
if (expandedSetIndex >= mMediaSets.size()) {
expandedSetIndex = Shared.INVALID;
}
if (expandedSetIndex == Shared.INVALID) {
// We purge the sets outside this visibleRange.
int numSets = mediaSets.size();
IndexRange visibleRange = mVisibleRange;
IndexRange bufferedRange = mBufferedRange;
boolean scanMediaSets = true;
for (int i = 0; i < numSets; ++i) {
if (i >= visibleRange.begin && i <= visibleRange.end && scanMediaSets) {
MediaSet set = mediaSets.get(i);
int numItemsLoaded = set.mNumItemsLoaded;
if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
synchronized (set) {
dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
set.checkForDeletedItems();
}
if (set.getNumExpectedItems() == 0) {
mediaSets.remove(set);
break;
}
if (mListener != null) {
mListenerNeedsUpdate = false;
mListener.onFeedChanged(this, mListenerNeedsLayout);
mListenerNeedsLayout = false;
}
sleepMs = 100;
scanMediaSets = false;
}
if (!set.setContainsValidItems()) {
mediaSets.remove(set);
if (mListener != null) {
mListenerNeedsUpdate = false;
mListener.onFeedChanged(this, mListenerNeedsLayout);
mListenerNeedsLayout = false;
}
break;
}
}
}
numSets = mediaSets.size();
for (int i = 0; i < numSets; ++i) {
MediaSet set = mediaSets.get(i);
if (i >= bufferedRange.begin && i <= bufferedRange.end) {
if (scanMediaSets) {
int numItemsLoaded = set.mNumItemsLoaded;
if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
synchronized (set) {
dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
set.checkForDeletedItems();
}
if (set.getNumExpectedItems() == 0) {
mediaSets.remove(set);
break;
}
if (mListener != null) {
mListenerNeedsUpdate = false;
mListener.onFeedChanged(this, mListenerNeedsLayout);
mListenerNeedsLayout = false;
}
sleepMs = 100;
scanMediaSets = false;
}
}
} else if (!mListenerNeedsUpdate && (i < bufferedRange.begin || i > bufferedRange.end)) {
// Purge this set to its initial status.
MediaClustering clustering = mClusterSets.get(set);
if (clustering != null) {
clustering.clear();
mClusterSets.remove(set);
}
if (set.getNumItems() != 0)
set.clear();
}
}
}
if (expandedSetIndex != Shared.INVALID) {
int numSets = mMediaSets.size();
for (int i = 0; i < numSets; ++i) {
// Purge other sets.
if (i != expandedSetIndex) {
MediaSet set = mediaSets.get(i);
MediaClustering clustering = mClusterSets.get(set);
if (clustering != null) {
clustering.clear();
mClusterSets.remove(set);
}
if (set.mNumItemsLoaded != 0)
set.clear();
}
}
// Make sure all the items are loaded for the album.
int numItemsLoaded = mediaSets.get(expandedSetIndex).mNumItemsLoaded;
int requestedItems = mVisibleRange.end;
// requestedItems count changes in clustering mode.
if (mInClusteringMode && mClusterSets != null) {
requestedItems = 0;
MediaClustering clustering = mClusterSets.get(mediaSets.get(expandedSetIndex));
if (clustering != null) {
ArrayList<Cluster> clusters = clustering.getClustersForDisplay();
int numClusters = clusters.size();
for (int i = 0; i < numClusters; i++) {
requestedItems += clusters.get(i).getNumExpectedItems();
}
}
}
MediaSet set = mediaSets.get(expandedSetIndex);
if (numItemsLoaded < set.getNumExpectedItems()) {
// We perform calculations for a window that gets
// anchored to a multiple of NUM_ITEMS_LOOKAHEAD.
// The start of the window is 0, x, 2x, 3x ... etc
// where x = NUM_ITEMS_LOOKAHEAD.
synchronized (set) {
dataSource.loadItemsForSet(this, set, numItemsLoaded, (requestedItems / NUM_ITEMS_LOOKAHEAD)
* NUM_ITEMS_LOOKAHEAD + NUM_ITEMS_LOOKAHEAD);
set.checkForDeletedItems();
}
if (set.getNumExpectedItems() == 0) {
mediaSets.remove(set);
mListenerNeedsUpdate = false;
mListener.onFeedChanged(this, mListenerNeedsLayout);
mListenerNeedsLayout = false;
}
if (numItemsLoaded != set.mNumItemsLoaded && mListener != null) {
mListenerNeedsUpdate = false;
mListener.onFeedChanged(this, mListenerNeedsLayout);
mListenerNeedsLayout = false;
}
}
}
MediaFilter filter = mMediaFilter;
if (filter != null && mMediaFilteredSet == null) {
if (expandedSetIndex != Shared.INVALID) {
MediaSet set = mediaSets.get(expandedSetIndex);
ArrayList<MediaItem> items = set.getItems();
int numItems = set.getNumItems();
MediaSet filteredSet = new MediaSet();
filteredSet.setNumExpectedItems(numItems);
mMediaFilteredSet = filteredSet;
for (int i = 0; i < numItems; ++i) {
MediaItem item = items.get(i);
if (filter.pass(item)) {
filteredSet.addItem(item);
}
}
filteredSet.updateNumExpectedItems();
filteredSet.generateTitle(true);
}
updateListener(true);
}
}
}
}
}
public void expandMediaSet(int mediaSetIndex) {
// We need to check if this slot can be focused or not.
if (mListener != null) {
mListener.onFeedAboutToChange(this);
}
if (mExpandedMediaSetIndex > 0 && mediaSetIndex == Shared.INVALID) {
// We are collapsing a previously expanded media set
if (mediaSetIndex < mMediaSets.size() && mExpandedMediaSetIndex >= 0 && mExpandedMediaSetIndex < mMediaSets.size()) {
MediaSet set = mMediaSets.get(mExpandedMediaSetIndex);
if (set.getNumItems() == 0) {
set.clear();
}
}
}
mExpandedMediaSetIndex = mediaSetIndex;
if (mediaSetIndex < mMediaSets.size() && mediaSetIndex >= 0) {
// Notify Picasa that the user entered the album.
// MediaSet set = mMediaSets.get(mediaSetIndex);
// PicasaService.requestSync(mContext,
// PicasaService.TYPE_ALBUM_PHOTOS, set.mPicasaAlbumId);
}
updateListener(true);
mMediaFeedNeedsToRun = true;
}
public boolean canExpandSet(int slotIndex) {
int mediaSetIndex = slotIndex;
if (mediaSetIndex < mMediaSets.size() && mediaSetIndex >= 0) {
MediaSet set = mMediaSets.get(mediaSetIndex);
if (set.getNumItems() > 0) {
MediaItem item = set.getItems().get(0);
if (item.mId == Shared.INVALID) {
return false;
}
return true;
}
}
return false;
}
public boolean hasExpandedMediaSet() {
return (mExpandedMediaSetIndex != Shared.INVALID);
}
public boolean restorePreviousClusteringState() {
boolean retVal = disableClusteringIfNecessary();
if (retVal) {
if (mListener != null) {
mListener.onFeedAboutToChange(this);
}
updateListener(true);
mMediaFeedNeedsToRun = true;
}
return retVal;
}
private boolean disableClusteringIfNecessary() {
if (mInClusteringMode) {
// Disable clustering.
mInClusteringMode = false;
mMediaFeedNeedsToRun = true;
return true;
}
return false;
}
public boolean isClustered() {
return mInClusteringMode;
}
public MediaSet getCurrentSet() {
if (mExpandedMediaSetIndex != Shared.INVALID && mExpandedMediaSetIndex < mMediaSets.size()) {
return mMediaSets.get(mExpandedMediaSetIndex);
}
return null;
}
public void performClustering() {
if (mListener != null) {
mListener.onFeedAboutToChange(this);
}
MediaSet setToUse = null;
if (mExpandedMediaSetIndex != Shared.INVALID && mExpandedMediaSetIndex < mMediaSets.size()) {
setToUse = mMediaSets.get(mExpandedMediaSetIndex);
}
if (setToUse != null) {
MediaClustering clustering = null;
synchronized (mClusterSets) {
// Make sure the computation is completed to the end.
clustering = mClusterSets.get(setToUse);
if (clustering != null) {
clustering.compute(null, true);
} else {
return;
}
}
mInClusteringMode = true;
updateListener(true);
}
}
public void moveSetToFront(MediaSet mediaSet) {
ArrayList<MediaSet> mediaSets = mMediaSets;
int numSets = mediaSets.size();
if (numSets == 0) {
mediaSets.add(mediaSet);
return;
}
MediaSet setToFind = mediaSets.get(0);
if (setToFind == mediaSet) {
return;
}
mediaSets.set(0, mediaSet);
int indexToSwapTill = -1;
for (int i = 1; i < numSets; ++i) {
MediaSet set = mediaSets.get(i);
if (set == mediaSet) {
mediaSets.set(i, setToFind);
indexToSwapTill = i;
break;
}
}
if (indexToSwapTill != Shared.INVALID) {
for (int i = indexToSwapTill; i > 1; --i) {
MediaSet setEnd = mediaSets.get(i);
MediaSet setPrev = mediaSets.get(i - 1);
mediaSets.set(i, setPrev);
mediaSets.set(i - 1, setEnd);
}
}
mMediaFeedNeedsToRun = true;
}
public MediaSet replaceMediaSet(long setId, DataSource dataSource) {
Log.i(TAG, "Replacing media set " + setId);
final MediaSet set = getMediaSet(setId);
if (set != null)
set.refresh();
return set;
}
public void setSingleImageMode(boolean singleImageMode) {
mSingleImageMode = singleImageMode;
}
public boolean isSingleImageMode() {
return mSingleImageMode;
}
public MediaSet getExpandedMediaSet() {
if (mExpandedMediaSetIndex == Shared.INVALID)
return null;
if (mExpandedMediaSetIndex >= mMediaSets.size())
return null;
return mMediaSets.get(mExpandedMediaSetIndex);
}
public void refresh() {
if (mDataSource != null) {
synchronized (mRequestedRefresh) {
mRequestedRefresh.add(mDataSource.getDatabaseUris());
}
}
}
private void refresh(final String[] databaseUris) {
synchronized (mMediaSets) {
if (mDataSource != null) {
synchronized (mRequestedRefresh) {
mRequestedRefresh.add(databaseUris);
}
}
}
}
public void onPause() {
final HashMap<String, ContentObserver> observers = mContentObservers;
final int numObservers = observers.size();
if (numObservers > 0) {
String[] uris = new String[numObservers];
final Set<String> keySet = observers.keySet();
if (keySet != null) {
uris = keySet.toArray(uris);
final int numUris = uris.length;
final ContentResolver cr = mContext.getContentResolver();
for (int i = 0; i < numUris; ++i) {
final String uri = uris[i];
if (uri != null) {
final ContentObserver observer = observers.get(uri);
cr.unregisterContentObserver(observer);
observers.remove(uri);
}
}
}
}
observers.clear();
}
public void onResume() {
final Context context = mContext;
final DataSource dataSource = mDataSource;
if (context == null || dataSource == null)
return;
// We setup the listeners for this datasource
final String[] uris = dataSource.getDatabaseUris();
final HashMap<String, ContentObserver> observers = mContentObservers;
if (context instanceof Gallery) {
final Gallery gallery = (Gallery) context;
final ContentResolver cr = context.getContentResolver();
if (uris != null) {
final int numUris = uris.length;
for (int i = 0; i < numUris; ++i) {
final String uri = uris[i];
final ContentObserver presentObserver = observers.get(uri);
if (presentObserver == null) {
final Handler handler = App.get(context).getHandler();
final ContentObserver observer = new ContentObserver(handler) {
public void onChange(boolean selfChange) {
if (!mWaitingForMediaScanner) {
MediaFeed.this.refresh(new String[] { uri });
}
}
};
cr.registerContentObserver(Uri.parse(uri), true, observer);
observers.put(uri, observer);
}
}
}
}
refresh();
}
}