• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.sdkuilib.internal.tasks;
18 
19 import com.android.sdklib.internal.repository.ITask;
20 import com.android.sdklib.internal.repository.ITaskMonitor;
21 import com.android.sdkuilib.ui.AuthenticationDialog;
22 import com.android.sdkuilib.ui.GridDialog;
23 import com.android.util.Pair;
24 
25 import org.eclipse.jface.dialogs.MessageDialog;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.widgets.Control;
28 import org.eclipse.swt.widgets.Event;
29 import org.eclipse.swt.widgets.Label;
30 import org.eclipse.swt.widgets.Listener;
31 import org.eclipse.swt.widgets.ProgressBar;
32 import org.eclipse.swt.widgets.Shell;
33 import org.eclipse.swt.widgets.Widget;
34 
35 
36 /**
37  * Implements a "view" that uses an existing progress bar, status button and
38  * status text to display a {@link ITaskMonitor}.
39  */
40 public final class ProgressView implements IProgressUiProvider {
41 
42     private static enum State {
43         /** View created but there's no task running. Next state can only be ACTIVE. */
44         IDLE,
45         /** A task is currently running. Next state is either STOP_PENDING or IDLE. */
46         ACTIVE,
47         /** Stop button has been clicked. Waiting for thread to finish. Next state is IDLE. */
48         STOP_PENDING,
49     }
50 
51     /** The current mode of operation of the dialog. */
52     private State mState = State.IDLE;
53 
54 
55 
56     // UI fields
57     private final Label mLabel;
58     private final Control mStopButton;
59     private final ProgressBar mProgressBar;
60 
61     /** Logger object. Cannot not be null. */
62     private final ILogUiProvider mLog;
63 
64     /**
65      * Creates a new {@link ProgressView} object, a simple "holder" for the various
66      * widgets used to display and update a progress + status bar.
67      *
68      * @param label The label to display titles of status updates (e.g. task titles and
69      *      calls to {@link #setDescription(String)}.) Must not be null.
70      * @param progressBar The progress bar to update during a task. Must not be null.
71      * @param stopButton The stop button. It will be disabled when there's no task that can
72      *      be interrupted. A selection listener will be attached to it. Optional. Can be null.
73      * @param log A <em>mandatory</em> logger object that will be used to report all the log.
74      *      Must not be null.
75      */
ProgressView( Label label, ProgressBar progressBar, Control stopButton, ILogUiProvider log)76     public ProgressView(
77             Label label,
78             ProgressBar progressBar,
79             Control stopButton,
80             ILogUiProvider log) {
81         mLabel = label;
82         mProgressBar = progressBar;
83         mLog = log;
84         mProgressBar.setEnabled(false);
85 
86         mStopButton = stopButton;
87         if (mStopButton != null) {
88             mStopButton.addListener(SWT.Selection, new Listener() {
89                 public void handleEvent(Event event) {
90                     if (mState == State.ACTIVE) {
91                         changeState(State.STOP_PENDING);
92                     }
93                 }
94             });
95         }
96     }
97 
98     /**
99      * Starts the task and block till it's either finished or canceled.
100      * This can be called from a non-UI thread safely.
101      */
startTask( final String title, final ITaskMonitor parentMonitor, final ITask task)102     public void startTask(
103             final String title,
104             final ITaskMonitor parentMonitor,
105             final ITask task) {
106         if (task != null) {
107             try {
108                 if (parentMonitor == null && !mProgressBar.isDisposed()) {
109                     mLabel.setText(title);
110                     mProgressBar.setSelection(0);
111                     mProgressBar.setEnabled(true);
112                     changeState(ProgressView.State.ACTIVE);
113                 }
114 
115                 Runnable r = new Runnable() {
116                     public void run() {
117                         if (parentMonitor == null) {
118                             task.run(new TaskMonitorImpl(ProgressView.this));
119 
120                         } else {
121                             // Use all the reminder of the parent monitor.
122                             if (parentMonitor.getProgressMax() == 0) {
123                                 parentMonitor.setProgressMax(1);
124                             }
125                             ITaskMonitor sub = parentMonitor.createSubMonitor(
126                                     parentMonitor.getProgressMax() - parentMonitor.getProgress());
127                             try {
128                                 task.run(sub);
129                             } finally {
130                                 int delta =
131                                     sub.getProgressMax() - sub.getProgress();
132                                 if (delta > 0) {
133                                     sub.incProgress(delta);
134                                 }
135                             }
136                         }
137                     }
138                 };
139 
140                 // If for some reason the UI has been disposed, just abort the thread.
141                 if (mProgressBar.isDisposed()) {
142                     return;
143                 }
144 
145                 if (TaskMonitorImpl.isTaskMonitorImpl(parentMonitor)) {
146                     // If there's a parent monitor and it's our own class, we know this parent
147                     // is already running a thread and the base one is running an event loop.
148                     // We should thus not run a second event loop and we can process the
149                     // runnable right here instead of spawning a thread inside the thread.
150                     r.run();
151 
152                 } else {
153                     // No parent monitor. This is the first one so we need a thread and
154                     // we need to process UI events.
155 
156                     final Thread t = new Thread(r, title);
157                     t.start();
158 
159                     // Process the app's event loop whilst we wait for the thread to finish
160                     while (!mProgressBar.isDisposed() && t.isAlive()) {
161                         if (!mProgressBar.getDisplay().readAndDispatch()) {
162                             mProgressBar.getDisplay().sleep();
163                         }
164                     }
165                 }
166             } catch (Exception e) {
167                 // TODO log
168 
169             } finally {
170                 if (parentMonitor == null && !mProgressBar.isDisposed()) {
171                     changeState(ProgressView.State.IDLE);
172                     mProgressBar.setSelection(0);
173                     mProgressBar.setEnabled(false);
174                 }
175             }
176         }
177     }
178 
syncExec(final Widget widget, final Runnable runnable)179     private void syncExec(final Widget widget, final Runnable runnable) {
180         if (widget != null && !widget.isDisposed()) {
181             widget.getDisplay().syncExec(new Runnable() {
182                 public void run() {
183                     // Check again whether the widget got disposed between the time where
184                     // we requested the syncExec and the time it actually happened.
185                     if (!widget.isDisposed()) {
186                         runnable.run();
187                     }
188                 }
189             });
190         }
191     }
192 
changeState(State state)193     private void changeState(State state) {
194         if (mState != null ) {
195             mState = state;
196         }
197 
198         syncExec(mStopButton, new Runnable() {
199             public void run() {
200                 mStopButton.setEnabled(mState == State.ACTIVE);
201             }
202         });
203 
204     }
205 
206     // --- Implementation of ITaskUiProvider ---
207 
isCancelRequested()208     public boolean isCancelRequested() {
209         return mState != State.ACTIVE;
210     }
211 
212     /**
213      * Sets the description in the current task dialog.
214      * This method can be invoked from a non-UI thread.
215      */
setDescription(final String description)216     public void setDescription(final String description) {
217         syncExec(mLabel, new Runnable() {
218             public void run() {
219                 mLabel.setText(description);
220             }
221         });
222 
223         mLog.setDescription(description);
224     }
225 
226     /**
227      * Logs a "normal" information line.
228      * This method can be invoked from a non-UI thread.
229      */
log(String log)230     public void log(String log) {
231         mLog.log(log);
232     }
233 
234     /**
235      * Logs an "error" information line.
236      * This method can be invoked from a non-UI thread.
237      */
logError(String log)238     public void logError(String log) {
239         mLog.logError(log);
240     }
241 
242     /**
243      * Logs a "verbose" information line, that is extra details which are typically
244      * not that useful for the end-user and might be hidden until explicitly shown.
245      * This method can be invoked from a non-UI thread.
246      */
logVerbose(String log)247     public void logVerbose(String log) {
248         mLog.logVerbose(log);
249     }
250 
251     /**
252      * Sets the max value of the progress bar.
253      * This method can be invoked from a non-UI thread.
254      *
255      * @see ProgressBar#setMaximum(int)
256      */
setProgressMax(final int max)257     public void setProgressMax(final int max) {
258         syncExec(mProgressBar, new Runnable() {
259             public void run() {
260                 mProgressBar.setMaximum(max);
261             }
262         });
263     }
264 
265     /**
266      * Sets the current value of the progress bar.
267      * This method can be invoked from a non-UI thread.
268      */
setProgress(final int value)269     public void setProgress(final int value) {
270         syncExec(mProgressBar, new Runnable() {
271             public void run() {
272                 mProgressBar.setSelection(value);
273             }
274         });
275     }
276 
277     /**
278      * Returns the current value of the progress bar,
279      * between 0 and up to {@link #setProgressMax(int)} - 1.
280      * This method can be invoked from a non-UI thread.
281      */
getProgress()282     public int getProgress() {
283         final int[] result = new int[] { 0 };
284 
285         if (!mProgressBar.isDisposed()) {
286             mProgressBar.getDisplay().syncExec(new Runnable() {
287                 public void run() {
288                     if (!mProgressBar.isDisposed()) {
289                         result[0] = mProgressBar.getSelection();
290                     }
291                 }
292             });
293         }
294 
295         return result[0];
296     }
297 
displayPrompt(final String title, final String message)298     public boolean displayPrompt(final String title, final String message) {
299         final boolean[] result = new boolean[] { false };
300 
301         syncExec(mProgressBar, new Runnable() {
302             public void run() {
303                 Shell shell = mProgressBar.getShell();
304                 result[0] = MessageDialog.openQuestion(shell, title, message);
305             }
306         });
307 
308         return result[0];
309     }
310 
311     /**
312      * This method opens a pop-up window which requests for User Login and
313      * password.
314      *
315      * @param title The title of the window.
316      * @param message The message to displayed in the login/password window.
317      * @return Returns a {@link Pair} holding the entered login and password.
318      *         The information must always be in the following order:
319      *         Login,Password. So in order to retrieve the <b>login</b> callers
320      *         should retrieve the first element, and the second value for the
321      *         <b>password</b>.
322      *         If operation is <b>canceled</b> by user the return value must be <b>null</b>.
323      * @see ITaskMonitor#displayLoginPasswordPrompt(String, String)
324      */
325     public Pair<String, String>
displayLoginPasswordPrompt(final String title, final String message)326             displayLoginPasswordPrompt(final String title, final String message) {
327         final String[] resultArray = new String[] {"", ""};
328         // open dialog and request login and password
329         syncExec(mProgressBar, new Runnable() {
330             public void run() {
331                 Shell shell = mProgressBar.getShell();
332                 AuthenticationDialog authenticationDialog = new AuthenticationDialog(shell,
333                         title,
334                         message);
335                 int dlgResult = authenticationDialog.open();
336                 if (dlgResult == GridDialog.OK) {
337                     resultArray[0] = authenticationDialog.getLogin();
338                     resultArray[1] = authenticationDialog.getPassword();
339                 } else {
340                     resultArray[0] = null;
341                     resultArray[1] = null;
342                 }
343             }
344         });
345         return resultArray[0] == null ? null : Pair.of(resultArray[0], resultArray[1]);
346     }
347 
348 }
349