FileDocCategorySizeDatePackage
NewProjectCreationPage.javaAPI DocAndroid 1.5 API55570Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.adt.wizards.newproject

NewProjectCreationPage

public class NewProjectCreationPage extends org.eclipse.jface.wizard.WizardPage
NewAndroidProjectCreationPage is a project creation page that provides the following fields:
  • Project name
  • SDK Target
  • Application name
  • Package name
  • Activity name
Note: this class is public so that it can be accessed from unit tests. It is however an internal class. Its API may change without notice. It should semantically be considered as a private final class. Do not derive from this class.

Fields Summary
private static final String
INITIAL_NAME
Initial value for all name fields (project, activity, application, package). Used whenever a value is requested before controls are created.
private static final boolean
INITIAL_CREATE_NEW_PROJECT
Initial value for the Create New Project radio; False means Create From Existing would be the default.
private static final boolean
INITIAL_USE_DEFAULT_LOCATION
Initial value for the Use Default Location check box.
private static final boolean
INITIAL_CREATE_ACTIVITY
Initial value for the Create Activity check box.
private static final Pattern
sProjectNamePattern
Pattern for characters accepted in a project name. Since this will be used as a directory name, we're being a bit conservative on purpose. It cannot start with a space.
private static String
sCustomLocationOsPath
Last user-browsed location, static so that it be remembered for the whole session
private static boolean
sAutoComputeCustomLocation
private final int
MSG_NONE
private final int
MSG_WARNING
private final int
MSG_ERROR
private String
mUserPackageName
private String
mUserActivityName
private boolean
mUserCreateActivityCheck
private String
mSourceFolder
private org.eclipse.swt.widgets.Text
mProjectNameField
private org.eclipse.swt.widgets.Text
mPackageNameField
private org.eclipse.swt.widgets.Text
mActivityNameField
private org.eclipse.swt.widgets.Text
mApplicationNameField
private org.eclipse.swt.widgets.Button
mCreateNewProjectRadio
private org.eclipse.swt.widgets.Button
mUseDefaultLocation
private org.eclipse.swt.widgets.Label
mLocationLabel
private org.eclipse.swt.widgets.Text
mLocationPathField
private org.eclipse.swt.widgets.Button
mBrowseButton
private org.eclipse.swt.widgets.Button
mCreateActivityCheck
private org.eclipse.swt.widgets.Text
mMinSdkVersionField
private com.android.sdkuilib.SdkTargetSelector
mSdkTargetSelector
private com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener
mSdkTargetChangeListener
private boolean
mInternalLocationPathUpdate
protected boolean
mInternalProjectNameUpdate
protected boolean
mInternalApplicationNameUpdate
private boolean
mInternalCreateActivityUpdate
private boolean
mInternalActivityNameUpdate
protected boolean
mProjectNameModifiedByUser
protected boolean
mApplicationNameModifiedByUser
private boolean
mInternalMinSdkVersionUpdate
private boolean
mMinSdkVersionModifiedByUser
Constructors Summary
public NewProjectCreationPage(String pageName)
Creates a new project creation wizard page.

param
pageName the name of this page



                       
       
        super(pageName);
        setPageComplete(false);
    
Methods Summary
public voidcreateControl(org.eclipse.swt.widgets.Composite parent)
Creates the top level control for this dialog page under the given parent composite.

see
org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)

        Composite composite = new Composite(parent, SWT.NULL);
        composite.setFont(parent.getFont());

        initializeDialogUnits(parent);

        composite.setLayout(new GridLayout());
        composite.setLayoutData(new GridData(GridData.FILL_BOTH));

        createProjectNameGroup(composite);
        createLocationGroup(composite);
        createTargetGroup(composite);
        createPropertiesGroup(composite);

        // Update state the first time
        enableLocationWidgets();

        // Show description the first time
        setErrorMessage(null);
        setMessage(null);
        setControl(composite);

        // Validate. This will complain about the first empty field.
        setPageComplete(validatePage());
    
private final voidcreateLocationGroup(org.eclipse.swt.widgets.Composite parent)
Creates the group for the Project options: [radio] Create new project [radio] Create project from existing sources [check] Use default location Location [text field] [browse button]

param
parent the parent composite

        Group group = new Group(parent, SWT.SHADOW_ETCHED_IN);
        // Layout has 4 columns of non-equal size
        group.setLayout(new GridLayout());
        group.setLayoutData(new GridData(GridData.FILL_BOTH));
        group.setFont(parent.getFont());
        group.setText("Contents");

        mCreateNewProjectRadio = new Button(group, SWT.RADIO);
        mCreateNewProjectRadio.setText("Create new project in workspace");
        mCreateNewProjectRadio.setSelection(INITIAL_CREATE_NEW_PROJECT);
        Button existing_project_radio = new Button(group, SWT.RADIO);
        existing_project_radio.setText("Create project from existing source");
        existing_project_radio.setSelection(!INITIAL_CREATE_NEW_PROJECT);

        mUseDefaultLocation = new Button(group, SWT.CHECK);
        mUseDefaultLocation.setText("Use default location");
        mUseDefaultLocation.setSelection(INITIAL_USE_DEFAULT_LOCATION);

        SelectionListener location_listener = new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                enableLocationWidgets();
                extractNamesFromAndroidManifest();
                setPageComplete(validatePage());
            }
        };

        mCreateNewProjectRadio.addSelectionListener(location_listener);
        existing_project_radio.addSelectionListener(location_listener);
        mUseDefaultLocation.addSelectionListener(location_listener);

        Composite location_group = new Composite(group, SWT.NONE);
        location_group.setLayout(new GridLayout(4, /* num columns */
                false /* columns of not equal size */));
        location_group.setLayoutData(new GridData(GridData.FILL_BOTH));
        location_group.setFont(parent.getFont());

        mLocationLabel = new Label(location_group, SWT.NONE);
        mLocationLabel.setText("Location:");

        mLocationPathField = new Text(location_group, SWT.BORDER);
        GridData data = new GridData(GridData.FILL, /* horizontal alignment */
                GridData.BEGINNING, /* vertical alignment */
                true,  /* grabExcessHorizontalSpace */
                false, /* grabExcessVerticalSpace */
                2,     /* horizontalSpan */
                1);    /* verticalSpan */
        mLocationPathField.setLayoutData(data);
        mLocationPathField.setFont(parent.getFont());
        mLocationPathField.addListener(SWT.Modify, new Listener() {
           public void handleEvent(Event event) {
               onLocationPathFieldModified();
            }
        });

        mBrowseButton = new Button(location_group, SWT.PUSH);
        mBrowseButton.setText("Browse...");
        setButtonLayoutData(mBrowseButton);
        mBrowseButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                openDirectoryBrowser();
            }
        });
    
private final voidcreateProjectNameGroup(org.eclipse.swt.widgets.Composite parent)
Creates the group for the project name: [label: "Project Name"] [text field]

param
parent the parent composite

        Composite group = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        group.setLayout(layout);
        group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        // new project label
        Label label = new Label(group, SWT.NONE);
        label.setText("Project name:");
        label.setFont(parent.getFont());
        label.setToolTipText("Name of the Eclipse project to create. It cannot be empty.");

        // new project name entry field
        mProjectNameField = new Text(group, SWT.BORDER);
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        mProjectNameField.setToolTipText("Name of the Eclipse project to create. It cannot be empty.");
        mProjectNameField.setLayoutData(data);
        mProjectNameField.setFont(parent.getFont());
        mProjectNameField.addListener(SWT.Modify, new Listener() {
            public void handleEvent(Event event) {
                if (!mInternalProjectNameUpdate) {
                    mProjectNameModifiedByUser = true;
                }
                updateLocationPathField(null);
            }
        });
    
private final voidcreatePropertiesGroup(org.eclipse.swt.widgets.Composite parent)
Creates the group for the project properties: - Package name [text field] - Activity name [text field] - Application name [text field]

param
parent the parent composite

        // package specification group
        Group group = new Group(parent, SWT.SHADOW_ETCHED_IN);
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        group.setLayout(layout);
        group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        group.setFont(parent.getFont());
        group.setText("Properties");

        // new application label
        Label label = new Label(group, SWT.NONE);
        label.setText("Application name:");
        label.setFont(parent.getFont());
        label.setToolTipText("Name of the Application. This is a free string. It can be empty.");

        // new application name entry field
        mApplicationNameField = new Text(group, SWT.BORDER);
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        mApplicationNameField.setToolTipText("Name of the Application. This is a free string. It can be empty.");
        mApplicationNameField.setLayoutData(data);
        mApplicationNameField.setFont(parent.getFont());
        mApplicationNameField.addListener(SWT.Modify, new Listener() {
           public void handleEvent(Event event) {
               if (!mInternalApplicationNameUpdate) {
                   mApplicationNameModifiedByUser = true;
               }
           } 
        });

        // new package label
        label = new Label(group, SWT.NONE);
        label.setText("Package name:");
        label.setFont(parent.getFont());
        label.setToolTipText("Namespace of the Package to create. This must be a Java namespace with at least two components.");

        // new package name entry field
        mPackageNameField = new Text(group, SWT.BORDER);
        data = new GridData(GridData.FILL_HORIZONTAL);
        mPackageNameField.setToolTipText("Namespace of the Package to create. This must be a Java namespace with at least two components.");
        mPackageNameField.setLayoutData(data);
        mPackageNameField.setFont(parent.getFont());
        mPackageNameField.addListener(SWT.Modify, new Listener() {
            public void handleEvent(Event event) {
                onPackageNameFieldModified();
            }
        });

        // new activity label
        mCreateActivityCheck = new Button(group, SWT.CHECK);
        mCreateActivityCheck.setText("Create Activity:");
        mCreateActivityCheck.setToolTipText("Specifies if you want to create a default Activity.");
        mCreateActivityCheck.setFont(parent.getFont());
        mCreateActivityCheck.setSelection(INITIAL_CREATE_ACTIVITY);
        mCreateActivityCheck.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                onCreateActivityCheckModified();
                enableLocationWidgets();
            }
        });

        // new activity name entry field
        mActivityNameField = new Text(group, SWT.BORDER);
        data = new GridData(GridData.FILL_HORIZONTAL);
        mActivityNameField.setToolTipText("Name of the Activity class to create. Must be a valid Java identifier.");
        mActivityNameField.setLayoutData(data);
        mActivityNameField.setFont(parent.getFont());
        mActivityNameField.addListener(SWT.Modify, new Listener() {
            public void handleEvent(Event event) {
                onActivityNameFieldModified();
            }
        });

        // min sdk version label
        label = new Label(group, SWT.NONE);
        label.setText("Min SDK Version:");
        label.setFont(parent.getFont());
        label.setToolTipText("The minimum SDK version number that the application requires. Must be an integer > 0. It can be empty.");

        // min sdk version entry field
        mMinSdkVersionField = new Text(group, SWT.BORDER);
        data = new GridData(GridData.FILL_HORIZONTAL);
        label.setToolTipText("The minimum SDK version number that the application requires. Must be an integer > 0. It can be empty.");
        mMinSdkVersionField.setLayoutData(data);
        mMinSdkVersionField.setFont(parent.getFont());
        mMinSdkVersionField.addListener(SWT.Modify, new Listener() {
            public void handleEvent(Event event) {
                onMinSdkVersionFieldModified();
                setPageComplete(validatePage());
            }
        });
    
private voidcreateTargetGroup(org.eclipse.swt.widgets.Composite parent)
Creates the target group. It only contains an SdkTargetSelector.

        Group group = new Group(parent, SWT.SHADOW_ETCHED_IN);
        // Layout has 1 column
        group.setLayout(new GridLayout());
        group.setLayoutData(new GridData(GridData.FILL_BOTH));
        group.setFont(parent.getFont());
        group.setText("Build Target");
        
        // The selector is created without targets. They are added below in the change listener.
        mSdkTargetSelector = new SdkTargetSelector(group, null);

        mSdkTargetChangeListener = new ITargetChangeListener() {
            public void onProjectTargetChange(IProject changedProject) {
                // Ignore
            }

            public void onTargetsLoaded() {
                // Update the sdk target selector with the new targets

                // get the targets from the sdk
                IAndroidTarget[] targets = null;
                if (Sdk.getCurrent() != null) {
                    targets = Sdk.getCurrent().getTargets();
                }
                mSdkTargetSelector.setTargets(targets);

                // If there's only one target, select it
                if (targets != null && targets.length == 1) {
                    mSdkTargetSelector.setSelection(targets[0]);
                }
            }
        };
        
        AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener);
        
        // Invoke it once to initialize the targets
        mSdkTargetChangeListener.onTargetsLoaded();
        
        mSdkTargetSelector.setSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onSdkTargetModified();
                updateLocationPathField(null);
                setPageComplete(validatePage());
            }
        });
    
public voiddispose()

        
        if (mSdkTargetChangeListener != null) {
            AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener);
            mSdkTargetChangeListener = null;
        }
        
        super.dispose();
    
private voidenableLocationWidgets()
Enables or disable the location widgets depending on the user selection: the location path is enabled when using the "existing source" mode (i.e. not new project) or in new project mode with the "use default location" turned off.

        boolean is_new_project = isNewProject();
        boolean use_default = useDefaultLocation();
        boolean location_enabled = !is_new_project || !use_default;
        boolean create_activity = isCreateActivity();
        
        mUseDefaultLocation.setEnabled(is_new_project);

        mLocationLabel.setEnabled(location_enabled);
        mLocationPathField.setEnabled(location_enabled);
        mBrowseButton.setEnabled(location_enabled);

        mPackageNameField.setEnabled(is_new_project);
        mCreateActivityCheck.setEnabled(is_new_project);
        mActivityNameField.setEnabled(is_new_project & create_activity);

        updateLocationPathField(null);
        updatePackageAndActivityFields();
    
private voidextractNamesFromAndroidManifest()
Extract names from an android manifest. This is done only if the user selected the "use existing source" and a manifest xml file can actually be found in the custom user directory.

        if (isNewProject()) {
            return;
        }

        String projectLocation = getProjectLocation();
        File f = new File(projectLocation);
        if (!f.isDirectory()) {
            return;
        }

        Path path = new Path(f.getPath());
        String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString();
        
        AndroidManifestParser manifestData = AndroidManifestParser.parseForData(osPath);
        if (manifestData == null) {
            return;
        }
        
        String packageName = null;
        Activity activity = null;
        String activityName = null;
        int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK;
        try {
            packageName = manifestData.getPackage();
            minSdkVersion = manifestData.getApiLevelRequirement();

            // try to get the first launcher activity. If none, just take the first activity.
            activity = manifestData.getLauncherActivity();
            if (activity == null) {
                Activity[] activities = manifestData.getActivities();
                if (activities != null && activities.length > 0) {
                    activity = activities[0];
                }
            }
        } catch (Exception e) {
            // ignore exceptions
        }

        if (packageName != null && packageName.length() > 0) {
            mPackageNameField.setText(packageName);
        }
        
        if (activity != null) {
            activityName = AndroidManifestParser.extractActivityName(activity.getName(),
                    packageName);
        }

        if (activityName != null && activityName.length() > 0) {
            mInternalActivityNameUpdate = true;
            mInternalCreateActivityUpdate = true;
            mActivityNameField.setText(activityName);
            mCreateActivityCheck.setSelection(true);
            mInternalCreateActivityUpdate = false;
            mInternalActivityNameUpdate = false;

            // If project name and application names are empty, use the activity
            // name as a default. If the activity name has dots, it's a part of a
            // package specification and only the last identifier must be used.
            if (activityName.indexOf('.") != -1) {
                String[] ids = activityName.split(AndroidConstants.RE_DOT);
                activityName = ids[ids.length - 1];
            }
            if (mProjectNameField.getText().length() == 0 ||
                    !mProjectNameModifiedByUser) {
                mInternalProjectNameUpdate = true;
                mProjectNameField.setText(activityName);
                mInternalProjectNameUpdate = false;
            }
            if (mApplicationNameField.getText().length() == 0 ||
                    !mApplicationNameModifiedByUser) {
                mInternalApplicationNameUpdate = true;
                mApplicationNameField.setText(activityName);
                mInternalApplicationNameUpdate = false;
            }
        } else {
            mInternalActivityNameUpdate = true;
            mInternalCreateActivityUpdate = true;
            mActivityNameField.setText("");  //$NON-NLS-1$
            mCreateActivityCheck.setSelection(false);
            mInternalCreateActivityUpdate = false;
            mInternalActivityNameUpdate = false;
            
            // There is no activity name to use to fill in the project and application
            // name. However if there's a package name, we can use this as a base.
            if (packageName != null && packageName.length() > 0) {
                // Package name is a java identifier, so it's most suitable for
                // an application name.

                if (mApplicationNameField.getText().length() == 0 ||
                        !mApplicationNameModifiedByUser) {
                    mInternalApplicationNameUpdate = true;
                    mApplicationNameField.setText(packageName);
                    mInternalApplicationNameUpdate = false;
                }

                // For the project name, remove any dots
                packageName = packageName.replace('.", '_");
                if (mProjectNameField.getText().length() == 0 ||
                        !mProjectNameModifiedByUser) {
                    mInternalProjectNameUpdate = true;
                    mProjectNameField.setText(packageName);
                    mInternalProjectNameUpdate = false;
                }
                
            }
        }

        // Select the target matching the manifest's sdk or build properties, if any
        boolean foundTarget = false;
        
        ProjectProperties p = ProjectProperties.create(projectLocation, null);
        if (p != null) {
            // Check the {build|default}.properties files if present
            p.merge(PropertyType.BUILD).merge(PropertyType.DEFAULT);
            String v = p.getProperty(ProjectProperties.PROPERTY_TARGET);
            IAndroidTarget target = Sdk.getCurrent().getTargetFromHashString(v);
            if (target != null) {
                mSdkTargetSelector.setSelection(target);
                foundTarget = true;
            }
        }

        if (!foundTarget && minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK) {
            try {
                for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
                    if (target.getApiVersionNumber() == minSdkVersion) {
                        mSdkTargetSelector.setSelection(target);
                        foundTarget = true;
                        break;
                    }
                }
            } catch(NumberFormatException e) {
                // ignore
            }
        }
        
        if (!foundTarget) {
            for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
                if (projectLocation.startsWith(target.getLocation())) {
                    mSdkTargetSelector.setSelection(target);
                    foundTarget = true;
                    break;
                }
            }
        }

        if (!foundTarget) {
            mInternalMinSdkVersionUpdate = true;
            mMinSdkVersionField.setText(
                    minSdkVersion == AndroidManifestParser.INVALID_MIN_SDK ? "" :
                        Integer.toString(minSdkVersion)); //$NON-NLS-1$
            mInternalMinSdkVersionUpdate = false;
        }
    
public java.lang.StringgetActivityName()
Returns the value of the activity name field with spaces trimmed.

        return mActivityNameField == null ? INITIAL_NAME : mActivityNameField.getText().trim();
    
public java.lang.StringgetApplicationName()
Returns the value of the application name field with spaces trimmed.

        // Return the name of the activity as default application name.
        return mApplicationNameField == null ? getActivityName()
                                             : mApplicationNameField.getText().trim();

    
public org.eclipse.core.runtime.IPathgetLocationPath()
Returns the current project location path as entered by the user, or its anticipated initial value. Note that if the default has been returned the path in a project description used to create a project should not be set.

return
the project location path or its anticipated initial value.

        return new Path(getProjectLocation());
    
private java.lang.StringgetLocationPathFieldValue()
Returns the location path field value with spaces trimmed.

        return mLocationPathField == null ? "" : mLocationPathField.getText().trim();
    
public java.lang.StringgetMinSdkVersion()
Returns the value of the min sdk version field with spaces trimmed.

        return mMinSdkVersionField == null ? "" : mMinSdkVersionField.getText().trim();
    
public java.lang.StringgetPackageName()
Returns the value of the package name field with spaces trimmed.

        return mPackageNameField == null ? INITIAL_NAME : mPackageNameField.getText().trim();
    
private org.eclipse.core.resources.IProjectgetProjectHandle()
Creates a project resource handle for the current project name field value.

This method does not create the project resource; this is the responsibility of IProject::create invoked by the new project resource wizard.

return
the new project resource handle

        return ResourcesPlugin.getWorkspace().getRoot().getProject(getProjectName());
    
public java.lang.StringgetProjectLocation()
Returns the current project location, depending on the Use Default Location check box.

        if (isNewProject() && useDefaultLocation()) {
            return Platform.getLocation().toString();
        } else {
            return getLocationPathFieldValue();
        }
    
public java.lang.StringgetProjectName()
Returns the value of the project name field with leading and trailing spaces removed.

        return mProjectNameField == null ? INITIAL_NAME : mProjectNameField.getText().trim();
    
public com.android.sdklib.IAndroidTargetgetSdkTarget()
Returns the current sdk target or null if none has been selected yet.

        return mSdkTargetSelector == null ? null : mSdkTargetSelector.getSelected();
    
public java.lang.StringgetSourceFolder()
Returns the internal source folder (for the "existing project" mode) or the default "src" constant.

        if (isNewProject() || mSourceFolder == null || mSourceFolder.length() == 0) {
            return SdkConstants.FD_SOURCES;
        } else {
            return mSourceFolder;
        }
    
public booleanisCreateActivity()
Returns the value of the "Create Activity" checkbox.

        return mCreateActivityCheck == null ? INITIAL_CREATE_ACTIVITY
                                              : mCreateActivityCheck.getSelection();
    
public booleanisNewProject()
Returns the value of the "Create New Project" radio.

        return mCreateNewProjectRadio == null ? INITIAL_CREATE_NEW_PROJECT
                                              : mCreateNewProjectRadio.getSelection();
    
private voidonActivityNameFieldModified()
The activity name field is either modified internally (from extractNamesFromAndroidManifest) or manually by the user when the custom_location mode is not set. Ignore the internal modification. When modified by the user, memorize the choice and validate the page.

        if (isNewProject() && !mInternalActivityNameUpdate) {
            mUserActivityName = getActivityName();
            setPageComplete(validatePage());
        }
    
private voidonCreateActivityCheckModified()
The create activity checkbox is either modified internally (from extractNamesFromAndroidManifest) or manually by the user. Ignore the internal modification. When modified by the user, memorize the choice and validate the page.

        if (isNewProject() && !mInternalCreateActivityUpdate) {
            mUserCreateActivityCheck = isCreateActivity();
        }
        setPageComplete(validatePage());
    
private voidonLocationPathFieldModified()
The location path field is either modified internally (from updateLocationPathField) or manually by the user when the custom_location mode is not set. Ignore the internal modification. When modified by the user, memorize the choice and validate the page.

        if (!mInternalLocationPathUpdate) {
            // When the updates doesn't come from updateLocationPathField, it must be the user
            // editing the field manually, in which case we want to save the value internally
            // and we disable auto-compute of the custom location (to avoid overriding the user
            // value)
            String newPath = getLocationPathFieldValue();
            sAutoComputeCustomLocation = sAutoComputeCustomLocation &&
                                         newPath.equals(sCustomLocationOsPath);
            sCustomLocationOsPath = newPath;
            extractNamesFromAndroidManifest();
            setPageComplete(validatePage());
        }
    
private voidonMinSdkVersionFieldModified()
Called when the min sdk version field has been modified. Ignore the internal modifications. When modified by the user, try to match a target with the same API level.

        if (mInternalMinSdkVersionUpdate) {
            return;
        }

        try {
            int version = Integer.parseInt(getMinSdkVersion());
            
            // Before changing, compare with the currently selected one, if any.
            // There can be multiple targets with the same sdk api version, so don't change
            // it if it's already at the right version.
            IAndroidTarget curr_target = getSdkTarget();
            if (curr_target != null && curr_target.getApiVersionNumber() == version) {
                return;
            }
            
            for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
                if (target.getApiVersionNumber() == version) {
                    mSdkTargetSelector.setSelection(target);
                    break;
                }
            }
        } catch (NumberFormatException e) {
            // ignore
        }

        mMinSdkVersionModifiedByUser = true;
    
private voidonPackageNameFieldModified()
The package name field is either modified internally (from extractNamesFromAndroidManifest) or manually by the user when the custom_location mode is not set. Ignore the internal modification. When modified by the user, memorize the choice and validate the page.

        if (isNewProject()) {
            mUserPackageName = getPackageName();
            setPageComplete(validatePage());
        }
    
private voidonSdkTargetModified()
Called when an SDK target is modified. If the minSdkVersion field hasn't been modified by the user yet, we change it to reflect the sdk api level that has just been selected.

        IAndroidTarget target = getSdkTarget();
        
        if (target != null && !mMinSdkVersionModifiedByUser) {
            mInternalMinSdkVersionUpdate = true;
            mMinSdkVersionField.setText(Integer.toString(target.getApiVersionNumber()));
            mInternalMinSdkVersionUpdate = false;
        }
    
private voidopenDirectoryBrowser()
Display a directory browser and update the location path field with the selected path


        String existing_dir = getLocationPathFieldValue();

        // Disable the path if it doesn't exist
        if (existing_dir.length() == 0) {
            existing_dir = null;
        } else {
            File f = new File(existing_dir);
            if (!f.exists()) {
                existing_dir = null;
            }
        }

        DirectoryDialog dd = new DirectoryDialog(mLocationPathField.getShell());
        dd.setMessage("Browse for folder");
        dd.setFilterPath(existing_dir);
        String abs_dir = dd.open();

        if (abs_dir != null) {
            updateLocationPathField(abs_dir);
            extractNamesFromAndroidManifest();
            setPageComplete(validatePage());
        }
    
private intsetStatus(java.lang.String message, int messageType)
Sets the error message for the wizard with the given message icon.

param
message The wizard message type, one of MSG_ERROR or MSG_WARNING.
return
As a convenience, always returns messageType so that the caller can return immediately.

        if (message == null) {
            setErrorMessage(null);
            setMessage(null);
        } else if (!message.equals(getMessage())) {
            setMessage(message, messageType == MSG_WARNING ? WizardPage.WARNING : WizardPage.ERROR);
        }
        return messageType;
    
public voidsetVisible(boolean visible)
Overrides @DialogPage.setVisible(boolean) to put the focus in the project name when the dialog is made visible.

        super.setVisible(visible);
        if (visible) {
            mProjectNameField.setFocus();
        }
    
private voidupdateLocationPathField(java.lang.String abs_dir)
Updates the location directory path field.
When custom user selection is enabled, use the abs_dir argument if not null and also save it internally. If abs_dir is null, restore the last saved abs_dir. This allows the user selection to be remembered when the user switches from default to custom.
When custom user selection is disabled, use the workspace default location with the current project name. This does not change the internally cached abs_dir.

param
abs_dir A new absolute directory path or null to use the default.

        boolean is_new_project = isNewProject();
        boolean use_default = useDefaultLocation();
        boolean custom_location = !is_new_project || !use_default;

        if (!mInternalLocationPathUpdate) {
            mInternalLocationPathUpdate = true;
            if (custom_location) {
                if (abs_dir != null) {
                    // We get here if the user selected a directory with the "Browse" button.
                    // Disable auto-compute of the custom location unless the user selected
                    // the exact same path.
                    sAutoComputeCustomLocation = sAutoComputeCustomLocation &&
                                                 abs_dir.equals(sCustomLocationOsPath);
                    sCustomLocationOsPath = TextProcessor.process(abs_dir);
                } else  if (sAutoComputeCustomLocation ||
                            (!is_new_project && !new File(sCustomLocationOsPath).isDirectory())) {
                    // By default select the samples directory of the current target
                    IAndroidTarget target = getSdkTarget();
                    if (target != null) {
                        sCustomLocationOsPath = target.getPath(IAndroidTarget.SAMPLES);
                    }

                    // If we don't have a target, select the base directory of the
                    // "universal sdk". If we don't even have that, use a root drive.
                    if (sCustomLocationOsPath == null || sCustomLocationOsPath.length() == 0) {
                        if (Sdk.getCurrent() != null) {
                            sCustomLocationOsPath = Sdk.getCurrent().getSdkLocation();
                        } else {
                            sCustomLocationOsPath = File.listRoots()[0].getAbsolutePath();
                        }
                    }
                }
                if (!mLocationPathField.getText().equals(sCustomLocationOsPath)) {
                    mLocationPathField.setText(sCustomLocationOsPath);
                }
            } else {
                String value = Platform.getLocation().append(getProjectName()).toString();
                value = TextProcessor.process(value);
                if (!mLocationPathField.getText().equals(value)) {
                    mLocationPathField.setText(value);
                }
            }
            setPageComplete(validatePage());
            mInternalLocationPathUpdate = false;
        }
    
private voidupdatePackageAndActivityFields()
Called when the radio buttons are changed between the "create new project" and the "use existing source" mode. This reverts the fields to whatever the user manually entered before.

        if (isNewProject()) {
            if (mUserPackageName.length() > 0 &&
                    !mPackageNameField.getText().equals(mUserPackageName)) {
                mPackageNameField.setText(mUserPackageName);
            }

            if (mUserActivityName.length() > 0 &&
                    !mActivityNameField.getText().equals(mUserActivityName)) {
                mInternalActivityNameUpdate = true;
                mActivityNameField.setText(mUserActivityName);
                mInternalActivityNameUpdate = false;
            }
            
            if (mUserCreateActivityCheck != mCreateActivityCheck.getSelection()) {
                mInternalCreateActivityUpdate = true;
                mCreateActivityCheck.setSelection(mUserCreateActivityCheck);
                mInternalCreateActivityUpdate = false;
            }
        }
    
public booleanuseDefaultLocation()
Returns the value of the Use Default Location field.

        return mUseDefaultLocation == null ? INITIAL_USE_DEFAULT_LOCATION
                                           : mUseDefaultLocation.getSelection();
    
private intvalidateActivityField()
Validates the activity name field.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.

        // Disregard if not creating an activity
        if (!isCreateActivity()) {
            return MSG_NONE;
        }

        // Validate activity field
        String activityFieldContents = getActivityName();
        if (activityFieldContents.length() == 0) {
            return setStatus("Activity name must be specified.", MSG_ERROR);
        }

        // The activity field can actually contain part of a sub-package name
        // or it can start with a dot "." to indicates it comes from the parent package name.
        String packageName = "";
        int pos = activityFieldContents.lastIndexOf('.");
        if (pos >= 0) {
            packageName = activityFieldContents.substring(0, pos);
            if (packageName.startsWith(".")) { //$NON-NLS-1$
                packageName = packageName.substring(1);
            }
            
            activityFieldContents = activityFieldContents.substring(pos + 1);
        }
        
        // the activity field can contain a simple java identifier, or a
        // package name or one that starts with a dot. So if it starts with a dot,
        // ignore this dot -- the rest must look like a package name.
        if (activityFieldContents.charAt(0) == '.") {
            activityFieldContents = activityFieldContents.substring(1);
        }
        
        // Check it's a valid activity string
        int result = MSG_NONE;
        IStatus status = JavaConventions.validateTypeVariableName(activityFieldContents,
                                                            "1.5", "1.5"); //$NON-NLS-1$ $NON-NLS-2$
        if (!status.isOK()) {
            result = setStatus(status.getMessage(),
                        status.getSeverity() == IStatus.ERROR ? MSG_ERROR : MSG_WARNING);
        }

        // Check it's a valid package string
        if (result != MSG_ERROR && packageName.length() > 0) {
            status = JavaConventions.validatePackageName(packageName,
                                                            "1.5", "1.5"); //$NON-NLS-1$ $NON-NLS-2$
            if (!status.isOK()) {
                result = setStatus(status.getMessage() + " (in the activity name)",
                            status.getSeverity() == IStatus.ERROR ? MSG_ERROR : MSG_WARNING);
            }
        }


        return result;
    
private intvalidateLocationPath(org.eclipse.core.resources.IWorkspace workspace)
Validates the location path field.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.

        Path path = new Path(getProjectLocation());
        if (isNewProject()) {
            if (!useDefaultLocation()) {
                // If not using the default value validate the location.
                URI uri = URIUtil.toURI(path.toOSString());
                IStatus locationStatus = workspace.validateProjectLocationURI(getProjectHandle(),
                        uri);
                if (!locationStatus.isOK()) {
                    return setStatus(locationStatus.getMessage(), MSG_ERROR);
                } else {
                    // The location is valid as far as Eclipse is concerned (i.e. mostly not
                    // an existing workspace project.) Check it either doesn't exist or is
                    // a directory that is empty.
                    File f = path.toFile();
                    if (f.exists() && !f.isDirectory()) {
                        return setStatus("A directory name must be specified.", MSG_ERROR);
                    } else if (f.isDirectory()) {
                        // However if the directory exists, we should put a warning if it is not
                        // empty. We don't put an error (we'll ask the user again for confirmation
                        // before using the directory.)
                        String[] l = f.list();
                        if (l.length != 0) {
                            return setStatus("The selected output directory is not empty.",
                                    MSG_WARNING);
                        }
                    }
                }
            } else {
                // Otherwise validate the path string is not empty
                if (getProjectLocation().length() == 0) {
                    return setStatus("A directory name must be specified.", MSG_ERROR);
                }

                File dest = path.append(getProjectName()).toFile();
                if (dest.exists()) {
                    return setStatus(String.format("There is already a file or directory named \"%1$s\" in the selected location.",
                            getProjectName()), MSG_ERROR);
                }
            }
        } else {
            // Must be an existing directory
            File f = path.toFile();
            if (!f.isDirectory()) {
                return setStatus("An existing directory name must be specified.", MSG_ERROR);
            }
            
            // Check there's an android manifest in the directory
            String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString();
            File manifestFile = new File(osPath);
            if (!manifestFile.isFile()) {
                return setStatus(
                        String.format("File %1$s not found in %2$s.",
                                AndroidConstants.FN_ANDROID_MANIFEST, f.getName()),
                                MSG_ERROR);
            }

            // Parse it and check the important fields.
            AndroidManifestParser manifestData = AndroidManifestParser.parseForData(osPath);
            if (manifestData == null) {
                return setStatus(
                        String.format("File %1$s could not be parsed.", osPath),
                        MSG_ERROR);
            }

            String packageName = manifestData.getPackage();
            if (packageName == null || packageName.length() == 0) {
                return setStatus(
                        String.format("No package name defined in %1$s.", osPath),
                        MSG_ERROR);
            }

            Activity[] activities = manifestData.getActivities();
            if (activities == null || activities.length == 0) {
                // This is acceptable now as long as no activity needs to be created
                if (isCreateActivity()) {
                    return setStatus(
                            String.format("No activity name defined in %1$s.", osPath),
                            MSG_ERROR);
                }
            }

            // If there's already a .project, tell the user to use import instead.
            if (path.append(".project").toFile().exists()) {  //$NON-NLS-1$
                return setStatus("An Eclipse project already exists in this directory. Consider using File > Import > Existing Project instead.",
                        MSG_WARNING);
            }
        }

        return MSG_NONE;
    
private intvalidateMinSdkVersionField()
Validates the sdk target choice.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.


        // If the min sdk version is empty, it is always accepted.
        if (getMinSdkVersion().length() == 0) {
            return MSG_NONE;
        }

        int version = AndroidManifestParser.INVALID_MIN_SDK;
        try {
            // If not empty, it must be a valid integer > 0
            version = Integer.parseInt(getMinSdkVersion());
        } catch (NumberFormatException e) {
            // ignore
        }
        
        if (version < 1) {
            return setStatus("Min SDK Version must be an integer > 0.", MSG_ERROR);
        }
                
        if (getSdkTarget() != null && getSdkTarget().getApiVersionNumber() != version) {
            return setStatus("The API level for the selected SDK target does not match the Min SDK version.",
                    MSG_WARNING);
        }

        return MSG_NONE;
    
private intvalidatePackageField()
Validates the package name field.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.

        // Validate package field
        String packageFieldContents = getPackageName();
        if (packageFieldContents.length() == 0) {
            return setStatus("Package name must be specified.", MSG_ERROR);
        }

        // Check it's a valid package string
        int result = MSG_NONE;
        IStatus status = JavaConventions.validatePackageName(packageFieldContents, "1.5", "1.5"); //$NON-NLS-1$ $NON-NLS-2$
        if (!status.isOK()) {
            result = setStatus(status.getMessage(),
                        status.getSeverity() == IStatus.ERROR ? MSG_ERROR : MSG_WARNING);
        }

        // The Android Activity Manager does not accept packages names with only one
        // identifier. Check the package name has at least one dot in them (the previous rule
        // validated that if such a dot exist, it's not the first nor last characters of the
        // string.)
        if (result != MSG_ERROR && packageFieldContents.indexOf('.") == -1) {
            return setStatus("Package name must have at least two identifiers.", MSG_ERROR);
        }

        return result;
    
protected booleanvalidatePage()
Returns whether this page's controls currently all contain valid values.

return
true if all controls are valid, and false if at least one is invalid

        IWorkspace workspace = ResourcesPlugin.getWorkspace();

        int status = validateProjectField(workspace);
        if ((status & MSG_ERROR) == 0) {
            status |= validateLocationPath(workspace);
        }
        if ((status & MSG_ERROR) == 0) {
            status |= validateSdkTarget();
        }
        if ((status & MSG_ERROR) == 0) {
            status |= validatePackageField();
        }
        if ((status & MSG_ERROR) == 0) {
            status |= validateActivityField();
        }
        if ((status & MSG_ERROR) == 0) {
            status |= validateMinSdkVersionField();
        }
        if ((status & MSG_ERROR) == 0) {
            status |= validateSourceFolder();
        }
        if (status == MSG_NONE)  {
            setStatus(null, MSG_NONE);
        }
        
        // Return false if there's an error so that the finish button be disabled.
        return (status & MSG_ERROR) == 0;
    
private intvalidateProjectField(org.eclipse.core.resources.IWorkspace workspace)
Validates the project name field.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.

        // Validate project field
        String projectFieldContents = getProjectName();
        if (projectFieldContents.length() == 0) {
            return setStatus("Project name must be specified", MSG_ERROR);
        }

        // Limit the project name to shell-agnostic characters since it will be used to
        // generate the final package
        if (!sProjectNamePattern.matcher(projectFieldContents).matches()) {
            return setStatus("The project name must start with an alphanumeric characters, followed by one or more alphanumerics, digits, dots, dashes, underscores or spaces.",
                    MSG_ERROR);
        }

        IStatus nameStatus = workspace.validateName(projectFieldContents, IResource.PROJECT);
        if (!nameStatus.isOK()) {
            return setStatus(nameStatus.getMessage(), MSG_ERROR);
        }

        if (getProjectHandle().exists()) {
            return setStatus("A project with that name already exists in the workspace",
                    MSG_ERROR);
        }

        return MSG_NONE;
    
private intvalidateSdkTarget()
Validates the sdk target choice.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.

        if (getSdkTarget() == null) {
            return setStatus("An SDK Target must be specified.", MSG_ERROR);
        }
        return MSG_NONE;
    
private intvalidateSourceFolder()
Validates that an existing project actually has a source folder. For project in "use existing source" mode, this tries to find the source folder. A source folder should be just under the project directory and it should have all the directories composing the package+activity name. As a side effect, it memorizes the source folder in mSourceFolder. TODO: support multiple source folders for multiple activities.

return
The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.

        // This check does nothing when creating a new project.
        // This check is also useless when no activity is present or created.
        if (isNewProject() || !isCreateActivity()) {
            return MSG_NONE;
        }

        String osTarget = getActivityName();
        
        if (osTarget.indexOf('.") == -1) {
            osTarget = getPackageName() + File.separator + osTarget;
        } else if (osTarget.indexOf('.") == 0) {
            osTarget = getPackageName() + osTarget;
        }
        osTarget = osTarget.replace('.", File.separatorChar) + AndroidConstants.DOT_JAVA;

        String projectPath = getProjectLocation();
        File projectDir = new File(projectPath);
        File[] all_dirs = projectDir.listFiles(new FileFilter() {
            public boolean accept(File pathname) {
                return pathname.isDirectory();
            }
        });
        for (File f : all_dirs) {
            Path path = new Path(f.getAbsolutePath());
            File java_activity = path.append(osTarget).toFile();
            if (java_activity.isFile()) {
                mSourceFolder = f.getName();
                return MSG_NONE;
            }
        }

        if (all_dirs.length > 0) {
            return setStatus(
                    String.format("%1$s can not be found under %2$s.", osTarget, projectPath),
                    MSG_ERROR);
        } else {
            return setStatus(
                    String.format("No source folders can be found in %1$s.", projectPath),
                    MSG_ERROR);
        }