mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-15 19:27:31 +00:00
Convert BitmapUtils to kotlin and migrate MediaUploader funcs to suspend
This commit is contained in:
parent
5756f055d9
commit
8491d1aac7
@ -208,6 +208,8 @@ dependencies {
|
|||||||
implementation "androidx.work:work-runtime:$work_version"
|
implementation "androidx.work:work-runtime:$work_version"
|
||||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||||
|
|
||||||
|
implementation "ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0"
|
||||||
|
|
||||||
implementation 'com.facebook.fresco:fresco:2.3.0'
|
implementation 'com.facebook.fresco:fresco:2.3.0'
|
||||||
implementation 'com.facebook.fresco:animated-webp:2.3.0'
|
implementation 'com.facebook.fresco:animated-webp:2.3.0'
|
||||||
implementation 'com.facebook.fresco:webpsupport:2.3.0'
|
implementation 'com.facebook.fresco:webpsupport:2.3.0'
|
||||||
|
@ -47,6 +47,7 @@ import awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;
|
|||||||
import awais.instagrabber.fragments.imageedit.filters.properties.Property;
|
import awais.instagrabber.fragments.imageedit.filters.properties.Property;
|
||||||
import awais.instagrabber.utils.AppExecutors;
|
import awais.instagrabber.utils.AppExecutors;
|
||||||
import awais.instagrabber.utils.BitmapUtils;
|
import awais.instagrabber.utils.BitmapUtils;
|
||||||
|
import awais.instagrabber.utils.CoroutineUtilsKt;
|
||||||
import awais.instagrabber.utils.SerializablePair;
|
import awais.instagrabber.utils.SerializablePair;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.FiltersFragmentViewModel;
|
import awais.instagrabber.viewmodels.FiltersFragmentViewModel;
|
||||||
@ -460,16 +461,21 @@ public class FiltersFragment extends Fragment {
|
|||||||
filtersAdapter.setSelected(position);
|
filtersAdapter.setSelected(position);
|
||||||
appliedFilter = filter;
|
appliedFilter = filter;
|
||||||
};
|
};
|
||||||
BitmapUtils.getThumbnail(context, sourceUri, new BitmapUtils.ThumbnailLoadCallback() {
|
BitmapUtils.getThumbnail(context, sourceUri, CoroutineUtilsKt.getContinuation((bitmapResult, throwable) -> {
|
||||||
@Override
|
if (throwable != null) {
|
||||||
public void onLoad(@Nullable final Bitmap bitmap, final int width, final int height) {
|
Log.e(TAG, "setupFilters: ", throwable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bitmapResult == null || bitmapResult.getBitmap() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
filtersAdapter = new FiltersAdapter(
|
filtersAdapter = new FiltersAdapter(
|
||||||
tuningFilters.values()
|
tuningFilters.values()
|
||||||
.stream()
|
.stream()
|
||||||
.map(Filter::getInstance)
|
.map(Filter::getInstance)
|
||||||
.collect(Collectors.toList()),
|
.collect(Collectors.toList()),
|
||||||
sourceUri.toString(),
|
sourceUri.toString(),
|
||||||
bitmap,
|
bitmapResult.getBitmap(),
|
||||||
onFilterClickListener
|
onFilterClickListener
|
||||||
);
|
);
|
||||||
appExecutors.getMainThread().execute(() -> {
|
appExecutors.getMainThread().execute(() -> {
|
||||||
@ -479,13 +485,7 @@ public class FiltersFragment extends Fragment {
|
|||||||
filtersAdapter.setSelectedFilter(appliedFilter.getInstance());
|
filtersAdapter.setSelectedFilter(appliedFilter.getInstance());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}));
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull final Throwable t) {
|
|
||||||
Log.e(TAG, "onFailure: ", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
addInitialFilter();
|
addInitialFilter();
|
||||||
binding.preview.setFilter(filterGroup);
|
binding.preview.setFilter(filterGroup);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import awais.instagrabber.repositories.responses.directmessages.*
|
|||||||
import awais.instagrabber.repositories.responses.giphy.GiphyGif
|
import awais.instagrabber.repositories.responses.giphy.GiphyGif
|
||||||
import awais.instagrabber.utils.*
|
import awais.instagrabber.utils.*
|
||||||
import awais.instagrabber.utils.MediaUploader.MediaUploadResponse
|
import awais.instagrabber.utils.MediaUploader.MediaUploadResponse
|
||||||
import awais.instagrabber.utils.MediaUploader.OnMediaUploadCompleteListener
|
|
||||||
import awais.instagrabber.utils.MediaUploader.uploadPhoto
|
import awais.instagrabber.utils.MediaUploader.uploadPhoto
|
||||||
import awais.instagrabber.utils.MediaUploader.uploadVideo
|
import awais.instagrabber.utils.MediaUploader.uploadVideo
|
||||||
import awais.instagrabber.utils.MediaUtils.OnInfoLoadListener
|
import awais.instagrabber.utils.MediaUtils.OnInfoLoadListener
|
||||||
@ -448,10 +447,11 @@ class ThreadManager private constructor(
|
|||||||
addItems(0, listOf(directItem))
|
addItems(0, listOf(directItem))
|
||||||
data.postValue(loading(directItem))
|
data.postValue(loading(directItem))
|
||||||
val uploadDmVoiceOptions = createUploadDmVoiceOptions(byteLength, duration)
|
val uploadDmVoiceOptions = createUploadDmVoiceOptions(byteLength, duration)
|
||||||
uploadVideo(uri, contentResolver, uploadDmVoiceOptions, object : OnMediaUploadCompleteListener {
|
scope.launch(Dispatchers.IO) {
|
||||||
override fun onUploadComplete(response: MediaUploadResponse) {
|
try {
|
||||||
|
val response = uploadVideo(uri, contentResolver, uploadDmVoiceOptions)
|
||||||
// Log.d(TAG, "onUploadComplete: " + response);
|
// Log.d(TAG, "onUploadComplete: " + response);
|
||||||
if (handleInvalidResponse(data, response)) return
|
if (handleInvalidResponse(data, response)) return@launch
|
||||||
val uploadFinishOptions = UploadFinishOptions(
|
val uploadFinishOptions = UploadFinishOptions(
|
||||||
uploadDmVoiceOptions.uploadId,
|
uploadDmVoiceOptions.uploadId,
|
||||||
"4",
|
"4",
|
||||||
@ -488,16 +488,14 @@ class ThreadManager private constructor(
|
|||||||
|
|
||||||
override fun onFailure(call: Call<String?>, t: Throwable) {
|
override fun onFailure(call: Call<String?>, t: Throwable) {
|
||||||
data.postValue(error(t.message, directItem))
|
data.postValue(error(t.message, directItem))
|
||||||
Log.e(TAG, "onFailure: ", t)
|
Log.e(TAG, "sendVoice: ", t)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} catch (e: Exception) {
|
||||||
|
data.postValue(error(e.message, directItem))
|
||||||
|
Log.e(TAG, "sendVoice: ", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(t: Throwable) {
|
|
||||||
data.postValue(error(t.message, directItem))
|
|
||||||
Log.e(TAG, "onFailure: ", t)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendReaction(
|
fun sendReaction(
|
||||||
@ -742,13 +740,12 @@ class ThreadManager private constructor(
|
|||||||
directItem.isPending = true
|
directItem.isPending = true
|
||||||
addItems(0, listOf(directItem))
|
addItems(0, listOf(directItem))
|
||||||
data.postValue(loading(directItem))
|
data.postValue(loading(directItem))
|
||||||
uploadPhoto(uri, contentResolver, object : OnMediaUploadCompleteListener {
|
|
||||||
override fun onUploadComplete(response: MediaUploadResponse) {
|
|
||||||
if (handleInvalidResponse(data, response)) return
|
|
||||||
val response1 = response.response ?: return
|
|
||||||
val uploadId = response1.optString("upload_id")
|
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
|
val response = uploadPhoto(uri, contentResolver)
|
||||||
|
if (handleInvalidResponse(data, response)) return@launch
|
||||||
|
val response1 = response.response ?: return@launch
|
||||||
|
val uploadId = response1.optString("upload_id")
|
||||||
val response2 = service.broadcastPhoto(clientContext, threadIdOrUserIds, uploadId)
|
val response2 = service.broadcastPhoto(clientContext, threadIdOrUserIds, uploadId)
|
||||||
parseResponse(response2, data, directItem)
|
parseResponse(response2, data, directItem)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -758,13 +755,6 @@ class ThreadManager private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(t: Throwable) {
|
|
||||||
data.postValue(error(t.message, directItem))
|
|
||||||
Log.e(TAG, "onFailure: ", t)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendVideo(
|
private fun sendVideo(
|
||||||
data: MutableLiveData<Resource<Any?>>,
|
data: MutableLiveData<Resource<Any?>>,
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
@ -806,10 +796,11 @@ class ThreadManager private constructor(
|
|||||||
addItems(0, listOf(directItem))
|
addItems(0, listOf(directItem))
|
||||||
data.postValue(loading(directItem))
|
data.postValue(loading(directItem))
|
||||||
val uploadDmVideoOptions = createUploadDmVideoOptions(byteLength, duration, width, height)
|
val uploadDmVideoOptions = createUploadDmVideoOptions(byteLength, duration, width, height)
|
||||||
uploadVideo(uri, contentResolver, uploadDmVideoOptions, object : OnMediaUploadCompleteListener {
|
scope.launch(Dispatchers.IO) {
|
||||||
override fun onUploadComplete(response: MediaUploadResponse) {
|
try {
|
||||||
|
val response = uploadVideo(uri, contentResolver, uploadDmVideoOptions)
|
||||||
// Log.d(TAG, "onUploadComplete: " + response);
|
// Log.d(TAG, "onUploadComplete: " + response);
|
||||||
if (handleInvalidResponse(data, response)) return
|
if (handleInvalidResponse(data, response)) return@launch
|
||||||
val uploadFinishOptions = UploadFinishOptions(
|
val uploadFinishOptions = UploadFinishOptions(
|
||||||
uploadDmVideoOptions.uploadId,
|
uploadDmVideoOptions.uploadId,
|
||||||
"2",
|
"2",
|
||||||
@ -843,19 +834,16 @@ class ThreadManager private constructor(
|
|||||||
data.postValue(error("uploadFinishRequest was not successful and response error body was null", directItem))
|
data.postValue(error("uploadFinishRequest was not successful and response error body was null", directItem))
|
||||||
Log.e(TAG, "uploadFinishRequest was not successful and response error body was null")
|
Log.e(TAG, "uploadFinishRequest was not successful and response error body was null")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<String?>, t: Throwable) {
|
override fun onFailure(call: Call<String?>, t: Throwable) {
|
||||||
data.postValue(error(t.message, directItem))
|
data.postValue(error(t.message, directItem))
|
||||||
Log.e(TAG, "onFailure: ", t)
|
Log.e(TAG, "sendVideo: ", t)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} catch (e: Exception) {
|
||||||
|
data.postValue(error(e.message, directItem))
|
||||||
|
Log.e(TAG, "sendVideo: ", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(t: Throwable) {
|
|
||||||
data.postValue(error(t.message, directItem))
|
|
||||||
Log.e(TAG, "onFailure: ", t)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseResponse(
|
private fun parseResponse(
|
||||||
|
@ -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!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
255
app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt
Normal file
255
app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,14 @@ import android.content.ContentResolver
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import awais.instagrabber.models.UploadVideoOptions
|
import awais.instagrabber.models.UploadVideoOptions
|
||||||
import awais.instagrabber.utils.BitmapUtils.ThumbnailLoadCallback
|
|
||||||
import awais.instagrabber.webservices.interceptors.AddCookiesInterceptor
|
import awais.instagrabber.webservices.interceptors.AddCookiesInterceptor
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import okio.BufferedSink
|
import okio.BufferedSink
|
||||||
import okio.Okio
|
import okio.Okio
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import ru.gildor.coroutines.okhttp.await
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -17,89 +19,61 @@ import java.io.InputStream
|
|||||||
|
|
||||||
object MediaUploader {
|
object MediaUploader {
|
||||||
private const val HOST = "https://i.instagram.com"
|
private const val HOST = "https://i.instagram.com"
|
||||||
private val appExecutors = AppExecutors
|
private val octetStreamMediaType: MediaType = requireNotNull(MediaType.parse("application/octet-stream")) {
|
||||||
|
"No media type found for application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
fun uploadPhoto(
|
suspend fun uploadPhoto(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
contentResolver: ContentResolver,
|
contentResolver: ContentResolver,
|
||||||
listener: OnMediaUploadCompleteListener,
|
): MediaUploadResponse {
|
||||||
) {
|
return withContext(Dispatchers.IO) {
|
||||||
BitmapUtils.loadBitmap(contentResolver, uri, 1000f, false, object : ThumbnailLoadCallback {
|
val bitmapResult = BitmapUtils.loadBitmap(contentResolver, uri, 1000f, false)
|
||||||
override fun onLoad(bitmap: Bitmap?, width: Int, height: Int) {
|
val bitmap = bitmapResult?.bitmap ?: throw IOException("bitmap is null")
|
||||||
if (bitmap == null) {
|
uploadPhoto(bitmap)
|
||||||
listener.onFailure(RuntimeException("Bitmap result was null"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
uploadPhoto(bitmap, listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(t: Throwable) {
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
listener.onFailure(t)
|
private suspend fun uploadPhoto(
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun uploadPhoto(
|
|
||||||
bitmap: Bitmap,
|
bitmap: Bitmap,
|
||||||
listener: OnMediaUploadCompleteListener,
|
): MediaUploadResponse = withContext(Dispatchers.IO) {
|
||||||
) {
|
val file: File = BitmapUtils.convertToJpegAndSaveToFile(bitmap, null)
|
||||||
appExecutors.tasksThread.submit {
|
val byteLength: Long = file.length()
|
||||||
val file: File
|
|
||||||
val byteLength: Long
|
|
||||||
try {
|
|
||||||
file = BitmapUtils.convertToJpegAndSaveToFile(bitmap, null)
|
|
||||||
byteLength = file.length()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
listener.onFailure(e)
|
|
||||||
return@submit
|
|
||||||
}
|
|
||||||
val options = createUploadPhotoOptions(byteLength)
|
val options = createUploadPhotoOptions(byteLength)
|
||||||
val headers = getUploadPhotoHeaders(options)
|
val headers = getUploadPhotoHeaders(options)
|
||||||
val url = HOST + "/rupload_igphoto/" + options.name + "/"
|
val url = HOST + "/rupload_igphoto/" + options.name + "/"
|
||||||
appExecutors.networkIO.execute {
|
|
||||||
try {
|
try {
|
||||||
FileInputStream(file).use { input -> upload(input, url, headers, listener) }
|
FileInputStream(file).use { input -> upload(input, url, headers) }
|
||||||
} catch (e: IOException) {
|
|
||||||
listener.onFailure(e)
|
|
||||||
} finally {
|
} finally {
|
||||||
file.delete()
|
file.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun uploadVideo(
|
@Suppress("BlockingMethodInNonBlockingContext") // See https://youtrack.jetbrains.com/issue/KTIJ-838
|
||||||
|
suspend fun uploadVideo(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
contentResolver: ContentResolver,
|
contentResolver: ContentResolver,
|
||||||
options: UploadVideoOptions,
|
options: UploadVideoOptions,
|
||||||
listener: OnMediaUploadCompleteListener,
|
): MediaUploadResponse = withContext(Dispatchers.IO) {
|
||||||
) {
|
|
||||||
appExecutors.tasksThread.submit {
|
|
||||||
val headers = getUploadVideoHeaders(options)
|
val headers = getUploadVideoHeaders(options)
|
||||||
val url = HOST + "/rupload_igvideo/" + options.name + "/"
|
val url = HOST + "/rupload_igvideo/" + options.name + "/"
|
||||||
appExecutors.networkIO.execute {
|
|
||||||
try {
|
|
||||||
contentResolver.openInputStream(uri).use { input ->
|
contentResolver.openInputStream(uri).use { input ->
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
listener.onFailure(RuntimeException("InputStream was null"))
|
// listener.onFailure(RuntimeException("InputStream was null"))
|
||||||
return@execute
|
throw IllegalStateException("InputStream was null")
|
||||||
}
|
|
||||||
upload(input, url, headers, listener)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
listener.onFailure(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
upload(input, url, headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upload(
|
@Throws(IOException::class)
|
||||||
|
private suspend fun upload(
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
url: String,
|
url: String,
|
||||||
headers: Map<String, String>,
|
headers: Map<String, String>,
|
||||||
listener: OnMediaUploadCompleteListener,
|
): MediaUploadResponse {
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
val client = OkHttpClient.Builder()
|
val client = OkHttpClient.Builder()
|
||||||
// .addInterceptor(new LoggingInterceptor())
|
// .addInterceptor(new LoggingInterceptor())
|
||||||
@ -110,24 +84,23 @@ object MediaUploader {
|
|||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.headers(Headers.of(headers))
|
.headers(Headers.of(headers))
|
||||||
.url(url)
|
.url(url)
|
||||||
.post(create(MediaType.parse("application/octet-stream"), input))
|
.post(create(octetStreamMediaType, input))
|
||||||
.build()
|
.build()
|
||||||
val call = client.newCall(request)
|
return withContext(Dispatchers.IO) {
|
||||||
val response = call.execute()
|
val response = client.newCall(request).await()
|
||||||
val body = response.body()
|
val body = response.body()
|
||||||
if (!response.isSuccessful) {
|
@Suppress("BlockingMethodInNonBlockingContext") // Blocked by https://github.com/square/okio/issues/501
|
||||||
listener.onFailure(IOException("Unexpected code " + response + if (body != null) ": " + body.string() else ""))
|
MediaUploadResponse(response.code(), if (body != null) JSONObject(body.string()) else null)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
listener.onUploadComplete(MediaUploadResponse(response.code(), if (body != null) JSONObject(body.string()) else null))
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
listener.onFailure(e)
|
// rethrow for proper stacktrace. See https://github.com/gildor/kotlin-coroutines-okhttp/tree/master#wrap-exception-manually
|
||||||
|
throw IOException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun create(mediaType: MediaType?, inputStream: InputStream): RequestBody {
|
private fun create(mediaType: MediaType, inputStream: InputStream): RequestBody {
|
||||||
return object : RequestBody() {
|
return object : RequestBody() {
|
||||||
override fun contentType(): MediaType? {
|
override fun contentType(): MediaType {
|
||||||
return mediaType
|
return mediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,10 +120,5 @@ object MediaUploader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnMediaUploadCompleteListener {
|
|
||||||
fun onUploadComplete(response: MediaUploadResponse)
|
|
||||||
fun onFailure(t: Throwable)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class MediaUploadResponse(val responseCode: Int, val response: JSONObject?)
|
data class MediaUploadResponse(val responseCode: Int, val response: JSONObject?)
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user