Created
May 23, 2019 06:51
-
-
Save fishkingsin/a2ce1599b165c6dbf650a62a87be82e0 to your computer and use it in GitHub Desktop.
ImageProcessingTask
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.creedon.Nixplay; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.BitmapFactory; | |
import android.net.Uri; | |
import android.os.AsyncTask; | |
import android.support.media.ExifInterface; | |
import android.util.Log; | |
import com.alinz.parkerdan.shareextension.RealPathUtil; | |
import com.crashlytics.android.Crashlytics; | |
import com.creedon.reactlibrary.imagepicker.utils.FileUtils; | |
import com.creedon.reactlibrary.imagepicker.utils.ImageResizer; | |
import com.facebook.react.bridge.Arguments; | |
import com.facebook.react.bridge.Promise; | |
import com.facebook.react.bridge.WritableArray; | |
import com.facebook.react.bridge.WritableMap; | |
import com.facebook.react.bridge.WritableNativeArray; | |
import org.json.JSONArray; | |
import org.json.JSONException; | |
import org.json.JSONObject; | |
import java.io.File; | |
import java.io.IOException; | |
import java.lang.ref.WeakReference; | |
import java.util.Date; | |
import java.util.Iterator; | |
/** | |
* _ _ _______ ________ _ _____ __ | |
* | \ | |_ _\ \ / /| ___ \ | / _ \ \ / / | |
* | \| | | | \ V / | |_/ / | / /_\ \ V / | |
* | . ` | | | / \ | __/| | | _ |\ / | |
* | |\ |_| |_/ /^\ \| | | |____| | | || | | |
* \_| \_/\___/\/ \/\_| \_____/\_| |_/\_/ | |
* <p> | |
* Created by jameskong on 2019-05-23. | |
*/ | |
public class ImageProcessingTask extends AsyncTask<Uri, JSONObject, WritableMap> { | |
private static final String TAG = "NixShareModule"; | |
private static final String URI = "uri"; | |
private static final String IMAGES_KEY = "images"; | |
private static final String FILEPROVIDER_KEY = "fileProvider"; | |
private static final String FILENAME = "fileName"; | |
private static final String FULLFILEPATH = "fullFilePath"; | |
private static final String MIME_TYPE = "mime"; | |
private static final String TYPE = "type"; | |
private static final String CAPTION = "caption"; | |
private static final String ID = "id"; | |
private static final String META = "meta"; | |
private static final String FILE_SIZE = "fileSize"; | |
private static final String ERROR_KEY = "error"; | |
private static final String LOADING_KEY = "loading"; | |
private WeakReference<Promise> promise; | |
private WeakReference<Activity> currentActivity; | |
private long fileSizeLimit = 20000000; | |
private int widthLimit = 8000; | |
private int heightLimit = 8000; | |
private int maxWidth = 1820; | |
private int maxHeight = 1820; | |
ImageProcessingTask(Activity currentActivity, Promise promise, | |
long fileSizeLimit, | |
int widthLimit, | |
int heightLimit, | |
int maxWidth, | |
int maxHeight) { | |
this.fileSizeLimit = fileSizeLimit; | |
this.widthLimit = widthLimit; | |
this.heightLimit = heightLimit; | |
this.maxWidth = maxWidth; | |
this.maxHeight = maxHeight; | |
this.currentActivity = new WeakReference<>(currentActivity); | |
this.promise = new WeakReference<>(promise); | |
} | |
// Runs in UI before background thread is called | |
@Override | |
protected void onPreExecute() { | |
super.onPreExecute(); | |
// Do something like display a progress bar | |
} | |
// This is run in a background thread | |
@Override | |
protected WritableMap doInBackground(Uri... uris) { | |
WritableMap map = Arguments.createMap(); | |
JSONArray data = new JSONArray(); | |
// get the string from params, which is an array | |
String fileProvider = ""; | |
// Do something that takes a long time, for example: | |
try { | |
for (int i = 0; i < uris.length; i++) { | |
Uri uri = uris[i]; | |
// Do things | |
fileProvider = uri.getAuthority(); | |
try { | |
JSONObject imageDataJSON = convertToBitmapAndCreateAFile(this.currentActivity.get(), uri); | |
data.put(imageDataJSON); | |
// Call this to update your progress | |
publishProgress(imageDataJSON); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
map.putString(ERROR_KEY, e.toString()); | |
} catch (JSONException e) { | |
e.printStackTrace(); | |
map.putString(ERROR_KEY, e.toString()); | |
} | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
map.putString(ERROR_KEY, e.toString()); | |
} | |
try { | |
map.putArray(IMAGES_KEY, convertJsonToArray(data)); | |
} catch (Exception e) { | |
map.putString(ERROR_KEY, e.toString()); | |
Crashlytics.logException(e); | |
e.printStackTrace(); | |
} | |
map.putString(FILEPROVIDER_KEY, fileProvider); | |
return map; | |
} | |
// This is called from background thread but runs in UI | |
@Override | |
protected void onProgressUpdate(JSONObject... values) { | |
super.onProgressUpdate(values); | |
// Do things like update the progress bar | |
} | |
// This runs in UI when background thread finishes | |
@Override | |
protected void onPostExecute(WritableMap result) { | |
super.onPostExecute(result); | |
if (!result.hasKey(ERROR_KEY)) { | |
this.promise.get().resolve(result); | |
} else { | |
this.promise.get().reject(new Error(result.getString(ERROR_KEY))); | |
} | |
// Do things like hide the progress bar or change a TextView | |
} | |
private JSONObject convertToBitmapAndCreateAFile(Activity activity, Uri uri) throws IOException, JSONException { | |
JSONObject mDetails = new JSONObject(); | |
File exportedFile = null; | |
String fileName = ""; | |
long fileSize = fileSizeLimit; | |
final BitmapFactory.Options options = new BitmapFactory.Options(); | |
options.inJustDecodeBounds = false; | |
options.inPurgeable = true; | |
options.inTempStorage = new byte[16 * 1024]; | |
options.inSampleSize = calculateInSampleSize(options, widthLimit, heightLimit); | |
try { | |
String mimeType = FileUtils.getMimeType(activity, uri); | |
fileName = FileUtils.getFileName(activity, uri); | |
if (uri.getAuthority() != null && !uri.getAuthority().equals("0@media")) { | |
// create temp file first | |
if (FileUtils.isValidMimeType(mimeType)) { | |
File tempFile = new File(activity.getCacheDir(), fileName); | |
if (!tempFile.exists()) { | |
tempFile = FileUtils.copyFromUri(uri, | |
activity, | |
activity.getCacheDir(), | |
fileName); | |
} | |
exportedFile = resizeFileWithouAuthority(activity, Uri.fromFile(tempFile), options); | |
} | |
} else { | |
Log.d(TAG, "No Authority: " + uri.toString()); | |
exportedFile = resizeFileWithouAuthority(activity, uri, options); | |
} | |
mDetails.put(ID, uri.toString()); | |
mDetails.put(FILENAME, fileName); | |
mDetails.put(MIME_TYPE, mimeType); | |
mDetails.put(TYPE, mimeType); | |
mDetails.put(CAPTION, ""); | |
mDetails.put(FILE_SIZE, fileSize); | |
if (exportedFile != null && exportedFile.exists() && exportedFile.length() > 0) { | |
Uri exportedFileUri = Uri.fromFile(exportedFile); | |
mDetails.put(URI, exportedFileUri.toString()); | |
mDetails.put(FULLFILEPATH, exportedFileUri.toString()); | |
mDetails.put(FILE_SIZE, exportedFile.length()); | |
mDetails.put(META, new JSONObject()); | |
} else { | |
JSONObject newMeta = new JSONObject(); | |
newMeta.put(ERROR_KEY, "Unable to export file :" + uri.toString()); | |
newMeta.put(LOADING_KEY, false); | |
mDetails.put(META, newMeta); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
throw e; | |
} | |
if (BuildConfig.DEBUG) Log.d(TAG, mDetails.toString()); | |
return mDetails; | |
} | |
/** | |
@param uri assume from file:// | |
*/ | |
private File resizeFileWithouAuthority(Context context, Uri uri, BitmapFactory.Options options) throws IOException { | |
String realPath = RealPathUtil.getRealPathFromURI(context, uri); | |
if (realPath == null) { | |
throw new NullPointerException("realPath is null"); | |
} | |
BitmapFactory.decodeFile(realPath, options); | |
int originalWidth = options.outWidth; | |
int originalHeight = options.outHeight; | |
File inputFile = new File(realPath); | |
ExifInterface exifInterface = null; | |
if (realPath != null) { | |
exifInterface = new ExifInterface(realPath); | |
} | |
String picturesDir = context.getCacheDir().getAbsolutePath(); | |
long fileSize = inputFile.length(); | |
/* | |
Android image criteria | |
1. if image file size > 21 mb downscale to both with/height < heightLimit or widthLimit | |
2. if resolution either with/height > 8000x8000, downscale to both with/height < widthLimit x heightLimit | |
3. if user turn on optimize upload resize | |
*/ | |
boolean needResize = (fileSize > fileSizeLimit || | |
(maxWidth > 0 && maxHeight > 0) || | |
(originalWidth > widthLimit || originalHeight > heightLimit) | |
); | |
File exportedFile = null; | |
if (needResize) { | |
int width = originalWidth > widthLimit ? ((maxWidth > 0 && maxHeight > 0) ? maxWidth : widthLimit) : originalWidth; | |
int height = originalHeight > heightLimit ? ((maxWidth > 0 && maxHeight > 0) ? maxHeight : heightLimit) : originalHeight; | |
int quality = fileSize > fileSizeLimit ? 80 : 100; | |
try { | |
exportedFile = ImageResizer.createResizedImage(context, | |
Uri.fromFile(new File(realPath)), | |
width, | |
height, | |
Bitmap.CompressFormat.JPEG, | |
quality, | |
0, | |
picturesDir, | |
exifInterface); | |
} catch (IOException e) { | |
Crashlytics.logException(e); | |
throw e; | |
} finally { | |
if (exportedFile == null) { | |
exportedFile = inputFile; | |
} | |
} | |
} else { | |
if (!inputFile.getParent().equals(context.getCacheDir().getAbsolutePath())) { | |
File outputFile = new File(picturesDir); | |
exportedFile = ImageResizer.copy(inputFile, outputFile, Long.toString(new Date().getTime()), Bitmap.CompressFormat.JPEG); | |
} else { | |
exportedFile = inputFile; | |
} | |
} | |
return exportedFile; | |
} | |
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { | |
final int height = options.outHeight; | |
final int width = options.outWidth; | |
int inSampleSize = 1; | |
if (height > reqHeight || width > reqWidth) { | |
final int halfHeight = height / 2; | |
final int halfWidth = width / 2; | |
while ((halfHeight / inSampleSize) > reqHeight | |
&& (halfWidth / inSampleSize) > reqWidth) { | |
inSampleSize *= 2; | |
} | |
} | |
return inSampleSize; | |
} | |
private static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONException { | |
WritableMap map = Arguments.createMap(); | |
Iterator<String> iterator = jsonObject.keys(); | |
while (iterator.hasNext()) { | |
String key = iterator.next(); | |
Object value = jsonObject.get(key); | |
if (value instanceof JSONObject) { | |
map.putMap(key, convertJsonToMap((JSONObject) value)); | |
} else if (value instanceof JSONArray) { | |
map.putArray(key, convertJsonToArray((JSONArray) value)); | |
} else if (value instanceof Boolean) { | |
map.putBoolean(key, (Boolean) value); | |
} else if (value instanceof Integer) { | |
map.putInt(key, (Integer) value); | |
} else if (value instanceof Double) { | |
map.putDouble(key, (Double) value); | |
} else if (value instanceof String) { | |
map.putString(key, (String) value); | |
} else { | |
map.putString(key, value.toString()); | |
} | |
} | |
return map; | |
} | |
private static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSONException { | |
WritableArray array = new WritableNativeArray(); | |
for (int i = 0; i < jsonArray.length(); i++) { | |
Object value = jsonArray.get(i); | |
if (value instanceof JSONObject) { | |
array.pushMap(convertJsonToMap((JSONObject) value)); | |
} else if (value instanceof JSONArray) { | |
array.pushArray(convertJsonToArray((JSONArray) value)); | |
} else if (value instanceof Boolean) { | |
array.pushBoolean((Boolean) value); | |
} else if (value instanceof Integer) { | |
array.pushInt((Integer) value); | |
} else if (value instanceof Double) { | |
array.pushDouble((Double) value); | |
} else if (value instanceof String) { | |
array.pushString((String) value); | |
} else { | |
array.pushString(value.toString()); | |
} | |
} | |
return array; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment