| /* |
| * 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()); |
| } |
| } |