├── .gitignore ├── DownloaderAsyncTask.cs ├── ExtensionMethods.cs ├── Hashset.cs ├── LRUCache.cs ├── MonoDroid.UrlImageViewHelper.csproj ├── README.md ├── SoftReference.cs ├── SoftReferenceHashTable.cs ├── UrlImageCache.cs ├── UrlImageViewCallback.cs └── UrlImageViewHelper.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | bin 3 | obj 4 | 5 | # mstest test results 6 | TestResults -------------------------------------------------------------------------------- /DownloaderAsyncTask.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | 8 | using Android.App; 9 | using Android.Content; 10 | using Android.Graphics.Drawables; 11 | using Android.Net.Http; 12 | using Android.OS; 13 | using Android.Runtime; 14 | using Android.Views; 15 | using Android.Widget; 16 | 17 | namespace UrlImageViewHelper 18 | { 19 | public class AnonymousAsyncTask : AsyncTask 20 | { 21 | public AnonymousAsyncTask(Func runInBackgroundFunc, Action postExecuteAction) 22 | { 23 | this.RunInBackgroundFunc = runInBackgroundFunc; 24 | this.PostExecuteAction = postExecuteAction; 25 | } 26 | 27 | public Func RunInBackgroundFunc; 28 | public Action PostExecuteAction; 29 | 30 | protected override TResult RunInBackground (params TParam[] @params) 31 | { 32 | return this.RunInBackgroundFunc(@params); 33 | } 34 | 35 | protected override Java.Lang.Object DoInBackground (params Java.Lang.Object[] native_parms) 36 | { 37 | return base.DoInBackground (native_parms); 38 | } 39 | 40 | protected override void OnPostExecute (TResult result) 41 | { 42 | this.PostExecuteAction(result); 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using Android.App; 8 | using Android.Content; 9 | using Android.Graphics.Drawables; 10 | using Android.OS; 11 | using Android.Runtime; 12 | using Android.Views; 13 | using Android.Widget; 14 | 15 | namespace UrlImageViewHelper 16 | { 17 | public static class ExtensionMethods 18 | { 19 | public static void SetUrlDrawable(this ImageView imageView, string url, int defaultResource) 20 | { 21 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultResource); 22 | } 23 | 24 | public static void SetUrlDrawable(this ImageView imageView, string url) 25 | { 26 | UrlImageViewHelper.SetUrlDrawable(imageView, url); 27 | } 28 | 29 | public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable) 30 | { 31 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable); 32 | } 33 | 34 | public static void SetUrlDrawable(this ImageView imageView, string url, int defaultResource, long cacheDurationMs) 35 | { 36 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultResource, cacheDurationMs); 37 | } 38 | 39 | public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs) 40 | { 41 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable, cacheDurationMs); 42 | } 43 | 44 | public static void SetUrlDrawable(this ImageView imageView, string url, IUrlImageViewCallback callback) 45 | { 46 | UrlImageViewHelper.SetUrlDrawable(imageView, url, callback); 47 | } 48 | 49 | public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable, IUrlImageViewCallback callback) 50 | { 51 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable, callback); 52 | } 53 | 54 | public static void SetUrlDrawable(this ImageView imageView, string url, int defaultResource, long cacheDurationMs, IUrlImageViewCallback callback) 55 | { 56 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultResource, cacheDurationMs, callback); 57 | } 58 | 59 | public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs, IUrlImageViewCallback callback) 60 | { 61 | UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable, cacheDurationMs, callback); 62 | } 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /Hashset.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using Android.App; 8 | using Android.Content; 9 | using Android.OS; 10 | using Android.Runtime; 11 | using Android.Views; 12 | using Android.Widget; 13 | 14 | namespace UrlImageViewHelper 15 | { 16 | public class HashTable 17 | { 18 | object lockObj = new object(); 19 | Dictionary hashset = new Dictionary(); 20 | 21 | public void Put(TKey key, TValue value) 22 | { 23 | lock(lockObj) 24 | { 25 | if (hashset.ContainsKey(key)) 26 | hashset[key] = value; 27 | else 28 | hashset.Add(key, value); 29 | } 30 | } 31 | 32 | public TValue Get(TKey key) 33 | { 34 | TValue result = default(TValue); 35 | 36 | lock (lockObj) 37 | { 38 | if (hashset.ContainsKey(key)) 39 | result = hashset[key]; 40 | } 41 | 42 | return result; 43 | } 44 | 45 | public void Remove(TKey key) 46 | { 47 | lock(lockObj) 48 | { 49 | hashset.Remove(key); 50 | } 51 | } 52 | 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /LRUCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UrlImageViewHelper 7 | { 8 | public class LRUCache : IDictionary 9 | { 10 | object sync = new object(); 11 | Dictionary data; 12 | IndexedLinkedList lruList = new IndexedLinkedList(); 13 | ICollection> dataAsCollection; 14 | int capacity; 15 | 16 | public LRUCache(int capacity) 17 | { 18 | 19 | if (capacity <= 0) 20 | { 21 | throw new ArgumentException("capacity should always be bigger than 0"); 22 | } 23 | 24 | data = new Dictionary(capacity); 25 | dataAsCollection = data; 26 | this.capacity = capacity; 27 | } 28 | 29 | public void Add(TKey key, TValue value) 30 | { 31 | if (!ContainsKey(key)) 32 | { 33 | this[key] = value; 34 | } 35 | else 36 | { 37 | throw new ArgumentException("An attempt was made to insert a duplicate key in the cache."); 38 | } 39 | } 40 | 41 | public bool ContainsKey(TKey key) 42 | { 43 | return data.ContainsKey(key); 44 | } 45 | 46 | public ICollection Keys 47 | { 48 | get 49 | { 50 | return data.Keys; 51 | } 52 | } 53 | 54 | public bool Remove(TKey key) 55 | { 56 | bool existed = data.Remove(key); 57 | lruList.Remove(key); 58 | return existed; 59 | } 60 | 61 | public bool TryGetValue(TKey key, out TValue value) 62 | { 63 | return data.TryGetValue(key, out value); 64 | } 65 | 66 | public ICollection Values 67 | { 68 | get { return data.Values; } 69 | } 70 | 71 | public TValue this[TKey key] 72 | { 73 | get 74 | { 75 | var value = data[key]; 76 | lruList.Remove(key); 77 | lruList.Add(key); 78 | return value; 79 | } 80 | set 81 | { 82 | data[key] = value; 83 | lruList.Remove(key); 84 | lruList.Add(key); 85 | 86 | if (data.Count > capacity) 87 | { 88 | Remove(lruList.First); 89 | lruList.RemoveFirst(); 90 | } 91 | } 92 | } 93 | 94 | public void Add(KeyValuePair item) 95 | { 96 | Add(item.Key, item.Value); 97 | } 98 | 99 | public void Clear() 100 | { 101 | data.Clear(); 102 | lruList.Clear(); 103 | } 104 | 105 | public bool Contains(KeyValuePair item) 106 | { 107 | return dataAsCollection.Contains(item); 108 | } 109 | 110 | public void ReclaimLRU(int itemsToReclaim) 111 | { 112 | while (--itemsToReclaim >= 0 && lruList.First != null) 113 | Remove(lruList.First); 114 | } 115 | 116 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 117 | { 118 | dataAsCollection.CopyTo(array, arrayIndex); 119 | } 120 | 121 | public int Count 122 | { 123 | get { return data.Count; } 124 | } 125 | 126 | public bool IsReadOnly 127 | { 128 | get { return false; } 129 | } 130 | 131 | public bool Remove(KeyValuePair item) 132 | { 133 | 134 | bool removed = dataAsCollection.Remove(item); 135 | if (removed) 136 | { 137 | lruList.Remove(item.Key); 138 | } 139 | return removed; 140 | } 141 | 142 | 143 | public IEnumerator> GetEnumerator() 144 | { 145 | return dataAsCollection.GetEnumerator(); 146 | } 147 | 148 | 149 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 150 | { 151 | return ((System.Collections.IEnumerable)data).GetEnumerator(); 152 | } 153 | 154 | } 155 | 156 | public class IndexedLinkedList 157 | { 158 | 159 | LinkedList data = new LinkedList(); 160 | Dictionary> index = new Dictionary>(); 161 | 162 | public void Add(T value) 163 | { 164 | index[value] = data.AddLast(value); 165 | } 166 | 167 | public void RemoveFirst() 168 | { 169 | index.Remove(data.First.Value); 170 | data.RemoveFirst(); 171 | } 172 | 173 | public void Remove(T value) 174 | { 175 | LinkedListNode node; 176 | if (index.TryGetValue(value, out node)) 177 | { 178 | data.Remove(node); 179 | index.Remove(value); 180 | } 181 | } 182 | 183 | public int Count 184 | { 185 | get 186 | { 187 | return data.Count; 188 | } 189 | } 190 | 191 | public void Clear() 192 | { 193 | data.Clear(); 194 | index.Clear(); 195 | } 196 | 197 | public T First 198 | { 199 | get 200 | { 201 | return data.First.Value; 202 | } 203 | } 204 | } 205 | 206 | } -------------------------------------------------------------------------------- /MonoDroid.UrlImageViewHelper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 10.0.0 7 | 2.0 8 | {57444E6A-23D9-443A-BA83-040CB9561E66} 9 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | UrlImageViewHelper 12 | Resources 13 | Assets 14 | UrlImageViewHelper 15 | 16 | 17 | True 18 | full 19 | False 20 | bin\Debug 21 | DEBUG; 22 | prompt 23 | 4 24 | False 25 | None 26 | 27 | 28 | none 29 | True 30 | bin\Release 31 | prompt 32 | 4 33 | False 34 | SdkOnly 35 | False 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MonoDroid.UrlImageViewHelper 2 | ============================ 3 | 4 | C# / Mono for Android Port of koush's UrlImageViewHelper 5 | 6 | You can see Koush's original project here: https://github.com/koush/UrlImageViewHelper 7 | 8 | Sample Usage 9 | ------------ 10 | 11 | It's really easy to use, there are a number of ExtensionMethods in the UrlImageViewHelper namespace for the ImageView class that take a couple different arguments as options. Here's a very simple example of loading an image from a url, but initially displaying a default image while the image from a url is loading: 12 | 13 | ```csharp 14 | var img = this.FindViewById(Resource.Id.SomeImageView); 15 | img.SetUrlDrawable("http://site.com/image.png", Resource.Drawable.DefaultUrlImage); 16 | ``` 17 | -------------------------------------------------------------------------------- /SoftReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using Android.App; 7 | using Android.Content; 8 | using Android.OS; 9 | using Android.Runtime; 10 | using Android.Views; 11 | using Android.Widget; 12 | 13 | namespace UrlImageViewHelper 14 | { 15 | //class SoftReference : WeakReference { public SoftReference(object target) : base(target) { NonWebCache[Guid.NewGuid().ToString()] = target; } } 16 | 17 | // Switched to trusting the GC for mono, so using WeakReference instead 18 | // public class SoftReference : WeakReference /* where T : Object */ { 19 | // public SoftReference(T target) : base(target) 20 | // { 21 | // } 22 | public class SoftReference 23 | { 24 | public SoftReference(T target) 25 | { 26 | _target = target; 27 | } 28 | 29 | T _target = default(T); 30 | 31 | public T Get() 32 | { 33 | return _target; 34 | 35 | //Android.Util.Log.Debug(UrlImageViewHelper.LOGTAG, "SoftReference OK? " + base.IsAlive.ToString()); 36 | 37 | //if (!base.IsAlive) 38 | //{ 39 | // Android.Util.Log.Debug(UrlImageViewHelper.LOGTAG, "Lost SoftReference"); 40 | // return default(T); 41 | 42 | // //throw new Exception("Lost SoftReference"); 43 | //} 44 | //return (T)base.Target; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /SoftReferenceHashTable.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | using Android.App; 9 | using Android.Content; 10 | using Android.OS; 11 | using Android.Runtime; 12 | using Android.Views; 13 | using Android.Widget; 14 | 15 | namespace UrlImageViewHelper 16 | { 17 | public class SoftReferenceHashTable 18 | { 19 | /* 20 | Dictionary> table = new Dictionary>(); 21 | 22 | public TValue Put(TKey key, TValue value) 23 | { 24 | var newVal = new SoftReference(value); 25 | 26 | if (table.ContainsKey(key)) 27 | table[key] = newVal; 28 | else 29 | table.Add(key, newVal); 30 | 31 | return newVal.Get(); 32 | } 33 | 34 | public TValue Get(TKey key) 35 | { 36 | if (!table.ContainsKey(key)) 37 | return default(TValue); 38 | 39 | var val = table[key]; 40 | 41 | if (val == null || val.Get() == null) 42 | { 43 | Android.Util.Log.Debug(UrlImageViewHelper.LOGTAG, key.ToString() + " Lost Reference"); 44 | table.Remove(key); 45 | return default(TValue); 46 | } 47 | 48 | return val.Get(); 49 | } 50 | */ 51 | 52 | object cacheLock = new object(); 53 | LRUCache cache = new LRUCache(100); 54 | 55 | public TValue Put(TKey key, TValue value) 56 | { 57 | //var newVal = new SoftReference(value); 58 | lock (cacheLock) 59 | { 60 | if (cache.ContainsKey(key)) 61 | cache[key] = value; 62 | else 63 | cache.Add(key, value); 64 | 65 | return value; 66 | } 67 | } 68 | 69 | public TValue Get(TKey key) 70 | { 71 | lock (cacheLock) 72 | { 73 | if (!cache.ContainsKey(key)) 74 | return default(TValue); 75 | 76 | var val = cache[key]; 77 | 78 | if (val == null) 79 | { 80 | Android.Util.Log.Debug(UrlImageViewHelper.LOGTAG, key.ToString() + " Lost Reference"); 81 | cache.Remove(key); 82 | return default(TValue); 83 | } 84 | 85 | return val; 86 | } 87 | } 88 | // 89 | // Hashtable> mTable = new Hashtable>(); 90 | // 91 | // public V put(K key, V value) { 92 | // SoftReference old = mTable.put(key, new SoftReference(value)); 93 | // if (old == null) 94 | // return null; 95 | // return old.get(); 96 | // } 97 | // 98 | // public V get(K key) { 99 | // SoftReference val = mTable.get(key); 100 | // if (val == null) 101 | // return null; 102 | // V ret = val.get(); 103 | // if (ret == null) 104 | // mTable.remove(key); 105 | // return ret; 106 | // } 107 | } 108 | } 109 | 110 | -------------------------------------------------------------------------------- /UrlImageCache.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using Android.App; 8 | using Android.Content; 9 | using Android.Graphics.Drawables; 10 | using Android.OS; 11 | using Android.Runtime; 12 | using Android.Views; 13 | using Android.Widget; 14 | 15 | namespace UrlImageViewHelper 16 | { 17 | public class UrlImageCache : SoftReferenceHashTable 18 | { 19 | static UrlImageCache instance; 20 | 21 | public static UrlImageCache Instance 22 | { 23 | get 24 | { 25 | if (instance == null) 26 | instance = new UrlImageCache(); 27 | 28 | return instance; 29 | } 30 | } 31 | 32 | 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /UrlImageViewCallback.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using Android.App; 8 | using Android.Content; 9 | using Android.Graphics.Drawables; 10 | using Android.OS; 11 | using Android.Runtime; 12 | using Android.Views; 13 | using Android.Widget; 14 | 15 | namespace UrlImageViewHelper 16 | { 17 | public interface IUrlImageViewCallback 18 | { 19 | void OnLoaded(ImageView imageView, Drawable loadedDrawable, string url, bool loadedFromCache); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /UrlImageViewHelper.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using Android.App; 8 | using Android.Content; 9 | using Android.Graphics.Drawables; 10 | using Android.OS; 11 | using Android.Runtime; 12 | using Android.Views; 13 | using Android.Widget; 14 | 15 | namespace UrlImageViewHelper 16 | { 17 | public class UrlImageViewHelper 18 | { 19 | public const string LOGTAG = "UrlImageViewHelper"; 20 | public const int CACHE_DURATION_INFINITE = int.MaxValue; 21 | public const int CACHE_DURATION_ONE_DAY = 1000 * 60 * 60 * 24; 22 | public const int CACHE_DURATION_TWO_DAYS = CACHE_DURATION_ONE_DAY * 2; 23 | public const int CACHE_DURATION_THREE_DAYS = CACHE_DURATION_ONE_DAY * 3; 24 | public const int CACHE_DURATION_FOUR_DAYS = CACHE_DURATION_ONE_DAY * 4; 25 | public const int CACHE_DURATION_FIVE_DAYS = CACHE_DURATION_ONE_DAY * 5; 26 | public const int CACHE_DURATION_SIX_DAYS = CACHE_DURATION_ONE_DAY * 6; 27 | public const int CACHE_DURATION_ONE_WEEK = CACHE_DURATION_ONE_DAY * 7; 28 | 29 | public static void SetUrlDrawable(ImageView imageView, string url, int defaultResource) 30 | { 31 | SetUrlDrawable(imageView.Context, imageView, url, defaultResource, CACHE_DURATION_THREE_DAYS); 32 | } 33 | 34 | public static void SetUrlDrawable(ImageView imageView, string url) 35 | { 36 | SetUrlDrawable(imageView.Context, imageView, url, null, CACHE_DURATION_THREE_DAYS, null); 37 | } 38 | 39 | public static void LoadDrawable(Context context, string url) 40 | { 41 | SetUrlDrawable(context, null, url, null, CACHE_DURATION_THREE_DAYS, null); 42 | } 43 | 44 | public static void SetUrlDrawable(ImageView imageView, string url, Drawable defaultDrawable) 45 | { 46 | SetUrlDrawable(imageView.Context, imageView, url, defaultDrawable, CACHE_DURATION_THREE_DAYS, null); 47 | } 48 | 49 | public static void SetUrlDrawable(ImageView imageView, string url, int defaultResource, long cacheDurationMs) 50 | { 51 | SetUrlDrawable(imageView.Context, imageView, url, defaultResource, cacheDurationMs); 52 | } 53 | 54 | public static void LoadUrlDrawable(Context context, string url, long cacheDurationMs) 55 | { 56 | SetUrlDrawable(context, null, url, null, cacheDurationMs, null); 57 | } 58 | 59 | public static void SetUrlDrawable(ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs) 60 | { 61 | SetUrlDrawable(imageView.Context, imageView, url, defaultDrawable, cacheDurationMs, null); 62 | } 63 | 64 | private static void SetUrlDrawable(Context context, ImageView imageView, string url, int defaultResource, long cacheDurationMs) 65 | { 66 | Drawable d = null; 67 | if (defaultResource != 0) 68 | d = imageView.Resources.GetDrawable(defaultResource); 69 | SetUrlDrawable(context, imageView, url, d, cacheDurationMs, null); 70 | } 71 | 72 | public static void SetUrlDrawable(ImageView imageView, string url, IUrlImageViewCallback callback) 73 | { 74 | SetUrlDrawable(imageView.Context, imageView, url, null, CACHE_DURATION_THREE_DAYS, callback); 75 | } 76 | 77 | public static void LoadUrlDrawable(Context context, string url, IUrlImageViewCallback callback) 78 | { 79 | SetUrlDrawable(context, null, url, null, CACHE_DURATION_THREE_DAYS, callback); 80 | } 81 | 82 | public static void SetUrlDrawable(ImageView imageView, string url, Drawable defaultDrawable, IUrlImageViewCallback callback) 83 | { 84 | SetUrlDrawable(imageView.Context, imageView, url, defaultDrawable, CACHE_DURATION_THREE_DAYS, callback); 85 | } 86 | 87 | public static void SetUrlDrawable(ImageView imageView, string url, int defaultResource, long cacheDurationMs, IUrlImageViewCallback callback) 88 | { 89 | SetUrlDrawable(imageView.Context, imageView, url, defaultResource, cacheDurationMs, callback); 90 | } 91 | 92 | public static void LoadUrlDrawable(Context context, string url, long cacheDurationMs, IUrlImageViewCallback callback) 93 | { 94 | SetUrlDrawable(context, null, url, null, cacheDurationMs, callback); 95 | } 96 | 97 | public static void SetUrlDrawable(ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs, IUrlImageViewCallback callback) 98 | { 99 | SetUrlDrawable(imageView.Context, imageView, url, defaultDrawable, cacheDurationMs, callback); 100 | } 101 | 102 | private static void SetUrlDrawable(Context context, ImageView imageView, string url, int defaultResource, long cacheDurationMs, IUrlImageViewCallback callback) 103 | { 104 | Drawable d = null; 105 | if (defaultResource != 0) 106 | d = imageView.Resources.GetDrawable(defaultResource); 107 | SetUrlDrawable(context, imageView, url, d, cacheDurationMs, callback); 108 | } 109 | 110 | protected static HashTable pendingViews = new HashTable(); 111 | protected static HashTable> pendingDownloads = new HashTable>(); 112 | static bool hasCleaned = false; 113 | 114 | static Android.Content.Res.Resources mResources; 115 | static Android.Util.DisplayMetrics mMetrics; 116 | 117 | private static void PrepareResources(Context context) 118 | { 119 | if (mMetrics != null) 120 | return; 121 | mMetrics = new Android.Util.DisplayMetrics(); 122 | Activity act = (Activity)context; 123 | act.WindowManager.DefaultDisplay.GetMetrics(mMetrics); 124 | var mgr = context.Assets; 125 | mResources = new Android.Content.Res.Resources(mgr, mMetrics, context.Resources.Configuration); 126 | } 127 | 128 | public static BitmapDrawable LoadDrawableFromFile(Context context, string filename) 129 | { 130 | PrepareResources(context); 131 | 132 | var bitmap = Android.Graphics.BitmapFactory.DecodeFile(filename); 133 | 134 | //var bitmap = Android.Graphics.BitmapFactory.DecodeStream(stream); 135 | //Log.i(LOGTAG, String.format("Loaded bitmap (%dx%d).", bitmap.getWidth(), bitmap.getHeight())); 136 | return new BitmapDrawable(mResources, bitmap); 137 | } 138 | 139 | private static void Cleanup(Context context) 140 | { 141 | if (hasCleaned) 142 | return; 143 | 144 | hasCleaned = true; 145 | 146 | try 147 | { 148 | var baseDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); 149 | 150 | var files = System.IO.Directory.GetFiles(baseDir); 151 | 152 | if (files == null || files.Length <= 0) 153 | return; 154 | 155 | foreach (var file in files) 156 | { 157 | if (!file.EndsWith(".urlimage")) 158 | continue; 159 | 160 | 161 | var f = new System.IO.FileInfo(file); 162 | if (DateTime.UtcNow > f.LastWriteTimeUtc) 163 | f.Delete(); 164 | } 165 | } 166 | catch { } 167 | } 168 | 169 | private static string GetFilenameForUrl(string url) 170 | { 171 | var hashCode = url.GetHashCode().ToString().Replace("-", "N"); 172 | return hashCode + ".urlimage"; 173 | } 174 | 175 | private static void SetUrlDrawable(Context context, ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs, IUrlImageViewCallback callback) 176 | { 177 | Cleanup(context); 178 | 179 | if (imageView != null) 180 | pendingViews.Remove(imageView); 181 | 182 | if (string.IsNullOrEmpty(url)) 183 | { 184 | if (imageView != null) 185 | imageView.SetImageDrawable(defaultDrawable); 186 | 187 | return; 188 | } 189 | 190 | var cache = UrlImageCache.Instance; 191 | var drawable = cache.Get(url); 192 | 193 | if (drawable != null) 194 | { 195 | if (imageView != null) 196 | imageView.SetImageDrawable(drawable); 197 | if (callback != null) 198 | callback.OnLoaded(imageView, drawable, url, true); 199 | return; 200 | } 201 | 202 | var baseDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); 203 | var filename = System.IO.Path.Combine(baseDir, GetFilenameForUrl(url)); 204 | 205 | var file = new System.IO.FileInfo(filename); 206 | if (file.Exists) 207 | { 208 | try 209 | { 210 | if (cacheDurationMs == CACHE_DURATION_INFINITE || DateTime.UtcNow < file.LastWriteTimeUtc.AddMilliseconds(cacheDurationMs)) 211 | { 212 | drawable = LoadDrawableFromFile(context, filename); 213 | //var fis = context.OpenFileInput(filename); 214 | //drawable = LoadDrawableFromStream(context, fis); 215 | //fis.Close(); 216 | 217 | if (imageView != null) 218 | imageView.SetImageDrawable(drawable); 219 | 220 | cache.Put(url, drawable); 221 | 222 | if (callback != null) 223 | callback.OnLoaded(imageView, drawable, url, true); 224 | 225 | return; 226 | } 227 | else 228 | { 229 | //TODO: File cache expired, refreshing 230 | Android.Util.Log.Debug(LOGTAG, "File Cache Expired: " + file.Name); 231 | } 232 | } 233 | catch (Exception ex) 234 | { 235 | Android.Util.Log.Debug(LOGTAG, "File Cache Exception " + ex.ToString()); 236 | } 237 | } 238 | 239 | if (imageView != null) 240 | imageView.SetImageDrawable(defaultDrawable); 241 | 242 | if (imageView != null) 243 | pendingViews.Put(imageView, url); 244 | 245 | //Check to see if another view is already waiting for this url so we don't download it again 246 | var currentDownload = pendingDownloads.Get(url); 247 | if (currentDownload != null) 248 | { 249 | if (imageView != null) 250 | currentDownload.Add(imageView); 251 | 252 | return; 253 | } 254 | 255 | var downloads = new List(); 256 | if (imageView != null) 257 | downloads.Add(imageView); 258 | 259 | pendingDownloads.Put(url, downloads); 260 | 261 | var downloaderTask = new AnonymousAsyncTask((p) => 262 | { 263 | try 264 | { 265 | var client = new System.Net.WebClient(); 266 | var data = client.DownloadData(url); 267 | 268 | System.IO.File.WriteAllBytes(filename, data); 269 | 270 | return LoadDrawableFromFile(context, filename); 271 | } 272 | catch (Exception ex) 273 | { 274 | Android.Util.Log.Debug(LOGTAG, "Download Error: " + ex.ToString()); 275 | return null; 276 | } 277 | 278 | 279 | }, (bd) => 280 | { 281 | try 282 | { 283 | var usableResult = bd; 284 | if (usableResult == null) 285 | usableResult = (BitmapDrawable)defaultDrawable; 286 | 287 | pendingDownloads.Remove(url); 288 | 289 | cache.Put(url, usableResult); 290 | 291 | foreach (var iv in downloads) 292 | { 293 | var pendingUrl = pendingViews.Get (iv); 294 | if (!url.Equals(pendingUrl)) 295 | continue; 296 | pendingViews.Remove(iv); 297 | 298 | if (usableResult != null) 299 | { 300 | var fnewImage = usableResult; 301 | var fimageView = iv; 302 | 303 | fimageView.SetImageDrawable(fnewImage); 304 | 305 | if (callback != null) 306 | callback.OnLoaded(fimageView, bd, url, false); 307 | } 308 | } 309 | } 310 | catch (Exception ex) 311 | { 312 | Android.Util.Log.Debug(LOGTAG, "PostExecute Error: " + ex.ToString()); 313 | } 314 | 315 | }); 316 | 317 | downloaderTask.Execute(new Java.Lang.Object[]{}); 318 | } 319 | } 320 | } 321 | 322 | --------------------------------------------------------------------------------