1/* 2 * Copyright (C) 2009 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.io.FileWrapper; 21import com.android.prefs.AndroidLocation.AndroidLocationException; 22import com.android.sdklib.IAndroidTarget; 23import com.android.sdklib.ISystemImage; 24import com.android.sdklib.SdkManager; 25import com.android.sdklib.internal.avd.AvdInfo; 26import com.android.sdklib.internal.avd.AvdManager; 27import com.android.sdklib.internal.avd.AvdManager.AvdConflict; 28import com.android.sdklib.internal.avd.HardwareProperties; 29import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty; 30import com.android.sdklib.internal.project.ProjectProperties; 31import com.android.sdkuilib.internal.repository.icons.ImageFactory; 32import com.android.sdkuilib.ui.GridDialog; 33import com.android.utils.ILogger; 34import com.android.utils.Pair; 35 36import org.eclipse.jface.dialogs.IDialogConstants; 37import org.eclipse.jface.viewers.CellEditor; 38import org.eclipse.jface.viewers.CellLabelProvider; 39import org.eclipse.jface.viewers.ComboBoxCellEditor; 40import org.eclipse.jface.viewers.EditingSupport; 41import org.eclipse.jface.viewers.ISelection; 42import org.eclipse.jface.viewers.ISelectionChangedListener; 43import org.eclipse.jface.viewers.IStructuredContentProvider; 44import org.eclipse.jface.viewers.IStructuredSelection; 45import org.eclipse.jface.viewers.SelectionChangedEvent; 46import org.eclipse.jface.viewers.TableViewer; 47import org.eclipse.jface.viewers.TableViewerColumn; 48import org.eclipse.jface.viewers.TextCellEditor; 49import org.eclipse.jface.viewers.Viewer; 50import org.eclipse.jface.viewers.ViewerCell; 51import org.eclipse.jface.window.Window; 52import org.eclipse.swt.SWT; 53import org.eclipse.swt.events.ModifyEvent; 54import org.eclipse.swt.events.ModifyListener; 55import org.eclipse.swt.events.SelectionAdapter; 56import org.eclipse.swt.events.SelectionEvent; 57import org.eclipse.swt.events.VerifyEvent; 58import org.eclipse.swt.events.VerifyListener; 59import org.eclipse.swt.graphics.Point; 60import org.eclipse.swt.layout.GridData; 61import org.eclipse.swt.layout.GridLayout; 62import org.eclipse.swt.widgets.Button; 63import org.eclipse.swt.widgets.Combo; 64import org.eclipse.swt.widgets.Composite; 65import org.eclipse.swt.widgets.Control; 66import org.eclipse.swt.widgets.FileDialog; 67import org.eclipse.swt.widgets.Group; 68import org.eclipse.swt.widgets.Label; 69import org.eclipse.swt.widgets.Shell; 70import org.eclipse.swt.widgets.Table; 71import org.eclipse.swt.widgets.TableColumn; 72import org.eclipse.swt.widgets.Text; 73 74import java.io.File; 75import java.util.ArrayList; 76import java.util.HashMap; 77import java.util.Map; 78import java.util.Map.Entry; 79import java.util.TreeMap; 80import java.util.regex.Matcher; 81 82/** 83 * AVD creation or edit dialog. 84 * 85 * TODO: 86 * - use SdkTargetSelector instead of Combo 87 * - tooltips on widgets. 88 * 89 */ 90final class LegacyAvdEditDialog extends GridDialog { 91 92 private final AvdManager mAvdManager; 93 private final TreeMap<String, IAndroidTarget> mCurrentTargets = 94 new TreeMap<String, IAndroidTarget>(); 95 96 private final Map<String, HardwareProperty> mHardwareMap; 97 private final Map<String, String> mProperties = new HashMap<String, String>(); 98 // a list of user-edited properties. 99 private final ArrayList<String> mEditedProperties = new ArrayList<String>(); 100 private final ImageFactory mImageFactory; 101 private final ILogger mSdkLog; 102 /** 103 * The original AvdInfo if we're editing an existing AVD. 104 * Null when we're creating a new AVD. 105 */ 106 private final AvdInfo mEditAvdInfo; 107 108 private Text mAvdName; 109 private Combo mTargetCombo; 110 111 private Combo mAbiTypeCombo; 112 private String mAbiType; 113 114 private Button mSdCardSizeRadio; 115 private Text mSdCardSize; 116 private Combo mSdCardSizeCombo; 117 118 private Text mSdCardFile; 119 private Button mBrowseSdCard; 120 private Button mSdCardFileRadio; 121 122 private Button mSnapshotCheck; 123 124 private Button mSkinListRadio; 125 private Combo mSkinCombo; 126 127 private Button mSkinSizeRadio; 128 private Text mSkinSizeWidth; 129 private Text mSkinSizeHeight; 130 131 private TableViewer mHardwareViewer; 132 private Button mDeleteHardwareProp; 133 134 private Button mForceCreation; 135 private Button mOkButton; 136 private Label mStatusIcon; 137 private Label mStatusLabel; 138 private Composite mStatusComposite; 139 140 /** 141 * {@link VerifyListener} for {@link Text} widgets that should only contains numbers. 142 */ 143 private final VerifyListener mDigitVerifier = new VerifyListener() { 144 @Override 145 public void verifyText(VerifyEvent event) { 146 int count = event.text.length(); 147 for (int i = 0 ; i < count ; i++) { 148 char c = event.text.charAt(i); 149 if (c < '0' || c > '9') { 150 event.doit = false; 151 return; 152 } 153 } 154 } 155 }; 156 157 /** 158 * Callback when the AVD name is changed. 159 * When creating a new AVD, enables the force checkbox if the name is a duplicate. 160 * When editing an existing AVD, it's OK for the name to match the existing AVD. 161 */ 162 private class CreateNameModifyListener implements ModifyListener { 163 @Override 164 public void modifyText(ModifyEvent e) { 165 String name = mAvdName.getText().trim(); 166 if (mEditAvdInfo == null || !name.equals(mEditAvdInfo.getName())) { 167 // Case where we're creating a new AVD or editing an existing one 168 // and the AVD name has been changed... check for name uniqueness. 169 170 Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(name); 171 if (conflict.getFirst() != AvdManager.AvdConflict.NO_CONFLICT) { 172 // If we're changing the state from disabled to enabled, make sure 173 // to uncheck the button, to force the user to voluntarily re-enforce it. 174 // This happens when editing an existing AVD and changing the name from 175 // the existing AVD to another different existing AVD. 176 if (!mForceCreation.isEnabled()) { 177 mForceCreation.setEnabled(true); 178 mForceCreation.setSelection(false); 179 } 180 } else { 181 mForceCreation.setEnabled(false); 182 mForceCreation.setSelection(false); 183 } 184 } else { 185 // Case where we're editing an existing AVD with the name unchanged. 186 187 mForceCreation.setEnabled(false); 188 mForceCreation.setSelection(false); 189 } 190 validatePage(); 191 } 192 } 193 194 /** 195 * {@link ModifyListener} used for live-validation of the fields content. 196 */ 197 private class ValidateListener extends SelectionAdapter implements ModifyListener { 198 @Override 199 public void modifyText(ModifyEvent e) { 200 validatePage(); 201 } 202 203 @Override 204 public void widgetSelected(SelectionEvent e) { 205 super.widgetSelected(e); 206 validatePage(); 207 } 208 } 209 210 /** 211 * Creates the dialog. Caller should then use {@link Window#open()} and 212 * refresh if the status is {@link Window#OK}. 213 * 214 * @param parentShell The parent shell. 215 * @param avdManager The existing {@link AvdManager} to use. Must not be null. 216 * @param imageFactory An existing {@link ImageFactory} to use. Must not be null. 217 * @param log An existing {@link ILogger} where output will go. Must not be null. 218 * @param editAvdInfo An optional {@link AvdInfo}. When null, the dialog is used 219 * to create a new AVD. When non-null, the dialog is used to <em>edit</em> this AVD. 220 */ 221 protected LegacyAvdEditDialog(Shell parentShell, 222 AvdManager avdManager, 223 ImageFactory imageFactory, 224 ILogger log, 225 AvdInfo editAvdInfo) { 226 super(parentShell, 2, false); 227 mAvdManager = avdManager; 228 mImageFactory = imageFactory; 229 mSdkLog = log; 230 mEditAvdInfo = editAvdInfo; 231 232 File hardwareDefs = null; 233 234 SdkManager sdkMan = avdManager.getSdkManager(); 235 if (sdkMan != null) { 236 String sdkPath = sdkMan.getLocation(); 237 if (sdkPath != null) { 238 hardwareDefs = new File (sdkPath + File.separator + 239 SdkConstants.OS_SDK_TOOLS_LIB_FOLDER, SdkConstants.FN_HARDWARE_INI); 240 } 241 } 242 243 if (hardwareDefs == null) { 244 log.error(null, "Failed to load file %s from SDK", SdkConstants.FN_HARDWARE_INI); 245 mHardwareMap = new HashMap<String, HardwareProperty>(); 246 } else { 247 mHardwareMap = HardwareProperties.parseHardwareDefinitions( 248 hardwareDefs, null /*sdkLog*/); 249 } 250 } 251 252 @Override 253 public void create() { 254 super.create(); 255 256 Point p = getShell().getSize(); 257 if (p.x < 400) { 258 p.x = 400; 259 } 260 getShell().setSize(p); 261 } 262 263 @Override 264 protected Control createContents(Composite parent) { 265 Control control = super.createContents(parent); 266 getShell().setText(mEditAvdInfo == null ? "Create new Android Virtual Device (AVD)" 267 : "Edit Android Virtual Device (AVD)"); 268 269 mOkButton = getButton(IDialogConstants.OK_ID); 270 271 fillExistingAvdInfo(); 272 validatePage(); 273 274 return control; 275 } 276 277 @Override 278 public void createDialogContent(final Composite parent) { 279 GridData gd; 280 GridLayout gl; 281 282 Label label = new Label(parent, SWT.NONE); 283 label.setText("Name:"); 284 String tooltip = "Name of the new Android Virtual Device"; 285 label.setToolTipText(tooltip); 286 287 mAvdName = new Text(parent, SWT.BORDER); 288 mAvdName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 289 mAvdName.addModifyListener(new CreateNameModifyListener()); 290 mAvdName.setToolTipText(tooltip); 291 292 label = new Label(parent, SWT.NONE); 293 label.setText("Target:"); 294 tooltip = "The version of Android to use in the virtual device"; 295 label.setToolTipText(tooltip); 296 297 mTargetCombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 298 mTargetCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 299 mTargetCombo.setToolTipText(tooltip); 300 mTargetCombo.addSelectionListener(new SelectionAdapter() { 301 @Override 302 public void widgetSelected(SelectionEvent e) { 303 super.widgetSelected(e); 304 reloadSkinCombo(); 305 reloadAbiTypeCombo(); 306 validatePage(); 307 } 308 }); 309 310 //ABI group 311 label = new Label(parent, SWT.NONE); 312 label.setText("CPU/ABI:"); 313 tooltip = "The CPU/ABI to use in the virtual device"; 314 label.setToolTipText(tooltip); 315 316 mAbiTypeCombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); 317 mAbiTypeCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 318 mAbiTypeCombo.setToolTipText(tooltip); 319 mAbiTypeCombo.addSelectionListener(new SelectionAdapter() { 320 @Override 321 public void widgetSelected(SelectionEvent e) { 322 super.widgetSelected(e); 323 validatePage(); 324 } 325 }); 326 mAbiTypeCombo.setEnabled(false); 327 328 // --- sd card group 329 label = new Label(parent, SWT.NONE); 330 label.setText("SD Card:"); 331 label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 332 false, false)); 333 334 final Group sdCardGroup = new Group(parent, SWT.NONE); 335 sdCardGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 336 sdCardGroup.setLayout(new GridLayout(3, false)); 337 338 mSdCardSizeRadio = new Button(sdCardGroup, SWT.RADIO); 339 mSdCardSizeRadio.setText("Size:"); 340 mSdCardSizeRadio.setToolTipText("Create a new SD Card file"); 341 mSdCardSizeRadio.addSelectionListener(new SelectionAdapter() { 342 @Override 343 public void widgetSelected(SelectionEvent arg0) { 344 boolean sizeMode = mSdCardSizeRadio.getSelection(); 345 enableSdCardWidgets(sizeMode); 346 validatePage(); 347 } 348 }); 349 350 ValidateListener validateListener = new ValidateListener(); 351 352 mSdCardSize = new Text(sdCardGroup, SWT.BORDER); 353 mSdCardSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 354 mSdCardSize.addVerifyListener(mDigitVerifier); 355 mSdCardSize.addModifyListener(validateListener); 356 mSdCardSize.setToolTipText("Size of the new SD Card file (must be at least 9 MiB)"); 357 358 mSdCardSizeCombo = new Combo(sdCardGroup, SWT.DROP_DOWN | SWT.READ_ONLY); 359 mSdCardSizeCombo.add("KiB"); 360 mSdCardSizeCombo.add("MiB"); 361 mSdCardSizeCombo.add("GiB"); 362 mSdCardSizeCombo.select(1); 363 mSdCardSizeCombo.addSelectionListener(validateListener); 364 365 mSdCardFileRadio = new Button(sdCardGroup, SWT.RADIO); 366 mSdCardFileRadio.setText("File:"); 367 mSdCardFileRadio.setToolTipText("Use an existing file for the SD Card"); 368 369 mSdCardFile = new Text(sdCardGroup, SWT.BORDER); 370 mSdCardFile.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 371 mSdCardFile.addModifyListener(validateListener); 372 mSdCardFile.setToolTipText("File to use for the SD Card"); 373 374 mBrowseSdCard = new Button(sdCardGroup, SWT.PUSH); 375 mBrowseSdCard.setText("Browse..."); 376 mBrowseSdCard.setToolTipText("Select the file to use for the SD Card"); 377 mBrowseSdCard.addSelectionListener(new SelectionAdapter() { 378 @Override 379 public void widgetSelected(SelectionEvent arg0) { 380 onBrowseSdCard(); 381 validatePage(); 382 } 383 }); 384 385 mSdCardSizeRadio.setSelection(true); 386 enableSdCardWidgets(true); 387 388 // --- snapshot group 389 390 label = new Label(parent, SWT.NONE); 391 label.setText("Snapshot:"); 392 label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 393 false, false)); 394 395 final Group snapshotGroup = new Group(parent, SWT.NONE); 396 snapshotGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 397 snapshotGroup.setLayout(new GridLayout(3, false)); 398 399 mSnapshotCheck = new Button(snapshotGroup, SWT.CHECK); 400 mSnapshotCheck.setText("Enabled"); 401 mSnapshotCheck.setToolTipText( 402 "Emulator's state will be persisted between emulator executions"); 403 404 // --- skin group 405 label = new Label(parent, SWT.NONE); 406 label.setText("Skin:"); 407 label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 408 false, false)); 409 410 final Group skinGroup = new Group(parent, SWT.NONE); 411 skinGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 412 skinGroup.setLayout(new GridLayout(4, false)); 413 414 mSkinListRadio = new Button(skinGroup, SWT.RADIO); 415 mSkinListRadio.setText("Built-in:"); 416 mSkinListRadio.setToolTipText("Select an emulated screen size provided by the current Android target"); 417 mSkinListRadio.addSelectionListener(new SelectionAdapter() { 418 @Override 419 public void widgetSelected(SelectionEvent arg0) { 420 boolean listMode = mSkinListRadio.getSelection(); 421 enableSkinWidgets(listMode); 422 validatePage(); 423 } 424 }); 425 426 mSkinCombo = new Combo(skinGroup, SWT.READ_ONLY | SWT.DROP_DOWN); 427 mSkinCombo.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 428 gd.horizontalSpan = 3; 429 mSkinCombo.addSelectionListener(new SelectionAdapter() { 430 @Override 431 public void widgetSelected(SelectionEvent arg0) { 432 // get the skin info 433 loadSkin(); 434 } 435 }); 436 437 mSkinSizeRadio = new Button(skinGroup, SWT.RADIO); 438 mSkinSizeRadio.setText("Resolution:"); 439 mSkinSizeRadio.setToolTipText("Select a custom emulated screen size"); 440 441 mSkinSizeWidth = new Text(skinGroup, SWT.BORDER); 442 mSkinSizeWidth.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 443 mSkinSizeWidth.addVerifyListener(mDigitVerifier); 444 mSkinSizeWidth.addModifyListener(validateListener); 445 mSkinSizeWidth.setToolTipText("Width in pixels of the emulated screen size"); 446 447 new Label(skinGroup, SWT.NONE).setText("x"); 448 449 mSkinSizeHeight = new Text(skinGroup, SWT.BORDER); 450 mSkinSizeHeight.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 451 mSkinSizeHeight.addVerifyListener(mDigitVerifier); 452 mSkinSizeHeight.addModifyListener(validateListener); 453 mSkinSizeHeight.setToolTipText("Height in pixels of the emulated screen size"); 454 455 mSkinListRadio.setSelection(true); 456 enableSkinWidgets(true); 457 458 // --- hardware group 459 label = new Label(parent, SWT.NONE); 460 label.setText("Hardware:"); 461 label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 462 false, false)); 463 464 final Group hwGroup = new Group(parent, SWT.NONE); 465 hwGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 466 hwGroup.setLayout(new GridLayout(2, false)); 467 468 createHardwareTable(hwGroup); 469 470 // composite for the side buttons 471 Composite hwButtons = new Composite(hwGroup, SWT.NONE); 472 hwButtons.setLayoutData(new GridData(GridData.FILL_VERTICAL)); 473 hwButtons.setLayout(gl = new GridLayout(1, false)); 474 gl.marginHeight = gl.marginWidth = 0; 475 476 Button b = new Button(hwButtons, SWT.PUSH | SWT.FLAT); 477 b.setText("New..."); 478 b.setToolTipText("Add a new hardware property"); 479 b.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 480 b.addSelectionListener(new SelectionAdapter() { 481 @Override 482 public void widgetSelected(SelectionEvent event) { 483 HardwarePropertyChooser dialog = new HardwarePropertyChooser(parent.getShell(), 484 mHardwareMap, mProperties.keySet()); 485 if (dialog.open() == Window.OK) { 486 HardwareProperty choice = dialog.getProperty(); 487 if (choice != null) { 488 mProperties.put(choice.getName(), choice.getDefault()); 489 mHardwareViewer.refresh(); 490 } 491 } 492 } 493 }); 494 mDeleteHardwareProp = new Button(hwButtons, SWT.PUSH | SWT.FLAT); 495 mDeleteHardwareProp.setText("Delete"); 496 mDeleteHardwareProp.setToolTipText("Delete the selected hardware property"); 497 mDeleteHardwareProp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 498 mDeleteHardwareProp.addSelectionListener(new SelectionAdapter() { 499 @Override 500 public void widgetSelected(SelectionEvent arg0) { 501 ISelection selection = mHardwareViewer.getSelection(); 502 if (selection instanceof IStructuredSelection) { 503 String hwName = (String)((IStructuredSelection)selection).getFirstElement(); 504 mProperties.remove(hwName); 505 mHardwareViewer.refresh(); 506 } 507 } 508 }); 509 mDeleteHardwareProp.setEnabled(false); 510 511 // --- end hardware group 512 513 mForceCreation = new Button(parent, SWT.CHECK); 514 mForceCreation.setText("Override the existing AVD with the same name"); 515 mForceCreation.setToolTipText("There's already an AVD with the same name. Check this to delete it and replace it by the new AVD."); 516 mForceCreation.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, 517 true, false, 2, 1)); 518 mForceCreation.setEnabled(false); 519 mForceCreation.addSelectionListener(validateListener); 520 521 // add a separator to separate from the ok/cancel button 522 label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); 523 label.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1)); 524 525 // add stuff for the error display 526 mStatusComposite = new Composite(parent, SWT.NONE); 527 mStatusComposite.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, 528 true, false, 3, 1)); 529 mStatusComposite.setLayout(gl = new GridLayout(2, false)); 530 gl.marginHeight = gl.marginWidth = 0; 531 532 mStatusIcon = new Label(mStatusComposite, SWT.NONE); 533 mStatusIcon.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, 534 false, false)); 535 mStatusLabel = new Label(mStatusComposite, SWT.NONE); 536 mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 537 mStatusLabel.setText(" \n "); //$NON-NLS-1$ 538 539 reloadTargetCombo(); 540 } 541 542 /** 543 * Creates the UI for the hardware properties table. 544 * This creates the {@link Table}, and several viewers ({@link TableViewer}, 545 * {@link TableViewerColumn}) and adds edit support for the 2nd column 546 */ 547 private void createHardwareTable(Composite parent) { 548 final Table hardwareTable = new Table(parent, SWT.SINGLE | SWT.FULL_SELECTION); 549 GridData gd = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL); 550 gd.widthHint = 200; 551 gd.heightHint = 100; 552 hardwareTable.setLayoutData(gd); 553 hardwareTable.setHeaderVisible(true); 554 hardwareTable.setLinesVisible(true); 555 556 // -- Table viewer 557 mHardwareViewer = new TableViewer(hardwareTable); 558 mHardwareViewer.addSelectionChangedListener(new ISelectionChangedListener() { 559 @Override 560 public void selectionChanged(SelectionChangedEvent event) { 561 // it's a single selection mode, we can just access the selection index 562 // from the table directly. 563 mDeleteHardwareProp.setEnabled(hardwareTable.getSelectionIndex() != -1); 564 } 565 }); 566 567 // only a content provider. Use viewers per column below (for editing support) 568 mHardwareViewer.setContentProvider(new IStructuredContentProvider() { 569 @Override 570 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 571 // we can just ignore this. we just use mProperties directly. 572 } 573 574 @Override 575 public Object[] getElements(Object arg0) { 576 return mProperties.keySet().toArray(); 577 } 578 579 @Override 580 public void dispose() { 581 // pass 582 } 583 }); 584 585 // -- column 1: prop abstract name 586 TableColumn col1 = new TableColumn(hardwareTable, SWT.LEFT); 587 col1.setText("Property"); 588 col1.setWidth(150); 589 TableViewerColumn tvc1 = new TableViewerColumn(mHardwareViewer, col1); 590 tvc1.setLabelProvider(new CellLabelProvider() { 591 @Override 592 public void update(ViewerCell cell) { 593 String name = cell.getElement().toString(); 594 HardwareProperty prop = mHardwareMap.get(name); 595 if (prop != null) { 596 cell.setText(prop.getAbstract()); 597 } else { 598 cell.setText(String.format("%1$s (Unknown)", name)); 599 } 600 } 601 }); 602 603 // -- column 2: prop value 604 TableColumn col2 = new TableColumn(hardwareTable, SWT.LEFT); 605 col2.setText("Value"); 606 col2.setWidth(50); 607 TableViewerColumn tvc2 = new TableViewerColumn(mHardwareViewer, col2); 608 tvc2.setLabelProvider(new CellLabelProvider() { 609 @Override 610 public void update(ViewerCell cell) { 611 String value = mProperties.get(cell.getElement()); 612 cell.setText(value != null ? value : ""); 613 } 614 }); 615 616 // add editing support to the 2nd column 617 tvc2.setEditingSupport(new EditingSupport(mHardwareViewer) { 618 @Override 619 protected void setValue(Object element, Object value) { 620 String hardwareName = (String)element; 621 HardwareProperty property = mHardwareMap.get(hardwareName); 622 int index; 623 switch (property.getType()) { 624 case INTEGER: 625 mProperties.put((String)element, (String)value); 626 break; 627 case DISKSIZE: 628 if (HardwareProperties.DISKSIZE_PATTERN.matcher((String)value).matches()) { 629 mProperties.put((String)element, (String)value); 630 } 631 break; 632 case BOOLEAN: 633 index = (Integer)value; 634 mProperties.put((String)element, HardwareProperties.BOOLEAN_VALUES[index]); 635 break; 636 case STRING_ENUM: 637 case INTEGER_ENUM: 638 // For a combo, value is the index of the enum to use. 639 index = (Integer)value; 640 String[] values = property.getEnum(); 641 if (values != null && values.length > index) { 642 mProperties.put((String)element, values[index]); 643 } 644 break; 645 } 646 mHardwareViewer.refresh(element); 647 } 648 649 @Override 650 protected Object getValue(Object element) { 651 String hardwareName = (String)element; 652 HardwareProperty property = mHardwareMap.get(hardwareName); 653 String value = mProperties.get(hardwareName); 654 switch (property.getType()) { 655 case INTEGER: 656 // intended fall-through. 657 case DISKSIZE: 658 return value; 659 case BOOLEAN: 660 return HardwareProperties.getBooleanValueIndex(value); 661 case STRING_ENUM: 662 case INTEGER_ENUM: 663 // For a combo, we need to return the index of the value in the enum 664 String[] values = property.getEnum(); 665 if (values != null) { 666 for (int i = 0; i < values.length; i++) { 667 if (values[i].equals(value)) { 668 return i; 669 } 670 } 671 } 672 } 673 674 return null; 675 } 676 677 @Override 678 protected CellEditor getCellEditor(Object element) { 679 String hardwareName = (String)element; 680 HardwareProperty property = mHardwareMap.get(hardwareName); 681 switch (property.getType()) { 682 // TODO: custom TextCellEditor that restrict input. 683 case INTEGER: 684 // intended fall-through. 685 case DISKSIZE: 686 return new TextCellEditor(hardwareTable); 687 case BOOLEAN: 688 return new ComboBoxCellEditor(hardwareTable, 689 HardwareProperties.BOOLEAN_VALUES, 690 SWT.READ_ONLY | SWT.DROP_DOWN); 691 case STRING_ENUM: 692 case INTEGER_ENUM: 693 String[] values = property.getEnum(); 694 if (values != null && values.length > 0) { 695 return new ComboBoxCellEditor(hardwareTable, 696 values, 697 SWT.READ_ONLY | SWT.DROP_DOWN); 698 } 699 } 700 return null; 701 } 702 703 @Override 704 protected boolean canEdit(Object element) { 705 String hardwareName = (String)element; 706 HardwareProperty property = mHardwareMap.get(hardwareName); 707 return property != null; 708 } 709 }); 710 711 712 mHardwareViewer.setInput(mProperties); 713 } 714 715 // -- Start of internal part ---------- 716 // Hide everything down-below from SWT designer 717 //$hide>>$ 718 719 /** 720 * When editing an existing AVD info, fill the UI that has just been created with 721 * the values from the AVD. 722 */ 723 public void fillExistingAvdInfo() { 724 if (mEditAvdInfo == null) { 725 return; 726 } 727 728 mAvdName.setText(mEditAvdInfo.getName()); 729 730 Map<String, String> props = mEditAvdInfo.getProperties(); 731 732 IAndroidTarget target = mEditAvdInfo.getTarget(); 733 if (target != null && !mCurrentTargets.isEmpty()) { 734 // Try to select the target in the target combo. 735 // This will fail if the AVD needs to be repaired. 736 // 737 // This is a linear search but the list is always 738 // small enough and we only do this once. 739 int n = mTargetCombo.getItemCount(); 740 for (int i = 0;i < n; i++) { 741 if (target.equals(mCurrentTargets.get(mTargetCombo.getItem(i)))) { 742 mTargetCombo.select(i); 743 reloadAbiTypeCombo(); 744 reloadSkinCombo(); 745 break; 746 } 747 } 748 } 749 750 // select the abi type 751 ISystemImage[] systemImages = getSystemImages(target); 752 if (target != null && systemImages.length > 0) { 753 mAbiTypeCombo.setEnabled(systemImages.length > 1); 754 String abiType = AvdInfo.getPrettyAbiType(mEditAvdInfo.getAbiType()); 755 int n = mAbiTypeCombo.getItemCount(); 756 for (int i = 0; i < n; i++) { 757 if (abiType.equals(mAbiTypeCombo.getItem(i))) { 758 mAbiTypeCombo.select(i); 759 reloadSkinCombo(); 760 break; 761 } 762 } 763 } 764 765 if (props != null) { 766 767 // First try the skin name and if it doesn't work fallback on the skin path 768 nextSkin: for (int s = 0; s < 2; s++) { 769 String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME 770 : AvdManager.AVD_INI_SKIN_PATH); 771 if (skin != null && skin.length() > 0) { 772 Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin); 773 if (m.matches() && m.groupCount() == 2) { 774 enableSkinWidgets(false); 775 mSkinListRadio.setSelection(false); 776 mSkinSizeRadio.setSelection(true); 777 mSkinSizeWidth.setText(m.group(1)); 778 mSkinSizeHeight.setText(m.group(2)); 779 break nextSkin; 780 } else { 781 enableSkinWidgets(true); 782 mSkinSizeRadio.setSelection(false); 783 mSkinListRadio.setSelection(true); 784 785 int n = mSkinCombo.getItemCount(); 786 for (int i = 0; i < n; i++) { 787 if (skin.equals(mSkinCombo.getItem(i))) { 788 mSkinCombo.select(i); 789 break nextSkin; 790 } 791 } 792 } 793 } 794 } 795 796 String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH); 797 if (sdcard != null && sdcard.length() > 0) { 798 enableSdCardWidgets(false); 799 mSdCardSizeRadio.setSelection(false); 800 mSdCardFileRadio.setSelection(true); 801 mSdCardFile.setText(sdcard); 802 } 803 804 sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE); 805 if (sdcard != null && sdcard.length() > 0) { 806 String[] values = new String[2]; 807 long sdcardSize = AvdManager.parseSdcardSize(sdcard, values); 808 809 if (sdcardSize != AvdManager.SDCARD_NOT_SIZE_PATTERN) { 810 enableSdCardWidgets(true); 811 mSdCardFileRadio.setSelection(false); 812 mSdCardSizeRadio.setSelection(true); 813 814 mSdCardSize.setText(values[0]); 815 816 String suffix = values[1]; 817 int n = mSdCardSizeCombo.getItemCount(); 818 for (int i = 0; i < n; i++) { 819 if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) { 820 mSdCardSizeCombo.select(i); 821 } 822 } 823 } 824 } 825 826 String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT); 827 if (snapshots != null && snapshots.length() > 0) { 828 mSnapshotCheck.setSelection(snapshots.equals("true")); 829 } 830 } 831 832 mProperties.clear(); 833 834 if (props != null) { 835 for (Entry<String, String> entry : props.entrySet()) { 836 HardwareProperty prop = mHardwareMap.get(entry.getKey()); 837 if (prop != null && prop.isValidForUi()) { 838 mProperties.put(entry.getKey(), entry.getValue()); 839 } 840 } 841 } 842 843 // Cleanup known non-hardware properties 844 mProperties.remove(AvdManager.AVD_INI_ABI_TYPE); 845 mProperties.remove(AvdManager.AVD_INI_CPU_ARCH); 846 mProperties.remove(AvdManager.AVD_INI_SKIN_PATH); 847 mProperties.remove(AvdManager.AVD_INI_SKIN_NAME); 848 mProperties.remove(AvdManager.AVD_INI_SDCARD_SIZE); 849 mProperties.remove(AvdManager.AVD_INI_SDCARD_PATH); 850 mProperties.remove(AvdManager.AVD_INI_SNAPSHOT_PRESENT); 851 mProperties.remove(AvdManager.AVD_INI_IMAGES_1); 852 mProperties.remove(AvdManager.AVD_INI_IMAGES_2); 853 854 mHardwareViewer.refresh(); 855 } 856 857 @Override 858 protected void okPressed() { 859 if (createAvd()) { 860 super.okPressed(); 861 } 862 } 863 864 /** 865 * Enable or disable the sd card widgets. 866 * @param sizeMode if true the size-based widgets are to be enabled, and the file-based ones 867 * disabled. 868 */ 869 private void enableSdCardWidgets(boolean sizeMode) { 870 mSdCardSize.setEnabled(sizeMode); 871 mSdCardSizeCombo.setEnabled(sizeMode); 872 873 mSdCardFile.setEnabled(!sizeMode); 874 mBrowseSdCard.setEnabled(!sizeMode); 875 } 876 877 /** 878 * Enable or disable the skin widgets. 879 * @param listMode if true the list-based widgets are to be enabled, and the size-based ones 880 * disabled. 881 */ 882 private void enableSkinWidgets(boolean listMode) { 883 mSkinCombo.setEnabled(listMode); 884 885 mSkinSizeWidth.setEnabled(!listMode); 886 mSkinSizeHeight.setEnabled(!listMode); 887 } 888 889 890 private void onBrowseSdCard() { 891 FileDialog dlg = new FileDialog(getContents().getShell(), SWT.OPEN); 892 dlg.setText("Choose SD Card image file."); 893 894 String fileName = dlg.open(); 895 if (fileName != null) { 896 mSdCardFile.setText(fileName); 897 } 898 } 899 900 901 902 private void reloadTargetCombo() { 903 String selected = null; 904 int index = mTargetCombo.getSelectionIndex(); 905 if (index >= 0) { 906 selected = mTargetCombo.getItem(index); 907 } 908 909 mCurrentTargets.clear(); 910 mTargetCombo.removeAll(); 911 912 boolean found = false; 913 index = -1; 914 915 SdkManager sdkManager = mAvdManager.getSdkManager(); 916 if (sdkManager != null) { 917 for (IAndroidTarget target : sdkManager.getTargets()) { 918 String name; 919 if (target.isPlatform()) { 920 name = String.format("%s - API Level %s", 921 target.getName(), 922 target.getVersion().getApiString()); 923 } else { 924 name = String.format("%s (%s) - API Level %s", 925 target.getName(), 926 target.getVendor(), 927 target.getVersion().getApiString()); 928 } 929 mCurrentTargets.put(name, target); 930 mTargetCombo.add(name); 931 if (!found) { 932 index++; 933 found = name.equals(selected); 934 } 935 } 936 } 937 938 mTargetCombo.setEnabled(mCurrentTargets.size() > 0); 939 940 if (found) { 941 mTargetCombo.select(index); 942 } 943 944 reloadSkinCombo(); 945 } 946 947 private void reloadSkinCombo() { 948 String selected = null; 949 int index = mSkinCombo.getSelectionIndex(); 950 if (index >= 0) { 951 selected = mSkinCombo.getItem(index); 952 } 953 954 mSkinCombo.removeAll(); 955 mSkinCombo.setEnabled(false); 956 957 index = mTargetCombo.getSelectionIndex(); 958 if (index >= 0) { 959 960 String targetName = mTargetCombo.getItem(index); 961 962 boolean found = false; 963 IAndroidTarget target = mCurrentTargets.get(targetName); 964 if (target != null) { 965 mSkinCombo.add(String.format("Default (%s)", target.getDefaultSkin())); 966 967 index = -1; 968 for (String skin : target.getSkins()) { 969 mSkinCombo.add(skin); 970 if (!found) { 971 index++; 972 found = skin.equals(selected); 973 } 974 } 975 976 mSkinCombo.setEnabled(true); 977 978 if (found) { 979 mSkinCombo.select(index); 980 } else { 981 mSkinCombo.select(0); // default 982 loadSkin(); 983 } 984 } 985 } 986 } 987 988 /** 989 * Reload all the abi types in the selection list 990 */ 991 private void reloadAbiTypeCombo() { 992 String selected = null; 993 boolean found = false; 994 995 int index = mTargetCombo.getSelectionIndex(); 996 if (index >= 0) { 997 String targetName = mTargetCombo.getItem(index); 998 IAndroidTarget target = mCurrentTargets.get(targetName); 999 1000 ISystemImage[] systemImages = getSystemImages(target); 1001 1002 mAbiTypeCombo.setEnabled(systemImages.length > 1); 1003 1004 // If user explicitly selected an ABI before, preserve that option 1005 // If user did not explicitly select before (only one option before) 1006 // force them to select 1007 index = mAbiTypeCombo.getSelectionIndex(); 1008 if (index >= 0 && mAbiTypeCombo.getItemCount() > 1) { 1009 selected = mAbiTypeCombo.getItem(index); 1010 } 1011 1012 mAbiTypeCombo.removeAll(); 1013 1014 int i; 1015 for ( i = 0; i < systemImages.length ; i++ ) { 1016 String prettyAbiType = AvdInfo.getPrettyAbiType(systemImages[i].getAbiType()); 1017 mAbiTypeCombo.add(prettyAbiType); 1018 if (!found) { 1019 found = prettyAbiType.equals(selected); 1020 if (found) { 1021 mAbiTypeCombo.select(i); 1022 } 1023 } 1024 } 1025 1026 if (systemImages.length == 1) { 1027 mAbiTypeCombo.select(0); 1028 } 1029 } 1030 } 1031 1032 /** 1033 * Validates the fields, displays errors and warnings. 1034 * Enables the finish button if there are no errors. 1035 */ 1036 private void validatePage() { 1037 String error = null; 1038 String warning = null; 1039 1040 // Validate AVD name 1041 String avdName = mAvdName.getText().trim(); 1042 boolean hasAvdName = avdName.length() > 0; 1043 boolean isCreate = mEditAvdInfo == null || !avdName.equals(mEditAvdInfo.getName()); 1044 1045 if (hasAvdName && !AvdManager.RE_AVD_NAME.matcher(avdName).matches()) { 1046 error = String.format( 1047 "AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", 1048 avdName, AvdManager.CHARS_AVD_NAME); 1049 } 1050 1051 // Validate target 1052 if (hasAvdName && error == null && mTargetCombo.getSelectionIndex() < 0) { 1053 error = "A target must be selected in order to create an AVD."; 1054 } 1055 1056 // validate abi type if the selected target supports multi archs. 1057 if (hasAvdName && error == null && mTargetCombo.getSelectionIndex() > 0) { 1058 int index = mTargetCombo.getSelectionIndex(); 1059 String targetName = mTargetCombo.getItem(index); 1060 IAndroidTarget target = mCurrentTargets.get(targetName); 1061 ISystemImage[] systemImages = getSystemImages(target); 1062 if (systemImages.length > 1 && mAbiTypeCombo.getSelectionIndex() < 0) { 1063 error = "An ABI type must be selected in order to create an AVD."; 1064 } 1065 } 1066 1067 // Validate SDCard path or value 1068 if (error == null) { 1069 // get the mode. We only need to check the file since the 1070 // verifier on the size Text will prevent invalid input 1071 boolean sdcardFileMode = mSdCardFileRadio.getSelection(); 1072 if (sdcardFileMode) { 1073 String sdName = mSdCardFile.getText().trim(); 1074 if (sdName.length() > 0 && !new File(sdName).isFile()) { 1075 error = "SD Card path isn't valid."; 1076 } 1077 } else { 1078 String valueString = mSdCardSize.getText(); 1079 if (valueString.length() > 0) { 1080 long value = 0; 1081 try { 1082 value = Long.parseLong(valueString); 1083 1084 int sizeIndex = mSdCardSizeCombo.getSelectionIndex(); 1085 if (sizeIndex >= 0) { 1086 // index 0 shifts by 10 (1024=K), index 1 by 20, etc. 1087 value <<= 10*(1 + sizeIndex); 1088 } 1089 1090 if (value < AvdManager.SDCARD_MIN_BYTE_SIZE || 1091 value > AvdManager.SDCARD_MAX_BYTE_SIZE) { 1092 value = 0; 1093 } 1094 } catch (Exception e) { 1095 // ignore, we'll test value below. 1096 } 1097 if (value <= 0) { 1098 error = "SD Card size is invalid. Range is 9 MiB..1023 GiB."; 1099 } else if (mEditAvdInfo != null) { 1100 // When editing an existing AVD, compare with the existing 1101 // sdcard size, if any. It only matters if there was an sdcard setting 1102 // before. 1103 Map<String, String> props = mEditAvdInfo.getProperties(); 1104 if (props != null) { 1105 String original = 1106 mEditAvdInfo.getProperties().get(AvdManager.AVD_INI_SDCARD_SIZE); 1107 if (original != null && original.length() > 0) { 1108 long originalSize = 1109 AvdManager.parseSdcardSize(original, null/*parsedStrings*/); 1110 if (originalSize > 0 && value != originalSize) { 1111 warning = "A new SD Card file will be created.\nThe current SD Card file will be lost."; 1112 } 1113 } 1114 } 1115 } 1116 } 1117 } 1118 } 1119 1120 // validate the skin 1121 if (error == null) { 1122 // get the mode, we should only check if it's in size mode since 1123 // the built-in list mode is always valid. 1124 if (mSkinSizeRadio.getSelection()) { 1125 // need both with and heigh to be non null 1126 String width = mSkinSizeWidth.getText(); // no need for trim, since the verifier 1127 String height = mSkinSizeHeight.getText(); // rejects non digit. 1128 1129 if (width.length() == 0 || height.length() == 0) { 1130 error = "Skin size is incorrect.\nBoth dimensions must be > 0."; 1131 } 1132 } 1133 } 1134 1135 // Check for duplicate AVD name 1136 if (isCreate && hasAvdName && error == null && !mForceCreation.getSelection()) { 1137 Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(avdName); 1138 assert conflict != null; 1139 switch(conflict.getFirst()) { 1140 case NO_CONFLICT: 1141 break; 1142 case CONFLICT_EXISTING_AVD: 1143 case CONFLICT_INVALID_AVD: 1144 error = String.format( 1145 "The AVD name '%s' is already used.\n" + 1146 "Check \"Override the existing AVD\" to delete the existing one.", 1147 avdName); 1148 break; 1149 case CONFLICT_EXISTING_PATH: 1150 error = String.format( 1151 "Conflict with %s\n" + 1152 "Check \"Override the existing AVD\" to delete the existing one.", 1153 conflict.getSecond()); 1154 break; 1155 default: 1156 // Hmm not supposed to happen... probably someone expanded the 1157 // enum without adding something here. In this case just do an 1158 // assert and use a generic error message. 1159 error = String.format( 1160 "Conflict %s with %s.\n" + 1161 "Check \"Override the existing AVD\" to delete the existing one.", 1162 conflict.getFirst().toString(), 1163 conflict.getSecond()); 1164 assert false; 1165 break; 1166 } 1167 } 1168 1169 if (error == null && mEditAvdInfo != null && isCreate) { 1170 warning = String.format("The AVD '%1$s' will be duplicated into '%2$s'.", 1171 mEditAvdInfo.getName(), 1172 avdName); 1173 } 1174 1175 // Validate the create button 1176 boolean can_create = hasAvdName && error == null; 1177 if (can_create) { 1178 can_create &= mTargetCombo.getSelectionIndex() >= 0; 1179 } 1180 mOkButton.setEnabled(can_create); 1181 1182 // Adjust the create button label as needed 1183 if (isCreate) { 1184 mOkButton.setText("Create AVD"); 1185 } else { 1186 mOkButton.setText("Edit AVD"); 1187 } 1188 1189 // -- update UI 1190 if (error != null) { 1191 mStatusIcon.setImage(mImageFactory.getImageByName("reject_icon16.png")); //$NON-NLS-1$ 1192 mStatusLabel.setText(error); 1193 } else if (warning != null) { 1194 mStatusIcon.setImage(mImageFactory.getImageByName("warning_icon16.png")); //$NON-NLS-1$ 1195 mStatusLabel.setText(warning); 1196 } else { 1197 mStatusIcon.setImage(null); 1198 mStatusLabel.setText(" \n "); //$NON-NLS-1$ 1199 } 1200 1201 mStatusComposite.pack(true); 1202 } 1203 1204 private void loadSkin() { 1205 int targetIndex = mTargetCombo.getSelectionIndex(); 1206 if (targetIndex < 0) { 1207 return; 1208 } 1209 1210 // resolve the target. 1211 String targetName = mTargetCombo.getItem(targetIndex); 1212 IAndroidTarget target = mCurrentTargets.get(targetName); 1213 if (target == null) { 1214 return; 1215 } 1216 1217 // get the skin name 1218 String skinName = null; 1219 int skinIndex = mSkinCombo.getSelectionIndex(); 1220 if (skinIndex < 0) { 1221 return; 1222 } else if (skinIndex == 0) { // default skin for the target 1223 skinName = target.getDefaultSkin(); 1224 } else { 1225 skinName = mSkinCombo.getItem(skinIndex); 1226 } 1227 1228 // load the skin properties 1229 String path = target.getPath(IAndroidTarget.SKINS); 1230 File skin = new File(path, skinName); 1231 if (skin.isDirectory() == false && target.isPlatform() == false) { 1232 // it's possible the skin is in the parent target 1233 path = target.getParent().getPath(IAndroidTarget.SKINS); 1234 skin = new File(path, skinName); 1235 } 1236 1237 if (skin.isDirectory() == false) { 1238 return; 1239 } 1240 1241 // now get the hardware.ini from the add-on (if applicable) and from the skin 1242 // (if applicable) 1243 HashMap<String, String> hardwareValues = new HashMap<String, String>(); 1244 if (target.isPlatform() == false) { 1245 FileWrapper targetHardwareFile = new FileWrapper(target.getLocation(), 1246 AvdManager.HARDWARE_INI); 1247 if (targetHardwareFile.isFile()) { 1248 Map<String, String> targetHardwareConfig = ProjectProperties.parsePropertyFile( 1249 targetHardwareFile, null /*log*/); 1250 1251 if (targetHardwareConfig != null) { 1252 hardwareValues.putAll(targetHardwareConfig); 1253 } 1254 } 1255 } 1256 1257 // from the skin 1258 FileWrapper skinHardwareFile = new FileWrapper(skin, AvdManager.HARDWARE_INI); 1259 if (skinHardwareFile.isFile()) { 1260 Map<String, String> skinHardwareConfig = ProjectProperties.parsePropertyFile( 1261 skinHardwareFile, null /*log*/); 1262 1263 if (skinHardwareConfig != null) { 1264 hardwareValues.putAll(skinHardwareConfig); 1265 } 1266 } 1267 1268 // now set those values in the list of properties for the AVD. 1269 // We just check that none of those properties have been edited by the user yet. 1270 for (Entry<String, String> entry : hardwareValues.entrySet()) { 1271 if (mEditedProperties.contains(entry.getKey()) == false) { 1272 mProperties.put(entry.getKey(), entry.getValue()); 1273 } 1274 } 1275 1276 mHardwareViewer.refresh(); 1277 } 1278 1279 /** 1280 * Creates an AVD from the values in the UI. Called when the user presses the OK button. 1281 */ 1282 private boolean createAvd() { 1283 String avdName = mAvdName.getText().trim(); 1284 int index = mTargetCombo.getSelectionIndex(); 1285 1286 // quick check on the name and the target selection 1287 if (avdName.length() == 0 || index < 0) { 1288 return false; 1289 } 1290 1291 // resolve the target. 1292 String targetName = mTargetCombo.getItem(index); 1293 IAndroidTarget target = mCurrentTargets.get(targetName); 1294 if (target == null) { 1295 return false; 1296 } 1297 1298 // get the abi type 1299 mAbiType = SdkConstants.ABI_ARMEABI; 1300 ISystemImage[] systemImages = getSystemImages(target); 1301 if (systemImages.length > 0) { 1302 int abiIndex = mAbiTypeCombo.getSelectionIndex(); 1303 if (abiIndex >= 0) { 1304 String prettyname = mAbiTypeCombo.getItem(abiIndex); 1305 //Extract the abi type 1306 int firstIndex = prettyname.indexOf("("); 1307 int lastIndex = prettyname.indexOf(")"); 1308 mAbiType = prettyname.substring(firstIndex+1, lastIndex); 1309 } 1310 } 1311 1312 // get the SD card data from the UI. 1313 String sdName = null; 1314 if (mSdCardSizeRadio.getSelection()) { 1315 // size mode 1316 String value = mSdCardSize.getText().trim(); 1317 if (value.length() > 0) { 1318 sdName = value; 1319 // add the unit 1320 switch (mSdCardSizeCombo.getSelectionIndex()) { 1321 case 0: 1322 sdName += "K"; //$NON-NLS-1$ 1323 break; 1324 case 1: 1325 sdName += "M"; //$NON-NLS-1$ 1326 break; 1327 case 2: 1328 sdName += "G"; //$NON-NLS-1$ 1329 break; 1330 default: 1331 // shouldn't be here 1332 assert false; 1333 } 1334 } 1335 } else { 1336 // file mode. 1337 sdName = mSdCardFile.getText().trim(); 1338 } 1339 1340 // get the Skin data from the UI 1341 String skinName = null; 1342 if (mSkinListRadio.getSelection()) { 1343 // built-in list provides the skin 1344 int skinIndex = mSkinCombo.getSelectionIndex(); 1345 if (skinIndex > 0) { 1346 // index 0 is the default, we don't use it 1347 skinName = mSkinCombo.getItem(skinIndex); 1348 } 1349 } else { 1350 // size mode. get both size and writes it as a skin name 1351 // thanks to validatePage() we know the content of the fields is correct 1352 skinName = mSkinSizeWidth.getText() + "x" + mSkinSizeHeight.getText(); //$NON-NLS-1$ 1353 } 1354 1355 ILogger log = mSdkLog; 1356 if (log == null || log instanceof MessageBoxLog) { 1357 // If the current logger is a message box, we use our own (to make sure 1358 // to display errors right away and customize the title). 1359 log = new MessageBoxLog( 1360 String.format("Result of creating AVD '%s':", avdName), 1361 getContents().getDisplay(), 1362 false /*logErrorsOnly*/); 1363 } 1364 1365 File avdFolder = null; 1366 try { 1367 avdFolder = AvdInfo.getDefaultAvdFolder(mAvdManager, avdName); 1368 } catch (AndroidLocationException e) { 1369 return false; 1370 } 1371 1372 boolean force = mForceCreation.getSelection(); 1373 boolean snapshot = mSnapshotCheck.getSelection(); 1374 1375 boolean success = false; 1376 AvdInfo avdInfo = mAvdManager.createAvd( 1377 avdFolder, 1378 avdName, 1379 target, 1380 mAbiType, 1381 skinName, 1382 sdName, 1383 mProperties, 1384 snapshot, 1385 force, 1386 mEditAvdInfo != null, //edit existing 1387 log); 1388 1389 success = avdInfo != null; 1390 1391 if (log instanceof MessageBoxLog) { 1392 ((MessageBoxLog) log).displayResult(success); 1393 } 1394 return success; 1395 } 1396 1397 /** 1398 * Returns the list of system images of a target. 1399 * <p/> 1400 * If target is null, returns an empty list. 1401 * If target is an add-on with no system images, return the list from its parent platform. 1402 * 1403 * @param target An IAndroidTarget. Can be null. 1404 * @return A non-null ISystemImage array. Can be empty. 1405 */ 1406 private ISystemImage[] getSystemImages(IAndroidTarget target) { 1407 if (target != null) { 1408 ISystemImage[] images = target.getSystemImages(); 1409 1410 if ((images == null || images.length == 0) && !target.isPlatform()) { 1411 // If an add-on does not provide any system images, use the ones from the parent. 1412 images = target.getParent().getSystemImages(); 1413 } 1414 1415 if (images != null) { 1416 return images; 1417 } 1418 } 1419 1420 return new ISystemImage[0]; 1421 } 1422 1423 // End of hiding from SWT Designer 1424 //$hide<<$ 1425} 1426