• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.SdkConstants;
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 import com.android.util.Pair;
25 
26 import org.eclipse.jface.dialogs.MessageDialog;
27 import org.eclipse.swt.SWT;
28 import org.eclipse.swt.events.SelectionAdapter;
29 import org.eclipse.swt.events.SelectionEvent;
30 import org.eclipse.swt.events.ShellAdapter;
31 import org.eclipse.swt.events.ShellEvent;
32 import org.eclipse.swt.graphics.Point;
33 import org.eclipse.swt.graphics.Rectangle;
34 import org.eclipse.swt.layout.GridData;
35 import org.eclipse.swt.layout.GridLayout;
36 import org.eclipse.swt.widgets.Button;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Dialog;
39 import org.eclipse.swt.widgets.Display;
40 import org.eclipse.swt.widgets.Label;
41 import org.eclipse.swt.widgets.ProgressBar;
42 import org.eclipse.swt.widgets.Shell;
43 import org.eclipse.swt.widgets.Text;
44 
45 
46 /**
47  * Implements a {@link ProgressTaskDialog}, used by the {@link ProgressTask} class.
48  * This separates the dialog UI from the task logic.
49  *
50  * Note: this does not implement the {@link ITaskMonitor} interface to avoid confusing
51  * SWT Designer.
52  */
53 final class ProgressTaskDialog extends Dialog implements IProgressUiProvider {
54 
55     /**
56      * Min Y location for dialog. Need to deal with the menu bar on mac os.
57      */
58     private final static int MIN_Y = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ?
59             20 : 0;
60 
61     private static enum CancelMode {
62         /** Cancel button says "Cancel" and is enabled. Waiting for user to cancel. */
63         ACTIVE,
64         /** Cancel button has been clicked. Waiting for thread to finish. */
65         CANCEL_PENDING,
66         /** Close pending. Close button clicked or thread finished but there were some
67          * messages so the user needs to manually close. */
68         CLOSE_MANUAL,
69         /** Close button clicked or thread finished. The window will automatically close. */
70         CLOSE_AUTO
71     }
72 
73     /** The current mode of operation of the dialog. */
74     private CancelMode mCancelMode = CancelMode.ACTIVE;
75 
76     /** Last dialog size for this session. */
77     private static Point sLastSize;
78 
79 
80     // UI fields
81     private Shell mDialogShell;
82     private Composite mRootComposite;
83     private Label mLabel;
84     private ProgressBar mProgressBar;
85     private Button mCancelButton;
86     private Text mResultText;
87 
88 
89     /**
90      * Create the dialog.
91      * @param parent Parent container
92      */
ProgressTaskDialog(Shell parent)93     public ProgressTaskDialog(Shell parent) {
94         super(parent, SWT.APPLICATION_MODAL);
95     }
96 
97     /**
98      * Open the dialog and blocks till it gets closed
99      * @param taskThread The thread to run the task. Cannot be null.
100      */
open(Thread taskThread)101     public void open(Thread taskThread) {
102         createContents();
103         positionShell();                        //$hide$ (hide from SWT designer)
104         mDialogShell.open();
105         mDialogShell.layout();
106 
107         startThread(taskThread);                //$hide$ (hide from SWT designer)
108 
109         Display display = getParent().getDisplay();
110         while (!mDialogShell.isDisposed() && mCancelMode != CancelMode.CLOSE_AUTO) {
111             if (!display.readAndDispatch()) {
112                 display.sleep();
113             }
114         }
115 
116         setCancelRequested();       //$hide$ (hide from SWT designer)
117 
118         if (!mDialogShell.isDisposed()) {
119             sLastSize = mDialogShell.getSize();
120             mDialogShell.close();
121         }
122     }
123 
124     /**
125      * Create contents of the dialog.
126      */
createContents()127     private void createContents() {
128         mDialogShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE);
129         mDialogShell.addShellListener(new ShellAdapter() {
130             @Override
131             public void shellClosed(ShellEvent e) {
132                 onShellClosed(e);
133             }
134         });
135         mDialogShell.setLayout(new GridLayout(1, false));
136         mDialogShell.setSize(450, 300);
137         mDialogShell.setText(getText());
138 
139         mRootComposite = new Composite(mDialogShell, SWT.NONE);
140         mRootComposite.setLayout(new GridLayout(2, false));
141         mRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
142 
143         mLabel = new Label(mRootComposite, SWT.NONE);
144         mLabel.setText("Task");
145         mLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
146 
147         mProgressBar = new ProgressBar(mRootComposite, SWT.NONE);
148         mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
149         mCancelButton = new Button(mRootComposite, SWT.NONE);
150         mCancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
151         mCancelButton.setText("Cancel");
152 
153         mCancelButton.addSelectionListener(new SelectionAdapter() {
154             @Override
155             public void widgetSelected(SelectionEvent e) {
156                 onCancelSelected();  //$hide$
157             }
158         });
159 
160         mResultText = new Text(mRootComposite,
161                 SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |
162                 SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
163         mResultText.setEditable(true);
164         mResultText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
165     }
166 
167     // -- End of UI, Start of internal logic ----------
168     // Hide everything down-below from SWT designer
169     //$hide>>$
170 
171     @Override
isCancelRequested()172     public boolean isCancelRequested() {
173         return mCancelMode != CancelMode.ACTIVE;
174     }
175 
176     /**
177      * Sets the mode to cancel pending.
178      * The first time this grays the cancel button, to let the user know that the
179      * cancel operation is pending.
180      */
setCancelRequested()181     public void setCancelRequested() {
182         if (!mDialogShell.isDisposed()) {
183             // The dialog is not disposed, make sure to run all this in the UI thread
184             // and lock on the cancel button mode.
185             mDialogShell.getDisplay().syncExec(new Runnable() {
186 
187                 @Override
188                 public void run() {
189                     synchronized (mCancelMode) {
190                         if (mCancelMode == CancelMode.ACTIVE) {
191                             mCancelMode = CancelMode.CANCEL_PENDING;
192 
193                             if (!mCancelButton.isDisposed()) {
194                                 mCancelButton.setEnabled(false);
195                             }
196                         }
197                     }
198                 }
199             });
200         } else {
201             // The dialog is disposed. Just set the boolean. We shouldn't be here.
202             if (mCancelMode == CancelMode.ACTIVE) {
203                 mCancelMode = CancelMode.CANCEL_PENDING;
204             }
205         }
206     }
207 
208     /**
209      * Sets the mode to close manual.
210      * The first time, this also ungrays the pause button and converts it to a close button.
211      */
setManualCloseRequested()212     public void setManualCloseRequested() {
213         if (!mDialogShell.isDisposed()) {
214             // The dialog is not disposed, make sure to run all this in the UI thread
215             // and lock on the cancel button mode.
216             mDialogShell.getDisplay().syncExec(new Runnable() {
217 
218                 @Override
219                 public void run() {
220                     synchronized (mCancelMode) {
221                         if (mCancelMode != CancelMode.CLOSE_MANUAL &&
222                                 mCancelMode != CancelMode.CLOSE_AUTO) {
223                             mCancelMode = CancelMode.CLOSE_MANUAL;
224 
225                             if (!mCancelButton.isDisposed()) {
226                                 mCancelButton.setEnabled(true);
227                                 mCancelButton.setText("Close");
228                             }
229                         }
230                     }
231                 }
232             });
233         } else {
234             // The dialog is disposed. Just set the booleans. We shouldn't be here.
235             if (mCancelMode != CancelMode.CLOSE_MANUAL &&
236                     mCancelMode != CancelMode.CLOSE_AUTO) {
237                 mCancelMode = CancelMode.CLOSE_MANUAL;
238             }
239         }
240     }
241 
242     /**
243      * Sets the mode to close auto.
244      * The main loop will just exit and close the shell at the first opportunity.
245      */
setAutoCloseRequested()246     public void setAutoCloseRequested() {
247         synchronized (mCancelMode) {
248             if (mCancelMode != CancelMode.CLOSE_AUTO) {
249                 mCancelMode = CancelMode.CLOSE_AUTO;
250             }
251         }
252     }
253 
254     /**
255      * Callback invoked when the cancel button is selected.
256      * When in closing mode, this simply closes the shell. Otherwise triggers a cancel.
257      */
onCancelSelected()258     private void onCancelSelected() {
259         if (mCancelMode == CancelMode.CLOSE_MANUAL) {
260             setAutoCloseRequested();
261         } else {
262             setCancelRequested();
263         }
264     }
265 
266     /**
267      * Callback invoked when the shell is closed either by clicking the close button
268      * on by calling shell.close().
269      * This does the same thing as clicking the cancel/close button unless the mode is
270      * to auto close in which case we should do nothing to let the shell close normally.
271      */
onShellClosed(ShellEvent e)272     private void onShellClosed(ShellEvent e) {
273         if (mCancelMode != CancelMode.CLOSE_AUTO) {
274             e.doit = false; // don't close directly
275             onCancelSelected();
276         }
277     }
278 
279     /**
280      * Sets the description in the current task dialog.
281      * This method can be invoked from a non-UI thread.
282      */
283     @Override
setDescription(final String description)284     public void setDescription(final String description) {
285         mDialogShell.getDisplay().syncExec(new Runnable() {
286             @Override
287             public void run() {
288                 if (!mLabel.isDisposed()) {
289                     mLabel.setText(description);
290                 }
291             }
292         });
293     }
294 
295     /**
296      * Adds to the log in the current task dialog.
297      * This method can be invoked from a non-UI thread.
298      */
299     @Override
log(final String info)300     public void log(final String info) {
301         if (!mDialogShell.isDisposed()) {
302             mDialogShell.getDisplay().syncExec(new Runnable() {
303                 @Override
304                 public void run() {
305                     if (!mResultText.isDisposed()) {
306                         mResultText.setVisible(true);
307                         String lastText = mResultText.getText();
308                         if (lastText != null &&
309                                 lastText.length() > 0 &&
310                                 !lastText.endsWith("\n") &&     //$NON-NLS-1$
311                                 !info.startsWith("\n")) {       //$NON-NLS-1$
312                             mResultText.append("\n");           //$NON-NLS-1$
313                         }
314                         mResultText.append(info);
315                     }
316                 }
317             });
318         }
319     }
320 
321     @Override
logError(String info)322     public void logError(String info) {
323         log(info);
324     }
325 
326     @Override
logVerbose(String info)327     public void logVerbose(String info) {
328         log(info);
329     }
330 
331     /**
332      * Sets the max value of the progress bar.
333      * This method can be invoked from a non-UI thread.
334      *
335      * @see ProgressBar#setMaximum(int)
336      */
337     @Override
setProgressMax(final int max)338     public void setProgressMax(final int max) {
339         if (!mDialogShell.isDisposed()) {
340             mDialogShell.getDisplay().syncExec(new Runnable() {
341                 @Override
342                 public void run() {
343                     if (!mProgressBar.isDisposed()) {
344                         mProgressBar.setMaximum(max);
345                     }
346                 }
347             });
348         }
349     }
350 
351     /**
352      * Sets the current value of the progress bar.
353      * This method can be invoked from a non-UI thread.
354      */
355     @Override
setProgress(final int value)356     public void setProgress(final int value) {
357         if (!mDialogShell.isDisposed()) {
358             mDialogShell.getDisplay().syncExec(new Runnable() {
359                 @Override
360                 public void run() {
361                     if (!mProgressBar.isDisposed()) {
362                         mProgressBar.setSelection(value);
363                     }
364                 }
365             });
366         }
367     }
368 
369     /**
370      * Returns the current value of the progress bar,
371      * between 0 and up to {@link #setProgressMax(int)} - 1.
372      * This method can be invoked from a non-UI thread.
373      */
374     @Override
getProgress()375     public int getProgress() {
376         final int[] result = new int[] { 0 };
377 
378         if (!mDialogShell.isDisposed()) {
379             mDialogShell.getDisplay().syncExec(new Runnable() {
380                 @Override
381                 public void run() {
382                     if (!mProgressBar.isDisposed()) {
383                         result[0] = mProgressBar.getSelection();
384                     }
385                 }
386             });
387         }
388 
389         return result[0];
390     }
391 
392     /**
393      * Display a yes/no question dialog box.
394      *
395      * This implementation allow this to be called from any thread, it
396      * makes sure the dialog is opened synchronously in the ui thread.
397      *
398      * @param title The title of the dialog box
399      * @param message The error message
400      * @return true if YES was clicked.
401      */
402     @Override
displayPrompt(final String title, final String message)403     public boolean displayPrompt(final String title, final String message) {
404         Display display = mDialogShell.getDisplay();
405 
406         // we need to ask the user what he wants to do.
407         final boolean[] result = new boolean[] { false };
408         display.syncExec(new Runnable() {
409             @Override
410             public void run() {
411                 result[0] = MessageDialog.openQuestion(mDialogShell, title, message);
412             }
413         });
414         return result[0];
415     }
416 
417     /**
418      * This method opens a pop-up window which requests for User Login and
419      * password.
420      *
421      * @param title The title of the window.
422      * @param message The message to displayed in the login/password window.
423      * @return Returns a {@link Pair} holding the entered login and password.
424      *         The information must always be in the following order:
425      *         Login,Password. So in order to retrieve the <b>login</b> callers
426      *         should retrieve the first element, and the second value for the
427      *         <b>password</b>.
428      *         If operation is <b>canceled</b> by user the return value must be <b>null</b>.
429      * @see ITaskMonitor#displayLoginCredentialsPrompt(String, String)
430      */
431     @Override
displayLoginCredentialsPrompt( final String title, final String message)432     public UserCredentials displayLoginCredentialsPrompt(
433             final String title, final String message) {
434         Display display = mDialogShell.getDisplay();
435 
436         // open dialog and request login and password
437         GetUserCredentialsTask task = new GetUserCredentialsTask(mDialogShell, title, message);
438         display.syncExec(task);
439 
440         return new UserCredentials(task.userName, task.password, task.workstation, task.domain);
441     }
442 
443     private static class GetUserCredentialsTask implements Runnable {
444         public String userName = null;
445         public String password = null;
446         public String workstation = null;
447         public String domain = null;
448 
449         private Shell mShell;
450         private String mTitle;
451         private String mMessage;
452 
GetUserCredentialsTask(Shell shell, String title, String message)453         public GetUserCredentialsTask(Shell shell, String title, String message) {
454             mShell = shell;
455             mTitle = title;
456             mMessage = message;
457         }
458 
459         @Override
run()460         public void run() {
461             AuthenticationDialog authenticationDialog = new AuthenticationDialog(mShell,
462                         mTitle, mMessage);
463             int dlgResult= authenticationDialog.open();
464             if(dlgResult == GridDialog.OK) {
465                 userName = authenticationDialog.getLogin();
466                 password = authenticationDialog.getPassword();
467                 workstation = authenticationDialog.getWorkstation();
468                 domain = authenticationDialog.getDomain();
469             }
470         }
471     }
472 
473     /**
474      * Starts the thread that runs the task.
475      * This is deferred till the UI is created.
476      */
startThread(Thread taskThread)477     private void startThread(Thread taskThread) {
478         if (taskThread != null) {
479             taskThread.start();
480         }
481     }
482 
483     /**
484      * Centers the dialog in its parent shell.
485      */
positionShell()486     private void positionShell() {
487         // Centers the dialog in its parent shell
488         Shell child = mDialogShell;
489         Shell parent = getParent();
490         if (child != null && parent != null) {
491 
492             // get the parent client area with a location relative to the display
493             Rectangle parentArea = parent.getClientArea();
494             Point parentLoc = parent.getLocation();
495             int px = parentLoc.x;
496             int py = parentLoc.y;
497             int pw = parentArea.width;
498             int ph = parentArea.height;
499 
500             // Reuse the last size if there's one, otherwise use the default
501             Point childSize = sLastSize != null ? sLastSize : child.getSize();
502             int cw = childSize.x;
503             int ch = childSize.y;
504 
505             int x = px + (pw - cw) / 2;
506             if (x < 0) x = 0;
507 
508             int y = py + (ph - ch) / 2;
509             if (y < MIN_Y) y = MIN_Y;
510 
511             child.setLocation(x, y);
512             child.setSize(cw, ch);
513         }
514     }
515 
516     // End of hiding from SWT Designer
517     //$hide<<$
518 }
519