Make layoutlib more lenient with recoverable errors
In many cases, throwing an exception will stop the inflation or rendering of a layout. Sometimes, we could recover from some of the failures and render a layout that is "usable". I've done a first pass trying to follow the following rules: - Try simple recoveries by choosing sensible defaults. Not trying complex recoveries. - Only recover if the exception is not part of the class behaviour. Avoided removing declared exceptions or exceptions that are expected by the framework. - I've kept assertions in place so, in most cases, the behaviour will be almost identical to the previous one with assertions enabled. Test: Checked with existing tests Change-Id: I0001fdd3c808cf405c4eb8d734b9bbe63493e05c
This commit is contained in:
@@ -740,9 +740,11 @@ public final class BridgeTypedArray extends TypedArray {
|
||||
}
|
||||
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));
|
||||
assert false :
|
||||
String.format("%1$s in %2$s%3$s is not a valid array resource.", resVal.getValue(),
|
||||
mNames[index], resIdMessage);
|
||||
|
||||
return new CharSequence[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -88,8 +88,9 @@ public final class BlendComposite implements Composite {
|
||||
|
||||
private void setAlpha(float alpha) {
|
||||
if (alpha < 0.0f || alpha > 1.0f) {
|
||||
throw new IllegalArgumentException(
|
||||
"alpha must be comprised between 0.0f and 1.0f");
|
||||
assert false : "alpha must be comprised between 0.0f and 1.0f";
|
||||
alpha = Math.min(alpha, 1.0f);
|
||||
alpha = Math.max(alpha, 0.0f);
|
||||
}
|
||||
|
||||
this.alpha = alpha;
|
||||
@@ -266,9 +267,21 @@ public final class BlendComposite implements Composite {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
default:
|
||||
assert false : "Blender not implement for " + composite.getMode().name();
|
||||
|
||||
// Ignore the blend
|
||||
return new Blender() {
|
||||
@Override
|
||||
public int[] blend(int[] src, int[] dst, int[] result) {
|
||||
result[0] = dst[0];
|
||||
result[1] = dst[1];
|
||||
result[2] = dst[2];
|
||||
result[3] = dst[3];
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
throw new IllegalArgumentException("Blender not implement for " +
|
||||
composite.getMode().name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,8 @@ public class FontFamily_Delegate {
|
||||
/*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex,
|
||||
FontConfig.Axis[] axes, int weight, int italic) {
|
||||
if (thisFontFamily.mBuilderPtr == 0) {
|
||||
throw new IllegalStateException("Unable to call addFont after freezing.");
|
||||
assert false : "Unable to call addFont after freezing.";
|
||||
return false;
|
||||
}
|
||||
final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mBuilderPtr);
|
||||
return delegate != null && delegate.addFont(path, ttcIndex, weight, italic);
|
||||
|
||||
@@ -49,21 +49,19 @@ public abstract class Gradient_Delegate extends Shader_Delegate {
|
||||
*/
|
||||
protected Gradient_Delegate(long nativeMatrix, int colors[], float positions[]) {
|
||||
super(nativeMatrix);
|
||||
if (colors.length < 2) {
|
||||
throw new IllegalArgumentException("needs >= 2 number of colors");
|
||||
}
|
||||
if (positions != null && colors.length != positions.length) {
|
||||
throw new IllegalArgumentException("color and position arrays must be of equal length");
|
||||
}
|
||||
assert colors.length >= 2 : "needs >= 2 number of colors";
|
||||
|
||||
if (positions == null) {
|
||||
float spacing = 1.f / (colors.length - 1);
|
||||
positions = new float[colors.length];
|
||||
positions[0] = 0.f;
|
||||
positions[colors.length-1] = 1.f;
|
||||
for (int i = 1; i < colors.length - 1 ; i++) {
|
||||
positions[colors.length - 1] = 1.f;
|
||||
for (int i = 1; i < colors.length - 1; i++) {
|
||||
positions[i] = spacing * i;
|
||||
}
|
||||
} else {
|
||||
assert colors.length == positions.length :
|
||||
"color and position " + "arrays must be of equal length";
|
||||
}
|
||||
|
||||
mColors = colors;
|
||||
|
||||
@@ -548,10 +548,11 @@ public final class Path_Delegate {
|
||||
case EVEN_ODD:
|
||||
case INVERSE_EVEN_ODD:
|
||||
return GeneralPath.WIND_EVEN_ODD;
|
||||
}
|
||||
|
||||
assert false;
|
||||
throw new IllegalArgumentException();
|
||||
default:
|
||||
assert false;
|
||||
return GeneralPath.WIND_NON_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -61,10 +61,8 @@ public class RoundRectangle extends RectangularShape {
|
||||
* ellipse that corner is a quarter of.
|
||||
*/
|
||||
public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
|
||||
if (cornerDimensions.length != 8) {
|
||||
throw new IllegalArgumentException("The array of corner dimensions must have eight " +
|
||||
"elements");
|
||||
}
|
||||
assert cornerDimensions.length == 8 : "The array of corner dimensions must have eight " +
|
||||
"elements";
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
@@ -585,8 +585,8 @@ public class VectorDrawable_Delegate {
|
||||
return this::setTrimPathOffset;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid VFullPath_Delegate property index "
|
||||
+ propertyIdx);
|
||||
assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx);
|
||||
return t -> {};
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -598,8 +598,8 @@ public class VectorDrawable_Delegate {
|
||||
return this::setFillColor;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid VFullPath_Delegate property index "
|
||||
+ propertyIdx);
|
||||
assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx);
|
||||
return t -> {};
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
@@ -827,8 +827,8 @@ public class VectorDrawable_Delegate {
|
||||
return this::setTranslateY;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid VGroup_Delegate property index "
|
||||
+ propertyIdx);
|
||||
assert false : ("Invalid VGroup_Delegate property index " + propertyIdx);
|
||||
return t -> {};
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@@ -164,7 +164,9 @@ public class StaticLayout_Delegate {
|
||||
builder.mTabStopCalculator);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unknown break strategy: " + builder.mBreakStrategy);
|
||||
assert false : "Unknown break strategy: " + builder.mBreakStrategy;
|
||||
builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
|
||||
builder.mTabStopCalculator);
|
||||
}
|
||||
builder.mLineBreaker.computeBreaks(recycle);
|
||||
return recycle.breaks.length;
|
||||
|
||||
@@ -361,7 +361,8 @@ public class PathParser_Delegate {
|
||||
}
|
||||
return Arrays.copyOf(results, count);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RuntimeException("error in parsing \"" + s + "\"", e);
|
||||
assert false : "error in parsing \"" + s + "\"" + e;
|
||||
return new float[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@@ -104,8 +106,10 @@ public class LayoutInflater_Delegate {
|
||||
if (layout == 0) {
|
||||
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
|
||||
if (value == null || value.length() <= 0) {
|
||||
throw new InflateException("You must specify a layout in the"
|
||||
+ " include tag: <include layout=\"@layout/layoutID\" />");
|
||||
Bridge.getLog().error(LayoutLog.TAG_BROKEN, "You must specify a layout in the"
|
||||
+ " include tag: <include layout=\"@layout/layoutID\" />", null);
|
||||
LayoutInflater.consumeChildElements(parser);
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to resolve the "?attr/name" string to an identifier.
|
||||
@@ -125,11 +129,11 @@ public class LayoutInflater_Delegate {
|
||||
if (layout == 0) {
|
||||
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
|
||||
if (value == null) {
|
||||
throw new InflateException("You must specifiy a layout in the"
|
||||
+ " include tag: <include layout=\"@layout/layoutID\" />");
|
||||
Bridge.getLog().error(LayoutLog.TAG_BROKEN, "You must specify a layout in the"
|
||||
+ " include tag: <include layout=\"@layout/layoutID\" />", null);
|
||||
} else {
|
||||
throw new InflateException("You must specifiy a valid layout "
|
||||
+ "reference. The layout ID " + value + " is not valid.");
|
||||
Bridge.getLog().error(LayoutLog.TAG_BROKEN, "You must specify a valid layout "
|
||||
+ "reference. The layout ID " + value + " is not valid.", null);
|
||||
}
|
||||
} else {
|
||||
final XmlResourceParser childParser =
|
||||
@@ -144,8 +148,11 @@ public class LayoutInflater_Delegate {
|
||||
}
|
||||
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
throw new InflateException(childParser.getPositionDescription() +
|
||||
": No start tag found!");
|
||||
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
|
||||
childParser.getPositionDescription() + ": No start tag found!",
|
||||
null);
|
||||
LayoutInflater.consumeChildElements(parser);
|
||||
return;
|
||||
}
|
||||
|
||||
final String childName = childParser.getName();
|
||||
@@ -219,7 +226,9 @@ public class LayoutInflater_Delegate {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new InflateException("<include /> can only be used inside of a ViewGroup");
|
||||
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
|
||||
"<include /> can only be used inside of a ViewGroup",
|
||||
null);
|
||||
}
|
||||
|
||||
LayoutInflater.consumeChildElements(parser);
|
||||
|
||||
@@ -37,7 +37,8 @@ public class RectShadowPainter {
|
||||
@NonNull Canvas canvas) {
|
||||
Rect outline = new Rect();
|
||||
if (!viewOutline.getRect(outline)) {
|
||||
throw new IllegalArgumentException("Outline is not a rect shadow");
|
||||
assert false : "Outline is not a rect shadow";
|
||||
return;
|
||||
}
|
||||
|
||||
Rect originCanvasRect = canvas.getClipBounds();
|
||||
|
||||
@@ -652,7 +652,8 @@ public class BridgeContext extends Context {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unsupported Service: " + service);
|
||||
assert false : "Unsupported Service: " + service;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -682,7 +683,9 @@ public class BridgeContext extends Context {
|
||||
}
|
||||
|
||||
if (style == null) {
|
||||
throw new Resources.NotFoundException();
|
||||
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
|
||||
"Failed to find style with " + resId, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import android.content.pm.VersionedPackage;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
@@ -307,7 +308,8 @@ public class BridgePackageManager extends PackageManager {
|
||||
|
||||
@Override
|
||||
public Drawable getInstantAppIcon(String packageName) {
|
||||
throw new UnsupportedOperationException();
|
||||
assert false : "Unsupported operation";
|
||||
return new ColorDrawable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
|
||||
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.layoutlib.bridge.MockView;
|
||||
import com.android.layoutlib.bridge.android.BridgeContext;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -53,35 +54,41 @@ public abstract class BridgeActionBar {
|
||||
mParams = params;
|
||||
mCallback = params.getLayoutlibCallback().getActionBarCallback();
|
||||
ResourceValue layoutName = getLayoutResource(context);
|
||||
if (layoutName == null) {
|
||||
throw new RuntimeException("Unable to find the layout for Action Bar.");
|
||||
}
|
||||
int layoutId;
|
||||
if (layoutName.isFramework()) {
|
||||
layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
|
||||
layoutName.getName(), 0);
|
||||
} else {
|
||||
layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
|
||||
layoutName.getName(), 0);
|
||||
|
||||
int layoutId = 0;
|
||||
if (layoutName == null) {
|
||||
assert false : "Unable to find the layout for Action Bar.";
|
||||
}
|
||||
else {
|
||||
if (layoutName.isFramework()) {
|
||||
layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
|
||||
layoutName.getName(), 0);
|
||||
} else {
|
||||
layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
|
||||
layoutName.getName(), 0);
|
||||
|
||||
}
|
||||
}
|
||||
if (layoutId == 0) {
|
||||
throw new RuntimeException(
|
||||
String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
|
||||
layoutName.getName(), layoutName.getResourceType()));
|
||||
}
|
||||
if (mCallback.isOverflowPopupNeeded()) {
|
||||
// Create a RelativeLayout around the action bar, to which the overflow popup may be
|
||||
// added.
|
||||
mEnclosingLayout = new RelativeLayout(mBridgeContext);
|
||||
setMatchParent(mEnclosingLayout);
|
||||
} else {
|
||||
assert false : String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
|
||||
layoutName.getName(), layoutName.getResourceType());
|
||||
mDecorContent = new MockView(context);
|
||||
mEnclosingLayout = null;
|
||||
}
|
||||
else {
|
||||
if (mCallback.isOverflowPopupNeeded()) {
|
||||
// Create a RelativeLayout around the action bar, to which the overflow popup may be
|
||||
// added.
|
||||
mEnclosingLayout = new RelativeLayout(mBridgeContext);
|
||||
setMatchParent(mEnclosingLayout);
|
||||
} else {
|
||||
mEnclosingLayout = null;
|
||||
}
|
||||
|
||||
// Inflate action bar layout.
|
||||
mDecorContent =
|
||||
getInflater(context).inflate(layoutId, mEnclosingLayout, mEnclosingLayout != null);
|
||||
// Inflate action bar layout.
|
||||
mDecorContent = getInflater(context).inflate(layoutId, mEnclosingLayout,
|
||||
mEnclosingLayout != null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,18 +83,18 @@ abstract class CustomBar extends LinearLayout {
|
||||
XmlPullParser parser;
|
||||
try {
|
||||
parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath), name);
|
||||
|
||||
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(parser, context, false);
|
||||
|
||||
try {
|
||||
inflater.inflate(bridgeParser, this, true);
|
||||
} finally {
|
||||
bridgeParser.ensurePopped();
|
||||
}
|
||||
} catch (XmlPullParserException e) {
|
||||
// Should not happen as the resource is bundled with the jar, and ParserFactory should
|
||||
// have been initialized.
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(parser, context, false);
|
||||
|
||||
try {
|
||||
inflater.inflate(bridgeParser, this, true);
|
||||
} finally {
|
||||
bridgeParser.ensurePopped();
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,8 @@ public class FrameworkActionBar extends BridgeActionBar {
|
||||
ArrayList<MenuItemImpl> menus = mActionBar.getMenuBuilder().getNonActionItems();
|
||||
ActionMenuPresenter presenter = mActionBar.getActionMenuPresenter();
|
||||
if (presenter == null) {
|
||||
throw new RuntimeException("Failed to create a Presenter for Action Bar Menus.");
|
||||
assert false : "Failed to create a Presenter for Action Bar Menus.";
|
||||
return false;
|
||||
}
|
||||
if (presenter.isOverflowReserved() &&
|
||||
menus != null) {
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.content.res.GradientColor;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap_Delegate;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.NinePatch_Delegate;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
@@ -184,7 +185,8 @@ public final class ResourceHelper {
|
||||
}
|
||||
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
throw new XmlPullParserException("No start tag found");
|
||||
assert false : "No start tag found";
|
||||
return null;
|
||||
}
|
||||
|
||||
final String name = blockParser.getName();
|
||||
|
||||
Reference in New Issue
Block a user