diff --git a/astrid/src/main/java/com/todoroo/astrid/actfm/CommentsFragment.java b/astrid/src/main/java/com/todoroo/astrid/actfm/CommentsFragment.java
index fdec84854..1127bea29 100644
--- a/astrid/src/main/java/com/todoroo/astrid/actfm/CommentsFragment.java
+++ b/astrid/src/main/java/com/todoroo/astrid/actfm/CommentsFragment.java
@@ -46,13 +46,10 @@ import com.todoroo.astrid.adapter.UpdateAdapter;
import com.todoroo.astrid.dao.UserActivityDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.UserActivity;
-import com.todoroo.astrid.helper.AsyncImageView;
import org.json.JSONObject;
import org.tasks.R;
-import edu.mit.mobile.android.imagecache.ImageCache;
-
public abstract class CommentsFragment extends SherlockListFragment {
// private TagData tagData;
@@ -72,15 +69,12 @@ public abstract class CommentsFragment extends SherlockListFragment {
protected static final int MENU_REFRESH_ID = Menu.FIRST;
- protected final ImageCache imageCache;
-
protected Resources resources;
@Autowired UserActivityDao userActivityDao;
public CommentsFragment() {
DependencyInjectionService.getInstance().inject(this);
- imageCache = AsyncImageView.getImageCache();
}
@Override
diff --git a/astrid/src/main/java/com/todoroo/astrid/actfm/TagSettingsActivity.java b/astrid/src/main/java/com/todoroo/astrid/actfm/TagSettingsActivity.java
index 5943c6bbe..2b9afca36 100644
--- a/astrid/src/main/java/com/todoroo/astrid/actfm/TagSettingsActivity.java
+++ b/astrid/src/main/java/com/todoroo/astrid/actfm/TagSettingsActivity.java
@@ -39,7 +39,6 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TagMetadata;
import com.todoroo.astrid.data.User;
-import com.todoroo.astrid.helper.AsyncImageView;
import com.todoroo.astrid.helper.UUIDHelper;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.ThemeService;
@@ -53,8 +52,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.tasks.R;
-import edu.mit.mobile.android.imagecache.ImageCache;
-
public class TagSettingsActivity extends SherlockFragmentActivity {
public static final String TOKEN_NEW_FILTER = "newFilter"; //$NON-NLS-1$
@@ -82,14 +79,12 @@ public class TagSettingsActivity extends SherlockFragmentActivity {
private EditText tagName;
private Bitmap setBitmap;
- private final ImageCache imageCache;
private boolean isNewTag = false;
private boolean isDialog;
public TagSettingsActivity() {
DependencyInjectionService.getInstance().inject(this);
- imageCache = AsyncImageView.getImageCache();
}
@Override
@@ -269,7 +264,6 @@ public class TagSettingsActivity extends SherlockFragmentActivity {
}
try {
String tagPicture = RemoteModel.PictureHelper.getPictureHash(tagData);
- imageCache.put(tagPicture, bitmap);
tagData.setValue(TagData.PICTURE, tagPicture);
}
catch (Exception e) {
diff --git a/astrid/src/main/java/com/todoroo/astrid/adapter/UpdateAdapter.java b/astrid/src/main/java/com/todoroo/astrid/adapter/UpdateAdapter.java
index fd3f085c0..b3e52fe2b 100644
--- a/astrid/src/main/java/com/todoroo/astrid/adapter/UpdateAdapter.java
+++ b/astrid/src/main/java/com/todoroo/astrid/adapter/UpdateAdapter.java
@@ -23,6 +23,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.CursorAdapter;
+import android.widget.ImageView;
import android.widget.TextView;
import com.todoroo.andlib.data.Property;
@@ -39,14 +40,9 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.User;
import com.todoroo.astrid.data.UserActivity;
-import com.todoroo.astrid.helper.AsyncImageView;
import org.tasks.R;
-import java.io.IOException;
-
-import edu.mit.mobile.android.imagecache.ImageCache;
-
/**
* Adapter for displaying a user's activity
*
@@ -60,7 +56,6 @@ public class UpdateAdapter extends CursorAdapter {
protected final Fragment fragment;
private final int resource;
private final LayoutInflater inflater;
- private final ImageCache imageCache;
private final String linkColor;
private final String fromView;
@@ -138,7 +133,6 @@ public class UpdateAdapter extends CursorAdapter {
inflater = (LayoutInflater) fragment.getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- imageCache = AsyncImageView.getImageCache();
this.fromView = fromView;
this.resource = resource;
@@ -269,7 +263,7 @@ public class UpdateAdapter extends CursorAdapter {
}
private void setupUserActivityRow(View view, UserActivity activity, User user) {
- final AsyncImageView commentPictureView = (AsyncImageView)view.findViewById(R.id.comment_picture); {
+ final ImageView commentPictureView = (ImageView)view.findViewById(R.id.comment_picture); {
String pictureThumb = activity.getPictureUrl(UserActivity.PICTURE, RemoteModel.PICTURE_MEDIUM);
String pictureFull = activity.getPictureUrl(UserActivity.PICTURE, RemoteModel.PICTURE_LARGE);
Bitmap updateBitmap = null;
@@ -277,7 +271,7 @@ public class UpdateAdapter extends CursorAdapter {
updateBitmap = activity.getPictureBitmap(UserActivity.PICTURE);
}
setupImagePopupForCommentView(view, commentPictureView, pictureThumb, pictureFull, updateBitmap,
- activity.getValue(UserActivity.MESSAGE), fragment, imageCache);
+ activity.getValue(UserActivity.MESSAGE), fragment);
}
// name
@@ -302,37 +296,23 @@ public class UpdateAdapter extends CursorAdapter {
return false;
}
- public static void setupImagePopupForCommentView(View view, AsyncImageView commentPictureView, final String pictureThumb, final String pictureFull, final Bitmap updateBitmap,
- final String message, final Fragment fragment, ImageCache imageCache) {
+ public static void setupImagePopupForCommentView(View view, ImageView commentPictureView, final String pictureThumb, final String pictureFull, final Bitmap updateBitmap,
+ final String message, final Fragment fragment) {
if ((!TextUtils.isEmpty(pictureThumb) && !"null".equals(pictureThumb)) || updateBitmap != null) { //$NON-NLS-1$
commentPictureView.setVisibility(View.VISIBLE);
if (updateBitmap != null) {
commentPictureView.setImageBitmap(updateBitmap);
- } else {
- commentPictureView.setUrl(pictureThumb);
- }
-
- if (pictureThumb != null && imageCache.contains(pictureThumb) && updateBitmap == null) {
- try {
- commentPictureView.setImageBitmap(imageCache.get(pictureThumb));
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else if (updateBitmap == null) {
- commentPictureView.setUrl(pictureThumb);
}
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog image = new AlertDialog.Builder(fragment.getActivity()).create();
- AsyncImageView imageView = new AsyncImageView(fragment.getActivity());
+ ImageView imageView = new ImageView(fragment.getActivity());
imageView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
imageView.setImageResource(android.R.drawable.ic_menu_gallery);
if (updateBitmap != null) {
imageView.setImageBitmap(updateBitmap);
- } else {
- imageView.setUrl(pictureFull);
}
image.setView(imageView);
diff --git a/astrid/src/main/java/com/todoroo/astrid/helper/AsyncImageView.java b/astrid/src/main/java/com/todoroo/astrid/helper/AsyncImageView.java
deleted file mode 100644
index cb8840755..000000000
--- a/astrid/src/main/java/com/todoroo/astrid/helper/AsyncImageView.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (c) 2012 Todoroo Inc
- *
- * See the file "LICENSE" for the full license governing this code.
- */
-package com.todoroo.astrid.helper;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-import com.todoroo.andlib.service.ContextManager;
-
-import java.io.IOException;
-
-import edu.mit.mobile.android.imagecache.ImageCache;
-
-public class AsyncImageView extends ImageView {
-
- private final ImageCache imageDiskCache;
- private Bitmap cacheImage;
- private String cacheURL = ""; //$NON-NLS-1$
- public AsyncImageView(Context context) {
- super(context);
- imageDiskCache = getImageCache();
- }
- public AsyncImageView(Context context, AttributeSet set) {
- super(context, set);
- imageDiskCache = getImageCache();
- }
- public AsyncImageView(Context context, AttributeSet set, int defStyle) {
- super(context, set, defStyle);
- imageDiskCache = getImageCache();
- }
-
- public void setUrl(String url) {
- if (cacheImage != null && cacheURL.equals(url) && !TextUtils.isEmpty(url)) {
- setImageBitmap(cacheImage);
- return;
- } else if (url != null && imageDiskCache != null && imageDiskCache.contains(url)) {
- try {
- cacheImage = imageDiskCache.get(url);
- setImageBitmap(cacheImage);
- cacheURL = url;
- return;
- } catch (IOException e) {
- //
- }
- }
- }
-
- private static volatile ImageCache imageCacheInstance = null;
-
- public static ImageCache getImageCache() {
- if (imageCacheInstance == null) {
- synchronized(AsyncImageView.class) {
- if (imageCacheInstance == null) {
- try {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- } catch (Exception e) {
- // Ignore
- }
- imageCacheInstance = new ImageCache(ContextManager.getContext(), CompressFormat.JPEG, 85);
- }
- }
- }
- return imageCacheInstance;
- }
-}
diff --git a/astrid/src/main/java/com/todoroo/astrid/notes/EditNoteActivity.java b/astrid/src/main/java/com/todoroo/astrid/notes/EditNoteActivity.java
index bf21cb1dc..a5ae951ad 100644
--- a/astrid/src/main/java/com/todoroo/astrid/notes/EditNoteActivity.java
+++ b/astrid/src/main/java/com/todoroo/astrid/notes/EditNoteActivity.java
@@ -27,6 +27,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -54,7 +55,6 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.User;
import com.todoroo.astrid.data.UserActivity;
-import com.todoroo.astrid.helper.AsyncImageView;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.TaskService;
@@ -70,8 +70,6 @@ import java.util.Date;
import java.util.LinkedList;
import java.util.List;
-import edu.mit.mobile.android.imagecache.ImageCache;
-
public class EditNoteActivity extends LinearLayout implements TimerActionListener {
public static final String EXTRA_TASK_ID = "task"; //$NON-NLS-1$
@@ -97,7 +95,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
private final Resources resources;
- private final ImageCache imageCache;
private final int cameraButton;
private final String linkColor;
private int historyCount = 0;
@@ -118,7 +115,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
super(fragment.getActivity());
DependencyInjectionService.getInstance().inject(this);
- imageCache = AsyncImageView.getImageCache();
this.fragment = fragment;
this.activity = (AstridActivity) fragment.getActivity();
@@ -395,9 +391,8 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
// picture
- final AsyncImageView commentPictureView = (AsyncImageView)view.findViewById(R.id.comment_picture); {
- UpdateAdapter.setupImagePopupForCommentView(view, commentPictureView, item.pictureThumb, item.pictureFull, item.commentBitmap, item.title.toString(), fragment, imageCache);
- }
+ final ImageView commentPictureView = (ImageView)view.findViewById(R.id.comment_picture);
+ UpdateAdapter.setupImagePopupForCommentView(view, commentPictureView, item.pictureThumb, item.pictureFull, item.commentBitmap, item.title.toString(), fragment);
}
public void refreshData() {
diff --git a/astrid/src/main/java/edu/mit/mobile/android/imagecache/DiskCache.java b/astrid/src/main/java/edu/mit/mobile/android/imagecache/DiskCache.java
deleted file mode 100644
index d2cf7bb4b..000000000
--- a/astrid/src/main/java/edu/mit/mobile/android/imagecache/DiskCache.java
+++ /dev/null
@@ -1,604 +0,0 @@
-package edu.mit.mobile.android.imagecache;
-
-/*
- * Copyright (C) 2011-2013 MIT Mobile Experience Lab
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-import android.os.Build;
-import android.os.StatFs;
-import android.util.Log;
-
-import com.todoroo.astrid.utility.Constants;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- *
- * A simple disk cache.
- *
- *
- *
- * By default, the maximum size of the cache is automatically set based on the amount of free space
- * available to the cache. Alternatively, a fixed size can be specified using
- * {@link #setCacheMaxSize(long)}.
- *
- *
- *
- * By default, the cache will automatically maintain its size by periodically checking to see if it
- * estimates that a trim is needed and if it is, proceeding to running {@link #trim()} on a worker
- * thread. This feature can be controlled by {@link #setAutoTrimFrequency(int)}.
- *
- *
- * @author Steve Pomeroy
- *
- * @param
- * the key to store/retrieve the value
- * @param
- * the value that will be stored to disk
- */
-public abstract class DiskCache {
- private static final String TAG = "DiskCache";
-
- /**
- * Automatically determines the maximum size of the cache based on available free space.
- */
- public static final int AUTO_MAX_CACHE_SIZE = 0;
-
- /**
- * The default number of cache hits before {@link #trim()} is automatically triggered. See
- * {@link #setAutoTrimFrequency(int)}.
- */
- public static final int DEFAULT_AUTO_TRIM_FREQUENCY = 10;
-
- /**
- * Pass to {@link #setAutoTrimFrequency(int)} to disable automatic trimming. See {@link #trim()}
- * .
- */
- public static final int AUTO_TRIM_DISABLED = 0;
-
- // /////////////////////////////////////////////
-
- private long mMaxDiskUsage = AUTO_MAX_CACHE_SIZE;
-
- private MessageDigest hash;
-
- private final File mCacheBase;
- private final String mCachePrefix, mCacheSuffix;
-
- private final ConcurrentLinkedQueue mQueue = new ConcurrentLinkedQueue();
-
- /**
- * In auto max cache mode, the maximum is set to the total free space divided by this amount.
- */
- private static final int AUTO_MAX_CACHE_SIZE_DIVISOR = 10;
-
- private int mAutoTrimFrequency = DEFAULT_AUTO_TRIM_FREQUENCY;
-
- private final ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(1, 5, 60, TimeUnit.SECONDS,
- new LinkedBlockingQueue());
-
- private int mAutoTrimHitCount = 1;
-
- private long mEstimatedDiskUsage;
-
- private long mEstimatedFreeSpace;
-
- /**
- * Creates a new disk cache with no cachePrefix or cacheSuffix
- */
- public DiskCache(File cacheBase) {
- this(cacheBase, null, null);
- }
-
- /**
- * Creates a new disk cache.
- *
- * @param cacheBase
- * The base directory within which all the cache files will be stored.
- * @param cachePrefix
- * If you want a prefix to the filenames, place one here. Otherwise, pass null.
- * @param cacheSuffix
- * A suffix to the cache filename. Null is also ok here.
- */
- public DiskCache(File cacheBase, String cachePrefix, String cacheSuffix) {
- mCacheBase = cacheBase;
- mCachePrefix = cachePrefix;
- mCacheSuffix = cacheSuffix;
-
- try {
- hash = MessageDigest.getInstance("SHA-1");
-
- } catch (final NoSuchAlgorithmException e) {
- try {
- hash = MessageDigest.getInstance("MD5");
- } catch (final NoSuchAlgorithmException e2) {
- final RuntimeException re = new RuntimeException("No available hashing algorithm");
- re.initCause(e2);
- throw re;
- }
- }
-
- updateDiskUsageInBg();
- }
-
- /**
- * Sets the maximum size of the cache, in bytes. The default is to automatically manage the max
- * size based on the available disk space. This can be explicitly set by passing this
- * {@link #AUTO_MAX_CACHE_SIZE}.
- *
- * @param maxSize
- * maximum size of the cache, in bytes.
- */
- public void setCacheMaxSize(long maxSize) {
- mMaxDiskUsage = maxSize;
- }
-
- /**
- * After this many puts, if it looks like there's a low space condition, {@link #trim()} will
- * automatically be called.
- *
- * @param autoTrimFrequency
- * Set to {@link #AUTO_TRIM_DISABLED} to turn off auto trim. The default is
- * {@link #DEFAULT_AUTO_TRIM_FREQUENCY}.
- */
- public void setAutoTrimFrequency(int autoTrimFrequency) {
- mAutoTrimFrequency = autoTrimFrequency;
- }
-
- /**
- * Updates cached estimates on the
- */
- private void updateDiskUsageEstimates() {
- final long diskUsage = getCacheDiskUsage();
-
- final long availableSpace = getFreeSpace();
-
- synchronized (this) {
- mEstimatedDiskUsage = diskUsage;
- mEstimatedFreeSpace = availableSpace;
- }
- }
-
- private void updateDiskUsageInBg() {
- mExecutor.execute(new Runnable() {
-
- @Override
- public void run() {
- updateDiskUsageEstimates();
- }
- });
- }
-
- /**
- * Gets the amount of space free on the cache volume.
- *
- * @return free space in bytes.
- */
- private long getFreeSpace() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
- return mCacheBase.getUsableSpace();
- } else {
- // maybe make singleton
- final StatFs stat = new StatFs(mCacheBase.getAbsolutePath());
- return (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
- }
- }
-
- /**
- * Gets the cache filename for the given key.
- */
- protected File getFile(K key) {
- return new File(mCacheBase, (mCachePrefix != null ? mCachePrefix : "") + hash(key)
- + (mCacheSuffix != null ? mCacheSuffix : ""));
- }
-
- /**
- * Writes the value stored in the cache to disk by calling
- * {@link #toDisk(Object, Object, OutputStream)}.
- *
- * @param key
- * The key to find the value.
- * @param value
- * the data to be written to disk.
- */
- public final synchronized void put(K key, V value) throws IOException, FileNotFoundException {
- final File saveHere = getFile(key);
-
- final OutputStream os = new FileOutputStream(saveHere);
- toDisk(key, value, os);
- os.close();
-
- mEstimatedDiskUsage += saveHere.length();
-
- touchEntry(saveHere);
-
- autotrim();
- }
-
- /**
- * Writes the contents of the InputStream straight to disk. It is the caller's responsibility to
- * ensure it's the same type as what would be written with
- * {@link #toDisk(Object, Object, OutputStream)}
- * @throws IOException
- * @throws FileNotFoundException
- */
- public final void putRaw(K key, InputStream value) throws IOException, FileNotFoundException {
-
- final File saveHere = getFile(key);
-
- final File tempFile = new File(saveHere.getAbsolutePath() + ".temp");
-
- boolean allGood = false;
- try {
- final OutputStream os = new FileOutputStream(tempFile);
-
- inputStreamToOutputStream(value, os);
- os.close();
-
- synchronized (this) {
- // overwrite
- saveHere.delete();
- tempFile.renameTo(saveHere);
- }
- allGood = true;
- } finally {
- // clean up on any exception
- if (!allGood) {
- saveHere.delete();
- tempFile.delete();
- }
- }
- if (allGood) {
- mEstimatedDiskUsage += saveHere.length();
-
- touchEntry(saveHere);
-
- autotrim();
- }
- }
-
- /**
- * Puts the key at the end of the queue, removing it if it's already present. This will cause it
- * to be removed last when {@link #trim()} is called.
- */
- private void touchEntry(File cacheFile) {
- if (mQueue.contains(cacheFile)) {
- mQueue.remove(cacheFile);
- }
- mQueue.add(cacheFile);
- }
-
- /**
- * Marks the given key as accessed recently. This will deprioritize it from automatically being
- * purged upon {@link #trim()}.
- */
- protected void touchKey(K key) {
- touchEntry(getFile(key));
- }
-
- /**
- * Call this every time you may be able to start a trim in the background. This implicitly runs
- * {@link #updateDiskUsageInBg()} each time it's called.
- */
- private void autotrim() {
- if (mAutoTrimFrequency == 0) {
- return;
- }
-
- mAutoTrimHitCount = (mAutoTrimHitCount + 1) % mAutoTrimFrequency;
-
- if (mAutoTrimHitCount == 0
- && mEstimatedDiskUsage > Math.min(mEstimatedFreeSpace, mMaxDiskUsage)) {
-
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- trim();
- }
- });
- }
-
- updateDiskUsageInBg();
- }
-
- /**
- * Reads from an inputstream, dumps to an outputstream
- * @throws IOException
- */
- static public void inputStreamToOutputStream(InputStream is, OutputStream os)
- throws IOException {
- final int bufsize = 8196 * 10;
- final byte[] cbuf = new byte[bufsize];
-
- for (int readBytes = is.read(cbuf, 0, bufsize); readBytes > 0; readBytes = is.read(cbuf, 0,
- bufsize)) {
- os.write(cbuf, 0, readBytes);
- }
- }
-
- /**
- * Reads the value from disk using {@link #fromDisk(Object, InputStream)}.
- *
- * @return The value for key or null if the key doesn't map to any existing entries.
- */
- public final synchronized V get(K key) throws IOException {
- final File readFrom = getFile(key);
-
- if (!readFrom.exists()) {
- return null;
- }
-
- final InputStream is = new FileInputStream(readFrom);
- final V out = fromDisk(key, is);
- is.close();
-
- touchEntry(readFrom);
-
- return out;
- }
-
- /**
- * Checks the disk cache for a given key.
- *
- * @return true if the disk cache contains the given key
- */
- public final synchronized boolean contains(K key) {
- final File readFrom = getFile(key);
-
- return readFrom.exists();
- }
-
- /**
- * Removes the item from the disk cache.
- *
- * @return true if the cached item has been removed or was already removed, false if it was not
- * able to be removed.
- */
- public synchronized boolean clear(K key) {
- final File readFrom = getFile(key);
-
- if (!readFrom.exists()) {
- return true;
- }
- final long size = readFrom.length();
-
- final boolean success = readFrom.delete();
-
- if (success) {
- mEstimatedDiskUsage -= size;
- }
-
- return success;
- }
-
- /**
- * Removes the item from the disk cache.
- *
- * @return true if the cached item has been removed or was already removed, false if it was not
- * able to be removed.
- */
- private synchronized boolean clear(File cacheFile) {
-
- if (!cacheFile.exists()) {
- return true;
- }
- final long size = cacheFile.length();
-
- final boolean success = cacheFile.delete();
-
- if (success) {
- mEstimatedDiskUsage -= size;
- }
-
- return success;
- }
-
- /**
- * Clears the cache files from disk.
- *
- * Note: this only clears files that match the given prefix/suffix.
- *
- * @return true if the operation succeeded without error. It is possible that it will fail and
- * the cache ends up being partially cleared.
- */
- public synchronized boolean clear() {
- boolean success = true;
-
- for (final File cacheFile : mCacheBase.listFiles(mCacheFileFilter)) {
- if (!cacheFile.delete()) {
- Log.e(TAG, "error deleting " + cacheFile);
- success = false;
- }
- }
- return success;
- }
-
- /**
- * @return the number of files in the cache
- * @deprecated please use {@link #getCacheEntryCount()} or {@link #getCacheDiskUsage()} instead.
- */
- @Deprecated
- public int getCacheSize() {
- return getCacheEntryCount();
- }
-
- /**
- * @return the number of files in the cache as it is on disk.
- */
- public int getCacheEntryCount() {
- return mCacheBase.listFiles(mCacheFileFilter).length;
- }
-
- /**
- * @return the size of the cache in bytes, as it is on disk.
- */
- public long getCacheDiskUsage() {
- long usage = 0;
- for (final File cacheFile : mCacheBase.listFiles(mCacheFileFilter)) {
- usage += cacheFile.length();
- }
- return usage;
- }
-
- private final CacheFileFilter mCacheFileFilter = new CacheFileFilter();
-
- private class CacheFileFilter implements FileFilter {
- @Override
- public boolean accept(File pathname) {
- final String path = pathname.getName();
- return (mCachePrefix != null ? path.startsWith(mCachePrefix) : true)
- && (mCacheSuffix != null ? path.endsWith(mCacheSuffix) : true);
- }
- };
-
- private final Comparator mLastModifiedOldestFirstComparator = new Comparator() {
-
- @Override
- public int compare(File lhs, File rhs) {
- return Long.valueOf(lhs.lastModified()).compareTo(rhs.lastModified());
- }
- };
-
- /**
- * Clears out cache entries in order to reduce the on-disk usage to the desired maximum size.
- * This is a somewhat expensive operation, so it should be done on a background thread.
- *
- * @return the number of bytes worth of files that were trimmed.
- * @see #setCacheMaxSize(long)
- */
- public synchronized long trim() {
-
- long desiredSize;
- final long freeSpace = getFreeSpace();
-
- if (mMaxDiskUsage > 0) {
- desiredSize = mMaxDiskUsage;
- } else {
- desiredSize = getFreeSpace() / AUTO_MAX_CACHE_SIZE_DIVISOR;
- }
-
- desiredSize = Math.min(freeSpace, desiredSize);
-
- final long sizeToTrim = Math.max(0, getCacheDiskUsage() - desiredSize);
-
- if (sizeToTrim == 0) {
- return 0;
- }
-
- long trimmed = 0;
-
- final List sorted = Arrays.asList(mCacheBase.listFiles(mCacheFileFilter));
- Collections.sort(sorted, mLastModifiedOldestFirstComparator);
-
- // first clear out any files that aren't in the queue
- for (final File cacheFile : sorted) {
- if (mQueue.contains(cacheFile)) {
- continue;
- }
-
- final long size = cacheFile.length();
- if (clear(cacheFile)) {
- trimmed += size;
- if (Constants.DEBUG) {
- Log.d(TAG, "trimmed unqueued " + cacheFile.getName() + " from cache.");
- }
- }
-
- if (trimmed >= sizeToTrim) {
- break;
- }
- }
-
- while (trimmed < sizeToTrim && !mQueue.isEmpty()) {
- final File cacheFile = mQueue.poll();
-
- // shouldn't happen due to the check above, but just in case...
- if (cacheFile == null) {
- break;
- }
-
- final long size = cacheFile.length();
-
- if (clear(cacheFile)) {
- trimmed += size;
- if (Constants.DEBUG) {
- Log.d(TAG, "trimmed " + cacheFile.getName() + " from cache.");
- }
- } else {
- Log.e(TAG, "error deleting " + cacheFile);
- }
- }
-
- if (Constants.DEBUG) {
- Log.d(TAG, "trimmed a total of " + trimmed + " bytes from cache.");
- }
- return trimmed;
- }
-
- /**
- * Implement this to do the actual disk writing. Do not close the OutputStream; it will be
- * closed for you.
- */
- protected abstract void toDisk(K key, V in, OutputStream out);
-
- /**
- * Implement this to do the actual disk reading.
- * @return a new instance of {@link V} containing the contents of in.
- */
- protected abstract V fromDisk(K key, InputStream in);
-
- /**
- * Using the key's {@link Object#toString() toString()} method, generates a string suitable for
- * using as a filename.
- *
- * @return a string uniquely representing the the key.
- */
- public String hash(K key) {
- final byte[] ba;
-
- // MessageDigest isn't threadsafe, so we need to ensure it doesn't tread on itself.
- synchronized (hash) {
- hash.update(key.toString().getBytes());
- ba = hash.digest();
- }
- final BigInteger bi = new BigInteger(1, ba);
- final String result = bi.toString(16);
- if (result.length() % 2 != 0) {
- return "0" + result;
- }
- return result;
-
- }
-}
diff --git a/astrid/src/main/java/edu/mit/mobile/android/imagecache/DrawableMemCache.java b/astrid/src/main/java/edu/mit/mobile/android/imagecache/DrawableMemCache.java
deleted file mode 100644
index f9fc71b19..000000000
--- a/astrid/src/main/java/edu/mit/mobile/android/imagecache/DrawableMemCache.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package edu.mit.mobile.android.imagecache;
-
-/*
- * Copyright (C) 2012 MIT Mobile Experience Lab
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.v4.util.LruCache;
-
-public class DrawableMemCache extends LruCache {
-
- private static final String TAG = DrawableMemCache.class.getSimpleName();
-
- public DrawableMemCache(int maxSize) {
- super(maxSize);
- }
-
- @Override
- protected int sizeOf(T key, Drawable value) {
- int size = 0;
- if (value instanceof BitmapDrawable) {
- final Bitmap b = ((BitmapDrawable) value).getBitmap();
- if (b != null) {
- size = b.getRowBytes() * b.getHeight();
- }
- }
- return size;
- }
-}
diff --git a/astrid/src/main/java/edu/mit/mobile/android/imagecache/ImageCache.java b/astrid/src/main/java/edu/mit/mobile/android/imagecache/ImageCache.java
deleted file mode 100644
index 82bca12d5..000000000
--- a/astrid/src/main/java/edu/mit/mobile/android/imagecache/ImageCache.java
+++ /dev/null
@@ -1,810 +0,0 @@
-package edu.mit.mobile.android.imagecache;
-
-/*
- * Copyright (C) 2011-2012 MIT Mobile Experience Lab
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.util.SparseArray;
-import android.widget.ImageView;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.StatusLine;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.HttpResponseException;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.params.ClientPNames;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.ssl.SSLSocketFactory;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.params.CoreConnectionPNames;
-import org.apache.http.params.HttpParams;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.concurrent.PriorityBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- *
- * An image download-and-cacher that also knows how to efficiently generate thumbnails of various
- * sizes.
- *
- *
- *
- * The cache is shared with the entire process, so make sure you
- * {@link #registerOnImageLoadListener(OnImageLoadListener)} and
- * {@link #unregisterOnImageLoadListener(OnImageLoadListener)} any load listeners in your
- * activities.
- *
- *
- * @author Steve Pomeroy
- *
- */
-public class ImageCache extends DiskCache {
- private static final String TAG = ImageCache.class.getSimpleName();
-
- static final boolean DEBUG = false;
-
- // whether to use Apache HttpClient or URL.openConnection()
- private static final boolean USE_APACHE_NC = true;
-
- // the below settings are copied from AsyncTask.java
- private static final int CORE_POOL_SIZE = 5; // thread
- private static final int MAXIMUM_POOL_SIZE = 128; // thread
- private static final int KEEP_ALIVE_TIME = 1; // second
-
- private final HashSet mImageLoadListeners = new HashSet();
-
- public static final int DEFAULT_CACHE_SIZE = (24 /* MiB */* 1024 * 1024); // in bytes
-
- private DrawableMemCache mMemCache = new DrawableMemCache(DEFAULT_CACHE_SIZE);
-
- private Integer mIDCounter = 0;
-
- private static ImageCache mInstance;
-
- // this is a custom Executor, as we want to have the tasks loaded in FILO order. FILO works
- // particularly well when scrolling with a ListView.
- private final ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
- MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
- new PriorityBlockingQueue());
-
- // ignored as SparseArray isn't thread-safe
- private final Map jobs = Collections
- .synchronizedMap(new HashMap());
-
- private final HttpClient hc;
-
- private CompressFormat mCompressFormat;
- private int mQuality;
-
- private final Resources mRes;
-
- private static final int MSG_IMAGE_LOADED = 100;
-
- private final KeyedLock mDownloading = new KeyedLock();
-
- private static class ImageLoadHandler extends Handler {
- private final ImageCache mCache;
-
- public ImageLoadHandler(ImageCache cache) {
- super();
- mCache = cache;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_IMAGE_LOADED:
- mCache.notifyListeners((LoadResult) msg.obj);
- break;
- }
- };
- }
-
- private final ImageLoadHandler mHandler = new ImageLoadHandler(this);
-
- // TODO make it so this is customizable on the instance level.
- /**
- * Gets an instance of the cache.
- *
- * @return an instance of the cache
- */
- public static ImageCache getInstance(Context context) {
- if (mInstance == null) {
- mInstance = new ImageCache(context, CompressFormat.JPEG, 85);
- }
- return mInstance;
- }
-
- /**
- * Generally, it's best to use the shared image cache using {@link #getInstance(Context)}. Use
- * this if you want to customize a cache or keep it separate.
- */
- public ImageCache(Context context, CompressFormat format, int quality) {
- super(context.getCacheDir(), null, getExtension(format));
- if (USE_APACHE_NC) {
- hc = getHttpClient();
- } else {
- hc = null;
- }
-
- mRes = context.getResources();
-
- mCompressFormat = format;
- mQuality = quality;
- }
-
- /**
- * Sets the compression format for resized images.
- */
- public void setCompressFormat(CompressFormat format) {
- mCompressFormat = format;
- }
-
- /**
- * Set the image quality. Hint to the compressor, 0-100. 0 meaning compress for small size, 100
- * meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the
- * quality setting
- */
- public void setQuality(int quality) {
- mQuality = quality;
- }
-
- /**
- * Sets the maximum size of the memory cache. Note, this will clear the memory cache.
- *
- * @param maxSize
- * the maximum size of the memory cache in bytes.
- */
- public void setMemCacheMaxSize(int maxSize) {
- mMemCache = new DrawableMemCache(maxSize);
- }
-
- private static String getExtension(CompressFormat format) {
- String extension;
- switch (format) {
- case JPEG:
- extension = ".jpg";
- break;
-
- case PNG:
- extension = ".png";
- break;
-
- default:
- throw new IllegalArgumentException();
- }
-
- return extension;
- }
-
- /**
- * If loading a number of images where you don't have a unique ID to represent the individual
- * load, this can be used to generate a sequential ID.
- *
- * @return a new unique ID
- */
- public int getNewID() {
- synchronized (mIDCounter) {
- return mIDCounter++;
- }
- }
-
- @Override
- protected Bitmap fromDisk(String key, InputStream in) {
-
- if (DEBUG) {
- Log.d(TAG, "disk cache hit for key " + key);
- }
- try {
- final Bitmap image = BitmapFactory.decodeStream(in);
- return image;
-
- } catch (final OutOfMemoryError oom) {
- oomClear();
- return null;
- }
- }
-
- @Override
- protected void toDisk(String key, Bitmap image, OutputStream out) {
- if (DEBUG) {
- Log.d(TAG, "disk cache write for key " + key);
- }
- if (image != null) {
- if (!image.compress(mCompressFormat, mQuality, out)) {
- Log.e(TAG, "error writing compressed image to disk for key " + key);
- }
- } else {
- Log.e(TAG, "Ignoring attempt to write null image to disk cache");
- }
- }
-
- /**
- * Gets an instance of AndroidHttpClient if the devices has it (it was introduced in 2.2), or
- * falls back on a http client that should work reasonably well.
- *
- * @return a working instance of an HttpClient
- */
- private HttpClient getHttpClient() {
- HttpClient ahc;
- try {
- final Class> ahcClass = Class.forName("android.net.http.AndroidHttpClient");
- final Method newInstance = ahcClass.getMethod("newInstance", String.class);
- ahc = (HttpClient) newInstance.invoke(null, "ImageCache");
-
- } catch (final ClassNotFoundException e) {
- DefaultHttpClient dhc = new DefaultHttpClient();
- final HttpParams params = dhc.getParams();
- dhc = null;
-
- params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 20 * 1000);
-
- final SchemeRegistry registry = new SchemeRegistry();
- registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
-
- final ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(params,
- registry);
- ahc = new DefaultHttpClient(manager, params);
-
- } catch (final NoSuchMethodException e) {
-
- final RuntimeException re = new RuntimeException("Programming error");
- re.initCause(e);
- throw re;
-
- } catch (final IllegalAccessException e) {
- final RuntimeException re = new RuntimeException("Programming error");
- re.initCause(e);
- throw re;
-
- } catch (final InvocationTargetException e) {
- final RuntimeException re = new RuntimeException("Programming error");
- re.initCause(e);
- throw re;
- }
- return ahc;
- }
-
- /**
- *
- * Registers an {@link OnImageLoadListener} with the cache. When an image is loaded
- * asynchronously either directly by way of {@link #scheduleLoadImage(int, Uri, int, int)} or
- * indirectly by {@link #loadImage(int, Uri, int, int)}, any registered listeners will get
- * called.
- *
- *
- *
- * This should probably be called from {@link Activity#onResume()}.
- *
- */
- public void registerOnImageLoadListener(OnImageLoadListener onImageLoadListener) {
- mImageLoadListeners.add(onImageLoadListener);
- }
-
- /**
- *
- * Unregisters the listener with the cache. This will not cancel any pending load requests.
- *
- *
- *
- * This should probably be called from {@link Activity#onPause()}.
- *
- */
- public void unregisterOnImageLoadListener(OnImageLoadListener onImageLoadListener) {
- mImageLoadListeners.remove(onImageLoadListener);
- }
-
- private class LoadResult {
- public LoadResult(int id, Uri image, Drawable drawable) {
- this.id = id;
- this.drawable = drawable;
- this.image = image;
- }
-
- final Uri image;
- final int id;
- final Drawable drawable;
- }
-
- /**
- * @param uri
- * the image uri
- * @return a key unique to the given uri
- */
- public String getKey(Uri uri) {
- return uri.toString();
- }
-
- /**
- * Gets the given key as a drawable, retrieving it from memory cache if it's present.
- *
- * @param key
- * a key generated by {@link #getKey(Uri)} or {@link #getKey(Uri, int, int)}
- * @return the drawable if it's in the memory cache or null.
- */
- public Drawable getDrawable(String key) {
- final Drawable img = mMemCache.get(key);
- if (img != null) {
- if (DEBUG) {
- Log.d(TAG, "mem cache hit for key " + key);
- }
- touchKey(key);
- return img;
- }
-
- return null;
- }
-
- /**
- * Puts a drawable into memory cache.
- *
- * @param key
- * a key generated by {@link #getKey(Uri)} or {@link #getKey(Uri, int, int)}
- */
- public void putDrawable(String key, Drawable drawable) {
- mMemCache.put(key, drawable);
- }
-
- /**
- * A blocking call to get an image. If it's in the cache, it'll return the drawable immediately.
- * Otherwise it will download, scale, and cache the image before returning it. For non-blocking
- * use, see {@link #loadImage(int, Uri, int, int)}
- *
- * @throws ClientProtocolException
- * @throws IOException
- * @throws ImageCacheException
- */
- public Drawable getImage(Uri uri, int width, int height) throws ClientProtocolException,
- IOException, ImageCacheException {
-
- final String scaledKey = getKey(uri, width, height);
-
- mDownloading.lock(scaledKey);
-
- try {
- Drawable d = getDrawable(scaledKey);
- if (d != null) {
- return d;
- }
-
- Bitmap bmp = get(scaledKey);
-
- if (bmp == null) {
- if ("file".equals(uri.getScheme())) {
- bmp = scaleLocalImage(new File(uri.getPath()), width, height);
- } else {
- final String sourceKey = getKey(uri);
-
- mDownloading.lock(sourceKey);
-
- try {
- if (!contains(sourceKey)) {
- downloadImage(sourceKey, uri);
- }
- } finally {
- mDownloading.unlock(sourceKey);
- }
-
- bmp = scaleLocalImage(getFile(sourceKey), width, height);
- if (bmp == null) {
- clear(sourceKey);
- }
- }
- put(scaledKey, bmp);
-
- }
- if (bmp == null) {
- throw new ImageCacheException("got null bitmap from request to scale");
-
- }
- d = new BitmapDrawable(mRes, bmp);
- putDrawable(scaledKey, d);
-
- return d;
-
- } finally {
- mDownloading.unlock(scaledKey);
- }
- }
-
- private final SparseArray mKeyCache = new SparseArray();
-
- /**
- * Returns an opaque cache key representing the given uri, width and height.
- *
- * @param uri
- * an image uri
- * @param width
- * the desired image max width
- * @param height
- * the desired image max height
- * @return a cache key unique to the given parameters
- */
- public String getKey(Uri uri, int width, int height) {
- // collisions are possible, but unlikely.
- final int hashId = uri.hashCode() + width + height * 10000;
-
- String key = mKeyCache.get(hashId);
- if (key == null) {
- key = uri.buildUpon().appendQueryParameter("width", String.valueOf(width))
- .appendQueryParameter("height", String.valueOf(height)).build().toString();
- mKeyCache.put(hashId, key);
- }
- return key;
- }
-
- @Override
- public synchronized boolean clear() {
- final boolean success = super.clear();
-
- mMemCache.evictAll();
-
- mKeyCache.clear();
-
- return success;
- }
-
- @Override
- public synchronized boolean clear(String key) {
- final boolean success = super.clear(key);
-
- mMemCache.remove(key);
-
- return success;
- }
-
- private class ImageLoadTask implements Runnable, Comparable {
- private final int id;
- private final Uri uri;
- private final int width;
- private final int height;
- private final long when = System.nanoTime();
-
- public ImageLoadTask(int id, Uri image, int width, int height) {
- this.id = id;
- this.uri = image;
- this.width = width;
- this.height = height;
- }
-
- @Override
- public void run() {
-
- if (DEBUG) {
- Log.d(TAG, "ImageLoadTask.doInBackground(" + id + ", " + uri + ", " + width + ", "
- + height + ")");
- }
-
- try {
- final LoadResult result = new LoadResult(id, uri, getImage(uri, width, height));
- synchronized (jobs) {
- if (jobs.containsKey(id)) {
- // Job still valid.
- jobs.remove(id);
- mHandler.obtainMessage(MSG_IMAGE_LOADED, result).sendToTarget();
- }
- }
-
- // TODO this exception came about, no idea why:
- // java.lang.IllegalArgumentException: Parser may not be null
- } catch (final IllegalArgumentException e) {
- Log.e(TAG, e.getLocalizedMessage(), e);
- } catch (final OutOfMemoryError oom) {
- oomClear();
- } catch (final ClientProtocolException e) {
- Log.e(TAG, e.getLocalizedMessage(), e);
- } catch (final IOException e) {
- Log.e(TAG, e.getLocalizedMessage(), e);
- } catch (final ImageCacheException e) {
- Log.e(TAG, e.getLocalizedMessage(), e);
- }
- }
-
- @Override
- public int compareTo(ImageLoadTask another) {
- return Long.valueOf(another.when).compareTo(when);
- };
- }
-
- private void oomClear() {
- Log.w(TAG, "out of memory, clearing mem cache");
- mMemCache.evictAll();
- }
-
- /**
- * Checks the cache for an image matching the given criteria and returns it. If it isn't
- * immediately available, calls {@link #scheduleLoadImage}.
- *
- * @param id
- * An ID to keep track of image load requests. For one-off loads, this can just be
- * the ID of the {@link ImageView}. Otherwise, an unique ID can be acquired using
- * {@link #getNewID()}.
- *
- * @param image
- * the image to be loaded. Can be a local file or a network resource.
- * @param width
- * the maximum width of the resulting image
- * @param height
- * the maximum height of the resulting image
- * @return the cached bitmap if it's available immediately or null if it needs to be loaded
- * asynchronously.
- */
- public Drawable loadImage(int id, Uri image, int width, int height) throws IOException {
- if (DEBUG) {
- Log.d(TAG, "loadImage(" + id + ", " + image + ", " + width + ", " + height + ")");
- }
- final Drawable res = getDrawable(getKey(image, width, height));
- if (res == null) {
- if (DEBUG) {
- Log.d(TAG,
- "Image not found in memory cache. Scheduling load from network / disk...");
- }
- scheduleLoadImage(id, image, width, height);
- }
- return res;
- }
-
- /**
- * Deprecated to make IDs ints instead of longs. See {@link #loadImage(int, Uri, int, int)}.
- *
- * @throws IOException
- */
- @Deprecated
- public Drawable loadImage(long id, Uri image, int width, int height) throws IOException {
- return loadImage(id, image, width, height);
- }
-
- /**
- * Schedules a load of the given image. When the image has finished loading and scaling, all
- * registered {@link OnImageLoadListener}s will be called.
- *
- * @param id
- * An ID to keep track of image load requests. For one-off loads, this can just be
- * the ID of the {@link ImageView}. Otherwise, an unique ID can be acquired using
- * {@link #getNewID()}.
- *
- * @param image
- * the image to be loaded. Can be a local file or a network resource.
- * @param width
- * the maximum width of the resulting image
- * @param height
- * the maximum height of the resulting image
- */
- public void scheduleLoadImage(int id, Uri image, int width, int height) {
- if (DEBUG) {
- Log.d(TAG, "executing new ImageLoadTask in background...");
- }
- final ImageLoadTask imt = new ImageLoadTask(id, image, width, height);
-
- jobs.put(id, imt);
- mExecutor.execute(imt);
- }
-
- /**
- * Deprecated in favour of {@link #scheduleLoadImage(int, Uri, int, int)}.
- */
- @Deprecated
- public void scheduleLoadImage(long id, Uri image, int width, int height) {
- scheduleLoadImage(id, image, width, height);
- }
-
- /**
- * Cancels all the asynchronous image loads. Note: currently does not function properly.
- *
- */
- public void cancelLoads() {
- jobs.clear();
- mExecutor.getQueue().clear();
- }
-
- public void cancel(int id) {
- synchronized (jobs) {
- final Runnable job = jobs.get(id);
- if (job != null) {
- jobs.remove(id);
- mExecutor.remove(job);
- if (DEBUG) {
- Log.d(TAG, "removed load id " + id);
- }
- }
- }
- }
-
- /**
- * Deprecated in favour of {@link #cancel(int)}.
- */
- @Deprecated
- public void cancel(long id) {
- cancel(id);
- }
-
- /**
- * Blocking call to scale a local file. Scales using preserving aspect ratio
- *
- * @param localFile
- * local image file to be scaled
- * @param width
- * maximum width
- * @param height
- * maximum height
- * @return the scaled image
- * @throws ClientProtocolException
- * @throws IOException
- */
- private static Bitmap scaleLocalImage(File localFile, int width, int height)
- throws ClientProtocolException, IOException {
-
- if (DEBUG) {
- Log.d(TAG, "scaleLocalImage(" + localFile + ", " + width + ", " + height + ")");
- }
-
- if (!localFile.exists()) {
- throw new IOException("local file does not exist: " + localFile);
- }
- if (!localFile.canRead()) {
- throw new IOException("cannot read from local file: " + localFile);
- }
-
- // the below borrowed from:
- // https://github.com/thest1/LazyList/blob/master/src/com/fedorvlasov/lazylist/ImageLoader.java
-
- // decode image size
- final BitmapFactory.Options o = new BitmapFactory.Options();
- o.inJustDecodeBounds = true;
-
- BitmapFactory.decodeStream(new FileInputStream(localFile), null, o);
-
- // Find the correct scale value. It should be the power of 2.
- //final int REQUIRED_WIDTH = width, REQUIRED_HEIGHT = height;
- int width_tmp = o.outWidth, height_tmp = o.outHeight;
- int scale = 1;
- while (true) {
- if (width_tmp / 2 <= width || height_tmp / 2 <= height) {
- break;
- }
- width_tmp /= 2;
- height_tmp /= 2;
- scale *= 2;
- }
-
- // decode with inSampleSize
- final BitmapFactory.Options o2 = new BitmapFactory.Options();
- o2.inSampleSize = scale;
- final Bitmap prescale = BitmapFactory
- .decodeStream(new FileInputStream(localFile), null, o2);
-
- if (prescale == null) {
- Log.e(TAG, localFile + " could not be decoded");
- } else if (DEBUG) {
- Log.d(TAG, "Successfully completed scaling of " + localFile + " to " + width + "x"
- + height);
- }
-
- return prescale;
- }
-
- /**
- * Blocking call to download an image. The image is placed directly into the disk cache at the
- * given key.
- *
- * @param uri
- * the location of the image
- * @throws ClientProtocolException
- * if the HTTP response code wasn't 200 or any other HTTP errors
- * @throws IOException
- */
- protected void downloadImage(String key, Uri uri) throws ClientProtocolException, IOException {
- if (DEBUG) {
- Log.d(TAG, "downloadImage(" + key + ", " + uri + ")");
- }
- if (USE_APACHE_NC) {
- final HttpGet get = new HttpGet(uri.toString());
- final HttpParams params = get.getParams();
- params.setParameter(ClientPNames.HANDLE_REDIRECTS, true);
-
- final HttpResponse hr = hc.execute(get);
- final StatusLine hs = hr.getStatusLine();
- if (hs.getStatusCode() != 200) {
- throw new HttpResponseException(hs.getStatusCode(), hs.getReasonPhrase());
- }
-
- final HttpEntity ent = hr.getEntity();
-
- // TODO I think this means that the source file must be a jpeg. fix this.
- try {
-
- putRaw(key, ent.getContent());
- if (DEBUG) {
- Log.d(TAG, "source file of " + uri + " saved to disk cache at location "
- + getFile(key).getAbsolutePath());
- }
- } finally {
- ent.consumeContent();
- }
- } else {
- final URLConnection con = new URL(uri.toString()).openConnection();
- putRaw(key, con.getInputStream());
- if (DEBUG) {
- Log.d(TAG,
- "source file of " + uri + " saved to disk cache at location "
- + getFile(key).getAbsolutePath());
- }
- }
-
- }
-
- private void notifyListeners(LoadResult result) {
- for (final OnImageLoadListener listener : mImageLoadListeners) {
- listener.onImageLoaded(result.id, result.image, result.drawable);
- }
- }
-
- /**
- * Implement this and register it using
- * {@link ImageCache#registerOnImageLoadListener(OnImageLoadListener)} to be notified when
- * asynchronous image loads have completed.
- *
- * @author Steve Pomeroy
- *
- */
- public interface OnImageLoadListener {
- /**
- * Called when the image has been loaded and scaled.
- *
- * @param id
- * the ID provided by {@link ImageCache#loadImage(int, Uri, int, int)} or
- * {@link ImageCache#scheduleLoadImage(int, Uri, int, int)}
- * @param imageUri
- * the uri of the image that was originally requested
- * @param image
- * the loaded and scaled image
- */
- public void onImageLoaded(int id, Uri imageUri, Drawable image);
- }
-}
diff --git a/astrid/src/main/java/edu/mit/mobile/android/imagecache/ImageCacheException.java b/astrid/src/main/java/edu/mit/mobile/android/imagecache/ImageCacheException.java
deleted file mode 100644
index e2c9f0903..000000000
--- a/astrid/src/main/java/edu/mit/mobile/android/imagecache/ImageCacheException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package edu.mit.mobile.android.imagecache;
-
-/*
- * Copyright (C) 2011-2012 MIT Mobile Experience Lab
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-public class ImageCacheException extends Exception {
-
- /**
- *
- */
- private static final long serialVersionUID = 997874306474290980L;
-
- public ImageCacheException(String message) {
- super(message);
- }
-}
diff --git a/astrid/src/main/java/edu/mit/mobile/android/imagecache/KeyedLock.java b/astrid/src/main/java/edu/mit/mobile/android/imagecache/KeyedLock.java
deleted file mode 100644
index a323bb647..000000000
--- a/astrid/src/main/java/edu/mit/mobile/android/imagecache/KeyedLock.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package edu.mit.mobile.android.imagecache;
-
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * A synchronization lock that creates a separate lock for each key.
- *
- * @author Steve Pomeroy
- *
- * @param
- */
-public class KeyedLock {
- private static final String TAG = KeyedLock.class.getSimpleName();
-
- private final Map mLocks = new HashMap();
-
- private static boolean DEBUG = false;
-
- public void lock(K key) {
- if (DEBUG) {
- log("acquiring lock for key " + key);
- }
-
- ReentrantLock lock;
- synchronized (mLocks) {
- lock = mLocks.get(key);
- if (lock == null) {
- lock = new ReentrantLock();
- mLocks.put(key, lock);
- if (DEBUG) {
- log(lock + " created new lock and added it to map");
- }
-
- }
- }
-
- lock.lock();
- }
-
- public void unlock(K key) {
- if (DEBUG) {
- log("unlocking lock for key " + key);
- }
- ReentrantLock lock;
-
- synchronized (mLocks) {
- lock = mLocks.get(key);
- if (lock == null) {
- Log.e(TAG, "Attempting to unlock lock for key " + key + " which has no entry");
- return;
- }
- if (DEBUG) {
- log(lock + " has queued threads " + lock.hasQueuedThreads() + " for key " + key);
- }
- // maybe entries should be removed when there are no queued threads. This would
- // occasionally fail...
- // final boolean queued = lock.hasQueuedThreads();
-
- lock.unlock();
- }
- }
-
- private void log(String message) {
-
- Log.d(TAG, Thread.currentThread().getId() + "\t" + message);
-
- }
-}
diff --git a/astrid/src/main/res/layout/update_adapter_row.xml b/astrid/src/main/res/layout/update_adapter_row.xml
index 382cc81c1..26e0656c6 100644
--- a/astrid/src/main/res/layout/update_adapter_row.xml
+++ b/astrid/src/main/res/layout/update_adapter_row.xml
@@ -49,12 +49,12 @@
-