2 * Copyright (C) 2009 The Android Open Source Project
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
17 package com.android.sdkuilib.internal.repository;
\r
20 import com.android.sdklib.ISdkLog;
\r
21 import com.android.sdklib.SdkConstants;
\r
22 import com.android.sdklib.internal.repository.RepoSource;
\r
23 import com.android.sdklib.internal.repository.RepoSources;
\r
24 import com.android.sdklib.repository.SdkRepository;
\r
25 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
\r
26 import com.android.sdkuilib.internal.tasks.ProgressTaskFactory;
\r
27 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;
\r
29 import org.eclipse.swt.SWT;
\r
30 import org.eclipse.swt.custom.SashForm;
\r
31 import org.eclipse.swt.custom.StackLayout;
\r
32 import org.eclipse.swt.events.DisposeEvent;
\r
33 import org.eclipse.swt.events.DisposeListener;
\r
34 import org.eclipse.swt.events.SelectionAdapter;
\r
35 import org.eclipse.swt.events.SelectionEvent;
\r
36 import org.eclipse.swt.graphics.Point;
\r
37 import org.eclipse.swt.layout.FillLayout;
\r
38 import org.eclipse.swt.widgets.Composite;
\r
39 import org.eclipse.swt.widgets.Display;
\r
40 import org.eclipse.swt.widgets.List;
\r
41 import org.eclipse.swt.widgets.Shell;
\r
43 import java.lang.reflect.Constructor;
\r
44 import java.util.ArrayList;
\r
47 * This is the private implementation of the UpdateWindow.
\r
49 public class UpdaterWindowImpl {
\r
51 private final Shell mParentShell;
\r
52 /** Internal data shared between the window and its pages. */
\r
53 private final UpdaterData mUpdaterData;
\r
54 /** The array of pages instances. Only one is visible at a time. */
\r
55 private ArrayList<Composite> mPages = new ArrayList<Composite>();
\r
56 /** Indicates a page change is due to an internal request. Prevents callbacks from looping. */
\r
57 private boolean mInternalPageChange;
\r
58 /** A list of extra pages to instantiate. Each entry is an object array with 2 elements:
\r
59 * the string title and the Composite class to instantiate to create the page. */
\r
60 private ArrayList<Object[]> mExtraPages;
\r
61 /** A factory to create progress task dialogs. */
\r
62 private ProgressTaskFactory mTaskFactory;
\r
63 /** The initial page to display. If null or not a know class, the first page will be displayed.
\r
64 * Must be set before the first call to {@link #open()}. */
\r
65 private Class<? extends Composite> mInitialPage;
\r
66 /** Sets whether the auto-update wizard will be shown when opening the window. */
\r
67 private boolean mRequestAutoUpdate;
\r
69 // --- UI members ---
\r
71 protected Shell mAndroidSdkUpdater;
\r
72 private SashForm mSashForm;
\r
73 private List mPageList;
\r
74 private Composite mPagesRootComposite;
\r
75 private LocalPackagesPage mLocalPackagePage;
\r
76 private RemotePackagesPage mRemotePackagesPage;
\r
77 private AvdManagerPage mAvdManagerPage;
\r
78 private StackLayout mStackLayout;
\r
81 * Creates a new window. Caller must call open(), which will block.
\r
83 * @param parentShell Parent shell.
\r
84 * @param sdkLog Logger. Cannot be null.
\r
85 * @param osSdkRoot The OS path to the SDK root.
\r
86 * @param userCanChangeSdkRoot If true, the window lets the user change the SDK path
\r
89 public UpdaterWindowImpl(Shell parentShell, ISdkLog sdkLog, String osSdkRoot,
\r
90 boolean userCanChangeSdkRoot) {
\r
91 mParentShell = parentShell;
\r
92 mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
\r
93 mUpdaterData.setUserCanChangeSdkRoot(userCanChangeSdkRoot);
\r
98 * @wbp.parser.entryPoint
\r
100 public void open() {
\r
101 if (mParentShell == null) {
\r
102 Display.setAppName("Android"); //$hide$ (hide from SWT designer)
\r
106 mAndroidSdkUpdater.open();
\r
107 mAndroidSdkUpdater.layout();
\r
109 if (postCreate()) { //$hide$ (hide from SWT designer)
\r
110 Display display = Display.getDefault();
\r
111 while (!mAndroidSdkUpdater.isDisposed()) {
\r
112 if (!display.readAndDispatch()) {
\r
118 dispose(); //$hide$
\r
122 * Create contents of the window.
\r
124 protected void createContents() {
\r
125 mAndroidSdkUpdater = new Shell(mParentShell, SWT.SHELL_TRIM);
\r
126 mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {
\r
127 public void widgetDisposed(DisposeEvent e) {
\r
128 onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)
\r
133 mAndroidSdkUpdater.setLayout(fl = new FillLayout(SWT.HORIZONTAL));
\r
134 fl.marginHeight = fl.marginWidth = 5;
\r
135 mAndroidSdkUpdater.setMinimumSize(new Point(200, 50));
\r
136 mAndroidSdkUpdater.setSize(745, 433);
\r
137 mAndroidSdkUpdater.setText("Android SDK and AVD Manager");
\r
139 mSashForm = new SashForm(mAndroidSdkUpdater, SWT.NONE);
\r
141 mPageList = new List(mSashForm, SWT.BORDER);
\r
142 mPageList.addSelectionListener(new SelectionAdapter() {
\r
144 public void widgetSelected(SelectionEvent e) {
\r
145 onPageListSelected(); //$hide$ (hide from SWT designer)
\r
149 mPagesRootComposite = new Composite(mSashForm, SWT.NONE);
\r
150 mStackLayout = new StackLayout();
\r
151 mPagesRootComposite.setLayout(mStackLayout);
\r
153 mAvdManagerPage = new AvdManagerPage(mPagesRootComposite, mUpdaterData);
\r
154 mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);
\r
155 mRemotePackagesPage = new RemotePackagesPage(mPagesRootComposite, mUpdaterData);
\r
157 mSashForm.setWeights(new int[] {150, 576});
\r
160 // -- Start of internal part ----------
\r
161 // Hide everything down-below from SWT designer
\r
164 // --- Public API -----------
\r
168 * Registers an extra page for the updater window.
\r
170 * Pages must derive from {@link Composite} and implement a constructor that takes
\r
171 * a single parent {@link Composite} argument.
\r
173 * All pages must be registered before the call to {@link #open()}.
\r
175 * @param title The title of the page.
\r
176 * @param pageClass The {@link Composite}-derived class that will implement the page.
\r
178 public void registerExtraPage(String title, Class<? extends Composite> pageClass) {
\r
179 if (mExtraPages == null) {
\r
180 mExtraPages = new ArrayList<Object[]>();
\r
182 mExtraPages.add(new Object[]{ title, pageClass });
\r
186 * Indicate the initial page that should be selected when the window opens.
\r
187 * This must be called before the call to {@link #open()}.
\r
188 * If null or if the page class is not found, the first page will be selected.
\r
190 public void setInitialPage(Class<? extends Composite> pageClass) {
\r
191 mInitialPage = pageClass;
\r
195 * Sets whether the auto-update wizard will be shown when opening the window.
\r
197 * This must be called before the call to {@link #open()}.
\r
199 public void setRequestAutoUpdate(boolean requestAutoUpdate) {
\r
200 mRequestAutoUpdate = requestAutoUpdate;
\r
204 * Adds a new listener to be notified when a change is made to the content of the SDK.
\r
206 public void addListeners(ISdkListener listener) {
\r
207 mUpdaterData.addListeners(listener);
\r
211 * Removes a new listener to be notified anymore when a change is made to the content of
\r
214 public void removeListener(ISdkListener listener) {
\r
215 mUpdaterData.removeListener(listener);
\r
218 // --- Internals & UI Callbacks -----------
\r
222 * Helper to return the SWT shell.
\r
224 private Shell getShell() {
\r
225 return mAndroidSdkUpdater;
\r
229 * Callback called when the window shell is disposed.
\r
231 private void onAndroidSdkUpdaterDispose() {
\r
232 if (mUpdaterData != null) {
\r
233 ImageFactory imgFactory = mUpdaterData.getImageFactory();
\r
234 if (imgFactory != null) {
\r
235 imgFactory.dispose();
\r
241 * Creates the icon of the window shell.
\r
243 private void setWindowImage(Shell androidSdkUpdater) {
\r
244 String imageName = "android_icon_16.png"; //$NON-NLS-1$
\r
245 if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
\r
246 imageName = "android_icon_128.png"; //$NON-NLS-1$
\r
249 if (mUpdaterData != null) {
\r
250 ImageFactory imgFactory = mUpdaterData.getImageFactory();
\r
251 if (imgFactory != null) {
\r
252 mAndroidSdkUpdater.setImage(imgFactory.getImageByName(imageName));
\r
258 * Once the UI has been created, initializes the content.
\r
259 * This creates the pages, selects the first one, setup sources and scan for local folders.
\r
261 * Returns true if we should show the window.
\r
263 private boolean postCreate() {
\r
264 mUpdaterData.setWindowShell(getShell());
\r
265 mTaskFactory = new ProgressTaskFactory(getShell());
\r
266 mUpdaterData.setTaskFactory(mTaskFactory);
\r
267 mUpdaterData.setImageFactory(new ImageFactory(getShell().getDisplay()));
\r
269 setWindowImage(mAndroidSdkUpdater);
\r
271 addPage(mAvdManagerPage, "Virtual Devices");
\r
272 addPage(mLocalPackagePage, "Installed Packages");
\r
273 addPage(mRemotePackagesPage, "Available Packages");
\r
278 for (Composite p : mPages) {
\r
279 if (p.getClass().equals(mInitialPage)) {
\r
285 displayPage(pageIndex);
\r
286 mPageList.setSelection(pageIndex);
\r
289 initializeSettings();
\r
291 if (mUpdaterData.checkIfInitFailed()) {
\r
295 mUpdaterData.notifyListeners(true /*init*/);
\r
297 if (mRequestAutoUpdate) {
\r
298 mUpdaterData.updateOrInstallAll(null /*selectedArchives*/);
\r
305 * Called by the main loop when the window has been disposed.
\r
307 private void dispose() {
\r
308 mUpdaterData.getSources().saveUserSources(mUpdaterData.getSdkLog());
\r
311 // --- page switching ---
\r
314 * Adds an instance of a page to the page list.
\r
316 * Each page is a {@link Composite}. The title of the page is stored in the
\r
317 * {@link Composite#getData()} field.
\r
319 private void addPage(Composite page, String title) {
\r
320 page.setData(title);
\r
322 mPageList.add(title);
\r
326 * Adds all extra pages. For each page, instantiates an instance of the {@link Composite}
\r
327 * using the constructor that takes a single {@link Composite} argument and then adds it
\r
328 * to the page list.
\r
330 @SuppressWarnings("unchecked")
\r
331 private void addExtraPages() {
\r
332 if (mExtraPages == null) {
\r
336 for (Object[] extraPage : mExtraPages) {
\r
337 String title = (String) extraPage[0];
\r
338 Class<? extends Composite> clazz = (Class<? extends Composite>) extraPage[1];
\r
340 // We want the constructor that takes a single Composite as parameter
\r
341 Constructor<? extends Composite> cons;
\r
343 cons = clazz.getConstructor(new Class<?>[] { Composite.class });
\r
344 Composite instance = cons.newInstance(new Object[] { mPagesRootComposite });
\r
345 addPage(instance, title);
\r
347 } catch (NoSuchMethodException e) {
\r
348 // There is no such constructor.
\r
349 mUpdaterData.getSdkLog().error(e,
\r
350 "Failed to add extra page %1$s. Constructor args must be (Composite parent).", //$NON-NLS-1$
\r
351 clazz.getSimpleName());
\r
353 } catch (Exception e) {
\r
354 // Log this instead of crashing the whole app.
\r
355 mUpdaterData.getSdkLog().error(e,
\r
356 "Failed to add extra page %1$s.", //$NON-NLS-1$
\r
357 clazz.getSimpleName());
\r
363 * Callback invoked when an item is selected in the page list.
\r
364 * If this is not an internal page change, displays the given page.
\r
366 private void onPageListSelected() {
\r
367 if (mInternalPageChange == false) {
\r
368 int index = mPageList.getSelectionIndex();
\r
370 displayPage(index);
\r
376 * Displays the page at the given index.
\r
378 * @param index An index between 0 and {@link #mPages}'s length - 1.
\r
380 private void displayPage(int index) {
\r
381 Composite page = mPages.get(index);
\r
382 if (page != null) {
\r
383 mStackLayout.topControl = page;
\r
384 mPagesRootComposite.layout(true);
\r
386 if (!mInternalPageChange) {
\r
387 mInternalPageChange = true;
\r
388 mPageList.setSelection(index);
\r
389 mInternalPageChange = false;
\r
395 * Used to initialize the sources.
\r
397 private void setupSources() {
\r
398 RepoSources sources = mUpdaterData.getSources();
\r
399 sources.add(new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /*userSource*/));
\r
401 // SDK_UPDATER_URLS is a semicolon-separated list of URLs that can be used to
\r
402 // seed the SDK Updater list for full repositories.
\r
403 String str = System.getenv("SDK_UPDATER_URLS");
\r
405 String[] urls = str.split(";");
\r
406 for (String url : urls) {
\r
407 if (url != null && url.length() > 0) {
\r
408 RepoSource s = new RepoSource(url, false /*userSource*/);
\r
409 if (!sources.hasSource(s)) {
\r
416 // Load user sources
\r
417 sources.loadUserSources(mUpdaterData.getSdkLog());
\r
419 // SDK_UPDATER_USER_URLS is a semicolon-separated list of URLs that can be used to
\r
420 // seed the SDK Updater list for user-only repositories. User sources can only provide
\r
421 // add-ons and extra packages.
\r
422 str = System.getenv("SDK_UPDATER_USER_URLS");
\r
424 String[] urls = str.split(";");
\r
425 for (String url : urls) {
\r
426 if (url != null && url.length() > 0) {
\r
427 RepoSource s = new RepoSource(url, true /*userSource*/);
\r
428 if (!sources.hasSource(s)) {
\r
435 mRemotePackagesPage.onSdkChange(false /*init*/);
\r
439 * Initializes settings.
\r
440 * This must be called after addExtraPages(), which created a settings page.
\r
441 * Iterate through all the pages to find the first (and supposedly unique) setting page,
\r
442 * and use it to load and apply these settings.
\r
444 private void initializeSettings() {
\r
445 SettingsController c = mUpdaterData.getSettingsController();
\r
449 for (Object page : mPages) {
\r
450 if (page instanceof ISettingsPage) {
\r
451 ISettingsPage settingsPage = (ISettingsPage) page;
\r
453 c.setSettingsPage(settingsPage);
\r
459 // End of hiding from SWT Designer
\r