| /* |
| * 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(); |
| } |
| } |