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 3f32ddc18..6e9cef8ac 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 @@ 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); 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 000000000..b1f5da786 --- /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 7667355fe..b2599efaa 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 @@ 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<<$ } 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 ceb701e79..08f5485aa 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 class ProgressTaskFactory implements ITaskFactory { } 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 70641fe3b..a7223b51b 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 @@ 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++; } }