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      */
ApplicationInfoPage(NewProjectWizardState values)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
createControl(Composite parent)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 */
showTestOptions(boolean visible)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 */
enableTestOptions(boolean 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
setVisible(boolean visible)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 
setSdkTargets(IAndroidTarget[] targets, IAndroidTarget target)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 
targetLabel(IAndroidTarget target)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
dispose()277     public void dispose() {
278         AdtPlugin.getDefault().removeTargetListener(this);
279         super.dispose();
280     }
281 
282     @Override
isPageComplete()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
modifyText(ModifyEvent e)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
widgetSelected(SelectionEvent e)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
widgetDefaultSelected(SelectionEvent e)437     public void widgetDefaultSelected(SelectionEvent e) {
438     }
439 
validatePage()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 
validateTestProjectLocation()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 
validateSourceFolder()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 
validateMinSdk()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 
validatePackage(String packageFieldContents)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 
validateClass(String className)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 
validateActivity()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      */
validateActivity(String activityFieldContents)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
onSdkLoaded()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
onProjectTargetChange(IProject changedProject)771     public void onProjectTargetChange(IProject changedProject) {
772         // Ignore
773     }
774 
775     @Override
onTargetLoaded(IAndroidTarget target)776     public void onTargetLoaded(IAndroidTarget target) {
777         // Ignore
778     }
779 
suggestTestApplicationName(String applicationName)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 
suggestTestProjectName(String projectName)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 
suggestTestPackage(String packagePath)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