SDK Updater: Refactor, polish and complete the progress dialog.

This splits the former ProgressTask in 2 parts:
ProgressDialog is just the SWT dialog and ProgressTask
is the task handling.

This helps avoiding confusing SWT Designer each time
I do a change in the logic. Plus it seems cleaner anyway.

The other thing this CL does is implement the pause/close
I originally wanted: the pause button is greyed once used
by the user whilst the task completes. After, it becomes
a "Close" button if there's a result text displayed.
Closing the window using the close box is now the same as
using the pause/close button (to avoid closing the dialog
with a running thread.)
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
index 3f32ddc..6e9cef8 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
@@ -226,7 +226,7 @@
 

     private void onUpdateInstalledPackage() {

         // TODO just a test, needs to be removed later.

-        ProgressTask.start(getShell(), "Test", new ITask() {

+        new ProgressTask(getShell(), "Test", new ITask() {

             public void run(ITaskMonitor monitor) {

                 monitor.setDescription("Test");

                 monitor.setProgressMax(100);

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressDialog.java
new file mode 100755
index 0000000..b1f5da7
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressDialog.java
@@ -0,0 +1,369 @@
+/*

+ * Copyright (C) 2009 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.internal.repository.ITaskMonitor;

+

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.events.SelectionAdapter;

+import org.eclipse.swt.events.SelectionEvent;

+import org.eclipse.swt.graphics.Point;

+import org.eclipse.swt.graphics.Rectangle;

+import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.layout.GridLayout;

+import org.eclipse.swt.widgets.Button;

+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Dialog;

+import org.eclipse.swt.widgets.Display;

+import org.eclipse.swt.widgets.Label;

+import org.eclipse.swt.widgets.ProgressBar;

+import org.eclipse.swt.widgets.Shell;

+import org.eclipse.swt.widgets.Text;

+import org.eclipse.swt.events.ShellAdapter;

+import org.eclipse.swt.events.ShellEvent;

+

+

+/**

+ * Implements a {@link ProgressDialog}, used by the {@link ProgressTask} class.

+ * This separates the dialog UI from the task logic.

+ *

+ * Note: this does not implement the {@link ITaskMonitor} interface to avoid confusing

+ * SWT Designer.

+ */

+final class ProgressDialog extends Dialog {

+

+    private enum CancelMode {

+        /** Cancel button says "Cancel" and is enabled. Waiting for user to cancel. */

+        ACTIVE,

+        /** Cancel button has been clicked. Waiting for thread to finish. */

+        CANCEL_PENDING,

+        /** Close pending. Close button clicked or thread finished but there were some

+         * messages so the user needs to manually close. */

+        CLOSE_MANUAL,

+        /** Close button clicked or thread finished. The window will automatically close. */

+        CLOSE_AUTO

+    }

+

+    /** The current mode of operation of the dialog. */

+    private CancelMode mCancelMode = CancelMode.ACTIVE;

+

+    /** Last dialog size for this session. */

+    private static Point sLastSize;

+

+

+    // UI fields

+    private Shell mDialogShell;

+    private Composite mRootComposite;

+    private Label mLabel;

+    private ProgressBar mProgressBar;

+    private Button mCancelButton;

+    private Text mResultText;

+    private final Thread mTaskThread;

+

+

+    /**

+     * Create the dialog.

+     * @param parent Parent container

+     * @param taskThread The thread to run the task.

+     */

+    public ProgressDialog(Shell parent, Thread taskThread) {

+        super(parent, SWT.APPLICATION_MODAL);

+        mTaskThread = taskThread;

+    }

+

+    /**

+     * Open the dialog and blocks till it gets closed

+     */

+    public void open() {

+        createContents();

+        positionShell();            //$hide$ (hide from SWT designer)

+        mDialogShell.open();

+        mDialogShell.layout();

+

+        startThread();              //$hide$ (hide from SWT designer)

+

+        Display display = getParent().getDisplay();

+        while (!mDialogShell.isDisposed() && mCancelMode != CancelMode.CLOSE_AUTO) {

+            if (!display.readAndDispatch()) {

+                display.sleep();

+            }

+        }

+

+        setCancelRequested();       //$hide$ (hide from SWT designer)

+

+        if (!mDialogShell.isDisposed()) {

+            sLastSize = mDialogShell.getSize();

+            mDialogShell.close();

+        }

+    }

+

+    /**

+     * Create contents of the dialog.

+     */

+    private void createContents() {

+        mDialogShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE);

+        mDialogShell.addShellListener(new ShellAdapter() {

+            @Override

+            public void shellClosed(ShellEvent e) {

+                onShellClosed(e);

+            }

+        });

+        mDialogShell.setLayout(new GridLayout(1, false));

+        mDialogShell.setSize(450, 300);

+        mDialogShell.setText(getText());

+

+        mRootComposite = new Composite(mDialogShell, SWT.NONE);

+        mRootComposite.setLayout(new GridLayout(2, false));

+        mRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

+

+        mLabel = new Label(mRootComposite, SWT.NONE);

+        mLabel.setText("Task");

+        mLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

+

+        mProgressBar = new ProgressBar(mRootComposite, SWT.NONE);

+        mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

+        mCancelButton = new Button(mRootComposite, SWT.NONE);

+        mCancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

+        mCancelButton.setText("Cancel");

+

+        mCancelButton.addSelectionListener(new SelectionAdapter() {

+            @Override

+            public void widgetSelected(SelectionEvent e) {

+                onCancelSelected();  //$hide$

+            }

+        });

+

+        mResultText = new Text(mRootComposite,

+                SWT.BORDER | SWT.READ_ONLY | SWT.V_SCROLL | SWT.MULTI);

+        mResultText.setEditable(true);

+        mResultText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));

+    }

+

+    // -- End of UI, Start of internal logic ----------

+    // Hide everything down-below from SWT designer

+    //$hide>>$

+

+    public boolean isCancelRequested() {

+        return mCancelMode != CancelMode.ACTIVE;

+    }

+

+    /**

+     * Sets the mode to cancel pending.

+     * The first time this grays the cancel button, to let the user know that the

+     * cancel operation is pending.

+     */

+    public void setCancelRequested() {

+        if (!mDialogShell.isDisposed()) {

+            // The dialog is not disposed, make sure to run all this in the UI thread

+            // and lock on the cancel button mode.

+            mDialogShell.getDisplay().syncExec(new Runnable() {

+

+                public void run() {

+                    synchronized (mCancelMode) {

+                        if (mCancelMode == CancelMode.ACTIVE) {

+                            mCancelMode = CancelMode.CANCEL_PENDING;

+

+                            if (!mCancelButton.isDisposed()) {

+                                mCancelButton.setEnabled(false);

+                            }

+                        }

+                    }

+                }

+            });

+        } else {

+            // The dialog is disposed. Just set the boolean. We shouldn't be here.

+            if (mCancelMode == CancelMode.ACTIVE) {

+                mCancelMode = CancelMode.CANCEL_PENDING;

+            }

+        }

+    }

+

+    /**

+     * Sets the mode to close manual.

+     * The first time, this also ungrays the pause button and converts it to a close button.

+     */

+    public void setManualCloseRequested() {

+        if (!mDialogShell.isDisposed()) {

+            // The dialog is not disposed, make sure to run all this in the UI thread

+            // and lock on the cancel button mode.

+            mDialogShell.getDisplay().syncExec(new Runnable() {

+

+                public void run() {

+                    synchronized (mCancelMode) {

+                        if (mCancelMode != CancelMode.CLOSE_MANUAL &&

+                                mCancelMode != CancelMode.CLOSE_AUTO) {

+                            mCancelMode = CancelMode.CLOSE_MANUAL;

+

+                            if (!mCancelButton.isDisposed()) {

+                                mCancelButton.setEnabled(true);

+                                mCancelButton.setText("Close");

+                            }

+                        }

+                    }

+                }

+            });

+        } else {

+            // The dialog is disposed. Just set the booleans. We shouldn't be here.

+            if (mCancelMode != CancelMode.CLOSE_MANUAL &&

+                    mCancelMode != CancelMode.CLOSE_AUTO) {

+                mCancelMode = CancelMode.CLOSE_MANUAL;

+            }

+        }

+    }

+

+    /**

+     * Sets the mode to close auto.

+     * The main loop will just exit and close the shell at the first opportunity.

+     */

+    public void setAutoCloseRequested() {

+        synchronized (mCancelMode) {

+            if (mCancelMode != CancelMode.CLOSE_AUTO) {

+                mCancelMode = CancelMode.CLOSE_AUTO;

+            }

+        }

+    }

+

+    /**

+     * Callback invoked when the cancel button is selected.

+     * When in closing mode, this simply closes the shell. Otherwise triggers a cancel.

+     */

+    private void onCancelSelected() {

+        if (mCancelMode == CancelMode.CLOSE_MANUAL) {

+            setAutoCloseRequested();

+        } else {

+            setCancelRequested();

+        }

+    }

+

+    /**

+     * Callback invoked when the shell is closed either by clicking the close button

+     * on by calling shell.close().

+     * This does the same thing as clicking the cancel/close button unless the mode is

+     * to auto close in which case we should do nothing to let the shell close normally.

+     */

+    private void onShellClosed(ShellEvent e) {

+        if (mCancelMode != CancelMode.CLOSE_AUTO) {

+            e.doit = false; // don't close directly

+            onCancelSelected();

+        }

+    }

+

+    /**

+     * Sets the description in the current task dialog.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void setDescription(final String descriptionFormat, final Object...args) {

+        mDialogShell.getDisplay().syncExec(new Runnable() {

+            public void run() {

+                if (!mLabel.isDisposed()) {

+                    mLabel.setText(String.format(descriptionFormat, args));

+                }

+            }

+        });

+    }

+

+    /**

+     * Sets the description in the current task dialog.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void setResult(final String resultFormat, final Object...args) {

+        if (!mDialogShell.isDisposed()) {

+            mDialogShell.getDisplay().syncExec(new Runnable() {

+                public void run() {

+                    if (!mResultText.isDisposed()) {

+                        mResultText.setVisible(true);

+                        mResultText.setText(String.format(resultFormat, args));

+                    }

+                }

+            });

+        }

+    }

+

+    /**

+     * Sets the max value of the progress bar.

+     * This method can be invoked from a non-UI thread.

+     *

+     * @see ProgressBar#setMaximum(int)

+     */

+    public void setProgressMax(final int max) {

+        if (!mDialogShell.isDisposed()) {

+            mDialogShell.getDisplay().syncExec(new Runnable() {

+                public void run() {

+                    if (!mProgressBar.isDisposed()) {

+                        mProgressBar.setMaximum(max);

+                    }

+                }

+            });

+        }

+    }

+

+    /**

+     * Increments the current value of the progress bar.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void incProgress(final int delta) {

+        if (!mDialogShell.isDisposed()) {

+            mDialogShell.getDisplay().syncExec(new Runnable() {

+                public void run() {

+                    if (!mProgressBar.isDisposed()) {

+                        mProgressBar.setSelection(mProgressBar.getSelection() + delta);

+                    }

+                }

+            });

+        }

+    }

+

+    /**

+     * Starts the thread that runs the task.

+     * This is deferred till the UI is created.

+     */

+    private void startThread() {

+        if (mTaskThread != null) {

+            mTaskThread.start();

+        }

+    }

+

+    /**

+     * Centers the dialog in its parent shell.

+     */

+    private void positionShell() {

+        // Centers the dialog in its parent shell

+        Shell child = mDialogShell;

+        Shell parent = getParent();

+        if (child != null && parent != null) {

+

+            // get the parent client area with a location relative to the display

+            Rectangle parentArea = parent.getClientArea();

+            Point parentLoc = parent.getLocation();

+            int px = parentLoc.x;

+            int py = parentLoc.y;

+            int pw = parentArea.width;

+            int ph = parentArea.height;

+

+            // Reuse the last size if there's one, otherwise use the default

+            Point childSize = sLastSize != null ? sLastSize : child.getSize();

+            int cw = childSize.x;

+            int ch = childSize.y;

+

+            child.setLocation(px + (pw - cw) / 2, py + (ph - ch) / 2);

+            child.setSize(cw, ch);

+        }

+    }

+

+    // End of hiding from SWT Designer

+    //$hide<<$

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
index 7667355..b2599ef 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
@@ -19,133 +19,29 @@
 import com.android.sdklib.internal.repository.ITask;

 import com.android.sdklib.internal.repository.ITaskMonitor;

 

-import org.eclipse.swt.SWT;

-import org.eclipse.swt.events.SelectionAdapter;

-import org.eclipse.swt.events.SelectionEvent;

-import org.eclipse.swt.layout.GridData;

-import org.eclipse.swt.layout.GridLayout;

-import org.eclipse.swt.widgets.Button;

-import org.eclipse.swt.widgets.Composite;

-import org.eclipse.swt.widgets.Dialog;

-import org.eclipse.swt.widgets.Display;

-import org.eclipse.swt.widgets.Label;

 import org.eclipse.swt.widgets.ProgressBar;

 import org.eclipse.swt.widgets.Shell;

-import org.eclipse.swt.widgets.Text;

 

-/*

- * TODO:

- * - trap window.close and treat it as a cancel request

- * - on cancel as been clicked *and* the task finished,, change it to a "close" button

+

+/**

+ * An {@link ITaskMonitor} that displays a {@link ProgressDialog}.

  */

+class ProgressTask implements ITaskMonitor {

 

-

-class ProgressTask extends Dialog

-    implements ITaskMonitor             //$hide$ (hide from SWT designer)

-    {

-

-    private boolean mCancelRequested;

-    private boolean mCloseRequested;

+    private ProgressDialog mDialog;

     private boolean mAutomaticallyCloseOnTaskCompletion = true;

 

 

-    // UI fields

-    private Shell mDialogShell;

-    private Composite mRootComposite;

-    private Label mLabel;

-    private ProgressBar mProgressBar;

-    private Button mCancelButton;

-    private Text mResultText;

-

-

-    /**

-     * Create the dialog.

-     * @param parent Parent container

-     */

-    public ProgressTask(Shell parent) {

-        super(parent, SWT.APPLICATION_MODAL);

-    }

-

-    /**

-     * Open the dialog and blocks till it gets closed

-     */

-    public void open() {

-        createContents();

-        mDialogShell.open();

-        mDialogShell.layout();

-        Display display = getParent().getDisplay();

-

-        startTask();    //$hide$ (hide from SWT designer)

-

-        while (!mDialogShell.isDisposed() && !mCloseRequested) {

-            if (!display.readAndDispatch()) {

-                display.sleep();

-            }

-        }

-

-        mCancelRequested = true;

-

-        if (!mDialogShell.isDisposed()) {

-            mDialogShell.close();

-        }

-    }

-

-    /**

-     * Create contents of the dialog.

-     */

-    private void createContents() {

-        mDialogShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);

-        mDialogShell.setLayout(new GridLayout(1, false));

-        mDialogShell.setSize(450, 300);

-        mDialogShell.setText(getText());

-

-        mRootComposite = new Composite(mDialogShell, SWT.NONE);

-        mRootComposite.setLayout(new GridLayout(2, false));

-        mRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

-

-        mLabel = new Label(mRootComposite, SWT.NONE);

-        mLabel.setText("Task");

-        mLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

-

-        mProgressBar = new ProgressBar(mRootComposite, SWT.NONE);

-        mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

-        mCancelButton = new Button(mRootComposite, SWT.NONE);

-        mCancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

-        mCancelButton.setText("Cancel");

-

-        mCancelButton.addSelectionListener(new SelectionAdapter() {

-            @Override

-            public void widgetSelected(SelectionEvent e) {

-                mCancelRequested = true;

-                mCancelButton.setEnabled(false);

-            }

-        });

-

-        mResultText = new Text(mRootComposite,

-                SWT.BORDER | SWT.READ_ONLY | SWT.V_SCROLL | SWT.MULTI);

-        mResultText.setEditable(true);

-        mResultText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));

-        mResultText.setVisible(false);

-    }

-

-    // -- End of UI, Start of internal logic ----------

-    // Hide everything down-below from SWT designer

-    //$hide>>$

-

-    private ITask mTask;

-

     /**

      * Creates a new {@link ProgressTask} with the given title.

      * The given task will execute in a separate thread (not the UI thread).

      *

      * This blocks till the thread ends.

      */

-    public static ProgressTask start(Shell parent, String title, ITask task) {

-        ProgressTask t = new ProgressTask(parent);

-        t.setText(title);

-        t.setTask(task);

-        t.open();

-        return t;

+    public ProgressTask(Shell parent, String title, ITask task) {

+        mDialog = new ProgressDialog(parent, createTaskThread(title, task));

+        mDialog.setText(title);

+        mDialog.open();

     }

 

     /**

@@ -153,13 +49,7 @@
      * This method can be invoke from a non-UI thread.

      */

     public void setDescription(final String descriptionFormat, final Object...args) {

-        mDialogShell.getDisplay().asyncExec(new Runnable() {

-            public void run() {

-                if (!mLabel.isDisposed()) {

-                    mLabel.setText(String.format(descriptionFormat, args));

-                }

-            }

-        });

+        mDialog.setDescription(descriptionFormat, args);

     }

 

     /**

@@ -168,16 +58,7 @@
      */

     public void setResult(final String resultFormat, final Object...args) {

         mAutomaticallyCloseOnTaskCompletion = false;

-        if (!mDialogShell.isDisposed()) {

-            mDialogShell.getDisplay().asyncExec(new Runnable() {

-                public void run() {

-                    if (!mResultText.isDisposed()) {

-                        mResultText.setVisible(true);

-                        mResultText.setText(String.format(resultFormat, args));

-                    }

-                }

-            });

-        }

+        mDialog.setResult(resultFormat, args);

     }

 

     /**

@@ -187,15 +68,7 @@
      * @see ProgressBar#setMaximum(int)

      */

     public void setProgressMax(final int max) {

-        if (!mDialogShell.isDisposed()) {

-            mDialogShell.getDisplay().asyncExec(new Runnable() {

-                public void run() {

-                    if (!mProgressBar.isDisposed()) {

-                        mProgressBar.setMaximum(max);

-                    }

-                }

-            });

-        }

+        mDialog.setProgressMax(max);

     }

 

     /**

@@ -204,15 +77,7 @@
      * This method can be invoked from a non-UI thread.

      */

     public void incProgress(final int delta) {

-        if (!mDialogShell.isDisposed()) {

-            mDialogShell.getDisplay().asyncExec(new Runnable() {

-                public void run() {

-                    if (!mProgressBar.isDisposed()) {

-                        mProgressBar.setSelection(mProgressBar.getSelection() + delta);

-                    }

-                }

-            });

-        }

+        mDialog.incProgress(delta);

     }

 

     /**

@@ -220,32 +85,28 @@
      * It is up to the task thread to pool this and exit.

      */

     public boolean cancelRequested() {

-        return mCancelRequested;

-    }

-

-    /** Sets the task that will execute in a separate thread. */

-    private void setTask(ITask task) {

-        mTask = task;

+        return mDialog.isCancelRequested();

     }

 

     /**

-     * Starts the task from {@link #setTask(ITask)} in a separate thread.

-     * When the task completes, set {@link #mCloseRequested} to end the dialog loop.

+     * Creates a thread to run the task. The thread has not been started yet.

+     * When the task completes, requests to close the dialog.

+     * @return A new thread that will run the task. The thread has not been started yet.

      */

-    private void startTask() {

-        if (mTask != null) {

-            new Thread(getText()) {

+    private Thread createTaskThread(String title, final ITask task) {

+        if (task != null) {

+            return new Thread(title) {

                 @Override

                 public void run() {

-                    mTask.run(ProgressTask.this);

+                    task.run(ProgressTask.this);

                     if (mAutomaticallyCloseOnTaskCompletion) {

-                        mCloseRequested = true;

+                        mDialog.setAutoCloseRequested();

+                    } else {

+                        mDialog.setManualCloseRequested();

                     }

                 }

-            }.start();

+            };

         }

+        return null;

     }

-

-    // End of hiding from SWT Designer

-    //$hide<<$

 }

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java
index ceb701e..08f5485 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java
@@ -34,6 +34,6 @@
     }

 

     public void start(String title, ITask task) {

-        ProgressTask.start(mShell, title, task);

+        new ProgressTask(mShell, title, task);

     }

 }

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
index 70641fe..a7223b5 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
@@ -350,7 +350,7 @@
 

                     if (!archive.isCompatible()) {

                         monitor.setResult("Skipping incompatible archive: %1$s",

-                                archive.getShortDescription());

+                                archive.getParentPackage().getShortDescription());

                         monitor.incProgress(NUM_FETCH_URL_MONITOR_INC + 10);

                         continue;

                     }

@@ -360,6 +360,8 @@
                         archiveFile = downloadArchive(archive, monitor);

                         if (archiveFile != null) {

                             if (installArchive(archive, archiveFile, monitor)) {

+                                monitor.setResult("Installed: %1$s",

+                                        archive.getParentPackage().getShortDescription());

                                 num_installed++;

                             }

                         }