aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
blob: d350a00dda9807c36bd1831b2f9195fbf7afaed6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
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();
    }
}