Merge "Propagate density through AdaptiveIconDrawable and BitmapDrawable" into oc-dev

am: 5be3647a9e

Change-Id: Ie0408d44a2641e704c18399c996aff7f996e2764
This commit is contained in:
Adam Lesinski
2017-04-21 22:42:31 +00:00
committed by android-build-merger
8 changed files with 201 additions and 71 deletions

View File

@@ -815,14 +815,7 @@ public class Resources {
*/
public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValue(id, value, true);
return impl.loadDrawable(this, value, id, theme, true);
} finally {
releaseTempTypedValue(value);
}
return getDrawableForDensity(id, 0, theme);
}
/**
@@ -844,7 +837,9 @@ public class Resources {
* This integer encodes the package, type, and resource entry.
* The value 0 is an invalid identifier.
* @param density the desired screen density indicated by the resource as
* found in {@link DisplayMetrics}.
* found in {@link DisplayMetrics}. A value of 0 means to use the
* density returned from {@link #getConfiguration()}.
* This is equivalent to calling {@link #getDrawable(int)}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
@@ -865,7 +860,9 @@ public class Resources {
* This integer encodes the package, type, and resource entry.
* The value 0 is an invalid identifier.
* @param density The desired screen density indicated by the resource as
* found in {@link DisplayMetrics}.
* found in {@link DisplayMetrics}. A value of 0 means to use the
* density returned from {@link #getConfiguration()}.
* This is equivalent to calling {@link #getDrawable(int, Theme)}.
* @param theme The theme used to style the drawable attributes, may be {@code null}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
@@ -876,37 +873,16 @@ public class Resources {
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValueForDensity(id, density, value, true);
// If the drawable's XML lives in our current density qualifier,
// it's okay to use a scaled version from the cache. Otherwise, we
// need to actually load the drawable from XML.
final DisplayMetrics metrics = impl.getDisplayMetrics();
final boolean useCache = value.density == metrics.densityDpi;
/*
* Pretend the requested density is actually the display density. If
* the drawable returned is not the requested density, then force it
* to be scaled later by dividing its density by the ratio of
* requested density to actual device density. Drawables that have
* undefined density or no density don't need to be handled here.
*/
if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
if (value.density == density) {
value.density = metrics.densityDpi;
} else {
value.density = (value.density * metrics.densityDpi) / density;
}
}
return impl.loadDrawable(this, value, id, theme, useCache);
return impl.loadDrawable(this, value, id, density, theme);
} finally {
releaseTempTypedValue(value);
}
}
@NonNull
Drawable loadDrawable(@NonNull TypedValue value, int id, @Nullable Theme theme)
Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
throws NotFoundException {
return mResourcesImpl.loadDrawable(this, value, id, theme, true);
return mResourcesImpl.loadDrawable(this, value, id, density, theme);
}
/**
@@ -1221,8 +1197,7 @@ public class Resources {
* used to open drawable, sound, and raw resources; it will fail on string
* and color resources.
*
* @param id The resource identifier to open, as generated by the appt
* tool.
* @param id The resource identifier to open, as generated by the aapt tool.
*
* @return InputStream Access to the resource data.
*
@@ -1278,7 +1253,7 @@ public class Resources {
* used to open drawable, sound, and raw resources; it will fail on string
* and color resources.
*
* @param id The resource identifier to open, as generated by the appt tool.
* @param id The resource identifier to open, as generated by the aapt tool.
* @param value The TypedValue object to hold the resource information.
*
* @return InputStream Access to the resource data.
@@ -1300,8 +1275,7 @@ public class Resources {
* as uncompressed data, which typically includes things like mp3 files
* and png images.
*
* @param id The resource identifier to open, as generated by the appt
* tool.
* @param id The resource identifier to open, as generated by the aapt tool.
*
* @return AssetFileDescriptor A new file descriptor you can use to read
* the resource. This includes the file descriptor itself, as well as the

View File

@@ -523,8 +523,27 @@ public class ResourcesImpl {
}
@Nullable
Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
boolean useCache) throws NotFoundException {
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
throws NotFoundException {
// If the drawable's XML lives in our current density qualifier,
// it's okay to use a scaled version from the cache. Otherwise, we
// need to actually load the drawable from XML.
final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;
// Pretend the requested density is actually the display density. If
// the drawable returned is not the requested density, then force it
// to be scaled later by dividing its density by the ratio of
// requested density to actual device density. Drawables that have
// undefined density or no density don't need to be handled here.
if (density > 0 && value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
if (value.density == density) {
value.density = mMetrics.densityDpi;
} else {
value.density = (value.density * mMetrics.densityDpi) / density;
}
}
try {
if (TRACE_FOR_PRELOAD) {
// Log only framework resources
@@ -576,7 +595,7 @@ public class ResourcesImpl {
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
dr = loadDrawableForCookie(wrapper, value, id, null);
dr = loadDrawableForCookie(wrapper, value, id, density, null);
}
// Determine if the drawable has unresolved theme attributes. If it
@@ -691,8 +710,8 @@ public class ResourcesImpl {
/**
* Loads a drawable from XML or resources stream.
*/
private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
Resources.Theme theme) {
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
int id, int density, @Nullable Resources.Theme theme) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -722,7 +741,7 @@ public class ResourcesImpl {
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
dr = Drawable.createFromXml(wrapper, rp, theme);
dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(

View File

@@ -923,6 +923,15 @@ public class TypedArray {
*/
@Nullable
public Drawable getDrawable(@StyleableRes int index) {
return getDrawableForDensity(index, 0);
}
/**
* Version of {@link #getDrawable(int)} that accepts an override density.
* @hide
*/
@Nullable
public Drawable getDrawableForDensity(@StyleableRes int index, int density) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
@@ -933,7 +942,13 @@ public class TypedArray {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
}
return mResources.loadDrawable(value, value.resourceId, mTheme);
if (density > 0) {
// If the density is overridden, the value in the TypedArray will not reflect this.
// Do a separate lookup of the resourceId with the density override.
mResources.getValueForDensity(value.resourceId, density, value, true);
}
return mResources.loadDrawable(value, value.resourceId, density, mTheme);
}
return null;
}

View File

@@ -16,8 +16,6 @@
package android.graphics.drawable;
import static android.graphics.drawable.Drawable.obtainAttributes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -218,14 +216,16 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
// The density may have changed since the last update. This will
// apply scaling to any existing constant state properties.
final int density = Drawable.resolveDensity(r, 0);
state.setDensity(density);
final int deviceDensity = Drawable.resolveDensity(r, 0);
state.setDensity(deviceDensity);
state.mSrcDensityOverride = mSrcDensityOverride;
final ChildDrawable[] array = state.mChildren;
for (int i = 0; i < state.mChildren.length; i++) {
final ChildDrawable layer = array[i];
layer.setDensity(density);
layer.setDensity(deviceDensity);
}
inflateLayers(r, parser, attrs, theme);
}
@@ -444,7 +444,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
/**
* Inflates child layers using the specified parser.
*/
void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final LayerState state = mLayerState;
@@ -491,7 +491,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
// We found a child drawable. Take ownership.
layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
layer.mDrawable = Drawable.createFromXmlInnerForDensity(r, parser, attrs,
mLayerState.mSrcDensityOverride, theme);
layer.mDrawable.setCallback(this);
state.mChildrenChangingConfigurations |=
layer.mDrawable.getChangingConfigurations();
@@ -509,7 +510,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
// Extract the theme attributes, if any.
layer.mThemeAttrs = a.extractThemeAttrs();
Drawable dr = a.getDrawable(R.styleable.AdaptiveIconDrawableLayer_drawable);
Drawable dr = a.getDrawableForDensity(R.styleable.AdaptiveIconDrawableLayer_drawable,
state.mSrcDensityOverride);
if (dr != null) {
if (layer.mDrawable != null) {
// It's possible that a drawable was already set, in which case
@@ -951,7 +953,13 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
final static int N_CHILDREN = 2;
ChildDrawable[] mChildren;
// The density at which to render the drawable and its children.
int mDensity;
// The density to use when inflating/looking up the children drawables. A value of 0 means
// use the system's density.
int mSrcDensityOverride = 0;
int mOpacityOverride = PixelFormat.UNKNOWN;
@Config int mChangingConfigurations;
@@ -986,6 +994,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
mAutoMirrored = orig.mAutoMirrored;
mThemeAttrs = orig.mThemeAttrs;
mOpacityOverride = orig.mOpacityOverride;
mSrcDensityOverride = orig.mSrcDensityOverride;
} else {
for (int i = 0; i < N_CHILDREN; i++) {
mChildren[i] = new ChildDrawable(mDensity);

View File

@@ -41,6 +41,7 @@ import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.TypedValue;
import android.view.Gravity;
import com.android.internal.R;
@@ -49,6 +50,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
/**
* A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
@@ -749,7 +751,7 @@ public class BitmapDrawable extends Drawable {
super.inflate(r, parser, attrs, theme);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
updateStateFromTypedArray(a);
updateStateFromTypedArray(a, mSrcDensityOverride);
verifyRequiredAttributes(a);
a.recycle();
@@ -775,7 +777,8 @@ public class BitmapDrawable extends Drawable {
/**
* Updates the constant state from the values in the typed array.
*/
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
private void updateStateFromTypedArray(TypedArray a, int srcDensityOverride)
throws XmlPullParserException {
final Resources r = a.getResources();
final BitmapState state = mBitmapState;
@@ -785,9 +788,37 @@ public class BitmapDrawable extends Drawable {
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
state.mSrcDensityOverride = srcDensityOverride;
state.mTargetDensity = Drawable.resolveDensity(r, 0);
final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
if (srcResId != 0) {
final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId);
final TypedValue value = new TypedValue();
r.getValueForDensity(srcResId, srcDensityOverride, value, true);
// Pretend the requested density is actually the display density. If
// the drawable returned is not the requested density, then force it
// to be scaled later by dividing its density by the ratio of
// requested density to actual device density. Drawables that have
// undefined density or no density don't need to be handled here.
if (srcDensityOverride > 0 && value.density > 0
&& value.density != TypedValue.DENSITY_NONE) {
if (value.density == srcDensityOverride) {
value.density = r.getDisplayMetrics().densityDpi;
} else {
value.density =
(value.density * r.getDisplayMetrics().densityDpi) / srcDensityOverride;
}
}
Bitmap bitmap = null;
try (InputStream is = r.openRawResource(srcResId, value)) {
bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
} catch (Exception e) {
// Do nothing and pick up the error below.
}
if (bitmap == null) {
throw new XmlPullParserException(a.getPositionDescription() +
": <bitmap> requires a valid 'src' attribute");
@@ -796,8 +827,6 @@ public class BitmapDrawable extends Drawable {
state.mBitmap = bitmap;
}
state.mTargetDensity = r.getDisplayMetrics().densityDpi;
final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false;
setMipMap(a.getBoolean(R.styleable.BitmapDrawable_mipMap, defMipMap));
@@ -839,8 +868,6 @@ public class BitmapDrawable extends Drawable {
if (tileModeY != TILE_MODE_UNDEFINED) {
setTileModeY(parseTileMode(tileModeY));
}
state.mTargetDensity = Drawable.resolveDensity(r, 0);
}
@Override
@@ -855,7 +882,7 @@ public class BitmapDrawable extends Drawable {
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.BitmapDrawable);
try {
updateStateFromTypedArray(a);
updateStateFromTypedArray(a, state.mSrcDensityOverride);
} catch (XmlPullParserException e) {
rethrowAsRuntimeException(e);
} finally {
@@ -929,7 +956,14 @@ public class BitmapDrawable extends Drawable {
float mBaseAlpha = 1.0f;
Shader.TileMode mTileModeX = null;
Shader.TileMode mTileModeY = null;
// The density to use when looking up the bitmap in Resources. A value of 0 means use
// the system's density.
int mSrcDensityOverride = 0;
// The density at which to render the bitmap.
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mAutoMirrored = false;
@Config int mChangingConfigurations;
@@ -949,6 +983,7 @@ public class BitmapDrawable extends Drawable {
mGravity = bitmapState.mGravity;
mTileModeX = bitmapState.mTileModeX;
mTileModeY = bitmapState.mTileModeY;
mSrcDensityOverride = bitmapState.mSrcDensityOverride;
mTargetDensity = bitmapState.mTargetDensity;
mBaseAlpha = bitmapState.mBaseAlpha;
mPaint = new Paint(bitmapState.mPaint);

View File

@@ -188,6 +188,21 @@ public abstract class Drawable {
private int mLayoutDirection;
/**
* The source density to use when looking up resources using
* {@link Resources#getDrawableForDensity(int, int, Theme)}. A value of 0 means there is no
* override and the system density will be used.
*
* NOTE(adamlesinski): This is transient state used to get around the public API that does not
* account for source density overrides. Custom drawables implemented by developers do not need
* to be aware of the source density override, as it is only used by Launcher to load higher
* resolution icons from external Resources packages, which do not execute custom code.
* This is all to support the {@link Resources#getDrawableForDensity(int, int, Theme)} API.
*
* @hide
*/
protected int mSrcDensityOverride = 0;
/**
* Draw in its bounds (set via setBounds) respecting optional effects such
* as alpha (set via setAlpha) and color filter (set via setColorFilter).
@@ -1197,7 +1212,8 @@ public abstract class Drawable {
* create resources in XML, see
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser)
@NonNull
public static Drawable createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser)
throws XmlPullParserException, IOException {
return createFromXml(r, parser, null);
}
@@ -1207,7 +1223,20 @@ public abstract class Drawable {
* For more information on how to create resources in XML, see
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
@NonNull
public static Drawable createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
@Nullable Theme theme) throws XmlPullParserException, IOException {
return createFromXmlForDensity(r, parser, 0, theme);
}
/**
* Version of {@link #createFromXml(Resources, XmlPullParser, Theme)} that accepts a density
* override.
* @hide
*/
@NonNull
public static Drawable createFromXmlForDensity(@NonNull Resources r,
@NonNull XmlPullParser parser, int density, @Nullable Theme theme)
throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
@@ -1222,7 +1251,7 @@ public abstract class Drawable {
throw new XmlPullParserException("No start tag found");
}
Drawable drawable = createFromXmlInner(r, parser, attrs, theme);
Drawable drawable = createFromXmlInnerForDensity(r, parser, attrs, density, theme);
if (drawable == null) {
throw new RuntimeException("Unknown initial tag: " + parser.getName());
@@ -1236,8 +1265,9 @@ public abstract class Drawable {
* a tag in an XML document, tries to create a Drawable from that tag.
* Returns null if the tag is not a valid drawable.
*/
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
@NonNull
public static Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
return createFromXmlInner(r, parser, attrs, null);
}
@@ -1247,14 +1277,29 @@ public abstract class Drawable {
* document, tries to create a Drawable from that tag. Returns {@code null}
* if the tag is not a valid drawable.
*/
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
return r.getDrawableInflater().inflateFromXml(parser.getName(), parser, attrs, theme);
@NonNull
public static Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
return createFromXmlInnerForDensity(r, parser, attrs, 0, theme);
}
/**
* Version of {@link #createFromXmlInner(Resources, XmlPullParser, AttributeSet, Theme)} that
* accepts an override density.
*/
@NonNull
static Drawable createFromXmlInnerForDensity(@NonNull Resources r,
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, int density,
@Nullable Theme theme) throws XmlPullParserException, IOException {
return r.getDrawableInflater().inflateFromXmlForDensity(parser.getName(), parser, attrs,
density, theme);
}
/**
* Create a drawable from file path name.
*/
@Nullable
public static Drawable createFromPath(String pathName) {
if (pathName == null) {
return null;
@@ -1315,6 +1360,16 @@ public abstract class Drawable {
mVisible = attrs.getBoolean(visibleAttr, mVisible);
}
/**
* Sets the source override density for this Drawable. If non-zero, this density is to be used
* for any calls to {@link Resources#getDrawableForDensity(int, int, Theme)} or
* {@link Resources#getValueForDensity(int, int, TypedValue, boolean)}.
* @hide
*/
final void setSrcDensityOverride(int density) {
mSrcDensityOverride = density;
}
/**
* This abstract class is used by {@link Drawable}s to store shared constant state and data
* between Drawables. {@link BitmapDrawable}s created from the same resource will for instance

View File

@@ -112,6 +112,17 @@ public final class DrawableInflater {
public Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
return inflateFromXmlForDensity(name, parser, attrs, 0, theme);
}
/**
* Version of {@link #inflateFromXml(String, XmlPullParser, AttributeSet, Theme)} that accepts
* an override density.
*/
@NonNull
Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, int density, @Nullable Theme theme)
throws XmlPullParserException, IOException {
// Inner classes must be referenced as Outer$Inner, but XML tag names
// can't contain $, so the <drawable> tag allows developers to specify
// the class in an attribute. We'll still run it through inflateFromTag
@@ -127,6 +138,7 @@ public final class DrawableInflater {
if (drawable == null) {
drawable = inflateFromClass(name);
}
drawable.setSrcDensityOverride(density);
drawable.inflate(mRes, parser, attrs, theme);
return drawable;
}

View File

@@ -131,6 +131,7 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
final int densityDpi = r.getDisplayMetrics().densityDpi;
final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
state.setDensity(targetDensity);
state.mSrcDensityOverride = mSrcDensityOverride;
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.DrawableWrapper);
updateStateFromTypedArray(a);
@@ -437,7 +438,8 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.START_TAG) {
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
dr = Drawable.createFromXmlInnerForDensity(r, parser, attrs,
mState.mSrcDensityOverride, theme);
}
}
@@ -452,6 +454,14 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
@Config int mChangingConfigurations;
int mDensity = DisplayMetrics.DENSITY_DEFAULT;
/**
* The density to use when looking up resources from
* {@link Resources#getDrawableForDensity(int, int, Theme)}.
* A value of 0 means there is no override and the system density will be used.
* @hide
*/
int mSrcDensityOverride = 0;
Drawable.ConstantState mDrawableState;
DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
@@ -459,6 +469,7 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
mDrawableState = orig.mDrawableState;
mSrcDensityOverride = orig.mSrcDensityOverride;
}
final int density;