OSDN Git Service

Update AVD creation UI to be easier for skin/sdcard.
authorXavier Ducrohet <xav@android.com>
Tue, 6 Oct 2009 23:54:20 +0000 (16:54 -0700)
committerXavier Ducrohet <xav@android.com>
Wed, 7 Oct 2009 00:06:51 +0000 (17:06 -0700)
BUG: 2162198

Change-Id: I47bf273a730a1bf6e0c3f47fb5247f02d06259a5

sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java

index 3c0369f..fbcf95a 100644 (file)
@@ -32,6 +32,8 @@ import org.eclipse.swt.events.ModifyEvent;
 import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
@@ -40,6 +42,7 @@ import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Text;
@@ -53,8 +56,6 @@ import java.util.TreeMap;
  * TODO:
  * - support custom hardware properties
  * - use SdkTargetSelector instead of Combo
- * - Better UI for the sdcard (radio button for K or M, info about what is valid value)
- * - Support for ###x### skins
  * - tooltips on widgets.
  *
  */
@@ -66,8 +67,15 @@ final class AvdCreationDialog extends Dialog {
 
     private Text mAvdName;
     private Combo mTargetCombo;
-    private Text mSdCard;
+
+    private Button mSdCardSizeRadio;
+    private Text mSdCardSize;
+    private Combo mSdCardSizeCombo;
+
+    private Text mSdCardFile;
     private Button mBrowseSdCard;
+    private Button mSdCardFileRadio;
+
     private Combo mSkinCombo;
     private Button mForceCreation;
     private Button mOkButton;
@@ -75,13 +83,32 @@ final class AvdCreationDialog extends Dialog {
     private Label mStatusLabel;
     private Composite mStatusComposite;
     private final ImageFactory mImageFactory;
+    private Button mSkinListRadio;
+    private Button mSkinSizeRadio;
+    private Text mSkinSizeWidth;
+    private Text mSkinSizeHeight;
+
+    /**
+     * {@link VerifyListener} for {@link Text} widgets that should only contains numbers.
+     */
+    private final VerifyListener mDigitVerifier = new VerifyListener() {
+        public void verifyText(VerifyEvent event) {
+            int count = event.text.length();
+            for (int i = 0 ; i < count ; i++) {
+                char c = event.text.charAt(i);
+                if (c < '0' || c > '9') {
+                    event.doit = false;
+                    return;
+                }
+            }
+        }
+    };
 
     /**
      * Callback when the AVD name is changed.
      * Enables the force checkbox if the name is a duplicate.
      */
     private class CreateNameModifyListener implements ModifyListener {
-
         public void modifyText(ModifyEvent e) {
             String name = mAvdName.getText().trim();
             AvdInfo avdMatch = mAvdManager.getAvd(name, false /*validAvdOnly*/);
@@ -96,8 +123,10 @@ final class AvdCreationDialog extends Dialog {
         }
     }
 
+    /**
+     * {@link ModifyListener} used for live-validation of the fields content.
+     */
     private class ValidateListener extends SelectionAdapter implements ModifyListener {
-
         public void modifyText(ModifyEvent e) {
             validatePage();
         }
@@ -140,24 +169,31 @@ final class AvdCreationDialog extends Dialog {
 
     @Override
     protected Control createDialogArea(Composite parent) {
+        GridData gd;
+        final int groupIndent = 30;
+
         Composite top = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout(2, false);
+        layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        layout.horizontalSpacing = convertHorizontalDLUsToPixels(
+                IDialogConstants.HORIZONTAL_SPACING);
+        top.setLayout(layout);
         top.setLayoutData(new GridData(GridData.FILL_BOTH));
-        top.setLayout(new GridLayout(3, false));
 
         Label label = new Label(top, SWT.NONE);
         label.setText("Name:");
 
         mAvdName = new Text(top, SWT.BORDER);
-        mAvdName.setLayoutData(new GridData(GridData.FILL, GridData.CENTER,
-                true, false, 2, 1));
+        mAvdName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
         mAvdName.addModifyListener(new CreateNameModifyListener());
 
         label = new Label(top, SWT.NONE);
         label.setText("Target:");
 
         mTargetCombo = new Combo(top, SWT.READ_ONLY | SWT.DROP_DOWN);
-        mTargetCombo.setLayoutData(new GridData(GridData.FILL, GridData.CENTER,
-                true, false, 2, 1));
+        mTargetCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
         mTargetCombo.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
@@ -167,18 +203,48 @@ final class AvdCreationDialog extends Dialog {
             }
         });
 
+        // --- sd card group
         label = new Label(top, SWT.NONE);
         label.setText("SD Card:");
-        label.setToolTipText("Either a path to an existing SD card image\n" +
-                "or an image size in K or M (e.g. 512K, 10M).");
+        label.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+        gd.horizontalSpan = 2;
+
+        final Group sdCardGroup = new Group(top, SWT.NONE);
+        sdCardGroup.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+        gd.horizontalIndent = groupIndent;
+        gd.horizontalSpan = 2;
+        sdCardGroup.setLayout(new GridLayout(3, false));
+
+        mSdCardSizeRadio = new Button(sdCardGroup, SWT.RADIO);
+        mSdCardSizeRadio.setText("Size:");
+        mSdCardSizeRadio.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent arg0) {
+                boolean sizeMode = mSdCardSizeRadio.getSelection();
+                enableSdCardWidgets(sizeMode);
+                validatePage();
+            }
+        });
 
         ValidateListener validateListener = new ValidateListener();
 
-        mSdCard = new Text(top, SWT.BORDER);
-        mSdCard.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-        mSdCard.addModifyListener(validateListener);
+        mSdCardSize = new Text(sdCardGroup, SWT.BORDER);
+        mSdCardSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mSdCardSize.addVerifyListener(mDigitVerifier);
+
+        mSdCardSizeCombo = new Combo(sdCardGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
+        mSdCardSizeCombo.add("K");
+        mSdCardSizeCombo.add("M");
+        mSdCardSizeCombo.select(1);
+
+        mSdCardFileRadio = new Button(sdCardGroup, SWT.RADIO);
+        mSdCardFileRadio.setText("File:");
+
+        mSdCardFile = new Text(sdCardGroup, SWT.BORDER);
+        mSdCardFile.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mSdCardFile.addModifyListener(validateListener);
 
-        mBrowseSdCard = new Button(top, SWT.PUSH);
+        mBrowseSdCard = new Button(sdCardGroup, SWT.PUSH);
         mBrowseSdCard.setText("Browse...");
         mBrowseSdCard.addSelectionListener(new SelectionAdapter() {
            @Override
@@ -188,19 +254,60 @@ final class AvdCreationDialog extends Dialog {
             }
         });
 
+        mSdCardSizeRadio.setSelection(true);
+        enableSdCardWidgets(true);
+
+        // --- skin group
         label = new Label(top, SWT.NONE);
-        label.setText("Skin");
+        label.setText("Skin:");
+        label.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+        gd.horizontalSpan = 2;
+
+        final Group skinGroup = new Group(top, SWT.NONE);
+        skinGroup.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+        gd.horizontalIndent = groupIndent;
+        gd.horizontalSpan = 2;
+        skinGroup.setLayout(new GridLayout(4, false));
+
+        mSkinListRadio = new Button(skinGroup, SWT.RADIO);
+        mSkinListRadio.setText("Built-in:");
+        mSkinListRadio.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent arg0) {
+                boolean listMode = mSkinListRadio.getSelection();
+                enableSkinWidgets(listMode);
+                validatePage();
+            }
+        });
 
-        mSkinCombo = new Combo(top, SWT.READ_ONLY | SWT.DROP_DOWN);
-        mSkinCombo.setLayoutData(new GridData(GridData.FILL, GridData.CENTER,
-                true, false, 2, 1));
+        mSkinCombo = new Combo(skinGroup, SWT.READ_ONLY | SWT.DROP_DOWN);
+        mSkinCombo.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+        gd.horizontalSpan = 3;
 
-        // dummies for alignment
-        label = new Label(top, SWT.NONE);
+        mSkinSizeRadio = new Button(skinGroup, SWT.RADIO);
+        mSkinSizeRadio.setText("Size:");
+
+        mSkinSizeWidth = new Text(skinGroup, SWT.BORDER);
+        mSkinSizeWidth.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mSkinSizeWidth.addVerifyListener(mDigitVerifier);
+        mSkinSizeWidth.addModifyListener(validateListener);
+
+        new Label(skinGroup, SWT.NONE).setText("x");
+
+        mSkinSizeHeight = new Text(skinGroup, SWT.BORDER);
+        mSkinSizeHeight.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mSkinSizeHeight.addVerifyListener(mDigitVerifier);
+        mSkinSizeHeight.addModifyListener(validateListener);
+
+        mSkinListRadio.setSelection(true);
+        enableSkinWidgets(true);
+
+        // --- end skin group
 
         mForceCreation = new Button(top, SWT.CHECK);
-        mForceCreation.setText("Force");
-        mForceCreation.setLayoutData(new GridData(GridData.FILL, GridData.CENTER,
+        mForceCreation.setText("Force create");
+        mForceCreation.setToolTipText("Select this to override any existing AVD");
+        mForceCreation.setLayoutData(new GridData(GridData.END, GridData.CENTER,
                 true, false, 2, 1));
         mForceCreation.setEnabled(false);
         mForceCreation.addSelectionListener(validateListener);
@@ -226,6 +333,7 @@ final class AvdCreationDialog extends Dialog {
 
         reloadTargetCombo();
 
+        applyDialogFont(top);
         return top;
     }
 
@@ -240,18 +348,44 @@ final class AvdCreationDialog extends Dialog {
 
     @Override
     protected void okPressed() {
-        if (onCreate()) {
+        if (createAvd()) {
             super.okPressed();
         }
     }
 
+    /**
+     * Enable or disable the sd card widgets.
+     * @param sizeMode if true the size-based widgets are to be enabled, and the file-based ones
+     * disabled.
+     */
+    private void enableSdCardWidgets(boolean sizeMode) {
+        mSdCardSize.setEnabled(sizeMode);
+        mSdCardSizeCombo.setEnabled(sizeMode);
+
+        mSdCardFile.setEnabled(!sizeMode);
+        mBrowseSdCard.setEnabled(!sizeMode);
+    }
+
+    /**
+     * Enable or disable the skin widgets.
+     * @param listMode if true the list-based widgets are to be enabled, and the size-based ones
+     * disabled.
+     */
+    private void enableSkinWidgets(boolean listMode) {
+        mSkinCombo.setEnabled(listMode);
+
+        mSkinSizeWidth.setEnabled(!listMode);
+        mSkinSizeHeight.setEnabled(!listMode);
+    }
+
+
     private void onBrowseSdCard() {
         FileDialog dlg = new FileDialog(getContents().getShell(), SWT.OPEN);
         dlg.setText("Choose SD Card image file.");
 
         String fileName = dlg.open();
         if (fileName != null) {
-            mSdCard.setText(fileName);
+            mSdCardFile.setText(fileName);
         }
     }
 
@@ -363,12 +497,29 @@ final class AvdCreationDialog extends Dialog {
 
         // Validate SDCard path or value
         if (error == null) {
-            String sdName = mSdCard.getText().trim();
+            // get the mode. We only need to check the file since the
+            // verifier on the size Text will prevent invalid input
+            boolean sdcardFileMode = mSdCardFileRadio.getSelection();
+            if (sdcardFileMode) {
+                String sdName = mSdCardFile.getText().trim();
+                if (sdName.length() > 0 && !new File(sdName).isFile()) {
+                    error = "SD Card path isn't valid.";
+                }
+            }
+        }
 
-            if (sdName.length() > 0 &&
-                    !new File(sdName).isFile() &&
-                    !AvdManager.SDCARD_SIZE_PATTERN.matcher(sdName).matches()) {
-                error = "SD Card must be either a file path or a size\nsuch as 128K or 64M.";
+        // validate the skin
+        if (error == null) {
+            // get the mode, we should only check if it's in size mode since
+            // the built-in list mode is always valid.
+            if (mSkinSizeRadio.getSelection()) {
+                // need both with and heigh to be non null
+                String width = mSkinSizeWidth.getText();   // no need for trim, since the verifier
+                String height = mSkinSizeHeight.getText(); // rejects non digit.
+
+                if (width.length() == 0 || height.length() == 0) {
+                    error = "Skin size is incorrect.\nBoth dimensions must be > 0";
+                }
             }
         }
 
@@ -378,7 +529,7 @@ final class AvdCreationDialog extends Dialog {
             if (avdMatch != null && !mForceCreation.getSelection()) {
                 error = String.format(
                         "The AVD name '%s' is already used.\n" +
-                        "Check \"Force\" to override existing AVD.",
+                        "Check \"Force create\" to override existing AVD.",
                         avdName);
             }
         }
@@ -405,27 +556,60 @@ final class AvdCreationDialog extends Dialog {
     /**
      * Creates an AVD from the values in the UI. Called when the user presses the OK button.
      */
-    private boolean onCreate() {
+    private boolean createAvd() {
         String avdName = mAvdName.getText().trim();
-        String sdName = mSdCard.getText().trim();
         int targetIndex = mTargetCombo.getSelectionIndex();
-        int skinIndex = mSkinCombo.getSelectionIndex();
-        boolean force = mForceCreation.getSelection();
 
+        // quick check on the name and the target selection
         if (avdName.length() == 0 || targetIndex < 0) {
             return false;
         }
 
+        // resolve the target.
         String targetName = mTargetCombo.getItem(targetIndex);
         IAndroidTarget target = mCurrentTargets.get(targetName);
         if (target == null) {
             return false;
         }
 
+        // get the SD card data from the UI.
+        String sdName = null;
+        if (mSdCardSizeRadio.getSelection()) {
+            // size mode
+            String value = mSdCardSize.getText().trim();
+            if (value.length() > 0) {
+                sdName = value;
+                // add the unit
+                switch (mSdCardSizeCombo.getSelectionIndex()) {
+                    case 0:
+                        sdName += "K";
+                        break;
+                    case 1:
+                        sdName += "M";
+                        break;
+                    default:
+                        // shouldn't be here
+                        assert false;
+                }
+            }
+        } else {
+            // file mode.
+            sdName = mSdCardFile.getText().trim();
+        }
+
+        // get the Skin data from the UI
         String skinName = null;
-        if (skinIndex > 0) {
-            // index 0 is the default, we don't use it
-            skinName = mSkinCombo.getItem(skinIndex);
+        if (mSkinListRadio.getSelection()) {
+            // built-in list provides the skin
+            int skinIndex = mSkinCombo.getSelectionIndex();
+            if (skinIndex > 0) {
+                // index 0 is the default, we don't use it
+                skinName = mSkinCombo.getItem(skinIndex);
+            }
+        } else {
+            // size mode. get both size and writes it as a skin name
+            // thanks to validatePage() we know the content of the fields is correct
+            skinName = mSkinSizeWidth.getText() + "x" + mSkinSizeHeight.getText(); //$NON-NLS-1$
         }
 
         SdkLog log = new SdkLog(String.format("Result of creating AVD '%s':", avdName),
@@ -440,6 +624,8 @@ final class AvdCreationDialog extends Dialog {
             return false;
         }
 
+        boolean force = mForceCreation.getSelection();
+
         boolean success = false;
         AvdInfo avdInfo = mAvdManager.createAvd(
                 avdFolder,
@@ -454,7 +640,6 @@ final class AvdCreationDialog extends Dialog {
         success = avdInfo != null;
 
         log.displayResult(success);
-
         return success;
     }
 }