diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java new file mode 100644 index 000000000..d350a00dd --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.wizards.templates; + +import static org.eclipse.core.resources.IResource.DEPTH_INFINITE; + +import com.android.SdkConstants; +import com.android.annotations.NonNull; +import com.android.annotations.VisibleForTesting; +import com.android.assetstudiolib.GraphicGenerator; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.actions.AddSupportJarAction; +import com.android.ide.eclipse.adt.internal.assetstudio.AssetType; +import com.android.ide.eclipse.adt.internal.assetstudio.ConfigureAssetSetPage; +import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectHelper; +import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator; +import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator.ProjectPopulator; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.CompositeChange; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.IWorkbench; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Wizard for creating new projects + */ +public class NewProjectWizard extends TemplateWizard { + private static final String PARENT_ACTIVITY_CLASS = "parentActivityClass"; //$NON-NLS-1$ + private static final String ACTIVITY_TITLE = "activityTitle"; //$NON-NLS-1$ + static final String IS_LAUNCHER = "isLauncher"; //$NON-NLS-1$ + static final String IS_NEW_PROJECT = "isNewProject"; //$NON-NLS-1$ + static final String IS_LIBRARY_PROJECT = "isLibraryProject"; //$NON-NLS-1$ + static final String ATTR_COPY_ICONS = "copyIcons"; //$NON-NLS-1$ + static final String ATTR_TARGET_API = "targetApi"; //$NON-NLS-1$ + static final String ATTR_MIN_API = "minApi"; //$NON-NLS-1$ + static final String ATTR_MIN_BUILD_API = "minBuildApi"; //$NON-NLS-1$ + static final String ATTR_BUILD_API = "buildApi"; //$NON-NLS-1$ + static final String ATTR_REVISION = "revision"; //$NON-NLS-1$ + static final String ATTR_MIN_API_LEVEL = "minApiLevel"; //$NON-NLS-1$ + static final String ATTR_PACKAGE_NAME = "packageName"; //$NON-NLS-1$ + static final String ATTR_APP_TITLE = "appTitle"; //$NON-NLS-1$ + static final String CATEGORY_PROJECTS = "projects"; //$NON-NLS-1$ + static final String CATEGORY_ACTIVITIES = "activities"; //$NON-NLS-1$ + static final String CATEGORY_OTHER = "other"; //$NON-NLS-1$ + static final String ATTR_APP_COMPAT = "appCompat"; //$NON-NLS-1$ + /** + * Reserved file name for the launcher icon, resolves to the xhdpi version + * + * @see CreateAssetSetWizardState#getImage + */ + public static final String DEFAULT_LAUNCHER_ICON = "launcher_icon"; //$NON-NLS-1$ + + private NewProjectPage mMainPage; + private ProjectContentsPage mContentsPage; + private ActivityPage mActivityPage; + private NewTemplatePage mTemplatePage; + private NewProjectWizardState mValues; + /** The project being created */ + private IProject mProject; + + @Override + public void init(IWorkbench workbench, IStructuredSelection selection) { + super.init(workbench, selection); + + setWindowTitle("New Android Application"); + + mValues = new NewProjectWizardState(); + mMainPage = new NewProjectPage(mValues); + mContentsPage = new ProjectContentsPage(mValues); + mContentsPage.init(selection, AdtUtils.getActivePart()); + mActivityPage = new ActivityPage(mValues, true, true); + mActivityPage.setLauncherActivitiesOnly(true); + } + + @Override + public void addPages() { + super.addPages(); + addPage(mMainPage); + addPage(mContentsPage); + addPage(mActivityPage); + } + + @Override + public IWizardPage getNextPage(IWizardPage page) { + if (page == mMainPage) { + return mContentsPage; + } + + if (page == mContentsPage) { + if (mValues.createIcon) { + // Bundle asset studio wizard to create the launcher icon + CreateAssetSetWizardState iconState = mValues.iconState; + iconState.type = AssetType.LAUNCHER; + iconState.outputName = "ic_launcher"; //$NON-NLS-1$ + iconState.background = new RGB(0xff, 0xff, 0xff); + iconState.foreground = new RGB(0x33, 0xb6, 0xea); + iconState.trim = true; + + // ADT 20: White icon with blue shape + //iconState.shape = GraphicGenerator.Shape.CIRCLE; + //iconState.sourceType = CreateAssetSetWizardState.SourceType.CLIPART; + //iconState.clipartName = "user.png"; //$NON-NLS-1$ + //iconState.padding = 10; + + // ADT 21: Use the platform packaging icon, but allow user to customize it + iconState.sourceType = CreateAssetSetWizardState.SourceType.IMAGE; + iconState.imagePath = new File(DEFAULT_LAUNCHER_ICON); + iconState.shape = GraphicGenerator.Shape.NONE; + iconState.padding = 0; + + WizardPage p = getIconPage(mValues.iconState); + p.setTitle("Configure Launcher Icon"); + return p; + } else { + if (mValues.createActivity) { + return mActivityPage; + } else { + return null; + } + } + } + + if (page == mIconPage) { + return mActivityPage; + } + + if (page == mActivityPage && mValues.createActivity) { + if (mTemplatePage == null) { + NewTemplateWizardState activityValues = mValues.activityValues; + + // Initialize the *default* activity name based on what we've derived + // from the project name + activityValues.defaults.put("activityName", mValues.activityName); + + // Hide those parameters that the template requires but that we don't want to + // ask the users about, since we will supply these values from the rest + // of the new project wizard. + Set<String> hidden = activityValues.hidden; + hidden.add(ATTR_PACKAGE_NAME); + hidden.add(ATTR_APP_TITLE); + hidden.add(ATTR_MIN_API); + hidden.add(ATTR_MIN_API_LEVEL); + hidden.add(ATTR_TARGET_API); + hidden.add(ATTR_BUILD_API); + hidden.add(IS_LAUNCHER); + // Don't ask about hierarchical parent activities in new projects where there + // can't possibly be any + hidden.add(PARENT_ACTIVITY_CLASS); + hidden.add(ACTIVITY_TITLE); // Not used for the first activity in the project + + mTemplatePage = new NewTemplatePage(activityValues, false); + addPage(mTemplatePage); + } + mTemplatePage.setCustomMinSdk(mValues.minSdkLevel, mValues.getBuildApi()); + return mTemplatePage; + } + + if (page == mTemplatePage) { + TemplateMetadata template = mValues.activityValues.getTemplateHandler().getTemplate(); + if (template != null + && !InstallDependencyPage.isInstalled(template.getDependencies())) { + return getDependencyPage(template, true); + } + } + + if (page == mTemplatePage || !mValues.createActivity && page == mActivityPage + || page == getDependencyPage(null, false)) { + return null; + } + + return super.getNextPage(page); + } + + @Override + public boolean canFinish() { + // Deal with lazy creation of some pages: these may not be in the page-list yet + // since they are constructed lazily, so consider that option here. + if (mValues.createIcon && (mIconPage == null || !mIconPage.isPageComplete())) { + return false; + } + if (mValues.createActivity && (mTemplatePage == null || !mTemplatePage.isPageComplete())) { + return false; + } + + // Override super behavior (which just calls isPageComplete() on each of the pages) + // to special case the template and icon pages since we want to skip them if + // the appropriate flags are not set. + for (IWizardPage page : getPages()) { + if (page == mTemplatePage && !mValues.createActivity) { + continue; + } + if (page == mIconPage && !mValues.createIcon) { + continue; + } + if (!page.isPageComplete()) { + return false; + } + } + + return true; + } + + @Override + @NonNull + protected IProject getProject() { + return mProject; + } + + @Override + @NonNull + protected List<String> getFilesToOpen() { + return mValues.template.getFilesToOpen(); + } + + @VisibleForTesting + NewProjectWizardState getValues() { + return mValues; + } + + @VisibleForTesting + void setValues(NewProjectWizardState values) { + mValues = values; + } + + @Override + protected List<Change> computeChanges() { + final TemplateHandler template = mValues.template; + // We'll be merging in an activity template, but don't create *~ backup files + // of the merged files (such as the manifest file) in that case. + // (NOTE: After the change from direct file manipulation to creating a list of Change + // objects, this no longer applies - but the code is kept around a little while longer + // in case we want to generate change objects that makes backups of merged files) + template.setBackupMergedFiles(false); + + // Generate basic output skeleton + Map<String, Object> paramMap = new HashMap<String, Object>(); + addProjectInfo(paramMap); + TemplateHandler.addDirectoryParameters(paramMap, getProject()); + // We don't know at this point whether the activity is going to need + // AppCompat so we just assume that it will. + if (mValues.createActivity && mValues.minSdkLevel < 14) { + paramMap.put(ATTR_APP_COMPAT, true); + getFinalizingActions().add(new Runnable() { + @Override + public void run() { + AddSupportJarAction.installAppCompatLibrary(mProject, true); + } + }); + } + + return template.render(mProject, paramMap); + } + + @Override + protected boolean performFinish(final IProgressMonitor monitor) + throws InvocationTargetException { + try { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + String name = mValues.projectName; + mProject = root.getProject(name); + + final TemplateHandler template = mValues.template; + // We'll be merging in an activity template, but don't create *~ backup files + // of the merged files (such as the manifest file) in that case. + template.setBackupMergedFiles(false); + + ProjectPopulator projectPopulator = new ProjectPopulator() { + @Override + public void populate(IProject project) throws InvocationTargetException { + // Copy in the proguard file; templates don't provide this one. + // add the default proguard config + File libFolder = new File(AdtPlugin.getOsSdkToolsFolder(), + SdkConstants.FD_LIB); + try { + assert project == mProject; + NewProjectCreator.addLocalFile(project, + new File(libFolder, SdkConstants.FN_PROJECT_PROGUARD_FILE), + // Write ProGuard config files with the extension .pro which + // is what is used in the ProGuard documentation and samples + SdkConstants.FN_PROJECT_PROGUARD_FILE, + new NullProgressMonitor()); + } catch (Exception e) { + AdtPlugin.log(e, null); + } + + try { + mProject.refreshLocal(DEPTH_INFINITE, new NullProgressMonitor()); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + + // Render the project template + List<Change> changes = computeChanges(); + if (!changes.isEmpty()) { + monitor.beginTask("Creating project...", changes.size()); + try { + CompositeChange composite = new CompositeChange("", + changes.toArray(new Change[changes.size()])); + composite.perform(monitor); + } catch (CoreException e) { + AdtPlugin.log(e, null); + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + } + + if (mValues.createIcon) { // TODO: Set progress + generateIcons(mProject); + } + + // Render the embedded activity template template + if (mValues.createActivity) { + final TemplateHandler activityTemplate = + mValues.activityValues.getTemplateHandler(); + // We'll be merging in an activity template, but don't create + // *~ backup files of the merged files (such as the manifest file) + // in that case. + activityTemplate.setBackupMergedFiles(false); + generateActivity(template, project, monitor); + } + } + }; + + NewProjectCreator.create(monitor, mProject, mValues.target, projectPopulator, + mValues.isLibrary, mValues.projectLocation, mValues.workingSets); + + // For new projects, ensure that we're actually using the preferred compliance, + // not just the default one + IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject); + if (javaProject != null) { + ProjectHelper.enforcePreferredCompilerCompliance(javaProject); + } + + try { + mProject.refreshLocal(DEPTH_INFINITE, new NullProgressMonitor()); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + + List<Runnable> finalizingTasks = getFinalizingActions(); + for (Runnable r : finalizingTasks) { + r.run(); + } + + return true; + } catch (Exception ioe) { + AdtPlugin.log(ioe, null); + return false; + } + } + + /** + * Generate custom icons into the project based on the asset studio wizard state + */ + private void generateIcons(final IProject newProject) { + // Generate the custom icons + assert mValues.createIcon; + ConfigureAssetSetPage.generateIcons(newProject, mValues.iconState, false, mIconPage); + } + + /** + * Generate the activity: Pre-populate information about the project the + * activity needs but that we don't need to ask about when creating a new + * project + */ + private void generateActivity(TemplateHandler projectTemplate, IProject project, + IProgressMonitor monitor) throws InvocationTargetException { + assert mValues.createActivity; + NewTemplateWizardState activityValues = mValues.activityValues; + Map<String, Object> parameters = activityValues.parameters; + + addProjectInfo(parameters); + + parameters.put(IS_NEW_PROJECT, true); + parameters.put(IS_LIBRARY_PROJECT, mValues.isLibrary); + // Ensure that activities created as part of a new project are marked as + // launcher activities + parameters.put(IS_LAUNCHER, true); + TemplateHandler.addDirectoryParameters(parameters, project); + + TemplateHandler activityTemplate = activityValues.getTemplateHandler(); + activityTemplate.setBackupMergedFiles(false); + List<Change> changes = activityTemplate.render(project, parameters); + if (!changes.isEmpty()) { + monitor.beginTask("Creating template...", changes.size()); + try { + CompositeChange composite = new CompositeChange("", + changes.toArray(new Change[changes.size()])); + composite.perform(monitor); + } catch (CoreException e) { + AdtPlugin.log(e, null); + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + } + + List<String> filesToOpen = activityTemplate.getFilesToOpen(); + projectTemplate.getFilesToOpen().addAll(filesToOpen); + + List<Runnable> finalizingActions = activityTemplate.getFinalizingActions(); + projectTemplate.getFinalizingActions().addAll(finalizingActions); + } + + private void addProjectInfo(Map<String, Object> parameters) { + parameters.put(ATTR_PACKAGE_NAME, mValues.packageName); + parameters.put(ATTR_APP_TITLE, mValues.applicationName); + parameters.put(ATTR_MIN_API, mValues.minSdk); + parameters.put(ATTR_MIN_API_LEVEL, mValues.minSdkLevel); + parameters.put(ATTR_TARGET_API, mValues.targetSdkLevel); + parameters.put(ATTR_BUILD_API, mValues.target.getVersion().getApiLevel()); + parameters.put(ATTR_COPY_ICONS, !mValues.createIcon); + parameters.putAll(mValues.parameters); + } + + @Override + @NonNull + protected List<Runnable> getFinalizingActions() { + return mValues.template.getFinalizingActions(); + } +} |