| /* |
| * 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.tools.idea.templates.Parameter; |
| import com.android.tools.idea.templates.Template; |
| import com.android.tools.idea.templates.TemplateManager; |
| import com.android.tools.idea.templates.TemplateMetadata; |
| import com.android.tools.idea.wizard.*; |
| import com.android.tools.idea.wizard.dynamic.DynamicWizardPath; |
| import com.android.tools.idea.wizard.template.TemplateWizard; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.util.io.FileUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| |
| import static com.android.tools.idea.templates.TemplateMetadata.*; |
| import static com.android.tools.idea.templates.TemplateUtils.checkedCreateDirectoryIfMissing; |
| import static com.android.tools.idea.npw.AddAndroidActivityPath.KEY_SELECTED_TEMPLATE; |
| import static com.android.tools.idea.npw.ConfigureFormFactorStep.NUM_ENABLED_FORM_FACTORS_KEY; |
| import static com.android.tools.idea.npw.NewModuleWizardState.ATTR_CREATE_ACTIVITY; |
| import static com.android.tools.idea.wizard.WizardConstants.*; |
| 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.createKey; |
| |
| /** |
| * Module creation for a given form factor |
| */ |
| public class NewFormFactorModulePath extends DynamicWizardPath { |
| private static final Logger LOG = Logger.getInstance(NewFormFactorModulePath.class); |
| private static final Key<Boolean> CREATE_ACTIVITY_KEY = createKey(ATTR_CREATE_ACTIVITY, PATH, Boolean.class); |
| |
| private static final Key<String> MODULE_LOCATION_KEY = createKey(ATTR_PROJECT_OUT, PATH, String.class); |
| private static final Key<String> RES_DIR_KEY = createKey(ATTR_RES_DIR, PATH, String.class); |
| private static final Key<String> SRC_DIR_KEY = createKey(ATTR_SRC_DIR, PATH, String.class); |
| private static final Key<String> AIDL_DIR_KEY = createKey(ATTR_AIDL_DIR, PATH, String.class); |
| private static final Key<String> MANIFEST_DIR_KEY = createKey(ATTR_MANIFEST_DIR, PATH, String.class); |
| private static final Key<String> TEST_DIR_KEY = createKey(ATTR_TEST_DIR, PATH, String.class); |
| |
| private static final Key<String> RES_OUT_KEY = createKey(ATTR_RES_OUT, PATH, String.class); |
| private static final Key<String> SRC_OUT_KEY = createKey(ATTR_SRC_OUT, PATH, String.class); |
| private static final Key<String> AIDL_OUT_KEY = createKey(ATTR_AIDL_OUT, PATH, String.class); |
| private static final Key<String> MANIFEST_OUT_KEY = createKey(ATTR_MANIFEST_OUT, PATH, String.class); |
| private static final Key<String> TEST_OUT_KEY = createKey(ATTR_TEST_OUT, PATH, String.class); |
| |
| private static final Key<String> RELATIVE_PACKAGE_KEY = createKey(ATTR_RELATIVE_PACKAGE, PATH, String.class); |
| private static final String RELATIVE_SRC_ROOT = FileUtil.join(TemplateWizard.MAIN_FLAVOR_SOURCE_PATH, TemplateWizard.JAVA_SOURCE_PATH); |
| private static final String RELATIVE_TEST_ROOT = FileUtil.join(TemplateWizard.TEST_SOURCE_PATH, TemplateWizard.JAVA_SOURCE_PATH); |
| |
| private FormFactorUtils.FormFactor myFormFactor; |
| private File myTemplateFile; |
| private Disposable myDisposable; |
| private final Key<Boolean> myIsIncludedKey; |
| private final Key<String> myModuleNameKey; |
| private TemplateParameterStep2 myParameterStep; |
| private List<File> myFilesToOpen = Lists.newArrayList(); |
| private String myDefaultModuleName = null; |
| private boolean myGradleSyncIfNecessary = true; |
| |
| public static List<NewFormFactorModulePath> getAvailableFormFactorModulePaths(@NotNull Disposable disposable) { |
| TemplateManager manager = TemplateManager.getInstance(); |
| List<File> applicationTemplates = manager.getTemplatesInCategory(Template.CATEGORY_APPLICATION); |
| List<NewFormFactorModulePath> toReturn = Lists.newArrayList(); |
| for (File templateFile : applicationTemplates) { |
| TemplateMetadata metadata = manager.getTemplate(templateFile); |
| if (metadata == null || metadata.getFormFactor() == null) { |
| continue; |
| } |
| FormFactorUtils.FormFactor formFactor = FormFactorUtils.FormFactor.get(metadata.getFormFactor()); |
| if (formFactor == null) { |
| continue; |
| } |
| NewFormFactorModulePath path = new NewFormFactorModulePath(formFactor, templateFile, disposable); |
| toReturn.add(path); |
| } |
| Collections.sort(toReturn, new Comparator<NewFormFactorModulePath>() { |
| @Override |
| public int compare(NewFormFactorModulePath p1, NewFormFactorModulePath p2) { |
| return p1.myFormFactor.compareTo(p2.myFormFactor); |
| } |
| }); |
| return toReturn; |
| } |
| |
| public NewFormFactorModulePath(@NotNull FormFactorUtils.FormFactor formFactor, @NotNull File templateFile, @NotNull Disposable disposable) { |
| myFormFactor = formFactor; |
| myTemplateFile = templateFile; |
| myDisposable = disposable; |
| myIsIncludedKey = FormFactorUtils.getInclusionKey(formFactor); |
| myModuleNameKey = FormFactorUtils.getModuleNameKey(formFactor); |
| } |
| |
| @Override |
| protected void init() { |
| myState.put(WizardConstants.IS_LIBRARY_KEY, false); |
| myState.put(SRC_DIR_KEY, calculateSrcDir()); |
| myState.put(RES_DIR_KEY, "src/main/res"); |
| myState.put(AIDL_DIR_KEY, "src/main/aidl"); |
| myState.put(MANIFEST_DIR_KEY, "src/main"); |
| myState.put(TEST_DIR_KEY, "src/androidTest"); |
| myState.put(CREATE_ACTIVITY_KEY, false); |
| myState.put(RELATIVE_PACKAGE_KEY, ""); |
| |
| addStep(new ConfigureAndroidModuleStepDynamic(myDisposable)); |
| addStep(new ActivityGalleryStep(myFormFactor, true, KEY_SELECTED_TEMPLATE, myDisposable)); |
| |
| Object packageName = myState.get(PACKAGE_NAME_KEY); |
| if (packageName == null) { |
| packageName = ""; |
| } |
| Map<String, Object> presetsMap = ImmutableMap |
| .of(PACKAGE_NAME_KEY.name, packageName, TemplateMetadata.ATTR_IS_LAUNCHER, true, TemplateMetadata.ATTR_PARENT_ACTIVITY_CLASS, ""); |
| myParameterStep = new TemplateParameterStep2(myFormFactor, presetsMap, myDisposable, PACKAGE_NAME_KEY, new SourceProvider[0]); |
| addStep(myParameterStep); |
| } |
| |
| @Override |
| public void onPathStarted(boolean fromBeginning) { |
| super.onPathStarted(fromBeginning); |
| updatePackageDerivedValues(); |
| } |
| |
| void updatePackageDerivedValues() { |
| // TODO: Refactor handling of presets in TemplateParameterStep2 so that this isn't necessary |
| myParameterStep.setPresetValue(PACKAGE_NAME_KEY.name, myState.get(PACKAGE_NAME_KEY)); |
| |
| Set<Key> keys = Sets.newHashSetWithExpectedSize(5); |
| keys.add(PACKAGE_NAME_KEY); |
| keys.add(SRC_DIR_KEY); |
| keys.add(TEST_DIR_KEY); |
| keys.add(PROJECT_LOCATION_KEY); |
| keys.add(NUM_ENABLED_FORM_FACTORS_KEY); |
| deriveValues(keys); |
| } |
| |
| @NotNull |
| private String calculateSrcDir() { |
| String packageSegment = myState.get(PACKAGE_NAME_KEY); |
| if (packageSegment == null) { |
| packageSegment = ""; |
| } else { |
| packageSegment = packageSegment.replace('.', File.separatorChar); |
| } |
| return FileUtil.join(RELATIVE_SRC_ROOT, packageSegment); |
| } |
| |
| @NotNull |
| private String calculateTestDir() { |
| String packageSegment = myState.get(PACKAGE_NAME_KEY); |
| if (packageSegment == null) { |
| packageSegment = ""; |
| } else { |
| packageSegment = packageSegment.replace('.', File.separatorChar); |
| } |
| return FileUtil.join(RELATIVE_TEST_ROOT, packageSegment); |
| } |
| |
| @Override |
| public void deriveValues(Set<Key> modified) { |
| if (modified.contains(NUM_ENABLED_FORM_FACTORS_KEY)) { |
| String moduleName = updateDefaultModuleName(myState.getNotNull(NUM_ENABLED_FORM_FACTORS_KEY, 0), myState.get(myModuleNameKey)); |
| myState.put(myModuleNameKey, moduleName); |
| } |
| boolean basePathModified = modified.contains(PROJECT_LOCATION_KEY) || modified.contains(myModuleNameKey); |
| if (basePathModified) { |
| myState.put(MODULE_LOCATION_KEY, FileUtil.join(myState.get(PROJECT_LOCATION_KEY), myState.get(myModuleNameKey))); |
| } |
| if (modified.contains(SRC_DIR_KEY) || modified.contains(PACKAGE_NAME_KEY)) { |
| myState.put(SRC_DIR_KEY, calculateSrcDir()); |
| } |
| if (modified.contains(TEST_DIR_KEY) || modified.contains(PACKAGE_NAME_KEY)) { |
| myState.put(TEST_DIR_KEY, calculateTestDir()); |
| } |
| if (modified.contains(SRC_DIR_KEY) || basePathModified) { |
| updateOutputPath(SRC_DIR_KEY, SRC_OUT_KEY); |
| } |
| if (modified.contains(RES_DIR_KEY) || basePathModified) { |
| updateOutputPath(RES_DIR_KEY, RES_OUT_KEY); |
| } |
| if (modified.contains(AIDL_DIR_KEY) || basePathModified) { |
| updateOutputPath(AIDL_DIR_KEY, AIDL_OUT_KEY); |
| } |
| if (modified.contains(MANIFEST_DIR_KEY) || basePathModified) { |
| updateOutputPath(MANIFEST_DIR_KEY, MANIFEST_OUT_KEY); |
| } |
| if (modified.contains(TEST_DIR_KEY) || basePathModified) { |
| updateOutputPath(TEST_DIR_KEY, TEST_OUT_KEY); |
| } |
| if (myState.containsKey(NEWLY_INSTALLED_API_KEY)) { |
| Integer newApiLevel = myState.get(NEWLY_INSTALLED_API_KEY); |
| assert newApiLevel != null; |
| Key<Integer> targetApiLevelKey = FormFactorUtils.getTargetApiLevelKey(myFormFactor); |
| Integer currentTargetLevel = myState.get(targetApiLevelKey); |
| if (currentTargetLevel == null || newApiLevel > currentTargetLevel) { |
| // If the newly installed is greater than the current target, we know we're not targeting |
| // a preview version, so we can safely set build/target api levels to the newly installed level |
| String newApiString = Integer.toString(newApiLevel); |
| myState.put(targetApiLevelKey, newApiLevel); |
| myState.put(FormFactorUtils.getTargetApiStringKey(myFormFactor), newApiString); |
| myState.put(FormFactorUtils.getBuildApiLevelKey(myFormFactor), newApiLevel); |
| myState.put(FormFactorUtils.getBuildApiKey(myFormFactor), newApiString); |
| } |
| } |
| } |
| |
| @NotNull |
| private String updateDefaultModuleName(int enabledFormfactorsCount, @Nullable String currentModuleName) { |
| if (currentModuleName == null || currentModuleName.equals(myDefaultModuleName)) { |
| myDefaultModuleName = enabledFormfactorsCount == 1 ? "app" : FormFactorUtils.getModuleName(myFormFactor); |
| return myDefaultModuleName; |
| } |
| else { |
| return currentModuleName; |
| } |
| } |
| |
| private void updateOutputPath(@NotNull Key<String> relativeDirKey, @NotNull Key<String> outputDirKey) { |
| String projectLocation = myState.get(PROJECT_LOCATION_KEY); |
| String moduleName = myState.get(myModuleNameKey); |
| String relativeLocation = myState.get(relativeDirKey); |
| if (relativeLocation == null || projectLocation == null || moduleName == null) { |
| return; |
| } |
| File baseLocation = new File(projectLocation, moduleName); |
| relativeLocation = FileUtil.toSystemDependentName(relativeLocation); |
| myState.put(outputDirKey, new File(baseLocation, relativeLocation).getPath()); |
| } |
| |
| @NotNull |
| @Override |
| public String getPathName() { |
| return myFormFactor + " Module Creation Path"; |
| } |
| |
| @Override |
| public boolean isPathVisible() { |
| Boolean included = myState.get(myIsIncludedKey); |
| return included == null ? false : included; |
| } |
| |
| @Override |
| public boolean performFinishingActions() { |
| ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); |
| if (progress != null) { |
| progress.setText("Initializing " + myFormFactor.toString()); |
| } |
| String projectLocation = myState.get(PROJECT_LOCATION_KEY); |
| if (projectLocation != null) { |
| File projectRoot = new File(projectLocation); |
| File moduleRoot = new File(projectRoot, myState.get(myModuleNameKey)); |
| try { |
| checkedCreateDirectoryIfMissing(moduleRoot); |
| } |
| catch (IOException e) { |
| LOG.error(e); |
| return false; |
| } |
| |
| Template template = Template.createFromPath(myTemplateFile); |
| Map<String, Object> templateState = FormFactorUtils.scrubFormFactorPrefixes(myFormFactor, myState.flatten()); |
| template.render(projectRoot, moduleRoot, templateState, myWizard.getProject(), myGradleSyncIfNecessary); |
| TemplateEntry templateEntry = myState.get(KEY_SELECTED_TEMPLATE); |
| if (templateEntry == null) { |
| return true; |
| } |
| Template activityTemplate = templateEntry.getTemplate(); |
| for (Parameter parameter : templateEntry.getMetadata().getParameters()) { |
| templateState.put(parameter.id, myState.get(myParameterStep.getParameterKey(parameter))); |
| } |
| activityTemplate.render(projectRoot, moduleRoot, templateState, myWizard.getProject(), myGradleSyncIfNecessary); |
| |
| // If the parent wizard supports opening files in the editor upon completion, do that |
| List<File> filesToOpen = myState.get(FILES_TO_OPEN_KEY); |
| if (filesToOpen != null) { |
| filesToOpen.addAll(activityTemplate.getFilesToOpen()); |
| } |
| |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| protected void setGradleSyncIfNecessary(boolean value) { |
| myGradleSyncIfNecessary = value; |
| } |
| } |