OSDN Git Service

AVD Selector: Add a "Start" button to launch the selected AVD in a
authorRaphael <raphael@google.com>
Thu, 9 Jul 2009 19:37:09 +0000 (15:37 -0400)
committerRaphael <raphael@google.com>
Thu, 9 Jul 2009 19:37:09 +0000 (15:37 -0400)
new emulator. Doesn't detect if the AVD is already running; instead
prints the emulator error.

eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java [moved from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressDialog.java with 96% similarity]
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java [moved from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java with 95% similarity]
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskFactory.java [moved from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java with 88% similarity]
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java

index 6de2601..aad1812 100644 (file)
@@ -1521,7 +1521,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
     /**
      * Get the stderr/stdout outputs of a process and return when the process is done.
      * Both <b>must</b> be read or the process will block on windows.
-     * @param process The process to get the ouput from
+     * @param process The process to get the output from
      */
     private void grabEmulatorOutput(final Process process) {
         // read the lines as they come. if null is returned, it's
index 6813d0b..d2fe5ae 100644 (file)
@@ -411,6 +411,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
         offsetComp.setLayout(layout);
 
         mPreferredAvdSelector = new AvdSelector(offsetComp,
+                mSdk.getSdkLocation(),
                 mSdk.getAvdManager(),
                 new NonRunningAvdFilter(),
                 DisplayMode.SIMPLE_SELECTION);
index 2378248..74d4c4b 100644 (file)
@@ -195,7 +195,9 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
         // create the selector with no manager, we'll reset the manager every time this is
         // displayed to ensure we have the latest one (dialog is reused but SDK could have
         // been changed in between.
-        mPreferredAvdSelector = new AvdSelector(offsetComp, null /* avd manager */,
+        mPreferredAvdSelector = new AvdSelector(offsetComp,
+                Sdk.getCurrent().getSdkLocation(),
+                null /* avd manager */,
                 DisplayMode.SIMPLE_CHECK);
         mPreferredAvdSelector.setTableHeightHint(100);
         mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
index d69ce5f..ccfd276 100755 (executable)
@@ -52,7 +52,10 @@ public class AvdManagerPage extends Composite implements ISdkListener {
         Label label = new Label(parent, SWT.NONE);\r
         label.setText("List of existing Android Virtual Devices:");\r
 \r
-        mAvdSelector = new AvdSelector(parent, mUpdaterData.getAvdManager(), DisplayMode.MANAGER);\r
+        mAvdSelector = new AvdSelector(parent,\r
+                mUpdaterData.getOsSdkRoot(),\r
+                mUpdaterData.getAvdManager(),\r
+                DisplayMode.MANAGER);\r
     }\r
 \r
     @Override\r
index 5c47b7c..e36bd6c 100755 (executable)
@@ -23,6 +23,7 @@ import com.android.sdklib.internal.repository.RepoSource;
 import com.android.sdklib.internal.repository.RepoSources;\r
 import com.android.sdklib.repository.SdkRepository;\r
 import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
+import com.android.sdkuilib.internal.tasks.ProgressTaskFactory;\r
 \r
 import org.eclipse.swt.SWT;\r
 import org.eclipse.swt.custom.SashForm;\r
@@ -14,7 +14,7 @@
  * limitations under the License.\r
  */\r
 \r
-package com.android.sdkuilib.internal.repository;\r
+package com.android.sdkuilib.internal.tasks;\r
 \r
 import com.android.sdklib.internal.repository.ITask;\r
 import com.android.sdklib.internal.repository.ITaskMonitor;\r
@@ -26,7 +26,7 @@ import org.eclipse.swt.widgets.Shell;
 /**\r
  * An {@link ITaskMonitor} that displays a {@link ProgressDialog}.\r
  */\r
-class ProgressTask implements ITaskMonitor {\r
+public final class ProgressTask implements ITaskMonitor {\r
 \r
     private static final double MAX_COUNT = 10000.0;\r
 \r
@@ -14,7 +14,7 @@
  * limitations under the License.\r
  */\r
 \r
-package com.android.sdkuilib.internal.repository;\r
+package com.android.sdkuilib.internal.tasks;\r
 \r
 import com.android.sdklib.internal.repository.ITask;\r
 import com.android.sdklib.internal.repository.ITaskFactory;\r
@@ -25,7 +25,7 @@ import org.eclipse.swt.widgets.Shell;
  * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog\r
  * for each new task.\r
  */\r
-public class ProgressTaskFactory implements ITaskFactory {\r
+public final class ProgressTaskFactory implements ITaskFactory {\r
 \r
     private final Shell mShell;\r
 \r
index 9ef76e2..8804c70 100644 (file)
@@ -19,7 +19,6 @@ package com.android.sdkuilib.internal.widgets;
 import com.android.prefs.AndroidLocation;
 import com.android.prefs.AndroidLocation.AndroidLocationException;
 import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.ISdkLog;
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.avd.AvdManager;
 import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
index ab02b8d..8826b4c 100644 (file)
@@ -20,10 +20,14 @@ import com.android.prefs.AndroidLocation.AndroidLocationException;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.ISdkLog;
 import com.android.sdklib.NullSdkLog;
+import com.android.sdklib.SdkConstants;
 import com.android.sdklib.internal.avd.AvdManager;
 import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
 import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
+import com.android.sdklib.internal.repository.ITask;
+import com.android.sdklib.internal.repository.ITaskMonitor;
 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
+import com.android.sdkuilib.internal.tasks.ProgressTask;
 import com.android.sdkuilib.repository.UpdaterWindow;
 
 import org.eclipse.jface.dialogs.MessageDialog;
@@ -49,16 +53,19 @@ import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
 import org.eclipse.swt.widgets.TableItem;
 
+import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 
 
 /**
  * The AVD selector is a table that is added to the given parent composite.
  * <p/>
- * To use, create it using {@link #AvdSelector(Composite, AvdManager, DisplayMode)} then
- * call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)}
- * and finally use {@link #getSelected()} to retrieve the selection.
+ * After using one of the constructors, call {@link #setSelection(AvdInfo)},
+ * {@link #setSelectionListener(SelectionListener)} and finally use
+ * {@link #getSelected()} to retrieve the selection.
  */
 public final class AvdSelector {
     private static int NUM_COL = 2;
@@ -66,6 +73,7 @@ public final class AvdSelector {
     private final DisplayMode mDisplayMode;
 
     private AvdManager mAvdManager;
+    private final String mOsSdkPath;
 
     private Table mTable;
     private Button mDeleteButton;
@@ -74,16 +82,20 @@ public final class AvdSelector {
     private Button mRefreshButton;
     private Button mManagerButton;
     private Button mUpdateButton;
+    private Button mStartButton;
 
     private SelectionListener mSelectionListener;
     private IAvdFilter mTargetFilter;
 
+    /** Defaults to true. Changed by the {@link #setEnabled(boolean)} method to represent the
+     * "global" enabled state on this composite. */
     private boolean mIsEnabled = true;
 
     private ImageFactory mImageFactory;
     private Image mOkImage;
     private Image mBrokenImage;
 
+
     /**
      * The display mode of the AVD Selector.
      */
@@ -170,15 +182,20 @@ public final class AvdSelector {
      * {@link IAndroidTarget} will be displayed.
      *
      * @param parent The parent composite where the selector will be added.
+     * @param osSdkPath The SDK root path. When not null, enables the start button to start
+     *                  an emulator on a given AVD.
      * @param manager the AVD manager.
      * @param filter When non-null, will allow filtering the AVDs to display.
-     * @param extraAction When non-null, displays an extra action button.
      * @param displayMode The display mode ({@link DisplayMode}).
+     *
+     * TODO: pass an ISdkLog and use it when reloading, starting the emulator, etc.
      */
     public AvdSelector(Composite parent,
+            String osSdkPath,
             AvdManager manager,
             IAvdFilter filter,
             DisplayMode displayMode) {
+        mOsSdkPath = osSdkPath;
         mAvdManager = manager;
         mTargetFilter = filter;
         mDisplayMode = displayMode;
@@ -265,6 +282,17 @@ public final class AvdSelector {
             }
         });
 
+        mStartButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+        mStartButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mStartButton.setText("Start...");
+        mStartButton.setToolTipText("Starts the selected AVD.");
+        mStartButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent arg0) {
+                onStart();
+            }
+        });
+
         Composite padding = new Composite(buttons, SWT.NONE);
         padding.setLayoutData(new GridData(GridData.FILL_VERTICAL));
 
@@ -327,9 +355,11 @@ public final class AvdSelector {
      * @param manager the AVD manager.
      * @param displayMode The display mode ({@link DisplayMode}).
      */
-    public AvdSelector(Composite parent, AvdManager manager,
+    public AvdSelector(Composite parent,
+            String osSdkPath,
+            AvdManager manager,
             DisplayMode displayMode) {
-        this(parent, manager, (IAvdFilter)null /* filter */, displayMode);
+        this(parent, osSdkPath, manager, (IAvdFilter)null /* filter */, displayMode);
     }
 
     /**
@@ -344,10 +374,11 @@ public final class AvdSelector {
      * @param displayMode The display mode ({@link DisplayMode}).
      */
     public AvdSelector(Composite parent,
+            String osSdkPath,
             AvdManager manager,
             IAndroidTarget filter,
             DisplayMode displayMode) {
-        this(parent, manager, new TargetBasedFilter(filter), displayMode);
+        this(parent, osSdkPath, manager, new TargetBasedFilter(filter), displayMode);
     }
     /**
      * Sets the table grid layout data.
@@ -373,7 +404,6 @@ public final class AvdSelector {
      * This must be called from the UI thread.
      *
      * @param reload if true, the AVD manager will reload the AVD from the disk.
-     * @throws AndroidLocationException if reload the AVD failed.
      * @return false if the reloading failed. This is always true if <var>reload</var> is
      * <code>false</code>.
      */
@@ -735,11 +765,13 @@ public final class AvdSelector {
     }
 
     /**
-     * Updates the enable state of the Details, Delete and Update buttons.
+     * Updates the enable state of the Details, Start, Delete and Update buttons.
      */
     private void enableActionButtons() {
         if (mIsEnabled == false) {
             mDetailsButton.setEnabled(false);
+            mStartButton.setEnabled(false);
+
             if (mDeleteButton != null) {
                 mDeleteButton.setEnabled(false);
             }
@@ -748,13 +780,20 @@ public final class AvdSelector {
             }
         } else {
             AvdInfo selection = getTableSelection();
+            boolean hasSelection = selection != null;
+
+            mDetailsButton.setEnabled(hasSelection);
+            mStartButton.setEnabled(mOsSdkPath != null &&
+                    hasSelection &&
+                    selection != null &&
+                    selection.getStatus() == AvdStatus.OK);
 
-            mDetailsButton.setEnabled(selection != null);
             if (mDeleteButton != null) {
-                mDeleteButton.setEnabled(selection != null);
+                mDeleteButton.setEnabled(hasSelection);
             }
             if (mUpdateButton != null) {
-                mUpdateButton.setEnabled(selection != null &&
+                mUpdateButton.setEnabled(hasSelection &&
+                        selection != null &&
                         selection.getStatus() == AvdStatus.ERROR_IMAGE_DIR);
             }
         }
@@ -849,6 +888,113 @@ public final class AvdSelector {
         refresh(true /*reload*/); // UpdaterWindow uses its own AVD manager so this one must reload.
     }
 
+    private void onStart() {
+        AvdInfo avdInfo = getTableSelection();
+
+        if (avdInfo == null || mOsSdkPath == null) {
+            return;
+        }
+
+        String path = mOsSdkPath +
+            File.separator +
+            SdkConstants.OS_SDK_TOOLS_FOLDER +
+            SdkConstants.FN_EMULATOR;
+
+        final String avdName = avdInfo.getName();
+
+        // build the command line based on the available parameters.
+        ArrayList<String> list = new ArrayList<String>();
+        list.add(path);
+        list.add("-avd");   //$NON-NLS-1$
+        list.add(avdName);
+
+        // convert the list into an array for the call to exec.
+        final String[] command = list.toArray(new String[list.size()]);
+
+        // launch the emulator
+        new ProgressTask(mTable.getShell(),
+                "Starting Android Emulator",
+                new ITask() {
+                    public void run(ITaskMonitor monitor) {
+                        try {
+                            monitor.setDescription("Starting emualator for AVD '%1$s'", avdName);
+                            int n = 10;
+                            monitor.setProgressMax(n);
+                            Process process = Runtime.getRuntime().exec(command);
+                            grabEmulatorOutput(process, monitor);
+
+                            // This small wait prevents the dialog from closing too fast:
+                            // When it works, the emulator returns immediately, even if no UI
+                            // is shown yet. And when it fails (because the AVD is locked/running)
+                            // if we don't have a wait we don't capture the error for some reason.
+                            for (int i = 0; i < n; i++) {
+                                try {
+                                    Thread.sleep(100);
+                                    monitor.incProgress(1);
+                                } catch (InterruptedException e) {
+                                    // ignore
+                                }
+                            }
+                        } catch (IOException e) {
+                            monitor.setResult("Failed to start emulator: %1$s", e.getMessage());
+                        }
+                    }
+        });
+
+    }
+
+    /**
+     * Get the stderr/stdout outputs of a process and return when the process is done.
+     * Both <b>must</b> be read or the process will block on windows.
+     * @param process The process to get the output from.
+     * @param monitor An {@link ISdkLog} to capture errors.
+     */
+    private void grabEmulatorOutput(final Process process, final ITaskMonitor monitor) {
+        // read the lines as they come. if null is returned, it's because the process finished
+        new Thread("emu-stderr") { //$NON-NLS-1$
+            @Override
+            public void run() {
+                // create a buffer to read the stderr output
+                InputStreamReader is = new InputStreamReader(process.getErrorStream());
+                BufferedReader errReader = new BufferedReader(is);
+
+                try {
+                    while (true) {
+                        String line = errReader.readLine();
+                        if (line != null) {
+                            monitor.setResult("%1$s", line);    //$NON-NLS-1$
+                        } else {
+                            break;
+                        }
+                    }
+                } catch (IOException e) {
+                    // do nothing.
+                }
+            }
+        }.start();
+
+        new Thread("emu-stdout") { //$NON-NLS-1$
+            @Override
+            public void run() {
+                InputStreamReader is = new InputStreamReader(process.getInputStream());
+                BufferedReader outReader = new BufferedReader(is);
+
+                try {
+                    while (true) {
+                        String line = outReader.readLine();
+                        if (line != null) {
+                            monitor.setResult("%1$s", line);    //$NON-NLS-1$
+                        } else {
+                            break;
+                        }
+                    }
+                } catch (IOException e) {
+                    // do nothing.
+                }
+            }
+        }.start();
+    }
+
     /**
      * Collects all log from the AVD action and displays it in a dialog.
      */