Back to project page contactphotosync.
The source code is released under:
GNU General Public License
If you think the Android project contactphotosync listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/** * PicasawebService.java - Simple Picasaweb API Library. * //w w w.jav a2 s. c om * Copyright (C) 2012 Mansour <mansour@oxplot.com> * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see * <http://www.gnu.org/licenses/>. */ package com.oxplot.contactphotosync; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.content.res.Resources.NotFoundException; public class PicasawebService { public static final String PW_SERVICE_NAME = "lh2"; private static final String PHOTO_FIELD_PARAM = "&fields=entry(title,summary,gphoto:id,gphoto:width,gphoto:height,content,link[@rel='edit'],link[@rel='edit-media'],exif:tags(exif:imageUniqueID))"; public String authToken; private String albumXML; private String albumUpdateXML; private String photoXML; private String photoUpdateXML; private static String encodeXML(String text) { Matcher m = Pattern.compile("[&\"'<>]").matcher(text); if (!m.find()) return text; StringBuffer buffer = new StringBuffer(text.substring(0, m.start())); do { char c = m.group().charAt(0); String repl; switch (c) { case '&': repl = "amp"; break; case '"': repl = "quot"; break; case '\'': repl = "apos"; break; case '<': repl = "lt"; break; case '>': repl = "gt"; break; default: repl = null; } m.appendReplacement(buffer, "&" + repl + ";"); } while (m.find()); m.appendTail(buffer); return buffer.toString(); } private static String decodeXML(String text) { Matcher m = Pattern.compile("&(amp|quot|apos|lt|gt);").matcher(text); if (!m.find()) return text; StringBuffer buffer = new StringBuffer(text.substring(0, m.start())); do { String ent = m.group(1); String repl; if ("amp".equals(ent)) repl = "&"; else if ("quot".equals(ent)) repl = "\""; else if ("apos".equals(ent)) repl = "'"; else if ("lt".equals(ent)) repl = "<"; else if ("gt".equals(ent)) repl = ">"; else repl = null; m.appendReplacement(buffer, repl); } while (m.find()); m.appendTail(buffer); return buffer.toString(); } private static String extractRegex(String data, String pattern) { Matcher m = Pattern.compile(pattern, Pattern.DOTALL).matcher(data); return m.find() ? m.group(1) : ""; } public PicasawebService(Context context) throws IOException { try { albumXML = inputStreamToString(context.getResources().openRawResource( R.raw.album)); albumUpdateXML = inputStreamToString(context.getResources() .openRawResource(R.raw.album_update)); photoXML = inputStreamToString(context.getResources().openRawResource( R.raw.photo)); photoUpdateXML = inputStreamToString(context.getResources() .openRawResource(R.raw.photo_update)); } catch (NotFoundException e) {} } private static String inputStreamToString(InputStream stream) throws IOException { StringBuffer sb = new StringBuffer(); BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8")); for (int c = br.read(); c != -1; c = br.read()) sb.append((char) c); return sb.toString(); } public PicasaAlbum createAlbum() { return new PicasaAlbum(); } public PicasaAlbum getAlbum(String id) throws IOException, PicasaAuthException { String url = "https://picasaweb.google.com/data/entry/api/user/default/albumid" + "?fields=entry(gphoto:id,title,summary,gphoto:access,updated,link[@rel='edit'])"; ByteArrayOutputStream baOut = new ByteArrayOutputStream(); int status = performPWCmd("GET", url, null, null, baOut); if (status == HttpURLConnection.HTTP_NOT_FOUND) return null; if (status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when retrieving album " + id + "."); Pattern entryPat = Pattern.compile("<entry>.+?</entry>", Pattern.DOTALL); Matcher m = entryPat.matcher(baOut.toString("UTF-8")); if (m.find()) return new PicasaAlbum(m.group()); return null; } public Collection<PicasaAlbum> listAlbums() throws IOException, PicasaAuthException { String url = "https://picasaweb.google.com/data/feed/api/user/default?max-results=1000000" + "&fields=entry(gphoto:id,title,summary,gphoto:access,updated,link[@rel='edit'])"; ByteArrayOutputStream baOut = new ByteArrayOutputStream(); int status = performPWCmd("GET", url, null, null, baOut); if (status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when retrieving list of albums."); ArrayList<PicasaAlbum> list = new ArrayList<PicasaAlbum>(); Pattern entryPat = Pattern.compile("<entry>.+?</entry>", Pattern.DOTALL); Matcher m = entryPat.matcher(baOut.toString("UTF-8")); while (m.find()) list.add(new PicasaAlbum(m.group())); return list; } private int performPWCmd(String method, String url, Map<String, String> headers, InputStream dataIn, OutputStream dataOut) throws IOException, PicasaAuthException { int bytesRead; byte[] buffer = new byte[4096]; OutputStream outStream = null; HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod(method); conn.setRequestProperty("Authorization", "GoogleLogin auth=" + authToken); if (headers != null) for (String k : headers.keySet()) conn.setRequestProperty(k, headers.get(k)); // Send POST data if (dataIn != null) { conn.setDoOutput(true); outStream = conn.getOutputStream(); bytesRead = dataIn.read(buffer); while (bytesRead >= 0) { outStream.write(buffer, 0, bytesRead); bytesRead = dataIn.read(buffer); } outStream.flush(); } int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_FORBIDDEN) throw new PicasaAuthException(); // Get response InputStream inStream = null; if (dataOut != null) { try { inStream = conn.getInputStream(); } catch (FileNotFoundException e1) { inStream = conn.getErrorStream(); } bytesRead = inStream.read(buffer); while (bytesRead >= 0) { dataOut.write(buffer, 0, bytesRead); bytesRead = inStream.read(buffer); } } if (outStream != null) outStream.close(); if (inStream != null) inStream.close(); return responseCode; } public class PicasaAlbum { private String id; private String editUrl; private String updated; public String title; public String summary; public String access; private PicasaAlbum(String data) { title = decodeXML(extractRegex(data, "<title type='text'>([^<]+)</title>")); summary = decodeXML(extractRegex(data, "<summary type='text'>([^<]+)</summary>")); access = decodeXML(extractRegex(data, "<gphoto:access>([^<]+)</gphoto:access>")); id = decodeXML(extractRegex(data, "<gphoto:id>([^<]+)</gphoto:id>")); updated = decodeXML(extractRegex(data, "<updated>([^<]+)</updated>")); editUrl = decodeXML(extractRegex(data, "<link rel='edit' type='application/atom[+]xml' href='([^']*)")); } private PicasaAlbum() { } public String getUpdated() { return updated; } public PicasaAlbum save() throws PicasaAuthException, IOException { ByteArrayInputStream newEntry = null; ByteArrayOutputStream baOut = new ByteArrayOutputStream(); String url; try { if (id != null) { url = editUrl; newEntry = new ByteArrayInputStream(String.format(albumUpdateXML, encodeXML(id), encodeXML(title), encodeXML(summary), encodeXML(access), new Date().getTime()).getBytes("UTF-8")); } else { newEntry = new ByteArrayInputStream(String.format(albumXML, encodeXML(title), encodeXML(summary), encodeXML(access), new Date().getTime()).getBytes("UTF-8")); url = "https://picasaweb.google.com/data/feed/api/user/default"; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } HashMap<String, String> headers = new HashMap<String, String>(); headers.put("Content-Type", "application/atom+xml"); int status = performPWCmd(id == null ? "POST" : "PUT", url, headers, newEntry, baOut); if (status != HttpURLConnection.HTTP_CREATED && status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when inserting/updating new album."); return new PicasaAlbum(baOut.toString("UTF-8")); } public PicasaPhoto deserializePhoto(DataInputStream is) throws IOException { PicasaPhoto p = new PicasaPhoto(this); try { p.id = is.readUTF(); p.uniqueId = is.readUTF(); p.title = is.readUTF(); p.summary = is.readUTF(); p.photoUrl = is.readUTF(); p.editUrl = is.readUTF(); p.editMediaUrl = is.readUTF(); } catch (EOFException e) { return null; } return p; } public PicasaPhoto createPhoto() { return new PicasaPhoto(this); } public PicasaPhoto getPhoto(String id) throws IOException, PicasaAuthException { String url = "https://picasaweb.google.com/data/entry/api/user/default/albumid/" + this.id + "/photoid/" + id + "?imgmax=1600" + PHOTO_FIELD_PARAM; ByteArrayOutputStream baOut = new ByteArrayOutputStream(); int status = performPWCmd("GET", url, null, null, baOut); if (status == HttpURLConnection.HTTP_NOT_FOUND) return null; if (status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when retrieving photo " + id + "."); Pattern entryPat = Pattern.compile("<entry>.+?</entry>", Pattern.DOTALL); Matcher m = entryPat.matcher(baOut.toString("UTF-8")); if (m.find()) return new PicasaPhoto(this, m.group()); return null; } public Collection<PicasaPhoto> listPhotos() throws IOException, PicasaAuthException { String url = "https://picasaweb.google.com/data/feed/api/user/default/albumid/" + id + "?imgmax=1600&max-results=1000000" + PHOTO_FIELD_PARAM; ByteArrayOutputStream baOut = new ByteArrayOutputStream(); int status = performPWCmd("GET", url, null, null, baOut); if (status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when retrieving list of photos for album " + id + "."); ArrayList<PicasaPhoto> list = new ArrayList<PicasaPhoto>(); Pattern entryPat = Pattern.compile("<entry>.+?</entry>", Pattern.DOTALL); Matcher m = entryPat.matcher(baOut.toString("UTF-8")); while (m.find()) list.add(new PicasaPhoto(this, m.group())); return list; } public String getId() { return id; } } public class PicasaPhoto { private String uniqueId; private String id; private PicasaAlbum album; private InputStream photoStream; private String editUrl; private String editMediaUrl; private String photoUrl; private String mimeType; private int width; private int height; public String title; public String summary; private PicasaPhoto(PicasaAlbum album, String data) { this.album = album; title = decodeXML(extractRegex(data, "<title type='text'>([^<]+)</title>")); summary = decodeXML(extractRegex(data, "<summary type='text'>([^<]+)</summary>")); uniqueId = decodeXML(extractRegex(data, "<exif:imageUniqueID>([^<]+)</exif:imageUniqueID>")); id = decodeXML(extractRegex(data, "<gphoto:id>([^<]+)</gphoto:id>")); editUrl = decodeXML(extractRegex(data, "<link rel='edit' type='application/atom[+]xml' href='([^']*)")); editMediaUrl = decodeXML(extractRegex(data, "<link rel='edit-media' type='image/[^']+' href='([^']*)")); mimeType = decodeXML(extractRegex(data, "<content type='(image/[^']+)' src")); photoUrl = decodeXML( extractRegex(data, "<content type='image/[^']+' src='([^']*)")) .replace("/s1600/", "/s0/"); width = Integer.parseInt(decodeXML(extractRegex(data, "<gphoto:width>([^<]+)</gphoto:width>"))); height = Integer.parseInt(decodeXML(extractRegex(data, "<gphoto:height>([^<]+)</gphoto:height>"))); } private PicasaPhoto(PicasaAlbum album) { this.album = album; } public int getWidth() { return width; } public int getHeight() { return height; } public String getMimeType() { return mimeType; } public String getUniqueId() { return uniqueId; } public String getId() { return id; } public PicasaAlbum getAlbum() { return album; } public PicasaPhoto save() throws PicasaAuthException, IOException { ByteArrayInputStream newEntry = null; ByteArrayOutputStream baOut = new ByteArrayOutputStream(); String url; try { if (id != null) { url = photoStream == null ? editUrl : editMediaUrl; newEntry = new ByteArrayInputStream(String.format(photoUpdateXML, encodeXML(id), encodeXML(album.getId()), encodeXML(title), encodeXML(summary), new Date().getTime()).getBytes("UTF-8")); } else { newEntry = new ByteArrayInputStream(String.format(photoXML, encodeXML(title), encodeXML(summary)).getBytes("UTF-8")); url = "https://picasaweb.google.com/data/feed/api/user/default/albumid/" + album.getId(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } HashMap<String, String> headers = new HashMap<String, String>(); InputStream baIn; if (photoStream == null) { headers.put("Content-Type", "application/atom+xml"); baIn = newEntry; } else { headers.put("Content-Type", "multipart/related; boundary=\"END_OF_PART\""); headers.put("MIME-version", "1.0"); ByteArrayInputStream bit1 = new ByteArrayInputStream( "Media multipart posting\r\n--END_OF_PART\r\nContent-Type: application/atom+xml\r\n\r\n" .getBytes("UTF-8")); ByteArrayInputStream bit2 = new ByteArrayInputStream( "\r\n--END_OF_PART\r\nContent-Type: image/jpeg\r\n\r\n" .getBytes("UTF-8")); ByteArrayInputStream bit3 = new ByteArrayInputStream( "\r\n--END_OF_PART--".getBytes("UTF-8")); baIn = new ConcatInputStreams(Arrays.asList(new InputStream[] { bit1, newEntry, bit2, photoStream, bit3 })); } int status = performPWCmd(id == null ? "POST" : "PUT", url, headers, baIn, baOut); if (status != HttpURLConnection.HTTP_CREATED && status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when inserting/updating photo."); return new PicasaPhoto(album, baOut.toString("UTF-8")); } public void downloadPhoto(OutputStream out) throws IOException, PicasaAuthException { int status = performPWCmd("GET", photoUrl, null, null, out); if (status == HttpURLConnection.HTTP_NOT_FOUND) throw new FileNotFoundException(); if (status != HttpURLConnection.HTTP_OK) throw new IOException("Got " + status + " HTTP status when inserting/updating photo."); } public void setPhotoStream(InputStream photoStream) { this.photoStream = photoStream; } public boolean delete() throws IOException, PicasaAuthException { return performPWCmd("DELETE", editUrl, null, null, null) == HttpURLConnection.HTTP_OK; } public void serialize(DataOutputStream os) throws IOException { os.writeUTF(id); os.writeUTF(uniqueId); os.writeUTF(title); os.writeUTF(summary); os.writeUTF(photoUrl); os.writeUTF(editUrl); os.writeUTF(editMediaUrl); } } @SuppressWarnings("serial") public static class PicasaAuthException extends Exception { } @SuppressWarnings("serial") public static class PicasaDataException extends Exception { } }