Merge "Update API, add more documentation on AdaptiveIconDrawable" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
76c13a5fce
@@ -13828,7 +13828,7 @@ package android.graphics.drawable {
|
||||
public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
|
||||
method public void draw(android.graphics.Canvas);
|
||||
method public android.graphics.drawable.Drawable getBackground();
|
||||
method public static float getExtraInsetPercentage();
|
||||
method public static float getExtraInsetFraction();
|
||||
method public android.graphics.drawable.Drawable getForeground();
|
||||
method public android.graphics.Path getIconMask();
|
||||
method public int getOpacity();
|
||||
|
||||
@@ -14595,7 +14595,7 @@ package android.graphics.drawable {
|
||||
public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
|
||||
method public void draw(android.graphics.Canvas);
|
||||
method public android.graphics.drawable.Drawable getBackground();
|
||||
method public static float getExtraInsetPercentage();
|
||||
method public static float getExtraInsetFraction();
|
||||
method public android.graphics.drawable.Drawable getForeground();
|
||||
method public android.graphics.Path getIconMask();
|
||||
method public int getOpacity();
|
||||
|
||||
@@ -13879,7 +13879,7 @@ package android.graphics.drawable {
|
||||
public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
|
||||
method public void draw(android.graphics.Canvas);
|
||||
method public android.graphics.drawable.Drawable getBackground();
|
||||
method public static float getExtraInsetPercentage();
|
||||
method public static float getExtraInsetFraction();
|
||||
method public android.graphics.drawable.Drawable getForeground();
|
||||
method public android.graphics.Path getIconMask();
|
||||
method public int getOpacity();
|
||||
|
||||
@@ -20,11 +20,8 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PaintFlagsDrawFilter;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -49,7 +46,7 @@ public final class LauncherIcons {
|
||||
public LauncherIcons(Context context) {
|
||||
mRes = context.getResources();
|
||||
DisplayMetrics metrics = mRes.getDisplayMetrics();
|
||||
mShadowInset = (int) metrics.density / DisplayMetrics.DENSITY_DEFAULT;
|
||||
mShadowInset = (int)(2 * metrics.density);
|
||||
mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
|
||||
Paint.FILTER_BITMAP_FLAG));
|
||||
mIconSize = (int) mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
|
||||
@@ -91,7 +88,6 @@ public final class LauncherIcons {
|
||||
return mShadowBitmap;
|
||||
}
|
||||
|
||||
int shadowSize = mIconSize - mShadowInset;
|
||||
mShadowBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ALPHA_8);
|
||||
mCanvas.setBitmap(mShadowBitmap);
|
||||
|
||||
|
||||
@@ -2,18 +2,22 @@ package android.graphics.drawable;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Path.Direction;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Log;
|
||||
import android.util.PathParser;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.Arrays;
|
||||
@@ -35,7 +39,7 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase {
|
||||
* Nothing is drawn.
|
||||
*/
|
||||
@Test
|
||||
public void testDrawWithoutSetBounds() throws Exception {
|
||||
public void testDraw_withoutBounds() throws Exception {
|
||||
mBackgroundDrawable = new ColorDrawable(Color.BLUE);
|
||||
mForegroundDrawable = new ColorDrawable(Color.RED);
|
||||
mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
|
||||
@@ -59,7 +63,7 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase {
|
||||
* When setBound is called, translate accordingly.
|
||||
*/
|
||||
@Test
|
||||
public void testDrawSetBounds() throws Exception {
|
||||
public void testDraw_withBounds() throws Exception {
|
||||
int dpi = 4 ;
|
||||
int top = 18 * dpi;
|
||||
int left = 18 * dpi;
|
||||
@@ -102,6 +106,71 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When setBound isn't called before getIconMask method is called.
|
||||
* default device config mask is returned.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIconMask_withoutBounds() throws Exception {
|
||||
mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
|
||||
Path pathFromDrawable = mIconDrawable.getIconMask();
|
||||
Path pathFromDeviceConfig = PathParser.createPathFromPathData(
|
||||
Resources.getSystem().getString(com.android.internal.R.string.config_icon_mask));
|
||||
|
||||
RectF boundFromDrawable = new RectF();
|
||||
pathFromDrawable.computeBounds(boundFromDrawable, true);
|
||||
|
||||
RectF boundFromDeviceConfig = new RectF();
|
||||
pathFromDeviceConfig.computeBounds(boundFromDeviceConfig, true);
|
||||
|
||||
double delta = 0.01;
|
||||
assertEquals("left", boundFromDrawable.left, boundFromDeviceConfig.left, delta);
|
||||
assertEquals("top", boundFromDrawable.top, boundFromDeviceConfig.top, delta);
|
||||
assertEquals("right", boundFromDrawable.right, boundFromDeviceConfig.right, delta);
|
||||
assertEquals("bottom", boundFromDrawable.bottom, boundFromDeviceConfig.bottom, delta);
|
||||
|
||||
assertTrue("path from device config is convex.", pathFromDeviceConfig.isConvex());
|
||||
assertTrue("path from drawable is convex.", pathFromDrawable.isConvex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIconMaskAfterSetBounds() throws Exception {
|
||||
int dpi = 4;
|
||||
int top = 18 * dpi;
|
||||
int left = 18 * dpi;
|
||||
int right = 90 * dpi;
|
||||
int bottom = 90 * dpi;
|
||||
|
||||
mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
|
||||
mIconDrawable.setBounds(left, top, right, bottom);
|
||||
RectF maskBounds = new RectF();
|
||||
|
||||
mIconDrawable.getIconMask().computeBounds(maskBounds, true);
|
||||
|
||||
double delta = 0.01;
|
||||
assertEquals("left", left, maskBounds.left, delta);
|
||||
assertEquals("top", top, maskBounds.top, delta);
|
||||
assertEquals("right", right, maskBounds.right, delta);
|
||||
assertEquals("bottom", bottom, maskBounds.bottom, delta);
|
||||
|
||||
assertTrue(mIconDrawable.getIconMask().isConvex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOutline_withBounds() throws Exception {
|
||||
int dpi = 4;
|
||||
int top = 18 * dpi;
|
||||
int left = 18 * dpi;
|
||||
int right = 90 * dpi;
|
||||
int bottom = 90 * dpi;
|
||||
|
||||
mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
|
||||
mIconDrawable.setBounds(left, top, right, bottom);
|
||||
Outline outline = new Outline();
|
||||
mIconDrawable.getOutline(outline);
|
||||
assertTrue("outline path should be convex", outline.mPath.isConvex());
|
||||
}
|
||||
|
||||
//
|
||||
// Utils
|
||||
//
|
||||
|
||||
@@ -119,13 +119,13 @@ public class IconTest extends AndroidTestCase {
|
||||
final AdaptiveIconDrawable draw1 = (AdaptiveIconDrawable) im1.loadDrawable(mContext);
|
||||
|
||||
final Bitmap test1 = Bitmap.createBitmap(
|
||||
(int)(draw1.getIntrinsicWidth() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetPercentage())),
|
||||
(int)(draw1.getIntrinsicHeight() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetPercentage())),
|
||||
(int)(draw1.getIntrinsicWidth() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())),
|
||||
(int)(draw1.getIntrinsicHeight() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
draw1.setBounds(0, 0,
|
||||
(int) (draw1.getIntrinsicWidth() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetPercentage())),
|
||||
(int) (draw1.getIntrinsicHeight() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetPercentage())));
|
||||
(int) (draw1.getIntrinsicWidth() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())),
|
||||
(int) (draw1.getIntrinsicHeight() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())));
|
||||
draw1.draw(new Canvas(test1));
|
||||
|
||||
final File dir = getContext().getExternalFilesDir(null);
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Outline;
|
||||
@@ -51,12 +52,27 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This drawable supports two layers: foreground and background.
|
||||
*
|
||||
* <p>The layers are clipped when rendering using the mask path defined in the device configuration.
|
||||
*
|
||||
* <p>This class can also be created via XML inflation using <code><adaptive-icon></code> tag
|
||||
* in addition to dynamic creation.
|
||||
*
|
||||
* <p>This drawable supports two drawable layers: foreground and background. The layers are clipped
|
||||
* when rendering using the mask defined in the device configuration.
|
||||
*
|
||||
* <ul>
|
||||
* <li>Both foreground and background layers should be sized at 108 x 108 dp.</li>
|
||||
* <li>The inner 72 x 72 dp of the icon appears within the masked viewport.</li>
|
||||
* <li>The outer 18 dp on each of the 4 sides of the layers is reserved for use by the system UI
|
||||
* surfaces to create interesting visual effects, such as parallax or pulsing.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Such motion effect is achieved by internally setting the bounds of the foreground and
|
||||
* background layer as following:
|
||||
* <pre>
|
||||
* Rect(getBounds().left - getBounds().getWidth() * #getExtraInsetFraction(),
|
||||
* getBounds().top - getBounds().getHeight() * #getExtraInsetFraction(),
|
||||
* getBounds().right + getBounds().getWidth() * #getExtraInsetFraction(),
|
||||
* getBounds().bottom + getBounds().getHeight() * #getExtraInsetFraction())
|
||||
* </pre>
|
||||
*/
|
||||
public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback {
|
||||
|
||||
@@ -65,7 +81,11 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
* @hide
|
||||
*/
|
||||
public static final float MASK_SIZE = 100f;
|
||||
private static final float SAFEZONE_SCALE = .9f;
|
||||
|
||||
/**
|
||||
* Launcher icons design guideline
|
||||
*/
|
||||
private static final float SAFEZONE_SCALE = 72f/66f;
|
||||
|
||||
/**
|
||||
* All four sides of the layers are padded with extra inset so as to provide
|
||||
@@ -80,7 +100,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
|
||||
|
||||
/**
|
||||
* Clip path defined in {@link com.android.internal.R.string.config_icon_mask}.
|
||||
* Clip path defined in R.string.config_icon_mask.
|
||||
*/
|
||||
private static Path sMask;
|
||||
|
||||
@@ -134,9 +154,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
|
||||
if (sMask == null) {
|
||||
sMask = PathParser.createPathFromPathData(
|
||||
Resources.getSystem().getString(com.android.internal.R.string.config_icon_mask));
|
||||
Resources.getSystem().getString(R.string.config_icon_mask));
|
||||
}
|
||||
mMask = new Path();
|
||||
mMask = PathParser.createPathFromPathData(
|
||||
Resources.getSystem().getString(R.string.config_icon_mask));
|
||||
mMaskMatrix = new Matrix();
|
||||
mCanvas = new Canvas();
|
||||
mTransparentRegion = new Region();
|
||||
@@ -212,13 +233,24 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
* All four sides of the layers are padded with extra inset so as to provide
|
||||
* extra content to reveal within the clip path when performing affine transformations on the
|
||||
* layers.
|
||||
*
|
||||
* @see #getForeground() and #getBackground() for more info on how this value is used
|
||||
*/
|
||||
public static float getExtraInsetFraction() {
|
||||
return EXTRA_INSET_PERCENTAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static float getExtraInsetPercentage() {
|
||||
return EXTRA_INSET_PERCENTAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only call this method after bound is set on this drawable.
|
||||
* When called before the bound is set, the returned path is identical to
|
||||
* R.string.config_icon_mask. After the bound is set, the
|
||||
* returned path's computed bound is same as the #getBounds().
|
||||
*
|
||||
* @return the mask path object used to clip the drawable
|
||||
*/
|
||||
@@ -227,6 +259,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the foreground drawable managed by this class. The bound of this drawable is
|
||||
* extended by {@link #getExtraInsetFraction()} * getBounds().width on left/right sides and by
|
||||
* {@link #getExtraInsetFraction()} * getBounds().height on top/bottom sides.
|
||||
*
|
||||
* @return the foreground drawable managed by this drawable
|
||||
*/
|
||||
public Drawable getForeground() {
|
||||
@@ -234,6 +270,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the foreground drawable managed by this class. The bound of this drawable is
|
||||
* extended by {@link #getExtraInsetFraction()} * getBounds().width on left/right sides and by
|
||||
* {@link #getExtraInsetFraction()} * getBounds().height on top/bottom sides.
|
||||
*
|
||||
* @return the background drawable managed by this drawable
|
||||
*/
|
||||
public Drawable getBackground() {
|
||||
@@ -293,10 +333,15 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
|
||||
mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
// mMaskBitmap bound [0, w] x [0, h]
|
||||
mCanvas.setBitmap(mMaskBitmap);
|
||||
mPaint.setShader(null);
|
||||
mCanvas.drawPath(mMask, mPaint);
|
||||
|
||||
// mMask bound [left, top, right, bottom]
|
||||
mMaskMatrix.postTranslate(b.left, b.top);
|
||||
mMask.reset();
|
||||
sMask.transform(mMaskMatrix, mMask);
|
||||
// reset everything that depends on the view bounds
|
||||
mTransparentRegion.setEmpty();
|
||||
mLayersShader = null;
|
||||
@@ -309,6 +354,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
|
||||
}
|
||||
if (mLayersShader == null) {
|
||||
mCanvas.setBitmap(mLayersBitmap);
|
||||
mCanvas.drawColor(Color.BLACK);
|
||||
for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
|
||||
if (mLayerState.mChildren[i] == null) {
|
||||
continue;
|
||||
|
||||
@@ -708,7 +708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
Drawable dr = mLauncherApps.getShortcutIconDrawable(
|
||||
makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), 0);
|
||||
assertTrue(dr instanceof AdaptiveIconDrawable);
|
||||
float viewportPercentage = 1 / (1 + 2 * AdaptiveIconDrawable.getExtraInsetPercentage());
|
||||
float viewportPercentage = 1 / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
|
||||
assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage),
|
||||
dr.getIntrinsicWidth());
|
||||
assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage),
|
||||
|
||||
Reference in New Issue
Block a user