1/* 2 * Copyright (C) 2012 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 17package com.android.sdkuilib.internal.widgets; 18 19import com.android.SdkConstants; 20import com.android.prefs.AndroidLocation.AndroidLocationException; 21import com.android.resources.Density; 22import com.android.resources.ScreenSize; 23import com.android.sdklib.IAndroidTarget; 24import com.android.sdklib.ISystemImage; 25import com.android.sdklib.SdkManager; 26import com.android.sdklib.devices.Camera; 27import com.android.sdklib.devices.CameraLocation; 28import com.android.sdklib.devices.Device; 29import com.android.sdklib.devices.DeviceManager; 30import com.android.sdklib.devices.Hardware; 31import com.android.sdklib.devices.Screen; 32import com.android.sdklib.devices.Storage; 33import com.android.sdklib.internal.avd.AvdInfo; 34import com.android.sdklib.internal.avd.AvdManager; 35import com.android.sdklib.internal.avd.AvdManager.AvdConflict; 36import com.android.sdklib.internal.avd.HardwareProperties; 37import com.android.sdkuilib.internal.repository.icons.ImageFactory; 38import com.android.sdkuilib.ui.GridDialog; 39import com.android.utils.ILogger; 40import com.android.utils.Pair; 41 42import org.eclipse.jface.dialogs.IDialogConstants; 43import org.eclipse.swt.SWT; 44import org.eclipse.swt.events.ModifyEvent; 45import org.eclipse.swt.events.ModifyListener; 46import org.eclipse.swt.events.SelectionAdapter; 47import org.eclipse.swt.events.SelectionEvent; 48import org.eclipse.swt.events.VerifyEvent; 49import org.eclipse.swt.events.VerifyListener; 50import org.eclipse.swt.layout.GridData; 51import org.eclipse.swt.layout.GridLayout; 52import org.eclipse.swt.widgets.Button; 53import org.eclipse.swt.widgets.Combo; 54import org.eclipse.swt.widgets.Composite; 55import org.eclipse.swt.widgets.Control; 56import org.eclipse.swt.widgets.FileDialog; 57import org.eclipse.swt.widgets.Group; 58import org.eclipse.swt.widgets.Label; 59import org.eclipse.swt.widgets.Shell; 60import org.eclipse.swt.widgets.Text; 61 62import java.io.File; 63import java.util.ArrayList; 64import java.util.List; 65import java.util.Map; 66import java.util.TreeMap; 67 68public class AvdCreationDialog extends GridDialog { 69 70 private AvdManager mAvdManager; 71 private ImageFactory mImageFactory; 72 private ILogger mSdkLog; 73 private AvdInfo mAvdInfo; 74 75 // A map from manufacturers to their list of devices. 76 private Map<String, List<Device>> mDeviceMap; 77 private final TreeMap<String, IAndroidTarget> mCurrentTargets = 78 new TreeMap<String, IAndroidTarget>(); 79 80 private Button mOkButton; 81 82 private Text mAvdName; 83 84 private Combo mDeviceManufacturer; 85 private Combo mDeviceName; 86 87 private Combo mTarget; 88 private Combo mAbi; 89 90 private Combo mFrontCamera; 91 private Combo mBackCamera; 92 93 private Button mSnapshot; 94 private Button mGpuEmulation; 95 96 private Text mRam; 97 private Text mVmHeap; 98 99 private Text mDataPartition; 100 private Combo mDataPartitionSize; 101 102 private Button mSdCardSizeRadio; 103 private Text mSdCardSize; 104 private Combo mSdCardSizeCombo; 105 private Button mSdCardFileRadio; 106 private Text mSdCardFile; 107 private Button mBrowseSdCard; 108 109 private Button mForceCreation; 110 private Composite mStatusComposite; 111 112 private Label mStatusIcon; 113 private Label mStatusLabel; 114 115 /** 116 * {@link VerifyListener} for {@link Text} widgets that should only contains 117 * numbers. 118 */ 119 private final VerifyListener mDigitVerifier = new VerifyListener() { 120 @Override 121 public void verifyText(VerifyEvent event) { 122 int count = event.text.length(); 123 for (int i = 0; i < count; i++) { 124 char c = event.text.charAt(i); 125 if (c < '0' || c > '9') { 126 event.doit = false; 127 return; 128 } 129 } 130 } 131 }; 132 133 public AvdCreationDialog(Shell shell, 134 AvdManager avdManager, 135 ImageFactory imageFactory, 136 ILogger log, 137 AvdInfo editAvdInfo) { 138 139 super(shell, 2, false); 140 mAvdManager = avdManager; 141 mImageFactory = imageFactory; 142 mSdkLog = log; 143 mAvdInfo = editAvdInfo; 144 145 mDeviceMap = new TreeMap<String, List<Device>>(); 146 147 SdkManager sdkMan = avdManager.getSdkManager(); 148 if (sdkMan != null && sdkMan.getLocation() != null) { 149 List<Device> devices = (new DeviceManager(log)).getDevices(sdkMan.getLocation()); 150 for (Device d : devices) { 151 List<Device> list; 152 if (mDeviceMap.containsKey(d.getManufacturer())) { 153 list = mDeviceMap.get(d.getManufacturer()); 154 } else { 155 list = new ArrayList<Device>(); 156 mDeviceMap.put(d.getManufacturer(), list); 157 } 158 list.add(d); 159 } 160 } 161 } 162 163 @Override 164 protected Control createContents(Composite parent) { 165 Control control = super.createContents(parent); 166 getShell().setText(mAvdInfo == null ? "Create new Android Virtual Device (AVD)" 167 : "Edit Android Virtual Device (AVD)"); 168 169 mOkButton = getButton(IDialogConstants.OK_ID); 170 171 if (mAvdInfo != null) { 172 fillExistingAvdInfo(mAvdInfo); 173 } 174 175 validatePage(); 176 return control; 177 } 178 179 @Override 180 public void createDialogContent(Composite parent) { 181 182 Label label; 183 String tooltip; 184 ValidateListener validateListener = new ValidateListener(); 185 186 // --- avd name 187 label = new Label(parent, SWT.NONE); 188 label.setText("AVD Name:"); 189 tooltip = "The name of the Android Virtual Device"; 190 label.setToolTipText(tooltip); 191 mAvdName = new Text(parent, SWT.BORDER); 192 mAvdName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 193 mAvdName.addModifyListener(new CreateNameModifyListener()); 194 195 // --- device selection 196 label = new Label(parent, SWT.NONE); 197 label.setText("Device\nManufacturer:"); 198 tooltip = "The manufacturer of the device this AVD will be based on"; 199 mDeviceManufacturer = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 200 for (String manufacturer : mDeviceMap.keySet()) { 201 mDeviceManufacturer.add(manufacturer); 202 } 203 mDeviceManufacturer.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 204 mDeviceManufacturer.addSelectionListener(new SelectionAdapter() { 205 @Override 206 public void widgetSelected(SelectionEvent e) { 207 reloadDeviceNameCombo(); 208 validatePage(); 209 } 210 }); 211 212 label = new Label(parent, SWT.NONE); 213 label.setText("Device Name:"); 214 tooltip = "The name of the device this AVD will be based on"; 215 mDeviceName = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 216 mDeviceName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 217 mDeviceName.addSelectionListener(new DeviceSelectionListener()); 218 219 // --- api target 220 label = new Label(parent, SWT.NONE); 221 label.setText("Target:"); 222 tooltip = "The target API of the AVD"; 223 label.setToolTipText(tooltip); 224 mTarget = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 225 mTarget.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 226 mTarget.setToolTipText(tooltip); 227 mTarget.addSelectionListener(new SelectionAdapter() { 228 @Override 229 public void widgetSelected(SelectionEvent e) { 230 reloadAbiTypeCombo(); 231 validatePage(); 232 } 233 }); 234 235 reloadTargetCombo(); 236 237 // --- avd ABIs 238 label = new Label(parent, SWT.NONE); 239 label.setText("CPU/ABI:"); 240 tooltip = "The CPU/ABI of the virtual device"; 241 label.setToolTipText(tooltip); 242 mAbi = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 243 mAbi.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 244 mAbi.setToolTipText(tooltip); 245 mAbi.addSelectionListener(validateListener); 246 247 label = new Label(parent, SWT.NONE); 248 label.setText("Front Camera:"); 249 tooltip = ""; 250 label.setToolTipText(tooltip); 251 mFrontCamera = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 252 mFrontCamera.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 253 mFrontCamera.add("None"); 254 mFrontCamera.add("Emulated"); 255 mFrontCamera.add("Webcam0"); 256 mFrontCamera.select(0); 257 258 label = new Label(parent, SWT.NONE); 259 label.setText("Back Camera:"); 260 tooltip = ""; 261 label.setToolTipText(tooltip); 262 mBackCamera = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 263 mBackCamera.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 264 mBackCamera.add("None"); 265 mBackCamera.add("Emulated"); 266 mBackCamera.add("Webcam0"); 267 mBackCamera.select(0); 268 269 toggleCameras(); 270 271 // --- memory options group 272 label = new Label(parent, SWT.NONE); 273 label.setText("Memory Options:"); 274 275 276 Group memoryGroup = new Group(parent, SWT.BORDER); 277 memoryGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 278 memoryGroup.setLayout(new GridLayout(4, false)); 279 280 label = new Label(memoryGroup, SWT.NONE); 281 label.setText("RAM:"); 282 tooltip = "The amount of RAM the emulated device should have in MiB"; 283 label.setToolTipText(tooltip); 284 mRam = new Text(memoryGroup, SWT.BORDER); 285 mRam.addVerifyListener(mDigitVerifier); 286 mRam.addModifyListener(validateListener); 287 mRam.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 288 289 label = new Label(memoryGroup, SWT.NONE); 290 label.setText("VM Heap:"); 291 tooltip = "The amount of memory, in MiB, available to typical Android applications"; 292 label.setToolTipText(tooltip); 293 mVmHeap = new Text(memoryGroup, SWT.BORDER); 294 mVmHeap.addVerifyListener(mDigitVerifier); 295 mVmHeap.addModifyListener(validateListener); 296 mVmHeap.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 297 mVmHeap.setToolTipText(tooltip); 298 299 // --- Data partition group 300 label = new Label(parent, SWT.NONE); 301 label.setText("Internal Storage:"); 302 tooltip = "The size of the data partition on the device."; 303 Group storageGroup = new Group(parent, SWT.NONE); 304 storageGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 305 storageGroup.setLayout(new GridLayout(2, false)); 306 mDataPartition = new Text(storageGroup, SWT.BORDER); 307 mDataPartition.setText("200"); 308 mDataPartition.addVerifyListener(mDigitVerifier); 309 mDataPartition.addModifyListener(validateListener); 310 mDataPartition.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 311 mDataPartitionSize = new Combo(storageGroup, SWT.READ_ONLY | SWT.DROP_DOWN); 312 mDataPartitionSize.add("MiB"); 313 mDataPartitionSize.add("GiB"); 314 mDataPartitionSize.select(0); 315 mDataPartitionSize.addModifyListener(validateListener); 316 317 // --- sd card group 318 label = new Label(parent, SWT.NONE); 319 label.setText("SD Card:"); 320 label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 321 false, false)); 322 323 final Group sdCardGroup = new Group(parent, SWT.NONE); 324 sdCardGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 325 sdCardGroup.setLayout(new GridLayout(3, false)); 326 327 mSdCardSizeRadio = new Button(sdCardGroup, SWT.RADIO); 328 mSdCardSizeRadio.setText("Size:"); 329 mSdCardSizeRadio.setToolTipText("Create a new SD Card file"); 330 mSdCardSizeRadio.addSelectionListener(new SelectionAdapter() { 331 @Override 332 public void widgetSelected(SelectionEvent arg0) { 333 boolean sizeMode = mSdCardSizeRadio.getSelection(); 334 enableSdCardWidgets(sizeMode); 335 validatePage(); 336 } 337 }); 338 339 mSdCardSize = new Text(sdCardGroup, SWT.BORDER); 340 mSdCardSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 341 mSdCardSize.addVerifyListener(mDigitVerifier); 342 mSdCardSize.addModifyListener(validateListener); 343 mSdCardSize.setToolTipText("Size of the new SD Card file (must be at least 9 MiB)"); 344 345 mSdCardSizeCombo = new Combo(sdCardGroup, SWT.DROP_DOWN | SWT.READ_ONLY); 346 mSdCardSizeCombo.add("KiB"); 347 mSdCardSizeCombo.add("MiB"); 348 mSdCardSizeCombo.add("GiB"); 349 mSdCardSizeCombo.select(1); 350 mSdCardSizeCombo.addSelectionListener(validateListener); 351 352 mSdCardFileRadio = new Button(sdCardGroup, SWT.RADIO); 353 mSdCardFileRadio.setText("File:"); 354 mSdCardFileRadio.setToolTipText("Use an existing file for the SD Card"); 355 356 mSdCardFile = new Text(sdCardGroup, SWT.BORDER); 357 mSdCardFile.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 358 mSdCardFile.addModifyListener(validateListener); 359 mSdCardFile.setToolTipText("File to use for the SD Card"); 360 361 mBrowseSdCard = new Button(sdCardGroup, SWT.PUSH); 362 mBrowseSdCard.setText("Browse..."); 363 mBrowseSdCard.setToolTipText("Select the file to use for the SD Card"); 364 mBrowseSdCard.addSelectionListener(new SelectionAdapter() { 365 @Override 366 public void widgetSelected(SelectionEvent arg0) { 367 onBrowseSdCard(); 368 validatePage(); 369 } 370 }); 371 372 mSdCardSizeRadio.setSelection(true); 373 enableSdCardWidgets(true); 374 375 // --- avd options group 376 label = new Label(parent, SWT.NONE); 377 label.setText("Emulation Options:"); 378 Group optionsGroup = new Group(parent, SWT.NONE); 379 optionsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 380 optionsGroup.setLayout(new GridLayout(2, true)); 381 mSnapshot = new Button(optionsGroup, SWT.CHECK); 382 mSnapshot.setText("Snapshot"); 383 mSnapshot.setToolTipText("Emulator's state will be persisted between emulator executions"); 384 mSnapshot.addSelectionListener(validateListener); 385 mGpuEmulation = new Button(optionsGroup, SWT.CHECK); 386 mGpuEmulation.setText("GPU Emulation"); 387 mGpuEmulation.setToolTipText("Enable hardware OpenGLES emulation"); 388 mGpuEmulation.addSelectionListener(validateListener); 389 390 // --- force creation group 391 mForceCreation = new Button(parent, SWT.CHECK); 392 mForceCreation.setText("Override the existing AVD with the same name"); 393 mForceCreation 394 .setToolTipText("There's already an AVD with the same name. Check this to delete it and replace it by the new AVD."); 395 mForceCreation.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, 396 true, false, 2, 1)); 397 mForceCreation.setEnabled(false); 398 mForceCreation.addSelectionListener(validateListener); 399 400 // add a separator to separate from the ok/cancel button 401 label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); 402 label.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1)); 403 404 // add stuff for the error display 405 mStatusComposite = new Composite(parent, SWT.NONE); 406 mStatusComposite.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, 407 true, false, 3, 1)); 408 GridLayout gl; 409 mStatusComposite.setLayout(gl = new GridLayout(2, false)); 410 gl.marginHeight = gl.marginWidth = 0; 411 412 mStatusIcon = new Label(mStatusComposite, SWT.NONE); 413 mStatusIcon.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 414 false, false)); 415 mStatusLabel = new Label(mStatusComposite, SWT.NONE); 416 mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 417 mStatusLabel.setText(""); //$NON-NLS-1$ 418 419 } 420 421 /** 422 * {@link ModifyListener} used for live-validation of the fields content. 423 */ 424 private class ValidateListener extends SelectionAdapter implements ModifyListener { 425 @Override 426 public void modifyText(ModifyEvent e) { 427 validatePage(); 428 } 429 430 @Override 431 public void widgetSelected(SelectionEvent e) { 432 super.widgetSelected(e); 433 validatePage(); 434 } 435 } 436 437 /** 438 * Callback when the AVD name is changed. When creating a new AVD, enables 439 * the force checkbox if the name is a duplicate. When editing an existing 440 * AVD, it's OK for the name to match the existing AVD. 441 */ 442 private class CreateNameModifyListener implements ModifyListener { 443 @Override 444 public void modifyText(ModifyEvent e) { 445 String name = mAvdName.getText().trim(); 446 if (mAvdInfo == null || !name.equals(mAvdInfo.getName())) { 447 // Case where we're creating a new AVD or editing an existing 448 // one 449 // and the AVD name has been changed... check for name 450 // uniqueness. 451 452 Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(name); 453 if (conflict.getFirst() != AvdManager.AvdConflict.NO_CONFLICT) { 454 // If we're changing the state from disabled to enabled, 455 // make sure 456 // to uncheck the button, to force the user to voluntarily 457 // re-enforce it. 458 // This happens when editing an existing AVD and changing 459 // the name from 460 // the existing AVD to another different existing AVD. 461 if (!mForceCreation.isEnabled()) { 462 mForceCreation.setEnabled(true); 463 mForceCreation.setSelection(false); 464 } 465 } else { 466 mForceCreation.setEnabled(false); 467 mForceCreation.setSelection(false); 468 } 469 } else { 470 // Case where we're editing an existing AVD with the name 471 // unchanged. 472 473 mForceCreation.setEnabled(false); 474 mForceCreation.setSelection(false); 475 } 476 validatePage(); 477 } 478 } 479 480 private class DeviceSelectionListener extends SelectionAdapter { 481 482 @Override 483 public void widgetSelected(SelectionEvent arg0) { 484 Device currentDevice = null; 485 for (Device d : mDeviceMap.get(mDeviceManufacturer.getText())) { 486 if (d.getName().equals(mDeviceName.getText())) { 487 currentDevice = d; 488 break; 489 } 490 } 491 492 if (currentDevice != null) { 493 Hardware hw = currentDevice.getDefaultHardware(); 494 Long ram = hw.getRam().getSizeAsUnit(Storage.Unit.MiB); 495 mRam.setText(Long.toString(ram)); 496 497 // Set the default VM heap size. This is based on the Android CDD minimums for each 498 // screen size and density. 499 Screen s = hw.getScreen(); 500 ScreenSize size = s.getSize(); 501 Density density = s.getPixelDensity(); 502 int vmHeapSize = 32; 503 if (size.equals(ScreenSize.XLARGE)) { 504 switch (density) { 505 case LOW: 506 case MEDIUM: 507 vmHeapSize = 32; 508 break; 509 case TV: 510 case HIGH: 511 vmHeapSize = 64; 512 break; 513 case XHIGH: 514 case XXHIGH: 515 vmHeapSize = 128; 516 } 517 } else { 518 switch (density) { 519 case LOW: 520 case MEDIUM: 521 vmHeapSize = 16; 522 break; 523 case TV: 524 case HIGH: 525 vmHeapSize = 32; 526 break; 527 case XHIGH: 528 case XXHIGH: 529 vmHeapSize = 64; 530 531 } 532 } 533 mVmHeap.setText(Integer.toString(vmHeapSize)); 534 } 535 536 toggleCameras(); 537 validatePage(); 538 } 539 540 } 541 542 private void toggleCameras() { 543 mFrontCamera.setEnabled(false); 544 mBackCamera.setEnabled(false); 545 if (mDeviceName.getSelectionIndex() >= 0) { 546 List<Device> devices = mDeviceMap.get(mDeviceManufacturer.getText()); 547 for (Device d : devices) { 548 if (mDeviceName.getText().equals(d.getName())) { 549 for (Camera c : d.getDefaultHardware().getCameras()) { 550 if (CameraLocation.FRONT.equals(c.getLocation())) { 551 mFrontCamera.setEnabled(true); 552 } 553 if (CameraLocation.BACK.equals(c.getLocation())) { 554 mBackCamera.setEnabled(true); 555 } 556 } 557 } 558 } 559 560 } 561 } 562 563 private void reloadDeviceNameCombo() { 564 mDeviceName.removeAll(); 565 if (mDeviceMap.containsKey(mDeviceManufacturer.getText())) { 566 for (final Device d : mDeviceMap.get(mDeviceManufacturer.getText())) { 567 mDeviceName.add(d.getName()); 568 } 569 } 570 571 } 572 573 private void reloadTargetCombo() { 574 String selected = null; 575 int index = mTarget.getSelectionIndex(); 576 if (index >= 0) { 577 selected = mTarget.getItem(index); 578 } 579 580 mCurrentTargets.clear(); 581 mTarget.removeAll(); 582 583 boolean found = false; 584 index = -1; 585 586 SdkManager sdkManager = mAvdManager.getSdkManager(); 587 if (sdkManager != null) { 588 for (IAndroidTarget target : sdkManager.getTargets()) { 589 String name; 590 if (target.isPlatform()) { 591 name = String.format("%s - API Level %s", 592 target.getName(), 593 target.getVersion().getApiString()); 594 } else { 595 name = String.format("%s (%s) - API Level %s", 596 target.getName(), 597 target.getVendor(), 598 target.getVersion().getApiString()); 599 } 600 mCurrentTargets.put(name, target); 601 mTarget.add(name); 602 if (!found) { 603 index++; 604 found = name.equals(selected); 605 } 606 } 607 } 608 609 mTarget.setEnabled(mCurrentTargets.size() > 0); 610 611 if (found) { 612 mTarget.select(index); 613 } 614 } 615 616 /** 617 * Reload all the abi types in the selection list 618 */ 619 private void reloadAbiTypeCombo() { 620 String selected = null; 621 boolean found = false; 622 623 int index = mTarget.getSelectionIndex(); 624 if (index >= 0) { 625 String targetName = mTarget.getItem(index); 626 IAndroidTarget target = mCurrentTargets.get(targetName); 627 628 ISystemImage[] systemImages = getSystemImages(target); 629 630 mAbi.setEnabled(systemImages.length > 1); 631 632 // If user explicitly selected an ABI before, preserve that option 633 // If user did not explicitly select before (only one option before) 634 // force them to select 635 index = mAbi.getSelectionIndex(); 636 if (index >= 0 && mAbi.getItemCount() > 1) { 637 selected = mAbi.getItem(index); 638 } 639 640 mAbi.removeAll(); 641 642 int i; 643 for (i = 0; i < systemImages.length; i++) { 644 String prettyAbiType = AvdInfo.getPrettyAbiType(systemImages[i].getAbiType()); 645 mAbi.add(prettyAbiType); 646 if (!found) { 647 found = prettyAbiType.equals(selected); 648 if (found) { 649 mAbi.select(i); 650 } 651 } 652 } 653 654 if (systemImages.length == 1) { 655 mAbi.select(0); 656 } 657 } 658 } 659 660 /** 661 * Enable or disable the sd card widgets. 662 * 663 * @param sizeMode if true the size-based widgets are to be enabled, and the 664 * file-based ones disabled. 665 */ 666 private void enableSdCardWidgets(boolean sizeMode) { 667 mSdCardSize.setEnabled(sizeMode); 668 mSdCardSizeCombo.setEnabled(sizeMode); 669 670 mSdCardFile.setEnabled(!sizeMode); 671 mBrowseSdCard.setEnabled(!sizeMode); 672 } 673 674 private void onBrowseSdCard() { 675 FileDialog dlg = new FileDialog(getContents().getShell(), SWT.OPEN); 676 dlg.setText("Choose SD Card image file."); 677 678 String fileName = dlg.open(); 679 if (fileName != null) { 680 mSdCardFile.setText(fileName); 681 } 682 } 683 684 @Override 685 public void okPressed() { 686 if (createAvd()) { 687 super.okPressed(); 688 } 689 } 690 691 private void validatePage() { 692 String error = null; 693 String warning = null; 694 boolean valid = true; 695 if (mAvdName.getText().isEmpty()) { 696 valid = false; 697 } 698 699 if (mDeviceManufacturer.getSelectionIndex() < 0 || mDeviceName.getSelectionIndex() < 0) { 700 valid = false; 701 } 702 703 if (mTarget.getSelectionIndex() < 0 || mAbi.getSelectionIndex() < 0) { 704 valid = false; 705 } 706 707 if (mRam.getText().isEmpty()) { 708 valid = false; 709 } 710 711 if (mVmHeap.getText().isEmpty()) { 712 valid = false; 713 } 714 715 if (mDataPartition.getText().isEmpty() || mDataPartitionSize.getSelectionIndex() < 0) { 716 valid = false; 717 error = "Data partition must be a valid file size."; 718 } 719 720 // validate sdcard size or file 721 if (mSdCardSizeRadio.getSelection()) { 722 if (!mSdCardSize.getText().isEmpty() && mSdCardSizeCombo.getSelectionIndex() >= 0) { 723 try { 724 long sdSize = Long.parseLong(mSdCardSize.getText()); 725 726 int sizeIndex = mSdCardSizeCombo.getSelectionIndex(); 727 if (sizeIndex >= 0) { 728 // index 0 shifts by 10 (1024=K), index 1 by 20, etc. 729 sdSize <<= 10 * (1 + sizeIndex); 730 } 731 732 if (sdSize < AvdManager.SDCARD_MIN_BYTE_SIZE || 733 sdSize > AvdManager.SDCARD_MAX_BYTE_SIZE) { 734 valid = false; 735 error = "SD Card size is invalid. Range is 9 MiB..1023 GiB."; 736 } 737 } catch (NumberFormatException e) { 738 valid = false; 739 error = " SD Card size must be a valid integer between 9 MiB and 1023 GiB"; 740 } 741 } 742 } else { 743 if (mSdCardFile.getText().isEmpty() || !new File(mSdCardFile.getText()).isFile()) { 744 valid = false; 745 error = "SD Card path isn't valid."; 746 } 747 } 748 749 if (mForceCreation.isEnabled() && !mForceCreation.getSelection()) { 750 valid = false; 751 error = String.format( 752 "The AVD name '%s' is already used.\n" + 753 "Check \"Override the existing AVD\" to delete the existing one.", 754 mAvdName.getText()); 755 } 756 757 if (mAvdInfo != null && !mAvdInfo.getName().equals(mAvdName.getText())) { 758 warning = String.format("The AVD '%1$s' will be duplicated into '%2$s'.", 759 mAvdInfo.getName(), 760 mAvdName.getText()); 761 } 762 763 if (mGpuEmulation.getSelection() && mSnapshot.getSelection()) { 764 valid = false; 765 error = "GPU Emulation and Snapshot cannot be used simultaneously"; 766 } 767 768 mOkButton.setEnabled(valid); 769 if (error != null) { 770 mStatusIcon.setImage(mImageFactory.getImageByName("reject_icon16.png")); //$NON-NLS-1$ 771 mStatusLabel.setText(error); 772 } else if (warning != null) { 773 mStatusIcon.setImage(mImageFactory.getImageByName("warning_icon16.png")); //$NON-NLS-1$ 774 mStatusLabel.setText(warning); 775 } else { 776 mStatusIcon.setImage(null); 777 mStatusLabel.setText(" \n "); //$NON-NLS-1$ 778 } 779 780 mStatusComposite.pack(true); 781 } 782 783 private boolean createAvd() { 784 785 String avdName = mAvdName.getText(); 786 if (avdName == null || avdName.isEmpty()) { 787 return false; 788 } 789 790 String targetName = mTarget.getItem(mTarget.getSelectionIndex()); 791 IAndroidTarget target = mCurrentTargets.get(targetName); 792 if (target == null) { 793 return false; 794 } 795 796 // get the abi type 797 String abiType = SdkConstants.ABI_ARMEABI; 798 ISystemImage[] systemImages = getSystemImages(target); 799 if (systemImages.length > 0) { 800 int abiIndex = mAbi.getSelectionIndex(); 801 if (abiIndex >= 0) { 802 String prettyname = mAbi.getItem(abiIndex); 803 // Extract the abi type 804 int firstIndex = prettyname.indexOf("("); 805 int lastIndex = prettyname.indexOf(")"); 806 abiType = prettyname.substring(firstIndex + 1, lastIndex); 807 } 808 } 809 810 // get the SD card data from the UI. 811 String sdName = null; 812 if (mSdCardSizeRadio.getSelection()) { 813 // size mode 814 String value = mSdCardSize.getText().trim(); 815 if (value.length() > 0) { 816 sdName = value; 817 // add the unit 818 switch (mSdCardSizeCombo.getSelectionIndex()) { 819 case 0: 820 sdName += "K"; //$NON-NLS-1$ 821 break; 822 case 1: 823 sdName += "M"; //$NON-NLS-1$ 824 break; 825 case 2: 826 sdName += "G"; //$NON-NLS-1$ 827 break; 828 default: 829 // shouldn't be here 830 assert false; 831 } 832 } 833 } else { 834 // file mode. 835 sdName = mSdCardFile.getText().trim(); 836 } 837 838 // Get the device 839 List<Device> devices = mDeviceMap.get(mDeviceManufacturer.getText()); 840 if (devices == null) { 841 return false; 842 } 843 844 Device device = null; 845 for (Device d : devices) { 846 if (mDeviceName.getText().equals(d.getName())) { 847 device = d; 848 break; 849 } 850 } 851 852 if (device == null) { 853 return false; 854 } 855 856 Screen s = device.getDefaultHardware().getScreen(); 857 String skinName = s.getXDimension() + "x" + s.getYDimension(); 858 859 ILogger log = mSdkLog; 860 if (log == null || log instanceof MessageBoxLog) { 861 // If the current logger is a message box, we use our own (to make sure 862 // to display errors right away and customize the title). 863 log = new MessageBoxLog( 864 String.format("Result of creating AVD '%s':", avdName), 865 getContents().getDisplay(), 866 false /* logErrorsOnly */); 867 } 868 869 Map<String, String> hwProps = DeviceManager.getHardwareProperties(device); 870 if (mGpuEmulation.getSelection()) { 871 hwProps.put(AvdManager.AVD_INI_GPU_EMULATION, HardwareProperties.BOOLEAN_YES); 872 } 873 874 File avdFolder = null; 875 try { 876 avdFolder = AvdInfo.getDefaultAvdFolder(mAvdManager, avdName); 877 } catch (AndroidLocationException e) { 878 return false; 879 } 880 881 // Although the device has this information, some devices have more RAM than we'd want to 882 // allocate to an emulator. 883 hwProps.put(AvdManager.AVD_INI_RAM_SIZE, mRam.getText()); 884 hwProps.put(AvdManager.AVD_INI_VM_HEAP_SIZE, mVmHeap.getText()); 885 886 String suffix; 887 switch (mDataPartitionSize.getSelectionIndex()) { 888 case 0: 889 suffix = "M"; 890 break; 891 case 1: 892 suffix = "G"; 893 break; 894 default: 895 suffix = "K"; 896 } 897 hwProps.put(AvdManager.AVD_INI_DATA_PARTITION_SIZE, mDataPartition.getText()+suffix); 898 899 if (mFrontCamera.isEnabled()) { 900 hwProps.put(AvdManager.AVD_INI_CAMERA_FRONT, 901 mFrontCamera.getText().toLowerCase()); 902 } 903 904 if (mBackCamera.isEnabled()) { 905 hwProps.put(AvdManager.AVD_INI_CAMERA_BACK, 906 mBackCamera.getText().toLowerCase()); 907 } 908 909 if (sdName != null) { 910 hwProps.put(HardwareProperties.HW_SDCARD, HardwareProperties.BOOLEAN_YES); 911 } 912 913 AvdInfo avdInfo = mAvdManager.createAvd(avdFolder, 914 avdName, 915 target, 916 abiType, 917 skinName, 918 sdName, 919 hwProps, 920 mSnapshot.getSelection(), 921 mForceCreation.getSelection(), 922 mAvdInfo != null, // edit existing 923 log); 924 925 boolean success = avdInfo != null; 926 927 if (log instanceof MessageBoxLog) { 928 ((MessageBoxLog) log).displayResult(success); 929 } 930 return success; 931 } 932 933 private void fillExistingAvdInfo(AvdInfo avd) { 934 mAvdName.setText(avd.getName()); 935 String manufacturer = avd.getDeviceManufacturer(); 936 for (int i = 0; i < mDeviceManufacturer.getItemCount(); i++) { 937 if (mDeviceManufacturer.getItem(i).equals(manufacturer)) { 938 mDeviceManufacturer.select(i); 939 break; 940 } 941 } 942 reloadDeviceNameCombo(); 943 944 String deviceName = avd.getDeviceName(); 945 for (int i = 0; i < mDeviceName.getItemCount(); i++) { 946 if (mDeviceName.getItem(i).equals(deviceName)) { 947 mDeviceName.select(i); 948 break; 949 } 950 } 951 toggleCameras(); 952 953 IAndroidTarget target = avd.getTarget(); 954 955 if (target != null && !mCurrentTargets.isEmpty()) { 956 // Try to select the target in the target combo. 957 // This will fail if the AVD needs to be repaired. 958 // 959 // This is a linear search but the list is always 960 // small enough and we only do this once. 961 int n = mTarget.getItemCount(); 962 for (int i = 0; i < n; i++) { 963 if (target.equals(mCurrentTargets.get(mTarget.getItem(i)))) { 964 mTarget.select(i); 965 reloadAbiTypeCombo(); 966 break; 967 } 968 } 969 } 970 971 ISystemImage[] systemImages = getSystemImages(target); 972 if (target != null && systemImages.length > 0) { 973 mAbi.setEnabled(systemImages.length > 1); 974 String abiType = AvdInfo.getPrettyAbiType(avd.getAbiType()); 975 int n = mAbi.getItemCount(); 976 for (int i = 0; i < n; i++) { 977 if (abiType.equals(mAbi.getItem(i))) { 978 mAbi.select(i); 979 break; 980 } 981 } 982 } 983 984 Map<String, String> props = avd.getProperties(); 985 986 if (props != null) { 987 String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT); 988 if (snapshots != null && snapshots.length() > 0) { 989 mSnapshot.setSelection(snapshots.equals("true")); 990 } 991 992 String gpuEmulation = props.get(AvdManager.AVD_INI_GPU_EMULATION); 993 mGpuEmulation.setSelection(gpuEmulation != null && 994 gpuEmulation.equals(HardwareProperties.BOOLEAN_VALUES[0])); 995 996 String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH); 997 if (sdcard != null && sdcard.length() > 0) { 998 enableSdCardWidgets(false); 999 mSdCardSizeRadio.setSelection(false); 1000 mSdCardFileRadio.setSelection(true); 1001 mSdCardFile.setText(sdcard); 1002 } 1003 1004 String ramSize = props.get(AvdManager.AVD_INI_RAM_SIZE); 1005 if (ramSize != null) { 1006 mRam.setText(ramSize); 1007 } 1008 1009 String vmHeapSize = props.get(AvdManager.AVD_INI_VM_HEAP_SIZE); 1010 if (vmHeapSize != null) { 1011 mVmHeap.setText(vmHeapSize); 1012 } 1013 1014 String dataPartitionSize = props.get(AvdManager.AVD_INI_DATA_PARTITION_SIZE); 1015 if (dataPartitionSize != null) { 1016 mDataPartition.setText( 1017 dataPartitionSize.substring(0, dataPartitionSize.length() - 1)); 1018 switch (dataPartitionSize.charAt(dataPartitionSize.length() - 1)) { 1019 case 'M': 1020 mDataPartitionSize.select(0); 1021 break; 1022 case 'G': 1023 mDataPartitionSize.select(1); 1024 break; 1025 default: 1026 mDataPartitionSize.select(-1); 1027 } 1028 } 1029 1030 String cameraFront = props.get(AvdManager.AVD_INI_CAMERA_FRONT); 1031 if (cameraFront != null) { 1032 String[] items = mFrontCamera.getItems(); 1033 for (int i = 0; i < items.length; i++) { 1034 if (items[i].toLowerCase().equals(cameraFront)) { 1035 mFrontCamera.select(i); 1036 break; 1037 } 1038 } 1039 } 1040 1041 String cameraBack = props.get(AvdManager.AVD_INI_CAMERA_BACK); 1042 if (cameraBack != null) { 1043 String[] items = mBackCamera.getItems(); 1044 for (int i = 0; i < items.length; i++) { 1045 if (items[i].toLowerCase().equals(cameraBack)) { 1046 mBackCamera.select(i); 1047 break; 1048 } 1049 } 1050 } 1051 1052 sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE); 1053 if (sdcard != null && sdcard.length() > 0) { 1054 String[] values = new String[2]; 1055 long sdcardSize = AvdManager.parseSdcardSize(sdcard, values); 1056 1057 if (sdcardSize != AvdManager.SDCARD_NOT_SIZE_PATTERN) { 1058 enableSdCardWidgets(true); 1059 mSdCardFileRadio.setSelection(false); 1060 mSdCardSizeRadio.setSelection(true); 1061 1062 mSdCardSize.setText(values[0]); 1063 1064 String suffix = values[1]; 1065 int n = mSdCardSizeCombo.getItemCount(); 1066 for (int i = 0; i < n; i++) { 1067 if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) { 1068 mSdCardSizeCombo.select(i); 1069 } 1070 } 1071 } 1072 } 1073 } 1074 } 1075 1076 /** 1077 * Returns the list of system images of a target. 1078 * <p/> 1079 * If target is null, returns an empty list. If target is an add-on with no 1080 * system images, return the list from its parent platform. 1081 * 1082 * @param target An IAndroidTarget. Can be null. 1083 * @return A non-null ISystemImage array. Can be empty. 1084 */ 1085 private ISystemImage[] getSystemImages(IAndroidTarget target) { 1086 if (target != null) { 1087 ISystemImage[] images = target.getSystemImages(); 1088 1089 if ((images == null || images.length == 0) && !target.isPlatform()) { 1090 // If an add-on does not provide any system images, use the ones 1091 // from the parent. 1092 images = target.getParent().getSystemImages(); 1093 } 1094 1095 if (images != null) { 1096 return images; 1097 } 1098 } 1099 1100 return new ISystemImage[0]; 1101 } 1102 1103} 1104