OSDN Git Service

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