/* * 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.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.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; import lombok.ast.libs.org.parboiled.google.collect.Lists; /** * 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) { minSdk = 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()); } }