Browse Source
Convert BitmapUtils to kotlin and migrate MediaUploader funcs to suspend
renovate/org.robolectric-robolectric-4.x
Convert BitmapUtils to kotlin and migrate MediaUploader funcs to suspend
renovate/org.robolectric-robolectric-4.x
Ammar Githam
4 years ago
6 changed files with 358 additions and 425 deletions
-
2app/build.gradle
-
22app/src/main/java/awais/instagrabber/fragments/imageedit/FiltersFragment.java
-
52app/src/main/java/awais/instagrabber/managers/ThreadManager.kt
-
280app/src/main/java/awais/instagrabber/utils/BitmapUtils.java
-
255app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt
-
104app/src/main/java/awais/instagrabber/utils/MediaUploader.kt
@ -1,280 +0,0 @@ |
|||||
package awais.instagrabber.utils; |
|
||||
|
|
||||
import android.content.ContentResolver; |
|
||||
import android.content.Context; |
|
||||
import android.graphics.Bitmap; |
|
||||
import android.graphics.BitmapFactory; |
|
||||
import android.net.Uri; |
|
||||
import android.util.Log; |
|
||||
import android.util.LruCache; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
import androidx.annotation.Nullable; |
|
||||
import androidx.core.util.Pair; |
|
||||
|
|
||||
import com.google.common.util.concurrent.FutureCallback; |
|
||||
import com.google.common.util.concurrent.Futures; |
|
||||
import com.google.common.util.concurrent.ListenableFuture; |
|
||||
|
|
||||
import java.io.File; |
|
||||
import java.io.FileOutputStream; |
|
||||
import java.io.IOException; |
|
||||
import java.io.InputStream; |
|
||||
import java.io.OutputStream; |
|
||||
import java.util.concurrent.ExecutorService; |
|
||||
import java.util.concurrent.Executors; |
|
||||
|
|
||||
public final class BitmapUtils { |
|
||||
private static final String TAG = BitmapUtils.class.getSimpleName(); |
|
||||
private static final LruCache<String, Bitmap> bitmapMemoryCache; |
|
||||
private static final AppExecutors appExecutors = AppExecutors.INSTANCE; |
|
||||
private static final ExecutorService callbackHandlers = Executors |
|
||||
.newCachedThreadPool(r -> new Thread(r, "bm-load-callback-handler#" + NumberUtils.random(0, 100))); |
|
||||
public static final float THUMBNAIL_SIZE = 200f; |
|
||||
|
|
||||
static { |
|
||||
// Get max available VM memory, exceeding this amount will throw an |
|
||||
// OutOfMemory exception. Stored in kilobytes as LruCache takes an |
|
||||
// int in its constructor. |
|
||||
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); |
|
||||
// Use 1/8th of the available memory for this memory cache. |
|
||||
final int cacheSize = maxMemory / 8; |
|
||||
bitmapMemoryCache = new LruCache<String, Bitmap>(cacheSize) { |
|
||||
@Override |
|
||||
protected int sizeOf(String key, Bitmap bitmap) { |
|
||||
// The cache size will be measured in kilobytes rather than |
|
||||
// number of items. |
|
||||
return bitmap.getByteCount() / 1024; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
public static void addBitmapToMemoryCache(final String key, final Bitmap bitmap, final boolean force) { |
|
||||
if (force || getBitmapFromMemCache(key) == null) { |
|
||||
bitmapMemoryCache.put(key, bitmap); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static Bitmap getBitmapFromMemCache(final String key) { |
|
||||
return bitmapMemoryCache.get(key); |
|
||||
} |
|
||||
|
|
||||
public static void getThumbnail(final Context context, final Uri uri, final ThumbnailLoadCallback callback) { |
|
||||
if (context == null || uri == null || callback == null) return; |
|
||||
final String key = uri.toString(); |
|
||||
final Bitmap cachedBitmap = getBitmapFromMemCache(key); |
|
||||
if (cachedBitmap != null) { |
|
||||
callback.onLoad(cachedBitmap, -1, -1); |
|
||||
return; |
|
||||
} |
|
||||
loadBitmap(context.getContentResolver(), uri, THUMBNAIL_SIZE, THUMBNAIL_SIZE, true, callback); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Loads bitmap from given Uri |
|
||||
* |
|
||||
* @param contentResolver {@link ContentResolver} to resolve the uri |
|
||||
* @param uri Uri from where Bitmap will be loaded |
|
||||
* @param reqWidth Required width |
|
||||
* @param reqHeight Required height |
|
||||
* @param addToCache true if the loaded bitmap should be added to the mem cache |
|
||||
* @param callback Bitmap load callback |
|
||||
*/ |
|
||||
public static void loadBitmap(final ContentResolver contentResolver, |
|
||||
final Uri uri, |
|
||||
final float reqWidth, |
|
||||
final float reqHeight, |
|
||||
final boolean addToCache, |
|
||||
final ThumbnailLoadCallback callback) { |
|
||||
loadBitmap(contentResolver, uri, reqWidth, reqHeight, -1, addToCache, callback); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Loads bitmap from given Uri |
|
||||
* |
|
||||
* @param contentResolver {@link ContentResolver} to resolve the uri |
|
||||
* @param uri Uri from where Bitmap will be loaded |
|
||||
* @param maxDimenSize Max size of the largest side of the image |
|
||||
* @param addToCache true if the loaded bitmap should be added to the mem cache |
|
||||
* @param callback Bitmap load callback |
|
||||
*/ |
|
||||
public static void loadBitmap(final ContentResolver contentResolver, |
|
||||
final Uri uri, |
|
||||
final float maxDimenSize, |
|
||||
final boolean addToCache, |
|
||||
final ThumbnailLoadCallback callback) { |
|
||||
loadBitmap(contentResolver, uri, -1, -1, maxDimenSize, addToCache, callback); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Loads bitmap from given Uri |
|
||||
* |
|
||||
* @param contentResolver {@link ContentResolver} to resolve the uri |
|
||||
* @param uri Uri from where {@link Bitmap} will be loaded |
|
||||
* @param reqWidth Required width (set to -1 if maxDimenSize provided) |
|
||||
* @param reqHeight Required height (set to -1 if maxDimenSize provided) |
|
||||
* @param maxDimenSize Max size of the largest side of the image (set to -1 if setting reqWidth and reqHeight) |
|
||||
* @param addToCache true if the loaded bitmap should be added to the mem cache |
|
||||
* @param callback Bitmap load callback |
|
||||
*/ |
|
||||
private static void loadBitmap(final ContentResolver contentResolver, |
|
||||
final Uri uri, |
|
||||
final float reqWidth, |
|
||||
final float reqHeight, |
|
||||
final float maxDimenSize, |
|
||||
final boolean addToCache, |
|
||||
final ThumbnailLoadCallback callback) { |
|
||||
if (contentResolver == null || uri == null || callback == null) return; |
|
||||
final ListenableFuture<BitmapResult> future = appExecutors |
|
||||
.getTasksThread() |
|
||||
.submit(() -> getBitmapResult(contentResolver, uri, reqWidth, reqHeight, maxDimenSize, addToCache)); |
|
||||
Futures.addCallback(future, new FutureCallback<BitmapResult>() { |
|
||||
@Override |
|
||||
public void onSuccess(@Nullable final BitmapResult result) { |
|
||||
if (result == null) { |
|
||||
callback.onLoad(null, -1, -1); |
|
||||
return; |
|
||||
} |
|
||||
callback.onLoad(result.bitmap, result.width, result.height); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(@NonNull final Throwable t) { |
|
||||
callback.onFailure(t); |
|
||||
} |
|
||||
}, callbackHandlers); |
|
||||
} |
|
||||
|
|
||||
@Nullable |
|
||||
public static BitmapResult getBitmapResult(final ContentResolver contentResolver, |
|
||||
final Uri uri, |
|
||||
final float reqWidth, |
|
||||
final float reqHeight, |
|
||||
final float maxDimenSize, |
|
||||
final boolean addToCache) { |
|
||||
BitmapFactory.Options bitmapOptions; |
|
||||
float actualReqWidth = reqWidth; |
|
||||
float actualReqHeight = reqHeight; |
|
||||
try (InputStream input = contentResolver.openInputStream(uri)) { |
|
||||
BitmapFactory.Options outBounds = new BitmapFactory.Options(); |
|
||||
outBounds.inJustDecodeBounds = true; |
|
||||
outBounds.inPreferredConfig = Bitmap.Config.ARGB_8888; |
|
||||
BitmapFactory.decodeStream(input, null, outBounds); |
|
||||
if ((outBounds.outWidth == -1) || (outBounds.outHeight == -1)) return null; |
|
||||
bitmapOptions = new BitmapFactory.Options(); |
|
||||
if (maxDimenSize > 0) { |
|
||||
// Raw height and width of image |
|
||||
final int height = outBounds.outHeight; |
|
||||
final int width = outBounds.outWidth; |
|
||||
final float ratio = (float) width / height; |
|
||||
if (height > width) { |
|
||||
actualReqHeight = maxDimenSize; |
|
||||
actualReqWidth = actualReqHeight * ratio; |
|
||||
} else { |
|
||||
actualReqWidth = maxDimenSize; |
|
||||
actualReqHeight = actualReqWidth / ratio; |
|
||||
} |
|
||||
} |
|
||||
bitmapOptions.inSampleSize = calculateInSampleSize(outBounds, actualReqWidth, actualReqHeight); |
|
||||
} catch (Exception e) { |
|
||||
Log.e(TAG, "loadBitmap: ", e); |
|
||||
return null; |
|
||||
} |
|
||||
try (InputStream input = contentResolver.openInputStream(uri)) { |
|
||||
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; |
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); |
|
||||
if (addToCache) { |
|
||||
addBitmapToMemoryCache(uri.toString(), bitmap, true); |
|
||||
} |
|
||||
return new BitmapResult(bitmap, (int) actualReqWidth, (int) actualReqHeight); |
|
||||
} catch (Exception e) { |
|
||||
Log.e(TAG, "loadBitmap: ", e); |
|
||||
} |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
public static class BitmapResult { |
|
||||
public Bitmap bitmap; |
|
||||
int width; |
|
||||
int height; |
|
||||
|
|
||||
public BitmapResult(final Bitmap bitmap, final int width, final int height) { |
|
||||
this.width = width; |
|
||||
this.height = height; |
|
||||
this.bitmap = bitmap; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private static int calculateInSampleSize(final BitmapFactory.Options options, final float reqWidth, final float reqHeight) { |
|
||||
// Raw height and width of image |
|
||||
final int height = options.outHeight; |
|
||||
final int width = options.outWidth; |
|
||||
int inSampleSize = 1; |
|
||||
if (height > reqHeight || width > reqWidth) { |
|
||||
final float halfHeight = height / 2f; |
|
||||
final float halfWidth = width / 2f; |
|
||||
// Calculate the largest inSampleSize value that is a power of 2 and keeps both |
|
||||
// height and width larger than the requested height and width. |
|
||||
while ((halfHeight / inSampleSize) >= reqHeight |
|
||||
&& (halfWidth / inSampleSize) >= reqWidth) { |
|
||||
inSampleSize *= 2; |
|
||||
} |
|
||||
} |
|
||||
return inSampleSize; |
|
||||
} |
|
||||
|
|
||||
public interface ThumbnailLoadCallback { |
|
||||
/** |
|
||||
* @param bitmap Resulting bitmap |
|
||||
* @param width width of the bitmap (Only correct if loadBitmap was called or -1) |
|
||||
* @param height height of the bitmap (Only correct if loadBitmap was called or -1) |
|
||||
*/ |
|
||||
void onLoad(@Nullable Bitmap bitmap, int width, int height); |
|
||||
|
|
||||
void onFailure(@NonNull Throwable t); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Decodes the bounds of an image from its Uri and returns a pair of the dimensions |
|
||||
* |
|
||||
* @param uri the Uri of the image |
|
||||
* @return dimensions of the image |
|
||||
*/ |
|
||||
public static Pair<Integer, Integer> decodeDimensions(@NonNull final ContentResolver contentResolver, |
|
||||
@NonNull final Uri uri) throws IOException { |
|
||||
BitmapFactory.Options options = new BitmapFactory.Options(); |
|
||||
options.inJustDecodeBounds = true; |
|
||||
try (final InputStream stream = contentResolver.openInputStream(uri)) { |
|
||||
BitmapFactory.decodeStream(stream, null, options); |
|
||||
return (options.outWidth == -1 || options.outHeight == -1) |
|
||||
? null |
|
||||
: new Pair<>(options.outWidth, options.outHeight); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static File convertToJpegAndSaveToFile(@NonNull final Bitmap bitmap, @Nullable final File file) throws IOException { |
|
||||
File tempFile = file; |
|
||||
if (file == null) { |
|
||||
tempFile = DownloadUtils.getTempFile(); |
|
||||
} |
|
||||
try (OutputStream output = new FileOutputStream(tempFile)) { |
|
||||
final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output); |
|
||||
if (!compressResult) { |
|
||||
throw new RuntimeException("Compression failed!"); |
|
||||
} |
|
||||
} |
|
||||
return tempFile; |
|
||||
} |
|
||||
|
|
||||
public static void convertToJpegAndSaveToUri(@NonNull Context context, |
|
||||
@NonNull final Bitmap bitmap, |
|
||||
@NonNull final Uri uri) throws Exception { |
|
||||
try (OutputStream output = context.getContentResolver().openOutputStream(uri)) { |
|
||||
final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output); |
|
||||
if (!compressResult) { |
|
||||
throw new RuntimeException("Compression failed!"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,255 @@ |
|||||
|
package awais.instagrabber.utils |
||||
|
|
||||
|
import android.content.ContentResolver |
||||
|
import android.content.Context |
||||
|
import android.graphics.Bitmap |
||||
|
import android.graphics.BitmapFactory |
||||
|
import android.net.Uri |
||||
|
import android.util.Log |
||||
|
import android.util.LruCache |
||||
|
import androidx.core.util.Pair |
||||
|
import awais.instagrabber.utils.extensions.TAG |
||||
|
import kotlinx.coroutines.Dispatchers |
||||
|
import kotlinx.coroutines.withContext |
||||
|
import java.io.File |
||||
|
import java.io.FileOutputStream |
||||
|
import java.io.IOException |
||||
|
|
||||
|
object BitmapUtils { |
||||
|
private val bitmapMemoryCache: LruCache<String, Bitmap> |
||||
|
|
||||
|
// private val appExecutors = AppExecutors |
||||
|
// private val callbackHandlers = Executors |
||||
|
// .newCachedThreadPool { r: Runnable? -> Thread(r, "bm-load-callback-handler#" + random(0, 100)) } |
||||
|
const val THUMBNAIL_SIZE = 200f |
||||
|
|
||||
|
@JvmStatic |
||||
|
fun addBitmapToMemoryCache(key: String, bitmap: Bitmap, force: Boolean) { |
||||
|
if (force || getBitmapFromMemCache(key) == null) { |
||||
|
bitmapMemoryCache.put(key, bitmap) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@JvmStatic |
||||
|
fun getBitmapFromMemCache(key: String): Bitmap? { |
||||
|
return bitmapMemoryCache[key] |
||||
|
} |
||||
|
|
||||
|
@JvmStatic |
||||
|
suspend fun getThumbnail(context: Context, uri: Uri): BitmapResult? { |
||||
|
val key = uri.toString() |
||||
|
val cachedBitmap = getBitmapFromMemCache(key) |
||||
|
if (cachedBitmap != null) { |
||||
|
return BitmapResult(cachedBitmap, -1, -1) |
||||
|
} |
||||
|
return loadBitmap(context.contentResolver, uri, THUMBNAIL_SIZE, THUMBNAIL_SIZE, true) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Loads bitmap from given Uri |
||||
|
* |
||||
|
* @param contentResolver [ContentResolver] to resolve the uri |
||||
|
* @param uri Uri from where Bitmap will be loaded |
||||
|
* @param reqWidth Required width |
||||
|
* @param reqHeight Required height |
||||
|
* @param addToCache true if the loaded bitmap should be added to the mem cache |
||||
|
// * @param callback Bitmap load callback |
||||
|
*/ |
||||
|
suspend fun loadBitmap( |
||||
|
contentResolver: ContentResolver?, |
||||
|
uri: Uri?, |
||||
|
reqWidth: Float, |
||||
|
reqHeight: Float, |
||||
|
addToCache: Boolean, |
||||
|
): BitmapResult? = loadBitmap(contentResolver, uri, reqWidth, reqHeight, -1f, addToCache) |
||||
|
|
||||
|
/** |
||||
|
* Loads bitmap from given Uri |
||||
|
* |
||||
|
* @param contentResolver [ContentResolver] to resolve the uri |
||||
|
* @param uri Uri from where Bitmap will be loaded |
||||
|
* @param maxDimenSize Max size of the largest side of the image |
||||
|
* @param addToCache true if the loaded bitmap should be added to the mem cache |
||||
|
// * @param callback Bitmap load callback |
||||
|
*/ |
||||
|
suspend fun loadBitmap( |
||||
|
contentResolver: ContentResolver?, |
||||
|
uri: Uri?, |
||||
|
maxDimenSize: Float, |
||||
|
addToCache: Boolean, |
||||
|
): BitmapResult? = loadBitmap(contentResolver, uri, -1f, -1f, maxDimenSize, addToCache) |
||||
|
|
||||
|
/** |
||||
|
* Loads bitmap from given Uri |
||||
|
* |
||||
|
* @param contentResolver [ContentResolver] to resolve the uri |
||||
|
* @param uri Uri from where [Bitmap] will be loaded |
||||
|
* @param reqWidth Required width (set to -1 if maxDimenSize provided) |
||||
|
* @param reqHeight Required height (set to -1 if maxDimenSize provided) |
||||
|
* @param maxDimenSize Max size of the largest side of the image (set to -1 if setting reqWidth and reqHeight) |
||||
|
* @param addToCache true if the loaded bitmap should be added to the mem cache |
||||
|
// * @param callback Bitmap load callback |
||||
|
*/ |
||||
|
private suspend fun loadBitmap( |
||||
|
contentResolver: ContentResolver?, |
||||
|
uri: Uri?, |
||||
|
reqWidth: Float, |
||||
|
reqHeight: Float, |
||||
|
maxDimenSize: Float, |
||||
|
addToCache: Boolean, |
||||
|
): BitmapResult? = |
||||
|
if (contentResolver == null || uri == null) null else withContext(Dispatchers.IO) { |
||||
|
getBitmapResult(contentResolver, |
||||
|
uri, |
||||
|
reqWidth, |
||||
|
reqHeight, |
||||
|
maxDimenSize, |
||||
|
addToCache) |
||||
|
} |
||||
|
|
||||
|
fun getBitmapResult( |
||||
|
contentResolver: ContentResolver, |
||||
|
uri: Uri, |
||||
|
reqWidth: Float, |
||||
|
reqHeight: Float, |
||||
|
maxDimenSize: Float, |
||||
|
addToCache: Boolean, |
||||
|
): BitmapResult? { |
||||
|
var bitmapOptions: BitmapFactory.Options |
||||
|
var actualReqWidth = reqWidth |
||||
|
var actualReqHeight = reqHeight |
||||
|
try { |
||||
|
contentResolver.openInputStream(uri).use { input -> |
||||
|
val outBounds = BitmapFactory.Options() |
||||
|
outBounds.inJustDecodeBounds = true |
||||
|
outBounds.inPreferredConfig = Bitmap.Config.ARGB_8888 |
||||
|
BitmapFactory.decodeStream(input, null, outBounds) |
||||
|
if (outBounds.outWidth == -1 || outBounds.outHeight == -1) return null |
||||
|
bitmapOptions = BitmapFactory.Options() |
||||
|
if (maxDimenSize > 0) { |
||||
|
// Raw height and width of image |
||||
|
val height = outBounds.outHeight |
||||
|
val width = outBounds.outWidth |
||||
|
val ratio = width.toFloat() / height |
||||
|
if (height > width) { |
||||
|
actualReqHeight = maxDimenSize |
||||
|
actualReqWidth = actualReqHeight * ratio |
||||
|
} else { |
||||
|
actualReqWidth = maxDimenSize |
||||
|
actualReqHeight = actualReqWidth / ratio |
||||
|
} |
||||
|
} |
||||
|
bitmapOptions.inSampleSize = calculateInSampleSize(outBounds, actualReqWidth, actualReqHeight) |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(TAG, "loadBitmap: ", e) |
||||
|
return null |
||||
|
} |
||||
|
try { |
||||
|
contentResolver.openInputStream(uri).use { input -> |
||||
|
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888 |
||||
|
val bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions) |
||||
|
if (addToCache && bitmap != null) { |
||||
|
addBitmapToMemoryCache(uri.toString(), bitmap, true) |
||||
|
} |
||||
|
return BitmapResult(bitmap, actualReqWidth.toInt(), actualReqHeight.toInt()) |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(TAG, "loadBitmap: ", e) |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Float, reqHeight: Float): Int { |
||||
|
// Raw height and width of image |
||||
|
val height = options.outHeight |
||||
|
val width = options.outWidth |
||||
|
var inSampleSize = 1 |
||||
|
if (height > reqHeight || width > reqWidth) { |
||||
|
val halfHeight = height / 2f |
||||
|
val halfWidth = width / 2f |
||||
|
// Calculate the largest inSampleSize value that is a power of 2 and keeps both |
||||
|
// height and width larger than the requested height and width. |
||||
|
while (halfHeight / inSampleSize >= reqHeight |
||||
|
&& halfWidth / inSampleSize >= reqWidth |
||||
|
) { |
||||
|
inSampleSize *= 2 |
||||
|
} |
||||
|
} |
||||
|
return inSampleSize |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Decodes the bounds of an image from its Uri and returns a pair of the dimensions |
||||
|
* |
||||
|
* @param uri the Uri of the image |
||||
|
* @return dimensions of the image |
||||
|
*/ |
||||
|
@Throws(IOException::class) |
||||
|
fun decodeDimensions( |
||||
|
contentResolver: ContentResolver, |
||||
|
uri: Uri, |
||||
|
): Pair<Int, Int>? { |
||||
|
val options = BitmapFactory.Options() |
||||
|
options.inJustDecodeBounds = true |
||||
|
contentResolver.openInputStream(uri).use { stream -> |
||||
|
BitmapFactory.decodeStream(stream, null, options) |
||||
|
return if (options.outWidth == -1 || options.outHeight == -1) null else Pair(options.outWidth, options.outHeight) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Throws(IOException::class) |
||||
|
fun convertToJpegAndSaveToFile(bitmap: Bitmap, file: File?): File { |
||||
|
val tempFile = file ?: DownloadUtils.getTempFile() |
||||
|
FileOutputStream(tempFile).use { output -> |
||||
|
val compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output) |
||||
|
if (!compressResult) { |
||||
|
throw RuntimeException("Compression failed!") |
||||
|
} |
||||
|
} |
||||
|
return tempFile |
||||
|
} |
||||
|
|
||||
|
@JvmStatic |
||||
|
@Throws(Exception::class) |
||||
|
fun convertToJpegAndSaveToUri( |
||||
|
context: Context, |
||||
|
bitmap: Bitmap, |
||||
|
uri: Uri, |
||||
|
) { |
||||
|
context.contentResolver.openOutputStream(uri).use { output -> |
||||
|
val compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output) |
||||
|
if (!compressResult) { |
||||
|
throw RuntimeException("Compression failed!") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class BitmapResult(var bitmap: Bitmap?, var width: Int, var height: Int) |
||||
|
|
||||
|
interface ThumbnailLoadCallback { |
||||
|
/** |
||||
|
* @param bitmap Resulting bitmap |
||||
|
* @param width width of the bitmap (Only correct if loadBitmap was called or -1) |
||||
|
* @param height height of the bitmap (Only correct if loadBitmap was called or -1) |
||||
|
*/ |
||||
|
fun onLoad(bitmap: Bitmap, width: Int, height: Int) |
||||
|
fun onFailure(t: Throwable) |
||||
|
} |
||||
|
|
||||
|
init { |
||||
|
// Get max available VM memory, exceeding this amount will throw an |
||||
|
// OutOfMemory exception. Stored in kilobytes as LruCache takes an |
||||
|
// int in its constructor. |
||||
|
val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt() |
||||
|
// Use 1/8th of the available memory for this memory cache. |
||||
|
val cacheSize: Int = maxMemory / 8 |
||||
|
bitmapMemoryCache = object : LruCache<String, Bitmap>(cacheSize) { |
||||
|
override fun sizeOf(key: String, bitmap: Bitmap): Int { |
||||
|
// The cache size will be measured in kilobytes rather than |
||||
|
// number of items. |
||||
|
return bitmap.byteCount / 1024 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue