Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
      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 package com.motorola.studio.android.model;
     17 
     18 import java.io.File;
     19 import java.util.ArrayList;
     20 import java.util.List;
     21 import java.util.regex.Matcher;
     22 import java.util.regex.Pattern;
     23 
     24 import org.eclipse.core.resources.IProject;
     25 import org.eclipse.core.resources.IResource;
     26 import org.eclipse.core.resources.IWorkspace;
     27 import org.eclipse.core.resources.ResourcesPlugin;
     28 import org.eclipse.core.runtime.IProgressMonitor;
     29 import org.eclipse.core.runtime.IStatus;
     30 import org.eclipse.core.runtime.Path;
     31 import org.eclipse.core.runtime.Platform;
     32 import org.eclipse.core.runtime.Status;
     33 import org.eclipse.jdt.core.JavaConventions;
     34 import org.eclipse.jface.wizard.IWizardContainer;
     35 import org.eclipse.osgi.util.NLS;
     36 
     37 import com.android.sdklib.IAndroidTarget;
     38 import com.motorola.studio.android.AndroidPlugin;
     39 import com.motorola.studio.android.adt.Sample;
     40 import com.motorola.studio.android.adt.SdkUtils;
     41 import com.motorola.studio.android.common.IAndroidConstants;
     42 import com.motorola.studio.android.common.exception.AndroidException;
     43 import com.motorola.studio.android.common.utilities.AndroidStatus;
     44 import com.motorola.studio.android.common.utilities.EclipseUtils;
     45 import com.motorola.studio.android.i18n.AndroidNLS;
     46 
     47 /**
     48  * Class that implements the model for the new project wizard
     49  */
     50 public class AndroidProject implements IWizardModel
     51 {
     52     private static final IWorkspace WORKSPACE = ResourcesPlugin.getWorkspace();
     53 
     54     private static final String SDK_VERSION = "1.5"; //$NON-NLS-1$
     55 
     56     private static final String BIN_FOLDER = "bin"; //$NON-NLS-1$
     57 
     58     private static final String CLASS_EXTENSION = ".class"; //$NON-NLS-1$
     59 
     60     private static final String R_DRAWABLE_CLASS = "R$drawable" + CLASS_EXTENSION; //$NON-NLS-1$
     61 
     62     private static final String SETTINGS_FOLDER = ".settings"; //$NON-NLS-1$
     63 
     64     private static final String SETTINGS_FILE = "org.eclipse.core.resources.prefs"; //$NON-NLS-1$
     65 
     66     public static final String ANDROID_NATURE = "com.android.ide.eclipse.adt.AndroidNature"; //$NON-NLS-1$
     67 
     68     private static final int MAX_PATH_LENGTH = 255;
     69 
     70     /**
     71      * Represents the Type of new Projects
     72      */
     73     public static enum SourceTypes
     74     {
     75         SAMPLE, EXISTING, NEW, WIDGET
     76     };
     77 
     78     /*
     79      * (non-Javadoc)
     80      *
     81      * @see java.lang.Object#finalize()
     82      */
     83     @Override
     84     public void finalize() throws Throwable
     85     {
     86         if (listener != null)
     87         {
     88             AndroidPlugin.getDefault().removeSDKLoaderListener(listener);
     89         }
     90         super.finalize();
     91     }
     92 
     93     private SourceTypes sourceType = SourceTypes.NEW;
     94 
     95     private Sample sample = null;
     96 
     97     private String location = null;
     98 
     99     private boolean useDefaultLocation = true;
    100 
    101     private boolean addNativeSupport = false;
    102 
    103     private boolean needToObfuscate = false;
    104 
    105     /**
    106      * Return the Type of the new Project
    107      *
    108      * @return
    109      */
    110     public SourceTypes getSourceType()
    111     {
    112         return sourceType;
    113     }
    114 
    115     /**
    116      * Change the Type of the new Project
    117      *
    118      * @param sourceType
    119      */
    120     public void setSourceType(SourceTypes sourceType)
    121     {
    122         this.sourceType = sourceType;
    123     }
    124 
    125     /**
    126      * Check if adding native support
    127      *
    128      * @return
    129      */
    130     public boolean isAddingNativeSupport()
    131     {
    132         return addNativeSupport;
    133     }
    134 
    135     /**
    136      * Set add native support property
    137      *
    138      * @param hasNativeSupport
    139      */
    140     public void setAddNativeSupport(boolean addNativeSupport)
    141     {
    142         this.addNativeSupport = addNativeSupport;
    143     }
    144 
    145     public void setNeedToObfuscate(boolean needToObfuscate)
    146     {
    147         this.needToObfuscate = needToObfuscate;
    148     }
    149 
    150     public boolean needToObfuscate()
    151     {
    152         return needToObfuscate;
    153     }
    154 
    155     /**
    156      * Returns the Project location.
    157      *
    158      * @see #isUsingDefaultLocation()
    159      * @return
    160      */
    161     public String getLocation()
    162     {
    163         return location;
    164     }
    165 
    166     /**
    167      * Return the sample to use
    168      *
    169      * @see #getSourceType()
    170      * @return
    171      */
    172     public Sample getSample()
    173     {
    174         return sample;
    175     }
    176 
    177     /**
    178      * Change the used sample
    179      *
    180      * @see #getSourceType()
    181      * @param sample
    182      */
    183     public void setSample(Sample sample)
    184     {
    185         this.sample = sample;
    186     }
    187 
    188     /**
    189      * Change the Project Location
    190      *
    191      * @param location
    192      */
    193     public void setLocation(String location)
    194     {
    195         this.location = location;
    196     }
    197 
    198     /**
    199      * Check if using the default location
    200      *
    201      * @return
    202      */
    203     public boolean isUsingDefaultLocation()
    204     {
    205         return useDefaultLocation;
    206     }
    207 
    208     /**
    209      * Set use default location property
    210      *
    211      * @param useDefaultLocation
    212      */
    213     public void setUseDefaultLocation(boolean useDefaultLocation)
    214     {
    215         this.useDefaultLocation = useDefaultLocation;
    216     }
    217 
    218     private String minSdkVersion = null;
    219 
    220     private String name = ""; //$NON-NLS-1$
    221 
    222     private String activityName = ""; //$NON-NLS-1$
    223 
    224     private String packageName = ""; //$NON-NLS-1$
    225 
    226     private IAndroidTarget sdkTarget = null;
    227 
    228     private List<String> sourceFolders = null;
    229 
    230     private boolean usingDefaultPackage = true;
    231 
    232     private final String PACKAGE_ROOT = "com."; //$NON-NLS-1$
    233 
    234     private Runnable listener = null;
    235 
    236     private String appName;
    237 
    238     /**
    239      * Check if using the defaul package name
    240      *
    241      * @return
    242      */
    243     public boolean isUsingDefaultPackage()
    244     {
    245         return usingDefaultPackage;
    246     }
    247 
    248     /**
    249      * Set if using the Default package name
    250      *
    251      * @param usingDefaultPackage
    252      */
    253     public void setUsingDefaultPackage(boolean usingDefaultPackage)
    254     {
    255         this.usingDefaultPackage = usingDefaultPackage;
    256     }
    257 
    258     /**
    259      * Creates a new AndroidProject
    260      */
    261     public AndroidProject()
    262     {
    263         // initialize SDK Targets
    264         if (SdkUtils.getCurrentSdk() == null)
    265         {
    266             // this listener will be called when a SDK is configured
    267             listener = new Runnable()
    268             {
    269 
    270                 public void run()
    271                 {
    272                     IAndroidTarget[] targets = SdkUtils.getAllTargets();
    273                     if ((targets != null) && (targets.length > 0))
    274                     {
    275                         int maxApiVersion = -1;
    276                         for (IAndroidTarget aTarget : targets)
    277                         {
    278                             int apiVersion = aTarget.getVersion().getApiLevel();
    279                             if (maxApiVersion < apiVersion)
    280                             {
    281                                 sdkTarget = aTarget;
    282                                 maxApiVersion = apiVersion;
    283                             }
    284                         }
    285                         // set the API string as the min SDK version - this is the way ADT does
    286                         minSdkVersion = sdkTarget.getVersion().getApiString();
    287                     }
    288                     AndroidPlugin.getDefault().removeSDKLoaderListener(listener);
    289                     listener = null;
    290                 }
    291 
    292             };
    293             AndroidPlugin.getDefault().addSDKLoaderListener(listener);
    294         }
    295         else
    296         {
    297             IAndroidTarget[] targets = SdkUtils.getAllTargets();
    298             if ((targets != null) && (targets.length > 0))
    299             {
    300                 int maxApiVersion = -1;
    301                 for (IAndroidTarget aTarget : targets)
    302                 {
    303                     int apiVersion = aTarget.getVersion().getApiLevel();
    304                     if (maxApiVersion < apiVersion)
    305                     {
    306                         sdkTarget = aTarget;
    307                         maxApiVersion = apiVersion;
    308                     }
    309                 }
    310                 // set the API string as the min SDK version - this is the way ADT does
    311                 minSdkVersion = sdkTarget.getVersion().getApiString();
    312             }
    313         }
    314 
    315         sourceFolders = new ArrayList(3);
    316         sourceFolders.add(IAndroidConstants.FD_SOURCES);
    317         sourceFolders.add(IAndroidConstants.FD_GEN_SOURCES);
    318     }
    319 
    320     /**
    321      * Return the Default Package Name.
    322      *
    323      * @return
    324      */
    325     private String getDefaultPackageName()
    326     {
    327         return PACKAGE_ROOT + name2package(name);
    328     }
    329 
    330     /**
    331      * Return the default name for activities.
    332      *
    333      * @return
    334      */
    335     public String getDefaultActivityName()
    336     {
    337         String activityName = name;
    338         activityName = activityName.replaceAll("^[0-9]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
    339         activityName = activityName.replaceAll("[\\.]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
    340         return activityName;
    341     }
    342 
    343     /**
    344      * Returns a valid package name from the Project Name
    345      *
    346      * @param name
    347      * @return
    348      */
    349     protected String name2package(String name)
    350     {
    351         String packageName = getDefaultActivityName().toLowerCase();
    352         packageName = packageName.replaceAll("[^A-Za-z0-9_]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
    353         if (packageName.endsWith("_") && (packageName.length() > 1)) //$NON-NLS-1$
    354         {
    355             packageName = packageName.substring(0, packageName.length() - 1);
    356         }
    357         return packageName;
    358     }
    359 
    360     /**
    361      * Return the Minimum SDK Version
    362      *
    363      * @return
    364      */
    365     public String getMinSdkVersion()
    366     {
    367         return minSdkVersion;
    368     }
    369 
    370     /**
    371      * Returns <code>true</code> in case the SDK is a preview,
    372      * <code>false</code> otherwise.
    373      *
    374      * @return Returns <code>true</code> in case the SDK is a preview,
    375      * <code>false</code> otherwise.
    376      */
    377     public boolean isSdkPreview()
    378     {
    379         return getSdkTarget().getVersion().isPreview();
    380     }
    381 
    382     /**
    383      * Return the project Name
    384      *
    385      * @return
    386      */
    387     public String getName()
    388     {
    389         return name;
    390     }
    391 
    392     /**
    393      * Return the package name. Or the Default package name, if using it.
    394      *
    395      * @return
    396      */
    397     public String getPackageName()
    398     {
    399         String packageName;
    400         if (isUsingDefaultPackage())
    401         {
    402             packageName = getDefaultPackageName();
    403         }
    404         else
    405         {
    406             packageName = this.packageName;
    407         }
    408         return packageName;
    409     }
    410 
    411     /**
    412      * Return the SDK target used by this project.
    413      *
    414      * @return
    415      */
    416     public IAndroidTarget getSdkTarget()
    417     {
    418         return sdkTarget;
    419     }
    420 
    421     /**
    422      * Return the Source Folder to be created in the project.
    423      *
    424      * @return
    425      */
    426     public List<String> getSourceFolders()
    427     {
    428         return sourceFolders;
    429     }
    430 
    431     /*
    432      * (non-Javadoc)
    433      *
    434      * @see com.motorola.studio.android.model.IWizardModel#getStatus()
    435      */
    436     public IStatus getStatus()
    437     {
    438         IStatus status = Status.OK_STATUS;
    439         // Validating Project Name
    440         if ((name == null) || (name.length() == 0))
    441         {
    442             status =
    443                     new AndroidStatus(IStatus.ERROR,
    444                             AndroidNLS.ERR_AndroidProject_ProjectNameMustBeSpecified);
    445         }
    446         else
    447         {
    448             Pattern pattern = Pattern.compile("([A-Za-z0-9_\\.])+"); //$NON-NLS-1$
    449             Matcher matcher = pattern.matcher(name);
    450 
    451             if (!matcher.matches())
    452             {
    453                 String errMsg = NLS.bind(AndroidNLS.ERR_AndroidProject_InvalidProjectName, name);
    454                 status = new AndroidStatus(IStatus.ERROR, errMsg);
    455             }
    456             else if (WORKSPACE.getRoot().getProject(name).exists())
    457             {
    458                 status =
    459                         new AndroidStatus(IStatus.ERROR,
    460                                 AndroidNLS.ERR_AndroidProject_DuplicatedProjectNameInWorkspace);
    461             }
    462             else
    463             {
    464                 status = WORKSPACE.validateName(name, IResource.PROJECT);
    465             }
    466         }
    467 
    468         if (status.isOK() && (sourceType == SourceTypes.EXISTING))
    469         {
    470 
    471             // Check first if the path is empty. If true, we show a info
    472             // message to the user
    473             if (getLocation().length() == 0)
    474             {
    475                 status =
    476                         new AndroidStatus(IStatus.INFO,
    477                                 AndroidNLS.ERR_AndroidProject_EmptySourceLocation);
    478             }
    479             else
    480             {
    481                 // Check if the manifest exists
    482                 Path path = new Path(getLocation());
    483                 String osPath = path.append(IAndroidConstants.FN_ANDROID_MANIFEST).toOSString();
    484                 File manifest = new File(osPath);
    485                 if (!manifest.exists() || !manifest.isFile())
    486                 {
    487                     String errMsg =
    488                             NLS.bind(AndroidNLS.ERR_AndroidProject_FileNotFoundError,
    489                                     IAndroidConstants.FN_ANDROID_MANIFEST, path.lastSegment());
    490                     status = new AndroidStatus(IStatus.ERROR, errMsg);
    491                 }
    492             }
    493         }
    494 
    495         // Validating Project Location
    496         if (status.isOK() && !isUsingDefaultLocation())
    497         {
    498             IProject handle = WORKSPACE.getRoot().getProject(name);
    499             status = WORKSPACE.validateProjectLocation(handle, new Path(location));
    500             if (status.isOK() && isNewProject() && (getLocation() != null))
    501             {
    502                 boolean projectLocationIsEmpty =
    503                         ProjectCreationSupport.validateNewProjectLocationIsEmpty(new Path(
    504                                 getLocation()));
    505                 if (!projectLocationIsEmpty)
    506                 {
    507                     status =
    508                             new AndroidStatus(IStatus.ERROR,
    509                                     AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolder);
    510                 }
    511             }
    512         }
    513 
    514         // Validating Project Location when adding Native Support on Windows
    515         if (status.isOK() && isAddingNativeSupport() && Platform.getOS().equals(Platform.OS_WIN32))
    516         {
    517             if ((isUsingDefaultLocation() && ResourcesPlugin.getWorkspace().getRoot().getLocation()
    518                     .toOSString().contains(" ")) //$NON-NLS-1$
    519                     || (!isUsingDefaultLocation() && location.contains(" "))) //$NON-NLS-1$
    520             {
    521 
    522                 status =
    523                         new AndroidStatus(IStatus.ERROR,
    524                                 AndroidNLS.ERR_AndroidProject_LocationContainsWhitespaces);
    525             }
    526 
    527         }
    528 
    529         // Validating if the project could create a "file name too long"
    530         // structure
    531         if (status.isOK())
    532         {
    533             String basePath = isUsingDefaultLocation() ? getDefaultLocation() : getLocation();
    534             String relativePackagePath = getPackageName().replace('.', File.separatorChar);
    535             String binPath =
    536                     basePath + File.separatorChar + BIN_FOLDER + File.separatorChar
    537                             + relativePackagePath;
    538 
    539             String rDrawableBinClass = binPath + File.separatorChar + R_DRAWABLE_CLASS;
    540 
    541             String settingsFilePath =
    542                     basePath + File.separatorChar + SETTINGS_FOLDER + File.separatorChar
    543                             + SETTINGS_FILE;
    544 
    545             String defaultClassBin =
    546                     binPath + File.separator + getDefaultActivityName() + CLASS_EXTENSION;
    547 
    548             int maxFilenameLength = getMax(new int[]
    549             {
    550                     rDrawableBinClass.length(), settingsFilePath.length(), defaultClassBin.length()
    551             });
    552 
    553             if (maxFilenameLength > MAX_PATH_LENGTH)
    554             {
    555                 status =
    556                         new AndroidStatus(IStatus.ERROR,
    557                                 AndroidNLS.ERR_AndroidProject_ProjectNameTooLong);
    558             }
    559         }
    560 
    561         // Validating SDK Target
    562         if (status.isOK() && (sdkTarget == null))
    563         {
    564             status =
    565                     new AndroidStatus(IStatus.ERROR,
    566                             AndroidNLS.ERR_AndroidProject_ASdkTargetMustBeSpecified);
    567         }
    568 
    569         // Validates the application name
    570         if (status.isOK()
    571                 && ((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET)))
    572         {
    573             if (appName.trim().length() == 0)
    574             {
    575                 status =
    576                         new AndroidStatus(IStatus.WARNING,
    577                                 AndroidNLS.WARN_AndroidProject_ApplicationNameIsEmpty);
    578             }
    579             if (appName.contains("&")) //$NON-NLS-1$
    580             {
    581                 status =
    582                         new AndroidStatus(IStatus.ERROR, NLS.bind(
    583                                 AndroidNLS.ERR_AndroidProject_InvalidApplicationName, appName));
    584             }
    585 
    586         }
    587 
    588         // Validating PackageName
    589         if (status.isOK()
    590                 && ((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET))
    591                 && !isUsingDefaultPackage())
    592         {
    593             if (getPackageName().length() == 0)
    594             {
    595                 status =
    596                         new AndroidStatus(IStatus.ERROR,
    597                                 AndroidNLS.ERR_AndroidProject_EmptyPackageName);
    598             }
    599             else if (status.isOK())
    600             {
    601                 status =
    602                         JavaConventions.validatePackageName(getPackageName(), SDK_VERSION,
    603                                 SDK_VERSION);
    604             }
    605 
    606             if (status.isOK() && (getPackageName().indexOf('.') == -1))
    607             {
    608                 // The Android Activity Manager does not accept packages names
    609                 // with only one
    610                 // identifier. Check the package name has at least one dot in
    611                 // them (the previous rule
    612                 // validated that if such a dot exist, it's not the first nor
    613                 // last characters of the
    614                 // string.)
    615                 status =
    616                         new AndroidStatus(IStatus.ERROR,
    617                                 AndroidNLS.ERR_AndroidProject_InvalidPackageName);
    618             }
    619 
    620             if (status.isOK())
    621             {
    622                 Pattern pattern = Pattern.compile("[A-Za-z0-9_\\.]+"); //$NON-NLS-1$
    623                 Matcher matcher = pattern.matcher(getPackageName());
    624 
    625                 if (!matcher.matches())
    626                 {
    627                     String errMsg =
    628                             NLS.bind(AndroidNLS.ERR_AndroidProject_InvalidCharsInPackageName,
    629                                     getPackageName());
    630 
    631                     status = new AndroidStatus(IStatus.ERROR, errMsg);
    632                 }
    633             }
    634         }
    635 
    636         // validating activity name
    637         if ((this.getSourceType() == SourceTypes.NEW)
    638                 || (this.getSourceType() == SourceTypes.WIDGET))
    639         {
    640             if ((activityName == null) || (activityName.length() == 0))
    641             {
    642                 status =
    643                         new AndroidStatus(IStatus.ERROR,
    644                                 AndroidNLS.ERR_AndroidProject_ActivityNameMustBeSpecified);
    645             }
    646             else
    647             {
    648                 String onlyChars = "[A-Za-z_]"; //$NON-NLS-1$
    649                 Pattern pattern = Pattern.compile("(" + onlyChars + ")(\\w)+"); //$NON-NLS-1$ //$NON-NLS-2$
    650                 Matcher matcher = pattern.matcher(activityName);
    651 
    652                 if (!matcher.matches())
    653                 {
    654                     String errMsg =
    655                             NLS.bind(AndroidNLS.ERR_AndroidProject_InvalidActivityName,
    656                                     activityName);
    657                     status = new AndroidStatus(IStatus.ERROR, errMsg);
    658                 }
    659             }
    660         }
    661 
    662         // Validating Min Sdk Version
    663         if (status.isOK()
    664                 && ((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET)))
    665         {
    666             // validate in case the sdk is preview
    667             if (isSdkPreview())
    668             {
    669                 String sdkAPI = getSdkTarget().getVersion().getApiString();
    670                 if (!sdkAPI.equals(getMinSdkVersion()))
    671                 {
    672                     status =
    673                             new AndroidStatus(IStatus.ERROR, NLS.bind(
    674                                     AndroidNLS.AndroidProject_MsgSDKVersionIsPreview, sdkAPI));
    675                 }
    676             }
    677             // since it is not a preview, validate it normally
    678             else
    679             {
    680                 int version = -1;
    681                 try
    682                 {
    683                     // If not empty, it must be a valid integer > 0
    684                     version = Integer.parseInt(getMinSdkVersion());
    685                 }
    686                 catch (NumberFormatException nfe)
    687                 {
    688                     status =
    689                             new AndroidStatus(IStatus.ERROR,
    690                                     AndroidNLS.ERR_AndroidProject_InvalidSdkVersion);
    691                 }
    692 
    693                 if (status.isOK() && (version < 1))
    694                 {
    695                     status =
    696                             new AndroidStatus(IStatus.ERROR,
    697                                     AndroidNLS.ERR_AndroidProject_InvalidSdkVersion);
    698                 }
    699 
    700                 if (status.isOK() && (getSdkTarget() != null))
    701                 {
    702                     if (getSdkTarget().getVersion().getApiLevel() > version)
    703                     {
    704                         status =
    705                                 new AndroidStatus(IStatus.WARNING,
    706                                         AndroidNLS.ERR_AndroidProject_InvalidApiLevel);
    707                     }
    708                     else if (getSdkTarget().getVersion().getApiLevel() < version)
    709                     {
    710                         status =
    711                                 new AndroidStatus(IStatus.ERROR,
    712                                         AndroidNLS.EXC_AndroidProject_InvalidMinimumSdkVersion);
    713                     }
    714                 }
    715             }
    716         }
    717 
    718         //validate if there are available samples
    719         if (status.isOK() && (sourceType == SourceTypes.SAMPLE) && (sdkTarget != null)
    720                 && (SdkUtils.getSamples(sdkTarget).length == 0))
    721         {
    722             status =
    723                     new AndroidStatus(IStatus.ERROR,
    724                             AndroidNLS.EXC_AndroidProject_NoSamplesAvailable);
    725         }
    726 
    727         // Validating Project Location when adding Obfuscation Support
    728         if (status.isOK() && needToObfuscate())
    729         {
    730             if ((isUsingDefaultLocation() && ResourcesPlugin.getWorkspace().getRoot().getLocation()
    731                     .toOSString().contains(" ")) //$NON-NLS-1$
    732                     || (!isUsingDefaultLocation() && location.contains(" "))) //$NON-NLS-1$
    733             {
    734 
    735                 status =
    736                         new AndroidStatus(IStatus.WARNING,
    737                                 AndroidNLS.WRN_Obfuscation_ProjectLocationContainWhitespaces);
    738             }
    739         }
    740 
    741         return status;
    742     }
    743 
    744     /*
    745      * (non-Javadoc)
    746      *
    747      * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation()
    748      */
    749     public boolean needMoreInformation()
    750     {
    751         boolean needMoreInformation =
    752                 (name == null) || (name.length() == 0)
    753                         || !WORKSPACE.validateName(name, IResource.PROJECT).isOK();
    754 
    755         if (!needMoreInformation)
    756         {
    757             boolean projectPathInformation =
    758                     (location == null)
    759                             || ((location.length() == 0) || !WORKSPACE.validateProjectLocation(
    760                                     WORKSPACE.getRoot().getProject(name), new Path(location))
    761                                     .isOK());
    762             boolean newProjectInformation = !useDefaultLocation && projectPathInformation;
    763             switch (sourceType)
    764             {
    765                 case NEW:
    766                     needMoreInformation = newProjectInformation;
    767                     break;
    768                 case SAMPLE:
    769                     needMoreInformation = newProjectInformation || (sample == null);
    770                     break;
    771                 case EXISTING:
    772                     needMoreInformation = projectPathInformation;
    773                     break;
    774                 case WIDGET:
    775                     needMoreInformation = newProjectInformation;
    776             }
    777         }
    778 
    779         if (!needMoreInformation)
    780         {
    781             if (((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET))
    782                     && !isUsingDefaultPackage())
    783             {
    784                 needMoreInformation =
    785                         (getPackageName().length() == 0)
    786                                 || !JavaConventions.validatePackageName(getPackageName(),
    787                                         SDK_VERSION, SDK_VERSION).isOK()
    788                                 || (getPackageName().indexOf('.') == -1);
    789             }
    790         }
    791 
    792         if (!needMoreInformation)
    793         {
    794             needMoreInformation = sdkTarget == null;
    795         }
    796         return needMoreInformation;
    797     }
    798 
    799     /* (non-Javadoc)
    800      * @see com.motorola.studio.android.model.IWizardModel#save(org.eclipse.jface.wizard.IWizardContainer, org.eclipse.core.runtime.IProgressMonitor)
    801      */
    802     public boolean save(IWizardContainer container, IProgressMonitor monitor)
    803     {
    804         try
    805         {
    806             return ProjectCreationSupport.createProject(this, container);
    807         }
    808         catch (AndroidException e)
    809         {
    810             IStatus status =
    811                     new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getLocalizedMessage());
    812 
    813             EclipseUtils.showErrorDialog(AndroidNLS.GEN_Error,
    814                     AndroidNLS.EXC_AndroidProject_AnErrorHasOccurredWhenCreatingTheProject, status);
    815             return false;
    816         }
    817     }
    818 
    819     /**
    820      * Change the SDK
    821      *
    822      * @param minSdkVersion
    823      */
    824     public void setMinSdkVersion(String minSdkVersion)
    825     {
    826         this.minSdkVersion = minSdkVersion;
    827     }
    828 
    829     /**
    830      * Change the project name
    831      *
    832      * @param name
    833      */
    834     public void setName(String name)
    835     {
    836         this.name = name.trim();
    837     }
    838 
    839     /**
    840      * Change the package name. If using default package name will throw an
    841      * {@link IllegalStateException}
    842      *
    843      * @param packageName
    844      */
    845     public void setPackageName(String packageName)
    846     {
    847         if (isUsingDefaultPackage())
    848         {
    849             throw new IllegalStateException();
    850         }
    851         this.packageName = packageName.trim();
    852     }
    853 
    854     /**
    855      * Change the SDK Target
    856      *
    857      * @param sdkTarget
    858      */
    859     public void setSdkTarget(IAndroidTarget sdkTarget)
    860     {
    861         this.sdkTarget = sdkTarget;
    862     }
    863 
    864     /**
    865      * Change the Source Folder
    866      *
    867      * @param sourceFolder
    868      */
    869     public void setSourceFolder(List<String> sourceFolders)
    870     {
    871         this.sourceFolders.clear();
    872         this.sourceFolders.addAll(sourceFolders);
    873     }
    874 
    875     /**
    876      * Return the Default location for this project
    877      *
    878      * @return
    879      */
    880     public String getDefaultLocation()
    881     {
    882         return ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString() + File.separator
    883                 + name;
    884     }
    885 
    886     /**
    887      * Check if this project is a new project.
    888      *
    889      * @return
    890      */
    891     public boolean isNewProject()
    892     {
    893         return getSourceType() != SourceTypes.EXISTING;
    894     }
    895 
    896     /**
    897      * Set the name of the activity being created with the project
    898      * This attribute is only valid for New Projects.
    899      * Other kind of project will ignore this attribute.
    900      */
    901     public void setActivityName(String activityName)
    902     {
    903         this.activityName = activityName;
    904     }
    905 
    906     /**
    907      * Return this project default activity name
    908      * @return an Activity name as string
    909      */
    910     public String getActivityName()
    911     {
    912         return this.activityName;
    913     }
    914 
    915     /**
    916      * Retrieves the maximum value from an int array
    917      *
    918      * @param values
    919      *            the int array
    920      *
    921      * @return the maximum value from the int array
    922      */
    923     private int getMax(int[] values)
    924     {
    925         int max = values[0];
    926 
    927         for (int i = 1; i < values.length; i++)
    928         {
    929             max = Math.max(max, values[i]);
    930         }
    931 
    932         return max;
    933     }
    934 
    935     /**
    936      * Set the application name to be used
    937      * The app name will be used only when the project is new
    938      * @param app_name
    939      */
    940     public void setApplicationName(String app_name)
    941     {
    942         this.appName = app_name;
    943     }
    944 
    945     /**
    946      * get the user defined application name
    947      * @return
    948      */
    949     public String getApplicationName()
    950     {
    951         return this.appName;
    952     }
    953 }
    954