mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-18 22:01:42 +08:00
fix(android): image asset handling regarding requestLegacyExternalStorage (#9373)
This commit is contained in:
24
packages/core/global-types.d.ts
vendored
24
packages/core/global-types.d.ts
vendored
@ -349,31 +349,31 @@ declare class WeakRef<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Java long from a number
|
* Create a Java long from a number
|
||||||
*/
|
*/
|
||||||
declare function long(value: number): any;
|
declare function long(value: number): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Java byte from a number
|
* Create a Java byte from a number
|
||||||
*/
|
*/
|
||||||
declare function byte(value: number): any;
|
declare function byte(value: number): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Java short from a number
|
* Create a Java short from a number
|
||||||
*/
|
*/
|
||||||
declare function short(value: number): any;
|
declare function short(value: number): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Java double from a number
|
* Create a Java double from a number
|
||||||
*/
|
*/
|
||||||
declare function double(value: number): any;
|
declare function double(value: number): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Java float from a number
|
* Create a Java float from a number
|
||||||
*/
|
*/
|
||||||
declare function float(value: number): any;
|
declare function float(value: number): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Java char from a string
|
* Create a Java char from a string
|
||||||
*/
|
*/
|
||||||
declare function char(value: string): any;
|
declare function char(value: string): any;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ImageAssetBase, getRequestedImageSize } from './image-asset-common';
|
import { ImageAssetBase, getRequestedImageSize } from './image-asset-common';
|
||||||
import { path as fsPath, knownFolders } from '../file-system';
|
import { path as fsPath, knownFolders } from '../file-system';
|
||||||
|
import { ad } from '../utils';
|
||||||
|
import { Screen } from '../platform';
|
||||||
export * from './image-asset-common';
|
export * from './image-asset-common';
|
||||||
|
|
||||||
export class ImageAsset extends ImageAssetBase {
|
export class ImageAsset extends ImageAssetBase {
|
||||||
@ -25,66 +26,20 @@ export class ImageAsset extends ImageAssetBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getImageAsync(callback: (image, error) => void) {
|
public getImageAsync(callback: (image, error) => void) {
|
||||||
const bitmapOptions = new android.graphics.BitmapFactory.Options();
|
org.nativescript.widgets.Utils.loadImageAsync(
|
||||||
bitmapOptions.inJustDecodeBounds = true;
|
ad.getApplicationContext(),
|
||||||
// read only the file size
|
this.android,
|
||||||
let bitmap = android.graphics.BitmapFactory.decodeFile(this.android, bitmapOptions);
|
JSON.stringify(this.options || {}),
|
||||||
const sourceSize = {
|
Screen.mainScreen.widthPixels,
|
||||||
width: bitmapOptions.outWidth,
|
Screen.mainScreen.heightPixels,
|
||||||
height: bitmapOptions.outHeight,
|
new org.nativescript.widgets.Utils.AsyncImageCallback({
|
||||||
};
|
onSuccess(bitmap) {
|
||||||
const requestedSize = getRequestedImageSize(sourceSize, this.options);
|
callback(bitmap, null);
|
||||||
|
},
|
||||||
const sampleSize = org.nativescript.widgets.image.Fetcher.calculateInSampleSize(bitmapOptions.outWidth, bitmapOptions.outHeight, requestedSize.width, requestedSize.height);
|
onError(ex) {
|
||||||
|
|
||||||
const finalBitmapOptions = new android.graphics.BitmapFactory.Options();
|
|
||||||
finalBitmapOptions.inSampleSize = sampleSize;
|
|
||||||
try {
|
|
||||||
let error = null;
|
|
||||||
// read as minimum bitmap as possible (slightly bigger than the requested size)
|
|
||||||
bitmap = android.graphics.BitmapFactory.decodeFile(this.android, finalBitmapOptions);
|
|
||||||
|
|
||||||
if (bitmap) {
|
|
||||||
if (requestedSize.width !== bitmap.getWidth() || requestedSize.height !== bitmap.getHeight()) {
|
|
||||||
// scale to exact size
|
|
||||||
bitmap = android.graphics.Bitmap.createScaledBitmap(bitmap, requestedSize.width, requestedSize.height, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rotationAngle = calculateAngleFromFile(this.android);
|
|
||||||
if (rotationAngle !== 0) {
|
|
||||||
const matrix = new android.graphics.Matrix();
|
|
||||||
matrix.postRotate(rotationAngle);
|
|
||||||
bitmap = android.graphics.Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bitmap) {
|
|
||||||
error = "Asset '" + this.android + "' cannot be found.";
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(bitmap, error);
|
|
||||||
} catch (ex) {
|
|
||||||
callback(null, ex);
|
callback(null, ex);
|
||||||
}
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateAngleFromFile(filename: string) {
|
|
||||||
let rotationAngle = 0;
|
|
||||||
const ei = new android.media.ExifInterface(filename);
|
|
||||||
const orientation = ei.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, android.media.ExifInterface.ORIENTATION_NORMAL);
|
|
||||||
|
|
||||||
switch (orientation) {
|
|
||||||
case android.media.ExifInterface.ORIENTATION_ROTATE_90:
|
|
||||||
rotationAngle = 90;
|
|
||||||
break;
|
|
||||||
case android.media.ExifInterface.ORIENTATION_ROTATE_180:
|
|
||||||
rotationAngle = 180;
|
|
||||||
break;
|
|
||||||
case android.media.ExifInterface.ORIENTATION_ROTATE_270:
|
|
||||||
rotationAngle = 270;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rotationAngle;
|
|
||||||
}
|
|
||||||
|
Binary file not shown.
@ -1,11 +1,6 @@
|
|||||||
declare module org {
|
declare module org {
|
||||||
module nativescript {
|
module nativescript {
|
||||||
module widgets {
|
module widgets {
|
||||||
|
|
||||||
export class Utils {
|
|
||||||
public static drawBoxShadow(view: android.view.View, value: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BoxShadowDrawable {
|
export class BoxShadowDrawable {
|
||||||
public constructor(drawable: android.graphics.drawable.Drawable, value: string);
|
public constructor(drawable: android.graphics.drawable.Drawable, value: string);
|
||||||
public getWrappedDrawable(): android.graphics.drawable.Drawable;
|
public getWrappedDrawable(): android.graphics.drawable.Drawable;
|
||||||
@ -632,3 +627,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module org {
|
||||||
|
export module nativescript {
|
||||||
|
export module widgets {
|
||||||
|
export class Utils {
|
||||||
|
public static class: java.lang.Class<org.nativescript.widgets.Utils>;
|
||||||
|
public static loadImageAsync(param0: globalAndroid.content.Context, param1: string, param2: string, param3: number, param4: number, param5: org.nativescript.widgets.Utils.AsyncImageCallback): void;
|
||||||
|
public static drawBoxShadow(param0: globalAndroid.view.View, param1: string): void;
|
||||||
|
public constructor();
|
||||||
|
}
|
||||||
|
export module Utils {
|
||||||
|
export class AsyncImageCallback {
|
||||||
|
public static class: java.lang.Class<org.nativescript.widgets.Utils.AsyncImageCallback>;
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of the org.nativescript.widgets.Utils$AsyncImageCallback interface with the provided implementation. An empty constructor exists calling super() when extending the interface class.
|
||||||
|
*/
|
||||||
|
public constructor(implementation: {
|
||||||
|
onSuccess(param0: globalAndroid.graphics.Bitmap): void;
|
||||||
|
onError(param0: java.lang.Exception): void;
|
||||||
|
});
|
||||||
|
public constructor();
|
||||||
|
public onSuccess(param0: globalAndroid.graphics.Bitmap): void;
|
||||||
|
public onError(param0: java.lang.Exception): void;
|
||||||
|
}
|
||||||
|
export class ImageAssetOptions {
|
||||||
|
public static class: java.lang.Class<org.nativescript.widgets.Utils.ImageAssetOptions>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -78,6 +78,7 @@ dependencies {
|
|||||||
implementation 'androidx.viewpager:viewpager:' + androidxVersion
|
implementation 'androidx.viewpager:viewpager:' + androidxVersion
|
||||||
implementation 'androidx.fragment:fragment:' + androidxVersion
|
implementation 'androidx.fragment:fragment:' + androidxVersion
|
||||||
implementation 'androidx.transition:transition:' + androidxVersion
|
implementation 'androidx.transition:transition:' + androidxVersion
|
||||||
|
implementation "androidx.exifinterface:exifinterface:1.3.2"
|
||||||
} else {
|
} else {
|
||||||
println 'Using support library'
|
println 'Using support library'
|
||||||
implementation 'com.android.support:support-v4:' + computeSupportVersion()
|
implementation 'com.android.support:support-v4:' + computeSupportVersion()
|
||||||
|
@ -17,13 +17,17 @@
|
|||||||
package org.nativescript.widgets.image;
|
package org.nativescript.widgets.image;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.media.ExifInterface;
|
import androidx.exifinterface.media.ExifInterface;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
|
||||||
@ -34,6 +38,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -257,8 +262,9 @@ public class Fetcher extends Worker {
|
|||||||
if (debuggable > 0) {
|
if (debuggable > 0) {
|
||||||
Log.v(TAG, "process: " + uri);
|
Log.v(TAG, "process: " + uri);
|
||||||
}
|
}
|
||||||
|
if(uri.startsWith(CONTENT_PREFIX)){
|
||||||
if (uri.startsWith(FILE_PREFIX)) {
|
return decodeSampledBitmapFromContent(uri,mResolver , decodeWidth, decodeHeight, keepAspectRatio, getCache());
|
||||||
|
}else if (uri.startsWith(FILE_PREFIX)) {
|
||||||
String filename = uri.substring(FILE_PREFIX.length());
|
String filename = uri.substring(FILE_PREFIX.length());
|
||||||
return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, keepAspectRatio, getCache());
|
return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, keepAspectRatio, getCache());
|
||||||
} else if (uri.startsWith(RESOURCE_PREFIX)) {
|
} else if (uri.startsWith(RESOURCE_PREFIX)) {
|
||||||
@ -474,6 +480,62 @@ public class Fetcher extends Worker {
|
|||||||
return scaleAndRotateBitmap(bitmap, ei, reqWidth, reqHeight, keepAspectRatio);
|
return scaleAndRotateBitmap(bitmap, ei, reqWidth, reqHeight, keepAspectRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void closePfd(ParcelFileDescriptor pfd){
|
||||||
|
if(pfd != null){
|
||||||
|
try {
|
||||||
|
pfd.close();
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and sample down a bitmap from a file to the requested width and height.
|
||||||
|
*
|
||||||
|
* @param content The content uri of the file to decode
|
||||||
|
* @param reqWidth The requested width of the resulting bitmap
|
||||||
|
* @param reqHeight The requested height of the resulting bitmap
|
||||||
|
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||||
|
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions
|
||||||
|
* that are equal to or greater than the requested width and height
|
||||||
|
*/
|
||||||
|
public static Bitmap decodeSampledBitmapFromContent(String content, ContentResolver resolver, int reqWidth, int reqHeight,
|
||||||
|
boolean keepAspectRatio, Cache cache) {
|
||||||
|
|
||||||
|
// First decode with inJustDecodeBounds=true to check dimensions
|
||||||
|
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
options.inJustDecodeBounds = true;
|
||||||
|
|
||||||
|
|
||||||
|
Uri uri = android.net.Uri.parse(content);
|
||||||
|
ParcelFileDescriptor pfd = null;
|
||||||
|
try {
|
||||||
|
pfd = resolver.openFileDescriptor(uri, "r");
|
||||||
|
BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, options);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.v(TAG, "File not found " + content);
|
||||||
|
closePfd(pfd);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
|
||||||
|
|
||||||
|
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||||
|
if (Utils.hasHoneycomb()) {
|
||||||
|
addInBitmapOptions(options, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode bitmap with inSampleSize set
|
||||||
|
options.inJustDecodeBounds = false;
|
||||||
|
|
||||||
|
final Bitmap bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, options);
|
||||||
|
|
||||||
|
ExifInterface ei = getExifInterface(pfd.getFileDescriptor());
|
||||||
|
closePfd(pfd);
|
||||||
|
|
||||||
|
return scaleAndRotateBitmap(bitmap, ei, reqWidth, reqHeight, keepAspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
private static Bitmap scaleAndRotateBitmap(Bitmap bitmap, ExifInterface ei, int reqWidth, int reqHeight,
|
private static Bitmap scaleAndRotateBitmap(Bitmap bitmap, ExifInterface ei, int reqWidth, int reqHeight,
|
||||||
boolean keepAspectRatio) {
|
boolean keepAspectRatio) {
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.nativescript.widgets.image;
|
package org.nativescript.widgets.image;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@ -37,6 +38,7 @@ public abstract class Worker {
|
|||||||
|
|
||||||
protected static final String RESOURCE_PREFIX = "res://";
|
protected static final String RESOURCE_PREFIX = "res://";
|
||||||
protected static final String FILE_PREFIX = "file:///";
|
protected static final String FILE_PREFIX = "file:///";
|
||||||
|
protected static final String CONTENT_PREFIX = "content://";
|
||||||
|
|
||||||
static final String TAG = "JS";
|
static final String TAG = "JS";
|
||||||
private static final int FADE_IN_TIME = 200;
|
private static final int FADE_IN_TIME = 200;
|
||||||
@ -49,7 +51,7 @@ public abstract class Worker {
|
|||||||
|
|
||||||
protected boolean mPauseWork = false;
|
protected boolean mPauseWork = false;
|
||||||
protected Resources mResources;
|
protected Resources mResources;
|
||||||
|
protected ContentResolver mResolver;
|
||||||
private static final int MESSAGE_CLEAR = 0;
|
private static final int MESSAGE_CLEAR = 0;
|
||||||
private static final int MESSAGE_INIT_DISK_CACHE = 1;
|
private static final int MESSAGE_INIT_DISK_CACHE = 1;
|
||||||
private static final int MESSAGE_FLUSH = 2;
|
private static final int MESSAGE_FLUSH = 2;
|
||||||
@ -59,7 +61,7 @@ public abstract class Worker {
|
|||||||
|
|
||||||
protected Worker(Context context) {
|
protected Worker(Context context) {
|
||||||
mResources = context.getResources();
|
mResources = context.getResources();
|
||||||
|
mResolver = context.getContentResolver();
|
||||||
// Negative means not initialized.
|
// Negative means not initialized.
|
||||||
if (debuggable < 0) {
|
if (debuggable < 0) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,12 +1,33 @@
|
|||||||
package org.nativescript.widgets;
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.exifinterface.media.ExifInterface;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
public static void drawBoxShadow(View view, String value) {
|
public static void drawBoxShadow(View view, String value) {
|
||||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||||
@ -16,14 +37,14 @@ public class Utils {
|
|||||||
|
|
||||||
Drawable currentBg = view.getBackground();
|
Drawable currentBg = view.getBackground();
|
||||||
|
|
||||||
if(currentBg != null) {
|
if (currentBg != null) {
|
||||||
Log.d("BoxShadowDrawable", "current BG is: " + currentBg.getClass().getName());
|
Log.d("BoxShadowDrawable", "current BG is: " + currentBg.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(currentBg == null) {
|
if (currentBg == null) {
|
||||||
Log.d("BoxShadowDrawable", "view had no background!");
|
Log.d("BoxShadowDrawable", "view had no background!");
|
||||||
currentBg = new ColorDrawable(Color.TRANSPARENT);
|
currentBg = new ColorDrawable(Color.TRANSPARENT);
|
||||||
} else if(currentBg instanceof BoxShadowDrawable) {
|
} else if (currentBg instanceof BoxShadowDrawable) {
|
||||||
currentBg = ((BoxShadowDrawable) view.getBackground()).getWrappedDrawable();
|
currentBg = ((BoxShadowDrawable) view.getBackground()).getWrappedDrawable();
|
||||||
Log.d("BoxShadowDrawable", "already a BoxShadowDrawable, getting wrapped drawable:" + currentBg.getClass().getName());
|
Log.d("BoxShadowDrawable", "already a BoxShadowDrawable, getting wrapped drawable:" + currentBg.getClass().getName());
|
||||||
}
|
}
|
||||||
@ -33,7 +54,7 @@ public class Utils {
|
|||||||
view.setBackground(new BoxShadowDrawable(currentBg, value));
|
view.setBackground(new BoxShadowDrawable(currentBg, value));
|
||||||
|
|
||||||
Drawable bg = view.getBackground();
|
Drawable bg = view.getBackground();
|
||||||
if(bg != null) {
|
if (bg != null) {
|
||||||
Log.d("BoxShadowDrawable", "new current bg: " + bg.getClass().getName());
|
Log.d("BoxShadowDrawable", "new current bg: " + bg.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +71,222 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface AsyncImageCallback {
|
||||||
|
void onSuccess(Bitmap bitmap);
|
||||||
|
|
||||||
|
void onError(Exception exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ImageAssetOptions {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
boolean keepAspectRatio;
|
||||||
|
boolean autoScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Executor executors = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
|
||||||
|
private static Pair<Integer, Integer> getAspectSafeDimensions(float sourceWidth, float sourceHeight, float reqWidth, float reqHeight) {
|
||||||
|
float widthCoef = sourceWidth / reqWidth;
|
||||||
|
float heightCoef = sourceHeight / reqHeight;
|
||||||
|
float aspectCoef = Math.min(widthCoef, heightCoef);
|
||||||
|
|
||||||
|
return new Pair<>((int) Math.floor(sourceWidth / aspectCoef), (int) Math.floor(sourceHeight / aspectCoef));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Pair<Integer, Integer> getRequestedImageSize(Pair<Integer, Integer> src, Pair<Integer, Integer> maxSize, ImageAssetOptions options) {
|
||||||
|
int reqWidth = options.width;
|
||||||
|
if (reqWidth <= 0) {
|
||||||
|
reqWidth = Math.min(src.first, maxSize.first);
|
||||||
|
}
|
||||||
|
int reqHeight = options.height;
|
||||||
|
if (reqHeight <= 0) {
|
||||||
|
reqHeight = Math.min(src.second, maxSize.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.keepAspectRatio) {
|
||||||
|
Pair<Integer, Integer> safeAspectSize = getAspectSafeDimensions(src.first, src.second, reqWidth, reqHeight);
|
||||||
|
reqWidth = safeAspectSize.first;
|
||||||
|
reqHeight = safeAspectSize.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(reqWidth, reqHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void closePfd(ParcelFileDescriptor pfd) {
|
||||||
|
if (pfd != null) {
|
||||||
|
try {
|
||||||
|
pfd.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int calculateAngleFromFile(String filename) {
|
||||||
|
int rotationAngle = 0;
|
||||||
|
ExifInterface ei;
|
||||||
|
try {
|
||||||
|
ei = new ExifInterface(filename);
|
||||||
|
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
||||||
|
|
||||||
|
switch (orientation) {
|
||||||
|
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||||
|
rotationAngle = 90;
|
||||||
|
break;
|
||||||
|
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||||
|
rotationAngle = 180;
|
||||||
|
break;
|
||||||
|
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||||
|
rotationAngle = 270;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotationAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int calculateAngleFromFileDescriptor(FileDescriptor fd) {
|
||||||
|
int rotationAngle = 0;
|
||||||
|
ExifInterface ei;
|
||||||
|
try {
|
||||||
|
ei = new ExifInterface(fd);
|
||||||
|
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
||||||
|
|
||||||
|
switch (orientation) {
|
||||||
|
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||||
|
rotationAngle = 90;
|
||||||
|
break;
|
||||||
|
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||||
|
rotationAngle = 180;
|
||||||
|
break;
|
||||||
|
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||||
|
rotationAngle = 270;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotationAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
public static void loadImageAsync(final Context context, final String src, final String options, final int maxWidth, final int maxHeight, final AsyncImageCallback callback) {
|
||||||
|
executors.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
|
||||||
|
bitmapOptions.inJustDecodeBounds = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Bitmap bitmap;
|
||||||
|
ParcelFileDescriptor pfd = null;
|
||||||
|
if (src.startsWith("content://")) {
|
||||||
|
Uri uri = android.net.Uri.parse(src);
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
try {
|
||||||
|
pfd = resolver.openFileDescriptor(uri, "r");
|
||||||
|
} catch (final FileNotFoundException e) {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
closePfd(pfd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
android.graphics.BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, bitmapOptions);
|
||||||
|
} else {
|
||||||
|
android.graphics.BitmapFactory.decodeFile(src, bitmapOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageAssetOptions opts = new ImageAssetOptions();
|
||||||
|
opts.keepAspectRatio = true;
|
||||||
|
opts.autoScaleFactor = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject object = new JSONObject(options);
|
||||||
|
opts.width = object.optInt("width", 0);
|
||||||
|
opts.height = object.optInt("height", 0);
|
||||||
|
opts.keepAspectRatio = object.optBoolean("keepAspectRatio", true);
|
||||||
|
opts.autoScaleFactor = object.optBoolean("autoScaleFactor", true);
|
||||||
|
} catch (JSONException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pair<Integer, Integer> sourceSize = new Pair<>(bitmapOptions.outWidth, bitmapOptions.outHeight);
|
||||||
|
Pair<Integer, Integer> maxSize = new Pair<>(maxWidth, maxHeight);
|
||||||
|
Pair<Integer, Integer> requestedSize = getRequestedImageSize(sourceSize, maxSize, opts);
|
||||||
|
int sampleSize = org.nativescript.widgets.image.Fetcher.calculateInSampleSize(bitmapOptions.outWidth, bitmapOptions.outHeight, requestedSize.first, requestedSize.second);
|
||||||
|
BitmapFactory.Options finalBitmapOptions = new BitmapFactory.Options();
|
||||||
|
finalBitmapOptions.inSampleSize = sampleSize;
|
||||||
|
|
||||||
|
|
||||||
|
String error = null;
|
||||||
|
// read as minimum bitmap as possible (slightly bigger than the requested size)
|
||||||
|
|
||||||
|
|
||||||
|
if (pfd != null) {
|
||||||
|
bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, finalBitmapOptions);
|
||||||
|
} else {
|
||||||
|
bitmap = android.graphics.BitmapFactory.decodeFile(src, finalBitmapOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (bitmap != null) {
|
||||||
|
if (requestedSize.first != bitmap.getWidth() || requestedSize.second != bitmap.getHeight()) {
|
||||||
|
// scale to exact size
|
||||||
|
bitmap = android.graphics.Bitmap.createScaledBitmap(bitmap, requestedSize.first, requestedSize.second, true);
|
||||||
|
}
|
||||||
|
int rotationAngle;
|
||||||
|
|
||||||
|
if (pfd != null) {
|
||||||
|
rotationAngle = calculateAngleFromFileDescriptor(pfd.getFileDescriptor());
|
||||||
|
closePfd(pfd);
|
||||||
|
} else {
|
||||||
|
rotationAngle = calculateAngleFromFile(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotationAngle != 0) {
|
||||||
|
Matrix matrix = new android.graphics.Matrix();
|
||||||
|
matrix.postRotate(rotationAngle);
|
||||||
|
bitmap = android.graphics.Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bitmap == null) {
|
||||||
|
error = "Asset '" + src + "' cannot be found.";
|
||||||
|
}
|
||||||
|
|
||||||
|
final String finalError = error;
|
||||||
|
final Bitmap finalBitmap = bitmap;
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (finalError != null) {
|
||||||
|
callback.onError(new Exception(finalError));
|
||||||
|
} else {
|
||||||
|
callback.onSuccess(finalBitmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// public static void clearBoxShadow(View view) {
|
// public static void clearBoxShadow(View view) {
|
||||||
// if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
// if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||||
// return;
|
// return;
|
||||||
|
Reference in New Issue
Block a user