-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pablo image library completed commit
- Loading branch information
Showing
1 changed file
with
194 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,199 @@ | ||
package com.ajithvgiri.pablo | ||
|
||
class Pablo { | ||
import android.content.Context | ||
import android.graphics.Bitmap | ||
import android.graphics.BitmapFactory | ||
import android.os.Handler | ||
import android.util.Log | ||
import android.widget.ImageView | ||
import java.io.* | ||
import java.net.HttpURLConnection | ||
import java.net.URL | ||
import java.util.* | ||
import java.util.concurrent.ExecutorService | ||
import java.util.concurrent.Executors | ||
|
||
class Pablo(context: Context) { | ||
|
||
var isCompress = true | ||
// Initialize MemoryCache | ||
var memoryCache = MemoryCache() | ||
private var fileCache: FileCache = FileCache(context) | ||
|
||
//Create Map (collection) to store image and image url in key value pair | ||
private val imageViews = Collections.synchronizedMap(WeakHashMap<ImageView, String>()) | ||
|
||
// Creates a thread pool that reuses a fixed number of | ||
// threads operating off a shared unbounded queue. | ||
private var executorService: ExecutorService = Executors.newFixedThreadPool(5) | ||
|
||
//handler to display images in UI thread | ||
var handler = Handler() | ||
|
||
// default image show in list (Before online image download) | ||
val placeHolder = R.drawable.default_placeholder | ||
|
||
fun displayImage(url: String, imageView: ImageView) { | ||
//Store image and url in Map | ||
imageViews[imageView] = url | ||
|
||
//Check image is stored in MemoryCache Map or not (see MemoryCache.java) | ||
val bitmap = memoryCache[url] | ||
|
||
if (bitmap != null) { | ||
// if image is stored in MemoryCache Map then | ||
// Show image in gridView | ||
imageView.setImageBitmap(bitmap) | ||
} else { | ||
//queue Photo to download from url | ||
queuePhoto(url, imageView) | ||
//Before downloading image show default image | ||
imageView.setImageResource(placeHolder) | ||
} | ||
} | ||
|
||
private fun queuePhoto(url: String, imageView: ImageView) { | ||
// Store image and url in PhotoToLoad object | ||
val p = PhotoToLoad(url, imageView) | ||
// pass PhotoToLoad object to PhotosLoader runnable class | ||
// and submit PhotosLoader runnable to executers to run runnable | ||
// Submits a PhotosLoader runnable task for execution | ||
executorService.submit(PhotosLoader(p)) | ||
} | ||
|
||
//Task for the queue | ||
inner class PhotoToLoad(var url: String, var imageView: ImageView) | ||
|
||
inner class PhotosLoader(private var photoToLoad: PhotoToLoad) : Runnable { | ||
override fun run() { | ||
try { | ||
//Check if image already downloaded | ||
if (imageViewReused(photoToLoad)) return | ||
// download image from web url | ||
val bmp = getBitmap(photoToLoad.url) | ||
// set image data in Memory Cache | ||
memoryCache.put(photoToLoad.url, bmp) | ||
if (imageViewReused(photoToLoad)) return | ||
// Get bitmap to display | ||
val bd = BitmapDisplayer(bmp, photoToLoad) | ||
// Causes the Runnable bd (BitmapDisplayer) to be added to the message queue. | ||
// The runnable will be run on the thread to which this handler is attached. | ||
// BitmapDisplayer run method will call | ||
handler.post(bd) | ||
} catch (th: Throwable) { | ||
Log.e("ImageLoader", "exception from PhotosLoader ${th.localizedMessage}") | ||
th.printStackTrace() | ||
} | ||
} | ||
|
||
} | ||
|
||
private fun getBitmap(url: String): Bitmap? { | ||
val f = fileCache.getFile(url) | ||
//from SD cache | ||
//CHECK : if trying to decode file which not exist in cache return null | ||
val b = decodeFile(f) | ||
return b ?: try { | ||
val imageUrl = URL(url) | ||
val conn = imageUrl.openConnection() as HttpURLConnection | ||
conn.connectTimeout = 30000 | ||
conn.readTimeout = 30000 | ||
conn.instanceFollowRedirects = true | ||
val inputStream = conn.inputStream | ||
// Constructs a new FileOutputStream that writes to file | ||
// if file not exist then it will create file | ||
val os: OutputStream = FileOutputStream(f) | ||
// See Utils class CopyStream method | ||
// It will each pixel from input stream and | ||
// write pixels to output stream (file) | ||
copyStream(inputStream, os) | ||
os.close() | ||
conn.disconnect() | ||
//Now file created and going to resize file with defined height | ||
// Decodes image and scales it to reduce memory consumption | ||
val bitmap: Bitmap? = decodeFile(f) | ||
bitmap | ||
} catch (ex: Throwable) { | ||
ex.printStackTrace() | ||
if (ex is OutOfMemoryError) memoryCache.clear() | ||
null | ||
} | ||
// Download image file from web | ||
} | ||
|
||
private fun copyStream(inputStream: InputStream, os: OutputStream) { | ||
val bufferSize = 1024 | ||
try { | ||
val bytes = ByteArray(bufferSize) | ||
while (true) { | ||
//Read byte from input stream | ||
val count = inputStream.read(bytes, 0, bufferSize) | ||
if (count == -1) break | ||
//Write byte from output stream | ||
os.write(bytes, 0, count) | ||
} | ||
} catch (ex: Exception) { | ||
Log.e("Utils", "Exception in ${ex.localizedMessage}") | ||
} | ||
} | ||
|
||
//Decodes image and scales it to reduce memory consumption | ||
private fun decodeFile(f: File): Bitmap? { | ||
try { | ||
//Decode image size | ||
val o = BitmapFactory.Options() | ||
o.inJustDecodeBounds = true | ||
val stream1 = FileInputStream(f) | ||
BitmapFactory.decodeStream(stream1, null, o) | ||
stream1.close() | ||
//Find the correct scale value. It should be the power of 2. | ||
// Set width/height of recreated image | ||
val requiredSize = 100 | ||
var widthTmp = o.outWidth | ||
var heightTmp = o.outHeight | ||
var scale = 1 | ||
while (isCompress) { | ||
if (widthTmp / 2 < requiredSize || heightTmp / 2 < requiredSize) break | ||
widthTmp /= 2 | ||
heightTmp /= 2 | ||
scale *= 2 | ||
} | ||
//decode with current scale values | ||
val o2 = BitmapFactory.Options() | ||
o2.inSampleSize = scale | ||
val stream2 = FileInputStream(f) | ||
val bitmap = BitmapFactory.decodeStream(stream2, null, o2) | ||
stream2.close() | ||
return bitmap | ||
} catch (e: FileNotFoundException) { | ||
} catch (e: IOException) { | ||
e.printStackTrace() | ||
} | ||
return null | ||
} | ||
|
||
fun imageViewReused(photoToLoad: PhotoToLoad): Boolean { | ||
val tag = imageViews[photoToLoad.imageView] | ||
//Check url is already exist in imageViews MAP | ||
return tag == null || tag != photoToLoad.url | ||
} | ||
|
||
//Used to display bitmap in the UI thread | ||
inner class BitmapDisplayer(private var bitmap: Bitmap?, private var photoToLoad: PhotoToLoad) : | ||
Runnable { | ||
override fun run() { | ||
if (imageViewReused(photoToLoad)) return | ||
// Show bitmap on UI | ||
if (bitmap != null) photoToLoad.imageView.setImageBitmap(bitmap) else photoToLoad.imageView.setImageResource( | ||
placeHolder | ||
) | ||
} | ||
|
||
} | ||
|
||
fun clearCache() { | ||
//Clear cache directory downloaded images and stored data in maps | ||
memoryCache.clear() | ||
fileCache.clear() | ||
} | ||
} |