/* * 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.tasks; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.ProgressBar; import org.eclipse.swt.widgets.Shell; /** * An {@link ITaskMonitor} that displays a {@link ProgressDialog}. */ public final class ProgressTask implements ITaskMonitor { private static final double MAX_COUNT = 10000.0; private final ProgressDialog mDialog; private boolean mAutomaticallyCloseOnTaskCompletion = true; private double mIncCoef = 0; private double mValue = 0; /** * 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 ProgressTask(Shell parent, String title, ITask task) { mDialog = new ProgressDialog(parent, createTaskThread(title, task)); mDialog.setText(title); mDialog.open(); } /** * Sets the description in the current task dialog. * This method can be invoked from a non-UI thread. */ public void setDescription(String descriptionFormat, Object...args) { mDialog.setDescription(descriptionFormat, args); } /** * Sets the description in the current task dialog. * This method can be invoked from a non-UI thread. */ public void setResult(String resultFormat, Object...args) { mAutomaticallyCloseOnTaskCompletion = false; mDialog.setResult(resultFormat, args); } /** * Sets the max value of the progress bar. * This method can be invoked from a non-UI thread. * * Weird things will happen if setProgressMax is called multiple times * *after* {@link #incProgress(int)}: we don't try to adjust it on the * fly. * * @see ProgressBar#setMaximum(int) */ public void setProgressMax(int max) { assert max > 0; // Always set the dialog's progress max to 10k since it only handles // integers and we want to have a better inner granularity. Instead // we use the max to compute a coefficient for inc deltas. mDialog.setProgressMax((int) MAX_COUNT); mIncCoef = max > 0 ? MAX_COUNT / max : 0; assert mIncCoef > 0; } /** * Increments the current value of the progress bar. * * This method can be invoked from a non-UI thread. */ public void incProgress(int delta) { if (delta > 0 && mIncCoef > 0) { internalIncProgress(delta * mIncCoef); } } private void internalIncProgress(double realDelta) { mValue += realDelta; mDialog.setProgress((int)mValue); } /** * Returns the current value of the progress bar, * between 0 and up to {@link #setProgressMax(int)} - 1. * * This method can be invoked from a non-UI thread. */ public int getProgress() { assert mIncCoef > 0; return mIncCoef > 0 ? (int)(mDialog.getProgress() / mIncCoef) : 0; } /** * Returns true if the "Cancel" button was selected. * It is up to the task thread to pool this and exit. */ public boolean isCancelRequested() { return mDialog.isCancelRequested(); } /** * 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 Thread createTaskThread(String title, final ITask task) { if (task != null) { return new Thread(title) { @Override public void run() { task.run(ProgressTask.this); if (mAutomaticallyCloseOnTaskCompletion) { mDialog.setAutoCloseRequested(); } else { mDialog.setManualCloseRequested(); } } }; } return null; } /** * Display a yes/no question dialog box. * * This implementation allow this to be called from any thread, it * makes sure the dialog is opened synchronously in the ui thread. * * @param title The title of the dialog box * @param message The error message * @return true if YES was clicked. */ public boolean displayPrompt(final String title, final String message) { final Shell shell = mDialog.getParent(); Display display = shell.getDisplay(); // we need to ask the user what he wants to do. final boolean[] result = new boolean[] { false }; display.syncExec(new Runnable() { public void run() { result[0] = MessageDialog.openQuestion(shell, title, message); } }); return result[0]; } /** * Creates a sub-monitor that will use up to tickCount on the progress bar. * tickCount must be 1 or more. */ public ITaskMonitor createSubMonitor(int tickCount) { assert mIncCoef > 0; assert tickCount > 0; return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef); } private interface ISubTaskMonitor extends ITaskMonitor { public void subIncProgress(double realDelta); } private static class SubTaskMonitor implements ISubTaskMonitor { private final ProgressTask mRoot; private final ISubTaskMonitor mParent; private final double mStart; private final double mSpan; private double mSubValue; private double mSubCoef; /** * Creates a new sub task monitor which will work for the given range [start, start+span] * in its parent. * * @param root The ProgressTask root * @param parent The immediate parent. Can be the null or another sub task monitor. * @param start The start value in the root's coordinates * @param span The span value in the root's coordinates */ public SubTaskMonitor(ProgressTask root, ISubTaskMonitor parent, double start, double span) { mRoot = root; mParent = parent; mStart = start; mSpan = span; mSubValue = start; } public boolean isCancelRequested() { return mRoot.isCancelRequested(); } public void setDescription(String descriptionFormat, Object... args) { mRoot.setDescription(descriptionFormat, args); } public void setResult(String resultFormat, Object... args) { mRoot.setResult(resultFormat, args); } public void setProgressMax(int max) { assert max > 0; mSubCoef = max > 0 ? mSpan / max : 0; assert mSubCoef > 0; } public int getProgress() { assert mSubCoef > 0; return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0; } public void incProgress(int delta) { if (delta > 0 && mSubCoef > 0) { subIncProgress(delta * mSubCoef); } } public void subIncProgress(double realDelta) { mSubValue += realDelta; if (mParent != null) { mParent.subIncProgress(realDelta); } else { mRoot.internalIncProgress(realDelta); } } public boolean displayPrompt(String title, String message) { return mRoot.displayPrompt(title, message); } public ITaskMonitor createSubMonitor(int tickCount) { assert mSubCoef > 0; assert tickCount > 0; return new SubTaskMonitor(mRoot, this, mSubValue, tickCount * mSubCoef); } } }