OSDN Git Service

01ab1ed83234e38779f8d6cfe4898b197001f1a7
[android-x86/packages-apps-Gallery2.git] / src / com / cooliris / picasa / PicasaApi.java
1 package com.cooliris.picasa;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5
6 import org.apache.http.HttpStatus;
7 import org.xml.sax.SAXException;
8
9 import android.accounts.Account;
10 import android.accounts.AccountManager;
11 import android.accounts.AuthenticatorException;
12 import android.accounts.OperationCanceledException;
13 import android.app.Activity;
14 import android.content.Context;
15 import android.content.SyncResult;
16 import android.net.Uri;
17 import android.os.Bundle;
18 import android.util.Log;
19 import android.util.Xml;
20
21 public final class PicasaApi {
22     public static final int RESULT_OK = 0;
23     public static final int RESULT_NOT_MODIFIED = 1;
24     public static final int RESULT_ERROR = 2;
25
26     private static final String TAG = "PicasaAPI";
27     private static final String BASE_URL = "http://picasaweb.google.com/data/feed/api/";
28     private static final String BASE_QUERY_STRING;
29
30     static {
31         // Build the base query string using screen dimensions.
32         final StringBuilder query = new StringBuilder("?imgmax=1024&max-results=1000&thumbsize=");
33         final String thumbnailSize = "144u,";
34         final String screennailSize = "1024u";
35         query.append(thumbnailSize);
36         query.append(screennailSize);
37         BASE_QUERY_STRING = query.toString() + "&visibility=visible";
38     }
39
40     private final GDataClient mClient;
41     private final GDataClient.Operation mOperation = new GDataClient.Operation();
42     private final GDataParser mParser = new GDataParser();
43     private final AlbumEntry mAlbumInstance = new AlbumEntry();
44     private final PhotoEntry mPhotoInstance = new PhotoEntry();
45     private AuthAccount mAuth;
46
47     public static final class AuthAccount {
48         public final String user;
49         public final String authToken;
50         public final Account account;
51
52         public AuthAccount(String user, String authToken, Account account) {
53             this.user = user;
54             this.authToken = authToken;
55             this.account = account;
56         }
57     }
58
59     public static Account[] getAccounts(Context context) {
60         // Return the list of accounts supporting the Picasa GData service.
61         AccountManager accountManager = AccountManager.get(context);
62         Account[] accounts = {};
63         try {
64             accounts = accountManager.getAccountsByTypeAndFeatures(PicasaService.ACCOUNT_TYPE,
65                     new String[] { PicasaService.FEATURE_SERVICE_NAME }, null, null).getResult();
66         } catch (OperationCanceledException e) {
67         } catch (AuthenticatorException e) {
68         } catch (IOException e) {
69         }
70         return accounts;
71     }
72
73     public static AuthAccount[] getAuthenticatedAccounts(Context context) {
74         AccountManager accountManager = AccountManager.get(context);
75         Account[] accounts = getAccounts(context);
76         int numAccounts = accounts.length;
77
78         ArrayList<AuthAccount> authAccounts = new ArrayList<AuthAccount>(numAccounts);
79         for (int i = 0; i != numAccounts; ++i) {
80             Account account = accounts[i];
81             String authToken;
82             try {
83                 // Get the token without user interaction.
84                 authToken = accountManager.blockingGetAuthToken(account, PicasaService.SERVICE_NAME, true);
85
86                 // TODO: Remove this once the build is signed by Google, since
87                 // we will always have permission.
88                 // This code requests permission from the user explicitly.
89                 if (context instanceof Activity) {
90                     Bundle bundle = accountManager.getAuthToken(account, PicasaService.SERVICE_NAME, null, (Activity) context,
91                             null, null).getResult();
92                     authToken = bundle.getString("authtoken");
93                     PicasaService.requestSync(context, PicasaService.TYPE_USERS_ALBUMS, -1);
94                 }
95
96                 // Add the account information to the list of accounts.
97                 if (authToken != null) {
98                     String username = account.name;
99                     if (username.contains("@gmail.") || username.contains("@googlemail.")) {
100                         // Strip the domain from GMail accounts for
101                         // canonicalization. TODO: is there an official way?
102                         username = username.substring(0, username.indexOf('@'));
103                     }
104                     authAccounts.add(new AuthAccount(username, authToken, account));
105                 }
106             } catch (OperationCanceledException e) {
107             } catch (IOException e) {
108             } catch (AuthenticatorException e) {
109             }
110         }
111         AuthAccount[] authArray = new AuthAccount[authAccounts.size()];
112         authAccounts.toArray(authArray);
113         return authArray;
114     }
115
116     public PicasaApi() {
117         mClient = new GDataClient();
118     }
119
120     public void setAuth(AuthAccount auth) {
121         mAuth = auth;
122         synchronized (mClient) {
123             mClient.setAuthToken(auth.authToken);
124         }
125     }
126
127     public int getAlbums(AccountManager accountManager, SyncResult syncResult, UserEntry user, GDataParser.EntryHandler handler) {
128         // Construct the query URL for user albums.
129         StringBuilder builder = new StringBuilder(BASE_URL);
130         builder.append("user/");
131         builder.append(Uri.encode(mAuth.user));
132         builder.append(BASE_QUERY_STRING);
133         builder.append("&kind=album");
134         try {
135             // Send the request.
136             synchronized (mOperation) {
137                 GDataClient.Operation operation = mOperation;
138                 operation.inOutEtag = user.albumsEtag;
139                 boolean retry = false;
140                 int numRetries = 1;
141                 do {
142                     retry = false;
143                     synchronized (mClient) {
144                         mClient.get(builder.toString(), operation);
145                     }
146                     switch (operation.outStatus) {
147                     case HttpStatus.SC_OK:
148                         break;
149                     case HttpStatus.SC_NOT_MODIFIED:
150                         return RESULT_NOT_MODIFIED;
151                     case HttpStatus.SC_FORBIDDEN:
152                     case HttpStatus.SC_UNAUTHORIZED:
153                         if (!retry) {
154                             accountManager.invalidateAuthToken(PicasaService.ACCOUNT_TYPE, mAuth.authToken);
155                             retry = true;
156                         }
157                         if (numRetries == 0) {
158                             ++syncResult.stats.numAuthExceptions;
159                         }
160                     default:
161                         Log.i(TAG, "getAlbums uri " + builder.toString());
162                         Log.e(TAG, "getAlbums: unexpected status code " + operation.outStatus + " data: "
163                                 + operation.outBody.toString());
164                         ++syncResult.stats.numIoExceptions;
165                         return RESULT_ERROR;
166                     }
167                     --numRetries;
168                 } while (retry && numRetries >= 0);
169
170                 // Store the new ETag for the user/albums feed.
171                 user.albumsEtag = operation.inOutEtag;
172
173                 // Parse the response.
174                 synchronized (mParser) {
175                     GDataParser parser = mParser;
176                     parser.setEntry(mAlbumInstance);
177                     parser.setHandler(handler);
178                     Xml.parse(operation.outBody, Xml.Encoding.UTF_8, parser);
179                 }
180             }
181             return RESULT_OK;
182         } catch (IOException e) {
183             Log.e(TAG, "getAlbums: " + e);
184             ++syncResult.stats.numIoExceptions;
185         } catch (SAXException e) {
186             Log.e(TAG, "getAlbums: " + e);
187             ++syncResult.stats.numParseExceptions;
188         }
189         return RESULT_ERROR;
190     }
191
192     public int getAlbumPhotos(AccountManager accountManager, SyncResult syncResult, AlbumEntry album,
193             GDataParser.EntryHandler handler) {
194         // Construct the query URL for user albums.
195         StringBuilder builder = new StringBuilder(BASE_URL);
196         builder.append("user/");
197         builder.append(Uri.encode(mAuth.user));
198         builder.append("/albumid/");
199         builder.append(album.id);
200         builder.append(BASE_QUERY_STRING);
201         builder.append("&kind=photo");
202         try {
203             // Send the request.
204             synchronized (mOperation) {
205                 GDataClient.Operation operation = mOperation;
206                 operation.inOutEtag = album.photosEtag;
207                 boolean retry = false;
208                 int numRetries = 1;
209                 do {
210                     retry = false;
211                     synchronized (mClient) {
212                         mClient.get(builder.toString(), operation);
213                     }
214                     switch (operation.outStatus) {
215                     case HttpStatus.SC_OK:
216                         break;
217                     case HttpStatus.SC_NOT_MODIFIED:
218                         return RESULT_NOT_MODIFIED;
219                     case HttpStatus.SC_FORBIDDEN:
220                     case HttpStatus.SC_UNAUTHORIZED:
221                         // We need to reset the authtoken and retry only once.
222                         if (!retry) {
223                             retry = true;
224                             accountManager.invalidateAuthToken(PicasaService.SERVICE_NAME, mAuth.authToken);
225                         }
226                         if (numRetries == 0) {
227                             ++syncResult.stats.numAuthExceptions;
228                         }
229                         break;
230                     default:
231                         Log.e(TAG, "getAlbumPhotos: " + builder.toString() + ", unexpected status code " + operation.outStatus);
232                         ++syncResult.stats.numIoExceptions;
233                         return RESULT_ERROR;
234                     }
235                     --numRetries;
236                 } while (retry && numRetries >= 0);
237
238                 // Store the new ETag for the album/photos feed.
239                 album.photosEtag = operation.inOutEtag;
240
241                 // Parse the response.
242                 synchronized (mParser) {
243                     GDataParser parser = mParser;
244                     parser.setEntry(mPhotoInstance);
245                     parser.setHandler(handler);
246                     Xml.parse(operation.outBody, Xml.Encoding.UTF_8, parser);
247                 }
248             }
249             return RESULT_OK;
250         } catch (IOException e) {
251             Log.e(TAG, "getAlbumPhotos: " + e);
252             ++syncResult.stats.numIoExceptions;
253             e.printStackTrace();
254         } catch (SAXException e) {
255             Log.e(TAG, "getAlbumPhotos: " + e);
256             ++syncResult.stats.numParseExceptions;
257             e.printStackTrace();
258         }
259         return RESULT_ERROR;
260     }
261
262     public int deleteEntry(String editUri) {
263         try {
264             synchronized (mOperation) {
265                 GDataClient.Operation operation = mOperation;
266                 operation.inOutEtag = null;
267                 synchronized (mClient) {
268                     mClient.delete(editUri, operation);
269                 }
270                 if (operation.outStatus == 200) {
271                     return RESULT_OK;
272                 } else {
273                     Log.e(TAG, "deleteEntry: failed with status code " + operation.outStatus);
274                 }
275             }
276         } catch (IOException e) {
277             Log.e(TAG, "deleteEntry: " + e);
278         }
279         return RESULT_ERROR;
280     }
281
282     /**
283      * Column names shared by multiple entry kinds.
284      */
285     public static class Columns {
286         public static final String _ID = "_id";
287         public static final String SYNC_ACCOUNT = "sync_account";
288         public static final String EDIT_URI = "edit_uri";
289         public static final String TITLE = "title";
290         public static final String SUMMARY = "summary";
291         public static final String DATE_PUBLISHED = "date_published";
292         public static final String DATE_UPDATED = "date_updated";
293         public static final String DATE_EDITED = "date_edited";
294         public static final String THUMBNAIL_URL = "thumbnail_url";
295         public static final String HTML_PAGE_URL = "html_page_url";
296     }
297 }