org.androidannotations.helper.ActivityIntentBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.androidannotations.helper.ActivityIntentBuilder.java

Source

/**
 * Copyright (C) 2010-2015 eBusiness Information, Excilys Group
 *
 * 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 org.androidannotations.helper;

import static com.sun.codemodel.JExpr._new;
import static com.sun.codemodel.JExpr.ref;
import static com.sun.codemodel.JMod.PRIVATE;
import static com.sun.codemodel.JMod.PUBLIC;
import static com.sun.codemodel.JMod.STATIC;

import java.util.List;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

import org.androidannotations.holder.HasIntentBuilder;

import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JConditional;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldRef;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JVar;

public class ActivityIntentBuilder extends IntentBuilder {

    private static final int MIN_SDK_WITH_FRAGMENT_SUPPORT = 11;

    private static final int MIN_SDK_WITH_ACTIVITY_OPTIONS = 16;

    private JFieldVar fragmentField;
    private JFieldVar fragmentSupportField;

    private JFieldRef optionsField;

    public ActivityIntentBuilder(HasIntentBuilder holder, AndroidManifest androidManifest) {
        super(holder, androidManifest);
    }

    @Override
    public void build() throws JClassAlreadyExistsException {
        super.build();

        optionsField = ref("lastOptions");

        createAdditionalConstructor(); // See issue #541
        createAdditionalIntentMethods();
        overrideStartForResultMethod();
    }

    private void createAdditionalIntentMethods() {
        if (hasFragmentInClasspath()) {
            // intent() with android.app.Fragment param
            JMethod method = holder.getGeneratedClass().method(STATIC | PUBLIC, holder.getIntentBuilderClass(),
                    "intent");
            JVar fragmentParam = method.param(holder.classes().FRAGMENT, "fragment");
            method.body()._return(_new(holder.getIntentBuilderClass()).arg(fragmentParam));
        }
        if (hasFragmentSupportInClasspath()) {
            // intent() with android.support.v4.app.Fragment param
            JMethod method = holder.getGeneratedClass().method(STATIC | PUBLIC, holder.getIntentBuilderClass(),
                    "intent");
            JVar fragmentParam = method.param(holder.classes().SUPPORT_V4_FRAGMENT, "supportFragment");
            method.body()._return(_new(holder.getIntentBuilderClass()).arg(fragmentParam));
        }
    }

    @Override
    protected JClass getSuperClass() {
        JClass superClass = holder.refClass(org.androidannotations.api.builder.ActivityIntentBuilder.class);
        return superClass.narrow(builderClass);
    }

    private void createAdditionalConstructor() {
        if (hasFragmentInClasspath()) {
            fragmentField = addFragmentConstructor(holder.classes().FRAGMENT, "fragment_");
        }
        if (hasFragmentSupportInClasspath()) {
            fragmentSupportField = addFragmentConstructor(holder.classes().SUPPORT_V4_FRAGMENT, "fragmentSupport_");
        }
    }

    private JFieldVar addFragmentConstructor(JClass fragmentClass, String fieldName) {
        JFieldVar fragmentField = holder.getIntentBuilderClass().field(PRIVATE, fragmentClass, fieldName);
        JExpression generatedClass = holder.getGeneratedClass().dotclass();

        JMethod constructor = holder.getIntentBuilderClass().constructor(JMod.PUBLIC);
        JVar constructorFragmentParam = constructor.param(fragmentClass, "fragment");
        JBlock constructorBody = constructor.body();
        constructorBody.invoke("super").arg(constructorFragmentParam.invoke("getActivity")).arg(generatedClass);
        constructorBody.assign(fragmentField, constructorFragmentParam);

        return fragmentField;
    }

    private void overrideStartForResultMethod() {
        JMethod method = holder.getIntentBuilderClass().method(PUBLIC, holder.codeModel().VOID, "startForResult");
        method.annotate(Override.class);
        JVar requestCode = method.param(holder.codeModel().INT, "requestCode");
        JBlock body = method.body();

        JConditional condition = null;
        if (fragmentSupportField != null) {
            condition = body._if(fragmentSupportField.ne(JExpr._null()));
            condition._then() //
                    .invoke(fragmentSupportField, "startActivityForResult").arg(intentField).arg(requestCode);
        }
        if (fragmentField != null) {
            if (condition == null) {
                condition = body._if(fragmentField.ne(JExpr._null()));
            } else {
                condition = condition._elseif(fragmentField.ne(JExpr._null()));
            }

            JBlock fragmentStartForResultInvocationBlock;

            if (hasActivityOptionsInFragment() && shouldGuardActivityOptions()) {
                fragmentStartForResultInvocationBlock = createCallWithIfGuard(requestCode, condition._then(),
                        fragmentField);
            } else {
                fragmentStartForResultInvocationBlock = condition._then();
            }
            JInvocation invocation = fragmentStartForResultInvocationBlock //
                    .invoke(fragmentField, "startActivityForResult").arg(intentField).arg(requestCode);
            if (hasActivityOptionsInFragment()) {
                invocation.arg(optionsField);
            }
        }

        JBlock activityStartInvocationBlock = null;

        if (condition != null) {
            activityStartInvocationBlock = condition._else();
        } else {
            activityStartInvocationBlock = method.body();
        }

        JConditional activityCondition = activityStartInvocationBlock
                ._if(contextField._instanceof(holder.classes().ACTIVITY));
        JBlock thenBlock = activityCondition._then();
        JVar activityVar = thenBlock.decl(holder.classes().ACTIVITY, "activity",
                JExpr.cast(holder.classes().ACTIVITY, contextField));

        if (hasActivityCompatInClasspath() && hasActivityOptionsInActivityCompat()) {
            thenBlock.staticInvoke(holder.classes().ACTIVITY_COMPAT, "startActivityForResult") //
                    .arg(activityVar).arg(intentField).arg(requestCode).arg(optionsField);
        } else if (hasActivityOptionsInFragment()) {
            JBlock startForResultInvocationBlock;
            if (shouldGuardActivityOptions()) {
                startForResultInvocationBlock = createCallWithIfGuard(requestCode, thenBlock, activityVar);
            } else {
                startForResultInvocationBlock = thenBlock;
            }

            startForResultInvocationBlock.invoke(activityVar, "startActivityForResult") //
                    .arg(intentField).arg(requestCode).arg(optionsField);
        } else {
            thenBlock.invoke(activityVar, "startActivityForResult").arg(intentField).arg(requestCode);
        }

        if (hasActivityOptionsInFragment()) {
            JBlock startInvocationBlock;
            if (shouldGuardActivityOptions()) {
                startInvocationBlock = createCallWithIfGuard(null, activityCondition._else(), contextField);
            } else {
                startInvocationBlock = activityCondition._else();
            }
            startInvocationBlock.invoke(contextField, "startActivity").arg(intentField).arg(optionsField);
        } else {
            activityCondition._else().invoke(contextField, "startActivity").arg(intentField);
        }
    }

    private JBlock createCallWithIfGuard(JVar requestCode, JBlock thenBlock, JExpression invocationTarget) {
        JConditional guardIf = thenBlock._if(holder.classes().BUILD_VERSION.staticRef("SDK_INT")
                .gte(holder.classes().BUILD_VERSION_CODES.staticRef("JELLY_BEAN")));
        JBlock startInvocationBlock = guardIf._then();
        String methodName = requestCode != null ? "startActivityForResult" : "startActivity";

        JInvocation invocation = guardIf._else().invoke(invocationTarget, methodName).arg(intentField);
        if (requestCode != null) {
            invocation.arg(requestCode);
        }
        return startInvocationBlock;
    }

    protected boolean hasFragmentInClasspath() {
        boolean fragmentExistsInSdk = androidManifest.getMinSdkVersion() >= MIN_SDK_WITH_FRAGMENT_SUPPORT;
        return fragmentExistsInSdk && elementUtils.getTypeElement(CanonicalNameConstants.FRAGMENT) != null;
    }

    protected boolean hasFragmentSupportInClasspath() {
        return elementUtils.getTypeElement(CanonicalNameConstants.SUPPORT_V4_FRAGMENT) != null;
    }

    protected boolean hasActivityCompatInClasspath() {
        return elementUtils.getTypeElement(CanonicalNameConstants.ACTIVITY_COMPAT) != null;
    }

    protected boolean hasActivityOptionsInFragment() {
        if (!hasFragmentInClasspath()) {
            return false;
        }

        TypeElement fragment = elementUtils.getTypeElement(CanonicalNameConstants.FRAGMENT);

        return hasActivityOptions(fragment, 1);
    }

    protected boolean hasActivityOptionsInActivityCompat() {
        TypeElement activityCompat = elementUtils.getTypeElement(CanonicalNameConstants.ACTIVITY_COMPAT);

        return hasActivityOptions(activityCompat, 2);
    }

    private boolean hasActivityOptions(TypeElement type, int optionsParamPosition) {
        if (type == null) {
            return false;
        }

        for (Element element : type.getEnclosedElements()) {
            if (element.getKind() == ElementKind.METHOD) {
                ExecutableElement executableElement = (ExecutableElement) element;
                if (executableElement.getSimpleName().contentEquals("startActivity")) {
                    List<? extends VariableElement> parameters = executableElement.getParameters();
                    if (parameters.size() == optionsParamPosition + 1) {
                        VariableElement parameter = parameters.get(optionsParamPosition);
                        if (parameter.asType().toString().equals(CanonicalNameConstants.BUNDLE)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    protected boolean shouldGuardActivityOptions() {
        return androidManifest.getMinSdkVersion() < MIN_SDK_WITH_ACTIVITY_OPTIONS;
    }
}