blob: eb9a5693c273480bd5eff0da801373571aba26ff [file] [log] [blame]
/*
* Copyright (C) 2013 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.wizard;
import com.android.annotations.VisibleForTesting;
import com.android.tools.idea.gradle.project.GradleProjectImporter;
import com.android.tools.idea.gradle.project.NewProjectImportGradleSyncListener;
import com.android.tools.idea.gradle.util.GradleUtil;
import com.android.tools.idea.sdk.VersionCheck;
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.templates.TemplateUtils;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.intellij.ide.startup.StartupManagerEx;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.pom.java.LanguageLevel;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import static com.android.tools.idea.templates.Template.CATEGORY_ACTIVITIES;
import static com.android.tools.idea.templates.TemplateMetadata.ATTR_JAVA_VERSION;
import static icons.AndroidIcons.Wizards.NewProjectSidePanel;
/**
* NewProjectWizard runs the wizard for creating entirely new Android projects. It takes the user
* through steps to configure the project, setting its location and build parameters, and allows
* the user to choose an activity to populate it. The wizard is template-driven, using templates
* that live in the ADK.
*/
public class NewProjectWizard extends TemplateWizard implements TemplateParameterStep.UpdateListener {
private static final Logger LOG = Logger.getInstance("#" + NewProjectWizard.class.getName());
private static final String ERROR_MSG_TITLE = "New Project Wizard";
private static final String UNABLE_TO_CREATE_DIR_FORMAT = "Unable to create directory '%1$s'.";
@VisibleForTesting NewProjectWizardState myWizardState;
@VisibleForTesting AssetStudioAssetGenerator myAssetGenerator;
@VisibleForTesting AssetSetStep myAssetSetStep;
@VisibleForTesting TemplateGalleryStep myChooseActivityStep;
@VisibleForTesting TemplateParameterStep myActivityParameterStep;
@VisibleForTesting ConfigureAndroidModuleStep myConfigureAndroidModuleStep;
@VisibleForTesting boolean myInitializationComplete = false;
public NewProjectWizard() {
super("New Project", null);
Window window = getWindow();
// Allow creation in headless mode for tests
if (window != null) {
getWindow().setMinimumSize(new Dimension(1000, 640));
}
else {
// We should always have a window unless we're in test mode
assert ApplicationManager.getApplication().isUnitTestMode();
}
init();
}
@Override
protected void init() {
if (!AndroidSdkUtils.isAndroidSdkAvailable() || !TemplateManager.templatesAreValid()) {
String title = "SDK problem";
String msg =
"<html>Your Android SDK is missing, out of date, or is missing templates. Please ensure you are using SDK version " +
VersionCheck.MIN_TOOLS_REV + " or later.<br>" +
"You can configure your SDK via <b>Configure | Project Defaults | Project Structure | SDKs</b></html>";
super.init();
Messages.showErrorDialog(msg, title);
throw new IllegalStateException(msg);
}
myWizardState = new NewProjectWizardState();
Template.convertApisToInt(myWizardState.getParameters());
myWizardState.put(TemplateMetadata.ATTR_GRADLE_VERSION, GradleUtil.GRADLE_LATEST_VERSION);
myWizardState.put(TemplateMetadata.ATTR_GRADLE_PLUGIN_VERSION, GradleUtil.GRADLE_PLUGIN_LATEST_VERSION);
myWizardState.put(TemplateMetadata.ATTR_PER_MODULE_REPOS, false);
myConfigureAndroidModuleStep = new ConfigureAndroidModuleStep(myWizardState, myProject, NewProjectSidePanel, this);
myConfigureAndroidModuleStep.updateStep();
myAssetSetStep = new AssetSetStep(myWizardState, myProject, null, NewProjectSidePanel, this, null);
Disposer.register(getDisposable(), myAssetSetStep);
myAssetGenerator = new AssetStudioAssetGenerator(myWizardState);
myAssetSetStep.finalizeAssetType(AssetStudioAssetGenerator.AssetType.LAUNCHER);
myChooseActivityStep =
new TemplateGalleryStep(myWizardState.getActivityTemplateState(), CATEGORY_ACTIVITIES, myProject, null, NewProjectSidePanel, this, null);
myActivityParameterStep = new TemplateParameterStep(myWizardState.getActivityTemplateState(), myProject, null, NewProjectSidePanel, this);
mySteps.add(myConfigureAndroidModuleStep);
mySteps.add(myAssetSetStep);
mySteps.add(myChooseActivityStep);
mySteps.add(myActivityParameterStep);
myInitializationComplete = true;
super.init();
}
@Override
public void update() {
if (!myInitializationComplete) {
return;
}
myAssetSetStep.setVisible(myWizardState.getBoolean(TemplateMetadata.ATTR_CREATE_ICONS));
myChooseActivityStep.setVisible(myWizardState.getBoolean(NewModuleWizardState.ATTR_CREATE_ACTIVITY));
myActivityParameterStep.setVisible(myWizardState.getBoolean(NewModuleWizardState.ATTR_CREATE_ACTIVITY));
super.update();
}
public void createProject() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
createProject(myWizardState, null, myAssetGenerator);
}
});
}
public static void createProject(@NotNull final NewModuleWizardState wizardState, @Nullable Project project,
@Nullable AssetStudioAssetGenerator assetGenerator) {
List<String> errors = Lists.newArrayList();
try {
wizardState.populateDirectoryParameters();
String moduleName = wizardState.getString(NewProjectWizardState.ATTR_MODULE_NAME);
String projectName = wizardState.getString(TemplateMetadata.ATTR_APP_TITLE);
File projectRoot = new File(wizardState.getString(NewModuleWizardState.ATTR_PROJECT_LOCATION));
File moduleRoot = new File(projectRoot, moduleName);
if (FileUtilRt.createDirectory(projectRoot)) {
if (wizardState.getBoolean(TemplateMetadata.ATTR_CREATE_ICONS) && assetGenerator != null) {
assetGenerator.outputImagesIntoDefaultVariant(moduleRoot);
}
wizardState.updateParameters();
wizardState.updateDependencies();
// If this is a new project, instantiate the project-level files
if (wizardState instanceof NewProjectWizardState) {
((NewProjectWizardState)wizardState).myProjectTemplate.render(projectRoot, moduleRoot, wizardState.myParameters);
setGradleWrapperExecutable(projectRoot);
}
wizardState.myTemplate.render(projectRoot, moduleRoot, wizardState.myParameters);
if (wizardState.getBoolean(NewModuleWizardState.ATTR_CREATE_ACTIVITY)) {
TemplateWizardState activityTemplateState = wizardState.getActivityTemplateState();
activityTemplateState.populateRelativePackage(null);
Template template = activityTemplateState.getTemplate();
assert template != null;
template.render(moduleRoot, moduleRoot, activityTemplateState.myParameters);
wizardState.myTemplate.getFilesToOpen().addAll(template.getFilesToOpen());
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
return;
}
GradleProjectImporter projectImporter = GradleProjectImporter.getInstance();
LanguageLevel initialLanguageLevel = null;
Object version = wizardState.hasAttr(ATTR_JAVA_VERSION) ? wizardState.get(ATTR_JAVA_VERSION) : null;
if (version != null) {
initialLanguageLevel = LanguageLevel.parse(version.toString());
}
projectImporter.importProject(projectName, projectRoot, true, new NewProjectImportGradleSyncListener() {
@Override
public void syncEnded(@NotNull final Project project) {
// Open files -- but wait until the Android facets are available, otherwise for example
// the layout editor won't add Design tabs to the file
StartupManagerEx manager = StartupManagerEx.getInstanceEx(project);
if (!manager.postStartupActivityPassed()) {
manager.registerPostStartupActivity(new Runnable() {
@Override
public void run() {
openTemplateFiles(project);
}
});
}
else {
openTemplateFiles(project);
}
}
private boolean openTemplateFiles(Project project) {
return TemplateUtils.openEditors(project, wizardState.myTemplate.getFilesToOpen(), true);
}
}, project, initialLanguageLevel);
} else {
errors.add(String.format(UNABLE_TO_CREATE_DIR_FORMAT, projectRoot.getPath()));
}
}
catch (Exception e) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new RuntimeException(e);
}
Messages.showErrorDialog(e.getMessage(), ERROR_MSG_TITLE);
LOG.error(e);
}
if (!errors.isEmpty()) {
String msg = errors.size() == 1 ? errors.get(0) : Joiner.on('\n').join(errors);
Messages.showErrorDialog(msg, ERROR_MSG_TITLE);
LOG.error(msg);
}
}
public static void setGradleWrapperExecutable(File projectRoot) throws IOException {
if (SystemInfo.isUnix) {
File gradlewFile = new File(projectRoot, "gradlew");
if (!gradlewFile.isFile()) {
LOG.error("Could not find gradle wrapper. Command line builds may not work properly.");
}
else {
FileUtil.setExecutableAttribute(gradlewFile.getPath(), true);
}
}
}
}