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