com.android.tools.idea.npw.AddAndroidActivityPath.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tools.idea.npw.AddAndroidActivityPath.java

Source

/*
 * Copyright (C) 2014 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.tools.idea.npw;

import com.android.builder.model.SourceProvider;
import com.android.sdklib.AndroidVersion;
import com.android.tools.idea.model.AndroidModel;
import com.android.tools.idea.model.AndroidModuleInfo;
import com.android.tools.idea.model.MergedManifest;
import com.android.tools.idea.npw.template.ConfigureTemplateParametersStep;
import com.android.tools.idea.npw.template.RenderTemplateModel;
import com.android.tools.idea.templates.*;
import com.android.tools.idea.templates.recipe.RenderingContext;
import com.android.tools.idea.wizard.dynamic.DynamicWizardPath;
import com.android.tools.idea.wizard.template.TemplateWizard;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.collect.*;
import com.intellij.ide.projectView.impl.ProjectRootsUtil;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.RecentsManager;
import com.intellij.util.ArrayUtil;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.IdeaSourceProvider;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.android.builder.model.AndroidProject.PROJECT_TYPE_ATOM;
import static com.android.tools.idea.templates.KeystoreUtils.getDebugKeystore;
import static com.android.tools.idea.templates.TemplateMetadata.*;
import static com.android.tools.idea.wizard.WizardConstants.IS_INSTANT_APP_KEY;
import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.Key;
import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.Scope.PATH;
import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.Scope.WIZARD;
import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.createKey;

/**
 * Wizard path for adding a new activity.
 *
 * @deprecated Replaced by {@link RenderTemplateModel} and {@link ConfigureTemplateParametersStep}.
 */
public final class AddAndroidActivityPath extends DynamicWizardPath {
    public static final Key<Boolean> KEY_IS_LAUNCHER = createKey("is.launcher.activity", PATH, Boolean.class);
    public static final Key<TemplateEntry> KEY_SELECTED_TEMPLATE = createKey("selected.template", PATH,
            TemplateEntry.class);
    public static final Key<AndroidVersion> KEY_MIN_SDK = createKey(ATTR_MIN_API, PATH, AndroidVersion.class);
    public static final Key<AndroidVersion> KEY_TARGET_API = createKey(ATTR_TARGET_API, PATH, AndroidVersion.class);
    public static final Key<Integer> KEY_BUILD_SDK = createKey(ATTR_BUILD_API, PATH, Integer.class);
    public static final Key<String> KEY_PACKAGE_NAME = createKey(ATTR_PACKAGE_NAME, PATH, String.class);
    public static final Key<SourceProvider> KEY_SOURCE_PROVIDER = createKey("source.provider", PATH,
            SourceProvider.class);
    public static final Key<String> KEY_SOURCE_PROVIDER_NAME = createKey(ATTR_SOURCE_PROVIDER_NAME, PATH,
            String.class);
    public static final Set<String> PACKAGE_NAME_PARAMETERS = ImmutableSet.of(ATTR_PACKAGE_NAME);
    public static final Set<String> CLASS_NAME_PARAMETERS = ImmutableSet.of(ATTR_PARENT_ACTIVITY_CLASS);
    public static final Key<Boolean> KEY_OPEN_EDITORS = createKey("open.editors", WIZARD, Boolean.class);
    public static final Set<Key<?>> IMPLICIT_PARAMETERS = ImmutableSet.of(KEY_PACKAGE_NAME,
            KEY_SOURCE_PROVIDER_NAME, IS_INSTANT_APP_KEY);

    private static final Logger LOG = Logger.getInstance(AddAndroidActivityPath.class);
    public static final String CUSTOMIZE_ACTIVITY_TITLE = "Customize the Activity";

    private TemplateParameterStep2 myParameterStep;
    private final boolean myIsNewModule;
    private IconStep myAssetStudioStep;
    private final VirtualFile myTargetFolder;
    @Nullable
    private final File myTemplate;
    private final Disposable myParentDisposable;

    /**
     * Creates a new instance of the wizard path.
     */
    public AddAndroidActivityPath(@Nullable VirtualFile targetFolder, @Nullable File template,
            Disposable parentDisposable) {
        myTemplate = template;
        myParentDisposable = parentDisposable;
        myIsNewModule = false;
        myTargetFolder = targetFolder != null && !targetFolder.isDirectory() ? targetFolder.getParent()
                : targetFolder;
    }

    private static FormFactor getFormFactor(
            @Nullable @SuppressWarnings("UnusedParameters") VirtualFile targetFolder) {
        // TODO There should be some way for this wizard to figure out form factor from a target or from a template
        return FormFactor.MOBILE;
    }

    /**
     * Finds and returns the main src directory for the given project or null if one cannot be found.
     */
    @Nullable
    private static File findSrcDirectory(@NotNull SourceProvider sourceProvider) {
        return Iterables.getFirst(sourceProvider.getJavaDirectories(), null);
    }

    @Nullable
    private static File findTestDirectory(@NotNull Module module) {
        List<VirtualFile> testsRoot = ModuleRootManager.getInstance(module)
                .getSourceRoots(JavaModuleSourceRootTypes.TESTS);
        return testsRoot.size() == 0 ? null : VfsUtilCore.virtualToIoFile(testsRoot.get(0));
    }

    /**
     * Finds and returns the main res directory for the given project or null if one cannot be found.
     */
    @Nullable
    private static File findResDirectory(@NotNull SourceProvider sourceProvider) {
        Collection<File> resDirectories = sourceProvider.getResDirectories();
        File resDir = null;
        if (!resDirectories.isEmpty()) {
            resDir = resDirectories.iterator().next();
        }
        return resDir;
    }

    /**
     * Finds and returns the main res directory for the given project or null if one cannot be found.
     */
    @Nullable
    private static File findAidlDir(@NotNull SourceProvider sourceProvider) {
        Collection<File> aidlDirectories = sourceProvider.getAidlDirectories();
        File resDir = null;
        if (!aidlDirectories.isEmpty()) {
            resDir = aidlDirectories.iterator().next();
        }
        return resDir;
    }

    /**
     * Finds and returns the main manifest directory for the given project or null if one cannot be found.
     */
    @Nullable
    private static File findManifestDirectory(@NotNull SourceProvider sourceProvider) {
        File manifestFile = sourceProvider.getManifestFile();
        File manifestDir = manifestFile.getParentFile();
        if (manifestDir != null) {
            return manifestDir;
        }
        return null;
    }

    @Nullable
    private static File getModuleRoot(@Nullable Module module) {
        if (module == null) {
            return null;
        }
        VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots();
        if (roots.length > 0) {
            return VfsUtilCore.virtualToIoFile(roots[0]);
        } else {
            return null;
        }
    }

    private static Map<String, Object> selectSourceProvider(@NotNull SourceProvider sourceProvider,
            @NotNull AndroidModel androidModel, @NotNull Module module, @NotNull String packageName) {
        Map<String, Object> paths = Maps.newHashMap();
        // Look up the resource directories inside this source set
        File moduleDirPath = androidModel.getRootDirPath();
        File javaDir = findSrcDirectory(sourceProvider);
        File testDir = findTestDirectory(module);
        String javaPath = getJavaPath(moduleDirPath, javaDir);
        paths.put(ATTR_SRC_DIR, javaPath);

        File resDir = findResDirectory(sourceProvider);
        if (resDir != null) {
            String resPath = FileUtil.getRelativePath(moduleDirPath, resDir);
            if (resPath != null) {
                resPath = FileUtil.toSystemIndependentName(resPath);
            }
            paths.put(ATTR_RES_DIR, resPath);
            paths.put(ATTR_RES_OUT, FileUtil.toSystemIndependentName(resDir.getPath()));
        }
        File manifestDir = findManifestDirectory(sourceProvider);
        if (manifestDir != null) {
            String manifestPath = FileUtil.getRelativePath(moduleDirPath, manifestDir);
            paths.put(ATTR_MANIFEST_DIR, manifestPath);
            paths.put(ATTR_MANIFEST_OUT, FileUtil.toSystemIndependentName(manifestDir.getPath()));
        }
        File aidlDir = findAidlDir(sourceProvider);
        if (aidlDir != null) {
            String aidlPath = FileUtil.getRelativePath(moduleDirPath, aidlDir);
            paths.put(ATTR_AIDL_DIR, aidlPath);
            paths.put(ATTR_AIDL_OUT, FileUtil.toSystemIndependentName(aidlDir.getPath()));
        }
        if (testDir == null) {
            @SuppressWarnings("deprecation")
            VirtualFile rootDir = androidModel.getRootDir();

            String absolutePath = Joiner.on('/').join(rootDir.getPath(), TemplateWizard.TEST_SOURCE_PATH,
                    TemplateWizard.JAVA_SOURCE_PATH);
            testDir = new File(FileUtil.toSystemDependentName(absolutePath));
        }
        assert javaPath != null;

        String sourceRootPackagePrefix = getSourceDirectoryPackagePrefix(module, javaDir);
        String relativePackageName = removeCommonPackagePrefix(sourceRootPackagePrefix, packageName);

        // Calculate package name
        paths.put(ATTR_PACKAGE_NAME, packageName);
        String relativePackageDir = relativePackageName.replace('.', File.separatorChar);
        File srcOut = new File(javaDir, relativePackageDir);
        File testOut = new File(testDir, relativePackageDir);
        paths.put(ATTR_TEST_DIR, FileUtil.toSystemIndependentName(testDir.getAbsolutePath()));
        paths.put(ATTR_TEST_OUT, FileUtil.toSystemIndependentName(testOut.getAbsolutePath()));
        paths.put(ATTR_APPLICATION_PACKAGE, MergedManifest.get(module).getPackage());
        paths.put(ATTR_SRC_OUT, FileUtil.toSystemIndependentName(srcOut.getAbsolutePath()));
        return paths;
    }

    /**
     * Returns the package prefix of the module's content root if it can be found.
     */
    @NotNull
    private static String getSourceDirectoryPackagePrefix(@NotNull Module module, File javaDir) {
        VirtualFile javaVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(javaDir);
        if (javaVirtualFile == null) {
            return "";
        }
        SourceFolder sourceFolder = ProjectRootsUtil.findSourceFolder(module, javaVirtualFile);
        if (sourceFolder == null) {
            return "";
        }
        return sourceFolder.getPackagePrefix();
    }

    /**
     * Makes the package name relative to a package prefix.
     *
     * Examples:
     * getRelativePackageName("com.google", "com.google.android") -> "android"
     * getRelativePackageName("com.google.android", "com.google.android") -> ""
     * getRelativePackageName("com.google.android", "not.google.android") -> "not.google.android"
     */
    @NotNull
    static String removeCommonPackagePrefix(@NotNull String packagePrefix, @NotNull String packageName) {
        String relativePackageName = packageName;
        if (packageName.equals(packagePrefix)) {
            relativePackageName = "";
        } else if (packageName.length() > packagePrefix.length() && packageName.startsWith(packagePrefix)
                && packageName.charAt(packagePrefix.length()) == '.') {
            relativePackageName = relativePackageName.substring(packagePrefix.length() + 1);
        }
        return relativePackageName;
    }

    @Nullable
    private static String getJavaPath(File ioModuleDir, @Nullable File javaDir) {
        String javaPath = null;
        if (javaDir != null) {
            javaPath = FileUtil.getRelativePath(ioModuleDir, javaDir);
            if (javaPath != null) {
                javaPath = FileUtil.toSystemIndependentName(javaPath);
            }
        }
        return javaPath;
    }

    public static List<String> getParameterValueHistory(@NotNull Parameter parameter, Project project) {
        List<String> entries = RecentsManager.getInstance(project)
                .getRecentEntries(getRecentHistoryKey(parameter.id));
        return entries == null ? ImmutableList.of() : entries;
    }

    public static String getRecentHistoryKey(@Nullable String parameter) {
        return "android.template." + parameter;
    }

    private static void saveRecentValues(@NotNull Project project, @NotNull Map<String, Object> state) {
        for (String id : Iterables.concat(PACKAGE_NAME_PARAMETERS, CLASS_NAME_PARAMETERS)) {
            String value = (String) state.get(id);
            if (!StringUtil.isEmpty(value)) {
                RecentsManager.getInstance(project).registerRecentEntry(getRecentHistoryKey(id), value);
            }
        }
    }

    @Override
    protected void init() {
        Module module = getModule();
        assert module != null;
        AndroidFacet facet = AndroidFacet.getInstance(module);
        assert facet != null;
        AndroidPlatform platform = AndroidPlatform.getInstance(module);

        if (platform != null) {
            myState.put(KEY_BUILD_SDK, platform.getTarget().getVersion().getFeatureLevel());
        }

        AndroidModuleInfo moduleInfo = AndroidModuleInfo.get(facet);
        AndroidVersion minSdkVersion = moduleInfo.getMinSdkVersion();

        myState.put(KEY_MIN_SDK, minSdkVersion);
        myState.put(KEY_TARGET_API, moduleInfo.getTargetSdkVersion());
        myState.put(KEY_PACKAGE_NAME, getInitialPackageName(module, facet));
        myState.put(KEY_OPEN_EDITORS, true);

        if (myTemplate == null) {
            FormFactor formFactor = getFormFactor(myTargetFolder);
            myState.put(FormFactorUtils.getMinApiLevelKey(formFactor), minSdkVersion.getApiLevel());
            myState.put(FormFactorUtils.getBuildApiLevelKey(formFactor),
                    moduleInfo.getTargetSdkVersion().getApiLevel());

            ActivityGalleryStep galleryStep = new ActivityGalleryStep(formFactor, false, KEY_SELECTED_TEMPLATE,
                    module, myParentDisposable);
            addStep(galleryStep);
        } else {
            TemplateMetadata templateMetadata = TemplateManager.getInstance().getTemplateMetadata(myTemplate);
            assert templateMetadata != null;
            myState.put(KEY_SELECTED_TEMPLATE, new TemplateEntry(myTemplate, templateMetadata));
        }
        SourceProvider[] sourceProviders = getSourceProviders(module, myTargetFolder);

        boolean isInstantAppModule = facet.getProjectType() == PROJECT_TYPE_ATOM;
        myState.put(IS_INSTANT_APP_KEY, isInstantAppModule);

        myParameterStep = new TemplateParameterStep2(getFormFactor(myTargetFolder), ImmutableMap.of(),
                myParentDisposable, KEY_PACKAGE_NAME, sourceProviders, CUSTOMIZE_ACTIVITY_TITLE);
        myAssetStudioStep = new IconStep(KEY_SELECTED_TEMPLATE, KEY_SOURCE_PROVIDER, myParentDisposable);

        addStep(myParameterStep);
        addStep(myAssetStudioStep);
    }

    @NotNull
    private static SourceProvider[] getSourceProviders(@Nullable Module module,
            @Nullable VirtualFile targetDirectory) {
        if (module != null) {
            AndroidFacet facet = AndroidFacet.getInstance(module);
            if (facet != null) {
                List<SourceProvider> providers;
                if (targetDirectory != null) {
                    providers = IdeaSourceProvider.getSourceProvidersForFile(facet, targetDirectory,
                            facet.getMainSourceProvider());
                } else {
                    providers = IdeaSourceProvider.getAllSourceProviders(facet);
                }
                return ArrayUtil.toObjectArray(providers, SourceProvider.class);
            }
        }
        return new SourceProvider[0];
    }

    /**
     * Initial package name is either a package user selected when invoking the wizard or default package for the module.
     */
    private String getInitialPackageName(Module module, AndroidFacet facet) {
        if (myTargetFolder != null) {
            List<SourceProvider> sourceProviders = IdeaSourceProvider.getSourceProvidersForFile(facet,
                    myTargetFolder, facet.getMainSourceProvider());
            File targetDirectoryFile = VfsUtilCore.virtualToIoFile(myTargetFolder);
            if (sourceProviders.size() > 0
                    && IdeaSourceProvider.containsFile(sourceProviders.get(0), targetDirectoryFile)) {
                File srcDirectory = findSrcDirectory(sourceProviders.get(0));
                if (srcDirectory != null) {
                    String packageName = ProjectRootManager.getInstance(module.getProject()).getFileIndex()
                            .getPackageNameByDirectory(myTargetFolder);
                    if (packageName != null) {
                        return packageName;
                    }
                }
            }
        }
        return getApplicationPackageName();
    }

    @NotNull
    @Override
    public String getPathName() {
        return "Add Android activity";
    }

    @Override
    public boolean canPerformFinishingActions() {
        return performFinishingOperation(true, null, null);
    }

    @Override
    public boolean performFinishingActions() {
        final Project project = getProject();
        assert project != null;

        final List<File> filesToOpen = Lists.newArrayList();
        final List<File> filesToReformat = Lists.newArrayList();

        boolean success = new WriteCommandAction<Boolean>(project, "New Activity") {
            @Override
            protected void run(@NotNull Result<Boolean> result) throws Throwable {
                boolean success = performFinishingOperation(false, filesToOpen, filesToReformat);
                if (success) {
                    myAssetStudioStep.createAssets();
                }
                result.setResult(success);
            }
        }.execute().getResultObject();
        if (!success) {
            return false;
        }

        ApplicationManager.getApplication().invokeLater(new Runnable() {
            @Override
            public void run() {
                if (Boolean.TRUE.equals(myState.get(KEY_OPEN_EDITORS))) {
                    TemplateUtils.openEditors(project, filesToOpen, true);
                }
            }
        });
        return true;
    }

    private boolean performFinishingOperation(boolean dryRun, @Nullable List<File> filesToOpen,
            @Nullable List<File> filesToReformat) {
        TemplateEntry templateEntry = myState.get(KEY_SELECTED_TEMPLATE);
        final Project project = getProject();
        Module module = getModule();
        assert templateEntry != null;
        assert project != null && module != null;
        final Template template = templateEntry.getTemplate();
        File moduleRoot = getModuleRoot(module);
        if (moduleRoot == null) {
            return false;
        }
        Map<String, Object> parameterMap = getTemplateParameterMap(templateEntry.getMetadata());
        saveRecentValues(project, parameterMap);
        // @formatter:off
        final RenderingContext context = RenderingContext.Builder.newContext(template, project)
                .withCommandName("New Activity").withDryRun(dryRun).withShowErrors(true).withModule(module)
                .withParams(parameterMap).intoOpenFiles(filesToOpen).intoTargetFiles(filesToReformat).build();
        // @formatter:on
        return template.render(context);
    }

    @NotNull
    private Map<String, Object> getTemplateParameterMap(@NotNull TemplateMetadata template) {
        Map<String, Object> parameterValueMap = Maps.newHashMap();
        parameterValueMap.put(ATTR_IS_NEW_PROJECT, myIsNewModule);
        parameterValueMap.putAll(getDirectories());
        for (Key<?> parameter : IMPLICIT_PARAMETERS) {
            parameterValueMap.put(parameter.name, myState.get(parameter));
        }
        for (Parameter parameter : template.getParameters()) {
            parameterValueMap.put(parameter.id, myState.get(myParameterStep.getParameterKey(parameter)));
        }
        try {
            parameterValueMap.put(ATTR_DEBUG_KEYSTORE_SHA1,
                    KeystoreUtils.sha1(KeystoreUtils.getOrCreateDefaultDebugKeystore()));
        } catch (Exception e) {
            LOG.info("Could not compute SHA1 hash of debug keystore.", e);
        }
        File moduleRoot = getModuleRoot(getModule());
        if (moduleRoot != null) {
            parameterValueMap.put(ATTR_PROJECT_OUT, FileUtil.toSystemIndependentName(moduleRoot.getAbsolutePath()));
        }
        if (Objects.equal(getApplicationPackageName(), parameterValueMap.get(ATTR_PACKAGE_NAME))) {
            parameterValueMap.remove(ATTR_APPLICATION_PACKAGE);
        }
        return parameterValueMap;
    }

    private String getApplicationPackageName() {
        //noinspection ConstantConditions
        AndroidModel androidModel = AndroidFacet.getInstance(getModule()).getAndroidModel();
        assert androidModel != null;
        return androidModel.getApplicationId();
    }

    private Map<String, Object> getDirectories() {
        Map<String, Object> templateParameters = Maps.newHashMap();

        Module module = getModule();
        assert module != null;
        AndroidFacet facet = AndroidFacet.getInstance(module);
        assert facet != null;
        AndroidPlatform platform = AndroidPlatform.getInstance(module);

        if (platform != null) {
            templateParameters.put(ATTR_BUILD_API, platform.getTarget().getVersion().getFeatureLevel());
            templateParameters.put(ATTR_BUILD_API_STRING, getBuildApiString(platform.getTarget().getVersion()));
        }
        // Read minSdkVersion and package from manifest and/or build.gradle files
        AndroidModuleInfo moduleInfo = AndroidModuleInfo.get(facet);
        AndroidModel androidModel = facet.getAndroidModel();

        SourceProvider sourceProvider1 = myState.get(KEY_SOURCE_PROVIDER);
        if (sourceProvider1 != null && androidModel != null) {
            String packageName = myState.get(KEY_PACKAGE_NAME);
            assert packageName != null;
            templateParameters.putAll(selectSourceProvider(sourceProvider1, androidModel, module, packageName));
        }
        AndroidVersion minSdkVersion = moduleInfo.getMinSdkVersion();
        String minSdkName = minSdkVersion.getApiString();

        templateParameters.put(ATTR_MIN_API, minSdkName);
        templateParameters.put(ATTR_TARGET_API, moduleInfo.getTargetSdkVersion().getApiLevel());
        templateParameters.put(ATTR_MIN_API_LEVEL, minSdkVersion.getFeatureLevel());

        templateParameters.put(ATTR_IS_LIBRARY_MODULE, facet.isLibraryProject());

        try {
            templateParameters.put(ATTR_DEBUG_KEYSTORE_SHA1, KeystoreUtils.sha1(getDebugKeystore(facet)));
        } catch (Exception e) {
            LOG.info("Could not compute SHA1 hash of debug keystore.", e);
            templateParameters.put(ATTR_DEBUG_KEYSTORE_SHA1, "");
        }

        @SuppressWarnings("deprecation")
        String projectLocation = NewModuleWizardState.ATTR_PROJECT_LOCATION;

        Project project = getProject();
        assert project != null;

        templateParameters.put(projectLocation, project.getBasePath());
        // We're really interested in the directory name on disk, not the module name. These will be different if you give a module the same
        // name as its containing project.
        String moduleName = new File(module.getModuleFilePath()).getParentFile().getName();
        templateParameters.put(FormFactorUtils.ATTR_MODULE_NAME, moduleName);

        return templateParameters;
    }

    public String getActionDescription() {
        TemplateEntry template = myState.get(KEY_SELECTED_TEMPLATE);
        return String.format("Add %1$s", template == null ? "Template" : template.getTitle());
    }
}