Home | History | Annotate | Download | only in newproject
      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      */
    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      */
    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      */
    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      */
    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 }