1 package com.cooliris.media;
3 import java.util.ArrayList;
4 import java.util.HashMap;
6 import android.accounts.Account;
7 import android.content.ContentProviderClient;
8 import android.content.ContentResolver;
9 import android.content.Context;
10 import android.database.ContentObserver;
11 import android.database.Cursor;
12 import android.net.Uri;
13 import android.os.Handler;
14 import android.os.RemoteException;
15 import android.util.Log;
17 import com.cooliris.picasa.AlbumEntry;
18 import com.cooliris.picasa.Entry;
19 import com.cooliris.picasa.EntrySchema;
20 import com.cooliris.picasa.PicasaApi;
21 import com.cooliris.picasa.PicasaContentProvider;
22 import com.cooliris.picasa.PicasaService;
24 public final class PicasaDataSource implements DataSource {
25 private static final String TAG = "PicasaDataSource";
26 public static final DiskCache sThumbnailCache = new DiskCache("picasa-thumbs");
27 private static final String DEFAULT_BUCKET_SORT_ORDER = AlbumEntry.Columns.USER + ", " + AlbumEntry.Columns.DATE_PUBLISHED
30 private ContentProviderClient mProviderClient;
31 private final Context mContext;
32 private ContentObserver mAlbumObserver;
33 private HashMap<String, Boolean> mAccountEnabled = new HashMap<String, Boolean>();
35 public PicasaDataSource(Context context) {
39 public void loadMediaSets(final MediaFeed feed) {
40 if (mProviderClient == null) {
41 mProviderClient = mContext.getContentResolver().acquireContentProviderClient(PicasaContentProvider.AUTHORITY);
43 // Force permission dialog to be displayed if necessary. TODO: remove this after signed by Google.
44 PicasaApi.getAccounts(mContext);
46 // Ensure that users are up to date. TODO: also listen for accounts changed broadcast.
47 PicasaService.requestSync(mContext, PicasaService.TYPE_USERS_ALBUMS, 0);
48 Handler handler = ((Gallery) mContext).getHandler();
49 ContentObserver albumObserver = new ContentObserver(handler) {
50 public void onChange(boolean selfChange) {
51 loadMediaSetsIntoFeed(feed, true);
54 mAlbumObserver = albumObserver;
55 loadMediaSetsIntoFeed(feed, true);
58 ContentResolver cr = mContext.getContentResolver();
59 cr.registerContentObserver(PicasaContentProvider.ALBUMS_URI, false, mAlbumObserver);
60 cr.registerContentObserver(PicasaContentProvider.PHOTOS_URI, false, mAlbumObserver);
63 public void shutdown() {
64 if (mAlbumObserver != null) {
65 ContentResolver cr = mContext.getContentResolver();
66 cr.unregisterContentObserver(mAlbumObserver);
70 public void loadItemsForSet(final MediaFeed feed, final MediaSet parentSet, int rangeStart, int rangeEnd) {
71 if (parentSet == null) {
74 // Return a list of items within an album.
75 addItemsToFeed(feed, parentSet, rangeStart, rangeEnd);
79 protected void loadMediaSetsIntoFeed(final MediaFeed feed, boolean sync) {
80 Account[] accounts = PicasaApi.getAccounts(mContext);
81 int numAccounts = accounts.length;
82 for (int i = 0; i < numAccounts; ++i) {
83 Account account = accounts[i];
84 boolean isEnabled = ContentResolver.getSyncAutomatically(account, PicasaContentProvider.AUTHORITY);
85 String username = account.name;
86 if (username.contains("@gmail.") || username.contains("@googlemail.")) {
87 // Strip the domain from GMail accounts for canonicalization. TODO: is there an official way?
88 username = username.substring(0, username.indexOf('@'));
90 mAccountEnabled.put(username, new Boolean(isEnabled));
92 ContentProviderClient client = mProviderClient;
96 EntrySchema albumSchema = AlbumEntry.SCHEMA;
97 Cursor cursor = client.query(PicasaContentProvider.ALBUMS_URI, albumSchema.getProjection(), null, null,
98 DEFAULT_BUCKET_SORT_ORDER);
99 AlbumEntry album = new AlbumEntry();
101 if (cursor.moveToFirst()) {
102 int numAlbums = cursor.getCount();
103 ArrayList<MediaSet> picasaSets = new ArrayList<MediaSet>(numAlbums);
105 albumSchema.cursorToObject(cursor, album);
106 Boolean accountEnabledObj = mAccountEnabled.get(album.user);
107 boolean accountEnabled = (accountEnabledObj == null) ? false : accountEnabledObj.booleanValue();
108 if (accountEnabled) {
109 mediaSet = feed.getMediaSet(album.id);
110 if (mediaSet == null) {
111 mediaSet = feed.addMediaSet(album.id, this);
112 mediaSet.mName = album.title;
113 mediaSet.mEditUri = album.editUri;
114 mediaSet.generateTitle(true);
116 mediaSet.setNumExpectedItems(album.numPhotos);
118 mediaSet.mPicasaAlbumId = album.id;
119 mediaSet.mSyncPending = album.photosDirty;
120 picasaSets.add(mediaSet);
122 } while (cursor.moveToNext());
125 } catch (RemoteException e) {
126 Log.e(TAG, "Error occurred loading albums");
130 private void addItemsToFeed(MediaFeed feed, MediaSet set, int start, int end) {
131 ContentProviderClient client = mProviderClient;
132 Cursor cursor = null;
134 // Query photos in the album.
135 EntrySchema photosSchema = PhotoProjection.SCHEMA;
136 String whereInAlbum = "album_id = " + Long.toString(set.mId);
137 cursor = client.query(PicasaContentProvider.PHOTOS_URI, photosSchema.getProjection(), whereInAlbum, null, null);
138 PhotoProjection photo = new PhotoProjection();
139 int count = cursor.getCount();
143 set.setNumExpectedItems(count);
144 set.generateTitle(true);
145 // Move to the next unread item.
146 int newIndex = start + 1;
147 if (newIndex > count || !cursor.move(newIndex)) {
150 set.updateNumExpectedItems();
151 set.generateTitle(true);
154 if (set.mNumItemsLoaded == 0) {
155 photosSchema.cursorToObject(cursor, photo);
156 set.mMinTimestamp = photo.dateTaken;
158 photosSchema.cursorToObject(cursor, photo);
159 set.mMinTimestamp = photo.dateTaken;
160 cursor.moveToFirst();
162 for (int i = 0; i < end; ++i) {
163 photosSchema.cursorToObject(cursor, photo);
164 MediaItem item = new MediaItem();
166 item.mEditUri = photo.editUri;
167 item.mMimeType = photo.contentType;
168 item.mDateTakenInMs = photo.dateTaken;
169 item.mLatitude = photo.latitude;
170 item.mLongitude = photo.longitude;
171 item.mThumbnailUri = photo.thumbnailUrl;
172 item.mScreennailUri = photo.screennailUrl;
173 item.mContentUri = photo.contentUrl;
174 item.mCaption = photo.title;
175 item.mWeblink = photo.htmlPageUrl;
176 item.mDescription = photo.summary;
177 item.mFilePath = item.mContentUri;
178 feed.addItemToMediaSet(item, set);
179 if (!cursor.moveToNext()) {
183 } catch (Exception e) {
184 Log.e(TAG, "Error occurred loading photos for album " + set.mId);
186 if (cursor != null) {
192 public void prime(final MediaItem item) {
195 public boolean performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) {
197 if (operation == MediaFeed.OPERATION_DELETE) {
198 ContentProviderClient client = mProviderClient;
199 for (int i = 0, numBuckets = mediaBuckets.size(); i != numBuckets; ++i) {
200 MediaBucket bucket = mediaBuckets.get(i);
201 ArrayList<MediaItem> items = bucket.mediaItems;
204 String albumUri = PicasaContentProvider.ALBUMS_URI + "/" + bucket.mediaSet.mId;
205 client.delete(Uri.parse(albumUri), null, null);
207 // Delete a set of photos.
208 for (int j = 0, numItems = items.size(); j != numItems; ++j) {
209 MediaItem item = items.get(j);
211 String itemUri = PicasaContentProvider.PHOTOS_URI + "/" + item.mId;
212 client.delete(Uri.parse(itemUri), null, null);
219 } catch (RemoteException e) {
224 public DiskCache getThumbnailCache() {
225 return sThumbnailCache;
229 * The projection of PhotoEntry needed by the data source.
231 private static final class PhotoProjection extends Entry {
232 public static final EntrySchema SCHEMA = new EntrySchema(PhotoProjection.class);
234 public String editUri;
238 public String summary;
239 @Column("date_taken")
240 public long dateTaken;
242 public double latitude;
244 public double longitude;
245 @Column("thumbnail_url")
246 public String thumbnailUrl;
247 @Column("screennail_url")
248 public String screennailUrl;
249 @Column("content_url")
250 public String contentUrl;
251 @Column("content_type")
252 public String contentType;
253 @Column("html_page_url")
254 public String htmlPageUrl;