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 package com.android.ide.eclipse.adt.internal.wizards.newproject;
     17 
     18 import com.android.SdkConstants;
     19 import com.android.ide.eclipse.adt.AdtConstants;
     20 import com.android.ide.eclipse.adt.AdtPlugin;
     21 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     22 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
     23 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
     24 import com.android.sdklib.IAndroidTarget;
     25 
     26 import org.eclipse.core.filesystem.URIUtil;
     27 import org.eclipse.core.resources.IProject;
     28 import org.eclipse.core.resources.IWorkspace;
     29 import org.eclipse.core.resources.ResourcesPlugin;
     30 import org.eclipse.core.runtime.IPath;
     31 import org.eclipse.core.runtime.IStatus;
     32 import org.eclipse.core.runtime.Path;
     33 import org.eclipse.core.runtime.Status;
     34 import org.eclipse.jdt.core.JavaConventions;
     35 import org.eclipse.jface.dialogs.IMessageProvider;
     36 import org.eclipse.jface.wizard.WizardPage;
     37 import org.eclipse.swt.SWT;
     38 import org.eclipse.swt.events.ModifyEvent;
     39 import org.eclipse.swt.events.ModifyListener;
     40 import org.eclipse.swt.events.SelectionEvent;
     41 import org.eclipse.swt.events.SelectionListener;
     42 import org.eclipse.swt.layout.GridData;
     43 import org.eclipse.swt.layout.GridLayout;
     44 import org.eclipse.swt.widgets.Button;
     45 import org.eclipse.swt.widgets.Combo;
     46 import org.eclipse.swt.widgets.Composite;
     47 import org.eclipse.swt.widgets.Label;
     48 import org.eclipse.swt.widgets.Text;
     49 
     50 import java.io.File;
     51 import java.io.FileFilter;
     52 import java.net.URI;
     53 
     54 /** Page where you choose the application name, activity name, and optional test project info */
     55 public class ApplicationInfoPage extends WizardPage implements SelectionListener, ModifyListener,
     56         ITargetChangeListener {
     57     private static final String JDK_15 = "1.5"; //$NON-NLS-1$
     58     private final static String DUMMY_PACKAGE = "your.package.namespace";
     59 
     60     /** Suffix added by default to activity names */
     61     static final String ACTIVITY_NAME_SUFFIX = "Activity"; //$NON-NLS-1$
     62 
     63     private final NewProjectWizardState mValues;
     64 
     65     private Text mApplicationText;
     66     private Text mPackageText;
     67     private Text mActivityText;
     68     private Button mCreateActivityCheckbox;
     69     private Combo mSdkCombo;
     70 
     71     private boolean mIgnore;
     72     private Button mCreateTestCheckbox;
     73     private Text mTestProjectNameText;
     74     private Text mTestApplicationText;
     75     private Text mTestPackageText;
     76     private Label mTestProjectNameLabel;
     77     private Label mTestApplicationLabel;
     78     private Label mTestPackageLabel;
     79 
     80     /**
     81      * Create the wizard.
     82      */
     83     ApplicationInfoPage(NewProjectWizardState values) {
     84         super("appInfo"); //$NON-NLS-1$
     85         mValues = values;
     86 
     87         setTitle("Application Info");
     88         setDescription("Configure the new Android Project");
     89         AdtPlugin.getDefault().addTargetListener(this);
     90     }
     91 
     92     /**
     93      * Create contents of the wizard.
     94      */
     95     @Override
     96     @SuppressWarnings("unused") // Eclipse marks SWT constructors with side effects as unused
     97     public void createControl(Composite parent) {
     98         Composite container = new Composite(parent, SWT.NULL);
     99         container.setLayout(new GridLayout(2, false));
    100 
    101         Label applicationLabel = new Label(container, SWT.NONE);
    102         applicationLabel.setText("Application Name:");
    103 
    104         mApplicationText = new Text(container, SWT.BORDER);
    105         mApplicationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    106         mApplicationText.addModifyListener(this);
    107 
    108         Label packageLabel = new Label(container, SWT.NONE);
    109         packageLabel.setText("Package Name:");
    110 
    111         mPackageText = new Text(container, SWT.BORDER);
    112         mPackageText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    113         mPackageText.addModifyListener(this);
    114 
    115         if (mValues.mode != Mode.TEST) {
    116             mCreateActivityCheckbox = new Button(container, SWT.CHECK);
    117             mCreateActivityCheckbox.setText("Create Activity:");
    118             mCreateActivityCheckbox.addSelectionListener(this);
    119 
    120             mActivityText = new Text(container, SWT.BORDER);
    121             mActivityText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    122             mActivityText.addModifyListener(this);
    123         }
    124 
    125         Label minSdkLabel = new Label(container, SWT.NONE);
    126         minSdkLabel.setText("Minimum SDK:");
    127 
    128         mSdkCombo = new Combo(container, SWT.NONE);
    129         GridData gdSdkCombo = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
    130         gdSdkCombo.widthHint = 200;
    131         mSdkCombo.setLayoutData(gdSdkCombo);
    132         mSdkCombo.addSelectionListener(this);
    133         mSdkCombo.addModifyListener(this);
    134 
    135         onSdkLoaded();
    136 
    137         setControl(container);
    138         new Label(container, SWT.NONE);
    139         new Label(container, SWT.NONE);
    140 
    141         mCreateTestCheckbox = new Button(container, SWT.CHECK);
    142         mCreateTestCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
    143         mCreateTestCheckbox.setText("Create a Test Project");
    144         mCreateTestCheckbox.addSelectionListener(this);
    145 
    146         mTestProjectNameLabel = new Label(container, SWT.NONE);
    147         mTestProjectNameLabel.setText("Test Project Name:");
    148 
    149         mTestProjectNameText = new Text(container, SWT.BORDER);
    150         mTestProjectNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    151         mTestProjectNameText.addModifyListener(this);
    152 
    153         mTestApplicationLabel = new Label(container, SWT.NONE);
    154         mTestApplicationLabel.setText("Test Application:");
    155 
    156         mTestApplicationText = new Text(container, SWT.BORDER);
    157         mTestApplicationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    158         mTestApplicationText.addModifyListener(this);
    159 
    160         mTestPackageLabel = new Label(container, SWT.NONE);
    161         mTestPackageLabel.setText("Test Package:");
    162 
    163         mTestPackageText = new Text(container, SWT.BORDER);
    164         mTestPackageText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    165         mTestPackageText.addModifyListener(this);
    166     }
    167 
    168     /** Controls whether the options for creating a paired test project should be shown */
    169     private void showTestOptions(boolean visible) {
    170         if (mValues.mode == Mode.SAMPLE) {
    171             visible = false;
    172         }
    173 
    174         mCreateTestCheckbox.setVisible(visible);
    175         mTestProjectNameLabel.setVisible(visible);
    176         mTestProjectNameText.setVisible(visible);
    177         mTestApplicationLabel.setVisible(visible);
    178         mTestApplicationText.setVisible(visible);
    179         mTestPackageLabel.setVisible(visible);
    180         mTestPackageText.setVisible(visible);
    181     }
    182 
    183     /** Controls whether the options for creating a paired test project should be enabled */
    184     private void enableTestOptions(boolean enabled) {
    185         mTestProjectNameLabel.setEnabled(enabled);
    186         mTestProjectNameText.setEnabled(enabled);
    187         mTestApplicationLabel.setEnabled(enabled);
    188         mTestApplicationText.setEnabled(enabled);
    189         mTestPackageLabel.setEnabled(enabled);
    190         mTestPackageText.setEnabled(enabled);
    191     }
    192 
    193     @Override
    194     public void setVisible(boolean visible) {
    195         super.setVisible(visible);
    196 
    197         if (visible) {
    198             try {
    199                 mIgnore = true;
    200                 if (mValues.applicationName != null) {
    201                     mApplicationText.setText(mValues.applicationName);
    202                 }
    203                 if (mValues.packageName != null) {
    204                     mPackageText.setText(mValues.packageName);
    205                 } else {
    206                     mPackageText.setText(DUMMY_PACKAGE);
    207                 }
    208 
    209                 if (mValues.mode != Mode.TEST) {
    210                     mCreateActivityCheckbox.setSelection(mValues.createActivity);
    211                     mActivityText.setEnabled(mValues.createActivity);
    212                     if (mValues.activityName != null) {
    213                         mActivityText.setText(mValues.activityName);
    214                     }
    215                 }
    216                 if (mValues.minSdk != null && mValues.minSdk.length() > 0) {
    217                     mSdkCombo.setText(mValues.minSdk);
    218                 }
    219 
    220                 showTestOptions(mValues.mode == Mode.ANY);
    221                 enableTestOptions(mCreateTestCheckbox.getSelection());
    222 
    223                 if (mValues.testProjectName != null) {
    224                     mTestProjectNameText.setText(mValues.testProjectName);
    225                 }
    226                 if (mValues.testApplicationName != null) {
    227                     mTestApplicationText.setText(mValues.testApplicationName);
    228                 }
    229                 if (mValues.testProjectName != null) {
    230                     mTestPackageText.setText(mValues.testProjectName);
    231                 }
    232             } finally {
    233                 mIgnore = false;
    234             }
    235         }
    236 
    237         // Start focus with the package name, since the other fields are typically assigned
    238         // reasonable defaults
    239         mPackageText.setFocus();
    240         mPackageText.selectAll();
    241 
    242         validatePage();
    243     }
    244 
    245     protected void setSdkTargets(IAndroidTarget[] targets, IAndroidTarget target) {
    246         if (targets == null) {
    247             targets = new IAndroidTarget[0];
    248         }
    249         int selectionIndex = -1;
    250         String[] items = new String[targets.length];
    251         for (int i = 0, n = targets.length; i < n; i++) {
    252             items[i] = targetLabel(targets[i]);
    253             if (targets[i] == target) {
    254                 selectionIndex = i;
    255             }
    256         }
    257         try {
    258             mIgnore = true;
    259             mSdkCombo.setItems(items);
    260             mSdkCombo.setData(targets);
    261             if (selectionIndex != -1) {
    262                 mSdkCombo.select(selectionIndex);
    263             }
    264         } finally {
    265             mIgnore = false;
    266         }
    267     }
    268 
    269     private String targetLabel(IAndroidTarget target) {
    270         // In the minimum SDK chooser, show the targets with api number and description,
    271         // such as "11 (Android 3.0)"
    272         return String.format("%1$s (%2$s)", target.getVersion().getApiString(),
    273                 target.getFullName());
    274     }
    275 
    276     @Override
    277     public void dispose() {
    278         AdtPlugin.getDefault().removeTargetListener(this);
    279         super.dispose();
    280     }
    281 
    282     @Override
    283     public boolean isPageComplete() {
    284         // This page is only needed when creating new projects
    285         if (mValues.useExisting || mValues.mode != Mode.ANY) {
    286             return true;
    287         }
    288 
    289         // Ensure that we reach this page
    290         if (mValues.packageName == null) {
    291             return false;
    292         }
    293 
    294         return super.isPageComplete();
    295     }
    296 
    297     @Override
    298     public void modifyText(ModifyEvent e) {
    299         if (mIgnore) {
    300             return;
    301         }
    302 
    303         Object source = e.getSource();
    304         if (source == mSdkCombo) {
    305             mValues.minSdk = mSdkCombo.getText().trim();
    306             IAndroidTarget[] targets = (IAndroidTarget[]) mSdkCombo.getData();
    307             // An editable combo will treat item selection the same way as a user edit,
    308             // so we need to see if the string looks like a labeled version
    309             int index = mSdkCombo.getSelectionIndex();
    310             if (index != -1) {
    311                 if (index >= 0 && index < targets.length) {
    312                     IAndroidTarget target = targets[index];
    313                     if (targetLabel(target).equals(mValues.minSdk)) {
    314                         mValues.minSdk = target.getVersion().getApiString();
    315                     }
    316                 }
    317             }
    318 
    319             // Ensure that we never pick up the (Android x.y) suffix shown in combobox
    320             // for readability
    321             int separator = mValues.minSdk.indexOf(' ');
    322             if (separator != -1) {
    323                 mValues.minSdk = mValues.minSdk.substring(0, separator);
    324             }
    325             mValues.minSdkModifiedByUser = true;
    326             mValues.updateSdkTargetToMatchMinSdkVersion();
    327         } else if (source == mApplicationText) {
    328             mValues.applicationName = mApplicationText.getText().trim();
    329             mValues.applicationNameModifiedByUser = true;
    330 
    331             if (!mValues.testApplicationNameModified) {
    332                 mValues.testApplicationName = suggestTestApplicationName(mValues.applicationName);
    333                 try {
    334                     mIgnore = true;
    335                     mTestApplicationText.setText(mValues.testApplicationName);
    336                 } finally {
    337                     mIgnore = false;
    338                 }
    339             }
    340 
    341         } else if (source == mPackageText) {
    342             mValues.packageName = mPackageText.getText().trim();
    343             mValues.packageNameModifiedByUser = true;
    344 
    345             if (!mValues.testPackageModified) {
    346                 mValues.testPackageName = suggestTestPackage(mValues.packageName);
    347                 try {
    348                     mIgnore = true;
    349                     mTestPackageText.setText(mValues.testPackageName);
    350                 } finally {
    351                     mIgnore = false;
    352                 }
    353             }
    354         } else if (source == mActivityText) {
    355             mValues.activityName = mActivityText.getText().trim();
    356             mValues.activityNameModifiedByUser = true;
    357         } else if (source == mTestApplicationText) {
    358             mValues.testApplicationName = mTestApplicationText.getText().trim();
    359             mValues.testApplicationNameModified = true;
    360         } else if (source == mTestPackageText) {
    361             mValues.testPackageName = mTestPackageText.getText().trim();
    362             mValues.testPackageModified = true;
    363         } else if (source == mTestProjectNameText) {
    364             mValues.testProjectName = mTestProjectNameText.getText().trim();
    365             mValues.testProjectModified = true;
    366         }
    367 
    368         validatePage();
    369     }
    370 
    371     @Override
    372     public void widgetSelected(SelectionEvent e) {
    373         if (mIgnore) {
    374             return;
    375         }
    376 
    377         Object source = e.getSource();
    378 
    379         if (source == mCreateActivityCheckbox) {
    380             mValues.createActivity = mCreateActivityCheckbox.getSelection();
    381             mActivityText.setEnabled(mValues.createActivity);
    382         } else if (source == mSdkCombo) {
    383             int index = mSdkCombo.getSelectionIndex();
    384             IAndroidTarget[] targets = (IAndroidTarget[]) mSdkCombo.getData();
    385             if (index != -1) {
    386                 if (index >= 0 && index < targets.length) {
    387                     IAndroidTarget target = targets[index];
    388                     // Even though we are showing the logical version name, we place the
    389                     // actual api number as the minimum SDK
    390                     mValues.minSdk = target.getVersion().getApiString();
    391                 }
    392             } else {
    393                 String text = mSdkCombo.getText();
    394                 boolean found = false;
    395                 for (IAndroidTarget target : targets) {
    396                     if (targetLabel(target).equals(text)) {
    397                         mValues.minSdk = target.getVersion().getApiString();
    398                         found = true;
    399                         break;
    400                     }
    401                 }
    402                 if (!found) {
    403                     mValues.minSdk = text;
    404                 }
    405             }
    406         } else if (source == mCreateTestCheckbox) {
    407             mValues.createPairProject = mCreateTestCheckbox.getSelection();
    408             enableTestOptions(mValues.createPairProject);
    409             if (mValues.createPairProject) {
    410                 if (mValues.testProjectName == null || mValues.testProjectName.length() == 0) {
    411                     mValues.testProjectName = suggestTestProjectName(mValues.projectName);
    412                 }
    413                 if (mValues.testApplicationName == null ||
    414                         mValues.testApplicationName.length() == 0) {
    415                     mValues.testApplicationName =
    416                             suggestTestApplicationName(mValues.applicationName);
    417                 }
    418                 if (mValues.testPackageName == null || mValues.testPackageName.length() == 0) {
    419                     mValues.testPackageName = suggestTestPackage(mValues.packageName);
    420                 }
    421 
    422                 try {
    423                     mIgnore = true;
    424                     mTestProjectNameText.setText(mValues.testProjectName);
    425                     mTestApplicationText.setText(mValues.testApplicationName);
    426                     mTestPackageText.setText(mValues.testPackageName);
    427                 } finally {
    428                     mIgnore = false;
    429                 }
    430             }
    431         }
    432 
    433         validatePage();
    434     }
    435 
    436     @Override
    437     public void widgetDefaultSelected(SelectionEvent e) {
    438     }
    439 
    440     private void validatePage() {
    441         IStatus status = validatePackage(mValues.packageName);
    442         if (status == null || status.getSeverity() != IStatus.ERROR) {
    443             IStatus validActivity = validateActivity();
    444             if (validActivity != null) {
    445                 status = validActivity;
    446             }
    447         }
    448         if (status == null || status.getSeverity() != IStatus.ERROR) {
    449             IStatus validMinSdk = validateMinSdk();
    450             if (validMinSdk != null) {
    451                 status = validMinSdk;
    452             }
    453         }
    454 
    455         if (status == null || status.getSeverity() != IStatus.ERROR) {
    456             IStatus validSourceFolder = validateSourceFolder();
    457             if (validSourceFolder != null) {
    458                 status = validSourceFolder;
    459             }
    460         }
    461 
    462         // If creating a test project to go along with the main project, also validate
    463         // the additional test project parameters
    464         if (status == null || status.getSeverity() != IStatus.ERROR) {
    465             if (mValues.createPairProject) {
    466                 IStatus validTestProject = ProjectNamePage.validateProjectName(
    467                         mValues.testProjectName);
    468                 if (validTestProject != null) {
    469                     status = validTestProject;
    470                 }
    471 
    472                 if (status == null || status.getSeverity() != IStatus.ERROR) {
    473                     IStatus validTestLocation = validateTestProjectLocation();
    474                     if (validTestLocation != null) {
    475                         status = validTestLocation;
    476                     }
    477                 }
    478 
    479                 if (status == null || status.getSeverity() != IStatus.ERROR) {
    480                     IStatus validTestPackage = validatePackage(mValues.testPackageName);
    481                     if (validTestPackage != null) {
    482                         status = new Status(validTestPackage.getSeverity(),
    483                                 AdtPlugin.PLUGIN_ID,
    484                                 validTestPackage.getMessage() + " (in test package)");
    485                     }
    486                 }
    487 
    488                 if (status == null || status.getSeverity() != IStatus.ERROR) {
    489                     if (mValues.projectName.equals(mValues.testProjectName)) {
    490                         status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    491                              "The main project name and the test project name must be different.");
    492                     }
    493                 }
    494             }
    495         }
    496 
    497         // -- update UI & enable finish if there's no error
    498         setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
    499         if (status != null) {
    500             setMessage(status.getMessage(),
    501                     status.getSeverity() == IStatus.ERROR
    502                         ? IMessageProvider.ERROR : IMessageProvider.WARNING);
    503         } else {
    504             setErrorMessage(null);
    505             setMessage(null);
    506         }
    507     }
    508 
    509     private IStatus validateTestProjectLocation() {
    510         assert mValues.createPairProject;
    511 
    512         // Validate location
    513         Path path = new Path(mValues.projectLocation.getPath());
    514         if (!mValues.useExisting) {
    515             if (!mValues.useDefaultLocation) {
    516                 // If not using the default value validate the location.
    517                 URI uri = URIUtil.toURI(path.toOSString());
    518                 IWorkspace workspace = ResourcesPlugin.getWorkspace();
    519                 IProject handle = workspace.getRoot().getProject(mValues.testProjectName);
    520                 IStatus locationStatus = workspace.validateProjectLocationURI(handle, uri);
    521                 if (!locationStatus.isOK()) {
    522                     return locationStatus;
    523                 }
    524                 // The location is valid as far as Eclipse is concerned (i.e. mostly not
    525                 // an existing workspace project.) Check it either doesn't exist or is
    526                 // a directory that is empty.
    527                 File f = path.toFile();
    528                 if (f.exists() && !f.isDirectory()) {
    529                     return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    530                             "A directory name must be specified.");
    531                 } else if (f.isDirectory()) {
    532                     // However if the directory exists, we should put a
    533                     // warning if it is not empty. We don't put an error
    534                     // (we'll ask the user again for confirmation before
    535                     // using the directory.)
    536                     String[] l = f.list();
    537                     if (l != null && l.length != 0) {
    538                         return new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
    539                                 "The selected output directory is not empty.");
    540                     }
    541                 }
    542             } else {
    543                 IPath destPath = path.removeLastSegments(1).append(mValues.testProjectName);
    544                 File dest = destPath.toFile();
    545                 if (dest.exists()) {
    546                     return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    547                             String.format(
    548                                     "There is already a file or directory named \"%1$s\" in the selected location.",
    549                             mValues.testProjectName));
    550                 }
    551             }
    552         }
    553 
    554         return null;
    555     }
    556 
    557     private IStatus validateSourceFolder() {
    558         // This check does nothing when creating a new project.
    559         // This check is also useless when no activity is present or created.
    560         mValues.sourceFolder = SdkConstants.FD_SOURCES;
    561         if (!mValues.useExisting || !mValues.createActivity) {
    562             return null;
    563         }
    564 
    565         String osTarget = mValues.activityName;
    566         if (osTarget.indexOf('.') == -1) {
    567             osTarget = mValues.packageName + File.separator + osTarget;
    568         } else if (osTarget.indexOf('.') == 0) {
    569             osTarget = mValues.packageName + osTarget;
    570         }
    571         osTarget = osTarget.replace('.', File.separatorChar) + SdkConstants.DOT_JAVA;
    572 
    573         File projectDir = mValues.projectLocation;
    574         File[] allDirs = projectDir.listFiles(new FileFilter() {
    575             @Override
    576             public boolean accept(File pathname) {
    577                 return pathname.isDirectory();
    578             }
    579         });
    580         if (allDirs != null) {
    581             boolean found = false;
    582             for (File f : allDirs) {
    583                 Path path = new Path(f.getAbsolutePath());
    584                 File java_activity = path.append(osTarget).toFile();
    585                 if (java_activity.isFile()) {
    586                     mValues.sourceFolder = f.getName();
    587                     found = true;
    588                     break;
    589                 }
    590             }
    591 
    592             if (!found) {
    593                 String projectPath = projectDir.getPath();
    594                 if (allDirs.length > 0) {
    595                     return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    596                             String.format("%1$s can not be found under %2$s.", osTarget,
    597                             projectPath));
    598                 } else {
    599                     return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    600                             String.format("No source folders can be found in %1$s.",
    601                             projectPath));
    602                 }
    603             }
    604         }
    605 
    606         return null;
    607     }
    608 
    609     private IStatus validateMinSdk() {
    610         // Validate min SDK field
    611         // If the min sdk version is empty, it is always accepted.
    612         if (mValues.minSdk == null || mValues.minSdk.length() == 0) {
    613             return null;
    614         }
    615 
    616         IAndroidTarget target = mValues.target;
    617         if (target == null) {
    618             return null;
    619         }
    620 
    621         // If the current target is a preview, explicitly indicate minSdkVersion
    622         // must be set to this target name.
    623         if (target.getVersion().isPreview() && !target.getVersion().equals(mValues.minSdk)) {
    624             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    625                     String.format(
    626                             "The SDK target is a preview. Min SDK Version must be set to '%s'.",
    627                             target.getVersion().getCodename()));
    628         }
    629 
    630         if (!target.getVersion().equals(mValues.minSdk)) {
    631             return new Status(target.getVersion().isPreview() ? IStatus.ERROR : IStatus.WARNING,
    632                     AdtPlugin.PLUGIN_ID,
    633                     "The API level for the selected SDK target does not match the Min SDK Version."
    634                     );
    635         }
    636 
    637         return null;
    638     }
    639 
    640     public static IStatus validatePackage(String packageFieldContents) {
    641         // Validate package
    642         if (packageFieldContents == null || packageFieldContents.length() == 0) {
    643             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    644                     "Package name must be specified.");
    645         } else if (packageFieldContents.equals(DUMMY_PACKAGE)) {
    646             // The dummy package name is just a placeholder package (which isn't even valid
    647             // because it contains the reserved Java keyword "package") but we want to
    648             // make the error message say that a proper package should be entered rather than
    649             // what's wrong with this specific package. (And the reason we provide a dummy
    650             // package rather than a blank line is to make it more clear to beginners what
    651             // we're looking for.
    652             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    653                     "Package name must be specified.");
    654         }
    655         // Check it's a valid package string
    656         IStatus status = JavaConventions.validatePackageName(packageFieldContents, JDK_15,
    657                 JDK_15);
    658         if (!status.isOK()) {
    659             return status;
    660         }
    661 
    662         // The Android Activity Manager does not accept packages names with only one
    663         // identifier. Check the package name has at least one dot in them (the previous rule
    664         // validated that if such a dot exist, it's not the first nor last characters of the
    665         // string.)
    666         if (packageFieldContents.indexOf('.') == -1) {
    667             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    668                     "Package name must have at least two identifiers.");
    669         }
    670 
    671         return null;
    672     }
    673 
    674     public static IStatus validateClass(String className) {
    675         if (className == null || className.length() == 0) {
    676             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    677                     "Class name must be specified.");
    678         }
    679         if (className.indexOf('.') != -1) {
    680             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    681                     "Enter just a class name, not a full package name");
    682         }
    683         return JavaConventions.validateJavaTypeName(className, JDK_15, JDK_15);
    684     }
    685 
    686     private IStatus validateActivity() {
    687         // Validate activity (if creating an activity)
    688         if (!mValues.createActivity) {
    689             return null;
    690         }
    691 
    692         return validateActivity(mValues.activityName);
    693     }
    694 
    695     /**
    696      * Validates the given activity name
    697      *
    698      * @param activityFieldContents the activity name to validate
    699      * @return a status for whether the activity name is valid
    700      */
    701     public static IStatus validateActivity(String activityFieldContents) {
    702         // Validate activity field
    703         if (activityFieldContents == null || activityFieldContents.length() == 0) {
    704             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    705                     "Activity name must be specified.");
    706         } else if (ACTIVITY_NAME_SUFFIX.equals(activityFieldContents)) {
    707             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a valid activity name");
    708         } else if (activityFieldContents.contains("..")) { //$NON-NLS-1$
    709             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    710                     "Package segments in activity name cannot be empty (..)");
    711         }
    712         // The activity field can actually contain part of a sub-package name
    713         // or it can start with a dot "." to indicates it comes from the parent package
    714         // name.
    715         String packageName = "";  //$NON-NLS-1$
    716         int pos = activityFieldContents.lastIndexOf('.');
    717         if (pos >= 0) {
    718             packageName = activityFieldContents.substring(0, pos);
    719             if (packageName.startsWith(".")) { //$NON-NLS-1$
    720                 packageName = packageName.substring(1);
    721             }
    722 
    723             activityFieldContents = activityFieldContents.substring(pos + 1);
    724         }
    725 
    726         // the activity field can contain a simple java identifier, or a
    727         // package name or one that starts with a dot. So if it starts with a dot,
    728         // ignore this dot -- the rest must look like a package name.
    729         if (activityFieldContents.length() > 0 && activityFieldContents.charAt(0) == '.') {
    730             activityFieldContents = activityFieldContents.substring(1);
    731         }
    732 
    733         // Check it's a valid activity string
    734         IStatus status = JavaConventions.validateTypeVariableName(activityFieldContents, JDK_15,
    735                 JDK_15);
    736         if (!status.isOK()) {
    737             return status;
    738         }
    739 
    740         // Check it's a valid package string
    741         if (packageName.length() > 0) {
    742             status = JavaConventions.validatePackageName(packageName, JDK_15, JDK_15);
    743             if (!status.isOK()) {
    744                 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    745                         status.getMessage() + " (in the activity name)");
    746             }
    747         }
    748 
    749         return null;
    750     }
    751 
    752     // ---- Implement ITargetChangeListener ----
    753 
    754     @Override
    755     public void onSdkLoaded() {
    756         if (mSdkCombo == null) {
    757             return;
    758         }
    759 
    760         // Update the sdk target selector with the new targets
    761 
    762         // get the targets from the sdk
    763         IAndroidTarget[] targets = null;
    764         if (Sdk.getCurrent() != null) {
    765             targets = Sdk.getCurrent().getTargets();
    766         }
    767         setSdkTargets(targets, mValues.target);
    768     }
    769 
    770     @Override
    771     public void onProjectTargetChange(IProject changedProject) {
    772         // Ignore
    773     }
    774 
    775     @Override
    776     public void onTargetLoaded(IAndroidTarget target) {
    777         // Ignore
    778     }
    779 
    780     public static String suggestTestApplicationName(String applicationName) {
    781         if (applicationName == null) {
    782             applicationName = ""; //$NON-NLS-1$
    783         }
    784         if (applicationName.indexOf(' ') != -1) {
    785             return applicationName + " Test"; //$NON-NLS-1$
    786         } else {
    787             return applicationName + "Test"; //$NON-NLS-1$
    788         }
    789     }
    790 
    791     public static String suggestTestProjectName(String projectName) {
    792         if (projectName == null) {
    793             projectName = ""; //$NON-NLS-1$
    794         }
    795         if (projectName.length() > 0 && Character.isUpperCase(projectName.charAt(0))) {
    796             return projectName + "Test"; //$NON-NLS-1$
    797         } else {
    798             return projectName + "-test"; //$NON-NLS-1$
    799         }
    800     }
    801 
    802 
    803     public static String suggestTestPackage(String packagePath) {
    804         if (packagePath == null) {
    805             packagePath = ""; //$NON-NLS-1$
    806         }
    807         return packagePath + ".test"; //$NON-NLS-1$
    808     }
    809 }
    810