summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2017-12-21 10:00:25 -0800
committerYigit Boyar <yboyar@google.com>2018-05-18 22:25:47 +0000
commit744db5c5a111d68c30302deae8b4ba1ea9355603 (patch)
treed77df39f15fa9cfffb37ccdf8f1b3fc1471db120
parent24c7c961ba1eb09dccab240bcd18fd71df09a7dc (diff)
downloaddata-binding-744db5c5a111d68c30302deae8b4ba1ea9355603.tar.gz
Fix kotlin.Unit method references in binding layouts.
When there is a binding adapter that receives a kotlin function which does not return, Kotlin converts it to Function0<kotlin.Unit>. But if there is a kotlin class with a method that does not return value, kotlin converts it to void. This becomes a problem for data binding because it cannot match the two when using function references. We cannot simply convert kotlin.Unit to void because we still generate code in java so we have to return a value in the implementation. Instead, this CL changes function matcher to accept the two as equals and also changes the code generators to handle the case manually. Also added a kotlin test app (finally). Bug: 70915745 Bug: 78662035 Test: KotlinTestApp Change-Id: I019ee7eb1dd635b12efd7725ccc22f0c63dc2d72
-rw-r--r--README.md6
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/MethodBaseExpr.java15
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java4
-rw-r--r--compiler/src/main/java/android/databinding/tool/store/SetterStore.java7
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt41
-rw-r--r--integration-tests/.gitignore1
-rw-r--r--integration-tests/KotlinTestApp/.gitignore10
-rw-r--r--integration-tests/KotlinTestApp/app/.gitignore1
-rw-r--r--integration-tests/KotlinTestApp/app/build.gradle65
-rw-r--r--integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/BindingActivityRule.kt39
-rw-r--r--integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/KotlinFunctionAdapterTest.kt75
-rw-r--r--integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/SimpleBindingTest.kt43
-rw-r--r--integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/TwoWayBindingTest.kt51
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml39
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/FunctionAdapterBindingModel.kt39
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TestActivity.kt22
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TextViewBindings.kt36
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TwoWayBindingModel.kt23
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/layout/function_adapter.xml50
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/layout/simple.xml39
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/layout/two_way.xml37
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/values/colors.xml21
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/values/dimens.xml19
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/values/strings.xml20
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/values/styles.xml36
-rw-r--r--integration-tests/KotlinTestApp/build.gradle36
-rw-r--r--integration-tests/KotlinTestApp/gradle.properties31
-rw-r--r--integration-tests/KotlinTestApp/gradlew.bat90
-rw-r--r--integration-tests/KotlinTestApp/settings.gradle17
-rw-r--r--integration-tests/ideCommonBuildScript.gradle1
30 files changed, 888 insertions, 26 deletions
diff --git a/README.md b/README.md
index ecfab2a6..130b8682 100644
--- a/README.md
+++ b/README.md
@@ -72,10 +72,12 @@ Some of data binding tests are only in AGP. To run them:
These are run by gradle build.
`gw :base:build-system:integration-test:application:cIT -D:base:build-system:integration-test:application:connectedIntegrationTest.single=DataBinding\*`
-
We also compile them in bazel builds:
-`bazel test //tools/base/build-system/integration-test/application:tests --test_filter=DataBinding\* --test_output=errors --action_env="GTEST_COLOR=1"`
+`bazel test //tools/base/build-system/integration-test/databinding:tests --test_output=errors
+--action_env="GTEST_COLOR=1"`
+
+You can pass `--test_filter=<test class name>\*` to filter certain tests
If you did run `./init.sh`, you can open integration tests in Android Studio.
diff --git a/compiler/src/main/java/android/databinding/tool/expr/MethodBaseExpr.java b/compiler/src/main/java/android/databinding/tool/expr/MethodBaseExpr.java
index 5f6e1783..3e51b517 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MethodBaseExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MethodBaseExpr.java
@@ -83,9 +83,20 @@ public abstract class MethodBaseExpr extends Expr {
final ModelClass[] listenerParameters = listenerMethod.getParameterTypes();
boolean isStatic = getTarget() instanceof StaticIdentifierExpr;
List<ModelMethod> methods = childType.findMethods(mName, isStatic);
+
+ final ModelClass listenerMethodReturnType = listenerMethod.getReturnType(null);
+ final boolean listenerIsKotlinUnit = listenerMethodReturnType.isKotlinUnit();
for (ModelMethod method : methods) {
- if (acceptsParameters(method, listenerParameters) &&
- method.getReturnType(null).equals(listenerMethod.getReturnType(null))) {
+ final boolean parametersMatch = acceptsParameters(method, listenerParameters);
+ if (!parametersMatch) {
+ // no need to check return types.
+ continue;
+ }
+ final ModelClass methodReturnType = method.getReturnType(null);
+ // accept kotlin.Unit vs void. b/78662035
+ final boolean returnTypesMatch = methodReturnType.equals(listenerMethodReturnType)
+ || (listenerIsKotlinUnit && methodReturnType.isVoid());
+ if (returnTypesMatch) {
target.getParents().remove(this);
resetResolvedType();
// replace this with ListenerExpr in parent
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
index 10218782..557ad25a 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
@@ -698,4 +698,8 @@ public abstract class ModelClass {
}
return false;
}
+
+ public boolean isKotlinUnit() {
+ return "kotlin.Unit".equals(getTypeName().toString());
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
index 24ad9e76..239ca848 100644
--- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
+++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -241,12 +241,9 @@ public class SetterStore {
ExecutableElement bindingMethod, boolean takesComponent) {
attribute = stripNamespace(attribute);
L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod);
- HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
+ HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods
+ .computeIfAbsent(attribute, k -> new HashMap<>());
- if (adapters == null) {
- adapters = new HashMap<AccessorKey, MethodDescription>();
- mStore.adapterMethods.put(attribute, adapters);
- }
List<? extends VariableElement> parameters = bindingMethod.getParameters();
final int viewIndex = takesComponent ? 1 : 0;
TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType());
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
index 18317af9..3581aaf6 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
@@ -607,7 +607,9 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
callbacks.groupBy { it.callbackWrapper }.forEach {
val wrapper = it.key
val lambdas = it.value
- val shouldReturn = !wrapper.method.returnType.isVoid
+ // special case kotlin unit. b/78662035
+ val returnKotlinUnit = wrapper.method.returnType.isKotlinUnit
+ val shouldReturn = !wrapper.method.returnType.isVoid && !returnKotlinUnit
if (shouldReturn) {
lambdas.forEach {
it.callbackExprModel.ext.forceLocalize.add(it.expr)
@@ -621,6 +623,8 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
nl(lambda.executionPath.toCode())
if (shouldReturn) {
nl("return ${lambda.expr.scopedName()};")
+ } else if (returnKotlinUnit) {
+ nl("return null;")
}
} else {
block("switch(${CallbackWrapper.SOURCE_ID})") {
@@ -628,10 +632,10 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
block("case ${lambda.callbackId}:") {
nl(lambda.callbackExprModel.localizeGlobalVariables(lambda))
nl(lambda.executionPath.toCode())
- if (shouldReturn) {
- nl("return ${lambda.expr.scopedName()};")
- } else {
- nl("break;")
+ when {
+ shouldReturn -> nl("return ${lambda.expr.scopedName()};")
+ returnKotlinUnit -> nl("return null;")
+ else -> nl("break;")
}
}
}
@@ -1191,7 +1195,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
} else {
extendsImplements = "extends"
}
- nl("public static class ${expr.listenerClassName} $extendsImplements ${listenerType.canonicalName}{") {
+ nl("public static class ${expr.listenerClassName} $extendsImplements $listenerType{") {
if (expr.target.isDynamic) {
tab("private ${expr.target.resolvedType.toJavaCode()} value;")
tab("public ${expr.listenerClassName} setValue(${expr.target.resolvedType.toJavaCode()} value) {") {
@@ -1209,22 +1213,25 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
"${it.value.toJavaCode()} arg${it.index}"
}.joinToString(", ")
}) {") {
- val obj: String
- if (expr.target.isDynamic) {
- obj = "this.value"
+ val obj = if (expr.target.isDynamic) {
+ "this.value"
} else {
- obj = expr.target.toCode().generate();
+ expr.target.toCode().generate()
}
- val returnStr: String
- if (!returnType.isVoid) {
- returnStr = "return "
+ val returnSuffix = if(returnType.isKotlinUnit) {
+ "return null;"
} else {
- returnStr = ""
+ ""
}
- val args = parameterTypes.withIndex().map {
+ val returnPrefix = if (!returnType.isVoid && !returnType.isKotlinUnit) {
+ "return "
+ } else {
+ ""
+ }
+ val args = parameterTypes.withIndex().joinToString(", ") {
"arg${it.index}"
- }.joinToString(", ")
- tab("$returnStr$obj.${expr.name}($args);")
+ }
+ tab("$returnPrefix$obj.${expr.name}($args); $returnSuffix")
}
tab("}")
}
diff --git a/integration-tests/.gitignore b/integration-tests/.gitignore
new file mode 100644
index 00000000..bff2d762
--- /dev/null
+++ b/integration-tests/.gitignore
@@ -0,0 +1 @@
+*.iml
diff --git a/integration-tests/KotlinTestApp/.gitignore b/integration-tests/KotlinTestApp/.gitignore
new file mode 100644
index 00000000..5edb4eeb
--- /dev/null
+++ b/integration-tests/KotlinTestApp/.gitignore
@@ -0,0 +1,10 @@
+*.iml
+.gradle
+/local.properties
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/integration-tests/KotlinTestApp/app/.gitignore b/integration-tests/KotlinTestApp/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/integration-tests/KotlinTestApp/app/build.gradle b/integration-tests/KotlinTestApp/app/build.gradle
new file mode 100644
index 00000000..42d0edd9
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/build.gradle
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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: 'com.android.application'
+
+apply plugin: 'kotlin-android'
+
+apply plugin: 'kotlin-android-extensions'
+
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion rootProject.latestCompileSdk
+ buildToolsVersion rootProject.buildToolsVersion
+ defaultConfig {
+ applicationId "androidx.databinding.kotlintestapp"
+ minSdkVersion 14
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ dataBinding {
+ enabled = true
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+}
+
+dependencies {
+ def supportVersion = "1.0.0-alpha1"
+ def lifecycleVersion = "2.0.0-alpha1"
+ def testingVersion = "1.1.0-alpha2"
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.kotlinVersion"
+ implementation "androidx.lifecycle:lifecycle-runtime:$lifecycleVersion"
+ implementation "androidx.lifecycle:lifecycle-livedata:$lifecycleVersion"
+ implementation "androidx.appcompat:appcompat:$supportVersion"
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation("androidx.test:runner:$testingVersion") {
+ exclude module: 'support-annotations'
+ }
+ androidTestImplementation("androidx.test:rules:$testingVersion") {
+ exclude module: 'support-annotations'
+ }
+
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0-alpha2') {
+ exclude module: 'support-annotations'
+ }
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/BindingActivityRule.kt b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/BindingActivityRule.kt
new file mode 100644
index 00000000..e8aa495f
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/BindingActivityRule.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
+import androidx.test.rule.ActivityTestRule
+
+class BindingActivityRule<out B : ViewDataBinding>(private val layoutId: Int)
+ : ActivityTestRule<TestActivity>(TestActivity::class.java) {
+ val binding: B by lazy {
+ val activity = activity
+ var result: B? = null
+ runOnUiThread {
+ result = DataBindingUtil.setContentView(activity, layoutId)!!
+ }
+ result!!
+ }
+
+ fun executePendingBindings() {
+ runOnUiThread {
+ binding.executePendingBindings()
+ }
+ }
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/KotlinFunctionAdapterTest.kt b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/KotlinFunctionAdapterTest.kt
new file mode 100644
index 00000000..253b53c9
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/KotlinFunctionAdapterTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import androidx.databinding.kotlintestapp.databinding.FunctionAdapterBinding
+import androidx.test.espresso.Espresso
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.longClick
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.runner.AndroidJUnit4
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class KotlinFunctionAdapterTest {
+ @Suppress("MemberVisibilityCanPrivate")
+ @Rule
+ @JvmField
+ val rule = BindingActivityRule<FunctionAdapterBinding>(R.layout.function_adapter)
+
+ @Test
+ fun methodReference() {
+ val model = FunctionAdapterBindingModel()
+ rule.binding.model = model
+ rule.executePendingBindings()
+ assertThat(model.refenceMethodCallCnt, `is`(1))
+ }
+
+ @Test
+ fun lambdaMethod() {
+ val model = FunctionAdapterBindingModel()
+ rule.binding.model = model
+ rule.executePendingBindings()
+ assertThat(model.lambdaMethodCallCnt, `is`(1))
+ }
+
+ @Test
+ fun methodRefenceWithParameter() {
+ val model = FunctionAdapterBindingModel()
+ rule.binding.model = model
+ rule.executePendingBindings()
+ assertThat(model.longClickCallbackCount, `is`(0))
+ onView(withId(R.id.textView1)).perform(longClick())
+ Espresso.onIdle()
+ assertThat(model.longClickCallbackCount, `is`(1))
+ }
+
+ @Test
+ fun lambdaFunction() {
+ val model = FunctionAdapterBindingModel()
+ rule.binding.model = model
+ rule.executePendingBindings()
+ assertThat(model.longClickCallbackCount, `is`(0))
+ onView(withId(R.id.textView2)).perform(longClick())
+ Espresso.onIdle()
+ assertThat(model.longClickCallbackCount, `is`(1))
+ }
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/SimpleBindingTest.kt b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/SimpleBindingTest.kt
new file mode 100644
index 00000000..784ffd77
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/SimpleBindingTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import androidx.databinding.kotlintestapp.databinding.SimpleBinding
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SimpleBindingTest {
+
+ @Suppress("MemberVisibilityCanPrivate")
+ @Rule
+ @JvmField
+ val rule = BindingActivityRule<SimpleBinding>(R.layout.simple)
+
+ @Test
+ fun textBinding() {
+ rule.binding.foo = "blah"
+ rule.executePendingBindings()
+ onView(withId(R.id.text1)).check(matches(withText("blah")))
+ }
+}
diff --git a/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/TwoWayBindingTest.kt b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/TwoWayBindingTest.kt
new file mode 100644
index 00000000..157526e3
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/TwoWayBindingTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import androidx.databinding.kotlintestapp.databinding.TwoWayBinding
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.runner.AndroidJUnit4
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TwoWayBindingTest {
+ @Suppress("MemberVisibilityCanPrivate")
+ @Rule
+ @JvmField
+ val rule = BindingActivityRule<TwoWayBinding>(R.layout.two_way)
+
+ @Test
+ fun editText() {
+ val model = TwoWayBindingModel()
+ model.username.set("foo")
+ rule.binding.model = model
+ rule.executePendingBindings()
+ onView(withId(R.id.userNameInput)).check(matches(withText("foo")))
+ onView(withId(R.id.userNameInput)).perform(ViewActions.clearText())
+ onView(withId(R.id.userNameInput)).perform(ViewActions.typeText("bar"))
+ rule.executePendingBindings()
+ assertThat(model.username.get(), `is`("bar"))
+ }
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml b/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..364d420e
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="androidx.databinding.kotlintestapp">
+
+ <application
+ android:allowBackup="true"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme"
+ tools:ignore="GoogleAppIndexingWarning">
+ <activity
+ android:name=".TestActivity"
+ android:label="@string/title_activity_test_activty"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/FunctionAdapterBindingModel.kt b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/FunctionAdapterBindingModel.kt
new file mode 100644
index 00000000..0f6356ae
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/FunctionAdapterBindingModel.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import android.view.View
+
+class FunctionAdapterBindingModel {
+ var refenceMethodCallCnt = 0
+ var lambdaMethodCallCnt = 0
+ var longClickCallbackCount = 0
+ var someValue: String = "val"
+
+ fun referencedMethod() {
+ refenceMethodCallCnt++
+ }
+
+ fun lambdaMethod() {
+ lambdaMethodCallCnt++
+ }
+
+ fun longClickCallback(view: View): Boolean {
+ longClickCallbackCount++
+ return true
+ }
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TestActivity.kt b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TestActivity.kt
new file mode 100644
index 00000000..d2f13bd5
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TestActivity.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import androidx.appcompat.app.AppCompatActivity
+
+class TestActivity : AppCompatActivity() {
+}
diff --git a/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TextViewBindings.kt b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TextViewBindings.kt
new file mode 100644
index 00000000..a9e04090
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TextViewBindings.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import android.annotation.SuppressLint
+import android.widget.TextView
+import androidx.databinding.BindingAdapter
+
+object TextViewBindings {
+ @JvmStatic
+ @BindingAdapter("kotlinFunction")
+ fun adapterWithKotlinFunction(textView: TextView, listener: (() -> Unit)?) {
+ listener?.invoke()
+ }
+
+ @SuppressLint("SetTextI18n")
+ @JvmStatic
+ @BindingAdapter("genericSetText")
+ fun <T> genericSetText(textView: TextView, input: T) {
+ textView.text = "x_${input}_x"
+ }
+}
diff --git a/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TwoWayBindingModel.kt b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TwoWayBindingModel.kt
new file mode 100644
index 00000000..03c33987
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/TwoWayBindingModel.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 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 androidx.databinding.kotlintestapp
+
+import androidx.databinding.ObservableField
+
+class TwoWayBindingModel {
+ val username = ObservableField<String>()
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/layout/function_adapter.xml b/integration-tests/KotlinTestApp/app/src/main/res/layout/function_adapter.xml
new file mode 100644
index 00000000..f86780d9
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/layout/function_adapter.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <data>
+
+ <variable
+ name="model"
+ type="androidx.databinding.kotlintestapp.FunctionAdapterBindingModel" />
+ </data>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/textView1"
+ android:text="bar"
+ android:onLongClick="@{model::longClickCallback}"
+ app:kotlinFunction="@{model::referencedMethod}"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/textView2"
+ app:kotlinFunction="@{() -> model.lambdaMethod()}"
+ android:onLongClick="@{(v) -> model.longClickCallback(v)}"
+ app:genericSetText="@{model.someValue}"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+</layout> \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/layout/simple.xml b/integration-tests/KotlinTestApp/app/src/main/res/layout/simple.xml
new file mode 100644
index 00000000..ff6c6209
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/layout/simple.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <data>
+
+ <variable
+ name="foo"
+ type="String" />
+ </data>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".SimpleBindingActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@{foo}"
+ android:id="@+id/text1" />
+ </LinearLayout>
+</layout> \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/layout/two_way.xml b/integration-tests/KotlinTestApp/app/src/main/res/layout/two_way.xml
new file mode 100644
index 00000000..be2e326d
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/layout/two_way.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <data>
+
+ <variable
+ name="model"
+ type="androidx.databinding.kotlintestapp.TwoWayBindingModel" />
+ </data>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/userNameInput"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@={model.username}" />
+ </LinearLayout>
+</layout> \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/values/colors.xml b/integration-tests/KotlinTestApp/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..235d5d03
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/values/dimens.xml b/integration-tests/KotlinTestApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..b18be617
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<resources>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/values/strings.xml b/integration-tests/KotlinTestApp/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..2814e638
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<resources>
+ <string name="app_name">KotlinTestApp</string>
+ <string name="title_activity_test_activty">TestActivty</string>
+</resources>
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/values/styles.xml b/integration-tests/KotlinTestApp/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..8147ead1
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,36 @@
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>
diff --git a/integration-tests/KotlinTestApp/build.gradle b/integration-tests/KotlinTestApp/build.gradle
new file mode 100644
index 00000000..ff3a7d1b
--- /dev/null
+++ b/integration-tests/KotlinTestApp/build.gradle
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+buildscript {
+ def runningInIde = project.hasProperty('android.injected.invoked.from.ide')
+ rootProject.ext.runningInIde = runningInIde
+ if (runningInIde) {
+ apply from: "../ideCommonBuildScript.gradle"
+ } else {
+ apply from: "../commonHeader.gradle"
+ apply from: "../commonBuildScript.gradle"
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.kotlinVersion"
+ }
+}
+if (!rootProject.ext.runningInIde) {
+ apply from: "../commonHeader.gradle"
+ subprojects {
+ project.apply from: "../../commonHeader.gradle"
+ project.apply from: "../../commonLocalRepo.gradle"
+ }
+} \ No newline at end of file
diff --git a/integration-tests/KotlinTestApp/gradle.properties b/integration-tests/KotlinTestApp/gradle.properties
new file mode 100644
index 00000000..a22b4868
--- /dev/null
+++ b/integration-tests/KotlinTestApp/gradle.properties
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2018 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.
+#
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+android.databinding.enableV2=true
+android.useAndroidX=true
+android.enableJetifier=true
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/integration-tests/KotlinTestApp/gradlew.bat b/integration-tests/KotlinTestApp/gradlew.bat
new file mode 100644
index 00000000..aec99730
--- /dev/null
+++ b/integration-tests/KotlinTestApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/integration-tests/KotlinTestApp/settings.gradle b/integration-tests/KotlinTestApp/settings.gradle
new file mode 100644
index 00000000..9b40b078
--- /dev/null
+++ b/integration-tests/KotlinTestApp/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+include ':app'
diff --git a/integration-tests/ideCommonBuildScript.gradle b/integration-tests/ideCommonBuildScript.gradle
index a8ef4908..0a07a6c9 100644
--- a/integration-tests/ideCommonBuildScript.gradle
+++ b/integration-tests/ideCommonBuildScript.gradle
@@ -3,6 +3,7 @@ rootProject.buildscript {
apply from: "${rootProject.ext.checkoutDir}/tools/buildSrc/base/version.gradle"
rootProject.ext.latestCompileSdk = 'android-P'
rootProject.ext.buildToolsVersion = "27.0.3"
+ rootProject.ext.kotlinVersion = '1.2.0'
repositories {
maven { url "file://${rootProject.ext.checkoutDir}/out/repo/" }