SDK Updater: support sample packages.

TODO: needs an icon and scanning /samples folders not matching
any current platform (in next CL)

BUG: 2384690
Change-Id: I07d55a8e1ff897bde10c475050d0e18ae3ca7da8
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
index 0dec3bb..af83e9c 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
@@ -35,7 +35,8 @@
 /**

  * Represents an add-on XML node in an SDK repository.

  */

-public class AddonPackage extends Package implements IPackageVersion {

+public class AddonPackage extends Package

+    implements IPackageVersion, IPlatformDependency {

 

     private static final String PROP_NAME      = "Addon.Name";      //$NON-NLS-1$

     private static final String PROP_VENDOR    = "Addon.Vendor";    //$NON-NLS-1$

@@ -177,7 +178,11 @@
         return mName;

     }

 

-    /** Returns the version, for platform, add-on and doc packages. */

+    /**

+     * Returns the version of the platform dependency of this package.

+     * <p/>

+     * An add-on has the same {@link AndroidVersion} as the platform it depends on.

+     */

     public AndroidVersion getVersion() {

         return mVersion;

     }

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
index ef5d2c3..8765905 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
@@ -37,8 +37,7 @@
 

 /**

  * A {@link Archive} is the base class for "something" that can be downloaded from

- * the SDK repository -- subclasses include {@link PlatformPackage}, {@link AddonPackage},

- * {@link DocPackage} and {@link ToolPackage}.

+ * the SDK repository.

  * <p/>

  * A package has some attributes (revision, description) and a list of archives

  * which represent the downloadable bits.

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
index edc4276..b91d7bd 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
@@ -31,6 +31,10 @@
 

 /**

  * Represents a doc XML node in an SDK repository.

+ * <p/>

+ * Note that a doc package has a version and thus implements {@link IPackageVersion}.

+ * However there is no mandatory dependency that limits installation so this does not

+ * implement {@link IPlatformDependency}.

  */

 public class DocPackage extends Package implements IPackageVersion {

 

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
index faa7c26..e62586a 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
@@ -31,7 +31,8 @@
 /**

  * Represents a extra XML node in an SDK repository.

  */

-public class ExtraPackage extends MinToolsPackage {

+public class ExtraPackage extends MinToolsPackage

+    implements IMinApiLevelDependency {

 

     private static final String PROP_PATH          = "Extra.Path";         //$NON-NLS-1$

     private static final String PROP_MIN_API_LEVEL = "Extra.MinApiLevel";  //$NON-NLS-1$

@@ -51,12 +52,6 @@
     private final int mMinApiLevel;

 

     /**

-     * The value of {@link #mMinApiLevel} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}

-     * was not specified in the XML source.

-     */

-    public static final int MIN_API_LEVEL_NOT_SPECIFIED = 0;

-

-    /**

      * Creates a new tool package from the attributes and elements of the given XML node.

      * <p/>

      * This constructor should throw an exception if the package cannot be created.

@@ -104,10 +99,6 @@
             getProperty(props, PROP_MIN_API_LEVEL, Integer.toString(MIN_API_LEVEL_NOT_SPECIFIED)));

     }

 

-    public int getMinApiLevel() {

-        return mMinApiLevel;

-    }

-

     /**

      * Save the properties of the current packages in the given {@link Properties} object.

      * These properties will later be give the constructor that takes a {@link Properties} object.

@@ -124,6 +115,14 @@
     }

 

     /**

+     * Returns the minimal API level required by this extra package, if > 0,

+     * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement.

+     */

+    public int getMinApiLevel() {

+        return mMinApiLevel;

+    }

+

+    /**

      * Static helper to check if a given path is acceptable for an "extra" package.

      */

     public boolean isPathValid() {

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinApiLevelDependency.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinApiLevelDependency.java
new file mode 100755
index 0000000..76c1955
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinApiLevelDependency.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 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.sdklib.internal.repository;
+
+import com.android.sdklib.repository.SdkRepository;
+
+/**
+ * Interface used to decorate a {@link Package} that has a dependency
+ * on a minimal API level, e.g. which XML has a <code>&lt;min-api-level&gt;</code> element.
+ * <p/>
+ * A package that has this dependency can only be installed if a platform with at least the
+ * requested API level is present or installed at the same time.
+ */
+public interface IMinApiLevelDependency {
+
+    /**
+     * The value of {@link #getMinApiLevel()} when the {@link SdkRepository#NODE_MIN_API_LEVEL}
+     * was not specified in the XML source.
+     */
+    public static final int MIN_API_LEVEL_NOT_SPECIFIED = 0;
+
+    /**
+     * Returns the minimal API level required by this extra package, if > 0,
+     * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement.
+     */
+    public abstract int getMinApiLevel();
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinToolsDependency.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinToolsDependency.java
new file mode 100755
index 0000000..669806b
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinToolsDependency.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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.sdklib.internal.repository;
+
+import com.android.sdklib.repository.SdkRepository;
+
+/**
+ * Interface used to decorate a {@link Package} that has a dependency
+ * on a minimal tools revision, e.g. which XML has a <code>&lt;min-tools-rev&gt;</code> element.
+ * <p/>
+ * A package that has this dependency can only be installed if the requested tools revision
+ * is present or installed at the same time.
+ */
+public interface IMinToolsDependency {
+
+    /**
+     * The value of {@link #getMinToolsRevision()} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}
+     * was not specified in the XML source.
+     */
+    public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;
+
+    /**
+     * The minimal revision of the tools package required by this extra package if > 0,
+     * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
+     */
+    public abstract int getMinToolsRevision();
+
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPackageVersion.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPackageVersion.java
index 46d7b5d..911ba8d 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPackageVersion.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPackageVersion.java
@@ -20,6 +20,11 @@
 

 /**

  * Interface for packages that provide an {@link AndroidVersion}.

+ * <p/>

+ * Note that {@link IPlatformDependency} is a similar interface, but with a different semantic.

+ * The {@link IPlatformDependency} denotes that a given package can only be installed if the

+ * requested platform is present, whereas this interface denotes that the given package simply

+ * has a version, which is not necessarily a dependency.

  */

 public interface IPackageVersion {

 

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPlatformDependency.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPlatformDependency.java
new file mode 100755
index 0000000..3aba333
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IPlatformDependency.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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.sdklib.internal.repository;
+
+import com.android.sdklib.AndroidVersion;
+
+/**
+ * Interface used to decorate a {@link Package} that has a dependency
+ * on a specific platform (API level and/or code name).
+ * <p/>
+ * A package that has this dependency can only be installed if a platform with at least the
+ * requested API level is present or installed at the same time.
+ * <p/>
+ * Note that although this interface looks like {@link IPackageVersion}, it does not convey
+ * the same semantic, that is {@link IPackageVersion} does <em>not</em> imply any dependency being
+ * a limiting factor as far as installation is concerned.
+ */
+public interface IPlatformDependency {
+
+    /** Returns the version of the platform dependency of this package. */
+    AndroidVersion getVersion();
+
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
index c547d46..b92f771 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -90,7 +90,9 @@
             visited.add(dir);

         }

 

-        // for platforms and add-ons, rely on the SdkManager parser

+        File samplesRoot = new File(osSdkRoot, SdkConstants.FD_SAMPLES);

+

+        // for platforms, add-ons and samples, rely on the SdkManager parser

         for(IAndroidTarget target : sdkManager.getTargets()) {

 

             Properties props = parseProperties(new File(target.getLocation(),

@@ -99,6 +101,22 @@
             try {

                 if (target.isPlatform()) {

                     pkg = new PlatformPackage(target, props);

+

+                    if (samplesRoot.isDirectory()) {

+                        // Get the samples dir for a platform if it is located in the new

+                        // root /samples dir. We purposely ignore "old" samples that are

+                        // located under the platform dir.

+                        String s = target.getPath(IAndroidTarget.SAMPLES);

+                        File f = new File(s);

+                        if (f.exists() && f.getParentFile().equals(samplesRoot)) {

+                            Properties props2 = parseProperties(

+                                    new File(f, SdkConstants.FN_SOURCE_PROP));

+                            SamplePackage pkg2 = new SamplePackage(target, props2);

+                            packages.add(pkg2);

+                            visited.add(f);

+                        }

+                    }

+

                 } else {

                     pkg = new AddonPackage(target, props);

                 }

@@ -114,6 +132,9 @@
 

         scanExtra(osSdkRoot, visited, packages, log);

 

+        // TODO scanSample folder for samples that have not been visited yet

+        // (e.g. for platforms that are not installed)

+

         mPackages = packages.toArray(new Package[packages.size()]);

         return mPackages;

     }

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java
index 43d0a1e..4779a1f 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java
@@ -27,9 +27,9 @@
 

 /**

  * Represents an XML node in an SDK repository that has a min-tools-rev requirement.

- * This is either a {@link PlatformPackage} or an {@link ExtraPackage}.

  */

-public abstract class MinToolsPackage extends Package {

+public abstract class MinToolsPackage extends Package

+    implements IMinToolsDependency {

 

     protected static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev";  //$NON-NLS-1$

 

@@ -40,12 +40,6 @@
     private final int mMinToolsRevision;

 

     /**

-     * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}

-     * was not specified in the XML source.

-     */

-    public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;

-

-    /**

      * Creates a new package from the attributes and elements of the given XML node.

      * <p/>

      * This constructor should throw an exception if the package cannot be created.

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
index 355bde4..e30e8a2 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
@@ -31,8 +31,7 @@
 

 /**

  * A {@link Package} is the base class for "something" that can be downloaded from

- * the SDK repository -- subclasses include {@link PlatformPackage}, {@link AddonPackage},

- * {@link DocPackage} and {@link ToolPackage}.

+ * the SDK repository.

  * <p/>

  * A package has some attributes (revision, description) and a list of archives

  * which represent the downloadable bits.

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
index 430839e..415cba5 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
@@ -723,6 +723,8 @@
                                 p = new DocPackage(this, child, licenses);

                             } else if (SdkRepository.NODE_TOOL.equals(name)) {

                                 p = new ToolPackage(this, child, licenses);

+                            } else if (SdkRepository.NODE_SAMPLE.equals(name)) {

+                                p = new SamplePackage(this, child, licenses);

                             }

                         }

 

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
new file mode 100755
index 0000000..f4c64a2
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
@@ -0,0 +1,243 @@
+/*

+ * 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.sdklib.internal.repository;

+

+import com.android.sdklib.AndroidVersion;

+import com.android.sdklib.IAndroidTarget;

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.SdkManager;

+import com.android.sdklib.AndroidVersion.AndroidVersionException;

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.w3c.dom.Node;

+

+import java.io.File;

+import java.util.Map;

+import java.util.Properties;

+

+/**

+ * Represents a sample XML node in an SDK repository.

+ */

+public class SamplePackage extends MinToolsPackage

+    implements IPackageVersion, IMinApiLevelDependency, IMinToolsDependency {

+

+    private static final String PROP_MIN_API_LEVEL = "Sample.MinApiLevel";  //$NON-NLS-1$

+

+    /** The matching platform version. */

+    private final AndroidVersion mVersion;

+

+    /**

+     * The minimal API level required by this extra package, if > 0,

+     * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement.

+     */

+    private final int mMinApiLevel;

+

+    /**

+     * Creates a new sample package from the attributes and elements of the given XML node.

+     * <p/>

+     * This constructor should throw an exception if the package cannot be created.

+     */

+    SamplePackage(RepoSource source, Node packageNode, Map<String,String> licenses) {

+        super(source, packageNode, licenses);

+

+        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);

+        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);

+        if (codeName.length() == 0) {

+            codeName = null;

+        }

+        mVersion = new AndroidVersion(apiLevel, codeName);

+

+        mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_API_LEVEL,

+                MIN_API_LEVEL_NOT_SPECIFIED);

+    }

+

+    /**

+     * Creates a new sample package based on an actual {@link IAndroidTarget} (which

+     * must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.

+     * <p/>

+     * The target <em>must</em> have an existing sample directory that uses the /samples

+     * root form rather than the old form where the samples dir was located under the

+     * platform dir.

+     * <p/>

+     * This is used to list local SDK folders in which case there is one archive which

+     * URL is the actual samples path location.

+     * <p/>

+     * By design, this creates a package with one and only one archive.

+     */

+    SamplePackage(IAndroidTarget target, Properties props) {

+        super(  null,                                   //source

+                props,                                  //properties

+                0,                                      //revision will be taken from props

+                null,                                   //license

+                null,                                   //description

+                null,                                   //descUrl

+                Os.ANY,                                 //archiveOs

+                Arch.ANY,                               //archiveArch

+                target.getPath(IAndroidTarget.SAMPLES)  //archiveOsPath

+                );

+

+        mVersion = target.getVersion();

+

+        mMinApiLevel = Integer.parseInt(

+            getProperty(props, PROP_MIN_API_LEVEL, Integer.toString(MIN_API_LEVEL_NOT_SPECIFIED)));

+    }

+

+    /**

+     * Creates a new sample package from an actual directory path and previously

+     * saved properties.

+     * <p/>

+     * This is used to list local SDK folders in which case there is one archive which

+     * URL is the actual samples path location.

+     * <p/>

+     * By design, this creates a package with one and only one archive.

+     *

+     * @throws AndroidVersionException if the {@link AndroidVersion} can't be restored

+     *                                 from properties.

+     */

+    SamplePackage(String archiveOsPath, Properties props) throws AndroidVersionException {

+        super(  null,                                   //source

+                props,                                  //properties

+                0,                                      //revision will be taken from props

+                null,                                   //license

+                null,                                   //description

+                null,                                   //descUrl

+                Os.ANY,                                 //archiveOs

+                Arch.ANY,                               //archiveArch

+                archiveOsPath                           //archiveOsPath

+                );

+

+        mVersion = new AndroidVersion(props);

+

+        mMinApiLevel = Integer.parseInt(

+            getProperty(props, PROP_MIN_API_LEVEL, Integer.toString(MIN_API_LEVEL_NOT_SPECIFIED)));

+    }

+

+    /**

+     * Save the properties of the current packages in the given {@link Properties} object.

+     * These properties will later be given to a constructor that takes a {@link Properties} object.

+     */

+    @Override

+    void saveProperties(Properties props) {

+        super.saveProperties(props);

+

+        mVersion.saveProperties(props);

+

+        if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) {

+            props.setProperty(PROP_MIN_API_LEVEL, Integer.toString(getMinApiLevel()));

+        }

+    }

+

+    /**

+     * Returns the minimal API level required by this extra package, if > 0,

+     * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement.

+     */

+    public int getMinApiLevel() {

+        return mMinApiLevel;

+    }

+

+    /** Returns the matching platform version. */

+    public AndroidVersion getVersion() {

+        return mVersion;

+    }

+

+    /** Returns a short description for an {@link IDescription}. */

+    @Override

+    public String getShortDescription() {

+        String s = String.format("Samples for SDK API %1$s%2$s, revision %3$d",

+                mVersion.getApiString(),

+                mVersion.isPreview() ? " Preview" : "",

+                getRevision());

+        return s;

+    }

+

+    /**

+     * Returns a long description for an {@link IDescription}.

+     *

+     * The long description is whatever the XML contains for the &lt;description&gt; field,

+     * or the short description if the former is empty.

+     */

+    @Override

+    public String getLongDescription() {

+        String s = getDescription();

+        if (s == null || s.length() == 0) {

+            s = getShortDescription();

+        }

+

+        if (s.indexOf("revision") == -1) {

+            s += String.format("\nRevision %1$d", getRevision());

+        }

+

+        return s;

+    }

+

+    /**

+     * Computes a potential installation folder if an archive of this package were

+     * to be installed right away in the given SDK root.

+     * <p/>

+     * A sample package is typically installed in SDK/samples/android-"version".

+     * However if we can find a different directory that already has this sample

+     * version installed, we'll use that one.

+     *

+     * @param osSdkRoot The OS path of the SDK root folder.

+     * @param suggestedDir A suggestion for the installation folder name, based on the root

+     *                     folder used in the zip archive.

+     * @param sdkManager An existing SDK manager to list current platforms and addons.

+     * @return A new {@link File} corresponding to the directory to use to install this package.

+     */

+    @Override

+    public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {

+

+        // First find if this platform is already installed. If so, reuse the same directory.

+        for (IAndroidTarget target : sdkManager.getTargets()) {

+            if (target.isPlatform() &&

+                    target.getVersion().equals(mVersion)) {

+                String p = target.getPath(IAndroidTarget.SAMPLES);

+                File f = new File(p);

+                if (f.isDirectory()) {

+                    return f;

+                }

+            }

+        }

+

+        // Otherwise, get a suitable default

+        File samples = new File(osSdkRoot, SdkConstants.FD_SAMPLES);

+        File folder = new File(samples,

+                String.format("android-%d", getVersion().getApiLevel())); //$NON-NLS-1$

+

+        for (int n = 1; folder.exists(); n++) {

+            // Keep trying till we find an unused directory.

+            folder = new File(samples,

+                    String.format("android-%d_%d", getVersion().getApiLevel(), n)); //$NON-NLS-1$

+        }

+

+        return folder;

+    }

+

+    @Override

+    public boolean sameItemAs(Package pkg) {

+        if (pkg instanceof SamplePackage) {

+            SamplePackage newPkg = (SamplePackage)pkg;

+

+            // check they are the same platform.

+            return newPkg.getVersion().equals(this.getVersion());

+        }

+

+        return false;

+    }

+}

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
index be9384a..fd5cf9e 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
@@ -56,6 +56,8 @@
     public static final String NODE_TOOL     = "tool";                          //$NON-NLS-1$

     /** A doc package. */

     public static final String NODE_DOC      = "doc";                           //$NON-NLS-1$

+    /** A sample package. */

+    public static final String NODE_SAMPLE   = "sample";                        //$NON-NLS-1$

     /** An extra package. */

     public static final String NODE_EXTRA    = "extra";                         //$NON-NLS-1$

 

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-2.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-2.xsd
index 37038c3..9bcc1bc 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-2.xsd
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-2.xsd
@@ -47,6 +47,7 @@
             <xsd:element name="add-on"   type="sr2:addonType"    />
             <xsd:element name="tool"     type="sr2:toolType"     />
             <xsd:element name="doc"      type="sr2:docType"      />
+            <xsd:element name="sample"   type="sr2:sampleType"   />
             <xsd:element name="extra"    type="sr2:extraType"    />
             <xsd:element name="license"  type="sr2:licenseType"  />
         </xsd:choice>
@@ -203,6 +204,41 @@
     </xsd:complexType>
 
 
+    <!-- The definition of an SDK sample package. -->
+
+    <xsd:complexType name="sampleType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK sample package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android API Level for the documentation. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this doc, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sr2:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sr2:archivesType" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
     <!-- The definition of an SDK extra package. This kind of package is for
          "free" content and specifies in which fixed root directory it must be
          installed.
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml b/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml
index 6259794..bf571d2 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml
+++ b/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml
@@ -289,4 +289,19 @@
         <sdk:min-api-level>42</sdk:min-api-level>

     </sdk:extra>

 

+    <sdk:sample>

+        <sdk:api-level>14</sdk:api-level>

+        <sdk:revision>24</sdk:revision>

+        <sdk:archives>

+            <sdk:archive os="any" arch="any">

+                <sdk:size>65537</sdk:size>

+                <sdk:checksum type="sha1">3822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>

+                <sdk:url>distrib/sample_duff.zip</sdk:url>

+            </sdk:archive>

+        </sdk:archives>

+        <sdk:description>Some sample package</sdk:description>

+        <sdk:desc-url>http://www.example.com/sample.html</sdk:desc-url>

+        <sdk:min-tools-rev>5</sdk:min-tools-rev>

+    </sdk:sample>

+

 </sdk:sdk-repository>

diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
index 192eb3e..36f277b 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
@@ -21,12 +21,16 @@
 import com.android.sdklib.internal.repository.Archive;

 import com.android.sdklib.internal.repository.DocPackage;

 import com.android.sdklib.internal.repository.ExtraPackage;

+import com.android.sdklib.internal.repository.IMinApiLevelDependency;

+import com.android.sdklib.internal.repository.IMinToolsDependency;

 import com.android.sdklib.internal.repository.IPackageVersion;

+import com.android.sdklib.internal.repository.IPlatformDependency;

 import com.android.sdklib.internal.repository.MinToolsPackage;

 import com.android.sdklib.internal.repository.Package;

 import com.android.sdklib.internal.repository.PlatformPackage;

 import com.android.sdklib.internal.repository.RepoSource;

 import com.android.sdklib.internal.repository.RepoSources;

+import com.android.sdklib.internal.repository.SamplePackage;

 import com.android.sdklib.internal.repository.ToolPackage;

 import com.android.sdklib.internal.repository.Package.UpdateInfo;

 

@@ -80,7 +84,7 @@
     }

 

     /**

-     * Finds new platforms that the user does not have in his/her local SDK

+     * Finds new packages that the user does not have in his/her local SDK

      * and adds them to the list of archives to install.

      */

     public void addNewPlatforms(ArrayList<ArchiveInfo> archives,

@@ -92,6 +96,7 @@
 

         // Find the highest platform installed

         float currentPlatformScore = 0;

+        float currentSampleScore = 0;

         float currentAddonScore = 0;

         float currentDocScore = 0;

         HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();

@@ -99,7 +104,7 @@
             int rev = p.getRevision();

             int api = 0;

             boolean isPreview = false;

-            if (p instanceof  IPackageVersion) {

+            if (p instanceof IPackageVersion) {

                 AndroidVersion vers = ((IPackageVersion) p).getVersion();

                 api = vers.getApiLevel();

                 isPreview = vers.isPreview();

@@ -112,6 +117,8 @@
 

             if (p instanceof PlatformPackage) {

                 currentPlatformScore = Math.max(currentPlatformScore, score);

+            } else if (p instanceof SamplePackage) {

+                currentSampleScore = Math.max(currentSampleScore, score);

             } else if (p instanceof AddonPackage) {

                 currentAddonScore = Math.max(currentAddonScore, score);

             } else if (p instanceof ExtraPackage) {

@@ -142,6 +149,8 @@
             boolean shouldAdd = false;

             if (p instanceof PlatformPackage) {

                 shouldAdd = score > currentPlatformScore;

+            } else if (p instanceof SamplePackage) {

+                shouldAdd = score > currentSampleScore;

             } else if (p instanceof AddonPackage) {

                 shouldAdd = score > currentAddonScore;

             } else if (p instanceof ExtraPackage) {

@@ -186,7 +195,6 @@
                 }

             }

         }

-

     }

 

     /**

@@ -314,6 +322,13 @@
         return ai;

     }

 

+    /**

+     * Resolves dependencies for a given package.

+     *

+     * Returns null if no dependencies were found.

+     * Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have

+     * at least size 1 and contain no null elements.

+     */

     private ArchiveInfo[] findDependency(Package pkg,

             ArrayList<ArchiveInfo> outArchives,

             Collection<Archive> selectedArchives,

@@ -326,11 +341,11 @@
         // - platform: *might* depends on tools of rev >= min-tools-rev

         // - extra: *might* depends on platform with api >= min-api-level

 

-        if (pkg instanceof AddonPackage) {

-            AddonPackage addon = (AddonPackage) pkg;

+        ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();

 

+        if (pkg instanceof IPlatformDependency) {

             ArchiveInfo ai = findPlatformDependency(

-                    addon,

+                    (IPlatformDependency) pkg,

                     outArchives,

                     selectedArchives,

                     remotePkgs,

@@ -338,44 +353,44 @@
                     localArchives);

 

             if (ai != null) {

-                return new ArchiveInfo[] { ai };

+                list.add(ai);

             }

+        }

 

-        } else if (pkg instanceof MinToolsPackage) {

-            MinToolsPackage platformOrExtra = (MinToolsPackage) pkg;

+        if (pkg instanceof IMinToolsDependency) {

 

-            int n = 0;

-            ArchiveInfo ai1 = findToolsDependency(

-                    platformOrExtra,

+            ArchiveInfo ai = findToolsDependency(

+                    (IMinToolsDependency) pkg,

                     outArchives,

                     selectedArchives,

                     remotePkgs,

                     remoteSources,

                     localArchives);

 

-            n += ai1 == null ? 0 : 1;

-

-            ArchiveInfo ai2 = null;

-            if (pkg instanceof ExtraPackage) {

-                ai2 = findExtraPlatformDependency(

-                        (ExtraPackage) pkg,

-                        outArchives,

-                        selectedArchives,

-                        remotePkgs,

-                        remoteSources,

-                        localArchives);

+            if (ai != null) {

+                list.add(ai);

             }

+        }

 

-            n += ai2 == null ? 0 : 1;

+        if (pkg instanceof IMinApiLevelDependency) {

 

-            if (n > 0) {

-                ArchiveInfo[] ais = new ArchiveInfo[n];

-                ais[0] = ai1 != null ? ai1 : ai2;

-                if (n > 1) ais[1] = ai2;

-                return ais;

+            ArchiveInfo ai = findMinApiLevelDependency(

+                    (IMinApiLevelDependency) pkg,

+                    outArchives,

+                    selectedArchives,

+                    remotePkgs,

+                    remoteSources,

+                    localArchives);

+

+            if (ai != null) {

+                list.add(ai);

             }

         }

 

+        if (list.size() > 0) {

+            return list.toArray(new ArchiveInfo[list.size()]);

+        }

+

         return null;

     }

 

@@ -387,14 +402,15 @@
      * Finds the tools dependency. If found, add it to the list of things to install.

      * Returns the archive info dependency, if any.

      */

-    protected ArchiveInfo findToolsDependency(MinToolsPackage platformOrExtra,

+    protected ArchiveInfo findToolsDependency(

+            IMinToolsDependency pkg,

             ArrayList<ArchiveInfo> outArchives,

             Collection<Archive> selectedArchives,

             ArrayList<Package> remotePkgs,

             RepoSource[] remoteSources,

             ArchiveInfo[] localArchives) {

         // This is the requirement to match.

-        int rev = platformOrExtra.getMinToolsRevision();

+        int rev = pkg.getMinToolsRevision();

 

         if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {

             // Well actually there's no requirement.

@@ -485,14 +501,14 @@
      * Returns the archive info dependency, if any.

      */

     protected ArchiveInfo findPlatformDependency(

-            AddonPackage addon,

+            IPlatformDependency pkg,

             ArrayList<ArchiveInfo> outArchives,

             Collection<Archive> selectedArchives,

             ArrayList<Package> remotePkgs,

             RepoSource[] remoteSources,

             ArchiveInfo[] localArchives) {

         // This is the requirement to match.

-        AndroidVersion v = addon.getVersion();

+        AndroidVersion v = pkg.getVersion();

 

         // Find a platform that would satisfy the requirement.

 

@@ -568,7 +584,7 @@
         // We end up here if nothing matches. We don't have a good platform to match.

         // We need to indicate this addon depends on a missing platform archive

         // so that it can be impossible to install later on.

-        return new MissingPlatformArchiveInfo(addon.getVersion());

+        return new MissingPlatformArchiveInfo(pkg.getVersion());

     }

 

     /**

@@ -583,15 +599,15 @@
      * Finds the platform dependency. If found, add it to the list of things to install.

      * Returns the archive info dependency, if any.

      */

-    protected ArchiveInfo findExtraPlatformDependency(

-            ExtraPackage extra,

+    protected ArchiveInfo findMinApiLevelDependency(

+            IMinApiLevelDependency pkg,

             ArrayList<ArchiveInfo> outArchives,

             Collection<Archive> selectedArchives,

             ArrayList<Package> remotePkgs,

             RepoSource[] remoteSources,

             ArchiveInfo[] localArchives) {

 

-        int api = extra.getMinApiLevel();

+        int api = pkg.getMinApiLevel();

 

         if (api == ExtraPackage.MIN_API_LEVEL_NOT_SPECIFIED) {

             return null;