Merge change I8e4697e1 into eclair
* changes:
New layout optimization tool. Run layoutopt on the command line.
diff --git a/tools/eclipse/changes.txt b/tools/eclipse/changes.txt
index 5805681..0c653c3 100644
--- a/tools/eclipse/changes.txt
+++ b/tools/eclipse/changes.txt
@@ -1,3 +1,6 @@
+0.9.4:
+- New "Create project from sample" choice in the New Project Wizard.
+
0.9.3:
- New wizard to create Android JUnit Test Projects.
- New AVD wizard.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
index 8a12e19..ae86f48 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
@@ -464,11 +464,11 @@
/**
* Returns whether the configuration is a match for the given reference config.
- * <p/>A match means that:
+ * <p/>A match means that, for each qualifier of this config
* <ul>
- * <li>This config does not use any qualifier not used by the reference config</li>
- * <li>The qualifier used by this config have the same values as the qualifiers of
- * the reference config.</li>
+ * <li>The reference config has no value set
+ * <li>or, the qualifier of the reference config is a match. Depending on the qualifier type
+ * this does not mean the same exact value.</li>
* </ul>
* @param referenceConfig The reference configuration to test against.
* @return true if the configuration matches.
@@ -478,14 +478,10 @@
ResourceQualifier testQualifier = mQualifiers[i];
ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i];
- // we only care if testQualifier is non null.
- if (testQualifier != null) {
- if (referenceQualifier == null) { // reference config doesn't specify anything
- // for this qualifier so we refuse it.
- return false;
- } else if (testQualifier.isMatchFor(referenceQualifier) == false) {
- return false;
- }
+ // it's only a non match if both qualifiers are non-null, and they don't match.
+ if (testQualifier != null && referenceQualifier != null &&
+ testQualifier.isMatchFor(referenceQualifier) == false) {
+ return false;
}
}
return true;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
index 9d715d0..9052672 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
@@ -542,66 +542,69 @@
for (int q = 0 ; q < count ; q++) {
// look to see if one resource has this qualifier.
// At the same time also record the best match value for the qualifier (if applicable).
+
+ // The reference value, to find the best match.
+ // Note that this qualifier could be null. In which case any qualifier found in the
+ // possible match, will all be considered best match.
ResourceQualifier referenceQualifier = referenceConfig.getQualifier(q);
- if (referenceQualifier != null) { // no need to check if it's null, since the loop
- // above will have removed the resources anyway.
- boolean found = false;
- ResourceQualifier bestMatch = null;
- for (Resource res : matchingResources) {
- ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
- if (qualifier != null) {
- // set the flag.
- found = true;
+ boolean found = false;
+ ResourceQualifier bestMatch = null; // this is to store the best match.
+ for (Resource res : matchingResources) {
+ ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
+ if (qualifier != null) {
+ // set the flag.
+ found = true;
- // now check for a best match.
+ // Now check for a best match. If the reference qualifier is null ,
+ // any qualifier is a "best" match (we don't need to record all of them.
+ // Instead the non compatible ones are removed below)
+ if (referenceQualifier != null) {
if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
bestMatch = qualifier;
}
}
}
+ }
- // if a resources as a qualifier at the current index, remove all the resources that
- // do not have one.
- // If there is one, and we have a bestComparable, also check that it's equal to the
- // best comparable.
- if (found) {
- for (int i = 0 ; i < matchingResources.size(); ) {
- Resource res = matchingResources.get(i);
- ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
+ // 4. If a resources has a qualifier at the current index, remove all the resources that
+ // do not have one, or whose qualifier value does not equal the best match found above
+ // unless there's no reference qualifier, in which case they are all considered
+ // "best" match.
+ if (found) {
+ for (int i = 0 ; i < matchingResources.size(); ) {
+ Resource res = matchingResources.get(i);
+ ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
- if (qualifier == null) { // no qualifier? remove the resources
- matchingResources.remove(res);
- } else if (bestMatch != null && bestMatch.equals(qualifier) == false) {
- // if there is a best match, only accept the resource if the qualifier
- // has the same best value.
- matchingResources.remove(res);
- } else {
- i++;
- }
+ if (qualifier == null) {
+ // this resources has no qualifier of this type: rejected.
+ matchingResources.remove(res);
+ } else if (referenceQualifier != null && bestMatch != null &&
+ bestMatch.equals(qualifier) == false) {
+ // there's a reference qualifier and there is a better match for it than
+ // this resource, so we reject it.
+ matchingResources.remove(res);
+ } else {
+ // looks like we keep this resource, move on to the next one.
+ i++;
}
+ }
- // at this point we may have run out of matching resources before going
- // through all the qualifiers.
- if (matchingResources.size() == 1) {
- return matchingResources.get(0);
- } else if (matchingResources.size() == 0) {
- return null;
- }
+ // at this point we may have run out of matching resources before going
+ // through all the qualifiers.
+ if (matchingResources.size() < 2) {
+ break;
}
}
}
- // went through all the qualifiers. We should not have more than one
- switch (matchingResources.size()) {
- case 0:
- return null;
- case 1:
- return matchingResources.get(1);
- case 2:
- assert false;
+ // Because we accept resources whose configuration have qualifiers where the reference
+ // configuration doesn't, we can end up with more than one match. In this case, we just
+ // take the first one.
+ if (matchingResources.size() == 0) {
+ return null;
}
- return null;
+ return matchingResources.get(1);
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
index 5067111..8d5cf27 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
@@ -54,6 +54,7 @@
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Event;
@@ -65,6 +66,7 @@
import java.io.File;
import java.io.FileFilter;
import java.net.URI;
+import java.util.ArrayList;
import java.util.regex.Pattern;
/**
@@ -90,9 +92,12 @@
/** Initial value for all name fields (project, activity, application, package). Used
* whenever a value is requested before controls are created. */
private static final String INITIAL_NAME = ""; //$NON-NLS-1$
- /** Initial value for the Create New Project radio; False means Create From Existing would be
- * the default.*/
+ /** Initial value for the Create New Project radio. */
private static final boolean INITIAL_CREATE_NEW_PROJECT = true;
+ /** Initial value for the Create Project From Sample. */
+ private static final boolean INITIAL_CREATE_FROM_SAMPLE = false;
+ /** Initial value for the Create Project From Existing Source. */
+ private static final boolean INITIAL_CREATE_FROM_SOURCE = false;
/** Initial value for the Use Default Location check box. */
private static final boolean INITIAL_USE_DEFAULT_LOCATION = true;
/** Initial value for the Create Activity check box. */
@@ -127,6 +132,7 @@
private Text mActivityNameField;
private Text mApplicationNameField;
private Button mCreateNewProjectRadio;
+ private Button mCreateFromSampleRadio;
private Button mUseDefaultLocation;
private Label mLocationLabel;
private Text mLocationPathField;
@@ -145,6 +151,10 @@
private boolean mApplicationNameModifiedByUser;
private boolean mInternalMinSdkVersionUpdate;
+ private final ArrayList<String> mSamplesPaths = new ArrayList<String>();
+ private Combo mSamplesCombo;
+
+
/**
* Creates a new project creation wizard page.
@@ -249,6 +259,12 @@
: mCreateNewProjectRadio.getSelection();
}
+ /** Returns the value of the "Create from Existing Sample" radio. */
+ public boolean isCreateFromSample() {
+ return mCreateFromSampleRadio == null ? INITIAL_CREATE_FROM_SAMPLE
+ : mCreateFromSampleRadio.getSelection();
+ }
+
/** Returns the value of the "Create Activity" checkbox. */
public boolean isCreateActivity() {
return mCreateActivityCheck == null ? INITIAL_CREATE_ACTIVITY
@@ -330,6 +346,7 @@
// Update state the first time
enableLocationWidgets();
+ loadSamplesForTarget(null /*target*/);
// Show description the first time
setErrorMessage(null);
@@ -407,9 +424,10 @@
mCreateNewProjectRadio = new Button(group, SWT.RADIO);
mCreateNewProjectRadio.setText("Create new project in workspace");
mCreateNewProjectRadio.setSelection(INITIAL_CREATE_NEW_PROJECT);
+
Button existing_project_radio = new Button(group, SWT.RADIO);
existing_project_radio.setText("Create project from existing source");
- existing_project_radio.setSelection(!INITIAL_CREATE_NEW_PROJECT);
+ existing_project_radio.setSelection(INITIAL_CREATE_FROM_SOURCE);
mUseDefaultLocation = new Button(group, SWT.CHECK);
mUseDefaultLocation.setText("Use default location");
@@ -462,6 +480,33 @@
onOpenDirectoryBrowser();
}
});
+
+ mCreateFromSampleRadio = new Button(group, SWT.RADIO);
+ mCreateFromSampleRadio.setText("Create project from existing sample");
+ mCreateFromSampleRadio.setSelection(INITIAL_CREATE_FROM_SAMPLE);
+ mCreateFromSampleRadio.addSelectionListener(location_listener);
+
+ Composite samples_group = new Composite(group, SWT.NONE);
+ samples_group.setLayout(new GridLayout(2, /* num columns */
+ false /* columns of not equal size */));
+ samples_group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ samples_group.setFont(parent.getFont());
+
+ new Label(samples_group, SWT.NONE).setText("Samples:");
+
+ mSamplesCombo = new Combo(samples_group, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mSamplesCombo.setEnabled(false);
+ mSamplesCombo.select(0);
+ mSamplesCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSamplesCombo.setToolTipText("Select a sample");
+
+ mSamplesCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onSampleSelected();
+ }
+ });
+
}
/**
@@ -625,9 +670,22 @@
return mLocationPathField == null ? "" : mLocationPathField.getText().trim(); //$NON-NLS-1$
}
- /** Returns the current project location, depending on the Use Default Location check box. */
+ /** Returns the current selected sample path,
+ * or an empty string if there's no valid selection. */
+ private String getSelectedSamplePath() {
+ int selIndex = mSamplesCombo.getSelectionIndex();
+ if (selIndex >= 0 && selIndex < mSamplesPaths.size()) {
+ return mSamplesPaths.get(selIndex);
+ }
+ return "";
+ }
+
+ /** Returns the current project location, depending on the Use Default Location check box
+ * or the Create From Sample check box. */
private String getProjectLocation() {
- if (mInfo.isNewProject() && mInfo.useDefaultLocation()) {
+ if (mInfo.isCreateFromSample()) {
+ return getSelectedSamplePath();
+ } else if (mInfo.isNewProject() && mInfo.useDefaultLocation()) {
return Platform.getLocation().toString();
} else {
return getLocationPathFieldValue();
@@ -681,14 +739,25 @@
}
/**
+ * A sample was selected. Update the location field, manifest and validate.
+ */
+ private void onSampleSelected() {
+ // Note that getProjectLocation() is automatically updated to use the currently
+ // selected sample. We just need to refresh the manifest data & validate.
+ extractNamesFromAndroidManifest();
+ validatePageComplete();
+ }
+
+ /**
* Enables or disable the location widgets depending on the user selection:
* the location path is enabled when using the "existing source" mode (i.e. not new project)
* or in new project mode with the "use default location" turned off.
*/
private void enableLocationWidgets() {
boolean is_new_project = mInfo.isNewProject();
- boolean use_default = mInfo.useDefaultLocation();
- boolean location_enabled = !is_new_project || !use_default;
+ boolean is_create_from_sample = mInfo.isCreateFromSample();
+ boolean use_default = mInfo.useDefaultLocation() && !is_create_from_sample;
+ boolean location_enabled = (!is_new_project || !use_default) && !is_create_from_sample;
boolean create_activity = mInfo.isCreateActivity();
mUseDefaultLocation.setEnabled(is_new_project);
@@ -697,6 +766,8 @@
mLocationPathField.setEnabled(location_enabled);
mBrowseButton.setEnabled(location_enabled);
+ mSamplesCombo.setEnabled(is_create_from_sample && mSamplesPaths.size() > 0);
+
mPackageNameField.setEnabled(is_new_project);
mCreateActivityCheck.setEnabled(is_new_project);
mActivityNameField.setEnabled(is_new_project & create_activity);
@@ -718,6 +789,12 @@
* @param abs_dir A new absolute directory path or null to use the default.
*/
private void updateLocationPathField(String abs_dir) {
+
+ // We don't touch the location path if using the "Create From Sample" mode
+ if (mInfo.isCreateFromSample()) {
+ return;
+ }
+
boolean is_new_project = mInfo.isNewProject();
boolean use_default = mInfo.useDefaultLocation();
boolean custom_location = !is_new_project || !use_default;
@@ -872,6 +949,10 @@
mMinSdkVersionField.setText(target.getVersion().getApiString());
mInternalMinSdkVersionUpdate = false;
}
+
+ loadSamplesForTarget(target);
+ enableLocationWidgets();
+ onSampleSelected();
}
/**
@@ -969,15 +1050,15 @@
String[] ids = activityName.split(AndroidConstants.RE_DOT);
activityName = ids[ids.length - 1];
}
- if (mProjectNameField.getText().length() == 0 ||
- !mProjectNameModifiedByUser) {
+ if (mProjectNameField.getText().length() == 0 || !mProjectNameModifiedByUser) {
mInternalProjectNameUpdate = true;
+ mProjectNameModifiedByUser = false;
mProjectNameField.setText(activityName);
mInternalProjectNameUpdate = false;
}
- if (mApplicationNameField.getText().length() == 0 ||
- !mApplicationNameModifiedByUser) {
+ if (mApplicationNameField.getText().length() == 0 || !mApplicationNameModifiedByUser) {
mInternalApplicationNameUpdate = true;
+ mApplicationNameModifiedByUser = false;
mApplicationNameField.setText(activityName);
mInternalApplicationNameUpdate = false;
}
@@ -1004,8 +1085,7 @@
// For the project name, remove any dots
packageName = packageName.replace('.', '_');
- if (mProjectNameField.getText().length() == 0 ||
- !mProjectNameModifiedByUser) {
+ if (mProjectNameField.getText().length() == 0 || !mProjectNameModifiedByUser) {
mInternalProjectNameUpdate = true;
mProjectNameField.setText(packageName);
mInternalProjectNameUpdate = false;
@@ -1015,7 +1095,8 @@
}
// Select the target matching the manifest's sdk or build properties, if any
- boolean foundTarget = false;
+ IAndroidTarget foundTarget = null;
+ IAndroidTarget currentTarget = mInfo.getSdkTarget();
ProjectProperties p = ProjectProperties.create(projectLocation, null);
if (p != null) {
@@ -1023,33 +1104,43 @@
p.merge(PropertyType.BUILD).merge(PropertyType.DEFAULT);
String v = p.getProperty(ProjectProperties.PROPERTY_TARGET);
IAndroidTarget target = Sdk.getCurrent().getTargetFromHashString(v);
- if (target != null) {
- mSdkTargetSelector.setSelection(target);
- foundTarget = true;
+ // We can change the current target if:
+ // - we found a new target
+ // - there is no current target
+ // - there is a current target but the new target is not <= to the current one.
+ if (target != null &&
+ (currentTarget == null || !target.isCompatibleBaseFor(currentTarget))) {
+ foundTarget = target;
}
}
- if (!foundTarget && minSdkVersion != null) {
+ if (foundTarget == null && minSdkVersion != null) {
+ // Otherwise try to match the requested sdk version
for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
- if (target.getVersion().equals(minSdkVersion)) {
- mSdkTargetSelector.setSelection(target);
- foundTarget = true;
+ if (target != null &&
+ target.getVersion().equals(minSdkVersion) &&
+ (currentTarget == null || !target.isCompatibleBaseFor(currentTarget))) {
+ foundTarget = target;
break;
}
}
}
- if (!foundTarget) {
+ if (foundTarget == null) {
+ // Or last attemp, try to match a sample project location
for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
- if (projectLocation.startsWith(target.getLocation())) {
- mSdkTargetSelector.setSelection(target);
- foundTarget = true;
+ if (target != null &&
+ projectLocation.startsWith(target.getLocation()) &&
+ (currentTarget == null || !target.isCompatibleBaseFor(currentTarget))) {
+ foundTarget = target;
break;
}
}
}
- if (!foundTarget) {
+ if (foundTarget != null) {
+ mSdkTargetSelector.setSelection(foundTarget);
+ } else {
mInternalMinSdkVersionUpdate = true;
if (minSdkVersion != null) {
mMinSdkVersionField.setText(minSdkVersion);
@@ -1059,6 +1150,104 @@
}
/**
+ * Updates the list of all samples for the given target SDK.
+ * The list is stored in mSamplesPaths as absolute directory paths.
+ * The combo is recreated to match this.
+ */
+ private void loadSamplesForTarget(IAndroidTarget target) {
+
+ // Keep the name of the old selection (if there were any samples)
+ String oldChoice = null;
+ if (mSamplesPaths.size() > 0) {
+ int selIndex = mSamplesCombo.getSelectionIndex();
+ if (selIndex > -1) {
+ oldChoice = mSamplesCombo.getItem(selIndex);
+ }
+ }
+
+ // Clear all current content
+ mSamplesCombo.removeAll();
+ mSamplesPaths.clear();
+
+ if (target != null) {
+ // Get the sample root path and recompute the list of samples
+ String samplesRootPath = target.getPath(IAndroidTarget.SAMPLES);
+
+ File samplesDir = new File(samplesRootPath);
+ findSamplesManifests(samplesDir, mSamplesPaths);
+
+ if (mSamplesPaths.size() == 0) {
+ // Odd, this target has no samples. Could happen with an addon.
+ mSamplesCombo.add("This target has no samples. Please select another target.");
+ mSamplesCombo.select(0);
+ return;
+ }
+
+ // Recompute the description of each sample (the relative path
+ // to the sample root). Also try to find the old selection.
+ int selIndex = 0;
+ int i = 0;
+ int n = samplesRootPath.length();
+ for (String path : mSamplesPaths) {
+ if (path.length() > n) {
+ path = path.substring(n);
+ if (path.charAt(0) == File.separatorChar) {
+ path = path.substring(1);
+ }
+ if (path.endsWith(File.separator)) {
+ path = path.substring(0, path.length() - 1);
+ }
+ path = path.replaceAll(Pattern.quote(File.separator), " > ");
+ }
+
+ if (oldChoice != null && oldChoice.equals(path)) {
+ selIndex = i;
+ }
+
+ mSamplesCombo.add(path);
+ i++;
+ }
+
+ mSamplesCombo.select(selIndex);
+
+ } else {
+ mSamplesCombo.add("Please select a target.");
+ mSamplesCombo.select(0);
+ }
+ }
+
+ /**
+ * Recursively find potential sample directories under the given directory.
+ * Actually lists any directory that contains an android manifest.
+ * Paths found are added the samplesPaths list.
+ */
+ private void findSamplesManifests(File samplesDir, ArrayList<String> samplesPaths) {
+ if (!samplesDir.isDirectory()) {
+ return;
+ }
+
+ for (File f : samplesDir.listFiles()) {
+ if (f.isDirectory()) {
+ // Assume this is a sample if it contains an android manifest.
+ File manifestFile = new File(f, SdkConstants.FN_ANDROID_MANIFEST_XML);
+ if (manifestFile.isFile()) {
+ samplesPaths.add(f.getPath());
+ }
+
+ // Recurse in the project, to find embedded tests sub-projects
+ // We can however skip this recursion for known android sub-dirs that
+ // can't have projects, namely for sources, assets and resources.
+ String leaf = f.getName();
+ if (!SdkConstants.FD_SOURCES.equals(leaf) &&
+ !SdkConstants.FD_ASSETS.equals(leaf) &&
+ !SdkConstants.FD_RES.equals(leaf)) {
+ findSamplesManifests(f, samplesPaths);
+ }
+ }
+ }
+ }
+
+ /**
* Returns whether this page's controls currently all contain valid values.
*
* @return <code>true</code> if all controls are valid, and
@@ -1069,10 +1258,10 @@
int status = validateProjectField(workspace);
if ((status & MSG_ERROR) == 0) {
- status |= validateLocationPath(workspace);
+ status |= validateSdkTarget();
}
if ((status & MSG_ERROR) == 0) {
- status |= validateSdkTarget();
+ status |= validateLocationPath(workspace);
}
if ((status & MSG_ERROR) == 0) {
status |= validatePackageField();
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
index 227f23e..d96534b 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
@@ -22,6 +22,7 @@
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
@@ -83,7 +84,7 @@
private String mSkinDisplay;
private boolean mEnableScaling = true;
- protected AvdStartDialog(Shell parentShell, AvdInfo avd, String sdkLocation,
+ AvdStartDialog(Shell parentShell, AvdInfo avd, String sdkLocation,
SettingsController settingsController) {
super(parentShell);
mAvd = avd;
@@ -111,7 +112,7 @@
}
@Override
- protected Control createDialogArea(Composite parent) {
+ protected Control createDialogArea(final Composite parent) {
GridData gd;
// create a composite with standard margins and spacing
@@ -150,7 +151,7 @@
scaleGroup.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
gd.horizontalIndent = 30;
gd.horizontalSpan = 2;
- scaleGroup.setLayout(new GridLayout(2, false));
+ scaleGroup.setLayout(new GridLayout(3, false));
l = new Label(scaleGroup, SWT.NONE);
l.setText("Screen Size (in):");
@@ -172,12 +173,16 @@
onScaleChange();
}
});
+ // empty composite, only 2 widgets on this line.
+ new Composite(scaleGroup, SWT.NONE).setLayoutData(gd = new GridData());
+ gd.widthHint = gd.heightHint = 0;
l = new Label(scaleGroup, SWT.NONE);
l.setText("Monitor dpi:");
mMonitorDpi = new Text(scaleGroup, SWT.BORDER);
mMonitorDpi.setText(Integer.toString(getMonitorDpi()));
- mMonitorDpi.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mMonitorDpi.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.widthHint = 50;
mMonitorDpi.addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent event) {
// check for digit only.
@@ -196,6 +201,19 @@
}
});
+ Button button = new Button(scaleGroup, SWT.PUSH | SWT.FLAT);
+ button.setText("?");
+ button.setToolTipText("Click to figure out your monitor's pixel density");
+ button.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent arg0) {
+ ResolutionChooserDialog dialog = new ResolutionChooserDialog(parent.getShell());
+ if (dialog.open() == Window.OK) {
+ mMonitorDpi.setText(Integer.toString(dialog.getDensity()));
+ }
+ }
+ });
+
scaleGroup.setEnabled(defaultState);
mScaleButton.addSelectionListener(new SelectionAdapter() {
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ResolutionChooserDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ResolutionChooserDialog.java
new file mode 100644
index 0000000..94ed3b9
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ResolutionChooserDialog.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.sdkuilib.internal.widgets;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Small dialog to let a user choose a screen size (from a fixed list) and a resolution
+ * (as returned by {@link Display#getMonitors()}).
+
+ * After the dialog as returned, one can query {@link #getDensity()} to get the chosen monitor
+ * pixel density.
+ */
+class ResolutionChooserDialog extends Dialog {
+ public final static float[] MONITOR_SIZES = new float[] {
+ 13.3f, 14, 15.4f, 15.6f, 17, 19, 20, 21, 24, 30,
+ };
+
+ private Button mButton;
+ private Combo mScreenSizeCombo;
+ private Combo mMonitorCombo;
+
+ private Monitor[] mMonitors;
+ private int mScreenSizeIndex = -1;
+ private int mMonitorIndex = 0;
+
+ ResolutionChooserDialog(Shell parentShell) {
+ super(parentShell);
+ }
+
+ /**
+ * Returns the pixel density of the user-chosen monitor.
+ */
+ int getDensity() {
+ float size = MONITOR_SIZES[mScreenSizeIndex];
+ Rectangle rect = mMonitors[mMonitorIndex].getBounds();
+
+ // compute the density
+ double d = Math.sqrt(rect.width * rect.width + rect.height * rect.height) / size;
+ return (int)Math.round(d);
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ newShell.setText("Monitor Density");
+ super.configureShell(newShell);
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Control control = super.createContents(parent);
+ mButton = getButton(IDialogConstants.OK_ID);
+ mButton.setEnabled(false);
+ return control;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // create a composite with standard margins and spacing
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+ layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+ layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+ layout.horizontalSpacing = convertHorizontalDLUsToPixels(
+ IDialogConstants.HORIZONTAL_SPACING);
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Label l = new Label(composite, SWT.NONE);
+ l.setText("Screen Size:");
+
+ mScreenSizeCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
+ for (float size : MONITOR_SIZES) {
+ if (Math.round(size) == size) {
+ mScreenSizeCombo.add(String.format("%.0f\"", size));
+ } else {
+ mScreenSizeCombo.add(String.format("%.1f\"", size));
+ }
+ }
+ mScreenSizeCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent arg0) {
+ mScreenSizeIndex = mScreenSizeCombo.getSelectionIndex();
+ mButton.setEnabled(mScreenSizeIndex != -1);
+ }
+ });
+
+ l = new Label(composite, SWT.NONE);
+ l.setText("Resolution:");
+
+ mMonitorCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mMonitors = parent.getDisplay().getMonitors();
+ for (Monitor m : mMonitors) {
+ Rectangle r = m.getBounds();
+ mMonitorCombo.add(String.format("%d x %d", r.width, r.height));
+ }
+ mMonitorCombo.select(mMonitorIndex);
+ mMonitorCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent arg0) {
+ mMonitorIndex = mMonitorCombo.getSelectionIndex();
+ }
+ });
+
+ applyDialogFont(composite);
+ return composite;
+ }
+}