• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.wizards.newproject;
18 
19 import com.android.SdkConstants;
20 import com.android.annotations.Nullable;
21 import com.android.ide.common.xml.ManifestData;
22 import com.android.ide.common.xml.ManifestData.Activity;
23 import com.android.ide.eclipse.adt.AdtConstants;
24 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
25 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
26 import com.android.sdklib.IAndroidTarget;
27 import com.android.sdklib.internal.project.ProjectProperties;
28 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
29 import com.android.utils.Pair;
30 import com.android.xml.AndroidManifest;
31 
32 import org.eclipse.core.resources.IProject;
33 import org.eclipse.core.runtime.Path;
34 import org.eclipse.core.runtime.Platform;
35 import org.eclipse.ui.IWorkingSet;
36 
37 import java.io.File;
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 /**
42  * The {@link NewProjectWizardState} holds the state used by the various pages
43  * in the {@link NewProjectWizard} and its variations, and it can also be used
44  * to pass project information to the {@link NewProjectCreator}.
45  */
46 public class NewProjectWizardState {
47     /** The mode to run the wizard in: creating test, or sample, or plain project */
48     public Mode mode;
49 
50     /**
51      * If true, the project should be created from an existing codebase (pointed
52      * to by the {@link #projectLocation} or in the case of sample projects, the
53      * {@link #chosenSample}. Otherwise, create a brand new project from scratch.
54      */
55     public boolean useExisting;
56 
57     /**
58      * Whether new projects should be created into the default project location
59      * (e.g. in the Eclipse workspace) or not
60      */
61     public boolean useDefaultLocation = true;
62 
63     /** The build target SDK */
64     public IAndroidTarget target;
65     /** True if the user has manually modified the target */
66     public boolean targetModifiedByUser;
67 
68     /** The location to store projects into */
69     public File projectLocation = new File(Platform.getLocation().toOSString());
70     /** True if the project location name has been manually edited by the user */
71     public boolean projectLocationModifiedByUser;
72 
73     /** The name of the project */
74     public String projectName = ""; //$NON-NLS-1$
75     /** True if the project name has been manually edited by the user */
76     public boolean projectNameModifiedByUser;
77 
78     /** The application name */
79     public String applicationName;
80     /** True if the application name has been manually edited by the user */
81     public boolean applicationNameModifiedByUser;
82 
83     /** The package path */
84     public String packageName;
85     /** True if the package name has been manually edited by the user */
86     public boolean packageNameModifiedByUser;
87 
88     /** True if a new activity should be created */
89     public boolean createActivity;
90 
91     /** The name of the new activity to be created */
92     public String activityName;
93     /** True if the activity name has been manually edited by the user */
94     public boolean activityNameModifiedByUser;
95 
96     /** The minimum SDK version to use with the project (may be null or blank) */
97     public String minSdk;
98     /** True if the minimum SDK version has been manually edited by the user */
99     public boolean minSdkModifiedByUser;
100     /**
101      * A list of paths to each of the available samples for the current SDK.
102      * The pair is (String: sample display name => File: sample directory).
103      * Note we want a list, not a map since we might have duplicates.
104      * */
105     public List<Pair<String, File>> samples = new ArrayList<Pair<String, File>>();
106     /** Path to the currently chosen sample */
107     public File chosenSample;
108 
109     /** The name of the source folder, relative to the project root */
110     public String sourceFolder = SdkConstants.FD_SOURCES;
111     /** The set of chosen working sets to use when creating the project */
112     public IWorkingSet[] workingSets = new IWorkingSet[0];
113 
114     /**
115      * A reference to a different project that the current test project will be
116      * testing.
117      */
118     public IProject testedProject;
119     /**
120      * If true, this test project should be testing itself, otherwise it will be
121      * testing the project pointed to by {@link #testedProject}.
122      */
123     public boolean testingSelf;
124 
125     // NOTE: These apply only to creating paired projects; when isTest is true
126     // we're using
127     // the normal fields above
128     /**
129      * If true, create a test project along with this plain project which will
130      * be testing the plain project. (This flag only applies when creating
131      * normal projects.)
132      */
133     public boolean createPairProject;
134     /**
135      * The application name of the test application (only applies when
136      * {@link #createPairProject} is true)
137      */
138     public String testApplicationName;
139     /**
140      * True if the testing application name has been modified by the user (only
141      * applies when {@link #createPairProject} is true)
142      */
143     public boolean testApplicationNameModified;
144     /**
145      * The package name of the test application (only applies when
146      * {@link #createPairProject} is true)
147      */
148     public String testPackageName;
149     /**
150      * True if the testing package name has been modified by the user (only
151      * applies when {@link #createPairProject} is true)
152      */
153     public boolean testPackageModified;
154     /**
155      * The project name of the test project (only applies when
156      * {@link #createPairProject} is true)
157      */
158     public String testProjectName;
159     /**
160      * True if the testing project name has been modified by the user (only
161      * applies when {@link #createPairProject} is true)
162      */
163     public boolean testProjectModified;
164     /** Package name of the tested app */
165     public String testTargetPackageName;
166 
167     /**
168      * Copy project into workspace? This flag only applies when importing
169      * projects (creating projects from existing source)
170      */
171     public boolean copyIntoWorkspace;
172 
173     /**
174      * List of projects to be imported. Null if not importing projects.
175      */
176     @Nullable
177     public List<ImportedProject> importProjects;
178 
179     /**
180      * Creates a new {@link NewProjectWizardState}
181      *
182      * @param mode the mode to run the wizard in
183      */
NewProjectWizardState(Mode mode)184     public NewProjectWizardState(Mode mode) {
185         this.mode = mode;
186         if (mode == Mode.SAMPLE) {
187             useExisting = true;
188         } else if (mode == Mode.TEST) {
189             createActivity = false;
190         }
191     }
192 
193     /**
194      * Extract information (package name, application name, minimum SDK etc) from
195      * the given Android project.
196      *
197      * @param path the path to the project to extract information from
198      */
extractFromAndroidManifest(Path path)199     public void extractFromAndroidManifest(Path path) {
200         String osPath = path.append(SdkConstants.FN_ANDROID_MANIFEST_XML).toOSString();
201         if (!(new File(osPath).exists())) {
202             return;
203         }
204 
205         ManifestData manifestData = AndroidManifestHelper.parseForData(osPath);
206         if (manifestData == null) {
207             return;
208         }
209 
210         String newPackageName = null;
211         Activity activity = null;
212         String newActivityName = null;
213         String minSdkVersion = null;
214         try {
215             newPackageName = manifestData.getPackage();
216             minSdkVersion = manifestData.getMinSdkVersionString();
217 
218             // try to get the first launcher activity. If none, just take the first activity.
219             activity = manifestData.getLauncherActivity();
220             if (activity == null) {
221                 Activity[] activities = manifestData.getActivities();
222                 if (activities != null && activities.length > 0) {
223                     activity = activities[0];
224                 }
225             }
226         } catch (Exception e) {
227             // ignore exceptions
228         }
229 
230         if (newPackageName != null && newPackageName.length() > 0) {
231             packageName = newPackageName;
232         }
233 
234         if (activity != null) {
235             newActivityName = AndroidManifest.extractActivityName(activity.getName(),
236                     newPackageName);
237         }
238 
239         if (newActivityName != null && newActivityName.length() > 0) {
240             activityName = newActivityName;
241             // we are "importing" an existing activity, not creating a new one
242             createActivity = false;
243 
244             // If project name and application names are empty, use the activity
245             // name as a default. If the activity name has dots, it's a part of a
246             // package specification and only the last identifier must be used.
247             if (newActivityName.indexOf('.') != -1) {
248                 String[] ids = newActivityName.split(AdtConstants.RE_DOT);
249                 newActivityName = ids[ids.length - 1];
250             }
251             if (projectName == null || projectName.length() == 0 ||
252                     !projectNameModifiedByUser) {
253                 projectName = newActivityName;
254                 projectNameModifiedByUser = false;
255             }
256             if (applicationName == null || applicationName.length() == 0 ||
257                     !applicationNameModifiedByUser) {
258                 applicationNameModifiedByUser = false;
259                 applicationName = newActivityName;
260             }
261         } else {
262             activityName = ""; //$NON-NLS-1$
263 
264             // There is no activity name to use to fill in the project and application
265             // name. However if there's a package name, we can use this as a base.
266             if (newPackageName != null && newPackageName.length() > 0) {
267                 // Package name is a java identifier, so it's most suitable for
268                 // an application name.
269 
270                 if (applicationName == null || applicationName.length() == 0 ||
271                         !applicationNameModifiedByUser) {
272                     applicationName = newPackageName;
273                 }
274 
275                 // For the project name, remove any dots
276                 newPackageName = newPackageName.replace('.', '_');
277                 if (projectName == null || projectName.length() == 0 ||
278                         !projectNameModifiedByUser) {
279                     projectName = newPackageName;
280                 }
281 
282             }
283         }
284 
285         if (mode == Mode.ANY && useExisting) {
286             updateSdkTargetToMatchProject(path.toFile());
287         }
288 
289         minSdk = minSdkVersion;
290         minSdkModifiedByUser = false;
291     }
292 
293     /**
294      * Try to find an SDK Target that matches the current MinSdkVersion.
295      *
296      * There can be multiple targets with the same sdk api version, so don't change
297      * it if it's already at the right version. Otherwise pick the first target
298      * that matches.
299      */
updateSdkTargetToMatchMinSdkVersion()300     public void updateSdkTargetToMatchMinSdkVersion() {
301         IAndroidTarget currentTarget = target;
302         if (currentTarget != null && currentTarget.getVersion().equals(minSdk)) {
303             return;
304         }
305 
306         Sdk sdk = Sdk.getCurrent();
307         if (sdk != null) {
308             IAndroidTarget[] targets = sdk.getTargets();
309             for (IAndroidTarget t : targets) {
310                 if (t.getVersion().equals(minSdk)) {
311                     target = t;
312                     return;
313                 }
314             }
315         }
316     }
317 
318     /**
319      * Updates the SDK to reflect the SDK required by the project at the given
320      * location
321      *
322      * @param location the location of the project
323      */
updateSdkTargetToMatchProject(File location)324     public void updateSdkTargetToMatchProject(File location) {
325         // Select the target matching the manifest's sdk or build properties, if any
326         IAndroidTarget foundTarget = null;
327         // This is the target currently in the UI
328         IAndroidTarget currentTarget = target;
329         String projectPath = location.getPath();
330 
331         // If there's a current target defined, we do not allow to change it when
332         // operating in the create-from-sample mode -- since the available sample list
333         // is tied to the current target, so changing it would invalidate the project we're
334         // trying to load in the first place.
335         if (!targetModifiedByUser) {
336             ProjectProperties p = ProjectProperties.load(projectPath,
337                     PropertyType.PROJECT);
338             if (p != null) {
339                 String v = p.getProperty(ProjectProperties.PROPERTY_TARGET);
340                 IAndroidTarget desiredTarget = Sdk.getCurrent().getTargetFromHashString(v);
341                 // We can change the current target if:
342                 // - we found a new desired target
343                 // - there is no current target
344                 // - or the current target can't run the desired target
345                 if (desiredTarget != null &&
346                         (currentTarget == null || !desiredTarget.canRunOn(currentTarget))) {
347                     foundTarget = desiredTarget;
348                 }
349             }
350 
351             Sdk sdk = Sdk.getCurrent();
352             IAndroidTarget[] targets = null;
353             if (sdk != null) {
354                 targets = sdk.getTargets();
355             }
356             if (targets == null) {
357                 targets = new IAndroidTarget[0];
358             }
359 
360             if (foundTarget == null && minSdk != null) {
361                 // Otherwise try to match the requested min-sdk-version if we find an
362                 // exact match, regardless of the currently selected target.
363                 for (IAndroidTarget existingTarget : targets) {
364                     if (existingTarget != null &&
365                             existingTarget.getVersion().equals(minSdk)) {
366                         foundTarget = existingTarget;
367                         break;
368                     }
369                 }
370             }
371 
372             if (foundTarget == null) {
373                 // Or last attempt, try to match a sample project location and use it
374                 // if we find an exact match, regardless of the currently selected target.
375                 for (IAndroidTarget existingTarget : targets) {
376                     if (existingTarget != null &&
377                             projectPath.startsWith(existingTarget.getLocation())) {
378                         foundTarget = existingTarget;
379                         break;
380                     }
381                 }
382             }
383         }
384 
385         if (foundTarget != null) {
386             target = foundTarget;
387         }
388     }
389 
390     /**
391      * Type of project being offered/created by the wizard
392      */
393     public enum Mode {
394         /** Create a sample project. Testing options are not presented. */
395         SAMPLE,
396 
397         /**
398          * Create a test project, either testing itself or some other project.
399          * Note that even if in the {@link #ANY} mode, a test project can be
400          * created as a *paired* project with the main project, so this flag
401          * only means that we are creating *just* a test project
402          */
403         TEST,
404 
405         /**
406          * Create an Android project, which can be a plain project, optionally
407          * with a paired test project, or a sample project (the first page
408          * contains toggles for choosing which
409          */
410         ANY;
411     }
412 }
413