feat(android): vector drawable support (#9464)

This commit is contained in:
Osei Fortune
2021-08-11 10:37:27 -07:00
committed by GitHub
parent 36900d7c05
commit 490f7dce80
23 changed files with 105 additions and 43 deletions

View File

@ -13,6 +13,7 @@
<Button text="css-playground" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<Button text="visibility-vs-hidden" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<Button text="image-async" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<Button text="vector-image" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
</StackLayout>
</ScrollView>
</StackLayout>

View File

@ -0,0 +1,5 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<StackLayout>
<Image src="res://outline_face_24"/>
</StackLayout>
</Page>

View File

@ -4,20 +4,6 @@
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>DebugSymbolsPath</key>
<string>dSYMs</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>TNSWidgets.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict>
<key>DebugSymbolsPath</key>
<string>dSYMs</string>
@ -35,6 +21,20 @@
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>DebugSymbolsPath</key>
<string>dSYMs</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>TNSWidgets.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>

View File

@ -34,7 +34,7 @@
</data>
<key>Info.plist</key>
<data>
Xj1cG5BPcNRo2kinT2TzpxSuwlc=
WsLLFjQ3sruW6pvEfLRTXorurcg=
</data>
<key>Modules/module.modulemap</key>
<data>

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip

View File

@ -79,6 +79,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;

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;
@ -18,6 +19,7 @@ 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;
@ -31,7 +33,18 @@ 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;

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;
@ -382,7 +381,6 @@ public class Fetcher extends Worker {
try {
final TypedValue value = new TypedValue();
is = res.openRawResource(resId, value);
bitmap = BitmapFactory.decodeResourceStream(res, value, is, null, options);
} catch (Exception e) {
/* do nothing.
@ -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();

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFA500"
android:pathData="M10.25,13c0,0.69 -0.56,1.25 -1.25,1.25S7.75,13.69 7.75,13s0.56,-1.25 1.25,-1.25 1.25,0.56 1.25,1.25zM15,11.75c-0.69,0 -1.25,0.56 -1.25,1.25s0.56,1.25 1.25,1.25 1.25,-0.56 1.25,-1.25 -0.56,-1.25 -1.25,-1.25zM22,12c0,5.52 -4.48,10 -10,10S2,17.52 2,12 6.48,2 12,2s10,4.48 10,10zM10.66,4.12C12.06,6.44 14.6,8 17.5,8c0.46,0 0.91,-0.05 1.34,-0.12C17.44,5.56 14.9,4 12,4c-0.46,0 -0.91,0.05 -1.34,0.12zM4.42,9.47c1.71,-0.97 3.03,-2.55 3.66,-4.44C6.37,6 5.05,7.58 4.42,9.47zM20,12c0,-0.78 -0.12,-1.53 -0.33,-2.24 -0.7,0.15 -1.42,0.24 -2.17,0.24 -3.13,0 -5.92,-1.44 -7.76,-3.69C8.69,8.87 6.6,10.88 4,11.86c0.01,0.04 0,0.09 0,0.14 0,4.41 3.59,8 8,8s8,-3.59 8,-8z"/>
</vector>