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