blob: 14f59c00d8afe6e56750172ae25bf61bdb693fe9 [file] [log] [blame]
/*
* 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());
}
}