From 5b5bdeb7e691980a1174650ce926edbaa87a5a3e Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 8 Mar 2012 22:05:18 -0800 Subject: NPW: Find samples in extras. Changes: - the SdkManager can now list extras that contain a "samples" directory. - The NPW uses that to list samples from extras that match the requested API level. The name of the sample in the list also indicates the extra's display name. Change-Id: Id6609f53b9ba84126e36bc33e5675ec6a0284814 --- .../android/ide/eclipse/adt/internal/sdk/Sdk.java | 20 ++++ .../wizards/newproject/NewProjectWizardState.java | 15 ++- .../wizards/newproject/SampleSelectionPage.java | 25 ++--- .../wizards/newproject/SdkSelectionPage.java | 124 +++++++++++++++++++-- 4 files changed, 147 insertions(+), 37 deletions(-) (limited to 'eclipse') diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index 63f381f..a661334 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.sdk; import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML; import static com.android.sdklib.SdkConstants.FD_RES; +import com.android.annotations.NonNull; import com.android.ddmlib.IDevice; import com.android.ide.common.rendering.LayoutLibrary; import com.android.ide.common.sdk.LoadStatus; @@ -303,6 +304,25 @@ public final class Sdk { } /** + * Returns a new {@link SdkManager} that can parse the SDK located + * at the current {@link #getSdkLocation()}. + *

+ * Implementation detail: The {@link Sdk} has its own internal manager with + * a custom logger which is not designed to be useful for outsiders. Callers + * who need their own {@link SdkManager} for parsing will often want to control + * the logger for their own need. + *

+ * This is just a convenient method equivalent to writing: + *

SdkManager.createManager(Sdk.getCurrent().getSdkLocation(), log);
+ * + * @param log The logger for the {@link SdkManager}. + * @return A new {@link SdkManager} parsing the same location. + */ + public @NonNull SdkManager getNewSdkManager(@NonNull ISdkLog log) { + return SdkManager.createManager(getSdkLocation(), log); + } + + /** * Returns the URL to the local documentation. * Can return null if no documentation is found in the current SDK. * diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java index 33f9884..264f09d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java @@ -26,6 +26,7 @@ import com.android.sdklib.internal.project.ProjectProperties.PropertyType; import com.android.sdklib.xml.AndroidManifest; import com.android.sdklib.xml.ManifestData; import com.android.sdklib.xml.ManifestData.Activity; +import com.android.util.Pair; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Path; @@ -94,14 +95,12 @@ public class NewProjectWizardState { public String minSdk; /** True if the minimum SDK version has been manually edited by the user */ public boolean minSdkModifiedByUser; - /** - * The directory where the samples are found. This field is only applicable - * when the wizard is running in create-sample-mode. - */ - public File samplesDir; - /** A list of paths to each of the available samples for the current SDK */ - public List samples = new ArrayList(); + * A list of paths to each of the available samples for the current SDK. + * The pair is (String: sample display name => File: sample directory). + * Note we want a list, not a map since we might have duplicates. + * */ + public List> samples = new ArrayList>(); /** Path to the currently chosen sample */ public File chosenSample; @@ -396,4 +395,4 @@ public class NewProjectWizardState { */ ANY; } -} \ No newline at end of file +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SampleSelectionPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SampleSelectionPage.java index d54697f..ef16d3d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SampleSelectionPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SampleSelectionPage.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.wizards.newproject; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode; import com.android.sdklib.IAndroidTarget; +import com.android.util.Pair; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; @@ -43,7 +44,6 @@ import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; import java.io.File; -import java.util.regex.Pattern; /** Page where the user can select a sample to "instantiate" */ class SampleSelectionPage extends WizardPage implements SelectionListener, ModifyListener { @@ -125,21 +125,11 @@ class SampleSelectionPage extends WizardPage implements SelectionListener, Modif @Override public String getText(Object element) { - File file = (File) element; - String path = file.getPath(); - int n = mValues.samplesDir.getPath().length(); - 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 (element instanceof Pair) { + Object name = ((Pair) element).getFirst(); + return name.toString(); } - - return path; + return element.toString(); // Fallback. Should not happen. } }; @@ -151,7 +141,7 @@ class SampleSelectionPage extends WizardPage implements SelectionListener, Modif mTableViewer.setInput(samples); mTable.select(0); - selectSample(mValues.samples.get(0)); + selectSample(mValues.samples.get(0).getSecond()); extractNamesFromAndroidManifest(); } } @@ -170,6 +160,7 @@ class SampleSelectionPage extends WizardPage implements SelectionListener, Modif } } + @SuppressWarnings("unchecked") @Override public void widgetSelected(SelectionEvent e) { if (mIgnore) { @@ -181,7 +172,7 @@ class SampleSelectionPage extends WizardPage implements SelectionListener, Modif int index = mTable.getSelectionIndex(); if (index >= 0) { Object[] roots = (Object[]) mTableViewer.getInput(); - selectSample((File) roots[index]); + selectSample(((Pair) roots[index]).getSecond()); } else { selectSample(null); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SdkSelectionPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SdkSelectionPage.java index 11d0e95..2713f01 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SdkSelectionPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/SdkSelectionPage.java @@ -15,17 +15,25 @@ */ package com.android.ide.eclipse.adt.internal.wizards.newproject; +import com.android.annotations.Nullable; import com.android.ide.common.sdk.LoadStatus; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode; +import com.android.io.FileWrapper; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.NullSdkLog; import com.android.sdklib.SdkConstants; +import com.android.sdklib.SdkManager; +import com.android.sdklib.xml.AndroidManifestParser; +import com.android.sdklib.xml.ManifestData; import com.android.sdkuilib.internal.widgets.SdkTargetSelector; +import com.android.util.Pair; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; @@ -38,7 +46,11 @@ import org.eclipse.swt.widgets.Group; import java.io.File; import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; /** A page in the New Project wizard where you select the target SDK */ class SdkSelectionPage extends WizardPage implements ITargetChangeListener { @@ -196,21 +208,43 @@ class SdkSelectionPage extends WizardPage implements ITargetChangeListener { // Get the sample root path and recompute the list of samples String samplesRootPath = target.getPath(IAndroidTarget.SAMPLES); - mValues.samplesDir = new File(samplesRootPath); - findSamplesManifests(mValues.samplesDir, mValues.samples); + File root = new File(samplesRootPath); + findSamplesManifests(root, root, null, null, mValues.samples); + + Sdk sdk = Sdk.getCurrent(); + if (sdk != null) { + // Parse the extras to see if we can find samples that are + // compatible with the selected target API. + // First we need an SdkManager that suppresses all output. + SdkManager sdkman = sdk.getNewSdkManager(new NullSdkLog()); + + Map extras = sdkman.getExtraSamples(); + for (Entry entry : extras.entrySet()) { + File path = entry.getKey(); + String name = entry.getValue(); + findSamplesManifests(path, path, name, + target.getVersion(), + mValues.samples); + } + } - if (mValues.samples.size() == 0) { + if (mValues.samples.isEmpty()) { return; } else { - Collections.sort(mValues.samples); + Collections.sort(mValues.samples, new Comparator>() { + @Override + public int compare(Pair o1, Pair o2) { + // Compare the display name of the sample + return o1.getFirst().compareTo(o2.getFirst()); + } + }); } - // Recompute the description of each sample (the relative path - // to the sample root). Also try to find the old selection. + // Try to find the old selection. if (previouslyChosenSample != null) { String previouslyChosenName = previouslyChosenSample.getName(); for (int i = 0, n = mValues.samples.size(); i < n; i++) { - File file = mValues.samples.get(i); + File file = mValues.samples.get(i).getSecond(); if (file.getName().equals(previouslyChosenName)) { mValues.chosenSample = file; break; @@ -224,18 +258,61 @@ class SdkSelectionPage extends WizardPage implements ITargetChangeListener { * 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. + * + * @param rootDir The "samples" root directory. Doesn't change during recursion. + * @param currDir The directory being scanned. Caller must initially set it to {@code rootDir}. + * @param extraName Optional name appended to the samples display name. Typically used to + * indicate a sample comes from a given extra package. + * @param targetVersion Optional target version filter. If non null, only samples that are + * compatible with the given target will be listed. + * @param samplesPaths A non-null list filled by this method with all samples found. The + * pair is (String: sample display name => File: sample directory). */ - private void findSamplesManifests(File samplesDir, List samplesPaths) { - if (!samplesDir.isDirectory()) { + private void findSamplesManifests( + File rootDir, + File currDir, + @Nullable String extraName, + @Nullable AndroidVersion targetVersion, + List> samplesPaths) { + if (!currDir.isDirectory()) { return; } - for (File f : samplesDir.listFiles()) { + for (File f : currDir.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); + try { + ManifestData data = + AndroidManifestParser.parse(new FileWrapper(manifestFile)); + if (data != null) { + boolean accept = false; + if (targetVersion == null) { + accept = true; + } else if (targetVersion != null) { + int i = data.getMinSdkVersion(); + if (i != ManifestData.MIN_SDK_CODENAME) { + accept = i <= targetVersion.getApiLevel(); + } else { + String s = data.getMinSdkVersionString(); + if (s != null) { + accept = s.equals(targetVersion.getCodename()); + } + } + } + + if (accept) { + String name = getSampleDisplayName(extraName, rootDir, f); + samplesPaths.add(Pair.of(name, f)); + } + } + } catch (Exception e) { + // Ignore. Don't use a sample which manifest doesn't parse correctly. + AdtPlugin.log(IStatus.INFO, + "NPW ignoring malformed manifest %s", //$NON-NLS-1$ + manifestFile.getAbsolutePath()); + } } // Recurse in the project, to find embedded tests sub-projects @@ -245,12 +322,35 @@ class SdkSelectionPage extends WizardPage implements ITargetChangeListener { if (!SdkConstants.FD_SOURCES.equals(leaf) && !SdkConstants.FD_ASSETS.equals(leaf) && !SdkConstants.FD_RES.equals(leaf)) { - findSamplesManifests(f, samplesPaths); + findSamplesManifests(rootDir, f, extraName, targetVersion, samplesPaths); } } } } + /** + * Compute the sample name compared to its root directory. + */ + private String getSampleDisplayName(String extraName, File rootDir, File sampleDir) { + String path = sampleDir.getPath(); + int n = rootDir.getPath().length(); + 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), " > "); //$NON-NLS-1$ + } + if (extraName != null) { + path = path + " [" + extraName + ']'; //$NON-NLS-1$ + } + + return path; + } + private void validatePage() { String error = null; -- cgit v1.1