From 4eb298a941c3f465944b63f1a06518e911681c89 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Fri, 21 Aug 2015 17:23:50 -0700 Subject: LayoutLib: Fix true transparency in status bar. Change-Id: Ieedf23cde9ab3e36c77501d30cc7e808e66a4782 --- .../src/com/android/layoutlib/bridge/bars/CustomBar.java | 16 ++++++++++------ .../com/android/layoutlib/bridge/bars/NavigationBar.java | 4 ++-- .../src/com/android/layoutlib/bridge/bars/StatusBar.java | 5 +++-- .../android/layoutlib/bridge/impl/RenderSessionImpl.java | 3 +-- 4 files changed, 16 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index b76ec17..567002e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -33,6 +33,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; @@ -227,16 +228,18 @@ abstract class CustomBar extends LinearLayout { * Find the background color for this bar from the theme attributes. Only relevant to StatusBar * and NavigationBar. *

- * Returns 0 if not found. + * Returns null if not found. * * @param colorAttrName the attribute name for the background color * @param translucentAttrName the attribute name for the translucency property of the bar. * * @throws NumberFormatException if color resolved to an invalid string. */ - protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) { + @Nullable + protected Integer getBarColor(@NonNull String colorAttrName, + @NonNull String translucentAttrName) { if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) { - return 0; + return null; } RenderResources renderResources = getContext().getRenderResources(); // First check if the bar is translucent. @@ -251,10 +254,11 @@ abstract class CustomBar extends LinearLayout { if (transparent) { return getColor(renderResources, colorAttrName); } - return 0; + return null; } - private static int getColor(RenderResources renderResources, String attr) { + @Nullable + private static Integer getColor(RenderResources renderResources, String attr) { // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue. ResourceValue resource = renderResources.findItemInTheme(attr, true); // Form @color/bar to the #AARRGGBB @@ -275,7 +279,7 @@ abstract class CustomBar extends LinearLayout { } } } - return 0; + return null; } private ResourceValue getResourceValue(String reference) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index 9c89bfe..d50ce23 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -65,8 +65,8 @@ public class NavigationBar extends CustomBar { super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, "navigation_bar.xml", simulatedPlatformVersion); - int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor(color == 0 ? 0xFF000000 : color); + Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == null ? 0xFF000000 : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 2dc7c65..95a5a58 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -71,8 +71,9 @@ public class StatusBar extends CustomBar { // FIXME: use FILL_H? setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT); - int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color); + Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor( + color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index ac7c409..24e1ce7 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -421,8 +421,7 @@ public class RenderSessionImpl extends RenderAction { gc.setComposite(AlphaComposite.Src); gc.setColor(new Color(0x00000000, true)); - gc.fillRect(0, 0, - mMeasuredScreenWidth, mMeasuredScreenHeight); + gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); // done gc.dispose(); -- cgit v1.1 From 753517700dc6ee95b9945a6c8857aa8c633cde2a Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Fri, 28 Aug 2015 11:53:46 -0700 Subject: Fix android:theme parsing for custom views. Change-Id: Iba83ba1d2e4a96461cc298a759e32e4e51e311a0 --- .../bridge/src/android/view/BridgeInflater.java | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 1e33e3a..f1726eb 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -36,6 +36,7 @@ import org.xmlpull.v1.XmlPullParser; import android.annotation.NonNull; import android.content.Context; +import android.content.res.TypedArray; import android.util.AttributeSet; import java.io.File; @@ -54,6 +55,9 @@ public final class BridgeInflater extends LayoutInflater { private ResourceReference mResourceReference; private Map mOpenDrawerLayouts; + // Keep in sync with the same value in LayoutInflater. + private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme }; + /** * List of class prefixes which are tried first by default. *

@@ -135,11 +139,23 @@ public final class BridgeInflater extends LayoutInflater { @Override public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, - boolean ignoreThemeAttrs) { + boolean ignoreThemeAttr) { View view; try { - view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs); + view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttr); } catch (InflateException e) { + // Creation of ContextThemeWrapper code is same as in the super method. + // Apply a theme wrapper, if allowed and one is specified. + if (!ignoreThemeAttr) { + final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); + final int themeResId = ta.getResourceId(0, 0); + if (themeResId != 0) { + context = new ContextThemeWrapper(context, themeResId); + } + ta.recycle(); + } + final Object lastContext = mConstructorArgs[0]; + mConstructorArgs[0] = context; // try to load the class from using the custom view loader try { view = loadCustomView(name, attrs); @@ -153,6 +169,8 @@ public final class BridgeInflater extends LayoutInflater { exception.initCause(e); } throw exception; + } finally { + mConstructorArgs[0] = lastContext; } } -- cgit v1.1 From 0e766de7c52d7e375e04676c93a872cb2d6eea63 Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Tue, 22 Sep 2015 11:08:35 +0100 Subject: Avoid calling scrollBy(0,0) on render Usually scrollBy(0, 0) won't have any effect but when called on the NumberPicker but, in some cases, it seems to cause an endless loop. When called from layoutlib, this will hold the read lock causing all operations trying to acquire the write lock to block forever. Bug: http://b.android.com/182506 Change-Id: Ib25427d3f32d804d1c481d979795eee71fbdbc6d --- .../src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java | 4 ---- 1 file changed, 4 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 24e1ce7..2a4f583 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -1050,11 +1050,7 @@ public class RenderSessionImpl extends RenderAction { } if (scrollPos != 0) { view.scrollBy(0, scrollPos); - } else { - view.scrollBy(0, scrollPos); } - } else { - view.scrollBy(0, scrollPos); } if (!(view instanceof ViewGroup)) { -- cgit v1.1 From 6649ca371139831369e063b937e25d7d6e97b9e6 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 23 Sep 2015 15:57:43 -0700 Subject: Fix TypedArray.getTextArray() Change-Id: Ic55b52840892fbf2a4258ba97ed31ca0283da8ed --- .../src/android/content/res/BridgeResources.java | 5 +---- .../src/android/content/res/BridgeTypedArray.java | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 163fbcb..3b7bf85 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -48,9 +48,6 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Iterator; -/** - * - */ public final class BridgeResources extends Resources { private BridgeContext mContext; @@ -278,7 +275,7 @@ public final class BridgeResources extends Resources { * always Strings. The ideal signature for the method should be <T super String>, but java * generics don't support it. */ - private T[] fillValues(ArrayResourceValue resValue, T[] values) { + T[] fillValues(ArrayResourceValue resValue, T[] values) { int i = 0; for (Iterator iterator = resValue.iterator(); iterator.hasNext(); i++) { @SuppressWarnings("unchecked") diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index 6a61090..31dd3d9 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -16,6 +16,7 @@ package android.content.res; +import com.android.ide.common.rendering.api.ArrayResourceValue; import com.android.ide.common.rendering.api.AttrResourceValue; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.RenderResources; @@ -33,6 +34,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.Nullable; +import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; @@ -740,12 +742,20 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public CharSequence[] getTextArray(int index) { - String value = getString(index); - if (value != null) { - return new CharSequence[] { value }; + if (!hasValue(index)) { + return null; } - - return null; + ResourceValue resVal = mResourceData[index]; + if (resVal instanceof ArrayResourceValue) { + ArrayResourceValue array = (ArrayResourceValue) resVal; + int count = array.getElementCount(); + return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null; + } + int id = getResourceId(index, 0); + String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : ""; + throw new NotFoundException( + String.format("%1$s in %2$s%3$s is not a valid array resource.", + resVal.getValue(), mNames[index], resIdMessage)); } @Override -- cgit v1.1 From e91096c03dda225240b1487f019bf3d082854587 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 28 Sep 2015 18:03:18 -0700 Subject: Support databinding in listitem layouts. Add a wrapper around the PullParser to support stripping out databinding parts. Bug: http://b.android.com/187428 Change-Id: I88080d8f4108cb5ae27a137ad20c5dd7d516f3ea --- .../src/android/content/res/BridgeResources.java | 2 +- .../bridge/src/android/view/BridgeInflater.java | 2 +- .../layoutlib/bridge/android/BridgeContext.java | 2 +- .../layoutlib/bridge/impl/LayoutParserWrapper.java | 378 +++++++++++++++++++++ .../layoutlib/bridge/impl/ParserFactory.java | 19 +- .../bridge/impl/LayoutParserWrapperTest.java | 183 ++++++++++ 6 files changed, 579 insertions(+), 7 deletions(-) create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java create mode 100644 tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 163fbcb..7a69a06 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -404,7 +404,7 @@ public final class BridgeResources extends Resources { if (xml.isFile()) { // we need to create a pull parser around the layout XML file, and then // give that to our XmlBlockParser - parser = ParserFactory.create(xml); + parser = ParserFactory.create(xml, true); } } diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index f1726eb..5db1bde 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -206,7 +206,7 @@ public final class BridgeInflater extends LayoutInflater { File f = new File(value.getValue()); if (f.isFile()) { try { - XmlPullParser parser = ParserFactory.create(f); + XmlPullParser parser = ParserFactory.create(f, true); BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, bridgeContext, value.isFramework()); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 689e359..b2dc29a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -436,7 +436,7 @@ public final class BridgeContext extends Context { // we need to create a pull parser around the layout XML file, and then // give that to our XmlBlockParser try { - XmlPullParser parser = ParserFactory.create(xml); + XmlPullParser parser = ParserFactory.create(xml, true); // set the resource ref to have correct view cookies mBridgeInflater.setResourceReference(resource); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java new file mode 100644 index 0000000..3d2a238 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.impl; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.annotation.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A wrapper around XmlPullParser that can peek forward to inspect if the file is a data-binding + * layout and some parts need to be stripped. + */ +public class LayoutParserWrapper implements XmlPullParser { + + // Data binding constants. + private static final String TAG_LAYOUT = "layout"; + private static final String TAG_DATA = "data"; + private static final String DEFAULT = "default="; + + private final XmlPullParser mDelegate; + + // Storage for peeked values. + private boolean mPeeked; + private int mEventType; + private int mDepth; + private int mNext; + private List mAttributes; + private String mText; + private String mName; + + // Used to end the document before the actual parser ends. + private int mFinalDepth = -1; + private boolean mEndNow; + + public LayoutParserWrapper(XmlPullParser delegate) { + mDelegate = delegate; + } + + public LayoutParserWrapper peekTillLayoutStart() throws IOException, XmlPullParserException { + final int STATE_LAYOUT_NOT_STARTED = 0; // tag not encountered yet. + final int STATE_ROOT_NOT_STARTED = 1; // the main view root not found yet. + final int STATE_INSIDE_DATA = 2; // START_TAG for found, but not END_TAG. + + int state = STATE_LAYOUT_NOT_STARTED; + int dataDepth = -1; // depth of the tag. Should be two. + while (true) { + int peekNext = peekNext(); + switch (peekNext) { + case START_TAG: + if (state == STATE_LAYOUT_NOT_STARTED) { + if (mName.equals(TAG_LAYOUT)) { + state = STATE_ROOT_NOT_STARTED; + } else { + return this; // no layout tag in the file. + } + } else if (state == STATE_ROOT_NOT_STARTED) { + if (mName.equals(TAG_DATA)) { + state = STATE_INSIDE_DATA; + dataDepth = mDepth; + } else { + mFinalDepth = mDepth; + return this; + } + } + break; + case END_TAG: + if (state == STATE_INSIDE_DATA) { + if (mDepth <= dataDepth) { + state = STATE_ROOT_NOT_STARTED; + } + } + break; + case END_DOCUMENT: + // No layout start found. + return this; + } + // consume the peeked tag. + next(); + } + } + + private int peekNext() throws IOException, XmlPullParserException { + if (mPeeked) { + return mNext; + } + mEventType = mDelegate.getEventType(); + mNext = mDelegate.next(); + if (mEventType == START_TAG) { + int count = mDelegate.getAttributeCount(); + mAttributes = count > 0 ? new ArrayList(count) : + Collections.emptyList(); + for (int i = 0; i < count; i++) { + mAttributes.add(new Attribute(mDelegate.getAttributeNamespace(i), + mDelegate.getAttributeName(i), mDelegate.getAttributeValue(i))); + } + } + mDepth = mDelegate.getDepth(); + mText = mDelegate.getText(); + mName = mDelegate.getName(); + mPeeked = true; + return mNext; + } + + private void reset() { + mAttributes = null; + mText = null; + mName = null; + mPeeked = false; + } + + @Override + public int next() throws XmlPullParserException, IOException { + int returnValue; + int depth; + if (mPeeked) { + returnValue = mNext; + depth = mDepth; + reset(); + } else if (mEndNow) { + return END_DOCUMENT; + } else { + returnValue = mDelegate.next(); + depth = getDepth(); + } + if (returnValue == END_TAG && depth <= mFinalDepth) { + mEndNow = true; + } + return returnValue; + } + + @Override + public int getEventType() throws XmlPullParserException { + return mPeeked ? mEventType : mDelegate.getEventType(); + } + + @Override + public int getDepth() { + return mPeeked ? mDepth : mDelegate.getDepth(); + } + + @Override + public String getName() { + return mPeeked ? mName : mDelegate.getName(); + } + + @Override + public String getText() { + return mPeeked ? mText : mDelegate.getText(); + } + + @Override + public String getAttributeValue(@Nullable String namespace, String name) { + String returnValue = null; + if (mPeeked) { + if (mAttributes == null) { + if (mEventType != START_TAG) { + throw new IndexOutOfBoundsException("getAttributeValue() called when not at " + + "START_TAG."); + } else { + return null; + } + } else { + for (Attribute attribute : mAttributes) { + //noinspection StringEquality for nullness check. + if (attribute.name.equals(name) && (attribute.namespace == namespace || + attribute.namespace != null && attribute.namespace.equals(namespace))) { + returnValue = attribute.value; + break; + } + } + } + } else { + returnValue = mDelegate.getAttributeValue(namespace, name); + } + // Check if the value is bound via data-binding, if yes get the default value. + if (returnValue != null && mFinalDepth >= 0 && returnValue.startsWith("@{")) { + // TODO: Improve the detection of default keyword. + int i = returnValue.lastIndexOf(DEFAULT); + return i > 0 ? returnValue.substring(i + DEFAULT.length(), returnValue.length() - 1) + : null; + } + return returnValue; + } + + private static class Attribute { + @Nullable + public final String namespace; + public final String name; + public final String value; + + public Attribute(@Nullable String namespace, String name, String value) { + this.namespace = namespace; + this.name = name; + this.value = value; + } + } + + // Not affected by peeking. + + @Override + public void setFeature(String s, boolean b) throws XmlPullParserException { + mDelegate.setFeature(s, b); + } + + @Override + public void setProperty(String s, Object o) throws XmlPullParserException { + mDelegate.setProperty(s, o); + } + + @Override + public void setInput(InputStream inputStream, String s) throws XmlPullParserException { + mDelegate.setInput(inputStream, s); + } + + @Override + public void setInput(Reader reader) throws XmlPullParserException { + mDelegate.setInput(reader); + } + + @Override + public String getInputEncoding() { + return mDelegate.getInputEncoding(); + } + + @Override + public String getNamespace(String s) { + return mDelegate.getNamespace(s); + } + + @Override + public String getPositionDescription() { + return mDelegate.getPositionDescription(); + } + + @Override + public int getLineNumber() { + return mDelegate.getLineNumber(); + } + + @Override + public String getNamespace() { + return mDelegate.getNamespace(); + } + + @Override + public int getColumnNumber() { + return mDelegate.getColumnNumber(); + } + + // -- We don't care much about the methods that follow. + + @Override + public void require(int i, String s, String s1) throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean getFeature(String s) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public void defineEntityReplacementText(String s, String s1) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public Object getProperty(String s) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int nextToken() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int getNamespaceCount(int i) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getNamespacePrefix(int i) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getNamespaceUri(int i) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean isWhitespace() throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public char[] getTextCharacters(int[] ints) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getPrefix() { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean isEmptyElementTag() throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int getAttributeCount() { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeNamespace(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeName(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributePrefix(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeType(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean isAttributeDefault(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeValue(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String nextText() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int nextTag() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java index 6e67f59..e273b2c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java @@ -53,24 +53,35 @@ public class ParserFactory { @NonNull public static XmlPullParser create(@NonNull File f) throws XmlPullParserException, FileNotFoundException { - InputStream stream = new FileInputStream(f); - return create(stream, f.getName(), f.length()); + return create(f, false); } + public static XmlPullParser create(@NonNull File f, boolean isLayout) + throws XmlPullParserException, FileNotFoundException { + InputStream stream = new FileInputStream(f); + return create(stream, f.getName(), f.length(), isLayout); + } @NonNull public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) throws XmlPullParserException { - return create(stream, name, -1); + return create(stream, name, -1, false); } @NonNull private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name, - long size) throws XmlPullParserException { + long size, boolean isLayout) throws XmlPullParserException { XmlPullParser parser = instantiateParser(name); stream = readAndClose(stream, name, size); parser.setInput(stream, ENCODING); + if (isLayout) { + try { + return new LayoutParserWrapper(parser).peekTillLayoutStart(); + } catch (IOException e) { + throw new XmlPullParserException(null, parser, e); + } + } return parser; } diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java new file mode 100644 index 0000000..2c33862 --- /dev/null +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.impl; + +import org.junit.Test; +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.StringReader; + +import static com.android.SdkConstants.NS_RESOURCES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.START_TAG; + + +public class LayoutParserWrapperTest { + @Test + @SuppressWarnings("StatementWithEmptyBody") // some for loops need to be empty statements. + public void testDataBindingLayout() throws Exception { + LayoutParserWrapper parser = getParserFromString(sDataBindingLayout); + parser.peekTillLayoutStart(); + assertEquals("Expected START_TAG", START_TAG, parser.next()); + assertEquals("RelativeLayout", parser.getName()); + for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT; + next = parser.next()); + assertEquals("Expected START_TAG", START_TAG, parser.getEventType()); + assertEquals("TextView", parser.getName()); + assertEquals("layout_width incorrect for first text view.", "wrap_content", + parser.getAttributeValue(NS_RESOURCES, "layout_width")); + // Ensure that data-binding part is stripped. + assertEquals("Bound attribute android:text incorrect", "World", + parser.getAttributeValue(NS_RESOURCES, "text")); + assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first", + parser.getAttributeValue(NS_RESOURCES, "id")); + for (int next = parser.next(); + (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT; + next = parser.next()); + assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType()); + assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next()); + } + + @Test + @SuppressWarnings("StatementWithEmptyBody") + public void testNonDataBindingLayout() throws Exception { + LayoutParserWrapper parser = getParserFromString(sNonDataBindingLayout); + parser.peekTillLayoutStart(); + assertEquals("Expected START_TAG", START_TAG, parser.next()); + assertEquals("RelativeLayout", parser.getName()); + for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT; + next = parser.next()); + assertEquals("Expected START_TAG", START_TAG, parser.getEventType()); + assertEquals("TextView", parser.getName()); + assertEquals("layout_width incorrect for first text view.", "wrap_content", + parser.getAttributeValue(NS_RESOURCES, "layout_width")); + // Ensure that value isn't modified. + assertEquals("Bound attribute android:text incorrect", "@{user.firstName,default=World}", + parser.getAttributeValue(NS_RESOURCES, "text")); + assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first", + parser.getAttributeValue(NS_RESOURCES, "id")); + for (int next = parser.next(); + (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT; + next = parser.next()); + assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType()); + assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next()); + } + + private static LayoutParserWrapper getParserFromString(String layoutContent) throws + XmlPullParserException { + XmlPullParser parser = new KXmlParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new StringReader(layoutContent)); + return new LayoutParserWrapper(parser); + } + + private static final String sDataBindingLayout = + //language=XML + "\n" + + "\n" + + "\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + "\n" + + " \n" + + "\n" + + " \n" + + "\n" + + " \n" + + " \n" + + ""; + + private static final String sNonDataBindingLayout = + //language=XML + "\n" + + "\n" + + " \n" + + "\n" + + " \n" + + "\n" + + " \n" + + ""; +} -- cgit v1.1 From 6dac0d4b7c55da32cb5deb9b589b1acb43cb536d Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Thu, 24 Sep 2015 14:52:59 +0100 Subject: Reduce the amount of allocations on Paint_Delegate When the text properties of the Paint object are updated, or when .set() is called, updateFontObject will be called. This method is currently at the top of the methods for number of allocations. This CL reduces the number of calls to updateFontObject by a 90%, reducing the number of Font allocations. Change-Id: I3e232f9e6e230da8ecdd19921faadf2d3f4ff763 --- .../src/android/graphics/FontFamily_Delegate.java | 8 ++- .../src/android/graphics/Paint_Delegate.java | 72 +++++++++++++++++----- 2 files changed, 61 insertions(+), 19 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java index 857e6d0..c7b24bc 100644 --- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java @@ -178,7 +178,9 @@ public class FontFamily_Delegate { desiredStyle.mIsItalic = isItalic; FontInfo bestFont = null; int bestMatch = Integer.MAX_VALUE; - for (FontInfo font : mFonts) { + //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int i = 0, n = mFonts.size(); i < n; i++) { + FontInfo font = mFonts.get(i); int match = computeMatch(font, desiredStyle); if (match < bestMatch) { bestMatch = match; @@ -415,7 +417,9 @@ public class FontFamily_Delegate { boolean isItalic = fontInfo.mIsItalic; // The list is usually just two fonts big. So iterating over all isn't as bad as it looks. // It's biggest for roboto where the size is 12. - for (FontInfo font : mFonts) { + //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int i = 0, n = mFonts.size(); i < n; i++) { + FontInfo font = mFonts.get(i); if (font.mWeight == weight && font.mIsItalic == isItalic) { return false; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 65b65ec..a545283 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -480,8 +480,10 @@ public class Paint_Delegate { return; } - delegate.mTextSize = textSize; - delegate.updateFontObject(); + if (delegate.mTextSize != textSize) { + delegate.mTextSize = textSize; + delegate.updateFontObject(); + } } @LayoutlibDelegate @@ -503,8 +505,10 @@ public class Paint_Delegate { return; } - delegate.mTextScaleX = scaleX; - delegate.updateFontObject(); + if (delegate.mTextScaleX != scaleX) { + delegate.mTextScaleX = scaleX; + delegate.updateFontObject(); + } } @LayoutlibDelegate @@ -526,8 +530,10 @@ public class Paint_Delegate { return; } - delegate.mTextSkewX = skewX; - delegate.updateFontObject(); + if (delegate.mTextSkewX != skewX) { + delegate.mTextSkewX = skewX; + delegate.updateFontObject(); + } } @LayoutlibDelegate @@ -897,9 +903,12 @@ public class Paint_Delegate { return 0; } - delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); - delegate.mNativeTypeface = typeface; - delegate.updateFontObject(); + Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface); + if (delegate.mTypeface != typefaceDelegate || delegate.mNativeTypeface != typeface) { + delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); + delegate.mNativeTypeface = typeface; + delegate.updateFontObject(); + } return typeface; } @@ -1214,13 +1223,31 @@ public class Paint_Delegate { mCap = paint.mCap; mJoin = paint.mJoin; mTextAlign = paint.mTextAlign; - mTypeface = paint.mTypeface; - mNativeTypeface = paint.mNativeTypeface; + + boolean needsFontUpdate = false; + if (mTypeface != paint.mTypeface || mNativeTypeface != paint.mNativeTypeface) { + mTypeface = paint.mTypeface; + mNativeTypeface = paint.mNativeTypeface; + needsFontUpdate = true; + } + + if (mTextSize != paint.mTextSize) { + mTextSize = paint.mTextSize; + needsFontUpdate = true; + } + + if (mTextScaleX != paint.mTextScaleX) { + mTextScaleX = paint.mTextScaleX; + needsFontUpdate = true; + } + + if (mTextSkewX != paint.mTextSkewX) { + mTextSkewX = paint.mTextSkewX; + needsFontUpdate = true; + } + mStrokeWidth = paint.mStrokeWidth; mStrokeMiter = paint.mStrokeMiter; - mTextSize = paint.mTextSize; - mTextScaleX = paint.mTextScaleX; - mTextSkewX = paint.mTextSkewX; mXfermode = paint.mXfermode; mColorFilter = paint.mColorFilter; mShader = paint.mShader; @@ -1228,7 +1255,10 @@ public class Paint_Delegate { mMaskFilter = paint.mMaskFilter; mRasterizer = paint.mRasterizer; mHintingMode = paint.mHintingMode; - updateFontObject(); + + if (needsFontUpdate) { + updateFontObject(); + } } private void reset() { @@ -1264,10 +1294,18 @@ public class Paint_Delegate { // Get the fonts from the TypeFace object. List fonts = mTypeface.getFonts(mFontVariant); + if (fonts.isEmpty()) { + mFonts = Collections.emptyList(); + return; + } + // create new font objects as well as FontMetrics, based on the current text size // and skew info. - ArrayList infoList = new ArrayList(fonts.size()); - for (Font font : fonts) { + int nFonts = fonts.size(); + ArrayList infoList = new ArrayList(nFonts); + //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int i = 0; i < nFonts; i++) { + Font font = fonts.get(i); if (font == null) { // If the font is null, add null to infoList. When rendering the text, if this // null is reached, a warning will be logged. -- cgit v1.1 From 1bd276f6f7ddce7959dc2e9e6f480d9be1e26fef Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 30 Sep 2015 12:49:42 -0700 Subject: Don't break error string (easier grepping) Change-Id: If52097ba90481644707dd0323ec63ca3ddf0986b --- .../src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java index 3d2a238..71e7fd2 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java @@ -176,8 +176,7 @@ public class LayoutParserWrapper implements XmlPullParser { if (mPeeked) { if (mAttributes == null) { if (mEventType != START_TAG) { - throw new IndexOutOfBoundsException("getAttributeValue() called when not at " + - "START_TAG."); + throw new IndexOutOfBoundsException("getAttributeValue() called when not at START_TAG."); } else { return null; } -- cgit v1.1 From e5d3baf22f046d0a579128fc258f6b92e700ca02 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 5 Oct 2015 14:37:54 -0700 Subject: LayoutLib: Fix bitmap scaling. Bug: http://b.android.com/187939 Change-Id: I20bf61746e8af3bb87ba9cfcc9bf58854ac7c5be --- .../layoutlib/bridge/src/android/content/res/BridgeResources.java | 8 +++++++- .../bridge/src/android/graphics/BitmapFactory_Delegate.java | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index f4b1f2c..0e39243 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -18,6 +18,7 @@ package android.content.res; import com.android.SdkConstants; import com.android.ide.common.rendering.api.ArrayResourceValue; +import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.ResourceValue; @@ -661,13 +662,18 @@ public final class BridgeResources extends Resources { Pair value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { - String v = value.getSecond().getValue(); + ResourceValue resVal = value.getSecond(); + String v = resVal.getValue(); if (v != null) { if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue, false /*requireUnit*/)) { return; } + if (resVal instanceof DensityBasedResourceValue) { + outValue.density = + ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue(); + } // else it's a string outValue.type = TypedValue.TYPE_STRING; diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index d858953..60514b6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -59,6 +59,7 @@ import java.util.Set; if (opts.inPremultiplied) { bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED); } + opts.inScaled = false; } try { -- cgit v1.1 From 081cebf52b19e848c07fb781b35fa1f96695c311 Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Wed, 7 Oct 2015 12:33:44 +0100 Subject: Implement Path.approximate and add PathMeasure support PathMeasure is needed to use the trimPath* properties in drawables. These properties are used in the vector drawable for indeterminate progress bars in Material so this is needed to be able to render them correctly in the preview. PathMeasure makes use of Path.approximate to calculate which segments to paint. Change-Id: Ic513f0a30a6aac0317f7c13cd75e9154c37405c8 --- .../animation/AnimatorInflater_Delegate.java | 59 ------ .../src/android/graphics/PathMeasure_Delegate.java | 210 +++++++++++++++++++++ .../bridge/src/android/graphics/Path_Delegate.java | 79 ++++---- .../android/tools/layoutlib/create/CreateInfo.java | 2 +- 4 files changed, 246 insertions(+), 104 deletions(-) delete mode 100644 tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java create mode 100644 tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java b/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java deleted file mode 100644 index 4475fa4..0000000 --- a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.animation; - -import com.android.tools.layoutlib.annotations.LayoutlibDelegate; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.content.res.Resources.Theme; -import android.util.AttributeSet; - -/** - * Delegate providing alternate implementation to static methods in {@link AnimatorInflater}. - */ -public class AnimatorInflater_Delegate { - - @LayoutlibDelegate - /*package*/ static Animator loadAnimator(Context context, int id) - throws NotFoundException { - return loadAnimator(context.getResources(), context.getTheme(), id); - } - - @LayoutlibDelegate - /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id) - throws NotFoundException { - return loadAnimator(resources, theme, id, 1); - } - - @LayoutlibDelegate - /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id, - float pathErrorScale) throws NotFoundException { - // This is a temporary fix to http://b.android.com/77865. This skips loading the - // animation altogether. - // TODO: Remove this override when Path.approximate() is supported. - return new FakeAnimator(); - } - - @LayoutlibDelegate - /*package*/ static ValueAnimator loadAnimator(Resources res, Theme theme, - AttributeSet attrs, ValueAnimator anim, float pathErrorScale) - throws NotFoundException { - return AnimatorInflater.loadAnimator_Original(res, theme, attrs, anim, pathErrorScale); - } -} diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java new file mode 100644 index 0000000..dd2978f --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; + +/** + * Delegate implementing the native methods of {@link android.graphics.PathMeasure} + *

+ * Through the layoutlib_create tool, the original native methods of PathMeasure have been + * replaced by + * calls to methods of the same name in this delegate class. + *

+ * This class behaves like the original native implementation, but in Java, keeping previously + * native data into its own objects and mapping them to int that are sent back and forth between it + * and the original PathMeasure class. + * + * @see DelegateManager + */ +public final class PathMeasure_Delegate { + // ---- delegate manager ---- + private static final DelegateManager sManager = + new DelegateManager(PathMeasure_Delegate.class); + + // ---- delegate data ---- + // This governs how accurate the approximation of the Path is. + private static final float PRECISION = 0.002f; + + /** + * Array containing the path points components. There are three components for each point: + *

+ */ + private float mPathPoints[]; + private long mNativePath; + + private PathMeasure_Delegate(long native_path, boolean forceClosed) { + mNativePath = native_path; + if (forceClosed && mNativePath != 0) { + // Copy the path and call close + mNativePath = Path_Delegate.init2(native_path); + Path_Delegate.native_close(mNativePath); + } + + mPathPoints = + mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : null; + } + + @LayoutlibDelegate + /*package*/ static long native_create(long native_path, boolean forceClosed) { + return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed)); + } + + @LayoutlibDelegate + /*package*/ static void native_destroy(long native_instance) { + sManager.removeJavaReferenceFor(native_instance); + } + + @LayoutlibDelegate + /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[], + float tan[]) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "PathMeasure.getPostTan is not supported.", null, null); + return false; + } + + @LayoutlibDelegate + /*package*/ static boolean native_getMatrix(long native_instance, float distance, long + native_matrix, int flags) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "PathMeasure.getMatrix is not supported.", null, null); + return false; + } + + @LayoutlibDelegate + /*package*/ static boolean native_nextContour(long native_instance) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "PathMeasure.nextContour is not supported.", null, null); + return false; + } + + @LayoutlibDelegate + /*package*/ static void native_setPath(long native_instance, long native_path, boolean + forceClosed) { + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + if (forceClosed && native_path != 0) { + // Copy the path and call close + native_path = Path_Delegate.init2(native_path); + Path_Delegate.native_close(native_path); + } + pathMeasure.mNativePath = native_path; + pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION); + } + + @LayoutlibDelegate + /*package*/ static float native_getLength(long native_instance) { + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + if (pathMeasure.mPathPoints == null) { + return 0; + } + + float length = 0; + int nPoints = pathMeasure.mPathPoints.length / 3; + for (int i = 1; i < nPoints; i++) { + length += Point2D.distance( + pathMeasure.mPathPoints[(i - 1) * 3 + 1], + pathMeasure.mPathPoints[(i - 1) * 3 + 2], + pathMeasure.mPathPoints[i*3 + 1], + pathMeasure.mPathPoints[i*3 + 2]); + } + + return length; + } + + @LayoutlibDelegate + /*package*/ static boolean native_isClosed(long native_instance) { + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath); + if (path == null) { + return false; + } + + PathIterator pathIterator = path.getJavaShape().getPathIterator(null); + + int type = 0; + float segment[] = new float[6]; + while (!pathIterator.isDone()) { + type = pathIterator.currentSegment(segment); + pathIterator.next(); + } + + // A path is a closed path if the last element is SEG_CLOSE + return type == PathIterator.SEG_CLOSE; + } + + @LayoutlibDelegate + /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD, + long native_dst_path, boolean startWithMoveTo) { + if (startD < 0) { + startD = 0; + } + + if (startD >= stopD) { + return false; + } + + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + if (pathMeasure.mPathPoints == null) { + return false; + } + + float accLength = 0; + boolean isZeroLength = true; // Whether the output has zero length or not + int nPoints = pathMeasure.mPathPoints.length / 3; + for (int i = 0; i < nPoints; i++) { + float x = pathMeasure.mPathPoints[i * 3 + 1]; + float y = pathMeasure.mPathPoints[i * 3 + 2]; + if (accLength >= startD && accLength <= stopD) { + if (startWithMoveTo) { + startWithMoveTo = false; + Path_Delegate.native_moveTo(native_dst_path, x, y); + } else { + isZeroLength = false; + Path_Delegate.native_lineTo(native_dst_path, x, y); + } + } + + if (i > 0) { + accLength += Point2D.distance( + pathMeasure.mPathPoints[(i - 1) * 3 + 1], + pathMeasure.mPathPoints[(i - 1) * 3 + 2], + pathMeasure.mPathPoints[i * 3 + 1], + pathMeasure.mPathPoints[i * 3 + 2]); + } + } + + return !isZeroLength; + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 3c9a062..a2a53fe 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -36,6 +36,7 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; +import java.util.ArrayList; /** * Delegate implementing the native methods of android.graphics.Path @@ -173,11 +174,8 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static boolean native_isEmpty(long nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); - if (pathDelegate == null) { - return true; - } + return pathDelegate == null || pathDelegate.isEmpty(); - return pathDelegate.isEmpty(); } @LayoutlibDelegate @@ -488,54 +486,44 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static float[] native_approximate(long nPath, float error) { - Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported", - null); Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { return null; } - PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null); - float[] tmp = new float[6]; - float[] coords = new float[6]; - boolean isFirstPoint = true; - while (!pathIterator.isDone()) { - int type = pathIterator.currentSegment(tmp); - switch (type) { - case PathIterator.SEG_MOVETO: - case PathIterator.SEG_LINETO: - store(tmp, coords, 1, isFirstPoint); - break; - case PathIterator.SEG_QUADTO: - store(tmp, coords, 2, isFirstPoint); - break; - case PathIterator.SEG_CUBICTO: - store(tmp, coords, 3, isFirstPoint); - break; - case PathIterator.SEG_CLOSE: - // No points returned. + // Get a FlatteningIterator + PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error); + + float segment[] = new float[6]; + float totalLength = 0; + ArrayList points = new ArrayList(); + Point2D.Float previousPoint = null; + while (!iterator.isDone()) { + int type = iterator.currentSegment(segment); + Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]); + // MoveTo shouldn't affect the length + if (previousPoint != null && type != PathIterator.SEG_MOVETO) { + totalLength += currentPoint.distance(previousPoint); } - isFirstPoint = false; - pathIterator.next(); + previousPoint = currentPoint; + points.add(currentPoint); + iterator.next(); } - if (isFirstPoint) { - // No points found - return new float[0]; - } else { - return coords; - } - } - private static void store(float[] src, float[] dst, int count, boolean isFirst) { - if (isFirst) { - dst[0] = 0; // fraction - dst[1] = src[0]; // abscissa - dst[2] = src[1]; // ordinate - } - if (count > 1 || !isFirst) { - dst[3] = 1; - dst[4] = src[2 * count - 2]; - dst[5] = src[2 * count - 1]; + int nPoints = points.size(); + float[] result = new float[nPoints * 3]; + previousPoint = null; + for (int i = 0; i < nPoints; i++) { + Point2D.Float point = points.get(i); + float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f; + result[i * 3] = distance / totalLength; + result[i * 3 + 1] = point.x; + result[i * 3 + 2] = point.y; + + totalLength += distance; + previousPoint = point; } + + return result; } // ---- Private helper methods ---- @@ -735,6 +723,9 @@ public final class Path_Delegate { */ private void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) { + if (isEmpty()) { + mPath.moveTo(0, 0); + } mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 484240f..9244310 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -157,7 +157,6 @@ public final class CreateInfo implements ICreateInfo { * The list of methods to rewrite as delegates. */ public final static String[] DELEGATE_METHODS = new String[] { - "android.animation.AnimatorInflater#loadAnimator", // TODO: remove when Path.approximate() is supported. "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", @@ -235,6 +234,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.Path", "android.graphics.PathDashPathEffect", "android.graphics.PathEffect", + "android.graphics.PathMeasure", "android.graphics.PixelXorXfermode", "android.graphics.PorterDuffColorFilter", "android.graphics.PorterDuffXfermode", -- cgit v1.1 From 4064e118e2d738ddd13366bf08210bb8de9e32ca Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 7 Oct 2015 09:26:46 -0700 Subject: Skip drawing empty shapes to keep Apple JRE happy. Bug: http://b.android.com/178278 Change-Id: I693912678ed79e9c6690560b618a605da5a807c6 --- tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index f8b3739..64cd503 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -35,6 +35,8 @@ import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; @@ -707,6 +709,12 @@ public final class Canvas_Delegate { @Override public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) { Shape shape = pathDelegate.getJavaShape(); + Rectangle2D bounds = shape.getBounds2D(); + if (bounds.isEmpty()) { + // Apple JRE 1.6 doesn't like drawing empty shapes. + // http://b.android.com/178278 + return; + } int style = paintDelegate.getStyle(); if (style == Paint.Style.FILL.nativeInt || -- cgit v1.1 From 9969f858c34ba7e35a977c819f2d1e24de586990 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 7 Oct 2015 11:47:50 -0700 Subject: LayoutLib: Fix ring rendering. Bug: http://b.android.com/65503 Change-Id: I6ed901703cffee345f3083ea3ddeb52a28f4ac64 --- .../drawable/GradientDrawable_Delegate.java | 73 ++++++++++++++++++++++ .../android/tools/layoutlib/create/CreateInfo.java | 1 + 2 files changed, 74 insertions(+) create mode 100644 tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java new file mode 100644 index 0000000..a3ad2aa --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.drawable; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.graphics.Path; +import android.graphics.drawable.GradientDrawable.GradientState; + +import java.lang.reflect.Field; + +/** + * Delegate implementing the native methods of {@link GradientDrawable} + * + * Through the layoutlib_create tool, the original native methods of GradientDrawable have been + * replaced by calls to methods of the same name in this delegate class. + */ +public class GradientDrawable_Delegate { + + /** + * The ring can be built either by drawing full circles, or by drawing arcs in case the + * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path + * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle. + */ + @LayoutlibDelegate + /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) { + boolean useLevel = st.mUseLevelForShape; + int level = thisDrawable.getLevel(); + // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel() + float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f; + Field mLevel = null; + if (sweep >= 360 || sweep <= -360) { + st.mUseLevelForShape = true; + // Use reflection to set the value of the field to prevent setting the drawable to + // dirty again. + try { + mLevel = Drawable.class.getDeclaredField("mLevel"); + mLevel.setAccessible(true); + mLevel.setInt(thisDrawable, 9999); // set to one less than max. + } catch (NoSuchFieldException e) { + // The field has been removed in a recent framework change. Fall back to old + // buggy behaviour. + } catch (IllegalAccessException e) { + // We've already set the field to be accessible. + assert false; + } + } + Path path = thisDrawable.buildRing_Original(st); + st.mUseLevelForShape = useLevel; + if (mLevel != null) { + try { + mLevel.setInt(thisDrawable, level); + } catch (IllegalAccessException e) { + assert false; + } + } + return path; + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 9244310..c9bc62e 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -166,6 +166,7 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.TypedArray#getValueAt", "android.content.res.TypedArray#obtain", "android.graphics.BitmapFactory#finishDecode", + "android.graphics.drawable.GradientDrawable#buildRing", "android.graphics.Typeface#getSystemFontConfigLocation", "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", -- cgit v1.1 From 2f01cf2ea6d8cc07711fccd6157f33226a77da93 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Fri, 9 Oct 2015 14:54:19 -0700 Subject: LayoutLib: Fix android:dependency for preferences. Bug: http://b.android.com/189314 Change-Id: I229632d11a2529e865f3f2e8eca3a78b47ad462c --- .../bridge/src/android/preference/Preference_Delegate.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java index 49ee642..2e44a77 100644 --- a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java +++ b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java @@ -29,9 +29,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ListView; -import java.util.HashMap; -import java.util.Map; - /** * Delegate that provides implementation for native methods in {@link Preference} *

@@ -59,9 +56,9 @@ public class Preference_Delegate { */ public static View inflatePreference(Context context, XmlPullParser parser, ViewGroup root) { PreferenceManager pm = new PreferenceManager(context); - PreferenceScreen ps = pm.getPreferenceScreen(); PreferenceInflater inflater = new BridgePreferenceInflater(context, pm); - ps = (PreferenceScreen) inflater.inflate(parser, ps, true); + PreferenceScreen ps = (PreferenceScreen) inflater.inflate(parser, null, true); + pm.setPreferences(ps); ListView preferenceView = createContainerView(context, root); ps.bind(preferenceView); return preferenceView; -- cgit v1.1 From 91fa3ba4cc8acd4caeeb88f90ce39198b5fea414 Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Tue, 3 Nov 2015 14:59:23 +0000 Subject: Remove auto-boxing during the inflate Change-Id: I3f9ba5d78363f878fa0c58ca8cbf736bd43fe1c0 --- .../src/com/android/layoutlib/bridge/android/BridgeContext.java | 6 +++--- .../bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index b2dc29a..f2d214c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -409,7 +409,7 @@ public final class BridgeContext extends Context { pushParser(blockParser); return Pair.of( mBridgeInflater.inflate(blockParser, parent, attachToRoot), - true); + Boolean.TRUE); } finally { popParser(); } @@ -447,7 +447,7 @@ public final class BridgeContext extends Context { pushParser(blockParser); return Pair.of( mBridgeInflater.inflate(blockParser, parent, attachToRoot), - false); + Boolean.FALSE); } finally { popParser(); } @@ -470,7 +470,7 @@ public final class BridgeContext extends Context { resource.getName()), null); } - return Pair.of(null, false); + return Pair.of(null, Boolean.FALSE); } @SuppressWarnings("deprecation") diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 567002e..42e55e2 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -117,11 +117,11 @@ abstract class CustomBar extends LinearLayout { density = iconLoader.getDensity(); String path = iconLoader.getPath(); // look for a cached bitmap - Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/); + Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/); if (bitmap == null) { try { bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density); - Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/); + Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/); } catch (IOException e) { return; } -- cgit v1.1 From 631c2ffd6ff1bfa7463d29a39a7ee74be019b3f9 Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Wed, 4 Nov 2015 11:35:34 +0000 Subject: Update StaticLayout_Delegate to use new nLoadHyphenator Small fix to update call parameters Change-Id: I4322aa212493ec2970ae17ec1918b9b7e2f8e93a --- tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java | 5 +++-- tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java index 5a59597..44ce731 100644 --- a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java @@ -20,9 +20,10 @@ import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.io.File; +import java.nio.ByteBuffer; /** - * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout} + * Delegate that overrides implementation for certain methods in {@link android.text.Hyphenator} *

* Through the layoutlib_create tool, selected methods of StaticLayout have been replaced * by calls to methods of the same name in this delegate class. @@ -38,7 +39,7 @@ public class Hyphenator_Delegate { return null; } - /*package*/ static long loadHyphenator(String patternData) { + /*package*/ static long loadHyphenator(ByteBuffer buf, int offset) { return sDelegateManager.addNewDelegate(new Hyphenator_Delegate()); } } diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index 1b0ba51..65c0a07 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -13,6 +13,7 @@ import android.icu.util.ULocale; import android.text.Primitive.PrimitiveType; import android.text.StaticLayout.LineBreaks; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -52,8 +53,8 @@ public class StaticLayout_Delegate { } @LayoutlibDelegate - /*package*/ static long nLoadHyphenator(String patternData) { - return Hyphenator_Delegate.loadHyphenator(patternData); + /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset) { + return Hyphenator_Delegate.loadHyphenator(buf, offset); } @LayoutlibDelegate -- cgit v1.1 From c9b8f95c99688c71184d90f53dcf90e52816d02f Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 4 Nov 2015 17:49:16 -0800 Subject: Fix Switch colors The conversion of the PorterDuff mode from int to enum was incorrect resulting in the wrong PorterDuff Mode being used. Change-Id: I7607d505b6e4382e6101869971154f6a77db279c --- .../com/android/layoutlib/bridge/impl/PorterDuffUtility.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java index 9588035..80d7c68 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java @@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge; import android.graphics.BlendComposite; import android.graphics.BlendComposite.BlendingMode; +import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffColorFilter_Delegate; import android.graphics.PorterDuffXfermode_Delegate; @@ -34,6 +35,8 @@ import java.awt.Composite; */ public final class PorterDuffUtility { + private static final int MODES_COUNT = Mode.values().length; + // Make the class non-instantiable. private PorterDuffUtility() { } @@ -43,12 +46,11 @@ public final class PorterDuffUtility { * {@link Mode#SRC_OVER} for invalid modes. */ public static Mode getPorterDuffMode(int porterDuffMode) { - Mode[] values = Mode.values(); - if (porterDuffMode >= 0 && porterDuffMode < values.length) { - return values[porterDuffMode]; + if (porterDuffMode >= 0 && porterDuffMode < MODES_COUNT) { + return PorterDuff.intToMode(porterDuffMode); } Bridge.getLog().error(LayoutLog.TAG_BROKEN, - String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/); + String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null); assert false; return Mode.SRC_OVER; } -- cgit v1.1 From d30c141a2dee881fd83e1415a50161def1acf227 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Tue, 20 Oct 2015 17:29:44 -0700 Subject: Improve error reporting on Exceptions in fw views. When there's an exception during the inflation of a framework view (for example invalid attributes), report the exception correctly. The earlier behaviour assumed the exception to be a ClassNotFoundException and tried to load it from the user's project. This is not longer the case. Also, update the MockView class to a FrameLayout with a single TextView. This means that the MockView is a ViewGroup and will not choke when someone attempts to add a View to it (although, the view will be silently dropped). Change-Id: Ice003817ceb627ebfbbbb245ab6be10f9141e728 --- .../bridge/src/android/view/BridgeInflater.java | 43 +++++++----- .../src/com/android/layoutlib/bridge/MockView.java | 77 ++++++++++++++++++---- 2 files changed, 91 insertions(+), 29 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 5db1bde..723e827 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -23,6 +23,7 @@ import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; +import com.android.layoutlib.bridge.MockView; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil; @@ -126,6 +127,9 @@ public final class BridgeInflater extends LayoutInflater { if (view == null) { view = loadCustomView(name, attrs); } + } catch (InflateException e) { + // Don't catch the InflateException below as that results in hiding the real cause. + throw e; } catch (Exception e) { // Wrap the real exception in a ClassNotFoundException, so that the calling method // can deal with it. @@ -154,23 +158,30 @@ public final class BridgeInflater extends LayoutInflater { } ta.recycle(); } - final Object lastContext = mConstructorArgs[0]; - mConstructorArgs[0] = context; - // try to load the class from using the custom view loader - try { - view = loadCustomView(name, attrs); - } catch (Exception e2) { - // Wrap the real exception in an InflateException so that the calling - // method can deal with it. - InflateException exception = new InflateException(); - if (!e2.getClass().equals(ClassNotFoundException.class)) { - exception.initCause(e2); - } else { - exception.initCause(e); + if (!(e.getCause() instanceof ClassNotFoundException)) { + // There is some unknown inflation exception in inflating a View that was found. + view = new MockView(context, attrs); + ((MockView) view).setText(name); + Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null); + } else { + final Object lastContext = mConstructorArgs[0]; + mConstructorArgs[0] = context; + // try to load the class from using the custom view loader + try { + view = loadCustomView(name, attrs); + } catch (Exception e2) { + // Wrap the real exception in an InflateException so that the calling + // method can deal with it. + InflateException exception = new InflateException(); + if (!e2.getClass().equals(ClassNotFoundException.class)) { + exception.initCause(e2); + } else { + exception.initCause(e); + } + throw exception; + } finally { + mConstructorArgs[0] = lastContext; } - throw exception; - } finally { - mConstructorArgs[0] = lastContext; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java index 44a9aad..d392f21 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java @@ -17,39 +17,90 @@ package com.android.layoutlib.bridge; import android.content.Context; -import android.graphics.Canvas; import android.util.AttributeSet; import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.TextView; /** * Base class for mocked views. - * - * TODO: implement onDraw and draw a rectangle in a random color with the name of the class - * (or better the id of the view). + *

+ * FrameLayout with a single TextView. Doesn't allow adding any other views to itself. */ -public class MockView extends TextView { +public class MockView extends FrameLayout { + + private final TextView mView; + + public MockView(Context context) { + this(context, null); + } public MockView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public MockView(Context context, AttributeSet attrs, int defStyle) { - this(context, attrs, defStyle, 0); + public MockView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); } public MockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - - setText(this.getClass().getSimpleName()); - setTextColor(0xFF000000); + mView = new TextView(context, attrs); + mView.setTextColor(0xFF000000); setGravity(Gravity.CENTER); + setText(getClass().getSimpleName()); + addView(mView); + setBackgroundColor(0xFF7F7F7F); + } + + // Only allow adding one TextView. + @Override + public void addView(View child) { + if (child == mView) { + super.addView(child); + } + } + + @Override + public void addView(View child, int index) { + if (child == mView) { + super.addView(child, index); + } } @Override - public void onDraw(Canvas canvas) { - canvas.drawARGB(0xFF, 0x7F, 0x7F, 0x7F); + public void addView(View child, int width, int height) { + if (child == mView) { + super.addView(child, width, height); + } + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (child == mView) { + super.addView(child, params); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child == mView) { + super.addView(child, index, params); + } + } + + // The following methods are called by the IDE via reflection, and should be considered part + // of the API. + // Historically, MockView used to be a textView and had these methods. Now, we simply delegate + // them to the contained textView. + + public void setText(CharSequence text) { + mView.setText(text); + } - super.onDraw(canvas); + public void setGravity(int gravity) { + mView.setGravity(gravity); } } -- cgit v1.1 From 6777f54fa44341dd4b23456c97d97c6e4ffe915f Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Fri, 13 Nov 2015 13:58:14 +0000 Subject: Fix delegation of methods within inner static classes Currently, delegation of inner static classes methods is broken since the rewritten method tries to pass an instance to the outer class. This updates the method generation to only pass the reference if the inner class is not static. Change-Id: I8493929cafdbd80968989b422af0f956fa65681a --- .../layoutlib/create/DelegateClassAdapter.java | 24 +++++++-- .../layoutlib/create/DelegateMethodAdapter.java | 8 ++- .../layoutlib/create/DelegateClassAdapterTest.java | 58 ++++++++++++++++++++++ .../layoutlib/create/dataclass/OuterClass.java | 10 ++++ .../OuterClass_StaticInnerClass_Delegate.java | 30 +++++++++++ 5 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java (limited to 'tools') diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java index ae4a57d..7ef7566 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java @@ -17,6 +17,7 @@ package com.android.tools.layoutlib.create; import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -40,6 +41,7 @@ public class DelegateClassAdapter extends ClassVisitor { private final String mClassName; private final Set mDelegateMethods; private final Log mLog; + private boolean mIsStaticInnerClass; /** * Creates a new {@link DelegateClassAdapter} that can transform some methods @@ -62,16 +64,30 @@ public class DelegateClassAdapter extends ClassVisitor { mLog = log; mClassName = className; mDelegateMethods = delegateMethods; + // If this is an inner class, by default, we assume it's static. If it's not we will detect + // by looking at the fields (see visitField) + mIsStaticInnerClass = className.contains("$"); } //---------------------------------- // Methods from the ClassAdapter @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + if (mIsStaticInnerClass && "this$0".equals(name)) { + // Having a "this$0" field, proves that this class is not a static inner class. + mIsStaticInnerClass = false; + } + + return super.visitField(access, name, desc, signature, value); + } + + @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; + boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0; boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) || @@ -96,7 +112,8 @@ public class DelegateClassAdapter extends ClassVisitor { MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions); DelegateMethodAdapter a = new DelegateMethodAdapter( - mLog, null, mwDelegate, mClassName, name, desc, isStatic); + mLog, null, mwDelegate, mClassName, name, desc, isStaticMethod, + mIsStaticInnerClass); // A native has no code to visit, so we need to generate it directly. a.generateDelegateCode(); @@ -120,6 +137,7 @@ public class DelegateClassAdapter extends ClassVisitor { desc, signature, exceptions); return new DelegateMethodAdapter( - mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic); + mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStaticMethod, + mIsStaticInnerClass); } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java index 12690db..cca9e57 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java @@ -85,6 +85,8 @@ class DelegateMethodAdapter extends MethodVisitor { private String mDesc; /** True if the original method is static. */ private final boolean mIsStatic; + /** True if the method is contained in a static inner class */ + private final boolean mIsStaticInnerClass; /** The internal class name (e.g. com/android/SomeClass$InnerClass.) */ private final String mClassName; /** The method name. */ @@ -120,7 +122,8 @@ class DelegateMethodAdapter extends MethodVisitor { String className, String methodName, String desc, - boolean isStatic) { + boolean isStatic, + boolean isStaticClass) { super(Opcodes.ASM4); mLog = log; mOrgWriter = mvOriginal; @@ -129,6 +132,7 @@ class DelegateMethodAdapter extends MethodVisitor { mMethodName = methodName; mDesc = desc; mIsStatic = isStatic; + mIsStaticInnerClass = isStaticClass; } /** @@ -206,7 +210,7 @@ class DelegateMethodAdapter extends MethodVisitor { // by the 'this' of any outer class, if any. if (!mIsStatic) { - if (outerType != null) { + if (outerType != null && !mIsStaticInnerClass) { // The first-level inner class has a package-protected member called 'this$0' // that points to the outer class. diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java index 648cea4..e37a09b 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.fail; import com.android.tools.layoutlib.create.dataclass.ClassWithNative; import com.android.tools.layoutlib.create.dataclass.OuterClass; import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass; +import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass; import org.junit.Before; import org.junit.Test; @@ -56,6 +57,8 @@ public class DelegateClassAdapterTest { private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName(); private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" + InnerClass.class.getSimpleName(); + private static final String STATIC_INNER_CLASS_NAME = + OuterClass.class.getCanonicalName() + "$" + StaticInnerClass.class.getSimpleName(); @Before public void setUp() throws Exception { @@ -294,6 +297,61 @@ public class DelegateClassAdapterTest { } } + @Test + public void testDelegateStaticInner() throws Throwable { + // We'll delegate the "get" method of both the inner and outer class. + HashSet delegateMethods = new HashSet(); + delegateMethods.add("get"); + + // Generate the delegate for the outer class. + ClassWriter cwOuter = new ClassWriter(0 /*flags*/); + String outerClassName = OUTER_CLASS_NAME.replace('.', '/'); + DelegateClassAdapter cvOuter = new DelegateClassAdapter( + mLog, cwOuter, outerClassName, delegateMethods); + ClassReader cr = new ClassReader(OUTER_CLASS_NAME); + cr.accept(cvOuter, 0 /* flags */); + + // Generate the delegate for the static inner class. + ClassWriter cwInner = new ClassWriter(0 /*flags*/); + String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/'); + DelegateClassAdapter cvInner = new DelegateClassAdapter( + mLog, cwInner, innerClassName, delegateMethods); + cr = new ClassReader(STATIC_INNER_CLASS_NAME); + cr.accept(cvInner, 0 /* flags */); + + // Load the generated classes in a different class loader and try them + ClassLoader2 cl2 = null; + try { + cl2 = new ClassLoader2() { + @Override + public void testModifiedInstance() throws Exception { + + // Check the outer class + Class outerClazz2 = loadClass(OUTER_CLASS_NAME); + Object o2 = outerClazz2.newInstance(); + assertNotNull(o2); + + // Check the inner class. Since it's not a static inner class, we need + // to use the hidden constructor that takes the outer class as first parameter. + Class innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME); + Constructor innerCons = innerClazz2.getConstructor(); + Object i2 = innerCons.newInstance(); + assertNotNull(i2); + + // The original StaticInner.get returns 100+10+20, + // but the delegate makes it return 6+10+20 + assertEquals(6+10+20, callGet(i2, 10, 20)); + assertEquals(100+10+20, callGet_Original(i2, 10, 20)); + } + }; + cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray()); + cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray()); + cl2.testModifiedInstance(); + } catch (Throwable t) { + throw dumpGeneratedClass(t, cl2); + } + } + //------- /** diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java index f083e76..6dfb816 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java @@ -45,6 +45,16 @@ public class OuterClass { } } + public static class StaticInnerClass { + public StaticInnerClass() { + } + + // StaticInnerClass.get returns 100 + a + b + public int get(int a, long b) { + return 100 + a + (int) b; + } + } + @SuppressWarnings("unused") private String privateMethod() { return "outerPrivateMethod"; diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java new file mode 100644 index 0000000..a29439e --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create.dataclass; + +import com.android.tools.layoutlib.create.DelegateClassAdapterTest; +import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass; + +/** + * Used by {@link DelegateClassAdapterTest}. + */ +public class OuterClass_StaticInnerClass_Delegate { + // The delegate override of Inner.get return 6 + a + b + public static int get(StaticInnerClass inner, int a, long b) { + return 6 + a + (int) b; + } +} -- cgit v1.1 From 2b0551175d57ce0f063566eea7670442b8b624fc Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 16 Nov 2015 15:46:12 -0800 Subject: WindowDecorActionBar name change fix. In change id I7582e242c7564c32feeb044fc9eff6bfb5c56536, WindowDecorActionBar's package name was changed. This breaks layoutlib. Fix the issue by checking both package names when loading the class. Change-Id: I5723a97f0f164d61c56061fef6cff18e3cdafaee --- .../layoutlib/bridge/bars/AppCompatActionBar.java | 23 +++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java index 868c6d3..b67afeb 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -16,10 +16,13 @@ package com.android.layoutlib.bridge.bars; +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.ide.common.rendering.api.StyleResourceValue; +import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.ResourceType; @@ -45,6 +48,8 @@ public class AppCompatActionBar extends BridgeActionBar { private Object mWindowDecorActionBar; private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar"; + // This is used on v23.1.1 and later. + private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar"; private Class mWindowActionBarClass; /** @@ -70,14 +75,26 @@ public class AppCompatActionBar extends BridgeActionBar { try { Class[] constructorParams = {View.class}; Object[] constructorArgs = {getDecorContent()}; - mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS, - constructorParams, constructorArgs); + LayoutlibCallback callback = params.getLayoutlibCallback(); + // First try to load the class as was available before appcompat v23.1.1, without + // logging warnings. + try { + mWindowDecorActionBar = callback.loadClass(WINDOW_ACTION_BAR_CLASS, + constructorParams, constructorArgs); + } catch (ClassNotFoundException ignore) { + } + if (mWindowDecorActionBar == null) { + // If failed, load the new class, while logging warnings. + mWindowDecorActionBar = callback.loadView(WINDOW_ACTION_BAR_CLASS_NEW, + constructorParams, constructorArgs); + } mWindowActionBarClass = mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass(); setupActionBar(); } catch (Exception e) { - e.printStackTrace(); + Bridge.getLog().warning(LayoutLog.TAG_BROKEN, + "Failed to load AppCompat ActionBar with unknown error.", e); } } -- cgit v1.1 From 99b54b364b6d4e0689933393a9b584e542fc2c54 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Tue, 17 Nov 2015 11:34:32 -0800 Subject: AppCompatActionBar: use findClass to test presence To test the presence of WindowDecorActionBar class, use findClass() instead of loadClass() which logs warnings. Change-Id: Ice8a5a6badd764c7f75b5a64a07c48f0a7cfef98 --- .../layoutlib/bridge/bars/AppCompatActionBar.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java index b67afeb..cdcf0ea 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -76,19 +76,18 @@ public class AppCompatActionBar extends BridgeActionBar { Class[] constructorParams = {View.class}; Object[] constructorArgs = {getDecorContent()}; LayoutlibCallback callback = params.getLayoutlibCallback(); - // First try to load the class as was available before appcompat v23.1.1, without - // logging warnings. + + // Check if the old action bar class is present. + String actionBarClass = WINDOW_ACTION_BAR_CLASS; try { - mWindowDecorActionBar = callback.loadClass(WINDOW_ACTION_BAR_CLASS, - constructorParams, constructorArgs); - } catch (ClassNotFoundException ignore) { - } - if (mWindowDecorActionBar == null) { - // If failed, load the new class, while logging warnings. - mWindowDecorActionBar = callback.loadView(WINDOW_ACTION_BAR_CLASS_NEW, - constructorParams, constructorArgs); + callback.findClass(actionBarClass); + } catch (ClassNotFoundException expected) { + // Failed to find the old class, use the newer one. + actionBarClass = WINDOW_ACTION_BAR_CLASS_NEW; } + mWindowDecorActionBar = callback.loadView(actionBarClass, + constructorParams, constructorArgs); mWindowActionBarClass = mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass(); setupActionBar(); -- cgit v1.1 From 22931224d9a9845d316a9ea30e56557bec0b2e45 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Tue, 17 Nov 2015 11:37:35 -0800 Subject: LayoutLib: IntelliJ changed project files. Change-Id: I1f99ab678067c26ba2e9938c9dce40c99a6725c3 --- tools/layoutlib/.idea/encodings.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/.idea/encodings.xml b/tools/layoutlib/.idea/encodings.xml index e206d70..f758959 100644 --- a/tools/layoutlib/.idea/encodings.xml +++ b/tools/layoutlib/.idea/encodings.xml @@ -1,5 +1,6 @@ - - - + + + + \ No newline at end of file -- cgit v1.1 From 4ea74d2960688db6aa13452765dd5f2d07a40189 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Tue, 17 Nov 2015 11:42:42 -0800 Subject: Revert "LayoutLib: Fix true transparency in status bar." This reverts commit 4eb298a941c3f465944b63f1a06518e911681c89. Even though this is techinically closer to what the framework does, the fix is not complete. As a result, it looks worse when using AppCompat themes. Revert the change for the release. Change-Id: I67c1002ea17dee7234d78e2d13aab876467bd70f --- .../src/com/android/layoutlib/bridge/bars/CustomBar.java | 16 ++++++---------- .../com/android/layoutlib/bridge/bars/NavigationBar.java | 4 ++-- .../src/com/android/layoutlib/bridge/bars/StatusBar.java | 5 ++--- .../android/layoutlib/bridge/impl/RenderSessionImpl.java | 3 ++- 4 files changed, 12 insertions(+), 16 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 42e55e2..a6e5fb8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -33,7 +33,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; @@ -228,18 +227,16 @@ abstract class CustomBar extends LinearLayout { * Find the background color for this bar from the theme attributes. Only relevant to StatusBar * and NavigationBar. *

- * Returns null if not found. + * Returns 0 if not found. * * @param colorAttrName the attribute name for the background color * @param translucentAttrName the attribute name for the translucency property of the bar. * * @throws NumberFormatException if color resolved to an invalid string. */ - @Nullable - protected Integer getBarColor(@NonNull String colorAttrName, - @NonNull String translucentAttrName) { + protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) { if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) { - return null; + return 0; } RenderResources renderResources = getContext().getRenderResources(); // First check if the bar is translucent. @@ -254,11 +251,10 @@ abstract class CustomBar extends LinearLayout { if (transparent) { return getColor(renderResources, colorAttrName); } - return null; + return 0; } - @Nullable - private static Integer getColor(RenderResources renderResources, String attr) { + private static int getColor(RenderResources renderResources, String attr) { // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue. ResourceValue resource = renderResources.findItemInTheme(attr, true); // Form @color/bar to the #AARRGGBB @@ -279,7 +275,7 @@ abstract class CustomBar extends LinearLayout { } } } - return null; + return 0; } private ResourceValue getResourceValue(String reference) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index d50ce23..9c89bfe 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -65,8 +65,8 @@ public class NavigationBar extends CustomBar { super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, "navigation_bar.xml", simulatedPlatformVersion); - Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor(color == null ? 0xFF000000 : color); + int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == 0 ? 0xFF000000 : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 95a5a58..2dc7c65 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -71,9 +71,8 @@ public class StatusBar extends CustomBar { // FIXME: use FILL_H? setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT); - Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor( - color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color); + int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 2a4f583..0ffa357 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -421,7 +421,8 @@ public class RenderSessionImpl extends RenderAction { gc.setComposite(AlphaComposite.Src); gc.setColor(new Color(0x00000000, true)); - gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); + gc.fillRect(0, 0, + mMeasuredScreenWidth, mMeasuredScreenHeight); // done gc.dispose(); -- cgit v1.1 From 65a834980cdbb9407a3f27e48cd0a8c472182cd6 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Thu, 19 Nov 2015 20:47:26 +0000 Subject: Revert "Update StaticLayout_Delegate to use new nLoadHyphenator" This reverts commit 631c2ffd6ff1bfa7463d29a39a7ee74be019b3f9. The revert is only for cherry-pick back to mnc-sdk-release. Change-Id: I42cb392528b7890b58d689c732f90c3b9ba5f74c --- tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java | 5 ++--- tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java index 44ce731..5a59597 100644 --- a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java @@ -20,10 +20,9 @@ import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.io.File; -import java.nio.ByteBuffer; /** - * Delegate that overrides implementation for certain methods in {@link android.text.Hyphenator} + * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout} *

* Through the layoutlib_create tool, selected methods of StaticLayout have been replaced * by calls to methods of the same name in this delegate class. @@ -39,7 +38,7 @@ public class Hyphenator_Delegate { return null; } - /*package*/ static long loadHyphenator(ByteBuffer buf, int offset) { + /*package*/ static long loadHyphenator(String patternData) { return sDelegateManager.addNewDelegate(new Hyphenator_Delegate()); } } diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index 65c0a07..1b0ba51 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -13,7 +13,6 @@ import android.icu.util.ULocale; import android.text.Primitive.PrimitiveType; import android.text.StaticLayout.LineBreaks; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -53,8 +52,8 @@ public class StaticLayout_Delegate { } @LayoutlibDelegate - /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset) { - return Hyphenator_Delegate.loadHyphenator(buf, offset); + /*package*/ static long nLoadHyphenator(String patternData) { + return Hyphenator_Delegate.loadHyphenator(patternData); } @LayoutlibDelegate -- cgit v1.1 From 66098964e240e76e6136b4dec3f9cc6234012695 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Thu, 19 Nov 2015 20:49:35 +0000 Subject: Revert "Revert "Update StaticLayout_Delegate to use new nLoadHyphenator"" This reverts commit 65a834980cdbb9407a3f27e48cd0a8c472182cd6. The original revert was only for cherry-pick to release branch. Change-Id: Ib78f9a06ce49681c767f65b262058f18a670e38e --- tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java | 5 +++-- tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java index 5a59597..44ce731 100644 --- a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java @@ -20,9 +20,10 @@ import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.io.File; +import java.nio.ByteBuffer; /** - * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout} + * Delegate that overrides implementation for certain methods in {@link android.text.Hyphenator} *

* Through the layoutlib_create tool, selected methods of StaticLayout have been replaced * by calls to methods of the same name in this delegate class. @@ -38,7 +39,7 @@ public class Hyphenator_Delegate { return null; } - /*package*/ static long loadHyphenator(String patternData) { + /*package*/ static long loadHyphenator(ByteBuffer buf, int offset) { return sDelegateManager.addNewDelegate(new Hyphenator_Delegate()); } } diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index 1b0ba51..65c0a07 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -13,6 +13,7 @@ import android.icu.util.ULocale; import android.text.Primitive.PrimitiveType; import android.text.StaticLayout.LineBreaks; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -52,8 +53,8 @@ public class StaticLayout_Delegate { } @LayoutlibDelegate - /*package*/ static long nLoadHyphenator(String patternData) { - return Hyphenator_Delegate.loadHyphenator(patternData); + /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset) { + return Hyphenator_Delegate.loadHyphenator(buf, offset); } @LayoutlibDelegate -- cgit v1.1 From b2c51556382acd3981e5a6380e2fb3d85c2f68a5 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 2 Dec 2015 15:40:19 -0800 Subject: DO NOT MERGE: AAPT: Dump uses-permission-sdk-23 Include permissions in dump badging and dump permissions. This will also add implied features for that permission, marked as -sdk-23 features. Ex: uses-permission: name='android.permission.ACCESS_FINE_LOCATION' uses-permission-sdk-23: name='android.permission.CAMERA' feature-group: label='' uses-feature: name='android.hardware.location' uses-implied-feature: name='android.hardware.location' reason='...' uses-feature-sdk-23: name='android.hardware.camera' uses-implied-feature-sdk-23: name='android.hardware.camera' reason='...' Bug:24939655 Change-Id: Ifb374696ed4760b0ba2f1cc3516f3bf08e6cb6a3 (cherry picked from commit 5f3b2ecb3e9d27e77bd59c7b9006a6239b773944) --- tools/aapt/Command.cpp | 436 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 285 insertions(+), 151 deletions(-) (limited to 'tools') diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 8a0a39c..d63fe31 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -383,6 +383,16 @@ static void printUsesPermission(const String8& name, bool optional=false, int ma } } +static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) { + printf("uses-permission-sdk-23: "); + + printf("name='%s'", ResTable::normalizeForOutput(name.string()).string()); + if (maxSdkVersion != -1) { + printf(" maxSdkVersion='%d'", maxSdkVersion); + } + printf("\n"); +} + static void printUsesImpliedPermission(const String8& name, const String8& reason) { printf("uses-implied-permission: name='%s' reason='%s'\n", ResTable::normalizeForOutput(name.string()).string(), @@ -463,12 +473,20 @@ static void printComponentPresence(const char* componentName) { * a pre-requisite or some other reason. */ struct ImpliedFeature { + ImpliedFeature() : impliedBySdk23(false) {} + ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {} + /** * Name of the implied feature. */ String8 name; /** + * Was this implied by a permission from SDK 23 ()? + */ + bool impliedBySdk23; + + /** * List of human-readable reasons for why this feature was implied. */ SortedVector reasons; @@ -497,18 +515,24 @@ struct FeatureGroup { }; static void addImpliedFeature(KeyedVector* impliedFeatures, - const char* name, const char* reason) { + const char* name, const char* reason, bool sdk23) { String8 name8(name); ssize_t idx = impliedFeatures->indexOfKey(name8); if (idx < 0) { - idx = impliedFeatures->add(name8, ImpliedFeature()); - impliedFeatures->editValueAt(idx).name = name8; + idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23)); } - impliedFeatures->editValueAt(idx).reasons.add(String8(reason)); + + ImpliedFeature* feature = &impliedFeatures->editValueAt(idx); + + // A non-sdk 23 implied feature takes precedence. + if (feature->impliedBySdk23 && !sdk23) { + feature->impliedBySdk23 = false; + } + feature->reasons.add(String8(reason)); } -static void printFeatureGroup(const FeatureGroup& grp, - const KeyedVector* impliedFeatures = NULL) { +static void printFeatureGroupImpl(const FeatureGroup& grp, + const KeyedVector* impliedFeatures) { printf("feature-group: label='%s'\n", grp.label.string()); if (grp.openGLESVersion > 0) { @@ -536,9 +560,11 @@ static void printFeatureGroup(const FeatureGroup& grp, String8 printableFeatureName(ResTable::normalizeForOutput( impliedFeature.name.string())); - printf(" uses-feature: name='%s'\n", printableFeatureName.string()); - printf(" uses-implied-feature: name='%s' reason='", - printableFeatureName.string()); + const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : ""; + + printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string()); + printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix, + printableFeatureName.string()); const size_t numReasons = impliedFeature.reasons.size(); for (size_t j = 0; j < numReasons; j++) { printf("%s", impliedFeature.reasons[j].string()); @@ -552,6 +578,15 @@ static void printFeatureGroup(const FeatureGroup& grp, } } +static void printFeatureGroup(const FeatureGroup& grp) { + printFeatureGroupImpl(grp, NULL); +} + +static void printDefaultFeatureGroup(const FeatureGroup& grp, + const KeyedVector& impliedFeatures) { + printFeatureGroupImpl(grp, &impliedFeatures); +} + static void addParentFeatures(FeatureGroup* grp, const String8& name) { if (name == "android.hardware.camera.autofocus" || name == "android.hardware.camera.flash") { @@ -572,6 +607,72 @@ static void addParentFeatures(FeatureGroup* grp, const String8& name) { } } +static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name, + KeyedVector* impliedFeatures, + bool impliedBySdk23Permission) { + if (name == "android.permission.CAMERA") { + addImpliedFeature(impliedFeatures, "android.hardware.camera", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_FINE_LOCATION") { + addImpliedFeature(impliedFeatures, "android.hardware.location.gps", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { + addImpliedFeature(impliedFeatures, "android.hardware.location.network", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || + name == "android.permission.INSTALL_LOCATION_PROVIDER") { + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.BLUETOOTH" || + name == "android.permission.BLUETOOTH_ADMIN") { + if (targetSdk > 4) { + addImpliedFeature(impliedFeatures, "android.hardware.bluetooth", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + addImpliedFeature(impliedFeatures, "android.hardware.bluetooth", + "targetSdkVersion > 4", impliedBySdk23Permission); + } + } else if (name == "android.permission.RECORD_AUDIO") { + addImpliedFeature(impliedFeatures, "android.hardware.microphone", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_WIFI_STATE" || + name == "android.permission.CHANGE_WIFI_STATE" || + name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { + addImpliedFeature(impliedFeatures, "android.hardware.wifi", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.CALL_PHONE" || + name == "android.permission.CALL_PRIVILEGED" || + name == "android.permission.MODIFY_PHONE_STATE" || + name == "android.permission.PROCESS_OUTGOING_CALLS" || + name == "android.permission.READ_SMS" || + name == "android.permission.RECEIVE_SMS" || + name == "android.permission.RECEIVE_MMS" || + name == "android.permission.RECEIVE_WAP_PUSH" || + name == "android.permission.SEND_SMS" || + name == "android.permission.WRITE_APN_SETTINGS" || + name == "android.permission.WRITE_SMS") { + addImpliedFeature(impliedFeatures, "android.hardware.telephony", + String8("requested a telephony permission").string(), + impliedBySdk23Permission); + } +} + /* * Handle the "dump" command, to extract select data from an archive. */ @@ -712,7 +813,8 @@ int doDump(Bundle* bundle) size_t len; ResXMLTree::event_code_t code; int depth = 0; - while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && + code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; continue; @@ -735,25 +837,53 @@ int doDump(Bundle* bundle) } String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL); printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); - } else if (depth == 2 && tag == "permission") { - String8 error; - String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); - goto bail; - } - printf("permission: %s\n", - ResTable::normalizeForOutput(name.string()).string()); - } else if (depth == 2 && tag == "uses-permission") { - String8 error; - String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); - goto bail; + } else if (depth == 2) { + if (tag == "permission") { + String8 error; + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + goto bail; + } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for permission\n"); + goto bail; + } + printf("permission: %s\n", + ResTable::normalizeForOutput(name.string()).string()); + } else if (tag == "uses-permission") { + String8 error; + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + goto bail; + } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n"); + goto bail; + } + printUsesPermission(name, + AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); + } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") { + String8 error; + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + goto bail; + } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for " + "uses-permission-sdk-23\n"); + goto bail; + } + printUsesPermissionSdk23( + name, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); } - printUsesPermission(name, - AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, - AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); } } } else if (strcmp("badging", option) == 0) { @@ -893,7 +1023,8 @@ int doDump(Bundle* bundle) Vector featureGroups; KeyedVector impliedFeatures; - while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && + code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; if (depth < 2) { @@ -924,8 +1055,10 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(aName.string()).string()); } printf(" label='%s' icon='%s'\n", - ResTable::normalizeForOutput(activityLabel.string()).string(), - ResTable::normalizeForOutput(activityIcon.string()).string()); + ResTable::normalizeForOutput(activityLabel.string()) + .string(), + ResTable::normalizeForOutput(activityIcon.string()) + .string()); } if (isLeanbackLauncherActivity) { printf("leanback-launchable-activity:"); @@ -934,9 +1067,12 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(aName.string()).string()); } printf(" label='%s' icon='%s' banner='%s'\n", - ResTable::normalizeForOutput(activityLabel.string()).string(), - ResTable::normalizeForOutput(activityIcon.string()).string(), - ResTable::normalizeForOutput(activityBanner.string()).string()); + ResTable::normalizeForOutput(activityLabel.string()) + .string(), + ResTable::normalizeForOutput(activityIcon.string()) + .string(), + ResTable::normalizeForOutput(activityBanner.string()) + .string()); } } if (!hasIntentFilter) { @@ -964,18 +1100,21 @@ int doDump(Bundle* bundle) hasLauncher |= catLauncher; hasCameraActivity |= actCamera; hasCameraSecureActivity |= actCameraSecure; - hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure; + hasOtherActivities |= + !actMainActivity && !actCamera && !actCameraSecure; } else if (withinReceiver) { hasWidgetReceivers |= actWidgetReceivers; hasDeviceAdminReceiver |= (actDeviceAdminEnabled && hasBindDeviceAdminPermission); - hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); + hasOtherReceivers |= + (!actWidgetReceivers && !actDeviceAdminEnabled); } else if (withinService) { hasImeService |= actImeService; hasWallpaperService |= actWallpaperService; hasAccessibilityService |= (actAccessibilityService && hasBindAccessibilityServicePermission); - hasPrintService |= (actPrintService && hasBindPrintServicePermission); + hasPrintService |= + (actPrintService && hasBindPrintServicePermission); hasNotificationListenerService |= actNotificationListenerService && hasBindNotificationListenerServicePermission; hasDreamService |= actDreamService && hasBindDreamServicePermission; @@ -984,7 +1123,8 @@ int doDump(Bundle* bundle) !actHostApduService && !actOffHostApduService && !actNotificationListenerService); } else if (withinProvider) { - hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes; + hasDocumentsProvider |= + actDocumentsProvider && hasRequiredSafAttributes; } } withinIntentFilter = false; @@ -1125,7 +1265,8 @@ int doDump(Bundle* bundle) goto bail; } - String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error); + String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, + &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", error.string()); @@ -1135,7 +1276,8 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(label.string()).string()); printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string()); if (banner != "") { - printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string()); + printf(" banner='%s'", + ResTable::normalizeForOutput(banner.string()).string()); } printf("\n"); if (testOnly != 0) { @@ -1178,13 +1320,15 @@ int doDump(Bundle* bundle) } } } else if (tag == "uses-sdk") { - int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, + &error); if (error != "") { error = ""; String8 name = AaptXml::getResolvedAttribute(res, tree, MIN_SDK_VERSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", + fprintf(stderr, + "ERROR getting 'android:minSdkVersion' attribute: %s\n", error.string()); goto bail; } @@ -1205,7 +1349,8 @@ int doDump(Bundle* bundle) String8 name = AaptXml::getResolvedAttribute(res, tree, TARGET_SDK_VERSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", + fprintf(stderr, + "ERROR getting 'android:targetSdkVersion' attribute: %s\n", error.string()); goto bail; } @@ -1297,90 +1442,58 @@ int doDump(Bundle* bundle) } } else if (tag == "uses-permission") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); - if (name != "" && error == "") { - if (name == "android.permission.CAMERA") { - addImpliedFeature(&impliedFeatures, "android.hardware.camera", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_FINE_LOCATION") { - addImpliedFeature(&impliedFeatures, "android.hardware.location.gps", - String8::format("requested %s permission", name.string()) - .string()); - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { - addImpliedFeature(&impliedFeatures, "android.hardware.location.network", - String8::format("requested %s permission", name.string()) - .string()); - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || - name == "android.permission.INSTALL_LOCATION_PROVIDER") { - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.BLUETOOTH" || - name == "android.permission.BLUETOOTH_ADMIN") { - if (targetSdk > 4) { - addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth", - String8::format("requested %s permission", name.string()) - .string()); - addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth", - "targetSdkVersion > 4"); - } - } else if (name == "android.permission.RECORD_AUDIO") { - addImpliedFeature(&impliedFeatures, "android.hardware.microphone", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_WIFI_STATE" || - name == "android.permission.CHANGE_WIFI_STATE" || - name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { - addImpliedFeature(&impliedFeatures, "android.hardware.wifi", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.CALL_PHONE" || - name == "android.permission.CALL_PRIVILEGED" || - name == "android.permission.MODIFY_PHONE_STATE" || - name == "android.permission.PROCESS_OUTGOING_CALLS" || - name == "android.permission.READ_SMS" || - name == "android.permission.RECEIVE_SMS" || - name == "android.permission.RECEIVE_MMS" || - name == "android.permission.RECEIVE_WAP_PUSH" || - name == "android.permission.SEND_SMS" || - name == "android.permission.WRITE_APN_SETTINGS" || - name == "android.permission.WRITE_SMS") { - addImpliedFeature(&impliedFeatures, "android.hardware.telephony", - String8("requested a telephony permission").string()); - } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { - hasWriteExternalStoragePermission = true; - } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { - hasReadExternalStoragePermission = true; - } else if (name == "android.permission.READ_PHONE_STATE") { - hasReadPhoneStatePermission = true; - } else if (name == "android.permission.READ_CONTACTS") { - hasReadContactsPermission = true; - } else if (name == "android.permission.WRITE_CONTACTS") { - hasWriteContactsPermission = true; - } else if (name == "android.permission.READ_CALL_LOG") { - hasReadCallLogPermission = true; - } else if (name == "android.permission.WRITE_CALL_LOG") { - hasWriteCallLogPermission = true; - } + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + error.string()); + goto bail; + } - printUsesPermission(name, - AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, - AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); - } else { + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n"); + goto bail; + } + + addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false); + + if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { + hasWriteExternalStoragePermission = true; + } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { + hasReadExternalStoragePermission = true; + } else if (name == "android.permission.READ_PHONE_STATE") { + hasReadPhoneStatePermission = true; + } else if (name == "android.permission.READ_CONTACTS") { + hasReadContactsPermission = true; + } else if (name == "android.permission.WRITE_CONTACTS") { + hasWriteContactsPermission = true; + } else if (name == "android.permission.READ_CALL_LOG") { + hasReadCallLogPermission = true; + } else if (name == "android.permission.WRITE_CALL_LOG") { + hasWriteCallLogPermission = true; + } + + printUsesPermission(name, + AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); + + } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") { + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); goto bail; } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for " + "uses-permission-sdk-23\n"); + goto bail; + } + + addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true); + + printUsesPermissionSdk23( + name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); + } else if (tag == "uses-package") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { @@ -1422,7 +1535,8 @@ int doDump(Bundle* bundle) } else if (tag == "package-verifier") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error); + String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, + &error); if (publicKey != "" && error == "") { printf("package-verifier: name='%s' publicKey='%s'\n", ResTable::normalizeForOutput(name.string()).string(), @@ -1485,12 +1599,18 @@ int doDump(Bundle* bundle) if (error == "") { if (orien == 0 || orien == 6 || orien == 8) { // Requests landscape, sensorLandscape, or reverseLandscape. - addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape", - "one or more activities have specified a landscape orientation"); + addImpliedFeature(&impliedFeatures, + "android.hardware.screen.landscape", + "one or more activities have specified a " + "landscape orientation", + false); } else if (orien == 1 || orien == 7 || orien == 9) { // Requests portrait, sensorPortrait, or reversePortrait. - addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait", - "one or more activities have specified a portrait orientation"); + addImpliedFeature(&impliedFeatures, + "android.hardware.screen.portrait", + "one or more activities have specified a " + "portrait orientation", + false); } } } else if (tag == "uses-library") { @@ -1524,8 +1644,10 @@ int doDump(Bundle* bundle) hasBindDeviceAdminPermission = true; } } else { - fprintf(stderr, "ERROR getting 'android:permission' attribute for" - " receiver '%s': %s\n", receiverName.string(), error.string()); + fprintf(stderr, + "ERROR getting 'android:permission' attribute for" + " receiver '%s': %s\n", + receiverName.string(), error.string()); } } else if (tag == "service") { withinService = true; @@ -1542,20 +1664,24 @@ int doDump(Bundle* bundle) if (error == "") { if (permission == "android.permission.BIND_INPUT_METHOD") { hasBindInputMethodPermission = true; - } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { + } else if (permission == + "android.permission.BIND_ACCESSIBILITY_SERVICE") { hasBindAccessibilityServicePermission = true; - } else if (permission == "android.permission.BIND_PRINT_SERVICE") { + } else if (permission == + "android.permission.BIND_PRINT_SERVICE") { hasBindPrintServicePermission = true; - } else if (permission == "android.permission.BIND_NFC_SERVICE") { + } else if (permission == + "android.permission.BIND_NFC_SERVICE") { hasBindNfcServicePermission = true; - } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") { + } else if (permission == + "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") { hasBindNotificationListenerServicePermission = true; } else if (permission == "android.permission.BIND_DREAM_SERVICE") { hasBindDreamServicePermission = true; } } else { - fprintf(stderr, "ERROR getting 'android:permission' attribute for" - " service '%s': %s\n", serviceName.string(), error.string()); + fprintf(stderr, "ERROR getting 'android:permission' attribute for " + "service '%s': %s\n", serviceName.string(), error.string()); } } else if (tag == "provider") { withinProvider = true; @@ -1563,7 +1689,8 @@ int doDump(Bundle* bundle) bool exported = AaptXml::getResolvedIntegerAttribute(res, tree, EXPORTED_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:" + fprintf(stderr, + "ERROR getting 'android:exported' attribute for provider:" " %s\n", error.string()); goto bail; } @@ -1571,16 +1698,17 @@ int doDump(Bundle* bundle) bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute( res, tree, GRANT_URI_PERMISSIONS_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:" - " %s\n", error.string()); + fprintf(stderr, + "ERROR getting 'android:grantUriPermissions' attribute for " + "provider: %s\n", error.string()); goto bail; } String8 permission = AaptXml::getResolvedAttribute(res, tree, PERMISSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:" - " %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:permission' attribute for " + "provider: %s\n", error.string()); goto bail; } @@ -1661,8 +1789,9 @@ int doDump(Bundle* bundle) } else if (withinService && tag == "meta-data") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for" - " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + fprintf(stderr, "ERROR getting 'android:name' attribute for " + "meta-data tag in service '%s': %s\n", serviceName.string(), + error.string()); goto bail; } @@ -1676,8 +1805,9 @@ int doDump(Bundle* bundle) String8 xmlPath = AaptXml::getResolvedAttribute(res, tree, RESOURCE_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:resource' attribute for" - " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + fprintf(stderr, "ERROR getting 'android:resource' attribute for " + "meta-data tag in service '%s': %s\n", + serviceName.string(), error.string()); goto bail; } @@ -1731,15 +1861,19 @@ int doDump(Bundle* bundle) actImeService = true; } else if (action == "android.service.wallpaper.WallpaperService") { actWallpaperService = true; - } else if (action == "android.accessibilityservice.AccessibilityService") { + } else if (action == + "android.accessibilityservice.AccessibilityService") { actAccessibilityService = true; - } else if (action == "android.printservice.PrintService") { + } else if (action =="android.printservice.PrintService") { actPrintService = true; - } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { + } else if (action == + "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { actHostApduService = true; - } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { + } else if (action == + "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { actOffHostApduService = true; - } else if (action == "android.service.notification.NotificationListenerService") { + } else if (action == + "android.service.notification.NotificationListenerService") { actNotificationListenerService = true; } else if (action == "android.service.dreams.DreamService") { actDreamService = true; @@ -1814,12 +1948,12 @@ int doDump(Bundle* bundle) } addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen", - "default feature for all apps"); + "default feature for all apps", false); const size_t numFeatureGroups = featureGroups.size(); if (numFeatureGroups == 0) { // If no tags were defined, apply auto-implied features. - printFeatureGroup(commonFeatures, &impliedFeatures); + printDefaultFeatureGroup(commonFeatures, impliedFeatures); } else { // tags are defined, so we ignore implied features and -- cgit v1.1