summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java4
-rw-r--r--tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java80
-rw-r--r--tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java14
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java36
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java27
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java219
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java42
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java14
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java93
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java115
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java7
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java247
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java113
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java179
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java2
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java2
20 files changed, 1052 insertions, 165 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 49c1e4b..5623526 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -443,7 +443,7 @@ public final class Canvas_Delegate {
AffineTransform matrixTx = matrixDelegate.getAffineTransform();
// combine them so that the given matrix is applied after.
- currentTx.preConcatenate(matrixTx);
+ currentTx.concatenate(matrixTx);
// give it to the graphics2D as a new matrix replacing all previous transform
snapshot.setTransform(currentTx);
@@ -735,7 +735,7 @@ public final class Canvas_Delegate {
/*package*/ static void native_drawCircle(int nativeCanvas,
float cx, float cy, float radius, int paint) {
native_drawOval(nativeCanvas,
- new RectF(cx - radius, cy - radius, radius*2, radius*2),
+ new RectF(cx - radius, cy - radius, radius, radius),
paint);
}
diff --git a/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
new file mode 100644
index 0000000..afbe97c
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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.os;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Delegate overriding selected methods of android.os.HandlerThread
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class HandlerThread_Delegate {
+
+ private static Map<BridgeContext, List<HandlerThread>> sThreads =
+ new HashMap<BridgeContext, List<HandlerThread>>();
+
+ public static void cleanUp(BridgeContext context) {
+ List<HandlerThread> list = sThreads.get(context);
+ if (list != null) {
+ for (HandlerThread thread : list) {
+ thread.quit();
+ }
+
+ list.clear();
+ sThreads.remove(context);
+ }
+ }
+
+ // -------- Delegate methods
+
+ @LayoutlibDelegate
+ /*package*/ static void run(HandlerThread theThread) {
+ // record the thread so that it can be quit() on clean up.
+ BridgeContext context = RenderAction.getCurrentContext();
+ List<HandlerThread> list = sThreads.get(context);
+ if (list == null) {
+ list = new ArrayList<HandlerThread>();
+ sThreads.put(context, list);
+ }
+
+ list.add(theThread);
+
+ // ---- START DEFAULT IMPLEMENTATION.
+
+ theThread.mTid = Process.myTid();
+ Looper.prepare();
+ synchronized (theThread) {
+ theThread.mLooper = Looper.myLooper();
+ theThread.notifyAll();
+ }
+ Process.setThreadPriority(theThread.mPriority);
+ theThread.onLooperPrepared();
+ Looper.loop();
+ theThread.mTid = -1;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index d5266a5..ea7242c 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -43,6 +43,8 @@ import java.io.IOException;
*/
public class LayoutInflater_Delegate {
+ public static boolean sIsInInclude = false;
+
@LayoutlibDelegate
/*package*/ static void parseInclude(LayoutInflater thisInflater,
XmlPullParser parser, View parent, AttributeSet attrs)
@@ -109,10 +111,22 @@ public class LayoutInflater_Delegate {
// false means we need to rely on the included layout params.
ViewGroup.LayoutParams params = null;
try {
+ // ---- START CHANGES
+ sIsInInclude = true;
+ // ---- END CHANGES
+
params = group.generateLayoutParams(attrs);
} catch (RuntimeException e) {
+ // ---- START CHANGES
+ sIsInInclude = false;
+ // ---- END CHANGES
+
params = group.generateLayoutParams(childAttrs);
} finally {
+ // ---- START CHANGES
+ sIsInInclude = false;
+ // ---- END CHANGES
+
if (params != null) {
view.setLayoutParams(params);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index b1a7824..c74eb33 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -25,6 +25,7 @@ import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.Result.Status;
import com.android.layoutlib.bridge.android.BridgeAssetManager;
import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.layoutlib.bridge.impl.RenderDrawable;
@@ -39,6 +40,9 @@ import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.Typeface_Delegate;
import android.os.Looper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import java.io.File;
import java.lang.ref.SoftReference;
@@ -192,10 +196,11 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.UNBOUND_RENDERING,
Capability.CUSTOM_BACKGROUND_COLOR,
Capability.RENDER,
- //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this.
+ Capability.LAYOUT_ONLY,
Capability.EMBEDDED_LAYOUT,
- Capability.VIEW_MANIPULATION);
-
+ Capability.VIEW_MANIPULATION,
+ Capability.ADAPTER_BINDING,
+ Capability.EXTENDED_VIEWINFO);
BridgeAssetManager.initSystem();
@@ -367,6 +372,31 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
}
+ @Override
+ public Result getViewParent(Object viewObject) {
+ if (viewObject instanceof View) {
+ return Status.SUCCESS.createResult(((View)viewObject).getParent());
+ }
+
+ throw new IllegalArgumentException("viewObject is not a View");
+ }
+
+ @Override
+ public Result getViewIndex(Object viewObject) {
+ if (viewObject instanceof View) {
+ View view = (View) viewObject;
+ ViewParent parentView = view.getParent();
+
+ if (parentView instanceof ViewGroup) {
+ Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view));
+ }
+
+ return Status.SUCCESS.createResult();
+ }
+
+ throw new IllegalArgumentException("viewObject is not a View");
+ }
+
/**
* Returns the lock for the bridge
*/
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index bf22c4d..e38b910 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -22,12 +22,10 @@ import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.common.rendering.api.Result.Status;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewParent;
import java.awt.image.BufferedImage;
import java.util.List;
@@ -83,31 +81,6 @@ public class BridgeRenderSession extends RenderSession {
}
@Override
- public Result getViewParent(Object viewObject) {
- if (viewObject instanceof View) {
- return Status.SUCCESS.createResult(((View)viewObject).getParent());
- }
-
- throw new IllegalArgumentException("viewObject is not a View");
- }
-
- @Override
- public Result getViewIndex(Object viewObject) {
- if (viewObject instanceof View) {
- View view = (View) viewObject;
- ViewParent parentView = view.getParent();
-
- if (parentView instanceof ViewGroup) {
- Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view));
- }
-
- return Status.SUCCESS.createResult();
- }
-
- throw new IllegalArgumentException("viewObject is not a View");
- }
-
- @Override
public Result render(long timeout) {
try {
Bridge.prepareThread();
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 1ca3182..3d50b2a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -19,21 +19,23 @@ package com.android.layoutlib.bridge;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.view.Gravity;
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).
*/
public class MockView extends TextView {
-
+
public MockView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
+
setText(this.getClass().getSimpleName());
setTextColor(0xFF000000);
+ setGravity(Gravity.CENTER);
}
@Override
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 c4eee17..ea3d5d2 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
@@ -16,9 +16,11 @@
package com.android.layoutlib.bridge.android;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
@@ -27,6 +29,10 @@ import com.android.layoutlib.bridge.impl.Stack;
import com.android.resources.ResourceType;
import com.android.util.Pair;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -57,6 +63,7 @@ import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import java.io.File;
import java.io.FileInputStream;
@@ -265,6 +272,107 @@ public final class BridgeContext extends Activity {
}
+ public ResourceReference resolveId(int id) {
+ // first get the String related to this id in the framework
+ Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+
+ if (resourceInfo != null) {
+ return new ResourceReference(resourceInfo.getSecond(), true);
+ }
+
+ // didn't find a match in the framework? look in the project.
+ if (mProjectCallback != null) {
+ resourceInfo = mProjectCallback.resolveResourceId(id);
+
+ if (resourceInfo != null) {
+ return new ResourceReference(resourceInfo.getSecond(), false);
+ }
+ }
+
+ return null;
+ }
+
+ public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent,
+ boolean attachToRoot, boolean skipCallbackParser) {
+ String layoutName = resource.getName();
+ boolean isPlatformLayout = resource.isFramework();
+
+ if (isPlatformLayout == false && skipCallbackParser == false) {
+ // check if the project callback can provide us with a custom parser.
+ ILayoutPullParser parser = mProjectCallback.getParser(layoutName);
+ if (parser != null) {
+ BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser,
+ this, resource.isFramework());
+ try {
+ pushParser(blockParser);
+ return Pair.of(
+ mBridgeInflater.inflate(blockParser, parent, attachToRoot),
+ true);
+ } finally {
+ popParser();
+ }
+ }
+ }
+
+ ResourceValue resValue;
+ if (resource instanceof ResourceValue) {
+ resValue = (ResourceValue) resource;
+ } else {
+ if (isPlatformLayout) {
+ resValue = mRenderResources.getFrameworkResource(ResourceType.LAYOUT,
+ resource.getName());
+ } else {
+ resValue = mRenderResources.getProjectResource(ResourceType.LAYOUT,
+ resource.getName());
+ }
+ }
+
+ if (resValue != null) {
+
+ File xml = new File(resValue.getValue());
+ if (xml.isFile()) {
+ // we need to create a pull parser around the layout XML file, and then
+ // give that to our XmlBlockParser
+ try {
+ KXmlParser parser = new KXmlParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
+
+ // set the resource ref to have correct view cookies
+ mBridgeInflater.setResourceReference(resource);
+
+ BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser,
+ this, resource.isFramework());
+ try {
+ pushParser(blockParser);
+ return Pair.of(
+ mBridgeInflater.inflate(blockParser, parent, attachToRoot),
+ false);
+ } finally {
+ popParser();
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + xml, e, null /*data*/);
+ // we'll return null below.
+ } catch (FileNotFoundException e) {
+ // this shouldn't happen since we check above.
+ } finally {
+ mBridgeInflater.setResourceReference(null);
+ }
+ } else {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ String.format("File %s is missing!", xml), null);
+ }
+ } else {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ String.format("Layout %s%s does not exist.", isPlatformLayout ? "android:" : "",
+ layoutName), null);
+ }
+
+ return Pair.of(null, false);
+ }
+
// ------------- Activity Methods
@Override
@@ -400,14 +508,13 @@ public final class BridgeContext extends Activity {
BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
isPlatformFile);
- // resolve the defStyleAttr value into a IStyleResourceValue
- StyleResourceValue defStyleValues = null;
-
// look for a custom style.
String customStyle = null;
if (set != null) {
customStyle = set.getAttributeValue(null /* namespace*/, "style");
}
+
+ StyleResourceValue customStyleValues = null;
if (customStyle != null) {
ResourceValue item = mRenderResources.findResValue(customStyle,
false /*forceFrameworkOnly*/);
@@ -416,75 +523,76 @@ public final class BridgeContext extends Activity {
item = mRenderResources.resolveResValue(item);
if (item instanceof StyleResourceValue) {
- defStyleValues = (StyleResourceValue)item;
+ customStyleValues = (StyleResourceValue)item;
}
}
- if (defStyleValues == null) {
- if (defStyleAttr != 0) {
- // get the name from the int.
- String defStyleName = searchAttr(defStyleAttr);
+ // resolve the defStyleAttr value into a IStyleResourceValue
+ StyleResourceValue defStyleValues = null;
- if (defaultPropMap != null) {
- defaultPropMap.put("style", defStyleName);
- }
+ if (defStyleAttr != 0) {
+ // get the name from the int.
+ String defStyleName = searchAttr(defStyleAttr);
- // look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
+ if (defaultPropMap != null) {
+ defaultPropMap.put("style", defStyleName);
+ }
- if (item != null) {
- // item is a reference to a style entry. Search for it.
- item = mRenderResources.findResValue(item.getValue(),
- false /*forceFrameworkOnly*/);
+ // look for the style in the current theme, and its parent:
+ ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
- if (item instanceof StyleResourceValue) {
- defStyleValues = (StyleResourceValue)item;
- }
- } else {
- Bridge.getLog().error(null,
- String.format(
- "Failed to find style '%s' in current theme", defStyleName),
- null /*data*/);
- }
- } else if (defStyleRes != 0) {
- Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
- if (value == null) {
- value = mProjectCallback.resolveResourceId(defStyleRes);
+ if (item != null) {
+ // item is a reference to a style entry. Search for it.
+ item = mRenderResources.findResValue(item.getValue(),
+ false /*forceFrameworkOnly*/);
+
+ if (item instanceof StyleResourceValue) {
+ defStyleValues = (StyleResourceValue)item;
}
+ } else {
+ Bridge.getLog().error(null,
+ String.format(
+ "Failed to find style '%s' in current theme", defStyleName),
+ null /*data*/);
+ }
+ } else if (defStyleRes != 0) {
+ Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+ if (value == null) {
+ value = mProjectCallback.resolveResourceId(defStyleRes);
+ }
- if (value != null) {
- if (value.getFirst() == ResourceType.STYLE) {
- // look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
- if (item != null) {
- if (item instanceof StyleResourceValue) {
- if (defaultPropMap != null) {
- defaultPropMap.put("style", item.getName());
- }
-
- defStyleValues = (StyleResourceValue)item;
+ if (value != null) {
+ if (value.getFirst() == ResourceType.STYLE) {
+ // look for the style in the current theme, and its parent:
+ ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
+ if (item != null) {
+ if (item instanceof StyleResourceValue) {
+ if (defaultPropMap != null) {
+ defaultPropMap.put("style", item.getName());
}
- } else {
- Bridge.getLog().error(null,
- String.format(
- "Style with id 0x%x (resolved to '%s') does not exist.",
- defStyleRes, value.getSecond()),
- null /*data*/);
+
+ defStyleValues = (StyleResourceValue)item;
}
} else {
Bridge.getLog().error(null,
String.format(
- "Resouce id 0x%x is not of type STYLE (instead %s)",
- defStyleRes, value.getFirst().toString()),
+ "Style with id 0x%x (resolved to '%s') does not exist.",
+ defStyleRes, value.getSecond()),
null /*data*/);
}
} else {
Bridge.getLog().error(null,
String.format(
- "Failed to find style with id 0x%x in current theme",
- defStyleRes),
+ "Resouce id 0x%x is not of type STYLE (instead %s)",
+ defStyleRes, value.getFirst().toString()),
null /*data*/);
}
+ } else {
+ Bridge.getLog().error(null,
+ String.format(
+ "Failed to find style with id 0x%x in current theme",
+ defStyleRes),
+ null /*data*/);
}
}
@@ -509,8 +617,13 @@ public final class BridgeContext extends Activity {
if (value == null) {
ResourceValue resValue = null;
- // look for the value in the defStyle first (and its parent if needed)
- if (defStyleValues != null) {
+ // look for the value in the custom style first (and its parent if needed)
+ if (customStyleValues != null) {
+ resValue = mRenderResources.findItemInStyle(customStyleValues, name);
+ }
+
+ // then look for the value in the default Style (and its parent if needed)
+ if (resValue == null && defStyleValues != null) {
resValue = mRenderResources.findItemInStyle(defStyleValues, name);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index cb8d8dd..edfe83e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -19,6 +19,7 @@ package com.android.layoutlib.bridge.android;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.MergeCookie;
+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.resources.ResourceType;
@@ -35,7 +36,7 @@ import android.view.View;
import android.view.ViewGroup;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
/**
* Custom implementation of {@link LayoutInflater} to handle custom views.
@@ -44,6 +45,7 @@ public final class BridgeInflater extends LayoutInflater {
private final IProjectCallback mProjectCallback;
private boolean mIsInMerge = false;
+ private ResourceReference mResourceReference;
/**
* List of class prefixes which are tried first by default.
@@ -175,7 +177,7 @@ public final class BridgeInflater extends LayoutInflater {
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, bridgeContext, false);
@@ -223,23 +225,33 @@ public final class BridgeInflater extends LayoutInflater {
// get the view key
Object viewKey = parser.getViewCookie();
- // if there's no view key and the depth is 1 (ie this is the first tag), or 2
- // (this is first item in included merge layout)
- // look for a previous parser in the context, and check if this one has a viewkey.
- int testDepth = mIsInMerge ? 2 : 1;
- if (viewKey == null && parser.getDepth() == testDepth) {
+ if (viewKey == null) {
+ int currentDepth = parser.getDepth();
+
+ // test whether we are in an included file or in a adapter binding view.
BridgeXmlBlockParser previousParser = bc.getPreviousParser();
if (previousParser != null) {
- viewKey = previousParser.getViewCookie();
+ // looks like we inside an embedded layout.
+ // only apply the cookie of the calling node (<include>) if we are at the
+ // top level of the embedded layout. If there is a merge tag, then
+ // skip it and look for the 2nd level
+ int testDepth = mIsInMerge ? 2 : 1;
+ if (currentDepth == testDepth) {
+ viewKey = previousParser.getViewCookie();
+ // if we are in a merge, wrap the cookie in a MergeCookie.
+ if (viewKey != null && mIsInMerge) {
+ viewKey = new MergeCookie(viewKey);
+ }
+ }
+ } else if (mResourceReference != null && currentDepth == 1) {
+ // else if there's a resource reference, this means we are in an adapter
+ // binding case. Set the resource ref as the view cookie only for the top
+ // level view.
+ viewKey = mResourceReference;
}
}
if (viewKey != null) {
- if (testDepth == 2) {
- // include-merge case
- viewKey = new MergeCookie(viewKey);
- }
-
bc.addViewKey(view, viewKey);
}
}
@@ -250,6 +262,10 @@ public final class BridgeInflater extends LayoutInflater {
mIsInMerge = isInMerge;
}
+ public void setResourceReference(ResourceReference reference) {
+ mResourceReference = reference;
+ }
+
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new BridgeInflater(this, newContext);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index 5e5aeb1..345f053 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -46,7 +46,6 @@ import android.view.ViewGroup.LayoutParams;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.InputStream;
/**
@@ -232,9 +231,8 @@ public final class BridgeResources extends Resources {
try {
// check if the current parser can provide us with a custom parser.
- BridgeXmlBlockParser currentParser = mContext.getCurrentParser();
- if (currentParser != null) {
- parser = currentParser.getParser(value.getName());
+ if (mPlatformResourceFlag[0] == false) {
+ parser = mProjectCallback.getParser(value.getName());
}
// create a new one manually if needed.
@@ -245,7 +243,7 @@ public final class BridgeResources extends Resources {
// give that to our XmlBlockParser
parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(xml));
+ parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
}
}
@@ -283,7 +281,7 @@ public final class BridgeResources extends Resources {
// give that to our XmlBlockParser
parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(xml));
+ parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
}
@@ -502,7 +500,7 @@ public final class BridgeResources extends Resources {
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
} catch (XmlPullParserException e) {
@@ -537,7 +535,7 @@ public final class BridgeResources extends Resources {
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
} catch (XmlPullParserException e) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index b9f769f..d4600a1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -36,10 +36,11 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
import android.view.ViewGroup.LayoutParams;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
import java.util.Arrays;
import java.util.Map;
@@ -315,7 +316,7 @@ public final class BridgeTypedArray extends TypedArray {
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, mContext, resValue.isFramework());
@@ -471,40 +472,23 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getDimensionPixelSize(int index, int defValue) {
- if (mResourceData[index] == null) {
- return defValue;
- }
+ try {
+ return getDimension(index);
+ } catch (RuntimeException e) {
+ if (mResourceData[index] != null) {
+ String s = mResourceData[index].getValue();
- String s = mResourceData[index].getValue();
+ if (s != null) {
+ // looks like we were unable to resolve the dimension value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null /*data*/);
+ }
+ }
- if (s == null) {
- return defValue;
- } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
- s.equals(BridgeConstants.FILL_PARENT)) {
- return LayoutParams.MATCH_PARENT;
- } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
- return LayoutParams.WRAP_CONTENT;
- } else if (RenderResources.REFERENCE_NULL.equals(s)) {
return defValue;
}
-
- if (ResourceHelper.stringToFloat(s, mValue)) {
- float f = mValue.getDimension(mBridgeResources.mMetrics);
-
- final int res = (int)(f+0.5f);
- if (res != 0) return res;
- if (f == 0) return 0;
- if (f > 0) return 1;
- return defValue; // this is basically unreachable.
- }
-
- // looks like we were unable to resolve the dimension value
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
- s, mNames[index]), null /*data*/);
-
- return defValue;
}
/**
@@ -521,7 +505,20 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getLayoutDimension(int index, String name) {
- return getDimensionPixelSize(index, 0);
+ try {
+ // this will throw an exception
+ return getDimension(index);
+ } catch (RuntimeException e) {
+
+ if (LayoutInflater_Delegate.sIsInInclude) {
+ throw new RuntimeException();
+ }
+
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ "You must supply a " + name + " attribute.", null);
+
+ return 0;
+ }
}
@Override
@@ -529,6 +526,36 @@ public final class BridgeTypedArray extends TypedArray {
return getDimensionPixelSize(index, defValue);
}
+ private int getDimension(int index) {
+ if (mResourceData[index] == null) {
+ throw new RuntimeException();
+ }
+
+ String s = mResourceData[index].getValue();
+
+ if (s == null) {
+ throw new RuntimeException();
+ } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+ s.equals(BridgeConstants.FILL_PARENT)) {
+ return LayoutParams.MATCH_PARENT;
+ } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+ return LayoutParams.WRAP_CONTENT;
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+ throw new RuntimeException();
+ }
+
+ if (ResourceHelper.stringToFloat(s, mValue)) {
+ float f = mValue.getDimension(mBridgeResources.mMetrics);
+
+ final int res = (int)(f+0.5f);
+ if (res != 0) return res;
+ if (f == 0) return 0;
+ if (f > 0) return 1;
+ }
+
+ throw new RuntimeException();
+ }
+
/**
* Retrieve a fractional unit attribute at <var>index</var>.
*
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index 2f54ae6..70dbaa4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -69,14 +69,6 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
return mPlatformFile;
}
- public ILayoutPullParser getParser(String layoutName) {
- if (mParser instanceof ILayoutPullParser) {
- return ((ILayoutPullParser)mParser).getParser(layoutName);
- }
-
- return null;
- }
-
public Object getViewCookie() {
if (mParser instanceof ILayoutPullParser) {
return ((ILayoutPullParser)mParser).getViewCookie();
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 0c4b0d3..060e6ee 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
@@ -73,7 +73,7 @@ abstract class CustomBar extends LinearLayout {
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(
getClass().getResourceAsStream(layoutPath),
- "UTF8");
+ "UTF8"); //$NON-NLS-1$
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, (BridgeContext) context, false /*platformFile*/);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 8e80c21..6194f5d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -29,6 +29,7 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.ResourceType;
+import android.os.HandlerThread_Delegate;
import android.util.DisplayMetrics;
import java.util.concurrent.TimeUnit;
@@ -228,6 +229,10 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
private void tearDown() {
// Make sure to remove static references, otherwise we could not unload the lib
mContext.disposeResources();
+
+ // quit HandlerThread created during this session.
+ HandlerThread_Delegate.cleanUp(sCurrentContext);
+
sCurrentContext = null;
Bridge.setLog(null);
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 348c579..f29a9d0 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
@@ -22,12 +22,14 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN;
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+import com.android.ide.common.rendering.api.AdapterBinding;
import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.SessionParams;
@@ -44,6 +46,8 @@ import com.android.layoutlib.bridge.android.BridgeWindowSession;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.bars.PhoneSystemBar;
import com.android.layoutlib.bridge.bars.TitleBar;
+import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
+import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.ResourceType;
import com.android.resources.ScreenSize;
import com.android.util.Pair;
@@ -62,8 +66,14 @@ import android.view.ViewGroup;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.AbsListView;
+import android.widget.AbsSpinner;
+import android.widget.AdapterView;
+import android.widget.ExpandableListView;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TabHost.TabSpec;
@@ -268,6 +278,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
View view = mInflater.inflate(mBlockParser, mContentRoot);
+ // done with the parser, pop it.
+ context.popParser();
+
// set the AttachInfo on the root view.
AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
new Handler(), null);
@@ -453,7 +466,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mViewRoot.draw(mCanvas);
}
- mViewInfoList = startVisitingViews(mViewRoot, 0);
+ mViewInfoList = startVisitingViews(mViewRoot, 0, params.getExtendedViewInfoMode());
// success!
return SUCCESS.createResult();
@@ -875,6 +888,75 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
throws PostInflateException {
if (view instanceof TabHost) {
setupTabHost((TabHost)view, projectCallback);
+ } else if (view instanceof AdapterView<?>) {
+ // get the view ID.
+ int id = view.getId();
+
+ BridgeContext context = getContext();
+
+ // get a ResourceReference from the integer ID.
+ ResourceReference listRef = context.resolveId(id);
+
+ if (listRef != null) {
+ SessionParams params = getParams();
+ AdapterBinding binding = params.getAdapterBindings().get(listRef);
+
+ // if there was no adapter binding, trying to get it from the call back.
+ if (binding == null) {
+ binding = params.getProjectCallback().getAdapterBinding(listRef,
+ context.getViewKey(view), view);
+ }
+
+ if (binding != null) {
+
+ if (view instanceof AbsListView) {
+ if ((binding.getFooterCount() > 0 || binding.getHeaderCount() > 0) &&
+ view instanceof ListView) {
+ ListView list = (ListView) view;
+
+ boolean skipCallbackParser = false;
+
+ int count = binding.getHeaderCount();
+ for (int i = 0 ; i < count ; i++) {
+ Pair<View, Boolean> pair = context.inflateView(
+ binding.getHeaderAt(i),
+ list, false /*attachToRoot*/, skipCallbackParser);
+ if (pair.getFirst() != null) {
+ list.addHeaderView(pair.getFirst());
+ }
+
+ skipCallbackParser |= pair.getSecond();
+ }
+
+ count = binding.getFooterCount();
+ for (int i = 0 ; i < count ; i++) {
+ Pair<View, Boolean> pair = context.inflateView(
+ binding.getFooterAt(i),
+ list, false /*attachToRoot*/, skipCallbackParser);
+ if (pair.getFirst() != null) {
+ list.addFooterView(pair.getFirst());
+ }
+
+ skipCallbackParser |= pair.getSecond();
+ }
+ }
+
+ if (view instanceof ExpandableListView) {
+ ((ExpandableListView) view).setAdapter(
+ new FakeExpandableAdapter(
+ listRef, binding, params.getProjectCallback()));
+ } else {
+ ((AbsListView) view).setAdapter(
+ new FakeAdapter(
+ listRef, binding, params.getProjectCallback()));
+ }
+ } else if (view instanceof AbsSpinner) {
+ ((AbsSpinner) view).setAdapter(
+ new FakeAdapter(
+ listRef, binding, params.getProjectCallback()));
+ }
+ }
+ }
} else if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup)view;
final int count = group.getChildCount();
@@ -959,7 +1041,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
- private List<ViewInfo> startVisitingViews(View view, int offset) {
+ private List<ViewInfo> startVisitingViews(View view, int offset, boolean setExtendedInfo) {
if (view == null) {
return null;
}
@@ -968,7 +1050,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
offset += view.getTop();
if (view == mContentRoot) {
- return visitAllChildren(mContentRoot, offset);
+ return visitAllChildren(mContentRoot, offset, setExtendedInfo);
}
// otherwise, look for mContentRoot in the children
@@ -976,7 +1058,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
ViewGroup group = ((ViewGroup) view);
for (int i = 0; i < group.getChildCount(); i++) {
- List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset);
+ List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset,
+ setExtendedInfo);
if (list != null) {
return list;
}
@@ -991,8 +1074,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
*/
- private ViewInfo visit(View view, int offset) {
+ private ViewInfo visit(View view, int offset, boolean setExtendedInfo) {
if (view == null) {
return null;
}
@@ -1002,9 +1086,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset,
view, view.getLayoutParams());
+ if (setExtendedInfo) {
+ MarginLayoutParams marginParams = null;
+ LayoutParams params = view.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ marginParams = (MarginLayoutParams) params;
+ }
+ result.setExtendedInfo(view.getBaseline(),
+ marginParams != null ? marginParams.leftMargin : 0,
+ marginParams != null ? marginParams.topMargin : 0,
+ marginParams != null ? marginParams.rightMargin : 0,
+ marginParams != null ? marginParams.bottomMargin : 0);
+ }
+
if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view);
- result.setChildren(visitAllChildren(group, 0 /*offset*/));
+ result.setChildren(visitAllChildren(group, 0 /*offset*/, setExtendedInfo));
}
return result;
@@ -1015,15 +1112,17 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* containing the bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
*/
- private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset) {
+ private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset,
+ boolean setExtendedInfo) {
if (viewGroup == null) {
return null;
}
List<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < viewGroup.getChildCount(); i++) {
- children.add(visit(viewGroup.getChildAt(i), offset));
+ children.add(visit(viewGroup.getChildAt(i), offset, setExtendedInfo));
}
return children;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 649160e..e5efa4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -44,7 +44,6 @@ import android.util.TypedValue;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -115,7 +114,7 @@ public final class ResourceHelper {
public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
String value = resValue.getValue();
- if (value != null) {
+ if (value != null && RenderResources.REFERENCE_NULL.equals(value) == false) {
// first check if the value is a file (xml most likely)
File f = new File(value);
if (f.isFile()) {
@@ -124,7 +123,7 @@ public final class ResourceHelper {
// providing an XmlPullParser
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, resValue.isFramework());
@@ -206,7 +205,7 @@ public final class ResourceHelper {
// let the framework inflate the Drawable from the XML file.
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, value.isFramework());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java
new file mode 100644
index 0000000..e0414fe
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2011 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.binding;
+
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.DataBindingItem;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.util.Pair;
+
+import android.database.DataSetObserver;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Checkable;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Base adapter to do fake data binding in {@link AdapterView} objects.
+ */
+public class BaseAdapter {
+
+ /**
+ * This is the items provided by the adapter. They are dynamically generated.
+ */
+ protected final static class AdapterItem {
+ private final DataBindingItem mItem;
+ private final int mType;
+ private final int mFullPosition;
+ private final int mPositionPerType;
+ private List<AdapterItem> mChildren;
+
+ protected AdapterItem(DataBindingItem item, int type, int fullPosition,
+ int positionPerType) {
+ mItem = item;
+ mType = type;
+ mFullPosition = fullPosition;
+ mPositionPerType = positionPerType;
+ }
+
+ void addChild(AdapterItem child) {
+ if (mChildren == null) {
+ mChildren = new ArrayList<AdapterItem>();
+ }
+
+ mChildren.add(child);
+ }
+
+ List<AdapterItem> getChildren() {
+ if (mChildren != null) {
+ return mChildren;
+ }
+
+ return Collections.emptyList();
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ int getFullPosition() {
+ return mFullPosition;
+ }
+
+ int getPositionPerType() {
+ return mPositionPerType;
+ }
+
+ DataBindingItem getDataBindingItem() {
+ return mItem;
+ }
+ }
+
+ private final AdapterBinding mBinding;
+ private final IProjectCallback mCallback;
+ private final ResourceReference mAdapterRef;
+ private boolean mSkipCallbackParser = false;
+
+ protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>();
+
+ protected BaseAdapter(ResourceReference adapterRef, AdapterBinding binding,
+ IProjectCallback callback) {
+ mAdapterRef = adapterRef;
+ mBinding = binding;
+ mCallback = callback;
+ }
+
+ // ------- Some Adapter method used by all children classes.
+
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public boolean isEmpty() {
+ return mItems.size() == 0;
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ // pass
+ }
+
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ // pass
+ }
+
+ // -------
+
+
+ protected AdapterBinding getBinding() {
+ return mBinding;
+ }
+
+ protected View getView(AdapterItem item, AdapterItem parentItem, View convertView,
+ ViewGroup parent) {
+ // we don't care about recycling here because we never scroll.
+ DataBindingItem dataBindingItem = item.getDataBindingItem();
+
+ BridgeContext context = RenderAction.getCurrentContext();
+
+ Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(),
+ parent, false /*attachToRoot*/, mSkipCallbackParser);
+
+ View view = pair.getFirst();
+ mSkipCallbackParser |= pair.getSecond();
+
+ if (view != null) {
+ fillView(context, view, item, parentItem);
+ } else {
+ // create a text view to display an error.
+ TextView tv = new TextView(context);
+ tv.setText("Unable to find layout: " + dataBindingItem.getViewReference().getName());
+ view = tv;
+ }
+
+ return view;
+ }
+
+ private void fillView(BridgeContext context, View view, AdapterItem item,
+ AdapterItem parentItem) {
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0 ; i < count ; i++) {
+ fillView(context, group.getChildAt(i), item, parentItem);
+ }
+ } else {
+ int id = view.getId();
+ if (id != 0) {
+ ResourceReference resolvedRef = context.resolveId(id);
+ if (resolvedRef != null) {
+ int fullPosition = item.getFullPosition();
+ int positionPerType = item.getPositionPerType();
+ int fullParentPosition = parentItem != null ? parentItem.getFullPosition() : 0;
+ int parentPositionPerType = parentItem != null ?
+ parentItem.getPositionPerType() : 0;
+
+ if (view instanceof TextView) {
+ TextView tv = (TextView) view;
+ Object value = mCallback.getAdapterItemValue(
+ mAdapterRef, context.getViewKey(view),
+ item.getDataBindingItem().getViewReference(),
+ fullPosition, positionPerType,
+ fullParentPosition, parentPositionPerType,
+ resolvedRef, ViewAttribute.TEXT, tv.getText().toString());
+ if (value != null) {
+ if (value.getClass() != ViewAttribute.TEXT.getAttributeClass()) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
+ "Wrong Adapter Item value class for TEXT. Expected String, got %s",
+ value.getClass().getName()), null);
+ } else {
+ tv.setText((String) value);
+ }
+ }
+ }
+
+ if (view instanceof Checkable) {
+ Checkable cb = (Checkable) view;
+
+ Object value = mCallback.getAdapterItemValue(
+ mAdapterRef, context.getViewKey(view),
+ item.getDataBindingItem().getViewReference(),
+ fullPosition, positionPerType,
+ fullParentPosition, parentPositionPerType,
+ resolvedRef, ViewAttribute.IS_CHECKED, cb.isChecked());
+ if (value != null) {
+ if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
+ "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
+ value.getClass().getName()), null);
+ } else {
+ cb.setChecked((Boolean) value);
+ }
+ }
+ }
+
+ if (view instanceof ImageView) {
+ ImageView iv = (ImageView) view;
+
+ Object value = mCallback.getAdapterItemValue(
+ mAdapterRef, context.getViewKey(view),
+ item.getDataBindingItem().getViewReference(),
+ fullPosition, positionPerType,
+ fullParentPosition, parentPositionPerType,
+ resolvedRef, ViewAttribute.SRC, iv.getDrawable());
+ if (value != null) {
+ if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
+ "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
+ value.getClass().getName()), null);
+ } else {
+ // FIXME
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
new file mode 100644
index 0000000..c9bb424
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 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.binding;
+
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.DataBindingItem;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.ResourceReference;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.SpinnerAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fake adapter to do fake data binding in {@link AdapterView} objects for {@link ListAdapter}
+ * and {@link SpinnerAdapter}.
+ *
+ */
+public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
+
+ // don't use a set because the order is important.
+ private final List<ResourceReference> mTypes = new ArrayList<ResourceReference>();
+
+ public FakeAdapter(ResourceReference adapterRef, AdapterBinding binding,
+ IProjectCallback callback) {
+ super(adapterRef, binding, callback);
+
+ final int repeatCount = getBinding().getRepeatCount();
+ final int itemCount = getBinding().getItemCount();
+
+ // Need an array to count for each type.
+ // This is likely too big, but is the max it can be.
+ int[] typeCount = new int[itemCount];
+
+ // We put several repeating sets.
+ for (int r = 0 ; r < repeatCount ; r++) {
+ // loop on the type of list items, and add however many for each type.
+ for (DataBindingItem dataBindingItem : getBinding()) {
+ ResourceReference viewRef = dataBindingItem.getViewReference();
+ int typeIndex = mTypes.indexOf(viewRef);
+ if (typeIndex == -1) {
+ typeIndex = mTypes.size();
+ mTypes.add(viewRef);
+ }
+
+ int count = dataBindingItem.getCount();
+
+ int index = typeCount[typeIndex];
+ typeCount[typeIndex] += count;
+
+ for (int k = 0 ; k < count ; k++) {
+ mItems.add(new AdapterItem(dataBindingItem, typeIndex, mItems.size(), index++));
+ }
+ }
+ }
+ }
+
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public int getItemViewType(int position) {
+ return mItems.get(position).getType();
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // we don't care about recycling here because we never scroll.
+ AdapterItem item = mItems.get(position);
+ return getView(item, null /*parentGroup*/, convertView, parent);
+ }
+
+ public int getViewTypeCount() {
+ return mTypes.size();
+ }
+
+ // ---- SpinnerAdapter
+
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ // pass
+ return null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
new file mode 100644
index 0000000..2c492e3
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2011 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.binding;
+
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.DataBindingItem;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.ResourceReference;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ExpandableListAdapter;
+import android.widget.HeterogeneousExpandableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FakeExpandableAdapter extends BaseAdapter implements ExpandableListAdapter,
+ HeterogeneousExpandableList {
+
+ // don't use a set because the order is important.
+ private final List<ResourceReference> mGroupTypes = new ArrayList<ResourceReference>();
+ private final List<ResourceReference> mChildrenTypes = new ArrayList<ResourceReference>();
+
+ public FakeExpandableAdapter(ResourceReference adapterRef, AdapterBinding binding,
+ IProjectCallback callback) {
+ super(adapterRef, binding, callback);
+
+ createItems(binding, binding.getItemCount(), binding.getRepeatCount(), mGroupTypes, 1);
+ }
+
+ private void createItems(Iterable<DataBindingItem> iterable, final int itemCount,
+ final int repeatCount, List<ResourceReference> types, int depth) {
+ // Need an array to count for each type.
+ // This is likely too big, but is the max it can be.
+ int[] typeCount = new int[itemCount];
+
+ // we put several repeating sets.
+ for (int r = 0 ; r < repeatCount ; r++) {
+ // loop on the type of list items, and add however many for each type.
+ for (DataBindingItem dataBindingItem : iterable) {
+ ResourceReference viewRef = dataBindingItem.getViewReference();
+ int typeIndex = types.indexOf(viewRef);
+ if (typeIndex == -1) {
+ typeIndex = types.size();
+ types.add(viewRef);
+ }
+
+ List<DataBindingItem> children = dataBindingItem.getChildren();
+ int count = dataBindingItem.getCount();
+
+ // if there are children, we use the count as a repeat count for the children.
+ if (children.size() > 0) {
+ count = 1;
+ }
+
+ int index = typeCount[typeIndex];
+ typeCount[typeIndex] += count;
+
+ for (int k = 0 ; k < count ; k++) {
+ AdapterItem item = new AdapterItem(dataBindingItem, typeIndex, mItems.size(),
+ index++);
+ mItems.add(item);
+
+ if (children.size() > 0) {
+ createItems(dataBindingItem, depth + 1);
+ }
+ }
+ }
+ }
+ }
+
+ private void createItems(DataBindingItem item, int depth) {
+ if (depth == 2) {
+ createItems(item, item.getChildren().size(), item.getCount(), mChildrenTypes, depth);
+ }
+ }
+
+ private AdapterItem getChildItem(int groupPosition, int childPosition) {
+ AdapterItem item = mItems.get(groupPosition);
+
+ List<AdapterItem> children = item.getChildren();
+ return children.get(childPosition);
+ }
+
+ // ---- ExpandableListAdapter
+
+ public int getGroupCount() {
+ return mItems.size();
+ }
+
+ public int getChildrenCount(int groupPosition) {
+ AdapterItem item = mItems.get(groupPosition);
+ return item.getChildren().size();
+ }
+
+ public Object getGroup(int groupPosition) {
+ return mItems.get(groupPosition);
+ }
+
+ public Object getChild(int groupPosition, int childPosition) {
+ return getChildItem(groupPosition, childPosition);
+ }
+
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ // we don't care about recycling here because we never scroll.
+ AdapterItem item = mItems.get(groupPosition);
+ return getView(item, null /*parentItem*/, convertView, parent);
+ }
+
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ // we don't care about recycling here because we never scroll.
+ AdapterItem parentItem = mItems.get(groupPosition);
+ AdapterItem item = getChildItem(groupPosition, childPosition);
+ return getView(item, parentItem, convertView, parent);
+ }
+
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ public long getCombinedGroupId(long groupId) {
+ return groupId << 16 | 0x0000FFFF;
+ }
+
+ public long getCombinedChildId(long groupId, long childId) {
+ return groupId << 16 | childId;
+ }
+
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ public void onGroupCollapsed(int groupPosition) {
+ // pass
+ }
+
+ public void onGroupExpanded(int groupPosition) {
+ // pass
+ }
+
+ // ---- HeterogeneousExpandableList
+
+ public int getChildType(int groupPosition, int childPosition) {
+ return getChildItem(groupPosition, childPosition).getType();
+ }
+
+ public int getChildTypeCount() {
+ return mChildrenTypes.size();
+ }
+
+ public int getGroupType(int groupPosition) {
+ return mItems.get(groupPosition).getType();
+ }
+
+ public int getGroupTypeCount() {
+ return mGroupTypes.size();
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 3252fb4..70d5446 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -44,7 +44,7 @@ public class BridgeXmlBlockParserTest extends TestCase {
InputStream input = this.getClass().getClassLoader().getResourceAsStream(
"com/android/layoutlib/testdata/layout1.xml");
- parser.setInput(input, null /*encoding*/);
+ parser.setInput(input, "UTF-8"); //$NON-NLS-1$
assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
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 85e6c3f..95506c6 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
@@ -100,11 +100,11 @@ public final class CreateInfo implements ICreateInfo {
"android.content.res.Resources$Theme#resolveAttribute",
"android.graphics.BitmapFactory#finishDecode",
"android.os.Handler#sendMessageAtTime",
+ "android.os.HandlerThread#run",
"android.os.Build#getString",
"android.view.LayoutInflater#parseInclude",
"android.view.View#isInEditMode",
"com.android.internal.util.XmlUtils#convertValueToInt",
- // TODO: comment out once DelegateClass is working
};
/**