• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.cooliris.media;
18 
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 
23 import android.accounts.Account;
24 import android.content.ContentProviderClient;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.database.ContentObserver;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Handler;
31 import android.os.RemoteException;
32 import android.util.Log;
33 
34 import com.cooliris.app.App;
35 import com.cooliris.picasa.AlbumEntry;
36 import com.cooliris.picasa.Entry;
37 import com.cooliris.picasa.EntrySchema;
38 import com.cooliris.picasa.PhotoEntry;
39 import com.cooliris.picasa.PicasaApi;
40 import com.cooliris.picasa.PicasaContentProvider;
41 import com.cooliris.picasa.PicasaService;
42 
43 public final class PicasaDataSource implements DataSource {
44     private static final String TAG = "PicasaDataSource";
45     public static final DiskCache sThumbnailCache = new DiskCache("picasa-thumbs");
46     private static final String DEFAULT_BUCKET_SORT_ORDER = AlbumEntry.Columns.USER + ", " + AlbumEntry.Columns.DATE_PUBLISHED
47             + " DESC";
48 
49     private ContentProviderClient mProviderClient;
50     private final Context mContext;
51 
PicasaDataSource(final Context context)52     public PicasaDataSource(final Context context) {
53         mContext = context;
54     }
55 
getAccountStatus(final Context context)56     public static final HashMap<String, Boolean> getAccountStatus(final Context context) {
57         final Account[] accounts = PicasaApi.getAccounts(context);
58         int numAccounts = accounts.length;
59         HashMap<String, Boolean> accountsEnabled = new HashMap<String, Boolean>(numAccounts);
60         for (int i = 0; i < numAccounts; ++i) {
61             Account account = accounts[i];
62             boolean isEnabled = ContentResolver.getSyncAutomatically(account, PicasaContentProvider.AUTHORITY);
63             String username = PicasaApi.canonicalizeUsername(account.name);
64             accountsEnabled.put(username, new Boolean(isEnabled));
65         }
66         return accountsEnabled;
67     }
68 
loadMediaSets(final MediaFeed feed)69     public void loadMediaSets(final MediaFeed feed) {
70         // We do this here and not in the constructor to speed application
71         // loading time since this method is called in a background thread
72         if (mProviderClient == null) {
73             mProviderClient = mContext.getContentResolver().acquireContentProviderClient(PicasaContentProvider.AUTHORITY);
74         }
75 
76         // Ensure that users are up to date. TODO: also listen for accounts
77         // changed broadcast.
78         PicasaService.requestSync(mContext, PicasaService.TYPE_USERS_ALBUMS, 0);
79         loadMediaSetsIntoFeed(feed, true);
80     }
81 
shutdown()82     public void shutdown() {
83         ;
84     }
85 
loadItemsForSet(final MediaFeed feed, final MediaSet parentSet, int rangeStart, int rangeEnd)86     public void loadItemsForSet(final MediaFeed feed, final MediaSet parentSet, int rangeStart, int rangeEnd) {
87         if (parentSet == null) {
88             return;
89         } else {
90             // Return a list of items within an album.
91             addItemsToFeed(feed, parentSet, rangeStart, rangeEnd);
92         }
93     }
94 
loadMediaSetsIntoFeed(final MediaFeed feed, boolean sync)95     protected void loadMediaSetsIntoFeed(final MediaFeed feed, boolean sync) {
96         final HashMap<String, Boolean> accountsEnabled = getAccountStatus(mContext);
97         final ContentProviderClient client = mProviderClient;
98         if (client == null)
99             return;
100         try {
101             final EntrySchema albumSchema = AlbumEntry.SCHEMA;
102             final Cursor cursor = client.query(PicasaContentProvider.ALBUMS_URI, albumSchema.getProjection(), null, null,
103                     DEFAULT_BUCKET_SORT_ORDER);
104             final AlbumEntry album = new AlbumEntry();
105             MediaSet mediaSet;
106             if (cursor.moveToFirst()) {
107                 final int numAlbums = cursor.getCount();
108                 final ArrayList<MediaSet> picasaSets = new ArrayList<MediaSet>(numAlbums);
109                 do {
110                     albumSchema.cursorToObject(cursor, album);
111                     String userLowerCase = album.syncAccount.toLowerCase();
112                     final Boolean accountEnabledObj = accountsEnabled.get(userLowerCase);
113                     final boolean accountEnabled = (accountEnabledObj == null) ? false : accountEnabledObj.booleanValue();
114                     if (accountEnabled) {
115                         mediaSet = feed.getMediaSet(album.id);
116                         if (mediaSet == null) {
117                             mediaSet = feed.addMediaSet(album.id, this);
118                             mediaSet.mName = album.title;
119                             mediaSet.mEditUri = album.editUri;
120                             mediaSet.generateTitle(true);
121                         } else {
122                             mediaSet.setNumExpectedItems(album.numPhotos);
123                         }
124                         mediaSet.mPicasaAlbumId = album.id;
125                         mediaSet.mIsLocal = false;
126                         mediaSet.mSyncPending = album.photosDirty;
127                         picasaSets.add(mediaSet);
128                     }
129                 } while (cursor.moveToNext());
130             }
131             cursor.close();
132         } catch (RemoteException e) {
133             Log.e(TAG, "Error occurred loading albums");
134         }
135     }
136 
addItemsToFeed(MediaFeed feed, MediaSet set, int start, int end)137     private void addItemsToFeed(MediaFeed feed, MediaSet set, int start, int end) {
138         final ContentProviderClient client = mProviderClient;
139         Cursor cursor = null;
140         try {
141             // Query photos in the album.
142             final EntrySchema photosSchema = PhotoProjection.SCHEMA;
143             final String whereInAlbum = "album_id = " + Long.toString(set.mId);
144             cursor = client.query(PicasaContentProvider.PHOTOS_URI, photosSchema.getProjection(), whereInAlbum, null, null);
145             final PhotoProjection photo = new PhotoProjection();
146             int count = cursor.getCount();
147             if (count < end) {
148                 end = count;
149             }
150             set.setNumExpectedItems(count);
151             set.generateTitle(true);
152             // Move to the next unread item.
153             final int newIndex = start + 1;
154             if (newIndex > count || !cursor.move(newIndex)) {
155                 end = 0;
156                 cursor.close();
157                 set.updateNumExpectedItems();
158                 set.generateTitle(true);
159                 return;
160             }
161             if (set.mNumItemsLoaded == 0) {
162                 photosSchema.cursorToObject(cursor, photo);
163                 set.mMinTimestamp = photo.dateTaken;
164                 cursor.moveToLast();
165                 photosSchema.cursorToObject(cursor, photo);
166                 set.mMinTimestamp = photo.dateTaken;
167                 cursor.moveToFirst();
168             }
169             for (int i = 0; i < end; ++i) {
170                 photosSchema.cursorToObject(cursor, photo);
171                 final MediaItem item = new MediaItem();
172                 item.mId = photo.id;
173                 item.mEditUri = photo.editUri;
174                 item.mMimeType = photo.contentType;
175                 item.mDateTakenInMs = photo.dateTaken;
176                 item.mLatitude = photo.latitude;
177                 item.mLongitude = photo.longitude;
178                 item.mThumbnailUri = photo.thumbnailUrl;
179                 item.mScreennailUri = photo.screennailUrl;
180                 item.mContentUri = photo.contentUrl;
181                 item.mCaption = photo.title;
182                 item.mWeblink = photo.htmlPageUrl;
183                 item.mDescription = photo.summary;
184                 item.mFilePath = item.mContentUri;
185                 feed.addItemToMediaSet(item, set);
186                 if (!cursor.moveToNext()) {
187                     break;
188                 }
189             }
190         } catch (Exception e) {
191             Log.e(TAG, "Error occurred loading photos for album " + set.mId);
192         } finally {
193             if (cursor != null) {
194                 cursor.close();
195             }
196         }
197     }
198 
performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data)199     public boolean performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) {
200         try {
201             if (operation == MediaFeed.OPERATION_DELETE) {
202                 ContentProviderClient client = mProviderClient;
203                 for (int i = 0, numBuckets = mediaBuckets.size(); i != numBuckets; ++i) {
204                     MediaBucket bucket = mediaBuckets.get(i);
205                     ArrayList<MediaItem> items = bucket.mediaItems;
206                     if (items == null) {
207                         // Delete an album.
208                         String albumUri = PicasaContentProvider.ALBUMS_URI + "/" + bucket.mediaSet.mId;
209                         client.delete(Uri.parse(albumUri), null, null);
210                     } else {
211                         // Delete a set of photos.
212                         for (int j = 0, numItems = items.size(); j != numItems; ++j) {
213                             MediaItem item = items.get(j);
214                             if (item != null) {
215                                 String itemUri = PicasaContentProvider.PHOTOS_URI + "/" + item.mId;
216                                 client.delete(Uri.parse(itemUri), null, null);
217                             }
218                         }
219                     }
220                 }
221             }
222             return true;
223         } catch (RemoteException e) {
224             return false;
225         }
226     }
227 
getThumbnailCache()228     public DiskCache getThumbnailCache() {
229         return sThumbnailCache;
230     }
231 
232     /**
233      * The projection of PhotoEntry needed by the data source.
234      */
235     private static final class PhotoProjection extends Entry {
236         public static final EntrySchema SCHEMA = new EntrySchema(PhotoProjection.class);
237         @Column("edit_uri")
238         public String editUri;
239         @Column("title")
240         public String title;
241         @Column("summary")
242         public String summary;
243         @Column("date_taken")
244         public long dateTaken;
245         @Column("latitude")
246         public double latitude;
247         @Column("longitude")
248         public double longitude;
249         @Column("thumbnail_url")
250         public String thumbnailUrl;
251         @Column("screennail_url")
252         public String screennailUrl;
253         @Column("content_url")
254         public String contentUrl;
255         @Column("content_type")
256         public String contentType;
257         @Column("html_page_url")
258         public String htmlPageUrl;
259     }
260 
getDatabaseUris()261     public String[] getDatabaseUris() {
262         return new String[] { PicasaContentProvider.ALBUMS_URI.toString(), PicasaContentProvider.PHOTOS_URI.toString()};
263     }
264 
refresh(final MediaFeed feed, final String[] databaseUris)265     public void refresh(final MediaFeed feed, final String[] databaseUris) {
266         // Depending on what URI changed, we either need to update the mediasets or the mediaitems of a set.
267         if (databaseUris != null && databaseUris.length > 0) {
268             if (ArrayUtils.contains(databaseUris, PicasaContentProvider.ALBUMS_URI.toString())) {
269                 // We need to refresh all mediasets of this datasource type.
270                 final ArrayList<MediaSet> mediaSets = feed.getMediaSets();
271                 final int numMediaSets = mediaSets.size();
272                 for (int i = 0; i < numMediaSets; ++i) {
273                     final MediaSet set = mediaSets.get(i);
274                     if (set.mDataSource instanceof PicasaDataSource) {
275                         set.mDataSource = this;
276                         set.refresh();
277                     }
278                 }
279             } else if (ArrayUtils.contains(databaseUris, PicasaContentProvider.PHOTOS_URI.toString())) {
280                 // We need to update just the one set that has these photos.
281                 // This operation is not yet supported, so we might as well refresh everything.
282                 final ArrayList<MediaSet> mediaSets = feed.getMediaSets();
283                 final int numMediaSets = mediaSets.size();
284                 for (int i = 0; i < numMediaSets; ++i) {
285                     final MediaSet set = mediaSets.get(i);
286                     if (set.mDataSource instanceof PicasaDataSource) {
287                         set.mDataSource = this;
288                         set.refresh();
289                     }
290                 }
291             }
292         }
293     }
294 }
295