summaryrefslogtreecommitdiff
path: root/graphics/java
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/java')
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java35
-rw-r--r--graphics/java/android/graphics/Bitmap.java2
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java3
-rw-r--r--graphics/java/android/graphics/Canvas.java7
-rw-r--r--graphics/java/android/graphics/FontFamily.java16
-rw-r--r--graphics/java/android/graphics/FontListParser.java16
-rw-r--r--graphics/java/android/graphics/Paint.java220
-rw-r--r--graphics/java/android/graphics/PixelFormat.java48
-rw-r--r--graphics/java/android/graphics/Rect.java33
-rw-r--r--graphics/java/android/graphics/Typeface.java651
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/RippleBackground.java137
-rw-r--r--graphics/java/android/graphics/drawable/RippleComponent.java244
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java132
-rw-r--r--graphics/java/android/graphics/drawable/RippleForeground.java387
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java93
-rw-r--r--graphics/java/android/graphics/fonts/FontVariationAxis.java2
-rw-r--r--graphics/java/android/graphics/pdf/PdfEditor.java29
-rw-r--r--graphics/java/android/graphics/pdf/PdfRenderer.java41
19 files changed, 1071 insertions, 1030 deletions
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 1f339f7aaa54..2d8c71794e7d 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -375,7 +375,7 @@ public abstract class BaseCanvas {
}
throwIfHasHwBitmapInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
- paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.getNativeInstance());
}
public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
@@ -387,7 +387,7 @@ public abstract class BaseCanvas {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.mBidiFlags, paint.getNativeInstance());
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
paint);
@@ -395,7 +395,7 @@ public abstract class BaseCanvas {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.mBidiFlags, paint.getNativeInstance());
TemporaryBuffer.recycle(buf);
}
}
@@ -403,7 +403,7 @@ public abstract class BaseCanvas {
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
throwIfHasHwBitmapInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
- paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.getNativeInstance());
}
public void drawText(@NonNull String text, int start, int end, float x, float y,
@@ -413,7 +413,7 @@ public abstract class BaseCanvas {
}
throwIfHasHwBitmapInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
- paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.getNativeInstance());
}
public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
@@ -424,7 +424,7 @@ public abstract class BaseCanvas {
throwIfHasHwBitmapInSwMode(paint);
nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
path.readOnlyNI(), hOffset, vOffset,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.mBidiFlags, paint.getNativeInstance());
}
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
@@ -432,7 +432,7 @@ public abstract class BaseCanvas {
if (text.length() > 0) {
throwIfHasHwBitmapInSwMode(paint);
nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ paint.mBidiFlags, paint.getNativeInstance());
}
}
@@ -453,7 +453,7 @@ public abstract class BaseCanvas {
throwIfHasHwBitmapInSwMode(paint);
nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
- x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ x, y, isRtl, paint.getNativeInstance());
}
public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
@@ -474,7 +474,7 @@ public abstract class BaseCanvas {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
- contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ contextEnd, x, y, isRtl, paint.getNativeInstance());
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawTextRun(this, start, end,
contextStart, contextEnd, x, y, isRtl, paint);
@@ -484,7 +484,7 @@ public abstract class BaseCanvas {
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
- 0, contextLen, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ 0, contextLen, x, y, isRtl, paint.getNativeInstance());
TemporaryBuffer.recycle(buf);
}
}
@@ -614,23 +614,20 @@ public abstract class BaseCanvas {
short[] indices, int indexOffset, int indexCount, long nativePaint);
private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
- float x, float y, int flags, long nativePaint, long nativeTypeface);
+ float x, float y, int flags, long nativePaint);
private static native void nDrawText(long nativeCanvas, String text, int start, int end,
- float x, float y, int flags, long nativePaint, long nativeTypeface);
+ float x, float y, int flags, long nativePaint);
private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end,
- int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint,
- long nativeTypeface);
+ int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint);
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
- int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
- long nativeTypeface);
+ int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint);
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
- long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint,
- long nativeTypeface);
+ long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
- float hOffset, float vOffset, int flags, long nativePaint, long nativeTypeface);
+ float hOffset, float vOffset, int flags, long nativePaint);
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 57c75490ec47..0072012f69ad 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,6 +21,7 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.WorkerThread;
import android.content.res.ResourcesImpl;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1233,6 +1234,7 @@ public final class Bitmap implements Parcelable {
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
+ @WorkerThread
public boolean compress(CompressFormat format, int quality, OutputStream stream) {
checkRecycled("Can't compress a recycled bitmap");
// do explicit check before calling the native method
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index ffb39e339119..f5bf754ae6ef 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -354,6 +354,7 @@ public class BitmapFactory {
* decode, in the case of which a more accurate, but slightly slower,
* IDCT method will be used instead.
*/
+ @Deprecated
public boolean inPreferQualityOverSpeed;
/**
@@ -412,6 +413,7 @@ public class BitmapFactory {
* can check, inbetween the bounds decode and the image decode, to see
* if the operation is canceled.
*/
+ @Deprecated
public boolean mCancel;
/**
@@ -426,6 +428,7 @@ public class BitmapFactory {
* or if inJustDecodeBounds is true, will set outWidth/outHeight
* to -1
*/
+ @Deprecated
public void requestCancelDecode() {
mCancel = true;
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 0301f2e6b555..f5e863305766 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -200,11 +200,6 @@ public class Canvas extends BaseCanvas {
}
/** @hide */
- public void setHighContrastText(boolean highContrastText) {
- nSetHighContrastText(mNativeCanvasWrapper, highContrastText);
- }
-
- /** @hide */
public void insertReorderBarrier() {}
/** @hide */
@@ -1242,8 +1237,6 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nIsOpaque(long canvasHandle);
@CriticalNative
- private static native void nSetHighContrastText(long renderer, boolean highContrastText);
- @CriticalNative
private static native int nGetWidth(long canvasHandle);
@CriticalNative
private static native int nGetHeight(long canvasHandle);
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index d9a77e752823..d77e6012fb46 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -16,9 +16,11 @@
package android.graphics;
+import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
import android.text.FontConfig;
+import android.text.TextUtils;
import android.util.Log;
import dalvik.annotation.optimization.CriticalNative;
@@ -48,8 +50,16 @@ public class FontFamily {
mBuilderPtr = nInitBuilder(null, 0);
}
- public FontFamily(String lang, int variant) {
- mBuilderPtr = nInitBuilder(lang, variant);
+ public FontFamily(@Nullable String[] langs, int variant) {
+ final String langsString;
+ if (langs == null || langs.length == 0) {
+ langsString = null;
+ } else if (langs.length == 1) {
+ langsString = langs[0];
+ } else {
+ langsString = TextUtils.join(",", langs);
+ }
+ mBuilderPtr = nInitBuilder(langsString, variant);
}
/**
@@ -174,7 +184,7 @@ public class FontFamily {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
}
- private static native long nInitBuilder(String lang, int variant);
+ private static native long nInitBuilder(String langs, int variant);
@CriticalNative
private static native long nCreateFamily(long mBuilderPtr);
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 7c07a302dfe9..9f672e30e05b 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -74,13 +74,14 @@ public class FontListParser {
private static FontConfig.Family readFamily(XmlPullParser parser)
throws XmlPullParserException, IOException {
- String name = parser.getAttributeValue(null, "name");
- String lang = parser.getAttributeValue(null, "lang");
- String variant = parser.getAttributeValue(null, "variant");
- List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
+ final String name = parser.getAttributeValue(null, "name");
+ final String lang = parser.getAttributeValue(null, "lang");
+ final String[] langs = lang == null ? null : lang.split("\\s+");
+ final String variant = parser.getAttributeValue(null, "variant");
+ final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
- String tag = parser.getName();
+ final String tag = parser.getName();
if (tag.equals("font")) {
fonts.add(readFont(parser));
} else {
@@ -95,7 +96,7 @@ public class FontListParser {
intVariant = FontConfig.Family.VARIANT_ELEGANT;
}
}
- return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang,
+ return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), langs,
intVariant);
}
@@ -111,6 +112,7 @@ public class FontListParser {
String weightStr = parser.getAttributeValue(null, "weight");
int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
+ String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
StringBuilder filename = new StringBuilder();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -126,7 +128,7 @@ public class FontListParser {
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
return new FontConfig.Font(sanitizedName, index,
- axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic);
+ axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
}
private static FontVariationAxis readAxis(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index aa9227c9bb08..317144a21d32 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -60,11 +60,6 @@ public class Paint {
Paint.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_PAINT_SIZE);
}
- /**
- * @hide
- */
- public long mNativeTypeface;
-
private ColorFilter mColorFilter;
private MaskFilter mMaskFilter;
private PathEffect mPathEffect;
@@ -93,7 +88,7 @@ public class Paint {
* A map from a string representation of the LocaleList to Minikin's language list ID.
*/
@GuardedBy("sCacheLock")
- private static final HashMap<String, Integer> sMinikinLangListIdCache = new HashMap<>();
+ private static final HashMap<String, Integer> sMinikinLocaleListIdCache = new HashMap<>();
/**
* @hide
@@ -523,7 +518,6 @@ public class Paint {
mShader = null;
mNativeShader = 0;
mTypeface = null;
- mNativeTypeface = 0;
mXfermode = null;
mHasCompatScaling = false;
@@ -566,7 +560,6 @@ public class Paint {
mShader = paint.mShader;
mNativeShader = paint.mNativeShader;
mTypeface = paint.mTypeface;
- mNativeTypeface = paint.mNativeTypeface;
mXfermode = paint.mXfermode;
mHasCompatScaling = paint.mHasCompatScaling;
@@ -822,14 +815,14 @@ public class Paint {
* @hide
*/
public float getUnderlinePosition() {
- return nGetUnderlinePosition(mNativePaint, mNativeTypeface);
+ return nGetUnderlinePosition(mNativePaint);
}
/**
* @hide
*/
public float getUnderlineThickness() {
- return nGetUnderlineThickness(mNativePaint, mNativeTypeface);
+ return nGetUnderlineThickness(mNativePaint);
}
/**
@@ -858,14 +851,14 @@ public class Paint {
* @hide
*/
public float getStrikeThruPosition() {
- return nGetStrikeThruPosition(mNativePaint, mNativeTypeface);
+ return nGetStrikeThruPosition(mNativePaint);
}
/**
* @hide
*/
public float getStrikeThruThickness() {
- return nGetStrikeThruThickness(mNativePaint, mNativeTypeface);
+ return nGetStrikeThruThickness(mNativePaint);
}
/**
@@ -1274,13 +1267,9 @@ public class Paint {
* @return typeface
*/
public Typeface setTypeface(Typeface typeface) {
- long typefaceNative = 0;
- if (typeface != null) {
- typefaceNative = typeface.native_instance;
- }
+ final long typefaceNative = typeface == null ? 0 : typeface.native_instance;
nSetTypeface(mNativePaint, typefaceNative);
mTypeface = typeface;
- mNativeTypeface = typefaceNative;
return typeface;
}
@@ -1456,16 +1445,16 @@ public class Paint {
private void syncTextLocalesWithMinikin() {
final String languageTags = mLocales.toLanguageTags();
- final Integer minikinLangListId;
+ final Integer minikinLocaleListId;
synchronized (sCacheLock) {
- minikinLangListId = sMinikinLangListIdCache.get(languageTags);
- if (minikinLangListId == null) {
+ minikinLocaleListId = sMinikinLocaleListIdCache.get(languageTags);
+ if (minikinLocaleListId == null) {
final int newID = nSetTextLocales(mNativePaint, languageTags);
- sMinikinLangListIdCache.put(languageTags, newID);
+ sMinikinLocaleListIdCache.put(languageTags, newID);
return;
}
}
- nSetTextLocalesByMinikinLangListId(mNativePaint, minikinLangListId.intValue());
+ nSetTextLocalesByMinikinLocaleListId(mNativePaint, minikinLocaleListId.intValue());
}
/**
@@ -1740,22 +1729,28 @@ public class Paint {
* Return the distance above (negative) the baseline (ascent) based on the
* current typeface and text size.
*
+ * <p>Note that this is the ascent of the main typeface, and actual text rendered may need a
+ * larger ascent because fallback fonts may get used in rendering the text.
+ *
* @return the distance above (negative) the baseline (ascent) based on the
* current typeface and text size.
*/
public float ascent() {
- return nAscent(mNativePaint, mNativeTypeface);
+ return nAscent(mNativePaint);
}
/**
* Return the distance below (positive) the baseline (descent) based on the
* current typeface and text size.
*
+ * <p>Note that this is the descent of the main typeface, and actual text rendered may need a
+ * larger descent because fallback fonts may get used in rendering the text.
+ *
* @return the distance below (positive) the baseline (descent) based on
* the current typeface and text size.
*/
public float descent() {
- return nDescent(mNativePaint, mNativeTypeface);
+ return nDescent(mNativePaint);
}
/**
@@ -1794,12 +1789,15 @@ public class Paint {
* settings for typeface, textSize, etc. If metrics is not null, return the
* fontmetric values in it.
*
+ * <p>Note that these are the values for the main typeface, and actual text rendered may need a
+ * larger set of values because fallback fonts may get used in rendering the text.
+ *
* @param metrics If this object is not null, its fields are filled with
* the appropriate values given the paint's text attributes.
* @return the font's recommended interline spacing.
*/
public float getFontMetrics(FontMetrics metrics) {
- return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
+ return nGetFontMetrics(mNativePaint, metrics);
}
/**
@@ -1855,10 +1853,13 @@ public class Paint {
* and clipping. If you want more control over the rounding, call
* getFontMetrics().
*
+ * <p>Note that these are the values for the main typeface, and actual text rendered may need a
+ * larger set of values because fallback fonts may get used in rendering the text.
+ *
* @return the font's interline spacing.
*/
public int getFontMetricsInt(FontMetricsInt fmi) {
- return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
+ return nGetFontMetricsInt(mNativePaint, fmi);
}
public FontMetricsInt getFontMetricsInt() {
@@ -1871,6 +1872,9 @@ public class Paint {
* Return the recommend line spacing based on the current typeface and
* text size.
*
+ * <p>Note that this is the value for the main typeface, and actual text rendered may need a
+ * larger value because fallback fonts may get used in rendering the text.
+ *
* @return recommend line spacing based on the current typeface and
* text size.
*/
@@ -1898,14 +1902,14 @@ public class Paint {
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
+ return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
index, count, index, count, mBidiFlags, null, 0));
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
- count, mBidiFlags, null, 0);
+ final float w = nGetTextAdvances(mNativePaint, text, index, count, index, count,
+ mBidiFlags, null, 0);
setTextSize(oldSize);
return (float) Math.ceil(w*mInvCompatScaling);
}
@@ -1930,13 +1934,13 @@ public class Paint {
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
+ return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
start, end, start, end, mBidiFlags, null, 0));
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
- end, mBidiFlags, null, 0);
+ final float w = nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags,
+ null, 0);
setTextSize(oldSize);
return (float) Math.ceil(w * mInvCompatScaling);
}
@@ -2019,14 +2023,14 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
- mBidiFlags, measuredWidth);
+ return nBreakText(mNativePaint, text, index, count, maxWidth, mBidiFlags,
+ measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count,
- maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
+ final int res = nBreakText(mNativePaint, text, index, count, maxWidth * mCompatScaling,
+ mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
@@ -2107,14 +2111,14 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ return nBreakText(mNativePaint, text, measureForwards,
maxWidth, mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
- maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
+ final int res = nBreakText(mNativePaint, text, measureForwards, maxWidth*mCompatScaling,
+ mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
@@ -2144,15 +2148,13 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
- mBidiFlags, widths, 0);
+ nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths, 0);
return count;
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
- mBidiFlags, widths, 0);
+ nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths, 0);
setTextSize(oldSize);
for (int i = 0; i < count; i++) {
widths[i] *= mInvCompatScaling;
@@ -2229,15 +2231,13 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
- mBidiFlags, widths, 0);
+ nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
return end - start;
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
- mBidiFlags, widths, 0);
+ nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
setTextSize(oldSize);
for (int i = 0; i < end - start; i++) {
widths[i] *= mInvCompatScaling;
@@ -2284,16 +2284,15 @@ public class Paint {
return 0f;
}
if (!mHasCompatScaling) {
- return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
- contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+ return nGetTextAdvances(mNativePaint, chars, index, count, contextIndex, contextCount,
+ isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
- contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
- advancesIndex);
+ final float res = nGetTextAdvances(mNativePaint, chars, index, count, contextIndex,
+ contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -2411,16 +2410,14 @@ public class Paint {
}
if (!mHasCompatScaling) {
- return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
- advancesIndex);
+ return nGetTextAdvances(mNativePaint, text, start, end, contextStart, contextEnd,
+ isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start,
- end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
- advancesIndex);
+ final float totalAdvance = nGetTextAdvances(mNativePaint, text, start, end, contextStart,
+ contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -2467,8 +2464,8 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
- contextStart, contextLength, dir, offset, cursorOpt);
+ return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength, dir, offset,
+ cursorOpt);
}
/**
@@ -2553,8 +2550,8 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
- contextStart, contextEnd, dir, offset, cursorOpt);
+ return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd, dir, offset,
+ cursorOpt);
}
/**
@@ -2574,8 +2571,7 @@ public class Paint {
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
- path.mutateNI());
+ nGetTextPath(mNativePaint, mBidiFlags, text, index, count, x, y, path.mutateNI());
}
/**
@@ -2595,8 +2591,7 @@ public class Paint {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
- path.mutateNI());
+ nGetTextPath(mNativePaint, mBidiFlags, text, start, end, x, y, path.mutateNI());
}
/**
@@ -2615,7 +2610,7 @@ public class Paint {
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
+ nGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds);
}
/**
@@ -2657,7 +2652,7 @@ public class Paint {
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
+ nGetCharArrayBounds(mNativePaint, text, index, count, mBidiFlags,
bounds);
}
@@ -2678,7 +2673,7 @@ public class Paint {
* @return true if the typeface has a glyph for the string
*/
public boolean hasGlyph(String string) {
- return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
+ return nHasGlyph(mNativePaint, mBidiFlags, string);
}
/**
@@ -2731,8 +2726,8 @@ public class Paint {
return 0.0f;
}
// TODO: take mCompatScaling into account (or eliminate compat scaling)?
- return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, isRtl, offset);
+ return nGetRunAdvance(mNativePaint, text, start, end, contextStart, contextEnd, isRtl,
+ offset);
}
/**
@@ -2747,7 +2742,7 @@ public class Paint {
* @param offset index of caret position
* @return width measurement between start and offset
*/
- public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
+ public float getRunAdvance(@NonNull CharSequence text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, int offset) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
@@ -2808,8 +2803,8 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
// TODO: take mCompatScaling into account (or eliminate compat scaling)?
- return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, isRtl, advance);
+ return nGetOffsetForAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
+ isRtl, advance);
}
/**
@@ -2847,38 +2842,31 @@ public class Paint {
private static native long nGetNativeFinalizer();
private static native long nInit();
private static native long nInitWithPaint(long paint);
- private static native int nBreakText(long nObject, long nTypeface,
- char[] text, int index, int count,
+ private static native int nBreakText(long nObject, char[] text, int index, int count,
float maxWidth, int bidiFlags, float[] measuredWidth);
- private static native int nBreakText(long nObject, long nTypeface,
- String text, boolean measureForwards,
+ private static native int nBreakText(long nObject, String text, boolean measureForwards,
float maxWidth, int bidiFlags, float[] measuredWidth);
- private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
- char[] text, int index, int count, int contextIndex, int contextCount,
- int bidiFlags, float[] advances, int advancesIndex);
- private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
- String text, int start, int end, int contextStart, int contextEnd,
- int bidiFlags, float[] advances, int advancesIndex);
- private native int nGetTextRunCursor(long paintPtr, long typefacePtr, char[] text,
- int contextStart, int contextLength, int dir, int offset, int cursorOpt);
- private native int nGetTextRunCursor(long paintPtr, long typefacePtr, String text,
- int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
- private static native void nGetTextPath(long paintPtr, long typefacePtr,
- int bidiFlags, char[] text, int index, int count, float x, float y, long path);
- private static native void nGetTextPath(long paintPtr, long typefacePtr,
- int bidiFlags, String text, int start, int end, float x, float y, long path);
- private static native void nGetStringBounds(long nativePaint, long typefacePtr,
- String text, int start, int end, int bidiFlags, Rect bounds);
- private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
- char[] text, int index, int count, int bidiFlags, Rect bounds);
- private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
- int bidiFlags, String string);
- private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
- char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
- int offset);
- private static native int nGetOffsetForAdvance(long paintPtr,
- long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
- boolean isRtl, float advance);
+ private static native float nGetTextAdvances(long paintPtr, char[] text, int index, int count,
+ int contextIndex, int contextCount, int bidiFlags, float[] advances, int advancesIndex);
+ private static native float nGetTextAdvances(long paintPtr, String text, int start, int end,
+ int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex);
+ private native int nGetTextRunCursor(long paintPtr, char[] text, int contextStart,
+ int contextLength, int dir, int offset, int cursorOpt);
+ private native int nGetTextRunCursor(long paintPtr, String text, int contextStart,
+ int contextEnd, int dir, int offset, int cursorOpt);
+ private static native void nGetTextPath(long paintPtr, int bidiFlags, char[] text, int index,
+ int count, float x, float y, long path);
+ private static native void nGetTextPath(long paintPtr, int bidiFlags, String text, int start,
+ int end, float x, float y, long path);
+ private static native void nGetStringBounds(long nativePaint, String text, int start, int end,
+ int bidiFlags, Rect bounds);
+ private static native void nGetCharArrayBounds(long nativePaint, char[] text, int index,
+ int count, int bidiFlags, Rect bounds);
+ private static native boolean nHasGlyph(long paintPtr, int bidiFlags, String string);
+ private static native float nGetRunAdvance(long paintPtr, char[] text, int start, int end,
+ int contextStart, int contextEnd, boolean isRtl, int offset);
+ private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
+ int contextStart, int contextEnd, boolean isRtl, float advance);
// ---------------- @FastNative ------------------------
@@ -2888,11 +2876,9 @@ public class Paint {
@FastNative
private static native void nSetFontFeatureSettings(long paintPtr, String settings);
@FastNative
- private static native float nGetFontMetrics(long paintPtr,
- long typefacePtr, FontMetrics metrics);
+ private static native float nGetFontMetrics(long paintPtr, FontMetrics metrics);
@FastNative
- private static native int nGetFontMetricsInt(long paintPtr,
- long typefacePtr, FontMetricsInt fmi);
+ private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
// ---------------- @CriticalNative ------------------------
@@ -2926,14 +2912,14 @@ public class Paint {
@CriticalNative
private static native long nSetMaskFilter(long paintPtr, long maskfilter);
@CriticalNative
- private static native long nSetTypeface(long paintPtr, long typeface);
+ private static native void nSetTypeface(long paintPtr, long typeface);
@CriticalNative
private static native int nGetTextAlign(long paintPtr);
@CriticalNative
private static native void nSetTextAlign(long paintPtr, int align);
@CriticalNative
- private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
- int mMinikinLangListId);
+ private static native void nSetTextLocalesByMinikinLocaleListId(long paintPtr,
+ int mMinikinLocaleListId);
@CriticalNative
private static native void nSetShadowLayer(long paintPtr,
float radius, float dx, float dy, int color);
@@ -3006,17 +2992,17 @@ public class Paint {
@CriticalNative
private static native void nSetTextSkewX(long paintPtr, float skewX);
@CriticalNative
- private static native float nAscent(long paintPtr, long typefacePtr);
+ private static native float nAscent(long paintPtr);
@CriticalNative
- private static native float nDescent(long paintPtr, long typefacePtr);
+ private static native float nDescent(long paintPtr);
@CriticalNative
- private static native float nGetUnderlinePosition(long paintPtr, long typefacePtr);
+ private static native float nGetUnderlinePosition(long paintPtr);
@CriticalNative
- private static native float nGetUnderlineThickness(long paintPtr, long typefacePtr);
+ private static native float nGetUnderlineThickness(long paintPtr);
@CriticalNative
- private static native float nGetStrikeThruPosition(long paintPtr, long typefacePtr);
+ private static native float nGetStrikeThruPosition(long paintPtr);
@CriticalNative
- private static native float nGetStrikeThruThickness(long paintPtr, long typefacePtr);
+ private static native float nGetStrikeThruThickness(long paintPtr);
@CriticalNative
private static native void nSetTextSize(long paintPtr, float textSize);
}
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index f93886dcb06b..96d6eeece0a3 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -185,4 +185,52 @@ public class PixelFormat {
return false;
}
+
+ /**
+ * @hide
+ */
+ public static String formatToString(@Format int format) {
+ switch (format) {
+ case UNKNOWN:
+ return "UNKNOWN";
+ case TRANSLUCENT:
+ return "TRANSLUCENT";
+ case TRANSPARENT:
+ return "TRANSPARENT";
+ case RGBA_8888:
+ return "RGBA_8888";
+ case RGBX_8888:
+ return "RGBX_8888";
+ case RGB_888:
+ return "RGB_888";
+ case RGB_565:
+ return "RGB_565";
+ case RGBA_5551:
+ return "RGBA_5551";
+ case RGBA_4444:
+ return "RGBA_4444";
+ case A_8:
+ return "A_8";
+ case L_8:
+ return "L_8";
+ case LA_88:
+ return "LA_88";
+ case RGB_332:
+ return "RGB_332";
+ case YCbCr_422_SP:
+ return "YCbCr_422_SP";
+ case YCbCr_420_SP:
+ return "YCbCr_420_SP";
+ case YCbCr_422_I:
+ return "YCbCr_422_I";
+ case RGBA_F16:
+ return "RGBA_F16";
+ case RGBA_1010102:
+ return "RGBA_1010102";
+ case JPEG:
+ return "JPEG";
+ default:
+ return Integer.toString(format);
+ }
+ }
}
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index deafb6638ece..aff942da78d1 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -194,7 +195,24 @@ public final class Rect implements Parcelable {
pw.print(top); pw.print("]["); pw.print(right);
pw.print(','); pw.print(bottom); pw.print(']');
}
-
+
+ /**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.graphics.RectProto}
+ *
+ * @param protoOutputStream Stream to write the Rect object to.
+ * @param fieldId Field Id of the Rect as defined in the parent message
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(RectProto.LEFT, left);
+ protoOutputStream.write(RectProto.TOP, top);
+ protoOutputStream.write(RectProto.RIGHT, right);
+ protoOutputStream.write(RectProto.BOTTOM, bottom);
+ protoOutputStream.end(token);
+ }
+
/**
* Returns true if the rectangle is empty (left >= right or top >= bottom)
*/
@@ -457,6 +475,19 @@ public final class Rect implements Parcelable {
}
/**
+ * If the specified rectangle intersects this rectangle, set this rectangle to that
+ * intersection, otherwise set this rectangle to the empty rectangle.
+ * @see #inset(int, int, int, int) but without checking if the rects overlap.
+ * @hide
+ */
+ public void intersectUnchecked(Rect other) {
+ left = Math.max(left, other.left);
+ top = Math.max(top, other.top);
+ right = Math.min(right, other.right);
+ bottom = Math.min(bottom, other.bottom);
+ }
+
+ /**
* If rectangles a and b intersect, return true and set this rectangle to
* that intersection, otherwise return false and do not change this
* rectangle. No check is performed to see if either rectangle is empty.
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c4b56c333c64..3d65bd226faf 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -38,6 +38,7 @@ import android.os.ResultReceiver;
import android.provider.FontRequest;
import android.provider.FontsContract;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
import android.util.LongSparseArray;
@@ -45,6 +46,7 @@ import android.util.LruCache;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
@@ -95,21 +97,33 @@ public class Typeface {
public static final Typeface MONOSPACE;
static Typeface[] sDefaults;
- private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
+
+ /**
+ * Cache for Typeface objects for style variant. Currently max size is 3.
+ */
+ @GuardedBy("sStyledCacheLock")
+ private static final LongSparseArray<SparseArray<Typeface>> sStyledTypefaceCache =
+ new LongSparseArray<>(3);
+ private static final Object sStyledCacheLock = new Object();
+
+ /**
+ * Cache for Typeface objects for weight variant. Currently max size is 3.
+ */
+ @GuardedBy("sWeightCacheLock")
+ private static final LongSparseArray<SparseArray<Typeface>> sWeightTypefaceCache =
new LongSparseArray<>(3);
+ private static final Object sWeightCacheLock = new Object();
/**
* Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
*/
- @GuardedBy("sLock")
+ @GuardedBy("sDynamicCacheLock")
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
+ private static final Object sDynamicCacheLock = new Object();
static Typeface sDefaultTypeface;
- static Map<String, Typeface> sSystemFontMap;
- static FontFamily[] sFallbackFonts;
- private static final Object sLock = new Object();
-
- static final String FONTS_CONFIG = "fonts.xml";
+ static final Map<String, Typeface> sSystemFontMap;
+ static final Map<String, FontFamily[]> sSystemFallbackMap;
/**
* @hide
@@ -121,6 +135,7 @@ public class Typeface {
public static final int BOLD = 1;
public static final int ITALIC = 2;
public static final int BOLD_ITALIC = 3;
+ /** @hide */ public static final int STYLE_MASK = 0x03;
private int mStyle = 0;
private int mWeight = 0;
@@ -129,6 +144,7 @@ public class Typeface {
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
/** @hide */
public static final int RESOLVE_BY_FONT_TABLE = -1;
+ private static final String DEFAULT_FAMILY = "sans-serif";
// Style value for building typeface.
private static final int STYLE_NORMAL = 0;
@@ -142,6 +158,13 @@ public class Typeface {
nativeSetDefault(t.native_instance);
}
+ // TODO: Make this public API. (b/64852739)
+ /** @hide */
+ @VisibleForTesting
+ public int getWeight() {
+ return mWeight;
+ }
+
/** Returns the typeface's intrinsic style attributes */
public int getStyle() {
return mStyle;
@@ -163,28 +186,27 @@ public class Typeface {
*/
@Nullable
public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
- if (sFallbackFonts != null) {
- synchronized (sDynamicTypefaceCache) {
- final String key = Builder.createAssetUid(
- mgr, path, 0 /* ttcIndex */, null /* axes */,
- RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
- Typeface typeface = sDynamicTypefaceCache.get(key);
- if (typeface != null) return typeface;
-
- FontFamily fontFamily = new FontFamily();
- // TODO: introduce ttc index and variation settings to resource type font.
- if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
- 0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
- RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
- if (!fontFamily.freeze()) {
- return null;
- }
- FontFamily[] families = {fontFamily};
- typeface = createFromFamiliesWithDefault(families,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
- sDynamicTypefaceCache.put(key, typeface);
- return typeface;
+ synchronized (sDynamicCacheLock) {
+ final String key = Builder.createAssetUid(
+ mgr, path, 0 /* ttcIndex */, null /* axes */,
+ RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
+ DEFAULT_FAMILY);
+ Typeface typeface = sDynamicTypefaceCache.get(key);
+ if (typeface != null) return typeface;
+
+ FontFamily fontFamily = new FontFamily();
+ // TODO: introduce ttc index and variation settings to resource type font.
+ if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
+ 0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
+ RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
+ if (!fontFamily.freeze()) {
+ return null;
}
+ FontFamily[] families = {fontFamily};
+ typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+ sDynamicTypefaceCache.put(key, typeface);
+ return typeface;
}
}
return null;
@@ -197,61 +219,57 @@ public class Typeface {
@Nullable
public static Typeface createFromResources(
FamilyResourceEntry entry, AssetManager mgr, String path) {
- if (sFallbackFonts != null) {
- if (entry instanceof ProviderResourceEntry) {
- final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
- // Downloadable font
- List<List<String>> givenCerts = providerEntry.getCerts();
- List<List<byte[]>> certs = new ArrayList<>();
- if (givenCerts != null) {
- for (int i = 0; i < givenCerts.size(); i++) {
- List<String> certSet = givenCerts.get(i);
- List<byte[]> byteArraySet = new ArrayList<>();
- for (int j = 0; j < certSet.size(); j++) {
- byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
- }
- certs.add(byteArraySet);
+ if (entry instanceof ProviderResourceEntry) {
+ final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
+ // Downloadable font
+ List<List<String>> givenCerts = providerEntry.getCerts();
+ List<List<byte[]>> certs = new ArrayList<>();
+ if (givenCerts != null) {
+ for (int i = 0; i < givenCerts.size(); i++) {
+ List<String> certSet = givenCerts.get(i);
+ List<byte[]> byteArraySet = new ArrayList<>();
+ for (int j = 0; j < certSet.size(); j++) {
+ byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
}
+ certs.add(byteArraySet);
}
- // Downloaded font and it wasn't cached, request it again and return a
- // default font instead (nothing we can do now).
- FontRequest request = new FontRequest(providerEntry.getAuthority(),
- providerEntry.getPackage(), providerEntry.getQuery(), certs);
- Typeface typeface = FontsContract.getFontSync(request);
- return typeface == null ? DEFAULT : typeface;
}
+ // Downloaded font and it wasn't cached, request it again and return a
+ // default font instead (nothing we can do now).
+ FontRequest request = new FontRequest(providerEntry.getAuthority(),
+ providerEntry.getPackage(), providerEntry.getQuery(), certs);
+ Typeface typeface = FontsContract.getFontSync(request);
+ return typeface == null ? DEFAULT : typeface;
+ }
- Typeface typeface = findFromCache(mgr, path);
- if (typeface != null) return typeface;
+ Typeface typeface = findFromCache(mgr, path);
+ if (typeface != null) return typeface;
- // family is FontFamilyFilesResourceEntry
- final FontFamilyFilesResourceEntry filesEntry =
- (FontFamilyFilesResourceEntry) entry;
+ // family is FontFamilyFilesResourceEntry
+ final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry;
- FontFamily fontFamily = new FontFamily();
- for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
- // TODO: Add ttc and variation font support. (b/37853920)
- if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
- 0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
- fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
- return null;
- }
- }
- if (!fontFamily.freeze()) {
+ FontFamily fontFamily = new FontFamily();
+ for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
+ if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
+ 0 /* resourceCookie */, false /* isAsset */, fontFile.getTtcIndex(),
+ fontFile.getWeight(), fontFile.getItalic(),
+ FontVariationAxis.fromFontVariationSettings(fontFile.getVariationSettings()))) {
return null;
}
- FontFamily[] familyChain = { fontFamily };
- typeface = createFromFamiliesWithDefault(familyChain,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
- synchronized (sDynamicTypefaceCache) {
- final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
- null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
- RESOLVE_BY_FONT_TABLE /* italic */);
- sDynamicTypefaceCache.put(key, typeface);
- }
- return typeface;
}
- return null;
+ if (!fontFamily.freeze()) {
+ return null;
+ }
+ FontFamily[] familyChain = { fontFamily };
+ typeface = createFromFamiliesWithDefault(familyChain, DEFAULT_FAMILY,
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+ synchronized (sDynamicCacheLock) {
+ final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
+ null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
+ RESOLVE_BY_FONT_TABLE /* italic */, DEFAULT_FAMILY);
+ sDynamicTypefaceCache.put(key, typeface);
+ }
+ return typeface;
}
/**
@@ -259,9 +277,10 @@ public class Typeface {
* @hide
*/
public static Typeface findFromCache(AssetManager mgr, String path) {
- synchronized (sDynamicTypefaceCache) {
+ synchronized (sDynamicCacheLock) {
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */,
- RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
+ RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
+ DEFAULT_FAMILY);
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) {
return typeface;
@@ -498,7 +517,7 @@ public class Typeface {
* @return Unique id for a given AssetManager and asset path.
*/
private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
- @Nullable FontVariationAxis[] axes, int weight, int italic) {
+ @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) {
final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
final StringBuilder builder = new StringBuilder();
final int size = pkgs.size();
@@ -513,7 +532,11 @@ public class Typeface {
builder.append(Integer.toString(weight));
builder.append("-");
builder.append(Integer.toString(italic));
- builder.append("-");
+ // Family name may contain hyphen. Use double hyphen for avoiding key conflicts before
+ // and after appending falblack name.
+ builder.append("--");
+ builder.append(fallback);
+ builder.append("--");
if (axes != null) {
for (FontVariationAxis axis : axes) {
builder.append(axis.getTag());
@@ -524,12 +547,6 @@ public class Typeface {
return builder.toString();
}
- private static final Object sLock = new Object();
- // TODO: Unify with Typeface.sTypefaceCache.
- @GuardedBy("sLock")
- private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
- new LongSparseArray<>(3);
-
private Typeface resolveFallbackTypeface() {
if (mFallbackFamilyName == null) {
return null;
@@ -547,29 +564,7 @@ public class Typeface {
final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight;
final boolean italic =
(mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
- final int key = weight << 1 | (italic ? 1 : 0);
-
- Typeface typeface;
- synchronized(sLock) {
- SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
- if (innerCache != null) {
- typeface = innerCache.get(key);
- if (typeface != null) {
- return typeface;
- }
- }
-
- typeface = new Typeface(
- nativeCreateFromTypefaceWithExactStyle(
- base.native_instance, weight, italic));
-
- if (innerCache == null) {
- innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
- sTypefaceCache.put(base.native_instance, innerCache);
- }
- innerCache.put(key, typeface);
- }
- return typeface;
+ return createWeightStyle(base, weight, italic);
}
/**
@@ -593,14 +588,16 @@ public class Typeface {
return resolveFallbackTypeface();
}
FontFamily[] families = { fontFamily };
- return createFromFamiliesWithDefault(families, mWeight, mItalic);
+ return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
+ mItalic);
} catch (IOException e) {
return resolveFallbackTypeface();
}
} else if (mAssetManager != null) { // Builder is created with asset manager.
final String key = createAssetUid(
- mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic);
- synchronized (sLock) {
+ mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic,
+ mFallbackFamilyName);
+ synchronized (sDynamicCacheLock) {
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
final FontFamily fontFamily = new FontFamily();
@@ -613,7 +610,8 @@ public class Typeface {
return resolveFallbackTypeface();
}
FontFamily[] families = { fontFamily };
- typeface = createFromFamiliesWithDefault(families, mWeight, mItalic);
+ typeface = createFromFamiliesWithDefault(families, mFallbackFamilyName,
+ mWeight, mItalic);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
}
@@ -627,7 +625,8 @@ public class Typeface {
return resolveFallbackTypeface();
}
FontFamily[] families = { fontFamily };
- return createFromFamiliesWithDefault(families, mWeight, mItalic);
+ return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
+ mItalic);
} else if (mFonts != null) {
final FontFamily fontFamily = new FontFamily();
boolean atLeastOneFont = false;
@@ -653,7 +652,8 @@ public class Typeface {
}
fontFamily.freeze();
FontFamily[] families = { fontFamily };
- return createFromFamiliesWithDefault(families, mWeight, mItalic);
+ return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
+ mItalic);
}
// Must not reach here.
@@ -673,10 +673,7 @@ public class Typeface {
* @return The best matching typeface.
*/
public static Typeface create(String familyName, int style) {
- if (sSystemFontMap != null) {
- return create(sSystemFontMap.get(familyName), style);
- }
- return null;
+ return create(sSystemFontMap.get(familyName), style);
}
/**
@@ -685,42 +682,98 @@ public class Typeface {
* style from the same family of an existing typeface object. If family is
* null, this selects from the default font's family.
*
- * @param family May be null. The name of the existing type face.
+ * <p>
+ * This method is not thread safe on API 27 or before.
+ * This method is thread safe on API 28 or after.
+ * </p>
+ *
+ * @param family An existing {@link Typeface} object. In case of {@code null}, the default
+ * typeface is used instead.
* @param style The style (normal, bold, italic) of the typeface.
* e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
* @return The best matching typeface.
*/
public static Typeface create(Typeface family, int style) {
- if (style < 0 || style > 3) {
- style = 0;
+ if ((style & ~STYLE_MASK) != 0) {
+ style = NORMAL;
+ }
+ if (family == null) {
+ family = sDefaultTypeface;
}
- long ni = 0;
- if (family != null) {
- // Return early if we're asked for the same face/style
- if (family.mStyle == style) {
- return family;
- }
- ni = family.native_instance;
+ // Return early if we're asked for the same face/style
+ if (family.mStyle == style) {
+ return family;
}
+ final long ni = family.native_instance;
+
Typeface typeface;
- SparseArray<Typeface> styles = sTypefaceCache.get(ni);
+ synchronized (sStyledCacheLock) {
+ SparseArray<Typeface> styles = sStyledTypefaceCache.get(ni);
- if (styles != null) {
- typeface = styles.get(style);
- if (typeface != null) {
- return typeface;
+ if (styles == null) {
+ styles = new SparseArray<Typeface>(4);
+ sStyledTypefaceCache.put(ni, styles);
+ } else {
+ typeface = styles.get(style);
+ if (typeface != null) {
+ return typeface;
+ }
}
+
+ typeface = new Typeface(nativeCreateFromTypeface(ni, style));
+ styles.put(style, typeface);
}
+ return typeface;
+ }
- typeface = new Typeface(nativeCreateFromTypeface(ni, style));
- if (styles == null) {
- styles = new SparseArray<Typeface>(4);
- sTypefaceCache.put(ni, styles);
+ /**
+ * Creates a typeface object that best matches the specified existing typeface and the specified
+ * weight and italic style
+ *
+ * <p>
+ * This method is thread safe.
+ * </p>
+ *
+ * @param family An existing {@link Typeface} object. In case of {@code null}, the default
+ * typeface is used instead.
+ * @param weight The desired weight to be drawn.
+ * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
+ * @return A {@link Typeface} object for drawing specified weight and italic style. Never
+ * returns {@code null}
+ */
+ public static @NonNull Typeface create(@Nullable Typeface family,
+ @IntRange(from = 1, to = 1000) int weight, boolean italic) {
+ Preconditions.checkArgumentInRange(weight, 0, 1000, "weight");
+ if (family == null) {
+ family = sDefaultTypeface;
}
- styles.put(style, typeface);
+ return createWeightStyle(family, weight, italic);
+ }
+
+ private static @NonNull Typeface createWeightStyle(@NonNull Typeface base,
+ @IntRange(from = 1, to = 1000) int weight, boolean italic) {
+ final int key = (weight << 1) | (italic ? 1 : 0);
+ Typeface typeface;
+ synchronized(sWeightCacheLock) {
+ SparseArray<Typeface> innerCache = sWeightTypefaceCache.get(base.native_instance);
+ if (innerCache == null) {
+ innerCache = new SparseArray<>(4);
+ sWeightTypefaceCache.put(base.native_instance, innerCache);
+ } else {
+ typeface = innerCache.get(key);
+ if (typeface != null) {
+ return typeface;
+ }
+ }
+
+ typeface = new Typeface(
+ nativeCreateFromTypefaceWithExactStyle(
+ base.native_instance, weight, italic));
+ innerCache.put(key, typeface);
+ }
return typeface;
}
@@ -751,34 +804,33 @@ public class Typeface {
if (path == null) {
throw new NullPointerException(); // for backward compatibility
}
- if (sFallbackFonts != null) {
- synchronized (sLock) {
- Typeface typeface = new Builder(mgr, path).build();
- if (typeface != null) return typeface;
+ synchronized (sDynamicCacheLock) {
+ Typeface typeface = new Builder(mgr, path).build();
+ if (typeface != null) return typeface;
- final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
- null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
- typeface = sDynamicTypefaceCache.get(key);
- if (typeface != null) return typeface;
+ final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
+ null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ DEFAULT_FAMILY);
+ typeface = sDynamicTypefaceCache.get(key);
+ if (typeface != null) return typeface;
- final FontFamily fontFamily = new FontFamily();
- if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
- 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
- null /* axes */)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback
- // font selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
- final FontFamily[] families = { fontFamily };
- typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
- RESOLVE_BY_FONT_TABLE);
- sDynamicTypefaceCache.put(key, typeface);
- return typeface;
- } else {
- fontFamily.abortCreation();
- }
+ final FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
+ 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ null /* axes */)) {
+ // Due to backward compatibility, even if the font is not supported by our font
+ // stack, we need to place the empty font at the first place. The typeface with
+ // empty font behaves different from default typeface especially in fallback
+ // font selection.
+ fontFamily.allowUnsupportedFont();
+ fontFamily.freeze();
+ final FontFamily[] families = { fontFamily };
+ typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+ sDynamicTypefaceCache.put(key, typeface);
+ return typeface;
+ } else {
+ fontFamily.abortCreation();
}
}
throw new RuntimeException("Font asset not found " + path);
@@ -815,22 +867,20 @@ public class Typeface {
* @return The new typeface.
*/
public static Typeface createFromFile(@Nullable String path) {
- if (sFallbackFonts != null) {
- final FontFamily fontFamily = new FontFamily();
- if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback font
- // selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
- FontFamily[] families = { fontFamily };
- return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
- RESOLVE_BY_FONT_TABLE);
- } else {
- fontFamily.abortCreation();
- }
+ final FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
+ // Due to backward compatibility, even if the font is not supported by our font
+ // stack, we need to place the empty font at the first place. The typeface with
+ // empty font behaves different from default typeface especially in fallback font
+ // selection.
+ fontFamily.allowUnsupportedFont();
+ fontFamily.freeze();
+ FontFamily[] families = { fontFamily };
+ return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+ } else {
+ fontFamily.abortCreation();
}
throw new RuntimeException("Font not found " + path);
}
@@ -852,6 +902,8 @@ public class Typeface {
/**
* Create a new typeface from an array of font families, including
* also the font families in the fallback list.
+ * @param fallbackName the family name. If given families don't support characters, the
+ * characters will be rendered with this family.
* @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
* case, the table information in the first family's font is used. If the first
* family has multiple fonts, the closest to the regular weight and upright font
@@ -863,13 +915,17 @@ public class Typeface {
* @param families array of font families
*/
private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
- int weight, int italic) {
- long[] ptrArray = new long[families.length + sFallbackFonts.length];
+ String fallbackName, int weight, int italic) {
+ FontFamily[] fallback = sSystemFallbackMap.get(fallbackName);
+ if (fallback == null) {
+ fallback = sSystemFallbackMap.get(DEFAULT_FAMILY);
+ }
+ long[] ptrArray = new long[families.length + fallback.length];
for (int i = 0; i < families.length; i++) {
ptrArray[i] = families[i].mNativePtr;
}
- for (int i = 0; i < sFallbackFonts.length; i++) {
- ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
+ for (int i = 0; i < fallback.length; i++) {
+ ptrArray[i + families.length] = fallback[i].mNativePtr;
}
return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
}
@@ -885,113 +941,190 @@ public class Typeface {
mWeight = nativeGetWeight(ni);
}
- private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
- Map<String, ByteBuffer> bufferForPath) {
- FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
- for (FontConfig.Font font : family.getFonts()) {
- String fullPathName = "/system/fonts/" + font.getFontName();
- ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
- if (fontBuffer == null) {
- try (FileInputStream file = new FileInputStream(fullPathName)) {
- FileChannel fileChannel = file.getChannel();
- long fontSize = fileChannel.size();
- fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
- bufferForPath.put(fullPathName, fontBuffer);
- } catch (IOException e) {
- Log.e(TAG, "Error mapping font file " + fullPathName);
+ private static @Nullable ByteBuffer mmap(String fullPath) {
+ try (FileInputStream file = new FileInputStream(fullPath)) {
+ final FileChannel fileChannel = file.getChannel();
+ final long fontSize = fileChannel.size();
+ return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+ } catch (IOException e) {
+ Log.e(TAG, "Error mapping font file " + fullPath);
+ return null;
+ }
+ }
+
+ private static @Nullable FontFamily createFontFamily(
+ String familyName, List<FontConfig.Font> fonts, String[] languageTags, int variant,
+ Map<String, ByteBuffer> cache, String fontDir) {
+ final FontFamily family = new FontFamily(languageTags, variant);
+ for (int i = 0; i < fonts.size(); i++) {
+ final FontConfig.Font font = fonts.get(i);
+ final String fullPath = fontDir + font.getFontName();
+ ByteBuffer buffer = cache.get(fullPath);
+ if (buffer == null) {
+ if (cache.containsKey(fullPath)) {
+ continue; // Already failed to mmap. Skip it.
+ }
+ buffer = mmap(fullPath);
+ cache.put(fullPath, buffer);
+ if (buffer == null) {
continue;
}
}
- if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
+ if (!family.addFontFromBuffer(buffer, font.getTtcIndex(), font.getAxes(),
font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) {
- Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
+ Log.e(TAG, "Error creating font " + fullPath + "#" + font.getTtcIndex());
}
}
- if (!fontFamily.freeze()) {
- // Treat as system error since reaching here means that a system pre-installed font
- // can't be used by our font stack.
- Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
+ if (!family.freeze()) {
+ Log.e(TAG, "Unable to load Family: " + familyName + " : "
+ + Arrays.toString(languageTags));
return null;
}
- return fontFamily;
+ return family;
}
- /*
- * (non-Javadoc)
+ private static void pushFamilyToFallback(FontConfig.Family xmlFamily,
+ ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
+ Map<String, ByteBuffer> cache,
+ String fontDir) {
+
+ final String[] languageTags = xmlFamily.getLanguages();
+ final int variant = xmlFamily.getVariant();
+
+ final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>();
+ final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>();
+
+ // Collect default fallback and specific fallback fonts.
+ for (final FontConfig.Font font : xmlFamily.getFonts()) {
+ final String fallbackName = font.getFallbackFor();
+ if (fallbackName == null) {
+ defaultFonts.add(font);
+ } else {
+ ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(fallbackName);
+ if (fallback == null) {
+ fallback = new ArrayList<>();
+ specificFallbackFonts.put(fallbackName, fallback);
+ }
+ fallback.add(font);
+ }
+ }
+
+ final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
+ xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir);
+
+ // Insert family into fallback map.
+ for (int i = 0; i < fallbackMap.size(); i++) {
+ final ArrayList<FontConfig.Font> fallback =
+ specificFallbackFonts.get(fallbackMap.keyAt(i));
+ if (fallback == null) {
+ if (defaultFamily != null) {
+ fallbackMap.valueAt(i).add(defaultFamily);
+ }
+ } else {
+ final FontFamily family = createFontFamily(
+ xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir);
+ if (family != null) {
+ fallbackMap.valueAt(i).add(family);
+ }
+ }
+ }
+ }
+
+ /**
+ * Build the system fallback from xml file.
*
- * This should only be called once, from the static class initializer block.
+ * @param xmlPath A full path string to the fonts.xml file.
+ * @param fontDir A full path string to the system font directory. This must end with
+ * slash('/').
+ * @param fontMap An output system font map. Caller must pass empty map.
+ * @param fallbackMap An output system fallback map. Caller must pass empty map.
+ * @hide
*/
- private static void init() {
- // Load font config and initialize Minikin state
- File systemFontConfigLocation = getSystemFontConfigLocation();
- File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
+ @VisibleForTesting
+ public static void buildSystemFallback(String xmlPath, String fontDir,
+ ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
try {
- FileInputStream fontsIn = new FileInputStream(configFilename);
- FontConfig fontConfig = FontListParser.parse(fontsIn);
-
- Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
-
- List<FontFamily> familyList = new ArrayList<FontFamily>();
- // Note that the default typeface is always present in the fallback list;
- // this is an enhancement from pre-Minikin behavior.
- for (int i = 0; i < fontConfig.getFamilies().length; i++) {
- FontConfig.Family f = fontConfig.getFamilies()[i];
- if (i == 0 || f.getName() == null) {
- FontFamily family = makeFamilyFromParsed(f, bufferForPath);
- if (family != null) {
- familyList.add(family);
- }
+ final FileInputStream fontsIn = new FileInputStream(xmlPath);
+ final FontConfig fontConfig = FontListParser.parse(fontsIn);
+
+ final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>();
+ final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies();
+
+ final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
+ // First traverse families which have a 'name' attribute to create fallback map.
+ for (final FontConfig.Family xmlFamily : xmlFamilies) {
+ final String familyName = xmlFamily.getName();
+ if (familyName == null) {
+ continue;
}
+ final FontFamily family = createFontFamily(
+ xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
+ xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir);
+ if (family == null) {
+ continue;
+ }
+ final ArrayList<FontFamily> fallback = new ArrayList<>();
+ fallback.add(family);
+ fallbackListMap.put(familyName, fallback);
}
- sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
- setDefault(Typeface.createFromFamilies(sFallbackFonts));
-
- Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
- for (int i = 0; i < fontConfig.getFamilies().length; i++) {
- Typeface typeface;
- FontConfig.Family f = fontConfig.getFamilies()[i];
- if (f.getName() != null) {
- if (i == 0) {
- // The first entry is the default typeface; no sense in
- // duplicating the corresponding FontFamily.
- typeface = sDefaultTypeface;
- } else {
- FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
- if (fontFamily == null) {
- continue;
- }
- FontFamily[] families = { fontFamily };
- typeface = Typeface.createFromFamiliesWithDefault(families,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
- }
- systemFonts.put(f.getName(), typeface);
+
+ // Then, add fallback fonts to the each fallback map.
+ for (int i = 0; i < xmlFamilies.length; i++) {
+ final FontConfig.Family xmlFamily = xmlFamilies[i];
+ // The first family (usually the sans-serif family) is always placed immediately
+ // after the primary family in the fallback.
+ if (i == 0 || xmlFamily.getName() == null) {
+ pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir);
+ }
+ }
+
+ // Build the font map and fallback map.
+ for (int i = 0; i < fallbackListMap.size(); i++) {
+ final String fallbackName = fallbackListMap.keyAt(i);
+ final List<FontFamily> familyList = fallbackListMap.valueAt(i);
+ final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]);
+
+ fallbackMap.put(fallbackName, families);
+ final long[] ptrArray = new long[families.length];
+ for (int j = 0; j < families.length; j++) {
+ ptrArray[j] = families[j].mNativePtr;
}
+ fontMap.put(fallbackName, new Typeface(nativeCreateFromArray(
+ ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)));
}
- for (FontConfig.Alias alias : fontConfig.getAliases()) {
- Typeface base = systemFonts.get(alias.getToName());
+
+ // Insert alias to font maps.
+ for (final FontConfig.Alias alias : fontConfig.getAliases()) {
+ Typeface base = fontMap.get(alias.getToName());
Typeface newFace = base;
int weight = alias.getWeight();
if (weight != 400) {
newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
}
- systemFonts.put(alias.getName(), newFace);
+ fontMap.put(alias.getName(), newFace);
}
- sSystemFontMap = systemFonts;
-
} catch (RuntimeException e) {
Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
// TODO: normal in non-Minikin case, remove or make error when Minikin-only
} catch (FileNotFoundException e) {
- Log.e(TAG, "Error opening " + configFilename, e);
+ Log.e(TAG, "Error opening " + xmlPath, e);
} catch (IOException e) {
- Log.e(TAG, "Error reading " + configFilename, e);
+ Log.e(TAG, "Error reading " + xmlPath, e);
} catch (XmlPullParserException e) {
- Log.e(TAG, "XML parse exception for " + configFilename, e);
+ Log.e(TAG, "XML parse exception for " + xmlPath, e);
}
}
static {
- init();
+ final ArrayMap<String, Typeface> systemFontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
+ buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", systemFontMap,
+ systemFallbackMap);
+ sSystemFontMap = Collections.unmodifiableMap(systemFontMap);
+ sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
+
+ setDefault(sSystemFontMap.get(DEFAULT_FAMILY));
+
// Set up defaults and typefaces exposed in public API
DEFAULT = create((String) null, 0);
DEFAULT_BOLD = create((String) null, Typeface.BOLD);
@@ -1008,10 +1141,6 @@ public class Typeface {
}
- private static File getSystemFontConfigLocation() {
- return new File("/system/etc/");
- }
-
@Override
protected void finalize() throws Throwable {
try {
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 90d6ab867fe1..e74dc6dc9671 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -132,7 +132,7 @@ import dalvik.annotation.optimization.FastNative;
* <td>translateY</td>
* </tr>
* <tr>
- * <td rowspan="8">&lt;path&gt;</td>
+ * <td rowspan="9">&lt;path&gt;</td>
* <td>pathData</td>
* </tr>
* <tr>
@@ -154,6 +154,9 @@ import dalvik.annotation.optimization.FastNative;
* <td>trimPathStart</td>
* </tr>
* <tr>
+ * <td>trimPathEnd</td>
+ * </tr>
+ * <tr>
* <td>trimPathOffset</td>
* </tr>
* <tr>
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 6bd2646f9299..dea194e4ffde 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -36,138 +36,69 @@ class RippleBackground extends RippleComponent {
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- private static final int OPACITY_ENTER_DURATION = 600;
- private static final int OPACITY_ENTER_DURATION_FAST = 120;
- private static final int OPACITY_EXIT_DURATION = 480;
+ private static final int OPACITY_DURATION = 80;
- // Hardware rendering properties.
- private CanvasProperty<Paint> mPropPaint;
- private CanvasProperty<Float> mPropRadius;
- private CanvasProperty<Float> mPropX;
- private CanvasProperty<Float> mPropY;
+ private ObjectAnimator mAnimator;
- // Software rendering properties.
private float mOpacity = 0;
/** Whether this ripple is bounded. */
private boolean mIsBounded;
- public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded,
- boolean forceSoftware) {
- super(owner, bounds, forceSoftware);
+ private boolean mFocused = false;
+ private boolean mHovered = false;
+
+ public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) {
+ super(owner, bounds);
mIsBounded = isBounded;
}
public boolean isVisible() {
- return mOpacity > 0 || isHardwareAnimating();
+ return mOpacity > 0;
}
- @Override
- protected boolean drawSoftware(Canvas c, Paint p) {
- boolean hasContent = false;
-
+ public void draw(Canvas c, Paint p) {
final int origAlpha = p.getAlpha();
- final int alpha = (int) (origAlpha * mOpacity + 0.5f);
+ final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255);
if (alpha > 0) {
p.setAlpha(alpha);
c.drawCircle(0, 0, mTargetRadius, p);
p.setAlpha(origAlpha);
- hasContent = true;
}
-
- return hasContent;
- }
-
- @Override
- protected boolean drawHardware(DisplayListCanvas c) {
- c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
- return true;
- }
-
- @Override
- protected Animator createSoftwareEnter(boolean fast) {
- // Linear enter based on current opacity.
- final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION;
- final int duration = (int) ((1 - mOpacity) * maxDuration);
-
- final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
- opacity.setAutoCancel(true);
- opacity.setDuration(duration);
- opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- return opacity;
}
- @Override
- protected Animator createSoftwareExit() {
- final AnimatorSet set = new AnimatorSet();
-
- // Linear exit after enter is completed.
- final ObjectAnimator exit = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 0);
- exit.setInterpolator(LINEAR_INTERPOLATOR);
- exit.setDuration(OPACITY_EXIT_DURATION);
- exit.setAutoCancel(true);
-
- final AnimatorSet.Builder builder = set.play(exit);
-
- // Linear "fast" enter based on current opacity.
- final int fastEnterDuration = mIsBounded ?
- (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
- if (fastEnterDuration > 0) {
- final ObjectAnimator enter = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 1);
- enter.setInterpolator(LINEAR_INTERPOLATOR);
- enter.setDuration(fastEnterDuration);
- enter.setAutoCancel(true);
-
- builder.after(enter);
+ public void setState(boolean focused, boolean hovered, boolean animateChanged) {
+ if (mHovered != hovered || mFocused != focused) {
+ mHovered = hovered;
+ mFocused = focused;
+ onStateChanged(animateChanged);
}
-
- return set;
}
- @Override
- protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
-
- final int targetAlpha = p.getAlpha();
- final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f);
- p.setAlpha(currentAlpha);
-
- mPropPaint = CanvasProperty.createPaint(p);
- mPropRadius = CanvasProperty.createFloat(mTargetRadius);
- mPropX = CanvasProperty.createFloat(0);
- mPropY = CanvasProperty.createFloat(0);
-
- final int fastEnterDuration = mIsBounded ?
- (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
-
- // Linear exit after enter is completed.
- final RenderNodeAnimator exit = new RenderNodeAnimator(
- mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- exit.setInterpolator(LINEAR_INTERPOLATOR);
- exit.setDuration(OPACITY_EXIT_DURATION);
- if (fastEnterDuration > 0) {
- exit.setStartDelay(fastEnterDuration);
- exit.setStartValue(targetAlpha);
+ private void onStateChanged(boolean animateChanged) {
+ float newOpacity = 0.0f;
+ if (mHovered) newOpacity += 1.0f;
+ if (mFocused) newOpacity += 1.0f;
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ mAnimator = null;
}
- set.add(exit);
-
- // Linear "fast" enter based on current opacity.
- if (fastEnterDuration > 0) {
- final RenderNodeAnimator enter = new RenderNodeAnimator(
- mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha);
- enter.setInterpolator(LINEAR_INTERPOLATOR);
- enter.setDuration(fastEnterDuration);
- set.add(enter);
+ if (animateChanged) {
+ mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity);
+ mAnimator.setDuration(OPACITY_DURATION);
+ mAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+ mAnimator.start();
+ } else {
+ mOpacity = newOpacity;
}
-
- return set;
}
- @Override
- protected void jumpValuesToExit() {
- mOpacity = 0;
+ public void jumpToFinal() {
+ if (mAnimator != null) {
+ mAnimator.end();
+ mAnimator = null;
+ }
}
private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> {
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index e83513c644db..0e38826eb34b 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -27,23 +27,14 @@ import android.view.RenderNodeAnimator;
import java.util.ArrayList;
/**
- * Abstract class that handles hardware/software hand-off and lifecycle for
- * animated ripple foreground and background components.
+ * Abstract class that handles size & positioning common to the ripple & focus states.
*/
abstract class RippleComponent {
- private final RippleDrawable mOwner;
+ protected final RippleDrawable mOwner;
/** Bounds used for computing max radius. May be modified by the owner. */
protected final Rect mBounds;
- /** Whether we can use hardware acceleration for the exit animation. */
- private boolean mHasDisplayListCanvas;
-
- private boolean mHasPendingHardwareAnimator;
- private RenderNodeAnimatorSet mHardwareAnimator;
-
- private Animator mSoftwareAnimator;
-
/** Whether we have an explicit maximum radius. */
private boolean mHasMaxRadius;
@@ -53,16 +44,9 @@ abstract class RippleComponent {
/** Screen density used to adjust pixel-based constants. */
protected float mDensityScale;
- /**
- * If set, force all ripple animations to not run on RenderThread, even if it would be
- * available.
- */
- private final boolean mForceSoftware;
-
- public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+ public RippleComponent(RippleDrawable owner, Rect bounds) {
mOwner = owner;
mBounds = bounds;
- mForceSoftware = forceSoftware;
}
public void onBoundsChange() {
@@ -92,89 +76,6 @@ abstract class RippleComponent {
}
/**
- * Starts a ripple enter animation.
- *
- * @param fast whether the ripple should enter quickly
- */
- public final void enter(boolean fast) {
- cancel();
-
- mSoftwareAnimator = createSoftwareEnter(fast);
-
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.start();
- }
- }
-
- /**
- * Starts a ripple exit animation.
- */
- public final void exit() {
- cancel();
-
- if (mHasDisplayListCanvas) {
- // We don't have access to a canvas here, but we expect one on the
- // next frame. We'll start the render thread animation then.
- mHasPendingHardwareAnimator = true;
-
- // Request another frame.
- invalidateSelf();
- } else {
- mSoftwareAnimator = createSoftwareExit();
- mSoftwareAnimator.start();
- }
- }
-
- /**
- * Cancels all animations. Software animation values are left in the
- * current state, while hardware animation values jump to the end state.
- */
- public void cancel() {
- cancelSoftwareAnimations();
- endHardwareAnimations();
- }
-
- /**
- * Ends all animations, jumping values to the end state.
- */
- public void end() {
- endSoftwareAnimations();
- endHardwareAnimations();
- }
-
- /**
- * Draws the ripple to the canvas, inheriting the paint's color and alpha
- * properties.
- *
- * @param c the canvas to which the ripple should be drawn
- * @param p the paint used to draw the ripple
- * @return {@code true} if something was drawn, {@code false} otherwise
- */
- public boolean draw(Canvas c, Paint p) {
- final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated()
- && c instanceof DisplayListCanvas;
- if (mHasDisplayListCanvas != hasDisplayListCanvas) {
- mHasDisplayListCanvas = hasDisplayListCanvas;
-
- if (!hasDisplayListCanvas) {
- // We've switched from hardware to non-hardware mode. Panic.
- endHardwareAnimations();
- }
- }
-
- if (hasDisplayListCanvas) {
- final DisplayListCanvas hw = (DisplayListCanvas) c;
- startPendingAnimation(hw, p);
-
- if (mHardwareAnimator != null) {
- return drawHardware(hw);
- }
- }
-
- return drawSoftware(c, p);
- }
-
- /**
* Populates {@code bounds} with the maximum drawing bounds of the ripple
* relative to its center. The resulting bounds should be translated into
* parent drawable coordinates before use.
@@ -186,77 +87,10 @@ abstract class RippleComponent {
bounds.set(-r, -r, r, r);
}
- /**
- * Starts the pending hardware animation, if available.
- *
- * @param hw hardware canvas on which the animation should draw
- * @param p paint whose properties the hardware canvas should use
- */
- private void startPendingAnimation(DisplayListCanvas hw, Paint p) {
- if (mHasPendingHardwareAnimator) {
- mHasPendingHardwareAnimator = false;
-
- mHardwareAnimator = createHardwareExit(new Paint(p));
- mHardwareAnimator.start(hw);
-
- // Preemptively jump the software values to the end state now that
- // the hardware exit has read whatever values it needs.
- jumpValuesToExit();
- }
- }
-
- /**
- * Cancels any current software animations, leaving the values in their
- * current state.
- */
- private void cancelSoftwareAnimations() {
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.cancel();
- mSoftwareAnimator = null;
- }
- }
-
- /**
- * Ends any current software animations, jumping the values to their end
- * state.
- */
- private void endSoftwareAnimations() {
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.end();
- mSoftwareAnimator = null;
- }
- }
-
- /**
- * Ends any pending or current hardware animations.
- * <p>
- * Hardware animations can't synchronize values back to the software
- * thread, so there is no "cancel" equivalent.
- */
- private void endHardwareAnimations() {
- if (mHardwareAnimator != null) {
- mHardwareAnimator.end();
- mHardwareAnimator = null;
- }
-
- if (mHasPendingHardwareAnimator) {
- mHasPendingHardwareAnimator = false;
-
- // Manually jump values to their exited state. Normally we'd do that
- // later when starting the hardware exit, but we're aborting early.
- jumpValuesToExit();
- }
- }
-
protected final void invalidateSelf() {
mOwner.invalidateSelf(false);
}
- protected final boolean isHardwareAnimating() {
- return mHardwareAnimator != null && mHardwareAnimator.isRunning()
- || mHasPendingHardwareAnimator;
- }
-
protected final void onHotspotBoundsChanged() {
if (!mHasMaxRadius) {
final float halfWidth = mBounds.width() / 2.0f;
@@ -276,76 +110,4 @@ abstract class RippleComponent {
protected void onTargetRadiusChanged(float targetRadius) {
// Stub.
}
-
- protected abstract Animator createSoftwareEnter(boolean fast);
-
- protected abstract Animator createSoftwareExit();
-
- protected abstract RenderNodeAnimatorSet createHardwareExit(Paint p);
-
- protected abstract boolean drawHardware(DisplayListCanvas c);
-
- protected abstract boolean drawSoftware(Canvas c, Paint p);
-
- /**
- * Called when the hardware exit is cancelled. Jumps software values to end
- * state to ensure that software and hardware values are synchronized.
- */
- protected abstract void jumpValuesToExit();
-
- public static class RenderNodeAnimatorSet {
- private final ArrayList<RenderNodeAnimator> mAnimators = new ArrayList<>();
-
- public void add(RenderNodeAnimator anim) {
- mAnimators.add(anim);
- }
-
- public void clear() {
- mAnimators.clear();
- }
-
- public void start(DisplayListCanvas target) {
- if (target == null) {
- throw new IllegalArgumentException("Hardware canvas must be non-null");
- }
-
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.setTarget(target);
- anim.start();
- }
- }
-
- public void cancel() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.cancel();
- }
- }
-
- public void end() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.end();
- }
- }
-
- public boolean isRunning() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- if (anim.isRunning()) {
- return true;
- }
- }
- return false;
- }
- }
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 8f314c9c36aa..8b185f2b8903 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -16,11 +16,6 @@
package android.graphics.drawable;
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo.Config;
@@ -42,6 +37,11 @@ import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.util.Arrays;
@@ -135,9 +135,6 @@ public class RippleDrawable extends LayerDrawable {
private PorterDuffColorFilter mMaskColorFilter;
private boolean mHasValidMask;
- /** Whether we expect to draw a background when visible. */
- private boolean mBackgroundActive;
-
/** The current ripple. May be actively animating or pending entry. */
private RippleForeground mRipple;
@@ -217,7 +214,7 @@ public class RippleDrawable extends LayerDrawable {
}
if (mBackground != null) {
- mBackground.end();
+ mBackground.jumpToFinal();
}
cancelExitingRipples();
@@ -267,8 +264,8 @@ public class RippleDrawable extends LayerDrawable {
}
setRippleActive(enabled && pressed);
- setBackgroundActive(hovered || focused || (enabled && pressed), focused || hovered);
+ setBackgroundActive(hovered, focused);
return changed;
}
@@ -283,14 +280,13 @@ public class RippleDrawable extends LayerDrawable {
}
}
- private void setBackgroundActive(boolean active, boolean focused) {
- if (mBackgroundActive != active) {
- mBackgroundActive = active;
- if (active) {
- tryBackgroundEnter(focused);
- } else {
- tryBackgroundExit();
- }
+ private void setBackgroundActive(boolean hovered, boolean focused) {
+ if (mBackground == null && (hovered || focused)) {
+ mBackground = new RippleBackground(this, mHotspotBounds, isBounded());
+ mBackground.setup(mState.mMaxRadius, mDensity);
+ }
+ if (mBackground != null) {
+ mBackground.setState(focused, hovered, true);
}
}
@@ -327,10 +323,6 @@ public class RippleDrawable extends LayerDrawable {
tryRippleEnter();
}
- if (mBackgroundActive) {
- tryBackgroundEnter(false);
- }
-
// Skip animations, just show the correct final states.
jumpToCurrentState();
}
@@ -546,26 +538,6 @@ public class RippleDrawable extends LayerDrawable {
}
/**
- * Creates an active hotspot at the specified location.
- */
- private void tryBackgroundEnter(boolean focused) {
- if (mBackground == null) {
- final boolean isBounded = isBounded();
- mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware);
- }
-
- mBackground.setup(mState.mMaxRadius, mDensity);
- mBackground.enter(focused);
- }
-
- private void tryBackgroundExit() {
- if (mBackground != null) {
- // Don't null out the background, we need it to draw!
- mBackground.exit();
- }
- }
-
- /**
* Attempts to start an enter animation for the active hotspot. Fails if
* there are too many animating ripples.
*/
@@ -593,7 +565,7 @@ public class RippleDrawable extends LayerDrawable {
}
mRipple.setup(mState.mMaxRadius, mDensity);
- mRipple.enter(false);
+ mRipple.enter();
}
/**
@@ -623,9 +595,7 @@ public class RippleDrawable extends LayerDrawable {
}
if (mBackground != null) {
- mBackground.end();
- mBackground = null;
- mBackgroundActive = false;
+ mBackground.setState(false, false, false);
}
cancelExitingRipples();
@@ -693,7 +663,9 @@ public class RippleDrawable extends LayerDrawable {
// have a mask or content and the ripple bounds if we're projecting.
final Rect bounds = getDirtyBounds();
final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(bounds);
+ if (isBounded()) {
+ canvas.clipRect(bounds);
+ }
drawContent(canvas);
drawBackgroundAndRipples(canvas);
@@ -856,6 +828,40 @@ public class RippleDrawable extends LayerDrawable {
final float y = mHotspotBounds.exactCenterY();
canvas.translate(x, y);
+ final Paint p = getRipplePaint();
+
+ if (background != null && background.isVisible()) {
+ background.draw(canvas, p);
+ }
+
+ if (count > 0) {
+ final RippleForeground[] ripples = mExitingRipples;
+ for (int i = 0; i < count; i++) {
+ ripples[i].draw(canvas, p);
+ }
+ }
+
+ if (active != null) {
+ active.draw(canvas, p);
+ }
+
+ canvas.translate(-x, -y);
+ }
+
+ private void drawMask(Canvas canvas) {
+ mMask.draw(canvas);
+ }
+
+ Paint getRipplePaint() {
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+ mRipplePaint.setStyle(Paint.Style.FILL);
+ }
+
+ final float x = mHotspotBounds.exactCenterX();
+ final float y = mHotspotBounds.exactCenterY();
+
updateMaskShaderIfNeeded();
// Position the shader to account for canvas translation.
@@ -869,7 +875,7 @@ public class RippleDrawable extends LayerDrawable {
// half so that the ripple and background together yield full alpha.
final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
final int halfAlpha = (Color.alpha(color) / 2) << 24;
- final Paint p = getRipplePaint();
+ final Paint p = mRipplePaint;
if (mMaskColorFilter != null) {
// The ripple timing depends on the paint's alpha value, so we need
@@ -888,35 +894,7 @@ public class RippleDrawable extends LayerDrawable {
p.setShader(null);
}
- if (background != null && background.isVisible()) {
- background.draw(canvas, p);
- }
-
- if (count > 0) {
- final RippleForeground[] ripples = mExitingRipples;
- for (int i = 0; i < count; i++) {
- ripples[i].draw(canvas, p);
- }
- }
-
- if (active != null) {
- active.draw(canvas, p);
- }
-
- canvas.translate(-x, -y);
- }
-
- private void drawMask(Canvas canvas) {
- mMask.draw(canvas);
- }
-
- private Paint getRipplePaint() {
- if (mRipplePaint == null) {
- mRipplePaint = new Paint();
- mRipplePaint.setAntiAlias(true);
- mRipplePaint.setStyle(Paint.Style.FILL);
- }
- return mRipplePaint;
+ return p;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 829733e9b097..0b5020cbe55c 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -18,7 +18,6 @@ package android.graphics.drawable;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
@@ -29,8 +28,11 @@ import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
+import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import java.util.ArrayList;
+
/**
* Draws a ripple foreground.
*/
@@ -40,8 +42,7 @@ class RippleForeground extends RippleComponent {
400f, 1.4f, 0);
// Pixel-based accelerations and velocities.
- private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024;
- private static final float WAVE_TOUCH_UP_ACCELERATION = 3400;
+ private static final float WAVE_TOUCH_DOWN_ACCELERATION = 2048;
private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
// Bounded ripple animation properties.
@@ -50,8 +51,9 @@ class RippleForeground extends RippleComponent {
private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
private static final float MAX_BOUNDED_RADIUS = 350;
- private static final int RIPPLE_ENTER_DELAY = 80;
- private static final int OPACITY_ENTER_DURATION_FAST = 120;
+ private static final int OPACITY_ENTER_DURATION = 75;
+ private static final int OPACITY_EXIT_DURATION = 150;
+ private static final int OPACITY_HOLD_DURATION = OPACITY_ENTER_DURATION + 150;
// Parent-relative values for starting position.
private float mStartingX;
@@ -73,24 +75,42 @@ class RippleForeground extends RippleComponent {
private float mBoundedRadius = 0;
// Software rendering properties.
- private float mOpacity = 1;
+ private float mOpacity = 0;
// Values used to tween between the start and end positions.
private float mTweenRadius = 0;
private float mTweenX = 0;
private float mTweenY = 0;
- /** Whether this ripple is bounded. */
- private boolean mIsBounded;
-
/** Whether this ripple has finished its exit animation. */
private boolean mHasFinishedExit;
+ /** Whether we can use hardware acceleration for the exit animation. */
+ private boolean mUsingProperties;
+
+ private long mEnterStartedAtMillis;
+
+ private ArrayList<RenderNodeAnimator> mPendingHwAnimators = new ArrayList<>();
+ private ArrayList<RenderNodeAnimator> mRunningHwAnimators = new ArrayList<>();
+
+ private ArrayList<Animator> mRunningSwAnimators = new ArrayList<>();
+
+ /**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private final boolean mForceSoftware;
+
+ /**
+ * If we have a bound, don't start from 0. Start from 60% of the max out of width and height.
+ */
+ private float mStartRadius = 0;
+
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
boolean isBounded, boolean forceSoftware) {
- super(owner, bounds, forceSoftware);
+ super(owner, bounds);
- mIsBounded = isBounded;
+ mForceSoftware = forceSoftware;
mStartingX = startingX;
mStartingY = startingY;
@@ -100,6 +120,8 @@ class RippleForeground extends RippleComponent {
} else {
mBoundedRadius = 0;
}
+ // Take 60% of the maximum of the width and height, then divided half to get the radius.
+ mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
}
@Override
@@ -107,10 +129,7 @@ class RippleForeground extends RippleComponent {
clampStartingPosition();
}
- @Override
- protected boolean drawSoftware(Canvas c, Paint p) {
- boolean hasContent = false;
-
+ private void drawSoftware(Canvas c, Paint p) {
final int origAlpha = p.getAlpha();
final int alpha = (int) (origAlpha * mOpacity + 0.5f);
final float radius = getCurrentRadius();
@@ -120,16 +139,51 @@ class RippleForeground extends RippleComponent {
p.setAlpha(alpha);
c.drawCircle(x, y, radius, p);
p.setAlpha(origAlpha);
- hasContent = true;
}
+ }
- return hasContent;
+ private void startPending(DisplayListCanvas c) {
+ if (!mPendingHwAnimators.isEmpty()) {
+ for (int i = 0; i < mPendingHwAnimators.size(); i++) {
+ RenderNodeAnimator animator = mPendingHwAnimators.get(i);
+ animator.setTarget(c);
+ animator.start();
+ mRunningHwAnimators.add(animator);
+ }
+ mPendingHwAnimators.clear();
+ }
}
- @Override
- protected boolean drawHardware(DisplayListCanvas c) {
- c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
- return true;
+ private void pruneHwFinished() {
+ if (!mRunningHwAnimators.isEmpty()) {
+ for (int i = mRunningHwAnimators.size() - 1; i >= 0; i--) {
+ if (!mRunningHwAnimators.get(i).isRunning()) {
+ mRunningHwAnimators.remove(i);
+ }
+ }
+ }
+ }
+
+ private void pruneSwFinished() {
+ if (!mRunningSwAnimators.isEmpty()) {
+ for (int i = mRunningSwAnimators.size() - 1; i >= 0; i--) {
+ if (!mRunningSwAnimators.get(i).isRunning()) {
+ mRunningSwAnimators.remove(i);
+ }
+ }
+ }
+ }
+
+ private void drawHardware(DisplayListCanvas c, Paint p) {
+ startPending(c);
+ pruneHwFinished();
+ if (mPropPaint != null) {
+ mUsingProperties = true;
+ c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
+ } else {
+ mUsingProperties = false;
+ drawSoftware(c, p);
+ }
}
/**
@@ -160,170 +214,152 @@ class RippleForeground extends RippleComponent {
return mHasFinishedExit;
}
- @Override
- protected Animator createSoftwareEnter(boolean fast) {
- // Bounded ripples don't have enter animations.
- if (mIsBounded) {
- return null;
+ private long computeFadeOutDelay() {
+ long timeSinceEnter = AnimationUtils.currentAnimationTimeMillis() - mEnterStartedAtMillis;
+ if (timeSinceEnter > 0 && timeSinceEnter < OPACITY_HOLD_DURATION) {
+ return OPACITY_HOLD_DURATION - timeSinceEnter;
+ }
+ return 0;
+ }
+
+ private void startSoftwareEnter() {
+ for (int i = 0; i < mRunningSwAnimators.size(); i++) {
+ mRunningSwAnimators.get(i).cancel();
}
+ mRunningSwAnimators.clear();
- final int duration = (int)
- (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
+ final int duration = getRadiusDuration();
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setAutoCancel(true);
tweenRadius.setDuration(duration);
- tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
- tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
+ tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
+ tweenRadius.start();
+ mRunningSwAnimators.add(tweenRadius);
final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setAutoCancel(true);
tweenOrigin.setDuration(duration);
- tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
- tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
+ tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
+ tweenOrigin.start();
+ mRunningSwAnimators.add(tweenOrigin);
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
- opacity.setAutoCancel(true);
- opacity.setDuration(OPACITY_ENTER_DURATION_FAST);
+ opacity.setDuration(OPACITY_ENTER_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- final AnimatorSet set = new AnimatorSet();
- set.play(tweenOrigin).with(tweenRadius).with(opacity);
-
- return set;
- }
-
- private float getCurrentX() {
- return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
+ opacity.start();
+ mRunningSwAnimators.add(opacity);
}
- private float getCurrentY() {
- return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
- }
-
- private int getRadiusExitDuration() {
- final float remainingRadius = mTargetRadius - getCurrentRadius();
- return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
- + WAVE_TOUCH_DOWN_ACCELERATION) * mDensityScale) + 0.5);
- }
-
- private float getCurrentRadius() {
- return MathUtils.lerp(0, mTargetRadius, mTweenRadius);
- }
-
- private int getOpacityExitDuration() {
- return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
- }
-
- /**
- * Compute target values that are dependent on bounding.
- */
- private void computeBoundedTargetValues() {
- mTargetX = (mClampedStartingX - mBounds.exactCenterX()) * .7f;
- mTargetY = (mClampedStartingY - mBounds.exactCenterY()) * .7f;
- mTargetRadius = mBoundedRadius;
- }
-
- @Override
- protected Animator createSoftwareExit() {
- final int radiusDuration;
- final int originDuration;
- final int opacityDuration;
- if (mIsBounded) {
- computeBoundedTargetValues();
-
- radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
- originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
- opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
- } else {
- radiusDuration = getRadiusExitDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
- }
-
- final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setAutoCancel(true);
- tweenRadius.setDuration(radiusDuration);
- tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setAutoCancel(true);
- tweenOrigin.setDuration(originDuration);
- tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
-
+ private void startSoftwareExit() {
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
- opacity.setAutoCancel(true);
- opacity.setDuration(opacityDuration);
+ opacity.setDuration(OPACITY_EXIT_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- final AnimatorSet set = new AnimatorSet();
- set.play(tweenOrigin).with(tweenRadius).with(opacity);
- set.addListener(mAnimationListener);
-
- return set;
+ opacity.addListener(mAnimationListener);
+ opacity.setStartDelay(computeFadeOutDelay());
+ opacity.start();
+ mRunningSwAnimators.add(opacity);
}
- @Override
- protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final int radiusDuration;
- final int originDuration;
- final int opacityDuration;
- if (mIsBounded) {
- computeBoundedTargetValues();
-
- radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
- originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
- opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
- } else {
- radiusDuration = getRadiusExitDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
- }
-
- final float startX = getCurrentX();
- final float startY = getCurrentY();
- final float startRadius = getCurrentRadius();
+ private void startHardwareEnter() {
+ if (mForceSoftware) { return; }
+ mPropX = CanvasProperty.createFloat(getCurrentX());
+ mPropY = CanvasProperty.createFloat(getCurrentY());
+ mPropRadius = CanvasProperty.createFloat(getCurrentRadius());
+ final Paint paint = mOwner.getRipplePaint();
+ mPropPaint = CanvasProperty.createPaint(paint);
- p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f));
-
- mPropPaint = CanvasProperty.createPaint(p);
- mPropRadius = CanvasProperty.createFloat(startRadius);
- mPropX = CanvasProperty.createFloat(startX);
- mPropY = CanvasProperty.createFloat(startY);
+ final int radiusDuration = getRadiusDuration();
final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
radius.setDuration(radiusDuration);
radius.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(radius);
final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
- x.setDuration(originDuration);
+ x.setDuration(radiusDuration);
x.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(x);
final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
- y.setDuration(originDuration);
+ y.setDuration(radiusDuration);
y.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(y);
+
+ final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
+ RenderNodeAnimator.PAINT_ALPHA, paint.getAlpha());
+ opacity.setDuration(OPACITY_ENTER_DURATION);
+ opacity.setInterpolator(LINEAR_INTERPOLATOR);
+ opacity.setStartValue(0);
+ mPendingHwAnimators.add(opacity);
+
+ invalidateSelf();
+ }
+
+ private void startHardwareExit() {
+ // Only run a hardware exit if we had a hardware enter to continue from
+ if (mForceSoftware || mPropPaint == null) return;
final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
- opacity.setDuration(opacityDuration);
+ opacity.setDuration(OPACITY_EXIT_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
opacity.addListener(mAnimationListener);
+ opacity.setStartDelay(computeFadeOutDelay());
+ mPendingHwAnimators.add(opacity);
+ invalidateSelf();
+ }
- final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
- set.add(radius);
- set.add(opacity);
- set.add(x);
- set.add(y);
+ /**
+ * Starts a ripple enter animation.
+ */
+ public final void enter() {
+ mEnterStartedAtMillis = AnimationUtils.currentAnimationTimeMillis();
+ startSoftwareEnter();
+ startHardwareEnter();
+ }
- return set;
+ /**
+ * Starts a ripple exit animation.
+ */
+ public final void exit() {
+ startSoftwareExit();
+ startHardwareExit();
}
- @Override
- protected void jumpValuesToExit() {
- mOpacity = 0;
- mTweenX = 1;
- mTweenY = 1;
- mTweenRadius = 1;
+ private float getCurrentX() {
+ return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
+ }
+
+ private float getCurrentY() {
+ return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
+ }
+
+ private int getRadiusDuration() {
+ final float remainingRadius = mTargetRadius - getCurrentRadius();
+ return (int) (1000 * Math.sqrt(remainingRadius / WAVE_TOUCH_DOWN_ACCELERATION *
+ mDensityScale) + 0.5);
+ }
+
+ private float getCurrentRadius() {
+ return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
+ }
+
+ /**
+ * Draws the ripple to the canvas, inheriting the paint's color and alpha
+ * properties.
+ *
+ * @param c the canvas to which the ripple should be drawn
+ * @param p the paint used to draw the ripple
+ */
+ public void draw(Canvas c, Paint p) {
+ final boolean hasDisplayListCanvas = !mForceSoftware && c instanceof DisplayListCanvas;
+
+ pruneSwFinished();
+ if (hasDisplayListCanvas) {
+ final DisplayListCanvas hw = (DisplayListCanvas) c;
+ drawHardware(hw, p);
+ } else {
+ drawSoftware(c, p);
+ }
}
/**
@@ -346,10 +382,39 @@ class RippleForeground extends RippleComponent {
}
}
+ /**
+ * Ends all animations, jumping values to the end state.
+ */
+ public void end() {
+ for (int i = 0; i < mRunningSwAnimators.size(); i++) {
+ mRunningSwAnimators.get(i).end();
+ }
+ mRunningSwAnimators.clear();
+ for (int i = 0; i < mRunningHwAnimators.size(); i++) {
+ mRunningHwAnimators.get(i).end();
+ }
+ mRunningHwAnimators.clear();
+ }
+
+ private void onAnimationPropertyChanged() {
+ if (!mUsingProperties) {
+ invalidateSelf();
+ }
+ }
+
private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
mHasFinishedExit = true;
+ pruneHwFinished();
+ pruneSwFinished();
+
+ if (mRunningHwAnimators.isEmpty()) {
+ mPropPaint = null;
+ mPropRadius = null;
+ mPropX = null;
+ mPropY = null;
+ }
}
};
@@ -388,7 +453,7 @@ class RippleForeground extends RippleComponent {
@Override
public void setValue(RippleForeground object, float value) {
object.mTweenRadius = value;
- object.invalidateSelf();
+ object.onAnimationPropertyChanged();
}
@Override
@@ -402,18 +467,18 @@ class RippleForeground extends RippleComponent {
*/
private static final FloatProperty<RippleForeground> TWEEN_ORIGIN =
new FloatProperty<RippleForeground>("tweenOrigin") {
- @Override
- public void setValue(RippleForeground object, float value) {
- object.mTweenX = value;
- object.mTweenY = value;
- object.invalidateSelf();
- }
+ @Override
+ public void setValue(RippleForeground object, float value) {
+ object.mTweenX = value;
+ object.mTweenY = value;
+ object.onAnimationPropertyChanged();
+ }
- @Override
- public Float get(RippleForeground object) {
- return object.mTweenX;
- }
- };
+ @Override
+ public Float get(RippleForeground object) {
+ return object.mTweenX;
+ }
+ };
/**
* Property for animating opacity between 0 and its target value.
@@ -423,7 +488,7 @@ class RippleForeground extends RippleComponent {
@Override
public void setValue(RippleForeground object, float value) {
object.mOpacity = value;
- object.invalidateSelf();
+ object.onAnimationPropertyChanged();
}
@Override
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index ceac3253e178..c71585f32155 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -213,12 +213,79 @@ import dalvik.system.VMRuntime;
* &lt;/vector&gt;
* </pre>
* </li>
- * <li>And here is an example of linear gradient color, which is supported in SDK 24+.
+ * <h4>Gradient support</h4>
+ * We support 3 types of gradients: {@link android.graphics.LinearGradient},
+ * {@link android.graphics.RadialGradient}, or {@link android.graphics.SweepGradient}.
+ * <p/>
+ * And we support all of 3 types of tile modes {@link android.graphics.Shader.TileMode}:
+ * CLAMP, REPEAT, MIRROR.
+ * <p/>
+ * All of the attributes are listed in {@link android.R.styleable#GradientColor}.
+ * Note that different attributes are relevant for different types of gradient.
+ * <table border="2" align="center" cellpadding="5">
+ * <thead>
+ * <tr>
+ * <th>LinearGradient</th>
+ * <th>RadialGradient</th>
+ * <th>SweepGradient</th>
+ * </tr>
+ * </thead>
+ * <tr>
+ * <td>startColor </td>
+ * <td>startColor</td>
+ * <td>startColor</td>
+ * </tr>
+ * <tr>
+ * <td>centerColor</td>
+ * <td>centerColor</td>
+ * <td>centerColor</td>
+ * </tr>
+ * <tr>
+ * <td>endColor</td>
+ * <td>endColor</td>
+ * <td>endColor</td>
+ * </tr>
+ * <tr>
+ * <td>type</td>
+ * <td>type</td>
+ * <td>type</td>
+ * </tr>
+ * <tr>
+ * <td>tileMode</td>
+ * <td>tileMode</td>
+ * <td>tileMode</td>
+ * </tr>
+ * <tr>
+ * <td>startX</td>
+ * <td>centerX</td>
+ * <td>centerX</td>
+ * </tr>
+ * <tr>
+ * <td>startY</td>
+ * <td>centerY</td>
+ * <td>centerY</td>
+ * </tr>
+ * <tr>
+ * <td>endX</td>
+ * <td>gradientRadius</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>endY</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ * <p/>
+ * Also note that if any color item {@link android.R.styleable#GradientColorItem} is defined, then
+ * startColor, centerColor and endColor will be ignored.
+ * <p/>
* See more details in {@link android.R.styleable#GradientColor} and
* {@link android.R.styleable#GradientColorItem}.
+ * <p/>
+ * Here is a simple example that defines a linear gradient.
* <pre>
* &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"
- * android:angle="90"
* android:startColor="?android:attr/colorPrimary"
* android:endColor="?android:attr/colorControlActivated"
* android:centerColor="#f00"
@@ -229,7 +296,18 @@ import dalvik.system.VMRuntime;
* android:type="linear"&gt;
* &lt;/gradient&gt;
* </pre>
- * </li>
+ * And here is a simple example that defines a radial gradient using color items.
+ * <pre>
+ * &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:centerX="300"
+ * android:centerY="300"
+ * android:gradientRadius="100"
+ * android:type="radial"&gt;
+ * &lt;item android:offset="0.1" android:color="#0ff"/&gt;
+ * &lt;item android:offset="0.4" android:color="#fff"/&gt;
+ * &lt;item android:offset="0.9" android:color="#ff0"/&gt;
+ * &lt;/gradient&gt;
+ * </pre>
*
*/
@@ -818,6 +896,13 @@ public class VectorDrawable extends Drawable {
return mVectorState.getNativeRenderer();
}
+ /**
+ * @hide
+ */
+ public void setAntiAlias(boolean aa) {
+ nSetAntiAlias(mVectorState.mNativeTree.get(), aa);
+ }
+
static class VectorDrawableState extends ConstantState {
// Variables below need to be copied (deep copy if applicable) for mutation.
int[] mThemeAttrs;
@@ -2191,6 +2276,8 @@ public class VectorDrawable extends Drawable {
@FastNative
private static native float nGetRootAlpha(long rendererPtr);
@FastNative
+ private static native void nSetAntiAlias(long rendererPtr, boolean aa);
+ @FastNative
private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
@FastNative
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 99564fab94cd..1b7408a03294 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -178,7 +178,7 @@ public final class FontVariationAxis {
* @return String a valid font variation settings string.
*/
public static @NonNull String toFontVariationSettings(@Nullable FontVariationAxis[] axes) {
- if (axes == null || axes.length == 0) {
+ if (axes == null) {
return "";
}
return TextUtils.join(",", axes);
diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java
index dc4b11791c1e..3821bc7ab063 100644
--- a/graphics/java/android/graphics/pdf/PdfEditor.java
+++ b/graphics/java/android/graphics/pdf/PdfEditor.java
@@ -40,7 +40,7 @@ public final class PdfEditor {
private final CloseGuard mCloseGuard = CloseGuard.get();
- private final long mNativeDocument;
+ private long mNativeDocument;
private int mPageCount;
@@ -78,12 +78,17 @@ public final class PdfEditor {
} catch (ErrnoException ee) {
throw new IllegalArgumentException("file descriptor not seekable");
}
-
mInput = input;
synchronized (PdfRenderer.sPdfiumLock) {
mNativeDocument = nativeOpen(mInput.getFd(), size);
- mPageCount = nativeGetPageCount(mNativeDocument);
+ try {
+ mPageCount = nativeGetPageCount(mNativeDocument);
+ } catch (Throwable t) {
+ nativeClose(mNativeDocument);
+ mNativeDocument = 0;
+ throw t;
+ }
}
mCloseGuard.open("close");
@@ -275,20 +280,24 @@ public final class PdfEditor {
mCloseGuard.warnIfOpen();
}
- if (mInput != null) {
- doClose();
- }
+ doClose();
} finally {
super.finalize();
}
}
private void doClose() {
- synchronized (PdfRenderer.sPdfiumLock) {
- nativeClose(mNativeDocument);
+ if (mNativeDocument != 0) {
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeClose(mNativeDocument);
+ }
+ mNativeDocument = 0;
+ }
+
+ if (mInput != null) {
+ IoUtils.closeQuietly(mInput);
+ mInput = null;
}
- IoUtils.closeQuietly(mInput);
- mInput = null;
mCloseGuard.close();
}
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index 29e1ea0fee5d..4a91705239c1 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -31,6 +31,8 @@ import android.system.OsConstants;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
+import libcore.io.IoUtils;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -110,7 +112,7 @@ public final class PdfRenderer implements AutoCloseable {
private final Point mTempPoint = new Point();
- private final long mNativeDocument;
+ private long mNativeDocument;
private final int mPageCount;
@@ -159,7 +161,6 @@ public final class PdfRenderer implements AutoCloseable {
} catch (ErrnoException ee) {
throw new IllegalArgumentException("file descriptor not seekable");
}
-
mInput = input;
synchronized (sPdfiumLock) {
@@ -168,6 +169,7 @@ public final class PdfRenderer implements AutoCloseable {
mPageCount = nativeGetPageCount(mNativeDocument);
} catch (Throwable t) {
nativeClose(mNativeDocument);
+ mNativeDocument = 0;
throw t;
}
}
@@ -234,9 +236,7 @@ public final class PdfRenderer implements AutoCloseable {
mCloseGuard.warnIfOpen();
}
- if (mInput != null) {
- doClose();
- }
+ doClose();
} finally {
super.finalize();
}
@@ -245,16 +245,20 @@ public final class PdfRenderer implements AutoCloseable {
private void doClose() {
if (mCurrentPage != null) {
mCurrentPage.close();
+ mCurrentPage = null;
}
- synchronized (sPdfiumLock) {
- nativeClose(mNativeDocument);
+
+ if (mNativeDocument != 0) {
+ synchronized (sPdfiumLock) {
+ nativeClose(mNativeDocument);
+ }
+ mNativeDocument = 0;
}
- try {
- mInput.close();
- } catch (IOException ioe) {
- /* ignore - best effort */
+
+ if (mInput != null) {
+ IoUtils.closeQuietly(mInput);
+ mInput = null;
}
- mInput = null;
mCloseGuard.close();
}
@@ -451,19 +455,20 @@ public final class PdfRenderer implements AutoCloseable {
mCloseGuard.warnIfOpen();
}
- if (mNativePage != 0) {
- doClose();
- }
+ doClose();
} finally {
super.finalize();
}
}
private void doClose() {
- synchronized (sPdfiumLock) {
- nativeClosePage(mNativePage);
+ if (mNativePage != 0) {
+ synchronized (sPdfiumLock) {
+ nativeClosePage(mNativePage);
+ }
+ mNativePage = 0;
}
- mNativePage = 0;
+
mCloseGuard.close();
mCurrentPage = null;
}