Merge "Dependency support for the templates"
diff --git a/build/tools.atree b/build/tools.atree
index d075fc1..a6aa99f 100644
--- a/build/tools.atree
+++ b/build/tools.atree
@@ -153,7 +153,7 @@
 sdk/templates/activities/BlankActivity                         tools/templates/activities/BlankActivity
 sdk/templates/activities/MasterDetailFlow                      tools/templates/activities/MasterDetailFlow
 sdk/templates/other/CustomView                                 tools/templates/other/CustomView
-sdk/templates/resources                                        tools/templates/resources
+#sdk/templates/resources                                        tools/templates/resources
 
 # SDK Controller
 sdk/apps/SdkController       tools/apps/SdkController
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java
index 7da3cc0..ebbd9f0 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java
@@ -16,13 +16,16 @@
 
 package com.android.ide.eclipse.adt.internal.actions;
 
+import com.android.annotations.Nullable;
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AdtUtils;
 import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.NullSdkLog;
 import com.android.sdklib.SdkConstants;
+import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
@@ -62,6 +65,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  * An action to add the android-support-v4.jar compatibility library
@@ -74,6 +78,12 @@
  */
 public class AddCompatibilityJarAction implements IObjectActionDelegate {
 
+    private static final String FD_ANDROID = "android";                            //$NON-NLS-1$
+    private static final String FD_SUPPORT = "support";                            //$NON-NLS-1$
+    private static final String FD_GRIDLAYOUT = "gridlayout";                      //$NON-NLS-1$
+    private static final String FD_V7 = "v7";                                      //$NON-NLS-1$
+    private static final String FD_V4 = "v4";                                      //$NON-NLS-1$
+    private static final String ANDROID_SUPPORT_V4_JAR = "android-support-v4.jar"; //$NON-NLS-1$
     private ISelection mSelection;
 
     /**
@@ -116,7 +126,7 @@
      * @return true if the installation was successful
      */
     public static boolean install(final IProject project) {
-        File jarPath = installSupport();
+        File jarPath = installSupport(-1);
         if (jarPath != null) {
             try {
                 return copyJarIntoProject(project, jarPath) != null;
@@ -128,7 +138,21 @@
         return false;
     }
 
-    private static File installSupport() {
+    /**
+     * Installs the Android Support library into the SDK extras/ folder. If a minimum
+     * revision number is specified, this method will check whether the package is already
+     * installed, and if the installed revision is at least as high as the requested revision,
+     * this method will exit without performing an update.
+     *
+     * @param minimumRevision a minimum revision, or -1 to upgrade
+     *            unconditionally. Note that this does <b>NOT</b> specify which
+     *            revision to install; the latest version will always be
+     *            installed.
+     * @return the location of the support jar file, or null if something went
+     *            wrong
+     */
+    @Nullable
+    public static File installSupport(int minimumRevision) {
 
         final Sdk sdk = Sdk.getCurrent();
         if (sdk == null) {
@@ -138,6 +162,18 @@
             return null;
         }
 
+        String sdkLocation = sdk.getSdkLocation();
+        if (minimumRevision > 0) {
+            File path = getCompatJarFile();
+            if (path != null) {
+                assert path.exists(); // guaranteed by the getCompatJarFile call
+                int installedRevision = getInstalledRevision();
+                if (installedRevision != -1 && minimumRevision >= installedRevision) {
+                    return path;
+                }
+            }
+        }
+
         // TODO: For the generic action, check the library isn't in the project already.
 
         // First call the package manager to make sure the package is installed
@@ -146,10 +182,10 @@
         AdtUpdateDialog window = new AdtUpdateDialog(
                 AdtPlugin.getDisplay().getActiveShell(),
                 new AdtConsoleSdkLog(),
-                sdk.getSdkLocation());
+                sdkLocation);
 
         Pair<Boolean, File> result = window.installExtraPackage(
-                "android", "support");    //$NON-NLS-1$ //$NON-NLS-2$
+                FD_ANDROID, FD_SUPPORT);
 
         // TODO: Make sure the version is at the required level; we know we need at least one
         // containing the v7 support
@@ -163,8 +199,8 @@
         // vN/android-support-vN.jar. Eventually we'll want to rely on info from the
         // package manifest anyway so this is irrelevant.
 
-        File path = new File(result.getSecond(), "v4");                   //$NON-NLS-1$
-        final File jarPath = new File(path, "android-support-v4.jar");    //$NON-NLS-1$
+        File path = new File(result.getSecond(), FD_V4);
+        final File jarPath = new File(path, ANDROID_SUPPORT_V4_JAR);
 
         if (!jarPath.isFile()) {
             AdtPlugin.printErrorToConsole("Android Compatibility JAR not found:",
@@ -176,6 +212,27 @@
     }
 
     /**
+     * Returns the installed revision number of the Android Compatibility
+     * library, or -1 if the package is not installed.
+     *
+     * @return the installed revision number, or -1
+     */
+    public static int getInstalledRevision() {
+        final Sdk sdk = Sdk.getCurrent();
+        if (sdk != null) {
+            String sdkLocation = sdk.getSdkLocation();
+            SdkManager manager = SdkManager.createManager(sdkLocation, new NullSdkLog());
+            Map<String, Integer> versions = manager.getExtrasVersions();
+            Integer version = versions.get("android/support"); //$NON-NLS-1$
+            if (version != null) {
+                return version.intValue();
+            }
+        }
+
+       return -1;
+    }
+
+    /**
      * Similar to {@link #install}, but rather than copy a jar into the given
      * project, it creates a new library project in the workspace for the
      * compatibility library, and adds a library dependency on the newly
@@ -188,29 +245,23 @@
      *         likely to be successful - e.g. the user has at least agreed to
      *         all installation prompts.)
      */
-    public static boolean installLibrary(final IProject project, boolean waitForFinish) {
+    public static boolean installGridLayoutLibrary(final IProject project, boolean waitForFinish) {
         final IJavaProject javaProject = JavaCore.create(project);
         if (javaProject != null) {
 
-            File sdk = new File(Sdk.getCurrent().getSdkLocation());
-            File supportPath = new File(sdk,
-                    SdkConstants.FD_EXTRAS + File.separator
-                    + "android" + File.separator   //$NON-NLS-1$
-                    + "support");                  //$NON-NLS-1$
+            File supportPath = getCompatPackageDir();
             if (!supportPath.isDirectory()) {
-                File path = installSupport();
+                File path = installSupport(8); // GridLayout arrived in rev 7 and fixed in rev 8
                 if (path == null) {
                     return false;
                 }
                 assert path.equals(supportPath);
             }
-            File libraryPath = new File(supportPath,
-                    "v7" + File.separator   //$NON-NLS-1$
-                    + "gridlayout");        //$NON-NLS-1$
+            File libraryPath = new File(supportPath, FD_V7 + File.separator + FD_GRIDLAYOUT);
             if (!libraryPath.isDirectory()) {
                 // Upgrade support package: it's out of date. The SDK manager will
                 // perform an upgrade to the latest version if the package is already installed.
-                File path = installSupport();
+                File path = installSupport(-1);
                 if (path == null) {
                     return false;
                 }
@@ -228,6 +279,36 @@
     }
 
     /**
+     * Returns the directory containing the support libraries (v4, v7, v13,
+     * ...), which may or may not exist
+     */
+    private static File getCompatPackageDir() {
+        File sdk = new File(Sdk.getCurrent().getSdkLocation());
+        File supportPath = new File(sdk,
+                SdkConstants.FD_EXTRAS + File.separator
+                + FD_ANDROID + File.separator
+                + FD_SUPPORT);
+        return supportPath;
+    }
+
+    /**
+     * Returns a path to the installed jar file for the compatibility library,
+     * or null if it does not exist
+     *
+     * @return a path to the library or null
+     */
+    @Nullable
+    public static File getCompatJarFile() {
+        File supportDir = getCompatPackageDir();
+        File path = new File(supportDir, FD_V4 + File.separator + ANDROID_SUPPORT_V4_JAR);
+        if (path.exists()) {
+            return path;
+        }
+
+        return null;
+    }
+
+    /**
      * Creates a library project in the Eclipse workspace out of the grid layout project
      * in the SDK tree.
      *
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java
index 45590b7..d5f5c3c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java
@@ -121,7 +121,7 @@
                         }
                     } else {
                         // Install library AND add dependency
-                        if (!AddCompatibilityJarAction.installLibrary(
+                        if (!AddCompatibilityJarAction.installGridLayoutLibrary(
                                 project,
                                 true /* waitForFinish */)) {
                             return tag;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizard.java
index 5115385..8f5a3e1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizard.java
@@ -173,6 +173,12 @@
         // are required dependencies of any platform.
         boolean result = updater.installNewSdk(apiLevels);
 
+        // TODO: Install extra package here as well since it is now core to most of
+        // the templates
+        // if (result) {
+        //     updater.installExtraPackage(vendor, path);
+        // }
+
         if (disposeShell) {
             shell.dispose();
         }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/InstallDependencyPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/InstallDependencyPage.java
new file mode 100644
index 0000000..bdd53b8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/InstallDependencyPage.java
@@ -0,0 +1,300 @@
+/*
+ * 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 com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.actions.AddCompatibilityJarAction;
+import com.android.util.Pair;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+
+class InstallDependencyPage extends WizardPage implements SelectionListener {
+    /**
+     * The compatibility library. This is the only library the templates
+     * currently support. The appearance of any other dependency in this
+     * template will be flagged as a validation error (and the user encouraged
+     * to upgrade to a newer ADT
+     */
+    static final String SUPPORT_LIBRARY_NAME = "android-support-v4"; //$NON-NLS-1$
+
+    /** URL containing more info */
+    private static final String URL =
+            "http://developer.android.com/sdk/compatibility-library.html"; //$NON-NLS-1$
+
+    private Button mCheckButton;
+    private Button mInstallButton;
+    private Link mLink;
+    private TemplateMetadata mTemplate;
+
+    InstallDependencyPage() {
+        super("dependency"); //$NON-NLS-1$
+        setTitle("Install Dependencies");
+    }
+
+    void setTemplate(TemplateMetadata template) {
+        if (template != mTemplate) {
+            mTemplate = template;
+            if (getControl() != null) {
+                validatePage();
+            }
+        }
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        super.setVisible(visible);
+        if (visible) {
+            updateVersionLabels();
+            validatePage();
+        }
+    }
+
+    @Override
+    public void createControl(Composite parent) {
+        Composite container = new Composite(parent, SWT.NULL);
+        setControl(container);
+        container.setLayout(new GridLayout(2, false));
+        // Remaining contents are created lazily, since this page is always added to
+        // the page list, but typically not shown
+
+        Label dependLabel = new Label(container, SWT.WRAP);
+        GridData gd_dependLabel = new GridData(SWT.LEFT, SWT.TOP, true, false, 2, 1);
+        gd_dependLabel.widthHint = NewTemplatePage.WIZARD_PAGE_WIDTH - 50;
+        dependLabel.setLayoutData(gd_dependLabel);
+        dependLabel.setText("This template depends on the Android Support library, which is " +
+                "either not installed, or the template depends on a more recent version than " +
+                "the one you have installed.");
+
+        mLink = new Link(container, SWT.NONE);
+        mLink.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+        mLink.setText("<a href=\"" + URL + "\">" + URL + "</a>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        mLink.addSelectionListener(this);
+
+        Label lblNewLabel_1 = new Label(container, SWT.NONE);
+        lblNewLabel_1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+
+        requiredLabel = new Label(container, SWT.NONE);
+        requiredLabel.setText("Required version:");
+
+        mRequiredVersion = new Label(container, SWT.NONE);
+        mRequiredVersion.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+
+        installedLabel = new Label(container, SWT.NONE);
+        installedLabel.setText("Installed version:");
+
+        mInstalledVersion = new Label(container, SWT.NONE);
+        mInstalledVersion.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+
+        Label lblNewLabel = new Label(container, SWT.NONE);
+        lblNewLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+
+        Label descLabel = new Label(container, SWT.WRAP);
+        GridData gd_descLabel = new GridData(SWT.LEFT, SWT.TOP, true, false, 2, 1);
+        gd_descLabel.widthHint = 550;
+        descLabel.setLayoutData(gd_descLabel);
+        descLabel.setText(
+                "You can install or upgrade it by clicking the Install button below, or " +
+                "alternatively, you can install it outside of Eclipse with the SDK Manager, " +
+                "then click on \"Check Again\" to proceed.");
+
+        mInstallButton = new Button(container, SWT.NONE);
+        mInstallButton.setText("Install/Upgrade");
+        mInstallButton.addSelectionListener(this);
+
+        mCheckButton = new Button(container, SWT.NONE);
+        mCheckButton.setText("Check Again");
+        mCheckButton.addSelectionListener(this);
+
+        mInstallButton.setFocus();
+    }
+
+    private void showNextPage() {
+        validatePage();
+        if (isPageComplete()) {
+            // Finish button will be enabled now
+            mInstallButton.setEnabled(false);
+            mCheckButton.setEnabled(false);
+
+            IWizard wizard = getWizard();
+            IWizardPage next = wizard.getNextPage(this);
+            if (next != null) {
+                wizard.getContainer().showPage(next);
+            }
+        }
+    }
+
+    @Override
+    public boolean isPageComplete() {
+        if (mTemplate == null) {
+            return true;
+        }
+
+        return super.isPageComplete() && isInstalled();
+    }
+
+    private boolean isInstalled() {
+        return isInstalled(mTemplate.getDependencies());
+    }
+
+    static String sCachedName;
+    static int sCachedVersion;
+    private Label requiredLabel;
+    private Label installedLabel;
+    private Label mRequiredVersion;
+    private Label mInstalledVersion;
+
+    public static boolean isInstalled(List<Pair<String, Integer>> dependencies) {
+        for (Pair<String, Integer> dependency : dependencies) {
+            String name = dependency.getFirst();
+            int required = dependency.getSecond();
+
+            int installed = -1;
+            if (SUPPORT_LIBRARY_NAME.equals(name)) {
+                installed = getInstalledSupportLibVersion();
+            }
+
+            if (installed == -1) {
+                return false;
+            }
+            if (required > installed) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static int getInstalledSupportLibVersion() {
+        if (SUPPORT_LIBRARY_NAME.equals(sCachedName)) {
+            return sCachedVersion;
+        } else {
+            int version = AddCompatibilityJarAction.getInstalledRevision();
+            sCachedName = SUPPORT_LIBRARY_NAME;
+            sCachedVersion = version;
+            return version;
+        }
+    }
+
+    private void updateVersionLabels() {
+        int version = getInstalledSupportLibVersion();
+        if (version == -1) {
+            mInstalledVersion.setText("Not installed");
+        } else {
+            mInstalledVersion.setText(Integer.toString(version));
+        }
+
+        if (mTemplate != null) {
+            for (Pair<String, Integer> dependency : mTemplate.getDependencies()) {
+                String name = dependency.getFirst();
+                if (name.equals(SUPPORT_LIBRARY_NAME)) {
+                    int required = dependency.getSecond();
+                    mRequiredVersion.setText(Integer.toString(required));
+                    break;
+                }
+            }
+        }
+    }
+
+    private void validatePage() {
+        if (mTemplate == null) {
+            return;
+        }
+
+        IStatus status = null;
+
+        List<Pair<String, Integer>> dependencies = mTemplate.getDependencies();
+        if (dependencies.size() > 1 || dependencies.size() == 1
+                && !dependencies.get(0).getFirst().equals(SUPPORT_LIBRARY_NAME)) {
+            status = new Status(IStatus.WARNING, AdtPlugin.PLUGIN_ID,
+                    "Unsupported template dependency: Upgrade your Android Eclipse plugin");
+        }
+
+        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);
+        }
+    }
+
+    // ---- Implements SelectionListener ----
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+        Object source = e.getSource();
+        if (source == mCheckButton) {
+            sCachedName = null;
+            if (isInstalled()) {
+                showNextPage();
+            } else {
+                updateVersionLabels();
+            }
+        } else if (source == mInstallButton) {
+            sCachedName = null;
+            for (Pair<String, Integer> dependency : mTemplate.getDependencies()) {
+                String name = dependency.getFirst();
+                if (SUPPORT_LIBRARY_NAME.equals(name)) {
+                    int version = dependency.getSecond();
+                    File installed = AddCompatibilityJarAction.installSupport(version);
+                    if (installed != null) {
+                        showNextPage();
+                    } else {
+                        updateVersionLabels();
+                    }
+                }
+            }
+        } else if (source == mLink) {
+            try {
+                IWorkbench workbench = PlatformUI.getWorkbench();
+                IWebBrowser browser = workbench.getBrowserSupport().getExternalBrowser();
+                browser.openURL(new URL(URL));
+            } catch (Exception ex) {
+                String message = String.format("Could not open browser. Vist\n%1$s\ninstead.",
+                        URL);
+                MessageDialog.openError(getShell(), "Browser Error", message);
+            }
+        }
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java
index a2e3518..52d7cec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java
@@ -54,10 +54,10 @@
 
     private IWorkbench mWorkbench;
     private UpdateToolsPage mUpdatePage;
-    private NewProjectPage mMainPage;
     private NewTemplatePage mTemplatePage;
     private ActivityPage mActivityPage;
     private NewProjectWizardState mValues;
+    protected InstallDependencyPage mDependencyPage;
     private NewTemplateWizardState mActivityValues;
 
     /** Creates a new {@link NewActivityWizard} */
@@ -99,7 +99,7 @@
     @Override
     public IWizardPage getStartingPage() {
         if (mUpdatePage != null && mUpdatePage.isPageComplete()) {
-            return mMainPage;
+            return mActivityPage;
         }
         return super.getStartingPage();
     }
@@ -118,6 +118,20 @@
                 addPage(mTemplatePage);
             }
             return mTemplatePage;
+        } else if (page == mTemplatePage) {
+            TemplateMetadata template = mActivityValues.getTemplateHandler().getTemplate();
+            if (template != null) {
+                if (InstallDependencyPage.isInstalled(template.getDependencies())) {
+                    return null;
+                } else {
+                    if (mDependencyPage == null) {
+                        mDependencyPage = new InstallDependencyPage();
+                        addPage(mDependencyPage);
+                    }
+                    mDependencyPage.setTemplate(template);
+                    return mDependencyPage;
+                }
+            }
         }
 
         return super.getNextPage(page);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
index 1de4b2c..2d67086 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
@@ -78,6 +78,7 @@
     private AppSkeletonPage mAppSkeletonPage;
     private NewTemplatePage mTemplatePage;
     private ActivityPage mActivityPage;
+    protected InstallDependencyPage mDependencyPage;
     private ConfigureAssetSetPage mIconPage;
     private NewProjectWizardState mValues;
 
@@ -154,7 +155,21 @@
             return mTemplatePage;
         }
 
-        if (page == mTemplatePage || !mValues.createActivity && page == mActivityPage) {
+        if (page == mTemplatePage ) {
+            TemplateMetadata template = mValues.activityValues.getTemplateHandler().getTemplate();
+            if (template != null
+                    && !InstallDependencyPage.isInstalled(template.getDependencies())) {
+                if (mDependencyPage == null) {
+                    mDependencyPage = new InstallDependencyPage();
+                    addPage(mDependencyPage);
+                }
+                mDependencyPage.setTemplate(template);
+                return mDependencyPage;
+            }
+        }
+
+        if (page == mTemplatePage || !mValues.createActivity && page == mActivityPage
+                || page == mDependencyPage) {
             if (mValues.createIcon) {
                 if (mIconPage == null) {
                     // Bundle asset studio wizard to create the launcher icon
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java
index 1e443bc..ea9774e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java
@@ -461,7 +461,7 @@
     @Override
     public boolean isPageComplete() {
         // Force user to reach this page before hitting Finish
-        return mShown;
+        return mShown && super.isPageComplete();
     }
 
     @Override
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
index 28f5ca6..666e2f6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
@@ -59,6 +59,7 @@
     protected IWorkbench mWorkbench;
     protected NewTemplatePage mMainPage;
     protected UpdateToolsPage mUpdatePage;
+    protected InstallDependencyPage mDependencyPage;
     protected NewTemplateWizardState mValues;
     private final String mTemplateName;
 
@@ -115,6 +116,22 @@
 
     @Override
     public IWizardPage getNextPage(IWizardPage page) {
+        if (page == mMainPage) {
+            TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
+            if (template != null) {
+                if (InstallDependencyPage.isInstalled(template.getDependencies())) {
+                    return null;
+                } else {
+                    if (mDependencyPage == null) {
+                        mDependencyPage = new InstallDependencyPage();
+                        addPage(mDependencyPage);
+                    }
+                    mDependencyPage.setTemplate(template);
+                    return mDependencyPage;
+                }
+            }
+        }
+
         return super.getNextPage(page);
     }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
index 1532228..16b38e3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
@@ -17,7 +17,9 @@
 
 import static com.android.ide.eclipse.adt.AdtConstants.DOT_FTL;
 import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.InstallDependencyPage.SUPPORT_LIBRARY_NAME;
 import static com.android.sdklib.SdkConstants.FD_EXTRAS;
+import static com.android.sdklib.SdkConstants.FD_NATIVE_LIBS;
 import static com.android.sdklib.SdkConstants.FD_TEMPLATES;
 import static com.android.sdklib.SdkConstants.FD_TOOLS;
 
@@ -25,6 +27,7 @@
 import com.android.annotations.Nullable;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.actions.AddCompatibilityJarAction;
 import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatPreferences;
 import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatStyle;
 import com.android.ide.eclipse.adt.internal.editors.formatting.XmlPrettyPrinter;
@@ -35,6 +38,7 @@
 import com.android.resources.ResourceFolderType;
 import com.android.sdklib.SdkConstants;
 import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
 import com.google.common.io.Files;
 
 import freemarker.cache.TemplateLoader;
@@ -75,8 +79,6 @@
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
 
-import lombok.ast.libs.org.parboiled.google.collect.Lists;
-
 /**
  * Handler which manages instantiating FreeMarker templates, copying resources
  * and merging into existing files
@@ -435,9 +437,25 @@
                         if (path != null) {
                             execute(freemarker, path, paramMap, outputPath);
                         }
+                    } else if (TAG_DEPENDENCY.equals(name)) {
+                        String dependencyName = attributes.getValue(ATTR_NAME);
+                        if (dependencyName.equals(SUPPORT_LIBRARY_NAME)) {
+                            // We assume the revision requirement has been satisfied
+                            // by the wizard
+                            File path = AddCompatibilityJarAction.getCompatJarFile();
+                            if (path != null) {
+                                File to = new File(outputPath, FD_NATIVE_LIBS
+                                        + File.separator + path.getName());
+                                try {
+                                    copy(path, to);
+                                } catch (IOException ioe) {
+                                    AdtPlugin.log(ioe, null);
+                                }
+                            }
+                        }
                     } else if (!name.equals("template") && !name.equals("category")
                             && !name.equals("option") && !name.equals(TAG_THUMBS) &&
-                            !name.equals(TAG_THUMB)) {
+                            !name.equals(TAG_THUMB) && !name.equals(TAG_DEPENDENCY)) {
                         System.err.println("WARNING: Unknown template directive " + name);
                     }
                 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java
index db0f8ce..f7037f1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java
@@ -24,6 +24,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.util.Pair;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
@@ -33,15 +34,19 @@
 import org.w3c.dom.NodeList;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import lombok.ast.libs.org.parboiled.google.collect.Lists;
+
 /** An ADT template along with metadata */
 class TemplateMetadata {
     private final Document mDocument;
     private final List<Parameter> mParameters;
     private final Map<String, Parameter> mParameterMap;
+    private List<Pair<String, Integer>> mDependencies;
 
     TemplateMetadata(@NonNull Document document) {
         mDocument = document;
@@ -59,7 +64,7 @@
         }
     }
 
-    public boolean isSupported() {
+    boolean isSupported() {
         String versionString = mDocument.getDocumentElement().getAttribute(ATTR_FORMAT);
         if (versionString != null && !versionString.isEmpty()) {
             try {
@@ -153,6 +158,34 @@
         return null;
     }
 
+    /**
+     * Returns the dependencies (as a list of pairs of names and revisions)
+     * required by this template
+     */
+    List<Pair<String, Integer>> getDependencies() {
+        if (mDependencies == null) {
+            NodeList elements = mDocument.getElementsByTagName(TemplateHandler.TAG_DEPENDENCY);
+            if (elements.getLength() == 0) {
+                return Collections.emptyList();
+            }
+
+            List<Pair<String, Integer>> dependencies = Lists.newArrayList();
+            for (int i = 0, n = elements.getLength(); i < n; i++) {
+                Element element = (Element) elements.item(i);
+                String name = element.getAttribute(TemplateHandler.ATTR_NAME);
+                int revision = -1;
+                String revisionString = element.getAttribute(TemplateHandler.ATTR_REVISION);
+                if (!revisionString.isEmpty()) {
+                    revision = Integer.parseInt(revisionString);
+                }
+                dependencies.add(Pair.of(name, revision));
+            }
+            mDependencies = dependencies;
+        }
+
+        return mDependencies;
+    }
+
     /** Returns the list of available parameters */
     @NonNull
     List<Parameter> getParameters() {
diff --git a/templates/activities/BlankActivity/recipe.xml.ftl b/templates/activities/BlankActivity/recipe.xml.ftl
index 97b9ba7..a6d0ce9 100644
--- a/templates/activities/BlankActivity/recipe.xml.ftl
+++ b/templates/activities/BlankActivity/recipe.xml.ftl
@@ -13,11 +13,6 @@
     <merge from="res/values-large/dimens.xml" />
     <merge from="res/values/strings.xml.ftl" />
 
-    <!-- Always include the support library because we use NavUtils, ViewPager, etc. -->
-    <!-- TODO: automatically overwrite only if the version is newer, otherwise silently fail -->
-    <copy from="${android.templatesRes}/android-support-v4.jar.bin"
-            to="libs/android-support-v4.jar" />
-
     <!-- Decide what kind of layout to add (viewpager or not) -->
     <#if navType?contains("pager")>
         <instantiate from="res/layout/activity_pager.xml.ftl"
diff --git a/templates/activities/BlankActivity/template.xml b/templates/activities/BlankActivity/template.xml
index 997051e..bce675d 100644
--- a/templates/activities/BlankActivity/template.xml
+++ b/templates/activities/BlankActivity/template.xml
@@ -4,6 +4,7 @@
     revision="1"
     name="New Blank Activity"
     description="Creates a new blank activity, with optional inner navigation.">
+    <dependency name="android-support-v4" revision="8" />
 
     <category value="Activities" />
 
diff --git a/templates/activities/MasterDetailFlow/recipe.xml.ftl b/templates/activities/MasterDetailFlow/recipe.xml.ftl
index a07635e..2c1f057 100644
--- a/templates/activities/MasterDetailFlow/recipe.xml.ftl
+++ b/templates/activities/MasterDetailFlow/recipe.xml.ftl
@@ -2,9 +2,6 @@
 <recipe>
     <merge from="AndroidManifest.xml.ftl" />
 
-    <copy from="${android.templatesRes}/android-support-v4.jar.bin"
-            to="libs/android-support-v4.jar" />
-
     <merge from="res/values-large/refs.xml.ftl" />
     <merge from="res/values-sw600dp/refs.xml.ftl" />
     <merge from="res/values/strings.xml.ftl" />
diff --git a/templates/activities/MasterDetailFlow/template.xml b/templates/activities/MasterDetailFlow/template.xml
index 4f759cf..05ddb68 100644
--- a/templates/activities/MasterDetailFlow/template.xml
+++ b/templates/activities/MasterDetailFlow/template.xml
@@ -4,6 +4,7 @@
     revision="1"
     name="New Master/Detail Flow"
     description="Creates a new master/detail flow, which is two columns on tablets, and one column on smaller screens. This creates a master fragment, detail fragment, and two activities.">
+    <dependency name="android-support-v4" revision="8" />
 
     <thumbs>
         <thumb>template_master_detail.png</thumb>
diff --git a/templates/projects/NewAndroidApplication/template.xml b/templates/projects/NewAndroidApplication/template.xml
index f0ee375..c8464c5 100644
--- a/templates/projects/NewAndroidApplication/template.xml
+++ b/templates/projects/NewAndroidApplication/template.xml
@@ -4,6 +4,7 @@
     revision="1"
     name="New Android Application"
     description="Creates a new Android application with an activity.">
+    <dependency name="android-support-v4" revision="8" />
 
     <thumbs>
         <thumb>template_new_project.png</thumb>
diff --git a/templates/resources/android-support-v4.jar.bin b/templates/resources/android-support-v4.jar.bin
deleted file mode 100644
index d006198..0000000
--- a/templates/resources/android-support-v4.jar.bin
+++ /dev/null
Binary files differ