• 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.repository;
18 
19 
20 import com.android.sdklib.ISdkLog;
21 import com.android.sdklib.SdkConstants;
22 import com.android.sdklib.internal.repository.RepoSource;
23 import com.android.sdklib.internal.repository.RepoSources;
24 import com.android.sdklib.repository.SdkRepository;
25 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
26 import com.android.sdkuilib.internal.tasks.ProgressTaskFactory;
27 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;
28 
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.custom.SashForm;
31 import org.eclipse.swt.custom.StackLayout;
32 import org.eclipse.swt.events.DisposeEvent;
33 import org.eclipse.swt.events.DisposeListener;
34 import org.eclipse.swt.events.SelectionAdapter;
35 import org.eclipse.swt.events.SelectionEvent;
36 import org.eclipse.swt.graphics.Point;
37 import org.eclipse.swt.layout.FillLayout;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Display;
40 import org.eclipse.swt.widgets.List;
41 import org.eclipse.swt.widgets.Shell;
42 
43 import java.lang.reflect.Constructor;
44 import java.util.ArrayList;
45 
46 /**
47  * This is the private implementation of the UpdateWindow.
48  */
49 public class UpdaterWindowImpl {
50 
51     private final Shell mParentShell;
52     /** Internal data shared between the window and its pages. */
53     private final UpdaterData mUpdaterData;
54     /** The array of pages instances. Only one is visible at a time. */
55     private ArrayList<Composite> mPages = new ArrayList<Composite>();
56     /** Indicates a page change is due to an internal request. Prevents callbacks from looping. */
57     private boolean mInternalPageChange;
58     /** A list of extra pages to instantiate. Each entry is an object array with 2 elements:
59      *  the string title and the Composite class to instantiate to create the page. */
60     private ArrayList<Object[]> mExtraPages;
61     /** A factory to create progress task dialogs. */
62     private ProgressTaskFactory mTaskFactory;
63     /** The initial page to display. If null or not a know class, the first page will be displayed.
64      * Must be set before the first call to {@link #open()}. */
65     private Class<? extends Composite> mInitialPage;
66     /** Sets whether the auto-update wizard will be shown when opening the window. */
67     private boolean mRequestAutoUpdate;
68 
69     // --- UI members ---
70 
71     protected Shell mAndroidSdkUpdater;
72     private SashForm mSashForm;
73     private List mPageList;
74     private Composite mPagesRootComposite;
75     private LocalPackagesPage mLocalPackagePage;
76     private RemotePackagesPage mRemotePackagesPage;
77     private AvdManagerPage mAvdManagerPage;
78     private StackLayout mStackLayout;
79 
UpdaterWindowImpl(Shell parentShell, ISdkLog sdkLog, String osSdkRoot, boolean userCanChangeSdkRoot)80     public UpdaterWindowImpl(Shell parentShell, ISdkLog sdkLog, String osSdkRoot,
81             boolean userCanChangeSdkRoot) {
82         mParentShell = parentShell;
83         mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
84         mUpdaterData.setUserCanChangeSdkRoot(userCanChangeSdkRoot);
85     }
86 
87     /**
88      * Open the window.
89      * @wbp.parser.entryPoint
90      */
open()91     public void open() {
92         if (mParentShell == null) {
93             Display.setAppName("Android"); //$hide$ (hide from SWT designer)
94         }
95 
96         createContents();
97         mAndroidSdkUpdater.open();
98         mAndroidSdkUpdater.layout();
99 
100         postCreate();    //$hide$ (hide from SWT designer)
101 
102         Display display = Display.getDefault();
103         while (!mAndroidSdkUpdater.isDisposed()) {
104             if (!display.readAndDispatch()) {
105                 display.sleep();
106             }
107         }
108 
109         dispose();  //$hide$
110     }
111 
112     /**
113      * Create contents of the window.
114      */
createContents()115     protected void createContents() {
116         mAndroidSdkUpdater = new Shell(mParentShell, SWT.SHELL_TRIM);
117         mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {
118             public void widgetDisposed(DisposeEvent e) {
119                 onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)
120             }
121         });
122 
123         FillLayout fl;
124         mAndroidSdkUpdater.setLayout(fl = new FillLayout(SWT.HORIZONTAL));
125         fl.marginHeight = fl.marginWidth = 5;
126         mAndroidSdkUpdater.setMinimumSize(new Point(200, 50));
127         mAndroidSdkUpdater.setSize(745, 433);
128         mAndroidSdkUpdater.setText("Android SDK and AVD Manager");
129 
130         mSashForm = new SashForm(mAndroidSdkUpdater, SWT.NONE);
131 
132         mPageList = new List(mSashForm, SWT.BORDER);
133         mPageList.addSelectionListener(new SelectionAdapter() {
134             @Override
135             public void widgetSelected(SelectionEvent e) {
136                 onPageListSelected();    //$hide$ (hide from SWT designer)
137             }
138         });
139 
140         mPagesRootComposite = new Composite(mSashForm, SWT.NONE);
141         mStackLayout = new StackLayout();
142         mPagesRootComposite.setLayout(mStackLayout);
143 
144         mAvdManagerPage = new AvdManagerPage(mPagesRootComposite, mUpdaterData);
145         mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);
146         mRemotePackagesPage = new RemotePackagesPage(mPagesRootComposite, mUpdaterData);
147 
148         mSashForm.setWeights(new int[] {150, 576});
149     }
150 
151     // -- Start of internal part ----------
152     // Hide everything down-below from SWT designer
153     //$hide>>$
154 
155     // --- Public API -----------
156 
157 
158     /**
159      * Registers an extra page for the updater window.
160      * <p/>
161      * Pages must derive from {@link Composite} and implement a constructor that takes
162      * a single parent {@link Composite} argument.
163      * <p/>
164      * All pages must be registered before the call to {@link #open()}.
165      *
166      * @param title The title of the page.
167      * @param pageClass The {@link Composite}-derived class that will implement the page.
168      */
registerExtraPage(String title, Class<? extends Composite> pageClass)169     public void registerExtraPage(String title, Class<? extends Composite> pageClass) {
170         if (mExtraPages == null) {
171             mExtraPages = new ArrayList<Object[]>();
172         }
173         mExtraPages.add(new Object[]{ title, pageClass });
174     }
175 
176     /**
177      * Indicate the initial page that should be selected when the window opens.
178      * This must be called before the call to {@link #open()}.
179      * If null or if the page class is not found, the first page will be selected.
180      */
setInitialPage(Class<? extends Composite> pageClass)181     public void setInitialPage(Class<? extends Composite> pageClass) {
182         mInitialPage = pageClass;
183     }
184 
185     /**
186      * Sets whether the auto-update wizard will be shown when opening the window.
187      * <p/>
188      * This must be called before the call to {@link #open()}.
189      */
setRequestAutoUpdate(boolean requestAutoUpdate)190     public void setRequestAutoUpdate(boolean requestAutoUpdate) {
191         mRequestAutoUpdate = requestAutoUpdate;
192     }
193 
194     /**
195      * Adds a new listener to be notified when a change is made to the content of the SDK.
196      */
addListeners(ISdkListener listener)197     public void addListeners(ISdkListener listener) {
198         mUpdaterData.addListeners(listener);
199     }
200 
201     /**
202      * Removes a new listener to be notified anymore when a change is made to the content of
203      * the SDK.
204      */
removeListener(ISdkListener listener)205     public void removeListener(ISdkListener listener) {
206         mUpdaterData.removeListener(listener);
207     }
208 
209     // --- Internals & UI Callbacks -----------
210 
211 
212     /**
213      * Helper to return the SWT shell.
214      */
getShell()215     private Shell getShell() {
216         return mAndroidSdkUpdater;
217     }
218 
219     /**
220      * Callback called when the window shell is disposed.
221      */
onAndroidSdkUpdaterDispose()222     private void onAndroidSdkUpdaterDispose() {
223         if (mUpdaterData != null) {
224             ImageFactory imgFactory = mUpdaterData.getImageFactory();
225             if (imgFactory != null) {
226                 imgFactory.dispose();
227             }
228         }
229     }
230 
231     /**
232      * Creates the icon of the window shell.
233      */
setWindowImage(Shell androidSdkUpdater)234     private void setWindowImage(Shell androidSdkUpdater) {
235         String imageName = "android_icon_16.png"; //$NON-NLS-1$
236         if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
237             imageName = "android_icon_128.png"; //$NON-NLS-1$
238         }
239 
240         if (mUpdaterData != null) {
241             ImageFactory imgFactory = mUpdaterData.getImageFactory();
242             if (imgFactory != null) {
243                 mAndroidSdkUpdater.setImage(imgFactory.getImageByName(imageName));
244             }
245         }
246     }
247 
248     /**
249      * Once the UI has been created, initializes the content.
250      * This creates the pages, selects the first one, setup sources and scan for local folders.
251      */
postCreate()252     private void postCreate() {
253         mUpdaterData.setWindowShell(getShell());
254         mTaskFactory = new ProgressTaskFactory(getShell());
255         mUpdaterData.setTaskFactory(mTaskFactory);
256         mUpdaterData.setImageFactory(new ImageFactory(getShell().getDisplay()));
257 
258         setWindowImage(mAndroidSdkUpdater);
259 
260         addPage(mAvdManagerPage,     "Virtual Devices");
261         addPage(mLocalPackagePage,   "Installed Packages");
262         addPage(mRemotePackagesPage, "Available Packages");
263         addExtraPages();
264 
265         int pageIndex = 0;
266         int i = 0;
267         for (Composite p : mPages) {
268             if (p.getClass().equals(mInitialPage)) {
269                 pageIndex = i;
270                 break;
271             }
272             i++;
273         }
274         displayPage(pageIndex);
275         mPageList.setSelection(pageIndex);
276 
277         setupSources();
278         initializeSettings();
279         mUpdaterData.notifyListeners();
280 
281         if (mRequestAutoUpdate) {
282             mUpdaterData.updateOrInstallAll(null /*selectedArchives*/);
283         }
284     }
285 
286     /**
287      * Called by the main loop when the window has been disposed.
288      */
dispose()289     private void dispose() {
290         mUpdaterData.getSources().saveUserSources(mUpdaterData.getSdkLog());
291     }
292 
293     // --- page switching ---
294 
295     /**
296      * Adds an instance of a page to the page list.
297      * <p/>
298      * Each page is a {@link Composite}. The title of the page is stored in the
299      * {@link Composite#getData()} field.
300      */
addPage(Composite page, String title)301     private void addPage(Composite page, String title) {
302         page.setData(title);
303         mPages.add(page);
304         mPageList.add(title);
305     }
306 
307     /**
308      * Adds all extra pages. For each page, instantiates an instance of the {@link Composite}
309      * using the constructor that takes a single {@link Composite} argument and then adds it
310      * to the page list.
311      */
312     @SuppressWarnings("unchecked")
addExtraPages()313     private void addExtraPages() {
314         if (mExtraPages == null) {
315             return;
316         }
317 
318         for (Object[] extraPage : mExtraPages) {
319             String title = (String) extraPage[0];
320             Class<? extends Composite> clazz = (Class<? extends Composite>) extraPage[1];
321 
322             // We want the constructor that takes a single Composite as parameter
323             Constructor<? extends Composite> cons;
324             try {
325                 cons = clazz.getConstructor(new Class<?>[] { Composite.class });
326                 Composite instance = cons.newInstance(new Object[] { mPagesRootComposite });
327                 addPage(instance, title);
328 
329             } catch (NoSuchMethodException e) {
330                 // There is no such constructor.
331                 mUpdaterData.getSdkLog().error(e,
332                         "Failed to add extra page %1$s. Constructor args must be (Composite parent).",  //$NON-NLS-1$
333                         clazz.getSimpleName());
334 
335             } catch (Exception e) {
336                 // Log this instead of crashing the whole app.
337                 mUpdaterData.getSdkLog().error(e,
338                         "Failed to add extra page %1$s.",  //$NON-NLS-1$
339                         clazz.getSimpleName());
340             }
341         }
342     }
343 
344     /**
345      * Callback invoked when an item is selected in the page list.
346      * If this is not an internal page change, displays the given page.
347      */
onPageListSelected()348     private void onPageListSelected() {
349         if (mInternalPageChange == false) {
350             int index = mPageList.getSelectionIndex();
351             if (index >= 0) {
352                 displayPage(index);
353             }
354         }
355     }
356 
357     /**
358      * Displays the page at the given index.
359      *
360      * @param index An index between 0 and {@link #mPages}'s length - 1.
361      */
displayPage(int index)362     private void displayPage(int index) {
363         Composite page = mPages.get(index);
364         if (page != null) {
365             mStackLayout.topControl = page;
366             mPagesRootComposite.layout(true);
367 
368             if (!mInternalPageChange) {
369                 mInternalPageChange = true;
370                 mPageList.setSelection(index);
371                 mInternalPageChange = false;
372             }
373         }
374     }
375 
376     /**
377      * Used to initialize the sources.
378      */
setupSources()379     private void setupSources() {
380         RepoSources sources = mUpdaterData.getSources();
381         sources.add(new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /*userSource*/));
382 
383         // SDK_UPDATER_URLS is a semicolon-separated list of URLs that can be used to
384         // seed the SDK Updater list for full repositories.
385         String str = System.getenv("SDK_UPDATER_URLS");
386         if (str != null) {
387             String[] urls = str.split(";");
388             for (String url : urls) {
389                 if (url != null && url.length() > 0) {
390                     RepoSource s = new RepoSource(url, false /*userSource*/);
391                     if (!sources.hasSource(s)) {
392                         sources.add(s);
393                     }
394                 }
395             }
396         }
397 
398         // Load user sources
399         sources.loadUserSources(mUpdaterData.getSdkLog());
400 
401         // SDK_UPDATER_USER_URLS is a semicolon-separated list of URLs that can be used to
402         // seed the SDK Updater list for user-only repositories. User sources can only provide
403         // add-ons and extra packages.
404         str = System.getenv("SDK_UPDATER_USER_URLS");
405         if (str != null) {
406             String[] urls = str.split(";");
407             for (String url : urls) {
408                 if (url != null && url.length() > 0) {
409                     RepoSource s = new RepoSource(url, true /*userSource*/);
410                     if (!sources.hasSource(s)) {
411                         sources.add(s);
412                     }
413                 }
414             }
415         }
416 
417         mRemotePackagesPage.onSdkChange();
418     }
419 
420     /**
421      * Initializes settings.
422      * This must be called after addExtraPages(), which created a settings page.
423      * Iterate through all the pages to find the first (and supposedly unique) setting page,
424      * and use it to load and apply these settings.
425      */
initializeSettings()426     private void initializeSettings() {
427         SettingsController c = mUpdaterData.getSettingsController();
428         c.loadSettings();
429         c.applySettings();
430 
431         for (Object page : mPages) {
432             if (page instanceof ISettingsPage) {
433                 ISettingsPage settingsPage = (ISettingsPage) page;
434 
435                 c.setSettingsPage(settingsPage);
436                 break;
437             }
438         }
439     }
440 
441     // End of hiding from SWT Designer
442     //$hide<<$
443 }
444