aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java931
1 files changed, 931 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java
new file mode 100644
index 000000000..14f59c00d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.wizards.templates;
+
+
+import static com.android.SdkConstants.ATTR_ID;
+import static com.android.ide.eclipse.adt.AdtUtils.extractClassName;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.NewTemplatePage.WIZARD_PAGE_WIDTH;
+
+import com.android.annotations.Nullable;
+import com.android.sdklib.SdkVersionInfo;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.ApplicationInfoPage;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.ProjectNamePage;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * First wizard page in the "New Project From Template" wizard
+ */
+public class NewProjectPage extends WizardPage
+ implements ModifyListener, SelectionListener, FocusListener {
+ private static final int FIELD_WIDTH = 300;
+ private static final String SAMPLE_PACKAGE_PREFIX = "com.example."; //$NON-NLS-1$
+ /** Suffix added by default to activity names */
+ static final String ACTIVITY_NAME_SUFFIX = "Activity"; //$NON-NLS-1$
+ /** Prefix added to default layout names */
+ static final String LAYOUT_NAME_PREFIX = "activity_"; //$NON-NLS-1$
+ private static final int INITIAL_MIN_SDK = 8;
+
+ private final NewProjectWizardState mValues;
+ private Map<String, Integer> mMinNameToApi;
+ private Parameter mThemeParameter;
+ private Combo mThemeCombo;
+
+ private Text mProjectText;
+ private Text mPackageText;
+ private Text mApplicationText;
+ private Combo mMinSdkCombo;
+ private Combo mTargetSdkCombo;
+ private Combo mBuildSdkCombo;
+ private Label mHelpIcon;
+ private Label mTipLabel;
+
+ private boolean mIgnore;
+ private ControlDecoration mApplicationDec;
+ private ControlDecoration mProjectDec;
+ private ControlDecoration mPackageDec;
+ private ControlDecoration mBuildTargetDec;
+ private ControlDecoration mMinSdkDec;
+ private ControlDecoration mTargetSdkDec;
+ private ControlDecoration mThemeDec;
+
+ NewProjectPage(NewProjectWizardState values) {
+ super("newAndroidApp"); //$NON-NLS-1$
+ mValues = values;
+ setTitle("New Android Application");
+ setDescription("Creates a new Android Application");
+ }
+
+ @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused
+ @Override
+ public void createControl(Composite parent) {
+ Composite container = new Composite(parent, SWT.NULL);
+ setControl(container);
+ GridLayout gl_container = new GridLayout(4, false);
+ gl_container.horizontalSpacing = 10;
+ container.setLayout(gl_container);
+
+ Label applicationLabel = new Label(container, SWT.NONE);
+ applicationLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ applicationLabel.setText("Application Name:");
+
+ mApplicationText = new Text(container, SWT.BORDER);
+ GridData gdApplicationText = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1);
+ gdApplicationText.widthHint = FIELD_WIDTH;
+ mApplicationText.setLayoutData(gdApplicationText);
+ mApplicationText.addModifyListener(this);
+ mApplicationText.addFocusListener(this);
+ mApplicationDec = createFieldDecoration(mApplicationText,
+ "The application name is shown in the Play Store, as well as in the " +
+ "Manage Application list in Settings.");
+
+ Label projectLabel = new Label(container, SWT.NONE);
+ projectLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ projectLabel.setText("Project Name:");
+ mProjectText = new Text(container, SWT.BORDER);
+ GridData gdProjectText = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1);
+ gdProjectText.widthHint = FIELD_WIDTH;
+ mProjectText.setLayoutData(gdProjectText);
+ mProjectText.addModifyListener(this);
+ mProjectText.addFocusListener(this);
+ mProjectDec = createFieldDecoration(mProjectText,
+ "The project name is only used by Eclipse, but must be unique within the " +
+ "workspace. This can typically be the same as the application name.");
+
+ Label packageLabel = new Label(container, SWT.NONE);
+ packageLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ packageLabel.setText("Package Name:");
+
+ mPackageText = new Text(container, SWT.BORDER);
+ GridData gdPackageText = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1);
+ gdPackageText.widthHint = FIELD_WIDTH;
+ mPackageText.setLayoutData(gdPackageText);
+ mPackageText.addModifyListener(this);
+ mPackageText.addFocusListener(this);
+ mPackageDec = createFieldDecoration(mPackageText,
+ "The package name must be a unique identifier for your application.\n" +
+ "It is typically not shown to users, but it *must* stay the same " +
+ "for the lifetime of your application; it is how multiple versions " +
+ "of the same application are considered the \"same app\".\nThis is " +
+ "typically the reverse domain name of your organization plus one or " +
+ "more application identifiers, and it must be a valid Java package " +
+ "name.");
+ new Label(container, SWT.NONE);
+
+ new Label(container, SWT.NONE);
+ new Label(container, SWT.NONE);
+ new Label(container, SWT.NONE);
+
+ // Min SDK
+
+ Label minSdkLabel = new Label(container, SWT.NONE);
+ minSdkLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ minSdkLabel.setText("Minimum Required SDK:");
+
+ mMinSdkCombo = new Combo(container, SWT.READ_ONLY);
+ GridData gdMinSdkCombo = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ gdMinSdkCombo.widthHint = FIELD_WIDTH;
+ mMinSdkCombo.setLayoutData(gdMinSdkCombo);
+
+ // Pick most recent platform
+ IAndroidTarget[] targets = getCompilationTargets();
+ mMinNameToApi = Maps.newHashMap();
+ List<String> targetLabels = new ArrayList<String>(targets.length);
+ for (IAndroidTarget target : targets) {
+ String targetLabel;
+ if (target.isPlatform()
+ && target.getVersion().getApiLevel() <= AdtUtils.getHighestKnownApiLevel()) {
+ targetLabel = AdtUtils.getAndroidName(target.getVersion().getApiLevel());
+ } else {
+ targetLabel = AdtUtils.getTargetLabel(target);
+ }
+ targetLabels.add(targetLabel);
+ mMinNameToApi.put(targetLabel, target.getVersion().getApiLevel());
+ }
+
+ List<String> codeNames = Lists.newArrayList();
+ int buildTargetIndex = -1;
+ for (int i = 0, n = targets.length; i < n; i++) {
+ IAndroidTarget target = targets[i];
+ AndroidVersion version = target.getVersion();
+ int apiLevel = version.getApiLevel();
+ if (version.isPreview()) {
+ String codeName = version.getCodename();
+ String targetLabel = codeName + " Preview";
+ codeNames.add(targetLabel);
+ mMinNameToApi.put(targetLabel, apiLevel);
+ } else if (target.isPlatform()
+ && (mValues.target == null ||
+ apiLevel > mValues.target.getVersion().getApiLevel())) {
+ mValues.target = target;
+ buildTargetIndex = i;
+ }
+ }
+ List<String> labels = new ArrayList<String>(24);
+ for (String label : AdtUtils.getKnownVersions()) {
+ labels.add(label);
+ }
+ assert labels.size() >= 15; // *Known* versions to ADT, not installed/available versions
+ for (String codeName : codeNames) {
+ labels.add(codeName);
+ }
+ String[] versions = labels.toArray(new String[labels.size()]);
+ mMinSdkCombo.setItems(versions);
+ if (mValues.target != null && mValues.target.getVersion().isPreview()) {
+ mValues.minSdk = mValues.target.getVersion().getCodename();
+ mMinSdkCombo.setText(mValues.minSdk);
+ mValues.iconState.minSdk = mValues.target.getVersion().getApiLevel();
+ mValues.minSdkLevel = mValues.iconState.minSdk;
+ } else {
+ mMinSdkCombo.select(INITIAL_MIN_SDK - 1);
+ mValues.minSdk = Integer.toString(INITIAL_MIN_SDK);
+ mValues.minSdkLevel = INITIAL_MIN_SDK;
+ mValues.iconState.minSdk = INITIAL_MIN_SDK;
+ }
+ mMinSdkCombo.addSelectionListener(this);
+ mMinSdkCombo.addFocusListener(this);
+ mMinSdkDec = createFieldDecoration(mMinSdkCombo,
+ "Choose the lowest version of Android that your application will support. Lower " +
+ "API levels target more devices, but means fewer features are available. By " +
+ "targeting API 8 and later, you reach approximately 95% of the market.");
+ new Label(container, SWT.NONE);
+
+ // Target SDK
+ Label targetSdkLabel = new Label(container, SWT.NONE);
+ targetSdkLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ targetSdkLabel.setText("Target SDK:");
+
+ mTargetSdkCombo = new Combo(container, SWT.READ_ONLY);
+ GridData gdTargetSdkCombo = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ gdTargetSdkCombo.widthHint = FIELD_WIDTH;
+ mTargetSdkCombo.setLayoutData(gdTargetSdkCombo);
+
+ mTargetSdkCombo.setItems(versions);
+ mTargetSdkCombo.select(mValues.targetSdkLevel - 1);
+
+ mTargetSdkCombo.addSelectionListener(this);
+ mTargetSdkCombo.addFocusListener(this);
+ mTargetSdkDec = createFieldDecoration(mTargetSdkCombo,
+ "Choose the highest API level that the application is known to work with. " +
+ "This attribute informs the system that you have tested against the target " +
+ "version and the system should not enable any compatibility behaviors to " +
+ "maintain your app's forward-compatibility with the target version. " +
+ "The application is still able to run on older versions " +
+ "(down to minSdkVersion). Your application may look dated if you are not " +
+ "targeting the current version.");
+ new Label(container, SWT.NONE);
+
+ // Build Version
+
+ Label buildSdkLabel = new Label(container, SWT.NONE);
+ buildSdkLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ buildSdkLabel.setText("Compile With:");
+
+ mBuildSdkCombo = new Combo(container, SWT.READ_ONLY);
+ GridData gdBuildSdkCombo = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ gdBuildSdkCombo.widthHint = FIELD_WIDTH;
+ mBuildSdkCombo.setLayoutData(gdBuildSdkCombo);
+ mBuildSdkCombo.setData(targets);
+ mBuildSdkCombo.setItems(targetLabels.toArray(new String[targetLabels.size()]));
+ if (buildTargetIndex != -1) {
+ mBuildSdkCombo.select(buildTargetIndex);
+ }
+
+ mBuildSdkCombo.addSelectionListener(this);
+ mBuildSdkCombo.addFocusListener(this);
+ mBuildTargetDec = createFieldDecoration(mBuildSdkCombo,
+ "Choose a target API to compile your code against, from your installed SDKs. " +
+ "This is typically the most recent version, or the first version that supports " +
+ "all the APIs you want to directly access without reflection.");
+ new Label(container, SWT.NONE);
+
+ TemplateMetadata metadata = mValues.template.getTemplate();
+ if (metadata != null) {
+ mThemeParameter = metadata.getParameter("baseTheme"); //$NON-NLS-1$
+ if (mThemeParameter != null && mThemeParameter.element != null) {
+ Label themeLabel = new Label(container, SWT.NONE);
+ themeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
+ themeLabel.setText("Theme:");
+
+ mThemeCombo = NewTemplatePage.createOptionCombo(mThemeParameter, container,
+ mValues.parameters, this, this);
+ GridData gdThemeCombo = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ gdThemeCombo.widthHint = FIELD_WIDTH;
+ mThemeCombo.setLayoutData(gdThemeCombo);
+ new Label(container, SWT.NONE);
+
+ mThemeDec = createFieldDecoration(mThemeCombo,
+ "Choose the base theme to use for the application");
+ }
+ }
+
+ new Label(container, SWT.NONE);
+ new Label(container, SWT.NONE);
+ new Label(container, SWT.NONE);
+ new Label(container, SWT.NONE);
+
+ Label label = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
+ label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 4, 1));
+
+ mHelpIcon = new Label(container, SWT.NONE);
+ mHelpIcon.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));
+ Image icon = IconFactory.getInstance().getIcon("quickfix");
+ mHelpIcon.setImage(icon);
+ mHelpIcon.setVisible(false);
+
+ mTipLabel = new Label(container, SWT.WRAP);
+ mTipLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
+
+ // Reserve space for 4 lines
+ mTipLabel.setText("\n\n\n\n"); //$NON-NLS-1$
+
+ // Reserve enough width to accommodate the various wizard pages up front
+ // (since they are created lazily, and we don't want the wizard to dynamically
+ // resize itself for small size adjustments as each successive page is slightly
+ // larger)
+ Label dummy = new Label(container, SWT.NONE);
+ GridData data = new GridData();
+ data.horizontalSpan = 4;
+ data.widthHint = WIZARD_PAGE_WIDTH;
+ dummy.setLayoutData(data);
+ }
+
+ /**
+ * Updates the theme selection such that it's valid for the current build
+ * and min sdk targets. Also runs {@link #validatePage} in case no valid entry was found.
+ * Does nothing if called on a template that does not supply a theme.
+ */
+ void updateTheme() {
+ if (mThemeParameter != null) {
+ // Pick the highest theme version that works for the current SDK level
+ Parameter parameter = NewTemplatePage.getParameter(mThemeCombo);
+ assert parameter == mThemeParameter;
+ if (parameter != null) {
+ String[] optionIds = (String[]) mThemeCombo.getData(ATTR_ID);
+ for (int index = optionIds.length - 1; index >= 0; index--) {
+ IStatus status = NewTemplatePage.validateCombo(null, mThemeParameter,
+ index, mValues.minSdkLevel, mValues.getBuildApi());
+ if (status == null || status.isOK()) {
+ String optionId = optionIds[index];
+ parameter.value = optionId;
+ parameter.edited = optionId != null && !optionId.toString().isEmpty();
+ mValues.parameters.put(parameter.id, optionId);
+ try {
+ mIgnore = true;
+ mThemeCombo.select(index);
+ } finally {
+ mIgnore = false;
+ }
+ break;
+ }
+ }
+ }
+
+ validatePage();
+ }
+ }
+
+ private IAndroidTarget[] getCompilationTargets() {
+ Sdk current = Sdk.getCurrent();
+ if (current == null) {
+ return new IAndroidTarget[0];
+ }
+ IAndroidTarget[] targets = current.getTargets();
+ List<IAndroidTarget> list = new ArrayList<IAndroidTarget>();
+
+ for (IAndroidTarget target : targets) {
+ if (target.isPlatform() == false &&
+ (target.getOptionalLibraries() == null ||
+ target.getOptionalLibraries().length == 0)) {
+ continue;
+ }
+ list.add(target);
+ }
+
+ return list.toArray(new IAndroidTarget[list.size()]);
+ }
+
+ private ControlDecoration createFieldDecoration(Control control, String description) {
+ ControlDecoration dec = new ControlDecoration(control, SWT.LEFT);
+ dec.setMarginWidth(2);
+ FieldDecoration errorFieldIndicator = FieldDecorationRegistry.getDefault().
+ getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);
+ dec.setImage(errorFieldIndicator.getImage());
+ dec.setDescriptionText(description);
+ control.setToolTipText(description);
+
+ return dec;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+
+ // DURING DEVELOPMENT ONLY
+ //if (assertionsEnabled()) {
+ // String uniqueProjectName = AdtUtils.getUniqueProjectName("Test", "");
+ // mProjectText.setText(uniqueProjectName);
+ // mPackageText.setText("test.pkg");
+ //}
+
+ validatePage();
+ }
+
+ // ---- Implements ModifyListener ----
+
+ @Override
+ public void modifyText(ModifyEvent e) {
+ if (mIgnore) {
+ return;
+ }
+
+ Object source = e.getSource();
+ if (source == mProjectText) {
+ mValues.projectName = mProjectText.getText();
+ updateProjectLocation(mValues.projectName);
+ mValues.projectModified = true;
+
+ try {
+ mIgnore = true;
+ if (!mValues.applicationModified) {
+ mValues.applicationName = mValues.projectName;
+ mApplicationText.setText(mValues.projectName);
+ }
+ updateActivityNames(mValues.projectName);
+ } finally {
+ mIgnore = false;
+ }
+ suggestPackage(mValues.projectName);
+ } else if (source == mPackageText) {
+ mValues.packageName = mPackageText.getText();
+ mValues.packageModified = true;
+ } else if (source == mApplicationText) {
+ mValues.applicationName = mApplicationText.getText();
+ mValues.applicationModified = true;
+
+ try {
+ mIgnore = true;
+ if (!mValues.projectModified) {
+ mValues.projectName = appNameToProjectName(mValues.applicationName);
+ mProjectText.setText(mValues.projectName);
+ updateProjectLocation(mValues.projectName);
+ }
+ updateActivityNames(mValues.applicationName);
+ } finally {
+ mIgnore = false;
+ }
+ suggestPackage(mValues.applicationName);
+ }
+
+ validatePage();
+ }
+
+ private String appNameToProjectName(String appName) {
+ // Strip out whitespace (and capitalize subsequent words where spaces were removed
+ boolean upcaseNext = false;
+ StringBuilder sb = new StringBuilder(appName.length());
+ for (int i = 0, n = appName.length(); i < n; i++) {
+ char c = appName.charAt(i);
+ if (c == ' ') {
+ upcaseNext = true;
+ } else if (upcaseNext) {
+ sb.append(Character.toUpperCase(c));
+ upcaseNext = false;
+ } else {
+ sb.append(c);
+ }
+ }
+
+ appName = sb.toString().trim();
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IStatus nameStatus = workspace.validateName(appName, IResource.PROJECT);
+ if (nameStatus.isOK()) {
+ return appName;
+ }
+
+ sb = new StringBuilder(appName.length());
+ for (int i = 0, n = appName.length(); i < n; i++) {
+ char c = appName.charAt(i);
+ if (Character.isLetterOrDigit(c) || c == '.' || c == '-') {
+ sb.append(c);
+ }
+ }
+
+ return sb.toString().trim();
+ }
+
+ /** If the project should be created in the workspace, then update the project location
+ * based on the project name. */
+ private void updateProjectLocation(String projectName) {
+ if (projectName == null) {
+ projectName = "";
+ }
+
+ if (mValues.useDefaultLocation) {
+ IPath workspace = Platform.getLocation();
+ String projectLocation = workspace.append(projectName).toOSString();
+ mValues.projectLocation = projectLocation;
+ }
+ }
+
+ private void updateActivityNames(String name) {
+ try {
+ mIgnore = true;
+ if (!mValues.activityNameModified) {
+ mValues.activityName = extractClassName(name) + ACTIVITY_NAME_SUFFIX;
+ }
+ if (!mValues.activityTitleModified) {
+ mValues.activityTitle = name;
+ }
+ } finally {
+ mIgnore = false;
+ }
+ }
+
+ // ---- Implements SelectionListener ----
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (mIgnore) {
+ return;
+ }
+
+ Object source = e.getSource();
+ if (source == mMinSdkCombo) {
+ mValues.minSdk = getSelectedMinSdk();
+ Integer minSdk = mMinNameToApi.get(mValues.minSdk);
+ if (minSdk == null) {
+ try {
+ minSdk = Integer.parseInt(mValues.minSdk);
+ } catch (NumberFormatException nufe) {
+ // If not a number, then the string is a codename, so treat it
+ // as a preview version.
+ minSdk = SdkVersionInfo.HIGHEST_KNOWN_API + 1;
+ }
+ }
+ mValues.iconState.minSdk = minSdk.intValue();
+ mValues.minSdkLevel = minSdk.intValue();
+
+ // If higher than build target, adjust build target
+ if (mValues.minSdkLevel > mValues.getBuildApi()) {
+ // Try to find a build target with an adequate build API
+ IAndroidTarget[] targets = (IAndroidTarget[]) mBuildSdkCombo.getData();
+ IAndroidTarget best = null;
+ int bestApi = Integer.MAX_VALUE;
+ int bestTargetIndex = -1;
+ for (int i = 0; i < targets.length; i++) {
+ IAndroidTarget target = targets[i];
+ if (!target.isPlatform()) {
+ continue;
+ }
+ int api = target.getVersion().getApiLevel();
+ if (api >= mValues.minSdkLevel && api < bestApi) {
+ best = target;
+ bestApi = api;
+ bestTargetIndex = i;
+ }
+ }
+
+ if (best != null) {
+ assert bestTargetIndex != -1;
+ mValues.target = best;
+ try {
+ mIgnore = true;
+ mBuildSdkCombo.select(bestTargetIndex);
+ } finally {
+ mIgnore = false;
+ }
+ }
+ }
+
+ // If higher than targetSdkVersion, adjust targetSdkVersion
+ if (mValues.minSdkLevel > mValues.targetSdkLevel) {
+ mValues.targetSdkLevel = mValues.minSdkLevel;
+ try {
+ mIgnore = true;
+ setSelectedTargetSdk(mValues.targetSdkLevel);
+ } finally {
+ mIgnore = false;
+ }
+ }
+ } else if (source == mBuildSdkCombo) {
+ mValues.target = getSelectedBuildTarget();
+
+ // If lower than min sdk target, adjust min sdk target
+ if (mValues.target.getVersion().isPreview()) {
+ mValues.minSdk = mValues.target.getVersion().getCodename();
+ try {
+ mIgnore = true;
+ mMinSdkCombo.setText(mValues.minSdk);
+ } finally {
+ mIgnore = false;
+ }
+ } else {
+ String minSdk = mValues.minSdk;
+ int buildApiLevel = mValues.target.getVersion().getApiLevel();
+ if (minSdk != null && !minSdk.isEmpty()
+ && Character.isDigit(minSdk.charAt(0))
+ && buildApiLevel < Integer.parseInt(minSdk)) {
+ mValues.minSdk = Integer.toString(buildApiLevel);
+ try {
+ mIgnore = true;
+ setSelectedMinSdk(buildApiLevel);
+ } finally {
+ mIgnore = false;
+ }
+ }
+ }
+ } else if (source == mTargetSdkCombo) {
+ mValues.targetSdkLevel = getSelectedTargetSdk();
+ }
+
+ validatePage();
+ }
+
+ private String getSelectedMinSdk() {
+ // If you're using a preview build, such as android-JellyBean, you have
+ // to use the codename, e.g. JellyBean, as the minimum SDK as well.
+ IAndroidTarget buildTarget = getSelectedBuildTarget();
+ if (buildTarget != null && buildTarget.getVersion().isPreview()) {
+ return buildTarget.getVersion().getCodename();
+ }
+
+ // +1: First API level (at index 0) is 1
+ return Integer.toString(mMinSdkCombo.getSelectionIndex() + 1);
+ }
+
+ private int getSelectedTargetSdk() {
+ // +1: First API level (at index 0) is 1
+ return mTargetSdkCombo.getSelectionIndex() + 1;
+ }
+
+ private void setSelectedMinSdk(int api) {
+ mMinSdkCombo.select(api - 1); // -1: First API level (at index 0) is 1
+ }
+
+ private void setSelectedTargetSdk(int api) {
+ mTargetSdkCombo.select(api - 1); // -1: First API level (at index 0) is 1
+ }
+
+ @Nullable
+ private IAndroidTarget getSelectedBuildTarget() {
+ IAndroidTarget[] targets = (IAndroidTarget[]) mBuildSdkCombo.getData();
+ int index = mBuildSdkCombo.getSelectionIndex();
+ if (index >= 0 && index < targets.length) {
+ return targets[index];
+ } else {
+ return null;
+ }
+ }
+
+ private void suggestPackage(String original) {
+ if (!mValues.packageModified) {
+ // Create default package name
+ StringBuilder sb = new StringBuilder();
+ sb.append(SAMPLE_PACKAGE_PREFIX);
+ appendPackage(sb, original);
+
+ String pkg = sb.toString();
+ if (pkg.endsWith(".")) { //$NON-NLS-1$
+ pkg = pkg.substring(0, pkg.length() - 1);
+ }
+ mValues.packageName = pkg;
+ try {
+ mIgnore = true;
+ mPackageText.setText(mValues.packageName);
+ } finally {
+ mIgnore = false;
+ }
+ }
+ }
+
+ private static void appendPackage(StringBuilder sb, String string) {
+ for (int i = 0, n = string.length(); i < n; i++) {
+ char c = string.charAt(i);
+ if (i == 0 && Character.isJavaIdentifierStart(c)
+ || i != 0 && Character.isJavaIdentifierPart(c)) {
+ sb.append(Character.toLowerCase(c));
+ } else if ((c == '.')
+ && (sb.length() > 0 && sb.charAt(sb.length() - 1) != '.')) {
+ sb.append('.');
+ } else if (c == '-') {
+ sb.append('_');
+ }
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ // ---- Implements FocusListener ----
+
+ @Override
+ public void focusGained(FocusEvent e) {
+ Object source = e.getSource();
+ String tip = "";
+ if (source == mApplicationText) {
+ tip = mApplicationDec.getDescriptionText();
+ } else if (source == mProjectText) {
+ tip = mProjectDec.getDescriptionText();
+ } else if (source == mBuildSdkCombo) {
+ tip = mBuildTargetDec.getDescriptionText();
+ } else if (source == mMinSdkCombo) {
+ tip = mMinSdkDec.getDescriptionText();
+ } else if (source == mPackageText) {
+ tip = mPackageDec.getDescriptionText();
+ if (mPackageText.getText().startsWith(SAMPLE_PACKAGE_PREFIX)) {
+ int length = SAMPLE_PACKAGE_PREFIX.length();
+ if (mPackageText.getText().length() > length
+ && SAMPLE_PACKAGE_PREFIX.endsWith(".")) { //$NON-NLS-1$
+ length--;
+ }
+ mPackageText.setSelection(0, length);
+ }
+ } else if (source == mTargetSdkCombo) {
+ tip = mTargetSdkDec.getDescriptionText();
+ } else if (source == mThemeCombo) {
+ tip = mThemeDec.getDescriptionText();
+ }
+ mTipLabel.setText(tip);
+ mHelpIcon.setVisible(tip.length() > 0);
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ mTipLabel.setText("");
+ mHelpIcon.setVisible(false);
+ }
+
+ // Validation
+
+ private void validatePage() {
+ IStatus status = mValues.template.validateTemplate(mValues.minSdkLevel,
+ mValues.getBuildApi());
+ if (status != null && !status.isOK()) {
+ updateDecorator(mApplicationDec, null, true);
+ updateDecorator(mPackageDec, null, true);
+ updateDecorator(mProjectDec, null, true);
+ updateDecorator(mThemeDec, null, true);
+ /* These never get marked with errors:
+ updateDecorator(mBuildTargetDec, null, true);
+ updateDecorator(mMinSdkDec, null, true);
+ updateDecorator(mTargetSdkDec, null, true);
+ */
+ } else {
+ IStatus appStatus = validateAppName();
+ if (appStatus != null && (status == null
+ || appStatus.getSeverity() > status.getSeverity())) {
+ status = appStatus;
+ }
+
+ IStatus projectStatus = validateProjectName();
+ if (projectStatus != null && (status == null
+ || projectStatus.getSeverity() > status.getSeverity())) {
+ status = projectStatus;
+ }
+
+ IStatus packageStatus = validatePackageName();
+ if (packageStatus != null && (status == null
+ || packageStatus.getSeverity() > status.getSeverity())) {
+ status = packageStatus;
+ }
+
+ IStatus locationStatus = ProjectContentsPage.validateLocationInWorkspace(mValues);
+ if (locationStatus != null && (status == null
+ || locationStatus.getSeverity() > status.getSeverity())) {
+ status = locationStatus;
+ }
+
+ if (status == null || status.getSeverity() != IStatus.ERROR) {
+ if (mValues.target == null) {
+ status = new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
+ "Select an Android build target version");
+ }
+ }
+
+ if (status == null || status.getSeverity() != IStatus.ERROR) {
+ if (mValues.minSdk == null || mValues.minSdk.isEmpty()) {
+ status = new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
+ "Select a minimum SDK version");
+ } else {
+ AndroidVersion version = mValues.target.getVersion();
+ if (version.isPreview()) {
+ if (version.getCodename().equals(mValues.minSdk) == false) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "Preview platforms require the min SDK version to match their codenames.");
+ }
+ } else if (mValues.target.getVersion().compareTo(
+ mValues.minSdkLevel,
+ version.isPreview() ? mValues.minSdk : null) < 0) {
+ status = new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
+ "The minimum SDK version is higher than the build target version");
+ }
+ if (status == null || status.getSeverity() != IStatus.ERROR) {
+ if (mValues.targetSdkLevel < mValues.minSdkLevel) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "The target SDK version should be at least as high as the minimum SDK version");
+ }
+ }
+ }
+ }
+
+ IStatus themeStatus = validateTheme();
+ if (themeStatus != null && (status == null
+ || themeStatus.getSeverity() > status.getSeverity())) {
+ status = themeStatus;
+ }
+ }
+
+ setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
+ if (status != null) {
+ setMessage(status.getMessage(),
+ status.getSeverity() == IStatus.ERROR
+ ? IMessageProvider.ERROR : IMessageProvider.WARNING);
+ } else {
+ setErrorMessage(null);
+ setMessage(null);
+ }
+ }
+
+ private IStatus validateAppName() {
+ String appName = mValues.applicationName;
+ IStatus status = null;
+ if (appName == null || appName.isEmpty()) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "Enter an application name (shown in launcher)");
+ } else if (Character.isLowerCase(mValues.applicationName.charAt(0))) {
+ status = new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
+ "The application name for most apps begins with an uppercase letter");
+ }
+
+ updateDecorator(mApplicationDec, status, true);
+
+ return status;
+ }
+
+ private IStatus validateProjectName() {
+ IStatus status = ProjectNamePage.validateProjectName(mValues.projectName);
+ updateDecorator(mProjectDec, status, true);
+
+ return status;
+ }
+
+ private IStatus validatePackageName() {
+ IStatus status;
+ if (mValues.packageName == null || mValues.packageName.startsWith(SAMPLE_PACKAGE_PREFIX)) {
+ if (mValues.packageName != null
+ && !mValues.packageName.equals(SAMPLE_PACKAGE_PREFIX)) {
+ status = ApplicationInfoPage.validatePackage(mValues.packageName);
+ if (status == null || status.isOK()) {
+ status = new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
+ String.format("The prefix '%1$s' is meant as a placeholder and should " +
+ "not be used", SAMPLE_PACKAGE_PREFIX));
+ }
+ } else {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "Package name must be specified.");
+ }
+ } else {
+ status = ApplicationInfoPage.validatePackage(mValues.packageName);
+ }
+
+ updateDecorator(mPackageDec, status, true);
+
+ return status;
+ }
+
+ private IStatus validateTheme() {
+ IStatus status = null;
+
+ if (mThemeParameter != null) {
+ status = NewTemplatePage.validateCombo(null, mThemeParameter,
+ mThemeCombo.getSelectionIndex(), mValues.minSdkLevel,
+ mValues.getBuildApi());
+
+ updateDecorator(mThemeDec, status, true);
+ }
+
+ return status;
+ }
+
+ private void updateDecorator(ControlDecoration decorator, IStatus status, boolean hasInfo) {
+ if (hasInfo) {
+ int severity = status != null ? status.getSeverity() : IStatus.OK;
+ setDecoratorType(decorator, severity);
+ } else {
+ if (status == null || status.isOK()) {
+ decorator.hide();
+ } else {
+ decorator.show();
+ }
+ }
+ }
+
+ private void setDecoratorType(ControlDecoration decorator, int severity) {
+ String id;
+ if (severity == IStatus.ERROR) {
+ id = FieldDecorationRegistry.DEC_ERROR;
+ } else if (severity == IStatus.WARNING) {
+ id = FieldDecorationRegistry.DEC_WARNING;
+ } else {
+ id = FieldDecorationRegistry.DEC_INFORMATION;
+ }
+ FieldDecoration errorFieldIndicator = FieldDecorationRegistry.getDefault().
+ getFieldDecoration(id);
+ decorator.setImage(errorFieldIndicator.getImage());
+ }
+}