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