• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.repository;
18 
19 import com.android.sdklib.ISdkLog;
20 import com.android.sdklib.SdkManager;
21 import com.android.sdklib.internal.repository.ITask;
22 import com.android.sdklib.internal.repository.ITaskFactory;
23 import com.android.sdklib.internal.repository.ITaskMonitor;
24 import com.android.sdklib.repository.SdkRepository;
25 
26 import java.util.ArrayList;
27 
28 /**
29  * Performs an update using only a non-interactive console output with no GUI.
30  * <p/>
31  * TODO: It may be useful in the future to let the filter specify packages names
32  * rather than package types, typically to let the user upgrade to a new platform.
33  * This can be achieved easily by simply allowing package names in the pkgFilter
34  * argument.
35  */
36 public class UpdateNoWindow {
37 
38     /** The {@link UpdaterData} to use. */
39     private final UpdaterData mUpdaterData;
40     /** The {@link ISdkLog} logger to use. */
41     private final ISdkLog mSdkLog;
42     /** The reply to any question asked by the update process. Currently this will
43      *   be yes/no for ability to replace modified samples or restart ADB. */
44     private final boolean mForce;
45 
46     /**
47      * Creates an UpdateNoWindow object that will update using the given SDK root
48      * and outputs to the given SDK logger.
49      *
50      * @param osSdkRoot The OS path of the SDK folder to update.
51      * @param sdkManager An existing SDK manager to list current platforms and addons.
52      * @param sdkLog A logger object, that should ideally output to a write-only console.
53      * @param force The reply to any question asked by the update process. Currently this will
54      *   be yes/no for ability to replace modified samples or restart ADB.
55      * @param useHttp True to force using HTTP instead of HTTPS for downloads.
56      */
UpdateNoWindow(String osSdkRoot, SdkManager sdkManager, ISdkLog sdkLog, boolean force, boolean useHttp)57     public UpdateNoWindow(String osSdkRoot,
58             SdkManager sdkManager,
59             ISdkLog sdkLog,
60             boolean force,
61             boolean useHttp) {
62         mSdkLog = sdkLog;
63         mForce = force;
64         mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
65 
66         // Change the in-memory settings to force the http/https mode
67         mUpdaterData.getSettingsController().setSetting(ISettingsPage.KEY_FORCE_HTTP, useHttp);
68 
69         // Use a factory that only outputs to the given ISdkLog.
70         mUpdaterData.setTaskFactory(new ConsoleTaskFactory());
71 
72         // Check that the AVD Manager has been correctly initialized. This is done separately
73         // from the constructor in the GUI-based UpdaterWindowImpl to give time to the UI to
74         // initialize before displaying a message box. Since we don't have any GUI here
75         // we can call it whenever we want.
76         if (mUpdaterData.checkIfInitFailed()) {
77             return;
78         }
79 
80         // Setup the default sources including the getenv overrides.
81         mUpdaterData.setupDefaultSources();
82 
83         mUpdaterData.getLocalSdkParser().parseSdk(osSdkRoot, sdkManager, sdkLog);
84 
85     }
86 
87     /**
88      * Performs the actual update.
89      *
90      * @param pkgFilter A list of {@link SdkRepository#NODES} to limit the type of packages
91      *   we can update. A null or empty list means to update everything possible.
92      * @param includeObsoletes True to also list and install obsolete packages.
93      * @param dryMode True to check what would be updated/installed but do not actually
94      *   download or install anything.
95      */
updateAll( ArrayList<String> pkgFilter, boolean includeObsoletes, boolean dryMode)96     public void updateAll(
97             ArrayList<String> pkgFilter,
98             boolean includeObsoletes,
99             boolean dryMode) {
100         mUpdaterData.updateOrInstallAll_NoGUI(pkgFilter, includeObsoletes, dryMode);
101     }
102 
103     /**
104      * A custom implementation of {@link ITaskFactory} that provides {@link ConsoleTask} objects.
105      */
106     private class ConsoleTaskFactory implements ITaskFactory {
start(String title, ITask task)107         public void start(String title, ITask task) {
108             new ConsoleTask(title, task);
109         }
110     }
111 
112     /**
113      * A custom implementation of {@link ITaskMonitor} that defers all output to the
114      * super {@link UpdateNoWindow#mSdkLog}.
115      */
116     private class ConsoleTask implements ITaskMonitor {
117 
118         private static final double MAX_COUNT = 10000.0;
119         private double mIncCoef = 0;
120         private double mValue = 0;
121         private String mLastDesc = null;
122         private String mLastProgressBase = null;
123 
124         /**
125          * Creates a new {@link ConsoleTask} with the given title.
126          */
ConsoleTask(String title, ITask task)127         public ConsoleTask(String title, ITask task) {
128             mSdkLog.printf("%s:\n", title);
129             task.run(this);
130         }
131 
132         /**
133          * Sets the description in the current task dialog.
134          */
setDescription(String descriptionFormat, Object...args)135         public void setDescription(String descriptionFormat, Object...args) {
136 
137             String last = mLastDesc;
138             String line = String.format("  " + descriptionFormat, args);
139 
140             // If the description contains a %, it generally indicates a recurring
141             // progress so we want a \r at the end.
142             if (line.indexOf('%') > -1) {
143                 if (mLastProgressBase != null && line.startsWith(mLastProgressBase)) {
144                     line = "    " + line.substring(mLastProgressBase.length());
145                 }
146                 line += "\r";
147             } else {
148                 mLastProgressBase = line;
149                 line += "\n";
150             }
151 
152             // Skip line if it's the same as the last one.
153             if (last != null && last.equals(line)) {
154                 return;
155             }
156             mLastDesc = line;
157 
158             // If the last line terminated with a \r but the new one doesn't, we need to
159             // insert a \n to avoid erasing the previous line.
160             if (last != null &&
161                     last.endsWith("\r") &&
162                     !line.endsWith("\r")) {
163                 line = "\n" + line;
164             }
165 
166             mSdkLog.printf("%s", line);
167         }
168 
169         /**
170          * Sets the description in the current task dialog.
171          */
setResult(String resultFormat, Object...args)172         public void setResult(String resultFormat, Object...args) {
173             setDescription(resultFormat, args);
174         }
175 
176         /**
177          * Sets the max value of the progress bar.
178          *
179          * Weird things will happen if setProgressMax is called multiple times
180          * *after* {@link #incProgress(int)}: we don't try to adjust it on the
181          * fly.
182          */
setProgressMax(int max)183         public void setProgressMax(int max) {
184             assert max > 0;
185             // Always set the dialog's progress max to 10k since it only handles
186             // integers and we want to have a better inner granularity. Instead
187             // we use the max to compute a coefficient for inc deltas.
188             mIncCoef = max > 0 ? MAX_COUNT / max : 0;
189             assert mIncCoef > 0;
190         }
191 
192         /**
193          * Increments the current value of the progress bar.
194          */
incProgress(int delta)195         public void incProgress(int delta) {
196             assert mIncCoef > 0;
197             assert delta > 0;
198             internalIncProgress(delta * mIncCoef);
199         }
200 
internalIncProgress(double realDelta)201         private void internalIncProgress(double realDelta) {
202             mValue += realDelta;
203             // max value is 10k, so 10k/100 == 100%.
204             // Experimentation shows that it is not really useful to display this
205             // progression since during download the description line will change.
206             // mSdkLog.printf("    [%3d%%]\r", ((int)mValue) / 100);
207         }
208 
209         /**
210          * Returns the current value of the progress bar,
211          * between 0 and up to {@link #setProgressMax(int)} - 1.
212          */
getProgress()213         public int getProgress() {
214             assert mIncCoef > 0;
215             return mIncCoef > 0 ? (int)(mValue / mIncCoef) : 0;
216         }
217 
218         /**
219          * Returns true if the "Cancel" button was selected.
220          */
isCancelRequested()221         public boolean isCancelRequested() {
222             return false;
223         }
224 
225         /**
226          * Display a yes/no question dialog box.
227          *
228          * This implementation allow this to be called from any thread, it
229          * makes sure the dialog is opened synchronously in the ui thread.
230          *
231          * @param title The title of the dialog box
232          * @param message The error message
233          * @return true if YES was clicked.
234          */
displayPrompt(final String title, final String message)235         public boolean displayPrompt(final String title, final String message) {
236             // TODO Make it interactive if mForce==false
237             mSdkLog.printf("\n%s\n%s\n[y/n] => %s\n",
238                     title,
239                     message,
240                     mForce ? "yes" : "no (use --force to override)");
241             return mForce;
242         }
243 
244         /**
245          * Creates a sub-monitor that will use up to tickCount on the progress bar.
246          * tickCount must be 1 or more.
247          */
createSubMonitor(int tickCount)248         public ITaskMonitor createSubMonitor(int tickCount) {
249             assert mIncCoef > 0;
250             assert tickCount > 0;
251             return new ConsoleSubTaskMonitor(this, null, mValue, tickCount * mIncCoef);
252         }
253     }
254 
255     private interface IConsoleSubTaskMonitor extends ITaskMonitor {
subIncProgress(double realDelta)256         public void subIncProgress(double realDelta);
257     }
258 
259     private static class ConsoleSubTaskMonitor implements IConsoleSubTaskMonitor {
260 
261         private final ConsoleTask mRoot;
262         private final IConsoleSubTaskMonitor mParent;
263         private final double mStart;
264         private final double mSpan;
265         private double mSubValue;
266         private double mSubCoef;
267 
268         /**
269          * Creates a new sub task monitor which will work for the given range [start, start+span]
270          * in its parent.
271          *
272          * @param root The ProgressTask root
273          * @param parent The immediate parent. Can be the null or another sub task monitor.
274          * @param start The start value in the root's coordinates
275          * @param span The span value in the root's coordinates
276          */
ConsoleSubTaskMonitor(ConsoleTask root, IConsoleSubTaskMonitor parent, double start, double span)277         public ConsoleSubTaskMonitor(ConsoleTask root,
278                 IConsoleSubTaskMonitor parent,
279                 double start,
280                 double span) {
281             mRoot = root;
282             mParent = parent;
283             mStart = start;
284             mSpan = span;
285             mSubValue = start;
286         }
287 
isCancelRequested()288         public boolean isCancelRequested() {
289             return mRoot.isCancelRequested();
290         }
291 
setDescription(String descriptionFormat, Object... args)292         public void setDescription(String descriptionFormat, Object... args) {
293             mRoot.setDescription(descriptionFormat, args);
294         }
295 
setResult(String resultFormat, Object... args)296         public void setResult(String resultFormat, Object... args) {
297             mRoot.setResult(resultFormat, args);
298         }
299 
setProgressMax(int max)300         public void setProgressMax(int max) {
301             assert max > 0;
302             mSubCoef = max > 0 ? mSpan / max : 0;
303             assert mSubCoef > 0;
304         }
305 
getProgress()306         public int getProgress() {
307             assert mSubCoef > 0;
308             return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;
309         }
310 
incProgress(int delta)311         public void incProgress(int delta) {
312             assert mSubCoef > 0;
313             subIncProgress(delta * mSubCoef);
314         }
315 
subIncProgress(double realDelta)316         public void subIncProgress(double realDelta) {
317             mSubValue += realDelta;
318             if (mParent != null) {
319                 mParent.subIncProgress(realDelta);
320             } else {
321                 mRoot.internalIncProgress(realDelta);
322             }
323         }
324 
displayPrompt(String title, String message)325         public boolean displayPrompt(String title, String message) {
326             return mRoot.displayPrompt(title, message);
327         }
328 
createSubMonitor(int tickCount)329         public ITaskMonitor createSubMonitor(int tickCount) {
330             assert mSubCoef > 0;
331             assert tickCount > 0;
332             return new ConsoleSubTaskMonitor(mRoot,
333                     this,
334                     mSubValue,
335                     tickCount * mSubCoef);
336         }
337     }
338 }
339