Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46bb16c3 authored by George Mount's avatar George Mount
Browse files

Make ViewStub support binding variables like include.

Bug 19969378
parent efff1c24
Loading
Loading
Loading
Loading
+62 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.databinding.tool.expr.Expr;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.SetterStore;
import android.databinding.tool.store.SetterStore.SetterCall;

public class Binding {

@@ -37,9 +38,17 @@ public class Binding {
    private SetterStore.SetterCall getSetterCall() {
        if (mSetterCall == null) {
            ModelClass viewType = mTarget.getResolvedType();
            if (viewType != null && viewType.extendsViewStub()) {
                if (isViewStubAttribute()) {
                    mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr);
                } else {
                    mSetterCall = new ViewStubSetterCall(mName);
                }
            } else {
                mSetterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName,
                        viewType, mExpr.getResolvedType(), mExpr.getModel().getImports());
            }
        }
        return mSetterCall;
    }

@@ -77,4 +86,55 @@ public class Binding {
    public Expr getExpr() {
        return mExpr;
    }

    private boolean isViewStubAttribute() {
        if ("android:inflatedId".equals(mName)) {
            return true;
        } else if ("android:layout".equals(mName)) {
            return true;
        } else if ("android:visibility".equals(mName)) {
            return true;
        } else {
            return false;
        }
    }

    private static class ViewStubSetterCall extends SetterCall {
        private final String mName;

        public ViewStubSetterCall(String name) {
            mName = name.substring(name.lastIndexOf(':') + 1);
        }

        @Override
        protected String toJavaInternal(String viewExpression, String converted) {
            return "if (" + viewExpression + ".isInflated()) " + viewExpression +
                    ".getBinding().setVariable(BR." + mName + ", " + converted + ")";
        }

        @Override
        public int getMinApi() {
            return 0;
        }
    }

    private static class ViewStubDirectCall extends SetterCall {
        private final SetterCall mWrappedCall;

        public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) {
            mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name,
                    viewType, expr.getResolvedType(), expr.getModel().getImports());
        }

        @Override
        protected String toJavaInternal(String viewExpression, String converted) {
            return "if (!" + viewExpression + ".isInflated()) " +
                    mWrappedCall.toJava(viewExpression + ".getViewStub()", converted);
        }

        @Override
        public int getMinApi() {
            return 0;
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ public abstract class ModelAnalyzer {
    public static final String VIEW_DATA_BINDING =
            "android.databinding.ViewDataBinding";

    public static final String VIEW_STUB_CLASS_NAME = "android.view.ViewStub";

    private ModelClass[] mListTypes;
    private ModelClass mMapType;
    private ModelClass mStringType;
@@ -77,6 +79,7 @@ public abstract class ModelAnalyzer {
    private ModelClass mObservableMapType;
    private ModelClass[] mObservableFieldTypes;
    private ModelClass mViewBindingType;
    private ModelClass mViewStubType;

    private static ModelAnalyzer sAnalyzer;

@@ -282,6 +285,13 @@ public abstract class ModelAnalyzer {
        return mObservableFieldTypes;
    }

    ModelClass getViewStubType() {
        if (mViewStubType == null) {
            mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null);
        }
        return mViewStubType;
    }

    private ModelClass loadClassErasure(String className) {
        return findClass(className, null).erasure();
    }
+7 −0
Original line number Diff line number Diff line
@@ -128,6 +128,13 @@ public abstract class ModelClass {
        return ModelAnalyzer.getInstance().getObjectType().equals(this);
    }

    /**
     * @return whether or not this ModelClass type extends ViewStub.
     */
    public boolean extendsViewStub() {
        return ModelAnalyzer.getInstance().getViewStubType().isAssignableFrom(this);
    }

    /**
     * @return whether or not this is an Observable type such as ObservableMap, ObservableList,
     * or Observable.
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;

import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.util.L;
import android.databinding.tool.util.ParserHelper;

+69 −37
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ class ExprModelExt {
    }
}

val ExprModel.ext by Delegates.lazy { (target : ExprModel) ->
val ExprModel.ext by Delegates.lazy { target : ExprModel ->
    ExprModelExt()
}

@@ -74,7 +74,7 @@ fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueFieldNam

fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base)

val BindingTarget.readableUniqueName by Delegates.lazy {(target: BindingTarget) ->
val BindingTarget.readableUniqueName by Delegates.lazy { target: BindingTarget ->
    val variableName : String
    if (target.getId() == null) {
        variableName = "boundView" + target.getTag()
@@ -84,7 +84,17 @@ val BindingTarget.readableUniqueName by Delegates.lazy {(target: BindingTarget)
    target.getModel().ext.getUniqueFieldName(variableName)
}

val BindingTarget.fieldName by Delegates.lazy { (target : BindingTarget) ->
fun BindingTarget.superConversion(variable : String) : String {
    if (isBinder()) {
        return "${getViewClass()}.bind(${variable})"
    } else if (getResolvedType() != null && getResolvedType().extendsViewStub()) {
        return "new android.databinding.ViewStubProxy((android.view.ViewStub) ${variable})"
    } else {
        return "(${interfaceType}) ${variable}"
    }
}

val BindingTarget.fieldName by Delegates.lazy { target : BindingTarget ->
    if (target.getFieldName() == null) {
        if (target.getId() == null) {
            target.setFieldName("m${target.readableUniqueName.capitalize()}")
@@ -96,61 +106,70 @@ val BindingTarget.fieldName by Delegates.lazy { (target : BindingTarget) ->
    target.getFieldName();
}

val BindingTarget.getterName by Delegates.lazy { (target : BindingTarget) ->
val BindingTarget.getterName by Delegates.lazy { target : BindingTarget ->
    "get${target.readableUniqueName.capitalize()}"
}

val BindingTarget.androidId by Delegates.lazy { (target : BindingTarget) ->
val BindingTarget.androidId by Delegates.lazy { target : BindingTarget ->
    "R.id.${target.getId().androidId()}"
}

val Expr.readableUniqueName by Delegates.lazy { (expr : Expr) ->
val BindingTarget.interfaceType by Delegates.lazy { target : BindingTarget ->
    if (target.getResolvedType() != null && target.getResolvedType().extendsViewStub()) {
        "android.databinding.ViewStubProxy"
    } else {
        target.getInterfaceType()
    }
}

val Expr.readableUniqueName by Delegates.lazy { expr : Expr ->
    Log.d { "readableUniqueName for ${expr.getUniqueKey()}" }
    val stripped = "${expr.getUniqueKey().stripNonJava()}"
    expr.getModel().ext.getUniqueFieldName(stripped)
}

val Expr.fieldName by Delegates.lazy { (expr : Expr) ->
    "m${expr.readableUniqueName.capitalize()}"
val Expr.readableName by Delegates.lazy { expr : Expr ->
    Log.d { "readableUniqueName for ${expr.getUniqueKey()}" }
    "${expr.getUniqueKey().stripNonJava()}"
}

val Expr.fieldName by Delegates.lazy { expr : Expr ->
    "m${expr.readableName.capitalize()}"
}

val Expr.hasFlag by Delegates.lazy { (expr : Expr) ->
val Expr.hasFlag by Delegates.lazy { expr : Expr ->
    expr.getId() < expr.getModel().getInvalidateableFieldLimit()
}

val Expr.localName by Delegates.lazy { (expr : Expr) ->
val Expr.localName by Delegates.lazy { expr : Expr ->
    if(expr.isVariable()) expr.fieldName else "${expr.readableUniqueName}"
}

val Expr.setterName by Delegates.lazy { (expr : Expr) ->
    "set${expr.readableUniqueName.capitalize()}"
val Expr.setterName by Delegates.lazy { expr : Expr ->
    "set${expr.readableName.capitalize()}"
}

val Expr.onChangeName by Delegates.lazy { (expr : Expr) ->
val Expr.onChangeName by Delegates.lazy { expr : Expr ->
    "onChange${expr.readableUniqueName.capitalize()}"
}

val Expr.getterName by Delegates.lazy { (expr : Expr) ->
    "get${expr.readableUniqueName.capitalize()}"
}

val Expr.staticFieldName by Delegates.lazy { (expr : Expr) ->
    "s${expr.readableUniqueName.capitalize()}"
val Expr.getterName by Delegates.lazy { expr : Expr ->
    "get${expr.readableName.capitalize()}"
}

val Expr.dirtyFlagName by Delegates.lazy { (expr : Expr) ->
val Expr.dirtyFlagName by Delegates.lazy { expr : Expr ->
    "sFlag${expr.readableUniqueName.capitalize()}"
}

val Expr.shouldReadFlagName by Delegates.lazy { (expr : Expr) ->
val Expr.shouldReadFlagName by Delegates.lazy { expr : Expr ->
    "sFlagRead${expr.readableUniqueName.capitalize()}"
}

val Expr.invalidateFlagName by Delegates.lazy { (expr : Expr) ->
val Expr.invalidateFlagName by Delegates.lazy { expr : Expr ->
    "sFlag${expr.readableUniqueName.capitalize()}Invalid"
}

val Expr.conditionalFlagPrefix by Delegates.lazy { (expr : Expr) ->
val Expr.conditionalFlagPrefix by Delegates.lazy { expr : Expr ->
    "sFlag${expr.readableUniqueName.capitalize()}Is"
}

@@ -238,22 +257,22 @@ fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic()
fun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix"


val Expr.dirtyFlagSet by Delegates.lazy { (expr : Expr) ->
val Expr.dirtyFlagSet by Delegates.lazy { expr : Expr ->
    val fs = FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount())
    expr.getModel().localizeFlag(fs, expr.dirtyFlagName)
}

val Expr.invalidateFlagSet by Delegates.lazy { (expr : Expr) ->
val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr ->
    val fs = FlagSet(expr.getId())
    expr.getModel().localizeFlag(fs, expr.invalidateFlagName)
}

val Expr.shouldReadFlagSet by Delegates.lazy { (expr : Expr) ->
val Expr.shouldReadFlagSet by Delegates.lazy { expr : Expr ->
    val fs = FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount())
    expr.getModel().localizeFlag(fs, expr.shouldReadFlagName)
}

val Expr.conditionalFlags by Delegates.lazy { (expr : Expr) ->
val Expr.conditionalFlags by Delegates.lazy { expr : Expr ->
    val model = expr.getModel()
    arrayListOf(model.localizeFlag(FlagSet(expr.getRequirementFlagIndex(false)),
            "${expr.conditionalFlagPrefix}False"),
@@ -408,18 +427,23 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
                    val index = indices.get(it)
                    if (!it.isUsed()) {
                        tab(", null")
                    } else if (index == null) {
                        tab(", (${it.getInterfaceType()}) root")
                    } else if (it.isBinder()) {
                        tab(", ${it.getViewClass()}.bind(views[${index}])")
                    } else{
                        tab(", (${it.getInterfaceType()}) views[${index}]")
                        val variableName : String
                        if (index == null) {
                            variableName = "root";
                        } else {
                            variableName = "views[${index}]"
                        }
                        tab(", ${it.superConversion(variableName)}")
                    }
                }
                tab(");")
            }
            val taggedViews = layoutBinder.getBindingTargets().filter{it.isUsed() && !it.isBinder()}
            taggedViews.forEach {
                if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) {
                    tab("this.${it.fieldName}.setContainingBinding(this);")
                }
                if (it.getTag() == null) {
                    if (it.getId() == null) {
                        tab("this.${it.fieldName} = (${it.getViewClass()}) root;")
@@ -584,7 +608,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {

    fun declareViews() = kcode("// views") {
        layoutBinder.getBindingTargets().filter {it.isUsed() && (it.getId() == null)}.forEach {
            nl("private final ${it.getInterfaceType()} ${it.fieldName};")
            nl("private final ${it.interfaceType} ${it.fieldName};")
        }
    }

@@ -596,7 +620,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {

    fun declareDirtyFlags() = kcode("// dirty flag") {
        model.ext.localizedFlags.forEach { flag ->
            flag.notEmpty { (suffix, value) ->
            flag.notEmpty { suffix, value ->
                nl("private")
                app(" ", if(flag.isDynamic()) null else "static final");
                app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = $value;")
@@ -682,6 +706,14 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
            includedBinders.filter{it.isUsed()}.forEach { binder ->
                tab("${binder.fieldName}.executePendingBindings();")
            }
            layoutBinder.getBindingTargets().filter{
                it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub()
            }.forEach {
                tab("if (${it.fieldName}.getBinding() != null) {") {
                    tab("${it.fieldName}.getBinding().executePendingBindings();")
                }
                tab("}")
            }
        }
        nl("}")
    }
@@ -760,12 +792,12 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
            nl("import android.databinding.ViewDataBinding;")
            nl("public abstract class ${baseClassName} extends ViewDataBinding {")
            layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach {
                tab("public final ${it.getInterfaceType()} ${it.fieldName};")
                tab("public final ${it.interfaceType} ${it.fieldName};")
            }
            nl("")
            tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") {
                layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach {
                    tab(", ${it.getInterfaceType()} ${it.readableUniqueName}")
                    tab(", ${it.interfaceType} ${it.readableUniqueName}")
                }
            }
            tab(") {") {
Loading