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

Commit a51e5038 authored by android-build-prod (mdb)'s avatar android-build-prod (mdb) Committed by android-build-merger
Browse files

Merge "Add application generation for root component generator." am: 86214b64

am: 29f7249f

Change-Id: Ife777f50e436af02df6c61d49c51fb9a9346b37d
parents 63302794 29f7249f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -438,6 +438,7 @@ BASE_DIR := java/com/android

LOCAL_SRC_FILES := \
	$(call all-java-files-under, $(BASE_DIR)/dialer/rootcomponentgenerator) \
	      $(BASE_DIR)/dialer/inject/GenerateDaggerApp.java \
        $(BASE_DIR)/dialer/inject/DialerRootComponent.java \
        $(BASE_DIR)/dialer/inject/DialerVariant.java \
        $(BASE_DIR)/dialer/inject/HasRootComponent.java \
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.dialer.inject;

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

/** Annotates an inner class where an application with dagger instance is needed. */
@Target({ElementType.TYPE})
public @interface GenerateDaggerApp {
  /** Name of the application to be generated. */
  String name();

  /** Dialer variant of the application to be generated. */
  DialerVariant variant();
}
+2 −2
Original line number Diff line number Diff line
@@ -20,8 +20,8 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/**
 * Only used by rootcomponent generator to store metadata for locating annotated
 * (@DialerComponent, @InstallIn) class.
 * Only used by rootcomponent generator to store metadata for locating annotated class with {@link
 * IncludeInDialerRoot}, {@link InstallIn}.
 */
@Target(ElementType.TYPE)
public @interface RootComponentGeneratorMetadata {
+146 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.dialer.rootcomponentgenerator;

import com.android.dialer.inject.DialerRootComponent;
import com.android.dialer.inject.DialerVariant;
import com.android.dialer.inject.GenerateDaggerApp;
import com.android.dialer.inject.HasRootComponent;
import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

/**
 * Generates an application class with dagger component instance for a type annotated with {@link
 * GenerateDaggerApp}.
 *
 * <p>Generated code example:
 *
 * <p><code>
 *  @DialerRootComponent(variant = DialerVariant.DIALER_TEST)
 * class GeneratedApplication extends Application implements HasRootComponent {
 *  private volatile Object rootComponent;
 *
 *  @Override
 *  @NonNull
 *  public final Object component() {
 *   Object result = rootComponent;
 *     if (result == null) {
 *       synchronized (this) {
 *         result = rootComponent;
 *         if (result == null) {
 *           rootComponent =
 *              result = DaggerDialerTest.builder().contextModule(new ContextModule(this)).build();
 *         }
 *       }
 *     }
 *   return result;
 *  }
 * }
 * </code>
 */
public class DaggerApplicationGeneratingStep implements ProcessingStep {

  private static final ClassName ANDROID_APPLICATION_CLASS_NAME =
      ClassName.get("android.app", "Application");

  private final ProcessingEnvironment processingEnv;

  public DaggerApplicationGeneratingStep(ProcessingEnvironment processingEnv) {
    this.processingEnv = processingEnv;
  }

  @Override
  public Set<? extends Class<? extends Annotation>> annotations() {
    return ImmutableSet.of(GenerateDaggerApp.class);
  }

  @Override
  public Set<? extends Element> process(
      SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
    for (Element element : elementsByAnnotation.get(GenerateDaggerApp.class)) {
      GenerateDaggerApp generateDaggerApp = element.getAnnotation(GenerateDaggerApp.class);
      RootComponentUtils.writeJavaFile(
          processingEnv,
          ClassName.get(MoreElements.asType(element)).packageName(),
          generateDaggerApplication(generateDaggerApp.name(), generateDaggerApp.variant()));
    }

    return Collections.emptySet();
  }

  private TypeSpec generateDaggerApplication(String name, DialerVariant variant) {
    return TypeSpec.classBuilder(name)
        .addAnnotation(
            AnnotationSpec.builder(DialerRootComponent.class)
                .addMember("variant", "$T.$L", DialerVariant.class, variant.name())
                .build())
        .superclass(ANDROID_APPLICATION_CLASS_NAME)
        .addSuperinterface(HasRootComponent.class)
        .addField(
            FieldSpec.builder(TypeName.OBJECT, "rootComponent", Modifier.PRIVATE, Modifier.VOLATILE)
                .build())
        .addMethod(generateComponentMethod(variant))
        .build();
  }

  private MethodSpec generateComponentMethod(DialerVariant dialerVariant) {
    return MethodSpec.overriding(getComponentMethodFromHasRootComponent())
        .addModifiers(Modifier.FINAL)
        .addAnnotation(ClassName.get("android.support.annotation", "NonNull"))
        .addStatement("$T result = rootComponent", TypeName.OBJECT)
        .beginControlFlow("if (result == null)")
        .beginControlFlow("synchronized (this)")
        .addStatement("result = rootComponent")
        .beginControlFlow("if (result == null)")
        .addStatement(
            "rootComponent = result = Dagger$L.builder().contextModule(new $T(this)).build()",
            dialerVariant,
            ClassName.get("com.android.dialer.inject", "ContextModule"))
        .endControlFlow()
        .endControlFlow()
        .endControlFlow()
        .addStatement("return result")
        .build();
  }

  private ExecutableElement getComponentMethodFromHasRootComponent() {
    TypeElement hasRootComponentInterafce =
        processingEnv.getElementUtils().getTypeElement(HasRootComponent.class.getTypeName());
    for (Element element : hasRootComponentInterafce.getEnclosedElements()) {
      if (element.getSimpleName().contentEquals("component")) {
        return MoreElements.asExecutable(element);
      }
    }
    throw new RuntimeException("No component method inside HasRootComponent!");
  }
}
+7 −2
Original line number Diff line number Diff line
@@ -72,8 +72,7 @@ final class RootComponentGeneratingStep implements ProcessingStep {
      // defer root components to the next round in case where the current build target contains
      // elements annotated with @InstallIn. Annotation processor cannot detect metadata files
      // generated in the same round and the metadata is accessible in the next round.
      if (elementsByAnnotation.containsKey(InstallIn.class)
          || elementsByAnnotation.containsKey(IncludeInDialerRoot.class)) {
      if (shouldDeferRootComponent(elementsByAnnotation)) {
        return elementsByAnnotation.get(DialerRootComponent.class);
      } else {
        generateRootComponent(MoreElements.asType(element));
@@ -82,6 +81,12 @@ final class RootComponentGeneratingStep implements ProcessingStep {
    return Collections.emptySet();
  }

  private boolean shouldDeferRootComponent(
      SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
    return elementsByAnnotation.containsKey(InstallIn.class)
        || elementsByAnnotation.containsKey(IncludeInDialerRoot.class);
  }

  /**
   * Generates a root component.
   *
Loading