diff --git a/tools/data-binding/TestApp/build.gradle b/tools/data-binding/TestApp/build.gradle index 4e42f20918e60..c21652e5a9f42 100644 --- a/tools/data-binding/TestApp/build.gradle +++ b/tools/data-binding/TestApp/build.gradle @@ -10,15 +10,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - buildscript { repositories { jcenter() mavenLocal() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' - classpath 'com.android.databinding:dataBinder:0.3-SNAPSHOT' + classpath "com.android.tools.build:gradle:$androidPluginVersion" + classpath "com.android.databinding:dataBinder:$version" } } apply plugin: 'com.android.application' diff --git a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseDataBinderTest.java b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseDataBinderTest.java index c64524e208f6c..94db7b2e76e05 100644 --- a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseDataBinderTest.java +++ b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseDataBinderTest.java @@ -16,6 +16,7 @@ package com.android.databinding.testapp; import com.android.databinding.library.DataBinder; import com.android.databinding.library.IViewDataBinder; +import android.content.pm.ActivityInfo; import android.os.Looper; import android.test.ActivityInstrumentationTestCase2; @@ -23,17 +24,24 @@ public class BaseDataBinderTest extends ActivityInstrumentationTestCase2 { private Class mBinderClass; private int mLayoutId; + private int mOrientation; protected T mBinder; public BaseDataBinderTest(final Class binderClass, final int layoutId) { + this(binderClass, layoutId, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + public BaseDataBinderTest(final Class binderClass, final int layoutId, final int orientation) { super(TestActivity.class); mBinderClass = binderClass; mLayoutId = layoutId; + mOrientation = orientation; } @Override protected void setUp() throws Exception { super.setUp(); + getActivity().setRequestedOrientation(mOrientation); createBinder(); } @@ -47,6 +55,7 @@ public class BaseDataBinderTest @Override public void run() { mBinder = DataBinder.createBinder(mBinderClass, getActivity(), mLayoutId, null); + getActivity().setContentView(mBinder.getRoot()); } }); if (!isMainThread()) { @@ -54,4 +63,22 @@ public class BaseDataBinderTest } assertNotNull(mBinder); } + + protected void assertMethod(Class klass, String methodName) throws NoSuchMethodException { + assertEquals(klass, mBinder.getClass().getDeclaredMethod(methodName).getReturnType()); + } + + protected void assertField(Class klass, String fieldName) throws NoSuchFieldException { + assertEquals(klass, mBinder.getClass().getDeclaredField(fieldName).getType()); + } + + protected void assertNoField(String fieldName) { + Exception[] ex = new Exception[1]; + try { + mBinder.getClass().getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + ex[0] = e; + } + assertNotNull(ex[0]); + } } diff --git a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseLandDataBinderTest.java b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseLandDataBinderTest.java new file mode 100644 index 0000000000000..0f983ebaa8d7b --- /dev/null +++ b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/BaseLandDataBinderTest.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.databinding.testapp; + +import com.android.databinding.library.IViewDataBinder; + +import android.content.pm.ActivityInfo; + +public class BaseLandDataBinderTest extends BaseDataBinderTest { + + public BaseLandDataBinderTest(Class binderClass, int layoutId) { + super(binderClass, layoutId, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } +} diff --git a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/LandDataBinderTest.java b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/LandDataBinderTest.java new file mode 100644 index 0000000000000..218c799430b68 --- /dev/null +++ b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/LandDataBinderTest.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.databinding.testapp; + +public class LandDataBinderTest { + +} diff --git a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/ViewBindingAdapterTest.java b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/ViewBindingAdapterTest.java index 7d9b8ae9dfab1..102725b7b4487 100644 --- a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/ViewBindingAdapterTest.java +++ b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/ViewBindingAdapterTest.java @@ -20,6 +20,7 @@ import com.android.databinding.testapp.vo.ViewBindingObject; import android.content.res.ColorStateList; import android.os.Build; +import android.test.UiThreadTest; import android.view.View; public class ViewBindingAdapterTest extends BaseDataBinderTest { @@ -33,8 +34,18 @@ public class ViewBindingAdapterTest extends BaseDataBinderTest { + + public LandscapeConfigTest() { + super(MultiResLayoutBinder.class, R.layout.multi_res_layout); + } + + public void testSharedViewIdAndVariableInheritance() + throws InterruptedException, NoSuchMethodException, NoSuchFieldException { + assertEquals("MultiResLayoutBinderLandImpl", mBinder.getClass().getSimpleName()); + assertMethod(TextView.class, "getObjectInLandTextView"); + assertMethod(TextView.class, "getObjectInDefaultTextView"); + assertMethod(View.class, "getObjectInDefaultTextView2"); + + assertField(TextView.class, "mObjectInLandTextView"); + assertField(TextView.class, "mObjectInDefaultTextView"); + assertField(TextView.class, "mObjectInDefaultTextView2"); + + assertField(NotBindableVo.class, "mObjectInLand"); + assertField(NotBindableVo.class, "mObjectInDefault"); + + // includes + assertMethod(IViewDataBinder.class, "getIncludedLayoutConflict"); + assertMethod(BasicBindingBinder.class, "getIncludedLayoutShared"); + assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutPort"); + assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutLand"); + + assertField(IncludedLayoutBinder.class, "mIncludedLayoutConflict"); + assertField(BasicBindingBinder.class, "mIncludedLayoutShared"); + assertField(ConditionalBindingBinder.class, "mIncludedLayoutLand"); + + assertNoField("mIncludedLayoutPort"); + } +} diff --git a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/multiconfig/PortraitConfigTest.java b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/multiconfig/PortraitConfigTest.java new file mode 100644 index 0000000000000..8790f04be3746 --- /dev/null +++ b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/multiconfig/PortraitConfigTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.databinding.testapp.multiconfig; + +import com.android.databinding.library.IViewDataBinder; +import com.android.databinding.testapp.BaseDataBinderTest; +import com.android.databinding.testapp.R; +import com.android.databinding.testapp.generated.BasicBindingBinder; +import com.android.databinding.testapp.generated.ConditionalBindingBinder; +import com.android.databinding.testapp.generated.IncludedLayoutBinder; +import com.android.databinding.testapp.generated.MultiResLayoutBinder; +import com.android.databinding.testapp.vo.NotBindableVo; + +import android.content.pm.ActivityInfo; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +public class PortraitConfigTest extends BaseDataBinderTest { + public PortraitConfigTest() { + super(MultiResLayoutBinder.class, R.layout.multi_res_layout, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + public void testSharedViewIdAndVariableInheritance() + throws InterruptedException, NoSuchMethodException, NoSuchFieldException { + assertEquals("MultiResLayoutBinderImpl", mBinder.getClass().getSimpleName()); + assertEquals("MultiResLayoutBinderImpl", mBinder.getClass().getSimpleName()); + assertMethod(TextView.class, "getObjectInLandTextView"); + assertMethod(TextView.class, "getObjectInDefaultTextView"); + assertMethod(View.class, "getObjectInDefaultTextView2"); + + assertNoField("mObjectInLandTextView"); + assertField(TextView.class, "mObjectInDefaultTextView"); + assertField(EditText.class, "mObjectInDefaultTextView2"); + + assertNoField("mObjectInLand"); + assertField(NotBindableVo.class, "mObjectInDefault"); + + + // includes + assertMethod(IViewDataBinder.class, "getIncludedLayoutConflict"); + assertMethod(BasicBindingBinder.class, "getIncludedLayoutShared"); + assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutPort"); + assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutLand"); + + assertField(BasicBindingBinder.class, "mIncludedLayoutConflict"); + assertField(BasicBindingBinder.class, "mIncludedLayoutShared"); + assertField(ConditionalBindingBinder.class, "mIncludedLayoutPort"); + + assertNoField("mIncludedLayoutLand"); + } +} diff --git a/tools/data-binding/TestApp/src/main/AndroidManifest.xml b/tools/data-binding/TestApp/src/main/AndroidManifest.xml index 83e112100072b..ae2581a67666e 100644 --- a/tools/data-binding/TestApp/src/main/AndroidManifest.xml +++ b/tools/data-binding/TestApp/src/main/AndroidManifest.xml @@ -18,7 +18,8 @@ android:label="@string/app_name" android:icon="@drawable/ic_launcher" > - + diff --git a/tools/data-binding/TestApp/src/main/res/layout-land/multi_res_layout.xml b/tools/data-binding/TestApp/src/main/res/layout-land/multi_res_layout.xml new file mode 100644 index 0000000000000..b70f029266eb0 --- /dev/null +++ b/tools/data-binding/TestApp/src/main/res/layout-land/multi_res_layout.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/data-binding/TestApp/src/main/res/layout/multi_res_layout.xml b/tools/data-binding/TestApp/src/main/res/layout/multi_res_layout.xml new file mode 100644 index 0000000000000..6ec7402279078 --- /dev/null +++ b/tools/data-binding/TestApp/src/main/res/layout/multi_res_layout.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/data-binding/baseLibrary/build.gradle b/tools/data-binding/baseLibrary/build.gradle index e32fabf98c609..d5ff440194ba4 100644 --- a/tools/data-binding/baseLibrary/build.gradle +++ b/tools/data-binding/baseLibrary/build.gradle @@ -1,62 +1,9 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'application' - -sourceCompatibility = 1.7 -mainClassName = "org.antlr.v4.Tool" - -buildscript { - repositories { - mavenLocal() - mavenCentral() - } -} repositories { mavenCentral() } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } - test { - java { - srcDir 'src/test/java' - } - } -} - dependencies { - compile 'com.tunnelvisionlabs:antlr4:4.4' - testCompile 'junit:junit:4.11' -} - -uploadArchives { - repositories { - mavenDeployer { - repository(url: mavenLocal().url) - pom.version = '0.3-SNAPSHOT' - pom.artifactId = 'baseLibrary' - pom.groupId='com.android.databinding' - } - } -} + testCompile group: 'junit', name: 'junit', version: '4.11' +} \ No newline at end of file diff --git a/tools/data-binding/build.gradle b/tools/data-binding/build.gradle index 717b9a6070320..d1bd9cd22f3c5 100644 --- a/tools/data-binding/build.gradle +++ b/tools/data-binding/build.gradle @@ -1,6 +1,7 @@ ext.kotlinVersion = '0.10.195' ext.releaseVersion = "0.3" ext.snapshotVersion = "0.3-SNAPSHOT" +ext.androidPluginVersion = "1.0.0" subprojects { group = 'com.android.databinding' diff --git a/tools/data-binding/compiler/src/main/java/com/android/databinding/BindingTarget.java b/tools/data-binding/compiler/src/main/java/com/android/databinding/BindingTarget.java index cd5c87afc1a10..ab0b34c29ec92 100644 --- a/tools/data-binding/compiler/src/main/java/com/android/databinding/BindingTarget.java +++ b/tools/data-binding/compiler/src/main/java/com/android/databinding/BindingTarget.java @@ -25,26 +25,39 @@ import java.util.ArrayList; import java.util.List; public class BindingTarget { - Node mNode; String mId; String mViewClass; List mBindings = new ArrayList<>(); ExprModel mModel; Class mResolvedClass; String mIncludedLayout; + // if this target presents itself in multiple layout files with different view types, + // it receives an interface type and should use it in the getter instead. + String mInterfaceType; + // if this target is inherited from a common layout interface, used is false so that we don't + // create find view by id etc for it. + boolean mUsed; - public BindingTarget(Node node, String id, String viewClass) { - mNode = node; + public BindingTarget(String id, String viewClass, boolean used) { mId = id; mViewClass = viewClass; + mUsed = used; + } + + public boolean isUsed() { + return mUsed; } public void addBinding(String name, Expr expr) { mBindings.add(new Binding(this, name, expr)); } - public Node getNode() { - return mNode; + public void setInterfaceType(String interfaceType) { + mInterfaceType = interfaceType; + } + + public String getInterfaceType() { + return mInterfaceType == null ? mViewClass : mInterfaceType; } public String getId() { diff --git a/tools/data-binding/compiler/src/main/java/com/android/databinding/DataBinder.java b/tools/data-binding/compiler/src/main/java/com/android/databinding/DataBinder.java index ff379a4cba0b0..4503939ef2cba 100644 --- a/tools/data-binding/compiler/src/main/java/com/android/databinding/DataBinder.java +++ b/tools/data-binding/compiler/src/main/java/com/android/databinding/DataBinder.java @@ -31,6 +31,7 @@ import org.xml.sax.SAXException; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Objects; @@ -53,7 +54,7 @@ public class DataBinder { private static final String XPATH_IMPORT_DEFINITIONS = "//import"; final String LAYOUT_PREFIX = "@layout/"; - List mLayoutBinders = new ArrayList<>(); + HashMap> mLayoutBinders = new HashMap<>(); public LayoutBinder parseXml(File xml, String pkg) throws ParserConfigurationException, IOException, SAXException, @@ -120,7 +121,7 @@ public class DataBinder { fullClassName = getFullViewClassName(nodeName); } final BindingTarget bindingTarget = layoutBinder - .createBindingTarget(parent, id.getNodeValue(), fullClassName); + .createBindingTarget(id.getNodeValue(), fullClassName, true); bindingTarget.setIncludedLayout(layoutName); int attrCount = attributes.getLength(); for (int i = 0; i < attrCount; i ++) { @@ -137,7 +138,10 @@ public class DataBinder { } if (!layoutBinder.isEmpty()) { - mLayoutBinders.add(layoutBinder); + if (!mLayoutBinders.containsKey(xml.getName())) { + mLayoutBinders.put(xml.getName(), new ArrayList()); + } + mLayoutBinders.get(xml.getName()).add(layoutBinder); } return layoutBinder; } @@ -178,7 +182,7 @@ public class DataBinder { return viewName; } - public List getLayoutBinders() { + public HashMap> getLayoutBinders() { return mLayoutBinders; } } diff --git a/tools/data-binding/compiler/src/main/java/com/android/databinding/LayoutBinder.java b/tools/data-binding/compiler/src/main/java/com/android/databinding/LayoutBinder.java index 88c9163da226a..cb2fc8d5ff4a4 100644 --- a/tools/data-binding/compiler/src/main/java/com/android/databinding/LayoutBinder.java +++ b/tools/data-binding/compiler/src/main/java/com/android/databinding/LayoutBinder.java @@ -17,7 +17,11 @@ package com.android.databinding; import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.android.databinding.expr.Dependency; +import com.android.databinding.util.Log; +import com.android.databinding.util.ParserHelper; import com.android.databinding.writer.LayoutBinderWriter; import com.android.databinding.expr.Expr; import com.android.databinding.expr.ExprModel; @@ -27,8 +31,13 @@ import com.android.databinding.util.L; import org.w3c.dom.Node; +import java.io.File; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; /** * Keeps all information about the bindings per layout file @@ -45,9 +54,16 @@ public class LayoutBinder { private String mProjectPackage; private String mBaseClassName; private String mLayoutname; + private File mFile; + private int id; + private final HashMap mUserDefinedVariables = new HashMap<>(); + private final HashMap mUserDefinedImports = new HashMap<>(); private LayoutBinderWriter mWriter; + // layout has different definitions in different configurations + private boolean mHasVariations = false; + public LayoutBinder(Node root) { mRoot = root; mExprModel = new ExprModel(); @@ -55,22 +71,55 @@ public class LayoutBinder { mBindingTargets = new ArrayList<>(); } + public void resolveWhichExpressionsAreUsed() { + List used = new ArrayList<>(); + for (BindingTarget target : mBindingTargets) { + for (Binding binding : target.getBindings()) { + binding.getExpr().setIsUsed(true); + used.add(binding.getExpr()); + } + } + while (!used.isEmpty()) { + Expr e = used.remove(used.size() - 1); + for (Dependency dep : e.getDependencies()) { + if (!dep.getOther().isUsed()) { + used.add(dep.getOther()); + dep.getOther().setIsUsed(true); + } + } + } + } + public IdentifierExpr addVariable(String name, String type) { + Preconditions.checkState(!mUserDefinedVariables.containsKey(name), + "%s has already been defined as %s", name, type); final IdentifierExpr id = mExprModel.identifier(name); id.setUserDefinedType(type); id.enableDirectInvalidation(); + mUserDefinedVariables.put(name, type); return id; } public StaticIdentifierExpr addImport(String alias, String type) { + Preconditions.checkState(!mUserDefinedImports.containsKey(alias), + "%s has already been defined as %s", alias, type); final StaticIdentifierExpr id = mExprModel.staticIdentifier(alias); L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName()); id.setUserDefinedType(type); + mUserDefinedImports.put(alias, type); return id; } - public BindingTarget createBindingTarget(Node parent, String nodeValue, String viewClassName) { - final BindingTarget target = new BindingTarget(parent, nodeValue, viewClassName); + public HashMap getUserDefinedVariables() { + return mUserDefinedVariables; + } + + public HashMap getUserDefinedImports() { + return mUserDefinedImports; + } + + public BindingTarget createBindingTarget(String nodeValue, String viewClassName, boolean used) { + final BindingTarget target = new BindingTarget(nodeValue, viewClassName, used); mBindingTargets.add(target); target.setModel(mExprModel); return target; @@ -147,10 +196,48 @@ public class LayoutBinder { } public String getClassName() { - return mBaseClassName + "Impl"; + final String suffix; + if (hasVariations()) { + // append configuration specifiers. + final String parentFileName = mFile.getParentFile().getName(); + L.d("parent file for %s is %s", mFile.getName(), parentFileName); + if ("layout".equals(parentFileName)) { + suffix = ""; + } else { + suffix = ParserHelper.INSTANCE$.toClassName(parentFileName.substring("layout-".length())); + } + } else { + suffix = ""; + } + return mBaseClassName + suffix + "Impl"; + } public String getInterfaceName() { return mBaseClassName; } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public File getFile() { + return mFile; + } + + public void setFile(File file) { + mFile = file; + } + + public boolean hasVariations() { + return mHasVariations; + } + + public void setHasVariations(boolean hasVariations) { + mHasVariations = hasVariations; + } } diff --git a/tools/data-binding/compiler/src/main/java/com/android/databinding/expr/Expr.java b/tools/data-binding/compiler/src/main/java/com/android/databinding/expr/Expr.java index c1f4244b7bab9..a773d66656efc 100644 --- a/tools/data-binding/compiler/src/main/java/com/android/databinding/expr/Expr.java +++ b/tools/data-binding/compiler/src/main/java/com/android/databinding/expr/Expr.java @@ -87,6 +87,7 @@ abstract public class Expr { * Used by generators when this expression is resolved. */ private boolean mRead; + private boolean mIsUsed = false; Expr(Iterable children) { for (Expr expr : children) { @@ -555,6 +556,14 @@ abstract public class Expr { return false; } + public void setIsUsed(boolean isUsed) { + mIsUsed = isUsed; + } + + public boolean isUsed() { + return mIsUsed; + } + static class Node { BitSet mBitSet = new BitSet(); diff --git a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/main.kt b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/main.kt index a4ace7d15aee9..6cc17a261ae20 100644 --- a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/main.kt +++ b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/main.kt @@ -52,6 +52,7 @@ import com.android.databinding.DataBinder import com.android.databinding.writer.DataBinderWriter import com.android.databinding.ClassAnalyzer import com.android.databinding.util.ParserHelper +import com.google.common.base.Preconditions public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Iterable, @@ -97,26 +98,109 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite findXmlFiles(it, xmlFiles) } } - //viewBinderRenderers.clear() + var layoutId = 0 for (xml in xmlFiles) { log("xmlFile $xml") - val layoutBinder = parseAndStripXml(xml, "$appPkg.generated") + val layoutBinder = parseAndStripXml(xml, "$appPkg.generated", layoutId.toString()) if (layoutBinder == null) { log("no bindings in $xml, skipping") continue } + layoutBinder.setId(layoutId) layoutBinder.setProjectPackage("$appPkg"); layoutBinder.setPackage("$appPkg.generated") layoutBinder.setBaseClassName("${ParserHelper.toClassName(xml.name)}Binder") layoutBinder.setLayoutname(toLayoutId(xml.name)) - + layoutBinder.setFile(xml) + layoutId ++ } + validateMultiResFiles() dbr = DataBinderWriter("com.android.databinding.library", appPkg, "GeneratedDataBinderRenderer", jDataBinder.getLayoutBinders()) } + /** + * checks which layout fields are defined in multiple places. Ensures their + * variables do not conflict + */ + public fun validateMultiResFiles() { + jDataBinder.getLayoutBinders() + .filter { it.getValue().size() > 1 } + .forEach { layout -> + // validate all ids are in correct view types + // and all variables have the same name + val variableTypes = hashMapOf() + val importTypes = hashMapOf() + + layout.getValue().forEach { + it.setHasVariations(true) + it.getUserDefinedVariables().forEach { + Preconditions.checkState(variableTypes.getOrPut(it.getKey(), {it.getValue()}).equals(it.getValue()), + "inconsistent variable types for %s for layout %s", it.getKey(), layout.key) + } + it.getUserDefinedImports().forEach { + Preconditions.checkState(importTypes.getOrPut(it.getKey(), {it.getValue()}).equals(it.getValue()), + "inconsistent import types for %s for layout %s", it.getKey(), layout.key) + } + } + // now add missing ones to each to ensure they can be referenced + Log.d { "checking for missing variables in ${layout.getKey()}" } + layout.getValue().forEach { binder -> + // TODO need to remove unused variables while generating the code. + variableTypes.filterNot { binder.getUserDefinedVariables().containsKey(it.key) } + .forEach { + binder.addVariable(it.key, it.value) + Log.d {"adding missing variable ${it.key} / ${it.value} to binder ${binder.getId()}"} + } + importTypes.filterNot { binder.getUserDefinedImports().containsKey(it.key) } + .forEach { + binder.addImport(it.key, it.value) + Log.d {"adding missing import ${it.key} / ${it.value} to binder ${binder.getId()}"} + } + } + val includeBindingIds = hashSetOf() + val viewBindingIds = hashSetOf() + val viewTypes = hashMapOf() + layout.getValue().forEach { + it.getBindingTargets().forEach { + if (it.isBinder()) { + Preconditions.checkState(!viewBindingIds.contains(it.mViewClass), + """Cannot use the same id for a View and an include tag. Error + for layout file ${layout.key}""") + includeBindingIds.add(it.mViewClass) + } else { + Preconditions.checkState(!includeBindingIds.contains(it.mViewClass), + """Cannot use the same id for a View and an include tag. Error + for layout file ${layout.key}""") + viewBindingIds.add(it.mViewClass) + } + if (it.mViewClass != viewTypes.getOrPut(it.getId(), {it.mViewClass})) { + if (it.isBinder()) { + viewTypes.put(it.getId(), "com.android.databinding.library.IViewDataBinder") + } else { + viewTypes.put(it.getId(), "android.view.View") + } + + } + } + } + layout.getValue().forEach { binder -> + viewTypes.forEach { common -> + val target = binder.getBindingTargets().firstOrNull { it.mId == common.key } + if (target == null) { + // undefined, just define + binder.createBindingTarget(common.key, common.value, false) + } else { + // set type + target.setInterfaceType(common.value) + } + } + } + } + } + public fun generatedCode() : Boolean { - return jDataBinder.getLayoutBinders().isNotEmpty() + return jDataBinder.getLayoutBinders().size() > 0 } public fun writeAttrFile() { @@ -127,7 +211,7 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite public fun writeDbrFile() : Unit = writeDbrFile(dbrOutputDir) public fun writeDbrFile(dir : File) : Unit { dir.mkdirs() - if (dbr.layoutBinders.isNotEmpty()) { + if (dbr.layoutBinders.size() > 0) { writeToFile(File(dir, "${dbr.className}.java"), dbr.write()) } } @@ -137,7 +221,7 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite public fun writeViewBinderInterfaces(dir : File) : Unit { dir.mkdirs() jDataBinder.getLayoutBinders().forEach { - writeToFile(File(dir, "${it.getInterfaceName()}.java"), it.writeViewBinderInterface()) + writeToFile(File(dir, "${it.value.first!!.getInterfaceName()}.java"), it.value.first!!.writeViewBinderInterface()) } } @@ -146,7 +230,9 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite public fun writeViewBinders(dir : File) : Unit { dir.mkdirs() jDataBinder.getLayoutBinders().forEach { - writeToFile(File(dir, "${it.getClassName()}.java"), it.writeViewBinder()) + it.getValue().forEach { + writeToFile(File(dir, "${it.getClassName()}.java"), it.writeViewBinder()) + } } } @@ -159,15 +245,15 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite private fun toLayoutId(name:String) : String = name.substring(0, name.indexOf(".")) - private fun stripBindingTags(xml : File) { - val res = XmlEditor.strip(xml) + private fun stripBindingTags(xml : File, newTag : String? = null) { + val res = XmlEditor.strip(xml, newTag) if (res != null) { Log.d{"file ${xml.getName()} has changed, overwriting ${xml.getAbsolutePath()}"} xml.writeText(res) } } - private fun stripFileAndGetOriginal(xml : File) : File? { + private fun stripFileAndGetOriginal(xml : File, binderId : String) : File? { System.out.println("parsing resourceY file ${xml.getAbsolutePath()}") val factory = DocumentBuilderFactory.newInstance() val builder = factory.newDocumentBuilder() @@ -194,14 +280,13 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite val variableNodes = getVariableNodes(doc, xPath) var changed = variableNodes.getLength() > 0 //TODO do we need to check more? if (changed) { - stripBindingTags(xml) + stripBindingTags(xml, binderId) } - return actualFile } - private fun parseAndStripXml(xml : File, pkg : String) : LayoutBinder? { - val original = stripFileAndGetOriginal(xml) + private fun parseAndStripXml(xml : File, pkg : String, binderId : String) : LayoutBinder? { + val original = stripFileAndGetOriginal(xml, binderId) return if (original == null) { null } else { diff --git a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/ParserHelper.kt b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/ParserHelper.kt index bb15afcd8db09..e1474b47415f0 100644 --- a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/ParserHelper.kt +++ b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/ParserHelper.kt @@ -17,7 +17,7 @@ object ParserHelper { public fun toClassName(name:String) : String { val dot = name.indexOf(".") val stripped = if (dot == -1) name else name.substring(0, name.indexOf(".")) - return stripped.split("_").map { "${it.substring(0,1).toUpperCase()}${it.substring(1)}" }.join("") + return stripped.split("[_-]").map { "${it.substring(0,1).toUpperCase()}${it.substring(1)}" }.join("") } } \ No newline at end of file diff --git a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt index 815108d676750..e57b661075f3e 100644 --- a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt +++ b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt @@ -28,6 +28,7 @@ import com.android.databinding.Position import com.android.databinding.toPosition import com.android.databinding.toEndPosition import java.util.Comparator +import com.google.common.base.Preconditions /** * Ugly inefficient class to strip unwanted tags from XML. @@ -35,9 +36,14 @@ import java.util.Comparator */ object XmlEditor { val reservedElementNames = arrayListOf("variable", "import") + var rootNodeContext: XMLParser.ElementContext? = null + var rootNodeHasTag = false val visitor = object : XMLParserBaseVisitor>>() { override fun visitAttribute(ctx: XMLParser.AttributeContext): MutableList>? { - log{"attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()}"} + log { "attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()} . parent: ${ctx.getParent()}" } + if (ctx.getParent() == rootNodeContext && ctx.attrName.getText() == "android:tag") { + rootNodeHasTag = true + } if (ctx.attrName.getText().startsWith("bind:")) { return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition())) @@ -50,7 +56,10 @@ object XmlEditor { } override fun visitElement(ctx: XMLParser.ElementContext): MutableList>? { - log{"elm ${ctx.elmName.getText()} || ${ctx.Name()}"} + log { "elm ${ctx.elmName.getText()} || ${ctx.Name()} paren : ${ctx.getParent()}" } + if (rootNodeContext == null) { + rootNodeContext = ctx + } if (reservedElementNames.contains(ctx.elmName?.getText()) || ctx.elmName.getText().startsWith("bind:")) { return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition())) } @@ -60,28 +69,37 @@ object XmlEditor { override fun defaultResult(): MutableList>? = arrayListOf() override fun aggregateResult(aggregate: MutableList>?, nextResult: MutableList>?): MutableList>? = - if (aggregate == null) { - nextResult - } else if (nextResult == null) { - aggregate - } else { - aggregate.addAll(nextResult) - aggregate - } + if (aggregate == null) { + nextResult + } else if (nextResult == null) { + aggregate + } else { + aggregate.addAll(nextResult) + aggregate + } } - fun strip(f : File) : String? { + fun strip(f: File, newTag: String? = null): String? { + rootNodeContext = null //clear it + rootNodeHasTag = false val inputStream = ANTLRInputStream(FileReader(f)) val lexer = XMLLexer(inputStream) val tokenStream = CommonTokenStream(lexer) val parser = XMLParser(tokenStream) val expr = parser.document() val parsedExpr = expr.accept(visitor) - if (parsedExpr.size() == 0) { + if (parsedExpr.isEmpty()) { return null//nothing to strip } + Preconditions.checkNotNull(rootNodeContext, "Cannot find root node for ${f.getName()}") + Preconditions.checkState(rootNodeHasTag == false, """You cannot set a tag in the layout + root if you are using binding. Invalid file: ${f}""") + val rootNodeBounds = Pair(rootNodeContext!!.getStart().toPosition(), rootNodeContext!!.getStop().toEndPosition()) + + log { "root node bounds: ${rootNodeBounds}" } val out = StringBuilder() val lines = f.readLines("utf-8") + lines.forEach { out.appendln(it) } // TODO we probably don't need to sort @@ -95,22 +113,30 @@ object XmlEditor { } }) var lineStarts = arrayListOf(0) - lines.withIndices().forEach { - if (it.first > 0) { - lineStarts.add(lineStarts[it.first - 1] + lines[it.first - 1].length() + 1) + lines.withIndex().forEach { + if (it.index > 0) { + lineStarts.add(lineStarts[it.index - 1] + lines[it.index - 1].length() + 1) } } - val seperator = System.lineSeparator().charAt(0) + val separator = System.lineSeparator().charAt(0) sorted.forEach { val posStart = lineStarts[it.first.line] + it.first.charIndex val posEnd = lineStarts[it.second.line] + it.second.charIndex - for( i in posStart..(posEnd - 1)) { - if (out.charAt(i) != seperator) { + for ( i in posStart..(posEnd - 1)) { + if (out.charAt(i) != separator) { out.setCharAt(i, ' ') } } } + Log.d{"new tag to set: $newTag"} + if (newTag != null) { + Preconditions.checkState(rootNodeBounds.first.line != rootNodeBounds.second.line, + """The root tag should be multi line to add the tag. ${f.getName()}""") + val line = rootNodeBounds.first.line + out.insert(lineStarts[line] + lines[line].length(), """ android:tag = "$newTag" """) + } + return out.toString() } diff --git a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt index 6f0b2469a1ebd..c3e0ce1353671 100644 --- a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt +++ b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt @@ -15,7 +15,7 @@ package com.android.databinding.writer import com.android.databinding.LayoutBinder -class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String, val layoutBinders : List ) { +class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String, val layoutBinders : Map> ) { fun write() = kcode("") { tab("package $pkg;") @@ -25,8 +25,23 @@ class DataBinderWriter(val pkg: String, val projectPackage: String, val classNam tab("public com.android.databinding.library.ViewDataBinder getDataBinder(android.view.View view, int layoutId) {") { tab("switch(layoutId) {") { layoutBinders.forEach { - tab("case R.layout.${it.getLayoutname()}:") { - tab("return new ${it.getPackage()}.${it.getClassName()}(view);") + tab("case R.layout.${it.value.first!!.getLayoutname()}:") { + if (it.value.size() == 1) { + tab("return new ${it.value.first!!.getPackage()}.${it.value.first!!.getClassName()}(view);") + } else { + // we should check the tag to decide which layout we need to inflate + tab("{") { + tab("final String tag = (String)view.getTag();") + tab("if(tag == null) throw new java.lang.RuntimeException(\"view must have a tag\");") + it.value.forEach { + // TODO don't use strings. not necessary + tab("if (tag.equals(String.valueOf(${it.getId()}))) {") { + tab("return new ${it.getPackage()}.${it.getClassName()}(view);") + } tab("}") + } + }tab("}") + } + } } } diff --git a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt index 4cd0edf455058..1bf01097cc168 100644 --- a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt +++ b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt @@ -323,34 +323,39 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { model.getExprMap().values().filterIsInstance(javaClass()).filter { it.isVariable() } } - public fun write() : String = - kcode("package ${layoutBinder.getPackage()};") { - nl("import ${layoutBinder.getProjectPackage()}.R;") - nl("import android.view.View;") - nl("public class ${className} extends com.android.databinding.library.ViewDataBinder implements ${interfaceName} {") { - tab(declareViews()) - tab(declareVariables()) - tab(declareConstructor()) - tab(declareInvalidateAll()) - tab(declareLog()) - tab(declareSetVariable()) - tab(variableSettersAndGetters()) - tab(viewGetters()) - tab(onFieldChange()) + val usedVariables by Delegates.lazy { + variables.filter {it.isUsed()} + } - tab(rebindDirty()) + public fun write() : String { + layoutBinder.resolveWhichExpressionsAreUsed() + return kcode("package ${layoutBinder.getPackage()};") { + nl("import ${layoutBinder.getProjectPackage()}.R;") + nl("import android.view.View;") + nl("public class ${className} extends com.android.databinding.library.ViewDataBinder implements ${interfaceName} {") { + tab(declareViews()) + tab(declareVariables()) + tab(declareConstructor()) + tab(declareInvalidateAll()) + tab(declareLog()) + tab(declareSetVariable()) + tab(variableSettersAndGetters()) + tab(viewGetters()) + tab(onFieldChange()) - tab(declareDirtyFlags()) - } - nl("}") - tab(flagMapping()) - tab("//end") - }.generate() + tab(rebindDirty()) + tab(declareDirtyFlags()) + } + nl("}") + tab(flagMapping()) + tab("//end") + }.generate() + } fun declareConstructor() = kcode("") { nl("public ${className}(View root) {") { tab("super(root, ${model.getObservables().size()});") - layoutBinder.getBindingTargets().forEach { + layoutBinder.getBindingTargets().filter{it.isUsed()}.forEach { if (it.isBinder()) { tab("this.${it.fieldName} = com.android.databinding.library.DataBinder.createBinder(root.findViewById(${it.androidId}), R.layout.${it.getIncludedLayout()});") } else { @@ -371,7 +376,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { for (i in (0..(mDirtyFlags.buckets.size() - 1))) { tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") } - includedBinders.forEach { binder -> + includedBinders.filter{it.isUsed()}.forEach { binder -> tab("${binder.fieldName}.invalidateAll();") } } @@ -381,7 +386,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { fun declareSetVariable() = kcode("") { nl("public boolean setVariable(int variableId, Object variable) {") { tab("switch(variableId) {") { - variables.forEach { + usedVariables.forEach { tab ("case ${it.getName().br()} :") { tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") tab("return true;") @@ -402,7 +407,18 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { } fun variableSettersAndGetters() = kcode("") { - variables.forEach { + variables.filterNot{it.isUsed()}.forEach { + nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { + tab("// not used, ignore") + } + nl("}") + nl("") + nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { + tab("return ${it.getDefaultValue()};") + } + nl("}") + } + usedVariables.forEach { nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { if (it.isObservable()) { tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});"); @@ -472,7 +488,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { } fun declareViews() = kcode("// views") { - layoutBinder.getBindingTargets().forEach { + layoutBinder.getBindingTargets().filter{it.isUsed()}.forEach { nl("private ${it.getViewClass()} ${it.fieldName};") } } @@ -480,15 +496,20 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { fun viewGetters() = kcode("// view getters") { layoutBinder.getBindingTargets().forEach { nl("@Override") - nl("public ${it.getViewClass()} ${it.getterName}() {") { - tab("return ${it.fieldName};") + nl("public ${it.getInterfaceType()} ${it.getterName}() {") { + if (it.isUsed()) { + tab("return ${it.fieldName};") + } else { + tab("return null;") + } + } nl("}") } } fun declareVariables() = kcode("// variables") { - variables.forEach { + usedVariables.forEach { nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") } } @@ -546,7 +567,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { } while(model.markBitsRead()) // - layoutBinder.getBindingTargets() + layoutBinder.getBindingTargets().filter { it.isUsed() } .flatMap { it.getBindings() } .groupBy { it.getExpr() } .forEach { @@ -562,7 +583,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { tab("}") } // - includedBinders.forEach { binder -> + includedBinders.filter{it.isUsed()}.forEach { binder -> tab("${binder.fieldName}.rebindDirty();") } } @@ -649,7 +670,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { tab("public void ${it.setterName}(${it.getUserDefinedType()} ${it.readableUniqueName});") } layoutBinder.getBindingTargets().forEach { - tab("public ${it.getViewClass()} ${it.getterName}();") + tab("public ${it.getInterfaceType()} ${it.getterName}();") } nl("}") }.generate() diff --git a/tools/data-binding/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/gradle/wrapper/gradle-wrapper.properties index a958d904cf77a..e533849fbddd9 100644 --- a/tools/data-binding/gradle/wrapper/gradle-wrapper.properties +++ b/tools/data-binding/gradle/wrapper/gradle-wrapper.properties @@ -1,22 +1,6 @@ -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -#Wed Apr 10 15:27:10 PDT 2013 +#Mon Feb 02 17:44:27 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip diff --git a/tools/data-binding/gradlePlugin/build.gradle b/tools/data-binding/gradlePlugin/build.gradle index 30836ddc043d6..a5f1011a21e54 100644 --- a/tools/data-binding/gradlePlugin/build.gradle +++ b/tools/data-binding/gradlePlugin/build.gradle @@ -28,7 +28,7 @@ buildscript { } dependencies { - compile 'com.android.tools.build:gradle:0.14.2' + compile "com.android.tools.build:gradle:$androidPluginVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" compile gradleApi() compile 'commons-io:commons-io:2.4' diff --git a/tools/data-binding/compiler/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties b/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties similarity index 100% rename from tools/data-binding/compiler/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties rename to tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties diff --git a/tools/data-binding/library/build.gradle b/tools/data-binding/library/build.gradle index 779cb85ea0430..e444a43d5ae2b 100644 --- a/tools/data-binding/library/build.gradle +++ b/tools/data-binding/library/build.gradle @@ -23,7 +23,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath "com.android.tools.build:gradle:$androidPluginVersion" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/tools/data-binding/samples/BindingDemo/build.gradle b/tools/data-binding/samples/BindingDemo/build.gradle index e4abd4e2cab6f..6dad139f0c9d7 100644 --- a/tools/data-binding/samples/BindingDemo/build.gradle +++ b/tools/data-binding/samples/BindingDemo/build.gradle @@ -23,7 +23,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:0.14.2' classpath 'com.android.databinding:dataBinder:0.3-SNAPSHOT' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files