2 * Copyright (C) 2010 Prasanta Paul, http://prasanta-paul.blogspot.com
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
19 import java.util.ArrayList;
\r
20 import java.util.HashMap;
\r
22 import com.pras.auth.Account;
\r
23 import com.pras.auth.Authenticator;
\r
24 import com.pras.auth.BasicAuthenticatorImpl;
\r
25 import com.pras.conn.HttpConHandler;
\r
26 import com.pras.conn.Response;
\r
27 import com.pras.sp.Entry;
\r
28 import com.pras.sp.Feed;
\r
29 import com.pras.sp.ParseFeed;
\r
33 * It is a SpreadSheet Generator Class. It accepts Gmail User ID and PassWord
\r
34 * to generate Authentication Token.
\r
36 * It uses 2 Google APIs-
\r
38 * SpreadSheet Create/Delete - <a href="http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html">Google Document API</a>
\r
40 * WorkSheet Create/Delete, Record Add - <a href="http://code.google.com/apis/spreadsheets/data/3.0/developers_guide.html">Google SpreadSheet API</a>
\r
42 * @author Prasanta Paul
\r
45 public class SpreadSheetFactory {
\r
47 private String TAG = "SpreadSheetFactory";
\r
49 // private String userName;
\r
50 // private String password;
\r
51 public static String authToken;
\r
53 //private final String GOOGLE_CLIENT_LOGIN_URL = "https://www.google.com/accounts/ClientLogin";
\r
55 private final String SP_GET_LIST_URL = "https://spreadsheets.google.com/feeds/spreadsheets/private/full";
\r
56 private final String DOCUMENT_LIST_API_URL = "https://docs.google.com/feeds/default/private/full";
\r
59 * Google service name (Reference)-
\r
60 * http://code.google.com/apis/gdata/faq.html
\r
62 final static String SPREADSHEET_API_SERVICE_NAME = "wise";
\r
63 final static String DOCUMENT_LIST_API_SERVICE_NAME = "writely";
\r
66 * List of presently stored SpreadSheets
\r
68 private ArrayList<SpreadSheet> spreadSheets = new ArrayList<SpreadSheet>();
\r
70 private Authenticator authenticator = null;
\r
72 private static SpreadSheetFactory factory;
\r
75 * This will return an existing SpreadSheetFactory instance or null
\r
78 * Make sure you have previously called getInstance(String userName, String password)
\r
82 public static SpreadSheetFactory getInstance(){
\r
83 return getInstance(null, null);
\r
87 * This will create SpreadSheetFactory Instance with valid User ID (e.g. abc@gmail.com) and password
\r
89 * @param userName Gmail account id e.g. abc@gmail.com
\r
90 * @param password Gmail account password
\r
93 public static SpreadSheetFactory getInstance(String email, String password){
\r
94 if(factory == null){
\r
95 if(email != null && password != null){
\r
96 Account account = new Account();
\r
97 account.setEmail(email);
\r
98 account.setPassword(password);
\r
99 BasicAuthenticatorImpl basicAuth = new BasicAuthenticatorImpl(account);
\r
101 factory = new SpreadSheetFactory(basicAuth);
\r
104 throw new IllegalAccessError("Missing Account Info. Please use getInstance(String email, String password) or getInstance(Authenticator authenticator)");
\r
111 * This will create SpreadSheetFactory Instance using your custom Authenticatior.
\r
113 * Use this if you want to use your custom Authenticator e.g. in Android you can use AccountManager
\r
114 * to create a custom Authenticator.
\r
116 * @param authenticator Your Custom Authenticator.
\r
119 public static SpreadSheetFactory getInstance(Authenticator authenticator){
\r
120 if(authenticator == null){
\r
121 throw new IllegalAccessError("No Authenticator defined");
\r
123 if(factory == null)
\r
124 factory = new SpreadSheetFactory(authenticator);
\r
129 * Provide Gmail user id and password. Use SpreadSheetFactory instance
\r
130 * to generate create/list/delete SpreadSheet
\r
135 // private SpreadSheetFactory(String userName, String password){
\r
136 // this.userName = userName;
\r
137 // this.password = password;
\r
140 private SpreadSheetFactory(Authenticator authenticator){
\r
141 this.authenticator = authenticator;
\r
145 * Deallocate SpreadSheetFactory instance
\r
147 public void flushMe(){
\r
149 if(spreadSheets != null)
\r
150 spreadSheets.clear();
\r
151 spreadSheets = null;
\r
153 authenticator = null;
\r
157 *Create SpreadSheet with the given name
\r
159 * @param spName SpreadSheet name
\r
161 public void createSpreadSheet(String spName){
\r
162 // login for Document List API
\r
163 login(DOCUMENT_LIST_API_SERVICE_NAME);
\r
165 // Create a SpreadSheet
\r
166 String postData = "<?xml version='1.0' encoding='UTF-8'?>" +
\r
167 "<entry xmlns='http://www.w3.org/2005/Atom'>"+
\r
168 "<category scheme='http://schemas.google.com/g/2005#kind'"+
\r
169 " term='http://schemas.google.com/docs/2007#spreadsheet'/>"+
\r
170 "<title>"+ spName +"</title>"+
\r
173 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
174 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ authToken);
\r
175 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
176 httpHeaders.put(HttpConHandler.CONTENT_LENGTH_HTTP_HEADER, ""+ postData.length());
\r
177 httpHeaders.put(HttpConHandler.CONTENT_TYPE_HTTP_HEADER, "application/atom+xml");
\r
180 HttpConHandler http = new HttpConHandler();
\r
181 http.doConnect(DOCUMENT_LIST_API_URL, HttpConHandler.HTTP_POST, httpHeaders, postData);
\r
183 // login for SpreadSheet API
\r
184 // revert back to SpreadSheet Auth Token
\r
185 login(SPREADSHEET_API_SERVICE_NAME);
\r
189 * Delete a SpreadSheet
\r
191 * @param resID Resource ID of the SpreadSheet you want to Delete
\r
193 public void deleteSpreadSheet(String resID){
\r
195 // login for Document List API
\r
196 login(DOCUMENT_LIST_API_SERVICE_NAME);
\r
198 // Delete HTTP request
\r
199 String url = "https://docs.google.com/feeds/default/private/full/"+ resID +"?delete=true";
\r
201 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
202 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
203 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
204 httpHeaders.put("If-Match", "*");
\r
206 HttpConHandler http = new HttpConHandler();
\r
207 Response res = http.doConnect(url, HttpConHandler.HTTP_DELETE, httpHeaders, null);
\r
209 if(!res.isError()){
\r
210 for(int i=0; i<spreadSheets.size(); i++){
\r
211 if(spreadSheets.get(0).getEntry().getResID() == resID)
\r
212 spreadSheets.remove(i);
\r
216 // login for SpreadSheet API
\r
217 // revert back to SpreadSheet Auth Token
\r
218 login(SPREADSHEET_API_SERVICE_NAME);
\r
222 * Get list of SpreadSheet with matching title. It will do Synch with Server
\r
224 * @param title SpreadSheet title
\r
225 * @param isTitleExact Whether title string should be an exact match
\r
229 public ArrayList<SpreadSheet> getSpreadSheet(String title, boolean isTitleExact){
\r
230 return getAllSpreadSheets(true, title, isTitleExact);
\r
234 * Get All stored SpreadSheets from Server
\r
238 public ArrayList<SpreadSheet> getAllSpreadSheets(){
\r
239 return getAllSpreadSheets(true);
\r
243 * Get All stored SpreadSheets either from Server or Local Cache
\r
245 * @param doRefresh Do you want to Synch with Server ?
\r
246 * @return List of Entry. Each Entry represents a SpreadSheet
\r
248 public ArrayList<SpreadSheet> getAllSpreadSheets(boolean doRefresh){
\r
249 return getAllSpreadSheets(doRefresh, null, false);
\r
253 * Get All stored SpreadSheets either from Server or Local Cache
\r
256 * @param title SpreadSheet title. <b>null</b> means all SpreadSheets. No need to do URL encode.
\r
257 * @param isTitleExact Whether title string should be an exact match
\r
260 public ArrayList<SpreadSheet> getAllSpreadSheets(boolean doRefresh, String title, boolean isTitleExact){
\r
264 * Retrieve data only if there is change in Feed
\r
265 * If-None-Match: W/"D0cERnk-eip7ImA9WBBXGEg."
\r
269 // Don't synch with Server
\r
270 return spreadSheets;
\r
273 // login to Spreadsheet Service
\r
274 login(SPREADSHEET_API_SERVICE_NAME);
\r
276 // get list of all spreadsheets
\r
277 String xmlOut = getSpreadSheetList(title, isTitleExact);
\r
279 if(xmlOut == null){
\r
280 Log.p(TAG, "No SpreadSheet Feed received from Server!!");
\r
285 ParseFeed pf = new ParseFeed();
\r
286 Feed f = pf.parse(xmlOut.getBytes());
\r
287 ArrayList<Entry> entries = f.getEntries();
\r
289 if(entries == null){
\r
290 // No SpreadSheet exists
\r
294 // Refresh SpreadSheet List from Server
\r
295 // Clear existing list
\r
296 spreadSheets.clear();
\r
298 for(int i=0; i<entries.size(); i++){
\r
299 spreadSheets.add(new SpreadSheet(entries.get(i)));
\r
302 return spreadSheets;
\r
306 * Share a SpreadSheet with a given list of Collaborators.
\r
307 * NOTE: You need not to mention editLink of Collaborator
\r
309 * @param sp The SpreadSheet you want to share
\r
310 * @param collaborators List of Collaborators with whim you want to share this.
\r
312 public void addSharePermission(SpreadSheet sp, Collaborator[] collaborators){
\r
314 if(sp == null || collaborators == null || collaborators.length == 0){
\r
315 throw new IllegalArgumentException("Please provide SpreadSheet ResID and Collaborator details to whom you want to share.");
\r
318 // Get Document List API Authentication Token
\r
319 login(DOCUMENT_LIST_API_SERVICE_NAME);
\r
322 String postData = "";
\r
323 for(int i=0; i<collaborators.length; i++){
\r
325 Collaborator c = collaborators[i];
\r
327 postData = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'>"+
\r
328 "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/acl/2007#accessRule'/>"+
\r
329 "<gAcl:role value='"+ c.getRole() +"'/>"+
\r
330 "<gAcl:scope type='"+ c.getScopeType() +"' value='"+ c.getScopeValue() +"'/>"+
\r
334 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
335 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ authToken);
\r
336 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
337 httpHeaders.put(HttpConHandler.CONTENT_TYPE_HTTP_HEADER, "application/atom+xml");
\r
340 HttpConHandler http = new HttpConHandler();
\r
341 String url = "https://docs.google.com/feeds/default/private/full/"+ sp.getResourceID() +"/acl";
\r
343 http.doConnect(url, HttpConHandler.HTTP_POST, httpHeaders, postData);
\r
346 // Get SpreadSheet API Authentication Token
\r
347 login(SPREADSHEET_API_SERVICE_NAME);
\r
352 * Get list of all Collaborators to whom this SpreadSheet is shared
\r
353 * @param sp SpreadSheet
\r
356 public ArrayList<Collaborator> getAllCollaborators(SpreadSheet sp){
\r
359 throw new IllegalArgumentException("Please provide SpreadSheet ResID and Emails to whom you want to share.");
\r
362 // Get Document List API Authentication Token
\r
363 login(DOCUMENT_LIST_API_SERVICE_NAME);
\r
365 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
366 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ authToken);
\r
367 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
369 HttpConHandler http = new HttpConHandler();
\r
370 String url = "https://docs.google.com/feeds/default/private/full/"+ sp.getResourceID() +"/acl";
\r
371 Response res = http.doConnect(url, HttpConHandler.HTTP_GET, httpHeaders, null);
\r
373 // Get SpreadSheet API Authentication Token
\r
374 login(SPREADSHEET_API_SERVICE_NAME);
\r
377 String xmlOut = res.getOutput();
\r
379 if(xmlOut == null){
\r
380 Log.p(TAG, "No XML feed from Server!!");
\r
385 ParseFeed pf = new ParseFeed();
\r
386 Feed f = pf.parse(xmlOut.getBytes());
\r
387 ArrayList<Entry> entries = f.getEntries();
\r
389 sp.clearCollaboratorList();
\r
391 if(entries != null){
\r
392 // Create Collaborator Instances
\r
393 System.out.println("Number of Collaborators: "+ entries.size());
\r
395 for(int i=0; i<entries.size(); i++){
\r
396 Entry e = entries.get(i);
\r
397 Collaborator c = new Collaborator();
\r
398 c.setEditLink(e.getEditLink());
\r
399 c.setScopeType(e.getAclScopeType());
\r
400 c.setScopeValue(e.getAclScopeValue());
\r
401 c.setRole(e.getAclRole());
\r
403 sp.addCollaborator(c);
\r
407 return sp.getCollaborators();
\r
411 * Change Share permission for a particular ACL entry
\r
413 * @param c Collaborator instance stored in SpreadSheet. It should have a valid EditLink URL
\r
414 * @param role {owner, writer, reader}
\r
416 public void changeSharePermission(Collaborator c, String role){
\r
417 // Get Document List API Authentication Token
\r
418 login(DOCUMENT_LIST_API_SERVICE_NAME);
\r
420 if(c.getEditLink() == null){
\r
421 throw new IllegalArgumentException("No EditLink URL defined in the Collaborator Instance");
\r
424 String url = c.getEditLink();
\r
426 String postData = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'>"+
\r
427 "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/acl/2007#accessRule'/>"+
\r
428 "<gAcl:role value='"+ role +"'/>"+
\r
429 "<gAcl:scope type='"+ c.getScopeType() +"' value='"+ c.getScopeValue() +"'/>"+
\r
432 // HTTP Header- Send PUT request
\r
433 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
434 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ authToken);
\r
435 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
436 httpHeaders.put(HttpConHandler.CONTENT_TYPE_HTTP_HEADER, "application/atom+xml");
\r
439 HttpConHandler http = new HttpConHandler();
\r
440 http.doConnect(url, HttpConHandler.HTTP_PUT, httpHeaders, postData);
\r
442 // Get SpreadSheet API Authentication Token
\r
443 login(SPREADSHEET_API_SERVICE_NAME);
\r
447 * Remove Share access of a selected user.
\r
448 * @param c Collaborator Instance. It should have the Edit Link.
\r
450 public void removeSharePermission(Collaborator c){
\r
451 // Get Document List API Authentication Token
\r
452 login(DOCUMENT_LIST_API_SERVICE_NAME);
\r
454 // HTTP Header- Send PUT request
\r
455 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
456 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ authToken);
\r
457 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
459 HttpConHandler http = new HttpConHandler();
\r
460 String url = c.getEditLink();
\r
462 http.doConnect(url, HttpConHandler.HTTP_DELETE, httpHeaders, null);
\r
464 // Get SpreadSheet API Authentication Token
\r
465 login(SPREADSHEET_API_SERVICE_NAME);
\r
469 * Login to get Authentication Token
\r
472 private void login(String service){
\r
474 // String postData = "accountType=GOOGLE&Email="+ userName +"&Passwd="+ password +"&service="+ service +"&source=test-app-log";
\r
476 // HttpConHandler http = new HttpConHandler();
\r
477 // Response res = http.doConnect(GOOGLE_CLIENT_LOGIN_URL, HttpConHandler.HTTP_POST, null, postData);
\r
479 // if(res.isError()){
\r
483 // String out = res.getOutput();
\r
485 // * Format of success response
\r
486 // * SID=<>LSID=<>Auth=<>
\r
488 // String[] parms = out.split("=");
\r
490 // for(int i=0; i<parms.length; i++){
\r
491 // if(parms[i].toLowerCase().endsWith("auth")){
\r
492 // authToken = parms[i+1];
\r
496 // Log.p(TAG, "AuthToken="+ authToken);
\r
497 // A generic approach to add Authenticator Plug-in
\r
498 Log.p(TAG, "New Auth...");
\r
499 authToken = authenticator.getAuthToken(service);
\r
503 * Get a list of stored SpreadSheets
\r
506 private String getSpreadSheetList(String title, boolean isTitleExact){
\r
508 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
509 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ authToken);
\r
510 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
512 HttpConHandler http = new HttpConHandler();
\r
513 String url = SP_GET_LIST_URL;
\r
515 // Add SpreadSheet Query Params (title and title-exact)
\r
517 url = url.concat("?title="+ HttpConHandler.encode(title));
\r
518 url = url.concat("&title-exact="+ isTitleExact);
\r
521 Response res = http.doConnect(url, HttpConHandler.HTTP_GET, httpHeaders, null);
\r
527 return res.getOutput();
\r