blob: 24cb1e7bf0779f6c29290f94101292e7fc829ef2 [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.Arrays;
import java.util.HashMap;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import com.cooliris.app.App;
import com.cooliris.picasa.AlbumEntry;
import com.cooliris.picasa.Entry;
import com.cooliris.picasa.EntrySchema;
import com.cooliris.picasa.PhotoEntry;
import com.cooliris.picasa.PicasaApi;
import com.cooliris.picasa.PicasaContentProvider;
import com.cooliris.picasa.PicasaService;
public final class PicasaDataSource implements DataSource {
private static final String TAG = "PicasaDataSource";
public static final DiskCache sThumbnailCache = new DiskCache("picasa-thumbs");
private static final String DEFAULT_BUCKET_SORT_ORDER = AlbumEntry.Columns.USER + ", " + AlbumEntry.Columns.DATE_PUBLISHED
+ " DESC";
private ContentProviderClient mProviderClient;
private final Context mContext;
public PicasaDataSource(final Context context) {
mContext = context;
}
public static final HashMap<String, Boolean> getAccountStatus(final Context context) {
final Account[] accounts = PicasaApi.getAccounts(context);
int numAccounts = accounts.length;
HashMap<String, Boolean> accountsEnabled = new HashMap<String, Boolean>(numAccounts);
for (int i = 0; i < numAccounts; ++i) {
Account account = accounts[i];
boolean isEnabled = ContentResolver.getSyncAutomatically(account, PicasaContentProvider.AUTHORITY);
String username = PicasaApi.canonicalizeUsername(account.name);
accountsEnabled.put(username, new Boolean(isEnabled));
}
return accountsEnabled;
}
public void loadMediaSets(final MediaFeed feed) {
// We do this here and not in the constructor to speed application
// loading time since this method is called in a background thread
if (mProviderClient == null) {
mProviderClient = mContext.getContentResolver().acquireContentProviderClient(PicasaContentProvider.AUTHORITY);
}
// Ensure that users are up to date. TODO: also listen for accounts
// changed broadcast.
PicasaService.requestSync(mContext, PicasaService.TYPE_USERS_ALBUMS, 0);
loadMediaSetsIntoFeed(feed, true);
}
public void shutdown() {
;
}
public void loadItemsForSet(final MediaFeed feed, final MediaSet parentSet, int rangeStart, int rangeEnd) {
if (parentSet == null) {
return;
} else {
// Return a list of items within an album.
addItemsToFeed(feed, parentSet, rangeStart, rangeEnd);
}
}
protected void loadMediaSetsIntoFeed(final MediaFeed feed, boolean sync) {
final HashMap<String, Boolean> accountsEnabled = getAccountStatus(mContext);
final ContentProviderClient client = mProviderClient;
if (client == null)
return;
try {
final EntrySchema albumSchema = AlbumEntry.SCHEMA;
final Cursor cursor = client.query(PicasaContentProvider.ALBUMS_URI, albumSchema.getProjection(), null, null,
DEFAULT_BUCKET_SORT_ORDER);
final AlbumEntry album = new AlbumEntry();
MediaSet mediaSet;
if (cursor.moveToFirst()) {
final int numAlbums = cursor.getCount();
final ArrayList<MediaSet> picasaSets = new ArrayList<MediaSet>(numAlbums);
do {
albumSchema.cursorToObject(cursor, album);
String userLowerCase = album.syncAccount.toLowerCase();
final Boolean accountEnabledObj = accountsEnabled.get(userLowerCase);
final boolean accountEnabled = (accountEnabledObj == null) ? false : accountEnabledObj.booleanValue();
if (accountEnabled) {
mediaSet = feed.getMediaSet(album.id);
if (mediaSet == null) {
mediaSet = feed.addMediaSet(album.id, this);
mediaSet.mName = album.title;
mediaSet.mEditUri = album.editUri;
mediaSet.generateTitle(true);
} else {
mediaSet.setNumExpectedItems(album.numPhotos);
}
mediaSet.mPicasaAlbumId = album.id;
mediaSet.mIsLocal = false;
mediaSet.mSyncPending = album.photosDirty;
picasaSets.add(mediaSet);
}
} while (cursor.moveToNext());
}
cursor.close();
} catch (RemoteException e) {
Log.e(TAG, "Error occurred loading albums");
}
}
private void addItemsToFeed(MediaFeed feed, MediaSet set, int start, int end) {
final ContentProviderClient client = mProviderClient;
Cursor cursor = null;
try {
// Query photos in the album.
final EntrySchema photosSchema = PhotoProjection.SCHEMA;
final String whereInAlbum = "album_id = " + Long.toString(set.mId);
cursor = client.query(PicasaContentProvider.PHOTOS_URI, photosSchema.getProjection(), whereInAlbum, null, null);
final PhotoProjection photo = new PhotoProjection();
int count = cursor.getCount();
if (count < end) {
end = count;
}
set.setNumExpectedItems(count);
set.generateTitle(true);
// Move to the next unread item.
final int newIndex = start + 1;
if (newIndex > count || !cursor.move(newIndex)) {
end = 0;
cursor.close();
set.updateNumExpectedItems();
set.generateTitle(true);
return;
}
if (set.mNumItemsLoaded == 0) {
photosSchema.cursorToObject(cursor, photo);
set.mMinTimestamp = photo.dateTaken;
cursor.moveToLast();
photosSchema.cursorToObject(cursor, photo);
set.mMinTimestamp = photo.dateTaken;
cursor.moveToFirst();
}
for (int i = 0; i < end; ++i) {
photosSchema.cursorToObject(cursor, photo);
final MediaItem item = new MediaItem();
item.mId = photo.id;
item.mEditUri = photo.editUri;
item.mMimeType = photo.contentType;
item.mDateTakenInMs = photo.dateTaken;
item.mLatitude = photo.latitude;
item.mLongitude = photo.longitude;
item.mThumbnailUri = photo.thumbnailUrl;
item.mScreennailUri = photo.screennailUrl;
item.mContentUri = photo.contentUrl;
item.mCaption = photo.title;
item.mWeblink = photo.htmlPageUrl;
item.mDescription = photo.summary;
item.mFilePath = item.mContentUri;
feed.addItemToMediaSet(item, set);
if (!cursor.moveToNext()) {
break;
}
}
} catch (Exception e) {
Log.e(TAG, "Error occurred loading photos for album " + set.mId);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
public boolean performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) {
try {
if (operation == MediaFeed.OPERATION_DELETE) {
ContentProviderClient client = mProviderClient;
for (int i = 0, numBuckets = mediaBuckets.size(); i != numBuckets; ++i) {
MediaBucket bucket = mediaBuckets.get(i);
ArrayList<MediaItem> items = bucket.mediaItems;
if (items == null) {
// Delete an album.
String albumUri = PicasaContentProvider.ALBUMS_URI + "/" + bucket.mediaSet.mId;
client.delete(Uri.parse(albumUri), null, null);
} else {
// Delete a set of photos.
for (int j = 0, numItems = items.size(); j != numItems; ++j) {
MediaItem item = items.get(j);
if (item != null) {
String itemUri = PicasaContentProvider.PHOTOS_URI + "/" + item.mId;
client.delete(Uri.parse(itemUri), null, null);
}
}
}
}
}
return true;
} catch (RemoteException e) {
return false;
}
}
public DiskCache getThumbnailCache() {
return sThumbnailCache;
}
/**
* The projection of PhotoEntry needed by the data source.
*/
private static final class PhotoProjection extends Entry {
public static final EntrySchema SCHEMA = new EntrySchema(PhotoProjection.class);
@Column("edit_uri")
public String editUri;
@Column("title")
public String title;
@Column("summary")
public String summary;
@Column("date_taken")
public long dateTaken;
@Column("latitude")
public double latitude;
@Column("longitude")
public double longitude;
@Column("thumbnail_url")
public String thumbnailUrl;
@Column("screennail_url")
public String screennailUrl;
@Column("content_url")
public String contentUrl;
@Column("content_type")
public String contentType;
@Column("html_page_url")
public String htmlPageUrl;
}
public String[] getDatabaseUris() {
return new String[] { PicasaContentProvider.ALBUMS_URI.toString(), PicasaContentProvider.PHOTOS_URI.toString()};
}
public void refresh(final MediaFeed feed, final String[] databaseUris) {
// Depending on what URI changed, we either need to update the mediasets or the mediaitems of a set.
if (databaseUris != null && databaseUris.length > 0) {
if (ArrayUtils.contains(databaseUris, PicasaContentProvider.ALBUMS_URI.toString())) {
// We need to refresh all mediasets of this datasource type.
final ArrayList<MediaSet> mediaSets = feed.getMediaSets();
final int numMediaSets = mediaSets.size();
for (int i = 0; i < numMediaSets; ++i) {
final MediaSet set = mediaSets.get(i);
if (set.mDataSource instanceof PicasaDataSource) {
set.mDataSource = this;
set.refresh();
}
}
} else if (ArrayUtils.contains(databaseUris, PicasaContentProvider.PHOTOS_URI.toString())) {
// We need to update just the one set that has these photos.
// This operation is not yet supported, so we might as well refresh everything.
final ArrayList<MediaSet> mediaSets = feed.getMediaSets();
final int numMediaSets = mediaSets.size();
for (int i = 0; i < numMediaSets; ++i) {
final MediaSet set = mediaSets.get(i);
if (set.mDataSource instanceof PicasaDataSource) {
set.mDataSource = this;
set.refresh();
}
}
}
}
}
}