Merge change 3016 into donut

* changes:
  SDK Updater: Refactor, polish and complete the progress dialog.
This commit is contained in:
Android (Google) Code Review
2009-06-03 11:01:14 -07:00
5 changed files with 400 additions and 168 deletions

View File

@@ -226,7 +226,7 @@ public class LocalPackagesPage extends Composite {
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);

View File

@@ -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<<$
}

View File

@@ -19,133 +19,29 @@ package com.android.sdkuilib.internal.repository;
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 @@ class ProgressTask extends Dialog
* 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 @@ class ProgressTask extends Dialog
*/
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 @@ class ProgressTask extends Dialog
* @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 @@ class ProgressTask extends Dialog
* 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 @@ class ProgressTask extends Dialog
* 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<<$
}

View File

@@ -34,6 +34,6 @@ public class ProgressTaskFactory implements ITaskFactory {
}
public void start(String title, ITask task) {
ProgressTask.start(mShell, title, task);
new ProgressTask(mShell, title, task);
}
}

View File

@@ -350,7 +350,7 @@ public class UpdaterWindowImpl {
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 @@ public class UpdaterWindowImpl {
archiveFile = downloadArchive(archive, monitor);
if (archiveFile != null) {
if (installArchive(archive, archiveFile, monitor)) {
monitor.setResult("Installed: %1$s",
archive.getParentPackage().getShortDescription());
num_installed++;
}
}