/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.sdkuilib.internal.repository.ui; import com.android.sdklib.AndroidVersion; import com.android.sdklib.internal.repository.packages.ExtraPackage; import com.android.sdklib.internal.repository.packages.Package; import com.android.sdklib.internal.repository.packages.PlatformPackage; import com.android.sdklib.internal.repository.packages.PlatformToolPackage; import com.android.sdklib.internal.repository.packages.ToolPackage; import com.android.sdklib.internal.repository.sources.SdkSource; import com.android.sdkuilib.internal.repository.SettingsController; import com.android.sdkuilib.internal.repository.UpdaterData; import com.android.sdkuilib.internal.repository.core.PackageLoader; import com.android.sdkuilib.internal.repository.core.SdkLogAdapter; import com.android.sdkuilib.internal.repository.core.PackageLoader.IAutoInstallTask; import com.android.sdkuilib.internal.tasks.ProgressView; import com.android.sdkuilib.internal.tasks.ProgressViewFactory; import com.android.sdkuilib.ui.GridDataBuilder; import com.android.sdkuilib.ui.GridLayoutBuilder; import com.android.sdkuilib.ui.SwtBaseDialog; import com.android.utils.ILogger; import com.android.utils.Pair; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ProgressBar; import org.eclipse.swt.widgets.Shell; import java.io.File; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * This is a private implementation of UpdateWindow for ADT, * designed to install a very specific package. * <p/> * Example of usage: * <pre> * AdtUpdateDialog dialog = new AdtUpdateDialog( * AdtPlugin.getDisplay().getActiveShell(), * new AdtConsoleSdkLog(), * sdk.getSdkLocation()); * * Pair<Boolean, File> result = dialog.installExtraPackage( * "android", "compatibility"); //$NON-NLS-1$ //$NON-NLS-2$ * or * Pair<Boolean, File> result = dialog.installPlatformPackage(11); * </pre> */ public class AdtUpdateDialog extends SwtBaseDialog { public static final int USE_MAX_REMOTE_API_LEVEL = 0; private static final String APP_NAME = "Android SDK Manager"; private final UpdaterData mUpdaterData; private Boolean mResultCode = Boolean.FALSE; private Map<Package, File> mResultPaths = null; private SettingsController mSettingsController; private PackageFilter mPackageFilter; private PackageLoader mPackageLoader; private ProgressBar mProgressBar; private Label mStatusText; /** * Creates a new {@link AdtUpdateDialog}. * Callers will want to call {@link #installExtraPackage} or * {@link #installPlatformPackage} after this. * * @param parentShell The existing parent shell. Must not be null. * @param sdkLog An SDK logger. Must not be null. * @param osSdkRoot The current SDK root OS path. Must not be null or empty. */ public AdtUpdateDialog( Shell parentShell, ILogger sdkLog, String osSdkRoot) { super(parentShell, SWT.NONE, APP_NAME); mUpdaterData = new UpdaterData(osSdkRoot, sdkLog); } /** * Displays the update dialog and triggers installation of the requested {@code extra} * package with the specified vendor and path attributes. * <p/> * Callers must not try to reuse this dialog after this call. * * @param vendor The extra package vendor string to match. * @param path The extra package path string to match. * @return A boolean indicating whether the installation was successful (meaning the package * was either already present, or got installed or updated properly) and a {@link File} * with the path to the root folder of the package. The file is null when the boolean * is false, otherwise it should point to an existing valid folder. * @wbp.parser.entryPoint */ public Pair<Boolean, File> installExtraPackage(String vendor, String path) { mPackageFilter = createExtraFilter(vendor, path); open(); File installPath = null; if (mResultPaths != null) { for (Entry<Package, File> entry : mResultPaths.entrySet()) { if (entry.getKey() instanceof ExtraPackage) { installPath = entry.getValue(); break; } } } return Pair.of(mResultCode, installPath); } /** * Displays the update dialog and triggers installation of platform-tools package. * <p/> * Callers must not try to reuse this dialog after this call. * * @return A boolean indicating whether the installation was successful (meaning the package * was either already present, or got installed or updated properly) and a {@link File} * with the path to the root folder of the package. The file is null when the boolean * is false, otherwise it should point to an existing valid folder. * @wbp.parser.entryPoint */ public Pair<Boolean, File> installPlatformTools() { mPackageFilter = createPlatformToolsFilter(); open(); File installPath = null; if (mResultPaths != null) { for (Entry<Package, File> entry : mResultPaths.entrySet()) { if (entry.getKey() instanceof ExtraPackage) { installPath = entry.getValue(); break; } } } return Pair.of(mResultCode, installPath); } /** * Displays the update dialog and triggers installation of the requested platform * package with the specified API level. * <p/> * Callers must not try to reuse this dialog after this call. * * @param apiLevel The platform API level to match. * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use * the highest API level available on the remote repository. * @return A boolean indicating whether the installation was successful (meaning the package * was either already present, or got installed or updated properly) and a {@link File} * with the path to the root folder of the package. The file is null when the boolean * is false, otherwise it should point to an existing valid folder. */ public Pair<Boolean, File> installPlatformPackage(int apiLevel) { mPackageFilter = createPlatformFilter(apiLevel); open(); File installPath = null; if (mResultPaths != null) { for (Entry<Package, File> entry : mResultPaths.entrySet()) { if (entry.getKey() instanceof PlatformPackage) { installPath = entry.getValue(); break; } } } return Pair.of(mResultCode, installPath); } /** * Displays the update dialog and triggers installation of a new SDK. This works by * requesting a remote platform package with the specified API levels as well as * the first tools or platform-tools packages available. * <p/> * Callers must not try to reuse this dialog after this call. * * @param apiLevels A set of platform API levels to match. * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use * the highest API level available in the repository. * @return A boolean indicating whether the installation was successful (meaning the packages * were either already present, or got installed or updated properly). */ public boolean installNewSdk(Set<Integer> apiLevels) { mPackageFilter = createNewSdkFilter(apiLevels); open(); return mResultCode.booleanValue(); } @Override protected void createContents() { Shell shell = getShell(); shell.setMinimumSize(new Point(450, 100)); shell.setSize(450, 100); mUpdaterData.setWindowShell(shell); GridLayoutBuilder.create(shell).columns(1); Composite composite1 = new Composite(shell, SWT.NONE); composite1.setLayout(new GridLayout(1, false)); GridDataBuilder.create(composite1).fill().grab(); mProgressBar = new ProgressBar(composite1, SWT.NONE); GridDataBuilder.create(mProgressBar).hFill().hGrab(); mStatusText = new Label(composite1, SWT.NONE); mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder GridDataBuilder.create(mStatusText).hFill().hGrab(); } @Override protected void postCreate() { ProgressViewFactory factory = new ProgressViewFactory(); factory.setProgressView(new ProgressView( mStatusText, mProgressBar, null /*buttonStop*/, new SdkLogAdapter(mUpdaterData.getSdkLog()))); mUpdaterData.setTaskFactory(factory); setupSources(); initializeSettings(); if (mUpdaterData.checkIfInitFailed()) { close(); return; } mUpdaterData.broadcastOnSdkLoaded(); mPackageLoader = new PackageLoader(mUpdaterData); } @Override protected void eventLoop() { mPackageLoader.loadPackagesWithInstallTask( mPackageFilter.installFlags(), new IAutoInstallTask() { @Override public Package[] filterLoadedSource(SdkSource source, Package[] packages) { for (Package pkg : packages) { mPackageFilter.visit(pkg); } return packages; } @Override public boolean acceptPackage(Package pkg) { // Is this the package we want to install? return mPackageFilter.accept(pkg); } @Override public void setResult(boolean success, Map<Package, File> installPaths) { // Capture the result from the installation. mResultCode = Boolean.valueOf(success); mResultPaths = installPaths; } @Override public void taskCompleted() { // We can close that window now. close(); } }); super.eventLoop(); } // -- Start of internal part ---------- // Hide everything down-below from SWT designer //$hide>>$ // --- Public API ----------- // --- Internals & UI Callbacks ----------- /** * Used to initialize the sources. */ private void setupSources() { mUpdaterData.setupDefaultSources(); } /** * Initializes settings. */ private void initializeSettings() { mSettingsController = mUpdaterData.getSettingsController(); mSettingsController.loadSettings(); mSettingsController.applySettings(); } // ---- private static abstract class PackageFilter { /** Returns the installer flags for the corresponding mode. */ abstract int installFlags(); /** Visit a new package definition, in case we need to adjust the filter dynamically. */ abstract void visit(Package pkg); /** Checks whether this is the package we've been looking for. */ abstract boolean accept(Package pkg); } public static PackageFilter createExtraFilter( final String vendor, final String path) { return new PackageFilter() { String mVendor = vendor; String mPath = path; @Override boolean accept(Package pkg) { if (pkg instanceof ExtraPackage) { ExtraPackage ep = (ExtraPackage) pkg; if (ep.getVendorId().equals(mVendor)) { // Check actual extra <path> field first if (ep.getPath().equals(mPath)) { return true; } // If not, check whether this is one of the <old-paths> values. for (String oldPath : ep.getOldPaths()) { if (oldPath.equals(mPath)) { return true; } } } } return false; } @Override void visit(Package pkg) { // nop } @Override int installFlags() { return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; } }; } private PackageFilter createPlatformToolsFilter() { return new PackageFilter() { @Override boolean accept(Package pkg) { return pkg instanceof PlatformToolPackage; } @Override void visit(Package pkg) { // nop } @Override int installFlags() { return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; } }; } public static PackageFilter createPlatformFilter(final int apiLevel) { return new PackageFilter() { int mApiLevel = apiLevel; boolean mFindMaxApi = apiLevel == USE_MAX_REMOTE_API_LEVEL; @Override boolean accept(Package pkg) { if (pkg instanceof PlatformPackage) { PlatformPackage pp = (PlatformPackage) pkg; AndroidVersion v = pp.getAndroidVersion(); return !v.isPreview() && v.getApiLevel() == mApiLevel; } return false; } @Override void visit(Package pkg) { // Try to find the max API in all remote packages if (mFindMaxApi && pkg instanceof PlatformPackage && !pkg.isLocal()) { PlatformPackage pp = (PlatformPackage) pkg; AndroidVersion v = pp.getAndroidVersion(); if (!v.isPreview()) { int api = v.getApiLevel(); if (api > mApiLevel) { mApiLevel = api; } } } } @Override int installFlags() { return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; } }; } public static PackageFilter createNewSdkFilter(final Set<Integer> apiLevels) { return new PackageFilter() { int mMaxApiLevel; boolean mFindMaxApi = apiLevels.contains(USE_MAX_REMOTE_API_LEVEL); boolean mNeedTools = true; boolean mNeedPlatformTools = true; @Override boolean accept(Package pkg) { if (!pkg.isLocal()) { if (pkg instanceof PlatformPackage) { PlatformPackage pp = (PlatformPackage) pkg; AndroidVersion v = pp.getAndroidVersion(); if (!v.isPreview()) { int level = v.getApiLevel(); if ((mFindMaxApi && level == mMaxApiLevel) || (level > 0 && apiLevels.contains(level))) { return true; } } } else if (mNeedTools && pkg instanceof ToolPackage) { // We want a tool package. There should be only one, // but in case of error just take the first one. mNeedTools = false; return true; } else if (mNeedPlatformTools && pkg instanceof PlatformToolPackage) { // We want a platform-tool package. There should be only one, // but in case of error just take the first one. mNeedPlatformTools = false; return true; } } return false; } @Override void visit(Package pkg) { // Try to find the max API in all remote packages if (mFindMaxApi && pkg instanceof PlatformPackage && !pkg.isLocal()) { PlatformPackage pp = (PlatformPackage) pkg; AndroidVersion v = pp.getAndroidVersion(); if (!v.isPreview()) { int api = v.getApiLevel(); if (api > mMaxApiLevel) { mMaxApiLevel = api; } } } } @Override int installFlags() { return UpdaterData.NO_TOOLS_MSG; } }; } // End of hiding from SWT Designer //$hide<<$ // ----- }