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