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