summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2020-10-01 09:59:28 -0700
committerManuel Vicente Vivo <mvivo@google.com>2020-12-03 10:59:30 +0000
commit11f3864741c0a335cc34ce18e1b65034cba517b1 (patch)
tree7f9a630288fd654445db6b5c39593160854900a8
parentde1bae70c096f01e4604c30c5fccc0e732a9724f (diff)
downloaddata-binding-11f3864741c0a335cc34ce18e1b65034cba517b1.tar.gz
Adds StateFlow support to data binding
Bug: 162839058 Test: KotlinTestApp Change-Id: I835d768cbc6d8bf44566bb15aae8fe786a005ed0
-rw-r--r--BUILD.bazel51
-rw-r--r--compilationTests/build.gradle1
-rw-r--r--compilationTests/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt43
-rw-r--r--compilationTests/src/test/resources/app_build.gradle4
-rw-r--r--compilationTests/src/test/resources/commonBuildScript.gradle1
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/Expr.java11
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt19
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt32
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt4
-rw-r--r--compilerCommon/src/main/kotlin/android/databinding/tool/LibTypes.kt14
-rw-r--r--compilerCommon/src/test/kotlin/android/databinding/tool/store/AndroidXConversionTest.kt2
-rw-r--r--extensions-support/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--extensions/baseAdapters/build.gradle2
-rw-r--r--extensions/build.gradle13
-rw-r--r--extensions/databindingKtx/build.gradle94
-rw-r--r--extensions/databindingKtx/src/main/AndroidManifest.xml17
-rw-r--r--extensions/databindingKtx/src/main/java/androidx/databinding/ViewDataBindingKtx.kt108
-rw-r--r--extensions/library/build.gradle4
-rw-r--r--extensions/library/src/main/java/androidx/databinding/CreateWeakListener.java28
-rw-r--r--extensions/library/src/main/java/androidx/databinding/ObservableReference.java28
-rw-r--r--extensions/library/src/main/java/androidx/databinding/ViewDataBinding.java150
-rw-r--r--extensions/library/src/main/java/androidx/databinding/WeakListener.java111
-rw-r--r--extensions/settings.gradle1
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--integration-tests/DynamicApp/app/build.gradle4
-rw-r--r--integration-tests/KotlinTestApp/app/build.gradle9
-rw-r--r--integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/StateFlowTest.kt161
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml2
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowContainer.kt23
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowViewModel.kt30
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/layout/adapter_from_library.xml2
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow.xml79
-rw-r--r--integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow_included.xml53
-rw-r--r--integration-tests/KotlinTestApp/build.gradle2
-rw-r--r--integration-tests/KotlinTestApp/library1/build.gradle3
-rw-r--r--integration-tests/TestApp/app/build.gradle2
-rw-r--r--integration-tests/ViewBindingWithDataBindingTestApp/app/build.gradle2
38 files changed, 978 insertions, 138 deletions
diff --git a/BUILD.bazel b/BUILD.bazel
index a6ad530d..0b207b5e 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -288,10 +288,11 @@ maven_repo(
"//prebuilts/tools/common/m2/repository/android/arch/lifecycle/extensions/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/android/arch/lifecycle/runtime/1.0.3:aar",
"//prebuilts/tools/common/m2/repository/androidx/annotation/annotation/1.0.0:jar",
+ "//prebuilts/tools/common/m2/repository/androidx/annotation/annotation/1.1.0:jar",
"//prebuilts/tools/common/m2/repository/androidx/appcompat/appcompat/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/appcompat/appcompat/1.0.2:aar",
- "//prebuilts/tools/common/m2/repository/androidx/arch/core/core-common/2.0.0:jar",
- "//prebuilts/tools/common/m2/repository/androidx/arch/core/core-runtime/2.0.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/arch/core/core-common/2.1.0:jar",
+ "//prebuilts/tools/common/m2/repository/androidx/arch/core/core-runtime/2.1.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/asynclayoutinflater/asynclayoutinflater/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/cardview/cardview/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/collection/collection/1.0.0:jar",
@@ -306,14 +307,19 @@ maven_repo(
"//prebuilts/tools/common/m2/repository/androidx/interpolator/interpolator/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/legacy/legacy-support-core-ui/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/legacy/legacy-support-core-utils/1.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-common/2.0.0:jar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-extensions/2.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata-core/2.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata/2.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-process/2.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-runtime/2.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-service/2.0.0:aar",
- "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-viewmodel/2.0.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-common/2.2.0:jar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-extensions/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata-core/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata/2.0.0:aar", #Workaround as loader:1.0.0 needs it
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-livedata/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-process/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-runtime-ktx/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-runtime/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-service/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.2.0:aar",
+ "//prebuilts/tools/common/m2/repository/androidx/lifecycle/lifecycle-viewmodel/2.2.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/loader/loader/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/localbroadcastmanager/localbroadcastmanager/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/androidx/print/print/1.0.0:aar",
@@ -335,9 +341,33 @@ maven_repo(
"//prebuilts/tools/common/m2/repository/com/android/support/support-media-compat/26.1.0:aar",
"//prebuilts/tools/common/m2/repository/com/android/support/support-v4/26.1.0:aar",
"//prebuilts/tools/common/m2/repository/com/android/support/support-vector-drawable/26.1.0:aar",
+ "//prebuilts/tools/common/m2/repository/com/github/gundy/semver4j/0.16.4:jar",
"//prebuilts/tools/common/m2/repository/com/google/android/material/material/1.0.0:aar",
"//prebuilts/tools/common/m2/repository/com/google/code/findbugs/jsr305/1.3.9:jar",
"//prebuilts/tools/common/m2/repository/com/google/errorprone/error_prone_annotations/2.1.3:jar",
+ "//prebuilts/tools/common/m2/repository/de/undercouch/gradle-download-task/4.0.2:jar",
+ "//prebuilts/tools/common/m2/repository/org/antlr/antlr4-runtime/4.5.2-1:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/intellij/deps/trove4j/1.0.20181211:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-android-extensions/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-build-common/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-compiler-runner/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-daemon-client/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-script-runtime/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-scripting-common/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-scripting-jvm/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-util-io/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlin/kotlin-util-klib/1.4.10:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines-android/1.4.1:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.7:jar",
+ "//prebuilts/tools/common/m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1:jar",
],
)
@@ -350,6 +380,7 @@ gradle_build(
"//prebuilts/studio/sdk:platform-tools",
"//prebuilts/studio/sdk:build-tools/latest",
"//tools/buildSrc/base:version.properties",
+ "//tools/buildSrc/base:dependencies.properties",
"//tools/data-binding:databinding.properties",
] + glob(
["extensions/**"],
diff --git a/compilationTests/build.gradle b/compilationTests/build.gradle
index 9398c5ea..a42cc26e 100644
--- a/compilationTests/build.gradle
+++ b/compilationTests/build.gradle
@@ -21,6 +21,7 @@ dependencies {
testCompile 'com.android.tools:annotations:24.5.0'
testCompile libs.guava
testCompile libs.kotlin_stdlib
+ testCompile 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
}
afterEvaluate {
diff --git a/compilationTests/gradle/wrapper/gradle-wrapper.properties b/compilationTests/gradle/wrapper/gradle-wrapper.properties
index b6f6d38a..73a14a3f 100644
--- a/compilationTests/gradle/wrapper/gradle-wrapper.properties
+++ b/compilationTests/gradle/wrapper/gradle-wrapper.properties
@@ -4,4 +4,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=../../../../../../../external/gradle/gradle-4.1-bin.zip
+distributionUrl=../../../../../../../external/gradle/gradle-6.6-bin.zip
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt
index 1a0440dc..001410b8 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt
@@ -26,7 +26,8 @@ import org.junit.runners.Parameterized
class ObservableGetDetectionTest(
private val type: String,
private val resolvedType: String, // e..g if it is ObservableInt, resolvedType is Int
- private val getter: String
+ private val getter: String,
+ private val constructor: String
) : BaseCompilationTest() {
@Test
fun detectGetterCallsOnObservables() {
@@ -71,7 +72,7 @@ class ObservableGetDetectionTest(
package com.example;
import androidx.databinding.*;
public class MyClass {
- public final $type value = new $type();
+ public final $type value = $constructor;
}
""".trimIndent())
writeFile("/app/src/main/res/layout/observable_get.xml",
@@ -100,7 +101,7 @@ class ObservableGetDetectionTest(
package com.example;
import androidx.databinding.*;
public class MyClass {
- public final $type value = new $type();
+ public final $type value = $constructor;
@InverseMethod("fromString")
public static String convertToString($resolvedType value) {
throw new RuntimeException("");
@@ -134,19 +135,31 @@ class ObservableGetDetectionTest(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun params() = arrayOf(
- arrayOf("ObservableByte", "byte"),
- arrayOf("ObservableBoolean", "boolean"),
- arrayOf("ObservableChar", "char"),
- arrayOf("ObservableShort", "short"),
- arrayOf("ObservableInt", "int"),
- arrayOf("ObservableLong", "long"),
- arrayOf("ObservableFloat", "float"),
- arrayOf("ObservableDouble", "double"),
- arrayOf("ObservableField<String>", "String")
+ arrayOf("androidx.databinding.ObservableByte", "byte"),
+ arrayOf("androidx.databinding.ObservableBoolean", "boolean"),
+ arrayOf("androidx.databinding.ObservableChar", "char"),
+ arrayOf("androidx.databinding.ObservableShort", "short"),
+ arrayOf("androidx.databinding.ObservableInt", "int"),
+ arrayOf("androidx.databinding.ObservableLong", "long"),
+ arrayOf("androidx.databinding.ObservableFloat", "float"),
+ arrayOf("androidx.databinding.ObservableDouble", "double"),
+ arrayOf("androidx.databinding.ObservableField<String>", "String")
).map {
- arrayOf("androidx.databinding.${it[0]}", it[1], "get()")
+ arrayOf(it[0], it[1], "get()", "new ${it[0]}()")
} + arrayOf(
- arrayOf("androidx.lifecycle.MutableLiveData<String>", "String", "getValue()")
+ arrayOf(
+ "androidx.lifecycle.MutableLiveData<String>",
+ "String",
+ "getValue()",
+ "new androidx.lifecycle.MutableLiveData<String>()"
+ )
+ ) + arrayOf(
+ arrayOf(
+ "kotlinx.coroutines.flow.MutableStateFlow<String>",
+ "String",
+ "getValue()",
+ "kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow(\"\")"
+ )
)
}
-} \ No newline at end of file
+}
diff --git a/compilationTests/src/test/resources/app_build.gradle b/compilationTests/src/test/resources/app_build.gradle
index ab61387d..ed269e3a 100644
--- a/compilationTests/src/test/resources/app_build.gradle
+++ b/compilationTests/src/test/resources/app_build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion rootProject.latestCompileSdk
@@ -26,6 +27,7 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
- implementation "androidx.lifecycle:lifecycle-livedata:2.0.0"
+ implementation "androidx.fragment:fragment:1.2.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
!@{DEPENDENCIES}
}
diff --git a/compilationTests/src/test/resources/commonBuildScript.gradle b/compilationTests/src/test/resources/commonBuildScript.gradle
index 35f21b4c..cd94804a 100644
--- a/compilationTests/src/test/resources/commonBuildScript.gradle
+++ b/compilationTests/src/test/resources/commonBuildScript.gradle
@@ -27,6 +27,7 @@ rootProject.buildscript {
dependencies {
classpath "com.android.tools.build:gradle:${buildVersion}"
+ classpath libs.kotlin_gradle_plugin
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/Expr.java b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
index 73770a55..c956dfcd 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/Expr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
@@ -191,14 +191,19 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
return getResolvedType().isObservable();
}
- public String getUpdateRegistrationCall() {
+ public String getUpdateRegistrationCall(int id, String value) {
if (!isObservable()) {
L.e("The expression isn't observable!");
}
+ String lastParams = id + ", " + value + ");";
if (getResolvedType().isLiveData()) {
- return "updateLiveDataRegistration";
+ return "updateLiveDataRegistration(" + lastParams;
}
- return "updateRegistration";
+ if (getResolvedType().isStateFlow()) {
+ return "androidx.databinding.ViewDataBindingKtx."
+ + "updateStateFlowRegistration(this, " + lastParams;
+ }
+ return "updateRegistration(" + lastParams;
}
public void setUnwrapObservableFields(boolean unwrapObservableFields) {
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt
index d2a86ee8..37f0e8a6 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt
@@ -53,6 +53,12 @@ abstract class ModelAnalyzer protected constructor(@JvmField val libTypes: LibTy
val mutableLiveDataType by lazy(LazyThreadSafetyMode.NONE) {
loadClassErasure(libTypes.mutableLiveData)
}
+ val stateFlowType by lazy(LazyThreadSafetyMode.NONE) {
+ loadClassErasure(libTypes.stateFlow)
+ }
+ val mutableStateFlowDataType by lazy(LazyThreadSafetyMode.NONE) {
+ loadClassErasure(libTypes.mutableStateFlow)
+ }
val viewDataBindingType by lazy(LazyThreadSafetyMode.NONE) {
val klass = findClass(libTypes.viewDataBinding, null)
Preconditions.checkNotNull(klass, "Cannot find %s class." +
@@ -141,6 +147,19 @@ abstract class ModelAnalyzer protected constructor(@JvmField val libTypes: LibTy
}
}
+ private val dataBindingKtxClass by lazy {
+ findClass(libTypes.dataBindingKtx, null)
+ }
+
+ fun checkDataBindingKtx() {
+ Preconditions.checkNotNull(
+ dataBindingKtxClass, """Data binding ktx is not enabled.
+ |
+ |Add dataBinding.addKtx = true to your build.gradle to enable it."""
+ .trimMargin()
+ )
+ }
+
fun findClass(className: String, imports: ImportBag?): ModelClass? {
return classFinderCache.find(className, imports)
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt
index 99b7f8c8..fb600443 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt
@@ -176,7 +176,8 @@ abstract class ModelClass {
return modelAnalyzer.observableType.isAssignableFrom(this) ||
modelAnalyzer.observableListType.isAssignableFrom(this) ||
modelAnalyzer.observableMapType.isAssignableFrom(this) ||
- (modelAnalyzer.liveDataType?.isAssignableFrom(this) ?: false)
+ (modelAnalyzer.liveDataType?.isAssignableFrom(this) ?: false) ||
+ (modelAnalyzer.stateFlowType?.isAssignableFrom(this) ?: false)
}
/**
@@ -206,24 +207,43 @@ abstract class ModelClass {
}
/**
+ * @return whether or not this is a StateFlow
+ */
+ val isStateFlow by lazy(LazyThreadSafetyMode.NONE) {
+ val modelAnalyzer = ModelAnalyzer.getInstance()
+ val isStateFlow = modelAnalyzer.stateFlowType?.isAssignableFrom(erasure()) ?: false
+ if (isStateFlow) {
+ modelAnalyzer.checkDataBindingKtx()
+ }
+ isStateFlow
+ }
+
+ /**
+ * @return whether or not this is a MutableStateFlow
+ */
+ val isMutableStateFlow by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().mutableStateFlowDataType?.isAssignableFrom(erasure()) ?: false
+ }
+
+ /**
* @return the name of the simple getter method when this is an ObservableField or LiveData or
- * `null` for any other type
+ * Flow or `null` for any other type
*/
val observableGetterName: String?
get() = when {
isObservableField -> "get"
- isLiveData -> "getValue"
+ isLiveData || isStateFlow -> "getValue"
else -> null
}
/**
* @return the name of the simple setter method when this is an ObservableField or
- * MutableLiveData or `null` for any other type.
+ * MutableLiveData or MutableStateFlow or `null` for any other type.
*/
val observableSetterName: String?
get() = when {
isObservableField -> "set"
- isMutableLiveData -> "setValue"
+ isMutableLiveData || isMutableStateFlow -> "setValue"
else -> null
}
@@ -651,4 +671,4 @@ abstract class ModelClass {
return fieldName
}
}
-} \ No newline at end of file
+}
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 26b8203a..1130f32d 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
@@ -754,7 +754,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
block("public void ${it.setterName}(${if (it.resolvedType.isPrimitive) "" else "@Nullable "}$argType ${it.readableName})") {
val used = it.isIsUsedInCallback || it.isUsed
if (used && it.isObservable) {
- nl("${it.updateRegistrationCall}(${it.id}, ${it.readableName});");
+ nl(it.getUpdateRegistrationCall(it.id, it.readableName))
}
nl("this.${it.fieldName} = ${it.readableName};")
if (used) {
@@ -1098,7 +1098,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder, val libTypes: LibTypes
app("", assignment)
}
it.value.filter { it.isObservable }.forEach { expr: Expr ->
- tab("${expr.updateRegistrationCall}(${expr.id}, ${expr.executePendingLocalName});")
+ tab(expr.getUpdateRegistrationCall(expr.id, expr.executePendingLocalName))
}
}
diff --git a/compilerCommon/src/main/kotlin/android/databinding/tool/LibTypes.kt b/compilerCommon/src/main/kotlin/android/databinding/tool/LibTypes.kt
index 3b848a29..4a4a4726 100644
--- a/compilerCommon/src/main/kotlin/android/databinding/tool/LibTypes.kt
+++ b/compilerCommon/src/main/kotlin/android/databinding/tool/LibTypes.kt
@@ -60,10 +60,22 @@ class LibTypes(val useAndroidX: Boolean) {
convert("android.arch.lifecycle.MutableLiveData")
}
+ val stateFlow by lazy(LazyThreadSafetyMode.NONE) {
+ "kotlinx.coroutines.flow.StateFlow"
+ }
+
+ val mutableStateFlow by lazy(LazyThreadSafetyMode.NONE) {
+ "kotlinx.coroutines.flow.MutableStateFlow"
+ }
+
val dataBindingComponent by lazy(LazyThreadSafetyMode.NONE) {
convert("android.databinding.DataBindingComponent")
}
+ val dataBindingKtx by lazy(LazyThreadSafetyMode.NONE) {
+ "androidx.databinding.ViewDataBindingKtx"
+ }
+
val dataBinderMapper by lazy(LazyThreadSafetyMode.NONE) {
convert("android.databinding.DataBinderMapper")
}
@@ -210,4 +222,4 @@ class LibTypes(val useAndroidX: Boolean) {
"android.arch.persistence." to "androidx.sqlite."
)
}
-} \ No newline at end of file
+}
diff --git a/compilerCommon/src/test/kotlin/android/databinding/tool/store/AndroidXConversionTest.kt b/compilerCommon/src/test/kotlin/android/databinding/tool/store/AndroidXConversionTest.kt
index 07a757d8..68fdd9ba 100644
--- a/compilerCommon/src/test/kotlin/android/databinding/tool/store/AndroidXConversionTest.kt
+++ b/compilerCommon/src/test/kotlin/android/databinding/tool/store/AndroidXConversionTest.kt
@@ -54,4 +54,4 @@ class AndroidXConversionTest {
`is`("androidx.recyclerview.widget.RecyclerView")
)
}
-} \ No newline at end of file
+}
diff --git a/extensions-support/gradle/wrapper/gradle-wrapper.properties b/extensions-support/gradle/wrapper/gradle-wrapper.properties
index 273873f6..fa1cc2e4 100644
--- a/extensions-support/gradle/wrapper/gradle-wrapper.properties
+++ b/extensions-support/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=../../../../external/gradle/gradle-6.5-bin.zip
+distributionUrl=../../../../external/gradle/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/extensions/baseAdapters/build.gradle b/extensions/baseAdapters/build.gradle
index 85ab1dbd..603925b3 100644
--- a/extensions/baseAdapters/build.gradle
+++ b/extensions/baseAdapters/build.gradle
@@ -96,4 +96,4 @@ task prebuild(type : Copy) {
rename { String fileName ->
"databinding-adapters.aar"
}
-} \ No newline at end of file
+}
diff --git a/extensions/build.gradle b/extensions/build.gradle
index 8b77e813..1bfd88b6 100644
--- a/extensions/build.gradle
+++ b/extensions/build.gradle
@@ -51,6 +51,8 @@ buildscript {
databindingProperties.load(new FileInputStream("$projectDir/../databinding.properties"))
Properties buildToolsProperties = new Properties()
buildToolsProperties.load(new FileInputStream("$projectDir/../../buildSrc/base/version.properties"))
+ Properties dependencyProperties = new Properties()
+ dependencyProperties.load(new FileInputStream("$projectDir/../../buildSrc/base/dependencies.properties"))
def runningInIde = project.hasProperty('android.injected.invoked.from.ide')
// this is done by bazel but if we are in IDE it also configures so we need to distinguish
@@ -107,7 +109,7 @@ buildscript {
}
ext.dataBindingConfig = databindingProperties
def supportLibVersion = "1.0.0"
- def lifecycleVersion = "2.0.0"
+ def lifecycleVersion = "2.2.0"
ext.libs = [:]
ext.libs.android_support_annotations = "androidx.annotation:annotation:$supportLibVersion"
ext.libs.android_support_collection = "androidx.collection:collection:$supportLibVersion"
@@ -115,9 +117,14 @@ buildscript {
ext.libs.android_support_appcompat_v7 = "androidx.appcompat:appcompat:$supportLibVersion"
ext.libs.android_arch_lifecycle_extensions = "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
ext.libs.android_arch_lifecycle_runtime = "androidx.lifecycle:lifecycle-runtime:$lifecycleVersion"
+ ext.libs.android_arch_lifecycle_runtime_ktx = "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
+ ext.libs.kotlin_gradle_plugin = dependencyProperties.kotlin_gradle_plugin
+ ext.libs.kotlin_stdlib = dependencyProperties.kotlin_stdlib
+ ext.libs.coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
dependencies {
classpath "com.android.tools.build:gradle:$TOOLS_VERSION"
+ classpath rootProject.ext.libs.kotlin_gradle_plugin
}
if (!autoConfigured) {
@@ -177,8 +184,8 @@ subprojects {
failOnVersionConflict()
preferProjectModules()
- force 'androidx.lifecycle:lifecycle-viewmodel:2.0.0'
- force 'androidx.lifecycle:lifecycle-livedata-core:2.0.0'
+ force 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
+ force 'androidx.lifecycle:lifecycle-livedata-core:2.2.0'
force 'androidx.fragment:fragment:1.0.0'
}
}
diff --git a/extensions/databindingKtx/build.gradle b/extensions/databindingKtx/build.gradle
new file mode 100644
index 00000000..4b8ae3e2
--- /dev/null
+++ b/extensions/databindingKtx/build.gradle
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 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.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion dataBindingConfig.targetSdkVersion
+ }
+ compileOptions {
+ sourceCompatibility dataBindingConfig.javaTargetCompatibility
+ targetCompatibility dataBindingConfig.javaSourceCompatibility
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+ packagingOptions {
+ exclude 'META-INF/LICENSE.txt'
+ exclude 'META-INF/NOTICE.txt'
+ }
+}
+
+configurations {
+ jarArchives
+}
+
+dependencies {
+ implementation project(':library')
+ implementation rootProject.ext.libs.kotlin_stdlib
+ implementation rootProject.ext.libs.coroutines
+ api rootProject.ext.libs.android_arch_lifecycle_extensions
+ api rootProject.ext.libs.android_arch_lifecycle_runtime_ktx
+}
+
+//create jar tasks
+android.libraryVariants.all { variant ->
+ def name = variant.buildType.name
+
+ if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
+ return // Skip debug builds.
+ }
+ def suffix = name.capitalize()
+
+ def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
+ classifier = 'sources'
+ from android.sourceSets.main.java.srcDirs
+ }
+
+ artifacts.add('archives', sourcesJarTask);
+}
+uploadArchives {
+ repositories {
+ mavenDeployer {
+ pom.artifactId = 'databinding-ktx'
+ pom.project {
+ licenses {
+ license {
+ name dataBindingConfig.licenseName
+ url dataBindingConfig.licenseUrl
+ distribution dataBindingConfig.licenseDistribution
+ }
+ }
+ }
+ }
+ }
+}
+
+task prebuildAar(type : Copy) {
+ dependsOn uploadArchives
+ from "$buildDir/outputs/aar/databinding-ktx-release.aar"
+ into dataBindingConfig.prebuildFolder
+ rename { String fileName ->
+ "databinding-ktx.aar"
+ }
+}
diff --git a/extensions/databindingKtx/src/main/AndroidManifest.xml b/extensions/databindingKtx/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..60e62e57
--- /dev/null
+++ b/extensions/databindingKtx/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<!--
+ Copyright (C) 2020 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 package="androidx.databinding.ktx" />
diff --git a/extensions/databindingKtx/src/main/java/androidx/databinding/ViewDataBindingKtx.kt b/extensions/databindingKtx/src/main/java/androidx/databinding/ViewDataBindingKtx.kt
new file mode 100644
index 00000000..509b44ab
--- /dev/null
+++ b/extensions/databindingKtx/src/main/java/androidx/databinding/ViewDataBindingKtx.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 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;
+
+import androidx.annotation.RestrictTo
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+import java.lang.ref.ReferenceQueue
+
+/**
+ * Helper methods for data binding Ktx features.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object ViewDataBindingKtx {
+
+ /**
+ * Method object extracted out to attach a listener to a bound StateFlow object.
+ */
+ private val CREATE_STATE_FLOW_LISTENER =
+ CreateWeakListener { viewDataBinding, localFieldId, referenceQueue ->
+ StateFlowListener(viewDataBinding, localFieldId, referenceQueue)
+ .listener
+ }
+
+ @Suppress("unused") // called by generated code
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @JvmStatic
+ fun updateStateFlowRegistration(
+ viewDataBinding: ViewDataBinding,
+ localFieldId: Int,
+ observable: Flow<*>?
+ ): Boolean {
+ viewDataBinding.mInStateFlowRegisterObserver = true
+ try {
+ return viewDataBinding.updateRegistration(
+ localFieldId, observable, CREATE_STATE_FLOW_LISTENER
+ )
+ } finally {
+ viewDataBinding.mInStateFlowRegisterObserver = false
+ }
+ }
+
+ internal class StateFlowListener(
+ binder: ViewDataBinding?,
+ localFieldId: Int,
+ referenceQueue: ReferenceQueue<ViewDataBinding>
+ ) : ObservableReference<Flow<Any?>> {
+
+ private var _lifecycleOwner : LifecycleOwner? = null
+ private var observerJob : Job? = null
+ private val listener = WeakListener<Flow<Any?>>(
+ binder, localFieldId, this, referenceQueue
+ )
+ override fun getListener(): WeakListener<Flow<Any?>> {
+ return listener
+ }
+
+ override fun addListener(target: Flow<Any?>?) {
+ val owner = _lifecycleOwner ?: return
+ if (target != null) {
+ startCollection(owner, target)
+ }
+ }
+
+ override fun removeListener(target: Flow<Any?>?) {
+ observerJob?.cancel()
+ observerJob = null
+ }
+
+ private fun startCollection(owner: LifecycleOwner, flow: Flow<Any?>) {
+ observerJob?.cancel()
+ observerJob = owner.lifecycleScope.launchWhenCreated {
+ flow.collect {
+ listener.binder?.handleFieldChange(listener.mLocalFieldId, listener.target, 0)
+ }
+ }
+ }
+
+ override fun setLifecycleOwner(lifecycleOwner: LifecycleOwner?) {
+ if (_lifecycleOwner === lifecycleOwner) {
+ return
+ }
+ observerJob?.cancel()
+ _lifecycleOwner = lifecycleOwner
+ val target = listener.target
+ if (lifecycleOwner != null && target != null) {
+ startCollection(lifecycleOwner, target)
+ }
+ }
+ }
+}
diff --git a/extensions/library/build.gradle b/extensions/library/build.gradle
index 3963d839..774c49d6 100644
--- a/extensions/library/build.gradle
+++ b/extensions/library/build.gradle
@@ -68,10 +68,10 @@ configurations {
dependencies {
implementation rootProject.ext.libs.android_support_collection
- implementation "androidx.databinding:databinding-common:${dataBindingConfig.version}"
+ api "androidx.databinding:databinding-common:${dataBindingConfig.version}"
api project(':viewbinding')
- compileOnly rootProject.ext.libs.android_arch_lifecycle_extensions
api rootProject.ext.libs.android_arch_lifecycle_runtime
+ compileOnly rootProject.ext.libs.android_arch_lifecycle_extensions
}
//create jar tasks
diff --git a/extensions/library/src/main/java/androidx/databinding/CreateWeakListener.java b/extensions/library/src/main/java/androidx/databinding/CreateWeakListener.java
new file mode 100644
index 00000000..398e468e
--- /dev/null
+++ b/extensions/library/src/main/java/androidx/databinding/CreateWeakListener.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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;
+
+import androidx.annotation.RestrictTo;
+import java.lang.ref.ReferenceQueue;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface CreateWeakListener {
+ WeakListener create(
+ ViewDataBinding viewDataBinding,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue);
+}
diff --git a/extensions/library/src/main/java/androidx/databinding/ObservableReference.java b/extensions/library/src/main/java/androidx/databinding/ObservableReference.java
new file mode 100644
index 00000000..447d47e9
--- /dev/null
+++ b/extensions/library/src/main/java/androidx/databinding/ObservableReference.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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;
+
+import androidx.annotation.RestrictTo;
+import androidx.lifecycle.LifecycleOwner;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface ObservableReference<T> {
+ WeakListener<T> getListener();
+ void addListener(T target);
+ void removeListener(T target);
+ void setLifecycleOwner(LifecycleOwner lifecycleOwner);
+}
diff --git a/extensions/library/src/main/java/androidx/databinding/ViewDataBinding.java b/extensions/library/src/main/java/androidx/databinding/ViewDataBinding.java
index 5df3a3cd..9612bd65 100644
--- a/extensions/library/src/main/java/androidx/databinding/ViewDataBinding.java
+++ b/extensions/library/src/main/java/androidx/databinding/ViewDataBinding.java
@@ -92,8 +92,13 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
*/
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
- public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
- return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
+ public WeakListener create(
+ ViewDataBinding viewDataBinding,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ return new WeakPropertyListener(viewDataBinding, localFieldId, referenceQueue)
+ .getListener();
}
};
@@ -102,8 +107,13 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
*/
private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
@Override
- public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
- return new WeakListListener(viewDataBinding, localFieldId).getListener();
+ public WeakListener create(
+ ViewDataBinding viewDataBinding,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ return new WeakListListener(viewDataBinding, localFieldId, referenceQueue)
+ .getListener();
}
};
@@ -112,8 +122,13 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
*/
private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
@Override
- public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
- return new WeakMapListener(viewDataBinding, localFieldId).getListener();
+ public WeakListener create(
+ ViewDataBinding viewDataBinding,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ return new WeakMapListener(viewDataBinding, localFieldId, referenceQueue)
+ .getListener();
}
};
@@ -122,8 +137,13 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
*/
private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
@Override
- public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
- return new LiveDataListener(viewDataBinding, localFieldId).getListener();
+ public WeakListener create(
+ ViewDataBinding viewDataBinding,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ return new LiveDataListener(viewDataBinding, localFieldId, referenceQueue)
+ .getListener();
}
};
@@ -272,6 +292,14 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
private boolean mInLiveDataRegisterObserver;
/**
+ * When StateFlow first collects for chances, it notifies immediately that there was a
+ * change. This flag identifies that we've just started observing StateFlow and we should ignore
+ * the change notification.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ protected boolean mInStateFlowRegisterObserver;
+
+ /**
* Needed for backwards binary compatibility.
* b/122936785
* @hide
@@ -539,9 +567,10 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
return mRoot;
}
- private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
- if (mInLiveDataRegisterObserver) {
- // We're in LiveData registration, which always results in a field change
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ protected void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
+ if (mInLiveDataRegisterObserver || mInStateFlowRegisterObserver) {
+ // We're in LiveData or StateFlow registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
@@ -602,7 +631,8 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
return listener.getTarget();
}
- private boolean updateRegistration(int localFieldId, Object observable,
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ protected boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
@@ -679,7 +709,7 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
- listener = listenerCreator.create(this, localFieldId);
+ listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
@@ -1374,66 +1404,16 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
);
}
- private interface ObservableReference<T> {
- WeakListener<T> getListener();
- void addListener(T target);
- void removeListener(T target);
- void setLifecycleOwner(LifecycleOwner lifecycleOwner);
- }
-
- private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
- private final ObservableReference<T> mObservable;
- protected final int mLocalFieldId;
- private T mTarget;
-
- public WeakListener(ViewDataBinding binder, int localFieldId,
- ObservableReference<T> observable) {
- super(binder, sReferenceQueue);
- mLocalFieldId = localFieldId;
- mObservable = observable;
- }
-
- public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
- mObservable.setLifecycleOwner(lifecycleOwner);
- }
-
- public void setTarget(T object) {
- unregister();
- mTarget = object;
- if (mTarget != null) {
- mObservable.addListener(mTarget);
- }
- }
-
- public boolean unregister() {
- boolean unregistered = false;
- if (mTarget != null) {
- mObservable.removeListener(mTarget);
- unregistered = true;
- }
- mTarget = null;
- return unregistered;
- }
-
- public T getTarget() {
- return mTarget;
- }
-
- protected ViewDataBinding getBinder() {
- ViewDataBinding binder = get();
- if (binder == null) {
- unregister(); // The binder is dead
- }
- return binder;
- }
- }
-
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
- public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
- mListener = new WeakListener<Observable>(binder, localFieldId, this);
+ public WeakPropertyListener(
+ ViewDataBinding binder,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ mListener = new WeakListener<Observable>(binder, localFieldId, this, referenceQueue);
}
@Override
@@ -1473,8 +1453,14 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
implements ObservableReference<ObservableList> {
final WeakListener<ObservableList> mListener;
- public WeakListListener(ViewDataBinding binder, int localFieldId) {
- mListener = new WeakListener<ObservableList>(binder, localFieldId, this);
+ public WeakListListener(
+ ViewDataBinding binder,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ mListener = new WeakListener<ObservableList>(
+ binder, localFieldId, this, referenceQueue
+ );
}
@Override
@@ -1535,8 +1521,14 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
implements ObservableReference<ObservableMap> {
final WeakListener<ObservableMap> mListener;
- public WeakMapListener(ViewDataBinding binder, int localFieldId) {
- mListener = new WeakListener<ObservableMap>(binder, localFieldId, this);
+ public WeakMapListener(
+ ViewDataBinding binder,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ mListener = new WeakListener<ObservableMap>(
+ binder, localFieldId, this, referenceQueue
+ );
}
@Override
@@ -1573,8 +1565,12 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
final WeakListener<LiveData<?>> mListener;
LifecycleOwner mLifecycleOwner;
- public LiveDataListener(ViewDataBinding binder, int localFieldId) {
- mListener = new WeakListener(binder, localFieldId, this);
+ public LiveDataListener(
+ ViewDataBinding binder,
+ int localFieldId,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ mListener = new WeakListener(binder, localFieldId, this, referenceQueue);
}
@Override
@@ -1618,10 +1614,6 @@ public abstract class ViewDataBinding extends BaseObservable implements ViewBind
}
}
- private interface CreateWeakListener {
- WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
- }
-
/**
* This class is used by generated subclasses of {@link ViewDataBinding} to track the
* included layouts contained in the bound layout. This class is an implementation
diff --git a/extensions/library/src/main/java/androidx/databinding/WeakListener.java b/extensions/library/src/main/java/androidx/databinding/WeakListener.java
new file mode 100644
index 00000000..0af2313b
--- /dev/null
+++ b/extensions/library/src/main/java/androidx/databinding/WeakListener.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 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;
+
+import android.annotation.TargetApi;
+
+import androidx.annotation.RestrictTo;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.OnLifecycleEvent;
+import android.content.res.ColorStateList;
+import androidx.databinding.CallbackRegistry.NotifierCallback;
+import android.graphics.drawable.Drawable;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import android.os.Looper;
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.SparseLongArray;
+import android.view.Choreographer;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewGroup;
+import androidx.viewbinding.ViewBinding;
+
+import androidx.databinding.library.R;
+import androidx.databinding.ObservableReference;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class WeakListener<T> extends WeakReference<ViewDataBinding> {
+ private final ObservableReference<T> mObservable;
+ protected final int mLocalFieldId;
+ private T mTarget;
+
+ public WeakListener(
+ ViewDataBinding binder,
+ int localFieldId,
+ ObservableReference<T> observable,
+ ReferenceQueue<ViewDataBinding> referenceQueue
+ ) {
+ super(binder, referenceQueue);
+ mLocalFieldId = localFieldId;
+ mObservable = observable;
+ }
+
+ public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
+ mObservable.setLifecycleOwner(lifecycleOwner);
+ }
+
+ public void setTarget(T object) {
+ unregister();
+ mTarget = object;
+ if (mTarget != null) {
+ mObservable.addListener(mTarget);
+ }
+ }
+
+ public boolean unregister() {
+ boolean unregistered = false;
+ if (mTarget != null) {
+ mObservable.removeListener(mTarget);
+ unregistered = true;
+ }
+ mTarget = null;
+ return unregistered;
+ }
+
+ public T getTarget() {
+ return mTarget;
+ }
+
+ @Nullable
+ protected ViewDataBinding getBinder() {
+ ViewDataBinding binder = get();
+ if (binder == null) {
+ unregister(); // The binder is dead
+ }
+ return binder;
+ }
+}
diff --git a/extensions/settings.gradle b/extensions/settings.gradle
index 90a9cf9a..c8e81194 100644
--- a/extensions/settings.gradle
+++ b/extensions/settings.gradle
@@ -20,6 +20,7 @@
include ':library'
include ':baseAdapters'
include ':viewbinding'
+include ':databindingKtx'
if (hasProperty("includeDoclava")) {
File externalRoot = new File(rootDir, '../../../external')
include ':doclava'
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 59b5f892..5028f28f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/integration-tests/DynamicApp/app/build.gradle b/integration-tests/DynamicApp/app/build.gradle
index 98bac453..ab7384a3 100644
--- a/integration-tests/DynamicApp/app/build.gradle
+++ b/integration-tests/DynamicApp/app/build.gradle
@@ -44,8 +44,8 @@ android {
dependencies {
api 'androidx.appcompat:appcompat:1.0.0'
- api 'androidx.lifecycle:lifecycle-extensions:2.0.0'
+ api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
-} \ No newline at end of file
+}
diff --git a/integration-tests/KotlinTestApp/app/build.gradle b/integration-tests/KotlinTestApp/app/build.gradle
index 76c4bfe0..752b3508 100644
--- a/integration-tests/KotlinTestApp/app/build.gradle
+++ b/integration-tests/KotlinTestApp/app/build.gradle
@@ -23,6 +23,7 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion rootProject.latestCompileSdk
+
defaultConfig {
applicationId "androidx.databinding.kotlintestapp"
minSdkVersion 14
@@ -41,14 +42,16 @@ android {
}
dependencies {
- def supportVersion = "1.0.0"
- def lifecycleVersion = "2.0.0"
+ def supportVersion = "1.2.0"
+ def lifecycleVersion = "2.2.0"
def testingVersion = "1.1.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":library1")
implementation "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.kotlinVersion"
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycleVersion"
- implementation "androidx.lifecycle:lifecycle-livedata:$lifecycleVersion"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
implementation "androidx.appcompat:appcompat:$supportVersion"
testImplementation 'junit:junit:4.12'
androidTestImplementation("androidx.test:runner:$testingVersion") {
diff --git a/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/StateFlowTest.kt b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/StateFlowTest.kt
new file mode 100644
index 00000000..36a6e8f6
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/androidTest/java/androidx/databinding/kotlintestapp/StateFlowTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 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.StateFlowBinding
+import androidx.databinding.kotlintestapp.vo.StateFlowContainer
+import androidx.databinding.kotlintestapp.vo.StateFlowViewModel
+import androidx.test.runner.AndroidJUnit4
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@RunWith(AndroidJUnit4::class)
+class StateFlowTest {
+
+ @Suppress("MemberVisibilityCanPrivate")
+ @Rule
+ @JvmField
+ val rule = BindingActivityRule<StateFlowBinding>(R.layout.state_flow)
+
+ private val stateFlow = MutableStateFlow("")
+ private val stateFlowHolder = MutableStateFlow(stateFlow)
+ private val stateFlowContainer = StateFlowContainer()
+ private val stateFlowViewModel = StateFlowViewModel()
+
+ @Test
+ fun testStateFlow() {
+ setupView()
+
+ assertEquals("", rule.binding.textView1.text.toString())
+ assertEquals("", rule.binding.included.textView1.text.toString())
+ assertEquals("", rule.binding.textView2.text.toString())
+ assertEquals("", rule.binding.included.textView2.text.toString())
+
+ val stateFlowExpected = "Hello world!"
+ stateFlow.value = stateFlowExpected
+ val containerExpected = "Hi there!"
+ stateFlowContainer.stateFlow.value = containerExpected
+ rule.executePendingBindings()
+
+ assertEquals(stateFlowExpected, rule.binding.textView1.text.toString())
+ assertEquals(stateFlowExpected, rule.binding.included.textView1.text.toString())
+ assertEquals(containerExpected, rule.binding.textView2.text.toString())
+ assertEquals(containerExpected, rule.binding.included.textView2.text.toString())
+ }
+
+ @Test
+ fun noLifecycleOwner() {
+ setupView(includeLifecycle = false)
+
+ assertEquals("", rule.binding.textView1.text.toString())
+ assertEquals("", rule.binding.included.textView1.text.toString())
+ assertEquals("", rule.binding.textView2.text.toString())
+ assertEquals("", rule.binding.included.textView2.text.toString())
+
+ stateFlow.value = "Hello world!"
+ stateFlowContainer.stateFlow.value = "Hi there!"
+ rule.executePendingBindings()
+
+ assertEquals("", rule.binding.textView1.text.toString())
+ assertEquals("", rule.binding.included.textView1.text.toString())
+ assertEquals("", rule.binding.textView2.text.toString())
+ assertEquals("", rule.binding.included.textView2.text.toString())
+ }
+
+ @Test
+ fun testMutableStateFlow() {
+ setupView()
+
+ assertEquals("", rule.binding.textView2.text.toString())
+ assertEquals("", rule.binding.included.textView2.text.toString())
+
+ val expected = "EditText changed"
+ rule.runOnUiThread {
+ rule.binding.editText1.setText(expected)
+ }
+ rule.executePendingBindings()
+
+ assertEquals(expected, rule.binding.textView2.text.toString())
+ assertEquals(expected, rule.binding.included.textView2.text.toString())
+ assertEquals(expected, stateFlowContainer.stateFlow.value)
+ }
+
+ @Test
+ fun stateFlowChanged() {
+ setupView()
+
+ stateFlow.value = "foo"
+ rule.executePendingBindings()
+ assertEquals("foo", rule.binding.textView1.text)
+
+ rule.runOnUiThread {
+ val anotherStateFlow = MutableStateFlow("new value")
+ rule.binding.stateFlow = anotherStateFlow
+ rule.executePendingBindings()
+ }
+
+ assertEquals("new value", rule.binding.textView1.text)
+ }
+
+ @Test
+ fun stateFlowOfStateFlowChanged() {
+ setupView()
+
+ stateFlow.value = "foo"
+ rule.executePendingBindings()
+ assertEquals("foo", rule.binding.textView3.text)
+
+ stateFlow.value = "bar"
+ rule.executePendingBindings()
+ assertEquals("bar", rule.binding.textView3.text)
+
+ rule.runOnUiThread {
+ val anotherStateFlow = MutableStateFlow("new value")
+ stateFlowHolder.value = anotherStateFlow
+ rule.executePendingBindings()
+ }
+
+ assertEquals("new value", rule.binding.textView3.text)
+ }
+
+ @Test
+ fun testStateInOperator() {
+ setupView()
+ assertEquals("Hi there", rule.binding.textView4.text.toString())
+ }
+
+ private fun setupView(includeLifecycle: Boolean = true) {
+ rule.runOnUiThread {
+ rule.binding.apply {
+ if (includeLifecycle) {
+ lifecycleOwner = rule.activity
+ }
+ stateFlow = this@StateFlowTest.stateFlow
+ stateFlowOfStateFlow = this@StateFlowTest.stateFlowHolder
+ stateFlowContainer = this@StateFlowTest.stateFlowContainer
+ stateFlowViewModel = this@StateFlowTest.stateFlowViewModel
+ }
+ }
+
+ rule.executePendingBindings()
+ }
+}
diff --git a/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml b/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml
index 364d420e..3eaa3208 100644
--- a/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml
+++ b/integration-tests/KotlinTestApp/app/src/main/AndroidManifest.xml
@@ -36,4 +36,4 @@
</activity>
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowContainer.kt b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowContainer.kt
new file mode 100644
index 00000000..25531fa2
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowContainer.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 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.vo
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class StateFlowContainer {
+ val stateFlow = MutableStateFlow("")
+}
diff --git a/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowViewModel.kt b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowViewModel.kt
new file mode 100644
index 00000000..358cb7ba
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/java/androidx/databinding/kotlintestapp/vo/StateFlowViewModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.vo
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.stateIn
+
+class StateFlowViewModel : ViewModel() {
+
+ val stateFlow = flow {
+ emit("Hi there")
+ }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
+}
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/layout/adapter_from_library.xml b/integration-tests/KotlinTestApp/app/src/main/res/layout/adapter_from_library.xml
index c301f861..8a354e23 100644
--- a/integration-tests/KotlinTestApp/app/src/main/res/layout/adapter_from_library.xml
+++ b/integration-tests/KotlinTestApp/app/src/main/res/layout/adapter_from_library.xml
@@ -16,4 +16,4 @@
android:id="@+id/library1TextView"
app:setTextViaLibrary1="@{text}"/>
</LinearLayout>
-</layout> \ No newline at end of file
+</layout>
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow.xml b/integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow.xml
new file mode 100644
index 00000000..79a05885
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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="stateFlow"
+ type="kotlinx.coroutines.flow.StateFlow&lt;String&gt;" />
+
+ <variable
+ name="stateFlowOfStateFlow"
+ type="kotlinx.coroutines.flow.StateFlow&lt;kotlinx.coroutines.flow.StateFlow&lt;String&gt;&gt;" />
+
+ <variable
+ name="stateFlowContainer"
+ type="androidx.databinding.kotlintestapp.vo.StateFlowContainer" />
+
+ <variable
+ name="stateFlowViewModel"
+ type="androidx.databinding.kotlintestapp.vo.StateFlowViewModel" />
+ </data>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{stateFlow}" />
+
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{stateFlowContainer.stateFlow}" />
+
+ <TextView
+ android:id="@+id/textView3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{stateFlowOfStateFlow}" />
+
+ <TextView
+ android:id="@+id/textView4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{stateFlowViewModel.stateFlow}" />
+
+ <EditText
+ android:id="@+id/editText1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@={stateFlowContainer.stateFlow}" />
+
+ <include layout="@layout/state_flow_included"
+ android:id="@+id/included"
+ app:stateFlow="@{stateFlow}"
+ app:stateFlowContainer="@{stateFlowContainer}" />
+
+ </LinearLayout>
+</layout>
diff --git a/integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow_included.xml b/integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow_included.xml
new file mode 100644
index 00000000..68c498e2
--- /dev/null
+++ b/integration-tests/KotlinTestApp/app/src/main/res/layout/state_flow_included.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 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="stateFlow"
+ type="kotlinx.coroutines.flow.StateFlow&lt;String&gt;" />
+
+ <variable
+ name="stateFlowContainer"
+ type="androidx.databinding.kotlintestapp.vo.StateFlowContainer" />
+ </data>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{stateFlow}" />
+
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{stateFlowContainer.stateFlow}" />
+
+ <EditText
+ android:id="@+id/editText1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@={stateFlowContainer.stateFlow}" />
+
+ </LinearLayout>
+</layout>
diff --git a/integration-tests/KotlinTestApp/build.gradle b/integration-tests/KotlinTestApp/build.gradle
index ff3a7d1b..d499539e 100644
--- a/integration-tests/KotlinTestApp/build.gradle
+++ b/integration-tests/KotlinTestApp/build.gradle
@@ -33,4 +33,4 @@ if (!rootProject.ext.runningInIde) {
project.apply from: "../../commonHeader.gradle"
project.apply from: "../../commonLocalRepo.gradle"
}
-} \ No newline at end of file
+}
diff --git a/integration-tests/KotlinTestApp/library1/build.gradle b/integration-tests/KotlinTestApp/library1/build.gradle
index 1fc6a245..2d7055bb 100644
--- a/integration-tests/KotlinTestApp/library1/build.gradle
+++ b/integration-tests/KotlinTestApp/library1/build.gradle
@@ -12,13 +12,14 @@
* 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.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion rootProject.latestCompileSdk
+
defaultConfig {
minSdkVersion 14
targetSdkVersion rootProject.latestCompileSdk
diff --git a/integration-tests/TestApp/app/build.gradle b/integration-tests/TestApp/app/build.gradle
index 993d61ee..f42560a6 100644
--- a/integration-tests/TestApp/app/build.gradle
+++ b/integration-tests/TestApp/app/build.gradle
@@ -32,7 +32,7 @@ android {
dependencies {
def supportVersion = "1.0.0"
- def lifecycleVersion = "2.0.0"
+ def lifecycleVersion = "2.2.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.annotation:annotation:$supportVersion"
compileOnly 'javax.annotation:jsr250-api:1.0'
diff --git a/integration-tests/ViewBindingWithDataBindingTestApp/app/build.gradle b/integration-tests/ViewBindingWithDataBindingTestApp/app/build.gradle
index e768a35f..d9620cf7 100644
--- a/integration-tests/ViewBindingWithDataBindingTestApp/app/build.gradle
+++ b/integration-tests/ViewBindingWithDataBindingTestApp/app/build.gradle
@@ -43,7 +43,7 @@ android {
dependencies {
def supportVersion = "1.0.0"
- def lifecycleVersion = "2.0.0"
+ def lifecycleVersion = "2.2.0"
def testingVersion = "1.1.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.kotlinVersion"