summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Sawicki2011-04-11 20:28:53 (EDT)
committer Chris Aniszczyk2011-04-12 09:33:02 (EDT)
commit80a1f56f72843996c600bda01bfd315e73f815ca (patch)
tree8aeb8237051de67c4dac9bc3b77e734c18120d23
parent7fcde315271bc162239fe9571b068b0570a277f6 (diff)
downloadegit-github-80a1f56f72843996c600bda01bfd315e73f815ca.zip
egit-github-80a1f56f72843996c600bda01bfd315e73f815ca.tar.gz
egit-github-80a1f56f72843996c600bda01bfd315e73f815ca.tar.bz2
Add class to fetch and cache avatar image datarefs/changes/64/3064/2
Change-Id: Ifaf6611f554deeef538c6b9dc7890021df0e8c26 Signed-off-by: Kevin Sawicki <kevin@github.com> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
-rw-r--r--org.eclipse.mylyn.github.ui/src/org/eclipse/mylyn/github/ui/internal/AvatarStore.java242
1 files changed, 242 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.github.ui/src/org/eclipse/mylyn/github/ui/internal/AvatarStore.java b/org.eclipse.mylyn.github.ui/src/org/eclipse/mylyn/github/ui/internal/AvatarStore.java
new file mode 100644
index 0000000..4b54d9f
--- /dev/null
+++ b/org.eclipse.mylyn.github.ui/src/org/eclipse/mylyn/github/ui/internal/AvatarStore.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.mylyn.github.ui.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Stores Avatars
+ */
+public class AvatarStore implements Serializable, ISchedulingRule {
+
+ /**
+ * Callback interface for avatar image data loaded
+ */
+ public interface IAvatarCallback {
+
+ /**
+ * Avatar loaded
+ *
+ * @param data
+ * @param store
+ */
+ void loaded(ImageData data, AvatarStore store);
+
+ }
+
+ /**
+ * serialVersionUID
+ */
+ private static final long serialVersionUID = -7791322302733784441L;
+
+ /**
+ * TIMEOUT
+ */
+ public static final int TIMEOUT = 30 * 1000;
+
+ /**
+ * BUFFER_SIZE
+ */
+ public static final int BUFFER_SIZE = 8192;
+
+ private Map<String, byte[]> avatars = new HashMap<String, byte[]>();
+
+ /**
+ * Get cached avatar image data
+ *
+ * @param url
+ * @return image data or null if not present in store
+ */
+ public ImageData getAvatar(String url) {
+ ImageData data = null;
+ url = normalize(url);
+ if (url != null) {
+ byte[] cached = this.avatars.get(url);
+ if (cached != null)
+ data = getData(cached);
+ }
+ return data;
+ }
+
+ /**
+ * Normalize url removing query parameters
+ *
+ * @param url
+ * @return normalized
+ */
+ protected String normalize(String url) {
+ try {
+ URL parsed = new URL(url);
+ parsed = new URL(parsed.getProtocol(), parsed.getHost(),
+ parsed.getPath());
+ return parsed.toExternalForm();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Load avatar
+ *
+ * @param url
+ * @param callback
+ */
+ public void loadAvatar(final String url, final IAvatarCallback callback) {
+ Job job = new Job(MessageFormat.format("Loading avatar for {0}", url)) {
+
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ ImageData data = loadAvatar(url);
+ if (data != null)
+ callback.loaded(data, AvatarStore.this);
+ } catch (IOException ignore) {
+ // Ignored
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ job.schedule();
+ }
+
+ /**
+ * Load avatar image data from url
+ *
+ * @param url
+ * @return avatar image data
+ * @throws IOException
+ */
+ public ImageData loadAvatar(String url) throws IOException {
+ url = normalize(url);
+ URL parsed = new URL(url);
+
+ byte[] data = this.avatars.get(url);
+ if (data != null)
+ return getData(data);
+
+ URLConnection connection = parsed.openConnection();
+ connection.setConnectTimeout(TIMEOUT);
+
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ InputStream input = connection.getInputStream();
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int read = input.read(buffer);
+ while (read != -1) {
+ output.write(buffer, 0, read);
+ read = input.read(buffer);
+ }
+ } finally {
+ try {
+ input.close();
+ } catch (IOException ignore) {
+ // Ignored
+ }
+ try {
+ output.close();
+ } catch (IOException ignore) {
+ // Ignored
+ }
+ }
+ data = output.toByteArray();
+ this.avatars.put(url, data);
+ return getData(data);
+ }
+
+ /**
+ * Get scaled image
+ *
+ * @param size
+ * @param data
+ * @return image data scaled to specified size
+ */
+ public Image getScaledImage(int size, ImageData data) {
+ Display display = PlatformUI.getWorkbench().getDisplay();
+ Image image = new Image(display, data);
+ Rectangle sourceBounds = image.getBounds();
+
+ // Return original image and don't scale if size matches request
+ if (sourceBounds.width == size)
+ return image;
+
+ Image scaled = new Image(display, size, size);
+ GC gc = new GC(scaled);
+ try {
+ gc.setAntialias(SWT.ON);
+ gc.setInterpolation(SWT.HIGH);
+ Rectangle targetBounds = scaled.getBounds();
+ gc.drawImage(image, 0, 0, sourceBounds.width, sourceBounds.height,
+ 0, 0, targetBounds.width, targetBounds.height);
+ } finally {
+ gc.dispose();
+ image.dispose();
+ }
+ return scaled;
+ }
+
+ /**
+ * Get avatar image data
+ *
+ * @param bytes
+ * @return image data
+ */
+ public ImageData getData(byte[] bytes) {
+ ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+ try {
+ ImageData[] images = new ImageLoader().load(stream);
+ if (images.length > 0)
+ return images[0];
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ignore) {
+ // Ignored
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ */
+ public boolean contains(ISchedulingRule rule) {
+ return this == rule;
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ */
+ public boolean isConflicting(ISchedulingRule rule) {
+ return this == rule;
+ }
+}