Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New view annotation for hook after view bound #242

Merged
merged 1 commit into from
Jul 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.airbnb.epoxy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This can be used to annotate methods inside classes with a {@link com.airbnb.epoxy.ModelView}
* annotation. Methods with this annotation will be called after a view instance is bound to a
* model and all model props have been set. This is useful if you need to wait until multiple props
* are set before doing certain initialization.
* <p>
* Methods with this annotation will be called after both the initial bind when the view comes on
* screen, and after partial binds when an onscreen view is updated.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface AfterPropsSet {
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This can be used to annotate methods inside classes with a {@link com.airbnb.epoxy.ModelView}
* annotation. Methods with this annotation will be called when the view is recycled by the
* RecyclerView.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnViewRecycled {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ boolean addToBindWithDiffMethod(Builder methodBuilder, ParameterSpec boundObject
ParameterSpec previousModelParam) {
return false;
}

public void addToHandlePostBindMethod(Builder postBindBuilder, ParameterSpec boundObjectParam) {

}
}

GeneratedModelWriter(Filer filer, Types typeUtils, ErrorLogger errorLogger,
Expand Down Expand Up @@ -429,6 +433,8 @@ private Iterable<MethodSpec> generateBindMethods(GeneratedModelInfo classInfo) {
addHashCodeValidationIfNecessary(postBindBuilder,
"The model was changed during the bind call.");

builderHooks.addToHandlePostBindMethod(postBindBuilder, boundObjectParam);

methods.add(postBindBuilder
.build());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package com.airbnb.epoxy

import com.airbnb.epoxy.ClassNames.EPOXY_LITHO_MODEL
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName

import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.lang.model.util.Elements
import javax.lang.model.util.Types

import com.airbnb.epoxy.ClassNames.EPOXY_LITHO_MODEL

internal class LithoModelInfo(
typeUtils: Types,
elementUtils: Elements,
layoutSpecClassElement: TypeElement) : GeneratedModelInfo() {
layoutSpecClassElement: TypeElement
) : GeneratedModelInfo() {

val lithoComponentName: ClassName

Expand All @@ -39,7 +38,7 @@ internal class LithoModelInfo(
* package, and with the "Spec" term removed from the name.
*/
fun getLithoComponentName(elementUtils: Elements,
layoutSpecClassElement: TypeElement): ClassName {
layoutSpecClassElement: TypeElement): ClassName {
val packageName = elementUtils.getPackageOf(layoutSpecClassElement).qualifiedName.toString()

// Litho doesn't appear to allow specs as nested classes, so we don't check for nested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

class ModelViewInfo extends GeneratedModelInfo {
final List<String> resetMethodNames = new ArrayList<>();
final List<String> afterPropsSetMethodNames = new ArrayList<>();
final TypeElement viewElement;
final Types typeUtils;
final Elements elements;
Expand Down Expand Up @@ -144,6 +145,10 @@ void addOnRecycleMethod(ExecutableElement resetMethod) {
resetMethodNames.add(resetMethod.getSimpleName().toString());
}

void addAfterPropsSetMethod(ExecutableElement afterPropsSetMethod) {
afterPropsSetMethodNames.add(afterPropsSetMethod.getSimpleName().toString());
}

LayoutResource getLayoutResource(LayoutResourceProcessor layoutResourceProcessor) {
ModelView annotation = viewElement.getAnnotation(ModelView.class);
int layoutValue = annotation.defaultLayout();
Expand All @@ -166,6 +171,10 @@ List<String> getResetMethodNames() {
return resetMethodNames;
}

List<String> getAfterPropsSetMethodNames() {
return afterPropsSetMethodNames;
}

List<ViewAttributeInfo> getViewAttributes() {
List<ViewAttributeInfo> result = new ArrayList<>(attributeInfo.size());
for (AttributeInfo info : attributeInfo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Collection<? extends GeneratedModelInfo> process(RoundEnvironment roundEnv,

processSetterAnnotations(roundEnv);
processResetAnnotations(roundEnv);
processAfterBindAnnotations(roundEnv);

updateViewsForInheritedViewAnnotations();

Expand Down Expand Up @@ -242,14 +243,35 @@ private void processResetAnnotations(RoundEnvironment roundEnv) {
ModelViewInfo info = getModelInfoForMethodElement(recycleMethod);
if (info == null) {
errorLogger.logError("%s annotation can only be used in classes annotated with %s",
ModelProp.class, ModelView.class);
OnViewRecycled.class, ModelView.class);
continue;
}

info.addOnRecycleMethod((ExecutableElement) recycleMethod);
}
}

private void processAfterBindAnnotations(RoundEnvironment roundEnv) {
for (Element afterPropsMethod : roundEnv.getElementsAnnotatedWith(AfterPropsSet.class)) {
if (!validateAfterPropsMethod(afterPropsMethod)) {
continue;
}

ModelViewInfo info = getModelInfoForMethodElement(afterPropsMethod);
if (info == null) {
errorLogger.logError("%s annotation can only be used in classes annotated with %s",
AfterPropsSet.class, ModelView.class);
continue;
}

info.addAfterPropsSetMethod((ExecutableElement) afterPropsMethod);
}
}

private boolean validateAfterPropsMethod(Element resetMethod) {
return validateExecutableElement(resetMethod, AfterPropsSet.class, 0);
}

/** Include props and reset methods from super class views. */
private void updateViewsForInheritedViewAnnotations() {
for (ModelViewInfo view : modelClassMap.values()) {
Expand All @@ -262,6 +284,7 @@ private void updateViewsForInheritedViewAnnotations() {
}

view.resetMethodNames.addAll(otherView.resetMethodNames);
view.afterPropsSetMethodNames.addAll(otherView.afterPropsSetMethodNames);

boolean samePackage =
belongToTheSamePackage(view.viewElement, otherView.viewElement, elements);
Expand Down Expand Up @@ -462,6 +485,13 @@ boolean addToBindWithDiffMethod(Builder methodBuilder, ParameterSpec boundObject
return true;
}

@Override
public void addToHandlePostBindMethod(Builder postBindBuilder,
ParameterSpec boundObjectParam) {

addAfterPropsAddedMethodsToBuilder(postBindBuilder, modelInfo, boundObjectParam);
}

@Override
void addToUnbindMethod(MethodSpec.Builder unbindBuilder, String unbindParamName) {
for (ViewAttributeInfo viewAttribute : modelInfo.getViewAttributes()) {
Expand Down Expand Up @@ -558,6 +588,13 @@ private void addResetMethodsToBuilder(Builder builder, ModelViewInfo modelViewIn
}
}

private void addAfterPropsAddedMethodsToBuilder(Builder methodBuilder, ModelViewInfo modelInfo,
ParameterSpec boundObjectParam) {
for (String methodName : modelInfo.getAfterPropsSetMethodNames()) {
methodBuilder.addStatement(boundObjectParam.name + "." + methodName + "()");
}
}

private ModelViewInfo getModelInfoForMethodElement(Element element) {
Element enclosingElement = element.getEnclosingElement();
if (enclosingElement == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -857,4 +857,23 @@ public void layoutOverloads() {
.and()
.generatesSources(generatedModel);
}

@Test
public void afterBindProps() {
JavaFileObject model = JavaFileObjects
.forResource("TestAfterBindPropsView.java");

JavaFileObject superModel = JavaFileObjects
.forResource("TestAfterBindPropsSuperView.java");

JavaFileObject generatedModel =
JavaFileObjects.forResource("TestAfterBindPropsViewModel_.java");

assert_().about(javaSources())
.that(asList(model, superModel))
.processedWith(new EpoxyProcessor())
.compilesWithoutError()
.and()
.generatesSources(generatedModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.airbnb.epoxy;

import android.content.Context;
import android.view.View;

@ModelView(defaultLayout = 1)
public abstract class TestAfterBindPropsSuperView extends View {

public TestAfterBindPropsSuperView(Context context) {
super(context);
}

@ModelProp
public void setFlagSuper(boolean flag) {

}

@AfterPropsSet
public void afterFlagSetSuper() {

}
}
21 changes: 21 additions & 0 deletions epoxy-processortest/src/test/resources/TestAfterBindPropsView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.airbnb.epoxy;

import android.content.Context;

@ModelView(defaultLayout = 1)
public class TestAfterBindPropsView extends TestAfterBindPropsSuperView {

public TestAfterBindPropsView(Context context) {
super(context);
}

@ModelProp
public void setFlag(boolean flag) {

}

@AfterPropsSet
public void afterFlagSet() {

}
}
Loading