1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sdkuilib.internal.repository.sdkman2; 18 19 20 import com.android.sdklib.AndroidVersion; 21 import com.android.sdklib.ISdkLog; 22 import com.android.sdklib.internal.repository.packages.ExtraPackage; 23 import com.android.sdklib.internal.repository.packages.Package; 24 import com.android.sdklib.internal.repository.packages.PlatformPackage; 25 import com.android.sdklib.internal.repository.packages.PlatformToolPackage; 26 import com.android.sdklib.internal.repository.packages.ToolPackage; 27 import com.android.sdklib.internal.repository.sources.SdkSource; 28 import com.android.sdkuilib.internal.repository.SettingsController; 29 import com.android.sdkuilib.internal.repository.UpdaterData; 30 import com.android.sdkuilib.internal.repository.sdkman2.PackageLoader.IAutoInstallTask; 31 import com.android.sdkuilib.internal.tasks.ProgressView; 32 import com.android.sdkuilib.internal.tasks.ProgressViewFactory; 33 import com.android.sdkuilib.ui.GridDataBuilder; 34 import com.android.sdkuilib.ui.GridLayoutBuilder; 35 import com.android.sdkuilib.ui.SwtBaseDialog; 36 import com.android.util.Pair; 37 38 import org.eclipse.swt.SWT; 39 import org.eclipse.swt.graphics.Point; 40 import org.eclipse.swt.layout.GridLayout; 41 import org.eclipse.swt.widgets.Composite; 42 import org.eclipse.swt.widgets.Label; 43 import org.eclipse.swt.widgets.ProgressBar; 44 import org.eclipse.swt.widgets.Shell; 45 46 import java.io.File; 47 import java.util.Map; 48 import java.util.Map.Entry; 49 import java.util.Set; 50 51 /** 52 * This is a private implementation of UpdateWindow for ADT, 53 * designed to install a very specific package. 54 * <p/> 55 * Example of usage: 56 * <pre> 57 * AdtUpdateDialog dialog = new AdtUpdateDialog( 58 * AdtPlugin.getDisplay().getActiveShell(), 59 * new AdtConsoleSdkLog(), 60 * sdk.getSdkLocation()); 61 * 62 * Pair<Boolean, File> result = dialog.installExtraPackage( 63 * "android", "compatibility"); //$NON-NLS-1$ //$NON-NLS-2$ 64 * or 65 * Pair<Boolean, File> result = dialog.installPlatformPackage(11); 66 * </pre> 67 */ 68 public class AdtUpdateDialog extends SwtBaseDialog { 69 70 public static final int USE_MAX_REMOTE_API_LEVEL = 0; 71 72 private static final String APP_NAME = "Android SDK Manager"; 73 private final UpdaterData mUpdaterData; 74 75 private Boolean mResultCode = Boolean.FALSE; 76 private Map<Package, File> mResultPaths = null; 77 private SettingsController mSettingsController; 78 private PackageFilter mPackageFilter; 79 private PackageLoader mPackageMananger; 80 81 private ProgressBar mProgressBar; 82 private Label mStatusText; 83 84 /** 85 * Creates a new {@link AdtUpdateDialog}. 86 * Callers will want to call {@link #installExtraPackage} or 87 * {@link #installPlatformPackage} after this. 88 * 89 * @param parentShell The existing parent shell. Must not be null. 90 * @param sdkLog An SDK logger. Must not be null. 91 * @param osSdkRoot The current SDK root OS path. Must not be null or empty. 92 */ AdtUpdateDialog( Shell parentShell, ISdkLog sdkLog, String osSdkRoot)93 public AdtUpdateDialog( 94 Shell parentShell, 95 ISdkLog sdkLog, 96 String osSdkRoot) { 97 super(parentShell, SWT.NONE, APP_NAME); 98 mUpdaterData = new UpdaterData(osSdkRoot, sdkLog); 99 } 100 101 /** 102 * Displays the update dialog and triggers installation of the requested {@code extra} 103 * package with the specified vendor and path attributes. 104 * <p/> 105 * Callers must not try to reuse this dialog after this call. 106 * 107 * @param vendor The extra package vendor string to match. 108 * @param path The extra package path string to match. 109 * @return A boolean indicating whether the installation was successful (meaning the package 110 * was either already present, or got installed or updated properly) and a {@link File} 111 * with the path to the root folder of the package. The file is null when the boolean 112 * is false, otherwise it should point to an existing valid folder. 113 * @wbp.parser.entryPoint 114 */ installExtraPackage(String vendor, String path)115 public Pair<Boolean, File> installExtraPackage(String vendor, String path) { 116 mPackageFilter = createExtraFilter(vendor, path); 117 open(); 118 119 File installPath = null; 120 if (mResultPaths != null) { 121 for (Entry<Package, File> entry : mResultPaths.entrySet()) { 122 if (entry.getKey() instanceof ExtraPackage) { 123 installPath = entry.getValue(); 124 break; 125 } 126 } 127 } 128 129 return Pair.of(mResultCode, installPath); 130 } 131 132 /** 133 * Displays the update dialog and triggers installation of the requested platform 134 * package with the specified API level. 135 * <p/> 136 * Callers must not try to reuse this dialog after this call. 137 * 138 * @param apiLevel The platform API level to match. 139 * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use 140 * the highest API level available on the remote repository. 141 * @return A boolean indicating whether the installation was successful (meaning the package 142 * was either already present, or got installed or updated properly) and a {@link File} 143 * with the path to the root folder of the package. The file is null when the boolean 144 * is false, otherwise it should point to an existing valid folder. 145 */ installPlatformPackage(int apiLevel)146 public Pair<Boolean, File> installPlatformPackage(int apiLevel) { 147 mPackageFilter = createPlatformFilter(apiLevel); 148 open(); 149 150 File installPath = null; 151 if (mResultPaths != null) { 152 for (Entry<Package, File> entry : mResultPaths.entrySet()) { 153 if (entry.getKey() instanceof PlatformPackage) { 154 installPath = entry.getValue(); 155 break; 156 } 157 } 158 } 159 160 return Pair.of(mResultCode, installPath); 161 } 162 163 /** 164 * Displays the update dialog and triggers installation of a new SDK. This works by 165 * requesting a remote platform package with the specified API levels as well as 166 * the first tools or platform-tools packages available. 167 * <p/> 168 * Callers must not try to reuse this dialog after this call. 169 * 170 * @param apiLevels A set of platform API levels to match. 171 * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use 172 * the highest API level available in the repository. 173 * @return A boolean indicating whether the installation was successful (meaning the packages 174 * were either already present, or got installed or updated properly). 175 */ installNewSdk(Set<Integer> apiLevels)176 public boolean installNewSdk(Set<Integer> apiLevels) { 177 mPackageFilter = createNewSdkFilter(apiLevels); 178 open(); 179 return mResultCode.booleanValue(); 180 } 181 182 @Override createContents()183 protected void createContents() { 184 Shell shell = getShell(); 185 shell.setMinimumSize(new Point(450, 100)); 186 shell.setSize(450, 100); 187 188 mUpdaterData.setWindowShell(shell); 189 190 GridLayoutBuilder.create(shell).columns(1); 191 192 Composite composite1 = new Composite(shell, SWT.NONE); 193 composite1.setLayout(new GridLayout(1, false)); 194 GridDataBuilder.create(composite1).fill().grab(); 195 196 mProgressBar = new ProgressBar(composite1, SWT.NONE); 197 GridDataBuilder.create(mProgressBar).hFill().hGrab(); 198 199 mStatusText = new Label(composite1, SWT.NONE); 200 mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder 201 GridDataBuilder.create(mStatusText).hFill().hGrab(); 202 } 203 204 @Override postCreate()205 protected void postCreate() { 206 ProgressViewFactory factory = new ProgressViewFactory(); 207 factory.setProgressView(new ProgressView( 208 mStatusText, 209 mProgressBar, 210 null /*buttonStop*/, 211 new SdkLogAdapter(mUpdaterData.getSdkLog()))); 212 mUpdaterData.setTaskFactory(factory); 213 214 setupSources(); 215 initializeSettings(); 216 217 if (mUpdaterData.checkIfInitFailed()) { 218 close(); 219 return; 220 } 221 222 mUpdaterData.broadcastOnSdkLoaded(); 223 224 mPackageMananger = new PackageLoader(mUpdaterData); 225 } 226 227 @Override eventLoop()228 protected void eventLoop() { 229 mPackageMananger.loadPackagesWithInstallTask( 230 mPackageFilter.installFlags(), 231 new IAutoInstallTask() { 232 @Override 233 public Package[] filterLoadedSource(SdkSource source, Package[] packages) { 234 for (Package pkg : packages) { 235 mPackageFilter.visit(pkg); 236 } 237 return packages; 238 } 239 240 @Override 241 public boolean acceptPackage(Package pkg) { 242 // Is this the package we want to install? 243 return mPackageFilter.accept(pkg); 244 } 245 246 @Override 247 public void setResult(boolean success, Map<Package, File> installPaths) { 248 // Capture the result from the installation. 249 mResultCode = Boolean.valueOf(success); 250 mResultPaths = installPaths; 251 } 252 253 @Override 254 public void taskCompleted() { 255 // We can close that window now. 256 close(); 257 } 258 }); 259 260 super.eventLoop(); 261 } 262 263 // -- Start of internal part ---------- 264 // Hide everything down-below from SWT designer 265 //$hide>>$ 266 267 // --- Public API ----------- 268 269 270 // --- Internals & UI Callbacks ----------- 271 272 /** 273 * Used to initialize the sources. 274 */ setupSources()275 private void setupSources() { 276 mUpdaterData.setupDefaultSources(); 277 } 278 279 /** 280 * Initializes settings. 281 */ initializeSettings()282 private void initializeSettings() { 283 mSettingsController = mUpdaterData.getSettingsController(); 284 mSettingsController.loadSettings(); 285 mSettingsController.applySettings(); 286 } 287 288 // ---- 289 290 private static abstract class PackageFilter { 291 /** Returns the installer flags for the corresponding mode. */ installFlags()292 abstract int installFlags(); 293 294 /** Visit a new package definition, in case we need to adjust the filter dynamically. */ visit(Package pkg)295 abstract void visit(Package pkg); 296 297 /** Checks whether this is the package we've been looking for. */ accept(Package pkg)298 abstract boolean accept(Package pkg); 299 } 300 createExtraFilter( final String vendor, final String path)301 public static PackageFilter createExtraFilter( 302 final String vendor, 303 final String path) { 304 return new PackageFilter() { 305 String mVendor = vendor; 306 String mPath = path; 307 308 @Override 309 boolean accept(Package pkg) { 310 if (pkg instanceof ExtraPackage) { 311 ExtraPackage ep = (ExtraPackage) pkg; 312 if (ep.getVendorId().equals(mVendor)) { 313 // Check actual extra <path> field first 314 if (ep.getPath().equals(mPath)) { 315 return true; 316 } 317 // If not, check whether this is one of the <old-paths> values. 318 for (String oldPath : ep.getOldPaths()) { 319 if (oldPath.equals(mPath)) { 320 return true; 321 } 322 } 323 } 324 } 325 return false; 326 } 327 328 @Override 329 void visit(Package pkg) { 330 // nop 331 } 332 333 @Override 334 int installFlags() { 335 return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; 336 } 337 }; 338 } 339 createPlatformFilter(final int apiLevel)340 public static PackageFilter createPlatformFilter(final int apiLevel) { 341 return new PackageFilter() { 342 int mApiLevel = apiLevel; 343 boolean mFindMaxApi = apiLevel == USE_MAX_REMOTE_API_LEVEL; 344 345 @Override 346 boolean accept(Package pkg) { 347 if (pkg instanceof PlatformPackage) { 348 PlatformPackage pp = (PlatformPackage) pkg; 349 AndroidVersion v = pp.getVersion(); 350 return !v.isPreview() && v.getApiLevel() == mApiLevel; 351 } 352 return false; 353 } 354 355 @Override 356 void visit(Package pkg) { 357 // Try to find the max API in all remote packages 358 if (mFindMaxApi && 359 pkg instanceof PlatformPackage && 360 !pkg.isLocal()) { 361 PlatformPackage pp = (PlatformPackage) pkg; 362 AndroidVersion v = pp.getVersion(); 363 if (!v.isPreview()) { 364 int api = v.getApiLevel(); 365 if (api > mApiLevel) { 366 mApiLevel = api; 367 } 368 } 369 } 370 } 371 372 @Override 373 int installFlags() { 374 return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; 375 } 376 }; 377 } 378 379 public static PackageFilter createNewSdkFilter(final Set<Integer> apiLevels) { 380 return new PackageFilter() { 381 int mMaxApiLevel; 382 boolean mFindMaxApi = apiLevels.contains(USE_MAX_REMOTE_API_LEVEL); 383 boolean mNeedTools = true; 384 boolean mNeedPlatformTools = true; 385 386 @Override 387 boolean accept(Package pkg) { 388 if (!pkg.isLocal()) { 389 if (pkg instanceof PlatformPackage) { 390 PlatformPackage pp = (PlatformPackage) pkg; 391 AndroidVersion v = pp.getVersion(); 392 if (!v.isPreview()) { 393 int level = v.getApiLevel(); 394 if ((mFindMaxApi && level == mMaxApiLevel) || 395 (level > 0 && apiLevels.contains(level))) { 396 return true; 397 } 398 } 399 } else if (mNeedTools && pkg instanceof ToolPackage) { 400 // We want a tool package. There should be only one, 401 // but in case of error just take the first one. 402 mNeedTools = false; 403 return true; 404 } else if (mNeedPlatformTools && pkg instanceof PlatformToolPackage) { 405 // We want a platform-tool package. There should be only one, 406 // but in case of error just take the first one. 407 mNeedPlatformTools = false; 408 return true; 409 } 410 } 411 return false; 412 } 413 414 @Override 415 void visit(Package pkg) { 416 // Try to find the max API in all remote packages 417 if (mFindMaxApi && 418 pkg instanceof PlatformPackage && 419 !pkg.isLocal()) { 420 PlatformPackage pp = (PlatformPackage) pkg; 421 AndroidVersion v = pp.getVersion(); 422 if (!v.isPreview()) { 423 int api = v.getApiLevel(); 424 if (api > mMaxApiLevel) { 425 mMaxApiLevel = api; 426 } 427 } 428 } 429 } 430 431 @Override 432 int installFlags() { 433 return UpdaterData.NO_TOOLS_MSG; 434 } 435 }; 436 } 437 438 439 440 // End of hiding from SWT Designer 441 //$hide<<$ 442 443 // ----- 444 445 } 446