summaryrefslogtreecommitdiff
path: root/compiler/src/main/java/android/databinding
diff options
context:
space:
mode:
authorGeorge Mount <mount@google.com>2017-11-16 08:42:43 -0800
committerYigit Boyar <yboyar@google.com>2017-11-27 12:54:08 -0800
commitf383e35e8a56958c89b92abe193fa2bad73d98d5 (patch)
tree0e8884ab5fea594d3e4a6287ce1627e5e5034852 /compiler/src/main/java/android/databinding
parent5a8588197dd14261e9010f326b62d15bbff0c23c (diff)
downloaddata-binding-f383e35e8a56958c89b92abe193fa2bad73d98d5.tar.gz
Make LiveData observable by data binding.
Bug: 36122539 Data binding needed to support observing LiveData with a LifecycleOwner. This CL adds listening to LiveData change events. If no LifecycleOwner exists, the LiveData will not be observed. Test: LiveDataTest Change-Id: I76b0a4d38a9107a0fd7f6e03594aea49be9d645b
Diffstat (limited to 'compiler/src/main/java/android/databinding')
-rw-r--r--compiler/src/main/java/android/databinding/tool/Binding.java10
-rw-r--r--compiler/src/main/java/android/databinding/tool/BindingTarget.java5
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/Expr.java20
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java14
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java22
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java53
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java5
-rw-r--r--compiler/src/main/java/android/databinding/tool/store/SetterStore.java16
8 files changed, 99 insertions, 46 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/Binding.java b/compiler/src/main/java/android/databinding/tool/Binding.java
index 529926bf..467a4cad 100644
--- a/compiler/src/main/java/android/databinding/tool/Binding.java
+++ b/compiler/src/main/java/android/databinding/tool/Binding.java
@@ -138,27 +138,19 @@ public class Binding implements LocationScopeProvider {
}
} else {
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
- boolean warn = false;
- if (mExpr.getResolvedType().isObservableField()) {
+ if (mExpr.getResolvedType().getObservableGetterName() != null) {
// If it is an ObservableField, try with the contents of it first.
Expr expr = mExpr.unwrapObservableField();
mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName,
viewType, expr.getResolvedType(), mExpr.getModel().getImports());
if (mSetterCall != null) {
mExpr = expr;
- } else {
- // No setter for the contents of the ObservableField.
- // If we find one for the ObservableField, we should warn.
- warn = true;
}
}
if (mSetterCall == null) {
// Now try with the value object directly
mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName,
viewType, mExpr.getResolvedType(), mExpr.getModel().getImports());
- if (warn && mSetterCall != null) {
- L.w(ErrorMessages.OBSERVABLE_FIELD_USE, mSetterCall.getDescription());
- }
}
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/BindingTarget.java b/compiler/src/main/java/android/databinding/tool/BindingTarget.java
index d24cb671..83c28b08 100644
--- a/compiler/src/main/java/android/databinding/tool/BindingTarget.java
+++ b/compiler/src/main/java/android/databinding/tool/BindingTarget.java
@@ -32,7 +32,6 @@ import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -207,7 +206,7 @@ public class BindingTarget implements LocationScopeProvider {
Scope.enter(binding);
attributes[i] = binding.getName();
final ModelClass type = binding.getExpr().getResolvedType();
- if (type.isObservableField()) {
+ if (type.getObservableGetterName() != null) {
hasObservableFields = true;
types[i] = binding.getExpr().unwrapObservableField().getResolvedType();
} else {
@@ -239,8 +238,6 @@ public class BindingTarget implements LocationScopeProvider {
getMultiAttributeSetterCalls(attributes, getResolvedType(), types);
List<MergedBinding> observableFieldCalls =
createMultiSetters(multiAttributeSetterCalls, false);
- observableFieldCalls.forEach(call -> L.w(ErrorMessages.OBSERVABLE_FIELD_USE,
- call.getMultiAttributeSetter().getDescription()));
merged.addAll(observableFieldCalls);
}
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 f05c950d..4a3dd324 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/Expr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
@@ -185,6 +185,16 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
return getResolvedType().isObservable();
}
+ public String getUpdateRegistrationCall() {
+ if (!isObservable()) {
+ L.e("The expression isn't observable!");
+ }
+ if (getResolvedType().isLiveData()) {
+ return "updateLiveDataRegistration";
+ }
+ return "updateRegistration";
+ }
+
public void setUnwrapObservableFields(boolean unwrapObservableFields) {
mUnwrapObservableFields = unwrapObservableFields;
}
@@ -855,8 +865,9 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
public Expr unwrapObservableField() {
Expr expr = this;
- while (expr.getResolvedType().isObservableField()) {
- Expr unwrapped = mModel.methodCall(expr, "get", Collections.EMPTY_LIST);
+ String simpleGetterName;
+ while ((simpleGetterName = expr.getResolvedType().getObservableGetterName()) != null) {
+ Expr unwrapped = mModel.methodCall(expr, simpleGetterName, Collections.EMPTY_LIST);
mModel.bindingExpr(unwrapped);
unwrapped.setUnwrapObservableFields(false);
expr = unwrapped;
@@ -884,10 +895,11 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
final Expr child = mChildren.get(childIndex);
Expr unwrapped = null;
Expr expr = child;
- while (expr.getResolvedType().isObservableField()
+ String simpleGetterName;
+ while ((simpleGetterName = expr.getResolvedType().getObservableGetterName()) != null
&& (type == null || (!type.isAssignableFrom(expr.getResolvedType())
&& !ModelMethod.isImplicitConversion(expr.getResolvedType(), type)))) {
- unwrapped = mModel.methodCall(expr, "get", Collections.EMPTY_LIST);
+ unwrapped = mModel.methodCall(expr, simpleGetterName, Collections.EMPTY_LIST);
if (unwrapped == this) {
L.w(ErrorMessages.OBSERVABLE_FIELD_GET, this);
return; // This was already unwrapped!
diff --git a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
index 4012f249..371925c2 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
@@ -31,7 +31,6 @@ import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import static android.databinding.tool.reflection.Callable.DYNAMIC;
@@ -273,8 +272,9 @@ public class MethodCallExpr extends Expr {
if (mMethod == null) {
return "Could not find the method " + mName + " to inverse for two-way binding";
}
- if (mName.equals("get") && getTarget().getResolvedType().isObservableField() &&
- getArgs().isEmpty()) {
+ final ModelClass targetResolvedType = getTarget().getResolvedType();
+ if (mName.equals(targetResolvedType.getObservableGetterName())
+ && targetResolvedType.getObservableSetterName() != null && getArgs().isEmpty()) {
return null;
}
String inverse = setterStore.getInverseMethod(mMethod);
@@ -289,11 +289,13 @@ public class MethodCallExpr extends Expr {
@Override
public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
getResolvedType(); // ensure mMethod has been resolved.
- if (mName.equals("get") && getTarget().getResolvedType().isObservableField() &&
- getArgs().isEmpty()) {
+ ModelClass targetResolvedType = getTarget().getResolvedType();
+ if (mName.equals(targetResolvedType.getObservableGetterName())
+ && getArgs().isEmpty()) {
Expr castExpr = model.castExpr(getResolvedType().toJavaCode(), value);
Expr target = getTarget().cloneToModel(model);
- Expr inverse = model.methodCall(target, "set", Lists.newArrayList(castExpr));
+ String simpleSetterName = targetResolvedType.getObservableSetterName();
+ Expr inverse = model.methodCall(target, simpleSetterName, Lists.newArrayList(castExpr));
inverse.setUnwrapObservableFields(false);
return inverse;
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
index ec0764fe..a6ecc5b7 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
@@ -18,7 +18,6 @@ package android.databinding.tool.reflection;
import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
-import android.databinding.tool.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
@@ -54,6 +53,11 @@ public abstract class ModelAnalyzer {
public static final String OBSERVABLE_MAP_CLASS_NAME = "android.databinding.ObservableMap";
+ public static final String LIVE_DATA_CLASS_NAME = "android.arch.lifecycle.LiveData";
+
+ public static final String MUTABLE_LIVE_DATA_CLASS_NAME =
+ "android.arch.lifecycle.MutableLiveData";
+
public static final String[] OBSERVABLE_FIELDS = {
"android.databinding.ObservableBoolean",
"android.databinding.ObservableByte",
@@ -79,6 +83,8 @@ public abstract class ModelAnalyzer {
private ModelClass mObservableType;
private ModelClass mObservableListType;
private ModelClass mObservableMapType;
+ private ModelClass mLiveDataType;
+ private ModelClass mMutableLiveDataType;
private ModelClass[] mObservableFieldTypes;
private ModelClass mViewBindingType;
private ModelClass mViewStubType;
@@ -300,6 +306,20 @@ public abstract class ModelAnalyzer {
return mObservableMapType;
}
+ ModelClass getLiveDataType() {
+ if (mLiveDataType == null) {
+ mLiveDataType = loadClassErasure(LIVE_DATA_CLASS_NAME);
+ }
+ return mLiveDataType;
+ }
+
+ ModelClass getMutableLiveDataType() {
+ if (mMutableLiveDataType == null) {
+ mMutableLiveDataType = loadClassErasure(MUTABLE_LIVE_DATA_CLASS_NAME);
+ }
+ return mMutableLiveDataType;
+ }
+
ModelClass getViewDataBindingType() {
if (mViewBindingType == null) {
mViewBindingType = findClass(VIEW_DATA_BINDING, null);
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 47cd3c43..e4351222 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
@@ -203,10 +203,11 @@ public abstract class ModelClass {
*/
public boolean isObservable() {
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
- return modelAnalyzer.getObservableType().isAssignableFrom(this) ||
- modelAnalyzer.getObservableListType().isAssignableFrom(this) ||
- modelAnalyzer.getObservableMapType().isAssignableFrom(this);
-
+ return modelAnalyzer.getObservableType().isAssignableFrom(this)
+ || modelAnalyzer.getObservableListType().isAssignableFrom(this)
+ || modelAnalyzer.getObservableMapType().isAssignableFrom(this)
+ || (modelAnalyzer.getLiveDataType() != null
+ && modelAnalyzer.getLiveDataType().isAssignableFrom(this));
}
/**
@@ -224,6 +225,50 @@ public abstract class ModelClass {
}
/**
+ * @return whether or not this is a LiveData
+ */
+ public boolean isLiveData() {
+ ModelClass liveDataType = ModelAnalyzer.getInstance().getLiveDataType();
+ return liveDataType != null && liveDataType.isAssignableFrom(erasure());
+ }
+
+ /**
+ * @return whether or not this is a MutableLiveData
+ */
+ public boolean isMutableLiveData() {
+ ModelClass mutableLiveDataType = ModelAnalyzer.getInstance().getMutableLiveDataType();
+ return mutableLiveDataType != null && mutableLiveDataType.isAssignableFrom(erasure());
+ }
+
+ /**
+ * @return the name of the simple getter method when this is an ObservableField or LiveData or
+ * {@code null} for any other type
+ */
+ public String getObservableGetterName() {
+ if (isObservableField()) {
+ return "get";
+ } else if (isLiveData()) {
+ return "getValue";
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return the name of the simple setter method when this is an ObservableField or
+ * MutableLiveData or {@code null} for any other type.
+ */
+ public String getObservableSetterName() {
+ if (isObservableField()) {
+ return "set";
+ } else if (isMutableLiveData()) {
+ return "setValue";
+ } else {
+ return null;
+ }
+ }
+
+ /**
* @return whether or not this ModelClass represents a void
*/
public abstract boolean isVoid();
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
index a7109277..b83ad214 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
@@ -99,8 +99,9 @@ public abstract class ModelMethod {
parametersMatch = false;
if (unwrapObservableFields) {
// try unwrapping an observable field argument, if possible
- while (arg.isObservableField()) {
- arg = arg.getMethod("get", Collections.EMPTY_LIST,
+ String simpleGetterName;
+ while ((simpleGetterName = arg.getObservableGetterName()) != null) {
+ arg = arg.getMethod(simpleGetterName, Collections.EMPTY_LIST,
false, false, false).getReturnType();
if (parameterType.isAssignableFrom(arg)
|| isImplicitConversion(arg, parameterType)) {
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 22220427..8c8fd4af 100644
--- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
+++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -256,12 +256,6 @@ public class SetterStore {
}
adapters.put(key, new MethodDescription(bindingMethod, 1, takesComponent));
-
- if (mClassAnalyzer.findClass(value, null).isObservableField()) {
- ExecutableType executableType = (ExecutableType) bindingMethod.asType();
- L.w(bindingMethod, ErrorMessages.OBSERVABLE_FIELD_USE,
- AnnotationTypeUtil.getInstance().toJava(bindingMethod, executableType));
- }
}
public void addInverseAdapter(ProcessingEnvironment processingEnv, String attribute,
@@ -358,16 +352,6 @@ public class SetterStore {
MethodDescription methodDescription = new MethodDescription(bindingMethod,
attributes.length, takesComponent);
mStore.multiValueAdapters.put(key, methodDescription);
-
- final long numObservableFields = Arrays.stream(key.parameterTypes)
- .map(type -> mClassAnalyzer.findClass(type, null))
- .filter(type -> type.isObservableField())
- .count();
- if (numObservableFields > 0) {
- ExecutableType executableType = (ExecutableType) bindingMethod.asType();
- L.w(bindingMethod, ErrorMessages.OBSERVABLE_FIELD_USE,
- AnnotationTypeUtil.getInstance().toJava(bindingMethod, executableType));
- }
}
private static void testRepeatedAttributes(MultiValueAdapterKey key, ExecutableElement method) {