summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java41
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java27
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java109
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java38
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java5
-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/impl/RenderSessionImpl.java82
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java2
-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
11 files changed, 799 insertions, 52 deletions
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 e6e9647..3c0c9a4 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;
@@ -196,7 +200,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.EMBEDDED_LAYOUT,
Capability.VIEW_MANIPULATION,
Capability.PLAY_ANIMATION,
- Capability.ANIMATED_VIEW_MANIPULATION);
+ Capability.ANIMATED_VIEW_MANIPULATION,
+ Capability.ADAPTER_BINDING);
BridgeAssetManager.initSystem();
@@ -369,6 +374,40 @@ 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");
+ }
+
+ @Override
+ public int getViewBaseline(Object viewObject) {
+ if (viewObject instanceof View) {
+ return ((View) viewObject).getBaseline();
+ }
+
+ 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 765fd99..529be97 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/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 7d794bd..e536028 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.app.Fragment;
import android.content.BroadcastReceiver;
@@ -59,11 +65,13 @@ 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;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -270,6 +278,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 FileReader(xml));
+
+ // 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
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 5740e8b..d6bbebd 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;
@@ -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.
@@ -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..273e493 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
@@ -232,9 +232,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.
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/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c5eaef9..c53cb23 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
@@ -23,12 +23,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;
@@ -47,6 +49,8 @@ import com.android.layoutlib.bridge.bars.FakeActionBar;
import com.android.layoutlib.bridge.bars.PhoneSystemBar;
import com.android.layoutlib.bridge.bars.TabletSystemBar;
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;
@@ -70,8 +74,13 @@ import android.view.ViewGroup;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
+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.QuickContactBadge;
import android.widget.TabHost;
import android.widget.TabWidget;
@@ -283,7 +292,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
-
// content frame
mContentRoot = new FrameLayout(context);
layoutParams = new LinearLayout.LayoutParams(
@@ -314,6 +322,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
View view = mInflater.inflate(mBlockParser, mContentRoot);
+ // done with the parser, pop it.
+ context.popParser();
+
Fragment_Delegate.setProjectCallback(null);
// set the AttachInfo on the root view.
@@ -1091,6 +1102,75 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
} else if (view instanceof QuickContactBadge) {
QuickContactBadge badge = (QuickContactBadge) view;
badge.setImageToDefault();
+ } 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();
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..96ab30f 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
@@ -115,7 +115,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()) {
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();
+ }
+}