mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-22 08:21:07 +08:00
Merge branch 'master' of github.com:NativeScript/NativeScript
# Conflicts: # package.json # packages/core/bundle-entry-points.ts # packages/core/color/color-common.ts # packages/core/color/index.d.ts # packages/core/globals/index.ts # packages/core/package.json # packages/core/ui/core/bindable/index.ts # packages/core/ui/core/properties/index.ts # packages/core/ui/core/view/index.android.ts # packages/core/ui/frame/index.android.ts # packages/core/ui/styling/style-scope.ts # packages/ui-mobile-base/android/gradle/wrapper/gradle-wrapper.properties # packages/webpack5/package.json # packages/webpack5/src/configuration/base.ts
This commit is contained in:
@ -84,6 +84,7 @@ dependencies {
|
||||
implementation 'androidx.fragment:fragment:' + androidxVersion
|
||||
implementation 'androidx.transition:transition:' + androidxVersion
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.2"
|
||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||
} else {
|
||||
println 'Using support library'
|
||||
implementation 'com.android.support:support-v4:' + computeSupportVersion()
|
||||
|
@ -7,6 +7,9 @@ import android.content.Context;
|
||||
import android.graphics.*;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import org.nativescript.widgets.image.BitmapOwner;
|
||||
import org.nativescript.widgets.image.Fetcher;
|
||||
@ -14,7 +17,7 @@ import org.nativescript.widgets.image.Worker;
|
||||
/**
|
||||
* @author hhristov
|
||||
*/
|
||||
public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
public class ImageView extends androidx.appcompat.widget.AppCompatImageView implements BitmapOwner {
|
||||
private static final double EPSILON = 1E-05;
|
||||
|
||||
private Path path = new Path();
|
||||
@ -36,6 +39,10 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
private Worker.OnImageLoadedListener mListener;
|
||||
private boolean mAttachedToWindow = false;
|
||||
|
||||
static {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
}
|
||||
|
||||
public float getRotationAngle() {
|
||||
return rotationAngle;
|
||||
}
|
||||
@ -214,7 +221,9 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
BorderDrawable background = this.getBackground() instanceof BorderDrawable ? (BorderDrawable) this.getBackground() : null;
|
||||
|
||||
if(this.mBitmap == null && this.getDrawable() != null) {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
if (this.mBitmap != null) {
|
||||
float borderTopLeftRadius, borderTopRightRadius, borderBottomRightRadius, borderBottomLeftRadius;
|
||||
|
||||
@ -332,4 +341,4 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
public void setDrawable(Drawable asyncDrawable) {
|
||||
this.setImageDrawable(asyncDrawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.nativescript.widgets;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
@ -12,23 +13,38 @@ import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Base64OutputStream;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
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 static Drawable getDrawable(String uri, Context context){
|
||||
String resPath = uri.substring("res://".length());
|
||||
int resId = context.getResources().getIdentifier(resPath, "drawable", context.getPackageName());
|
||||
if (resId > 0) {
|
||||
return AppCompatResources.getDrawable(context, resId);
|
||||
} else {
|
||||
Log.v("JS", "Missing Image with resourceID: " + uri);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static void drawBoxShadow(View view, String value) {
|
||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||
return;
|
||||
@ -72,7 +88,7 @@ public class Utils {
|
||||
}
|
||||
|
||||
public interface AsyncImageCallback {
|
||||
void onSuccess(Bitmap bitmap);
|
||||
void onSuccess(Object bitmap);
|
||||
|
||||
void onError(Exception exception);
|
||||
}
|
||||
@ -287,6 +303,145 @@ public class Utils {
|
||||
});
|
||||
}
|
||||
|
||||
static Bitmap.CompressFormat getTargetFormat(String format) {
|
||||
switch (format) {
|
||||
case "jpeg":
|
||||
case "jpg":
|
||||
return Bitmap.CompressFormat.JPEG;
|
||||
default:
|
||||
return Bitmap.CompressFormat.PNG;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void saveToFileAsync(final Bitmap bitmap, final String path, final String format, final int quality, final AsyncImageCallback callback) {
|
||||
executors.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean isSuccess = false;
|
||||
Exception exception = null;
|
||||
if (bitmap != null) {
|
||||
Bitmap.CompressFormat targetFormat = getTargetFormat(format);
|
||||
try (BufferedOutputStream outputStream = new BufferedOutputStream(new java.io.FileOutputStream(path))) {
|
||||
isSuccess = bitmap.compress(targetFormat, quality, outputStream);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
final Exception finalException = exception;
|
||||
final boolean finalIsSuccess = isSuccess;
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (finalException != null) {
|
||||
callback.onError(finalException);
|
||||
} else {
|
||||
callback.onSuccess(finalIsSuccess);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void toBase64StringAsync(final Bitmap bitmap, final String format, final int quality, final AsyncImageCallback callback) {
|
||||
executors.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String result = null;
|
||||
Exception exception = null;
|
||||
if (bitmap != null) {
|
||||
|
||||
Bitmap.CompressFormat targetFormat = getTargetFormat(format);
|
||||
|
||||
try (
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
Base64OutputStream base64Stream = new Base64OutputStream(outputStream, android.util.Base64.NO_WRAP)
|
||||
) {
|
||||
bitmap.compress(targetFormat, quality, base64Stream);
|
||||
result = outputStream.toString();
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
final Exception finalException = exception;
|
||||
final String finalResult = result;
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (finalException != null) {
|
||||
callback.onError(finalException);
|
||||
} else {
|
||||
callback.onSuccess(finalResult);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static Pair<Integer, Integer> getScaledDimensions(float width, float height, float maxSize) {
|
||||
if (height >= width) {
|
||||
if (height <= maxSize) {
|
||||
// if image already smaller than the required height
|
||||
return new Pair<>((int) width, (int) height);
|
||||
}
|
||||
|
||||
return new Pair<>(
|
||||
Math.round((maxSize * width) / height)
|
||||
, (int) height);
|
||||
}
|
||||
|
||||
if (width <= maxSize) {
|
||||
// if image already smaller than the required width
|
||||
return new Pair<>((int) width, (int) height);
|
||||
}
|
||||
|
||||
return new Pair<>((int) maxSize, Math.round((maxSize * height) / width));
|
||||
|
||||
}
|
||||
|
||||
public static void resizeAsync(final Bitmap bitmap, final float maxSize, final String options, final AsyncImageCallback callback) {
|
||||
executors.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Bitmap result = null;
|
||||
Exception exception = null;
|
||||
if (bitmap != null) {
|
||||
Pair<Integer, Integer> dim = getScaledDimensions(bitmap.getWidth(), bitmap.getHeight(), maxSize);
|
||||
boolean filter = false;
|
||||
if (options != null) {
|
||||
try {
|
||||
JSONObject json = new JSONObject(options);
|
||||
filter = json.optBoolean("filter", false);
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
result = android.graphics.Bitmap.createScaledBitmap(bitmap, dim.first, dim.second, filter);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
final Exception finalException = exception;
|
||||
final Bitmap finalResult = result;
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (finalException != null) {
|
||||
callback.onError(finalException);
|
||||
} else {
|
||||
callback.onSuccess(finalResult);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// public static void clearBoxShadow(View view) {
|
||||
// if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||
// return;
|
||||
|
@ -20,16 +20,15 @@ import android.annotation.TargetApi;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
@ -377,13 +376,12 @@ public class Fetcher extends Worker {
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
Bitmap bitmap = null;
|
||||
InputStream is = null;
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
final TypedValue value = new TypedValue();
|
||||
is = res.openRawResource(resId, value);
|
||||
|
||||
bitmap = BitmapFactory.decodeResourceStream(res, value, is, null, options);
|
||||
final TypedValue value = new TypedValue();
|
||||
is = res.openRawResource(resId, value);
|
||||
bitmap = BitmapFactory.decodeResourceStream(res, value, is, null, options);
|
||||
} catch (Exception e) {
|
||||
/* do nothing.
|
||||
If the exception happened on open, bm will be null.
|
||||
@ -391,8 +389,10 @@ public class Fetcher extends Worker {
|
||||
*/
|
||||
}
|
||||
|
||||
if (bitmap == null && options != null && options.inBitmap != null) {
|
||||
throw new IllegalArgumentException("Problem decoding into existing bitmap");
|
||||
|
||||
if (bitmap == null) {
|
||||
// throw new IllegalArgumentException("Problem decoding into existing bitmap");
|
||||
return null;
|
||||
}
|
||||
|
||||
ExifInterface ei = getExifInterface(is);
|
||||
@ -449,7 +449,7 @@ public class Fetcher extends Worker {
|
||||
/**
|
||||
* Decode and sample down a bitmap from a file to the requested width and height.
|
||||
*
|
||||
* @param filename The full path of the file to decode
|
||||
* @param fileName The full path 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
|
@ -26,9 +26,10 @@ import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageView;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import org.nativescript.widgets.Utils;
|
||||
|
||||
/**
|
||||
* This class wraps up completing some arbitrary long running work when loading a bitmap to an
|
||||
* ImageView. It handles things like using a memory and disk cache, running the work in a background
|
||||
@ -52,6 +53,7 @@ public abstract class Worker {
|
||||
protected boolean mPauseWork = false;
|
||||
protected Resources mResources;
|
||||
protected ContentResolver mResolver;
|
||||
protected Context mContext;
|
||||
private static final int MESSAGE_CLEAR = 0;
|
||||
private static final int MESSAGE_INIT_DISK_CACHE = 1;
|
||||
private static final int MESSAGE_FLUSH = 2;
|
||||
@ -62,6 +64,7 @@ public abstract class Worker {
|
||||
protected Worker(Context context) {
|
||||
mResources = context.getResources();
|
||||
mResolver = context.getContentResolver();
|
||||
mContext = context;
|
||||
// Negative means not initialized.
|
||||
if (debuggable < 0) {
|
||||
try {
|
||||
@ -102,7 +105,7 @@ public abstract class Worker {
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap value = null;
|
||||
Object value = null;
|
||||
String cacheUri = uri;
|
||||
|
||||
if (debuggable > 0) {
|
||||
@ -124,9 +127,14 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "loadImage.addBitmapToCache: " + owner + ", src: " + cacheUri);
|
||||
}
|
||||
mCache.addBitmapToCache(cacheUri, value);
|
||||
mCache.addBitmapToCache(cacheUri, (Bitmap) value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try loading as drawable */
|
||||
if(value == null){
|
||||
value = Utils.getDrawable(uri, mContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
@ -134,7 +142,12 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "Set ImageBitmap on: " + owner + " to: " + uri);
|
||||
}
|
||||
owner.setBitmap(value);
|
||||
if(value instanceof Drawable){
|
||||
owner.setDrawable((Drawable) value);
|
||||
}else {
|
||||
owner.setBitmap((Bitmap) value);
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "OnImageLoadedListener on: " + owner + " to: " + uri);
|
||||
@ -145,6 +158,8 @@ public abstract class Worker {
|
||||
final BitmapWorkerTask task = new BitmapWorkerTask(uri, owner, decodeWidth, decodeHeight, keepAspectRatio, useCache, listener);
|
||||
final AsyncDrawable asyncDrawable =
|
||||
new AsyncDrawable(mResources, mLoadingBitmap, task);
|
||||
|
||||
|
||||
owner.setDrawable(asyncDrawable);
|
||||
|
||||
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
|
||||
@ -277,7 +292,7 @@ public abstract class Worker {
|
||||
/**
|
||||
* The actual AsyncTask that will asynchronously process the image.
|
||||
*/
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, Object> {
|
||||
private int mDecodeWidth;
|
||||
private int mDecodeHeight;
|
||||
private boolean mKeepAspectRatio;
|
||||
@ -306,13 +321,13 @@ public abstract class Worker {
|
||||
* Background processing.
|
||||
*/
|
||||
@Override
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
protected Object doInBackground(Void... params) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + mUri);
|
||||
}
|
||||
|
||||
|
||||
Bitmap bitmap = null;
|
||||
Object bitmap = null;
|
||||
|
||||
// Wait here if work is paused and the task is not cancelled
|
||||
synchronized (mPauseWorkLock) {
|
||||
@ -327,8 +342,7 @@ public abstract class Worker {
|
||||
// another thread and the ImageView that was originally bound to this task is still
|
||||
// bound back to this task and our "exit early" flag is not set, then call the main
|
||||
// process method (as implemented by a subclass)
|
||||
if (bitmap == null && !isCancelled() && getAttachedOwner() != null
|
||||
&& !mExitTasksEarly) {
|
||||
if (!isCancelled() && getAttachedOwner() != null && !mExitTasksEarly) {
|
||||
bitmap = processBitmap(mUri, mDecodeWidth, mDecodeHeight, mKeepAspectRatio, mCacheImage);
|
||||
}
|
||||
|
||||
@ -341,10 +355,15 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "addBitmapToCache: " + imageViewReference.get() + ", src: " + mCacheUri);
|
||||
}
|
||||
mCache.addBitmapToCache(mCacheUri, bitmap);
|
||||
mCache.addBitmapToCache(mCacheUri, (Bitmap) bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try loading as Drawable */
|
||||
if (bitmap == null){
|
||||
bitmap = Utils.getDrawable(mUri, mContext);
|
||||
}
|
||||
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "doInBackground - finished work");
|
||||
}
|
||||
@ -356,7 +375,7 @@ public abstract class Worker {
|
||||
* Once the image is processed, associates it to the imageView
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap value) {
|
||||
protected void onPostExecute(Object value) {
|
||||
boolean success = false;
|
||||
// if cancel was called on this task or the "exit early" flag is set then we're done
|
||||
if (isCancelled() || mExitTasksEarly) {
|
||||
@ -377,7 +396,11 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "Set ImageDrawable on: " + owner + " to: " + mUri);
|
||||
}
|
||||
owner.setBitmap(value);
|
||||
if(value instanceof Drawable){
|
||||
owner.setDrawable((Drawable) value);
|
||||
} else if(value instanceof Bitmap){
|
||||
owner.setBitmap((Bitmap) value);
|
||||
}
|
||||
}
|
||||
|
||||
if (mOnImageLoadedListener != null) {
|
||||
@ -389,7 +412,7 @@ public abstract class Worker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(Bitmap value) {
|
||||
protected void onCancelled(Object value) {
|
||||
super.onCancelled(value);
|
||||
synchronized (mPauseWorkLock) {
|
||||
mPauseWorkLock.notifyAll();
|
Reference in New Issue
Block a user