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:
Martin Guillon
2021-09-16 10:16:45 +02:00
200 changed files with 199773 additions and 3798 deletions

View File

@ -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()

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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();