mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 11:35:34 +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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user