From 36e11cb354f375f462ddfbfee2f764e4d9f89f99 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 1 Jun 2009 15:45:33 -0700 Subject: [PATCH] SDK Updater: Placeholder pages for about and settings. These pages are now in the SdkManager project with a mechanism to instantiate them from the UI on demand. --- .../app/src/com/android/sdkmanager/Main.java | 5 + .../internal/repository/AboutPage.java | 72 +++++++++++ .../internal/repository/SettingsPage.java | 109 ++++++++++++++++ .../repository/LocalPackagesPage.java | 48 +++++-- .../internal/repository/UpdaterData.java | 9 ++ .../repository/UpdaterWindowImpl.java | 121 ++++++++++++++++-- .../sdkuilib/repository/UpdaterWindow.java | 23 +++- 7 files changed, 368 insertions(+), 19 deletions(-) create mode 100755 tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java create mode 100755 tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java index 94f30628c..26af49824 100644 --- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -29,6 +29,8 @@ import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty; import com.android.sdklib.internal.project.ProjectCreator; import com.android.sdklib.internal.project.ProjectCreator.OutputLevel; +import com.android.sdkmanager.internal.repository.AboutPage; +import com.android.sdkmanager.internal.repository.SettingsPage; import com.android.sdkuilib.repository.UpdaterWindow; import java.io.File; @@ -238,8 +240,11 @@ class Main { private void showMainWindow() { try { UpdaterWindow window = new UpdaterWindow( + mSdkLog, mOsSdkFolder, false /*userCanChangeSdkRoot*/); + window.registerPage("Settings", SettingsPage.class); + window.registerPage("About", AboutPage.class); window.open(); } catch (Exception e) { e.printStackTrace(); diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java new file mode 100755 index 000000000..49aad2992 --- /dev/null +++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkmanager.internal.repository; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +/* + * TODO list + * - Change version to be a constant pulled from somewhere. + */ + +public class AboutPage extends Composite { + + private Label mLabel; + + /** + * Create the composite. + * @param parent The parent of the composite. + */ + public AboutPage(Composite parent) { + super(parent, SWT.BORDER); + + createContents(this); + + postCreate(); //$hide$ + } + + private void createContents(Composite parent) { + parent.setLayout(new GridLayout(1, false)); + + mLabel = new Label(parent, SWT.NONE); + mLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true, 1, 1)); + mLabel.setText("Android SDK Updater.\n\nVersion 0.1.\n\nCopyright (C) 2009 The Android Open Source Project."); + } + + @Override + protected void checkSubclass() { + // Disable the check that prevents subclassing of SWT components + } + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + /** + * Called by the constructor right after {@link #createContents(Composite)}. + */ + private void postCreate() { + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java new file mode 100755 index 000000000..df72cf92b --- /dev/null +++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkmanager.internal.repository; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/* + * TODO list + * - The window should probably set a callback to be notified when settings are changed. + * - Actually use the settings. + */ + +public class SettingsPage extends Composite { + + private Group mProxySettingsGroup; + private Group mPlaceholderGroup; + private Button mApplyButton; + private Label mSomeMoreSettings; + private Label mProxyServerLabel; + private Label mProxyPortLabel; + private Text mProxyServerText; + private Text mProxyPortText; + + /** + * Create the composite. + * @param parent The parent of the composite. + */ + public SettingsPage(Composite parent) { + super(parent, SWT.BORDER); + + createContents(this); + + mProxySettingsGroup = new Group(this, SWT.NONE); + mProxySettingsGroup.setText("Proxy Settings"); + mProxySettingsGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + mProxySettingsGroup.setLayout(new GridLayout(2, false)); + + mProxyServerLabel = new Label(mProxySettingsGroup, SWT.NONE); + mProxyServerLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mProxyServerLabel.setText("HTTP Proxy Server"); + + mProxyServerText = new Text(mProxySettingsGroup, SWT.BORDER); + mProxyServerText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + mProxyPortLabel = new Label(mProxySettingsGroup, SWT.NONE); + mProxyPortLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mProxyPortLabel.setText("HTTP Proxy Port"); + + mProxyPortText = new Text(mProxySettingsGroup, SWT.BORDER); + mProxyPortText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + mPlaceholderGroup = new Group(this, SWT.NONE); + mPlaceholderGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + mPlaceholderGroup.setText("Placeholder"); + mPlaceholderGroup.setLayout(new GridLayout(1, false)); + + mSomeMoreSettings = new Label(mPlaceholderGroup, SWT.NONE); + mSomeMoreSettings.setText("Some more settings here"); + + mApplyButton = new Button(this, SWT.NONE); + mApplyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mApplyButton.setText("Apply"); + + postCreate(); //$hide$ + } + + private void createContents(Composite parent) { + parent.setLayout(new GridLayout(1, false)); + } + + @Override + protected void checkSubclass() { + // Disable the check that prevents subclassing of SWT components + } + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + /** + * Called by the constructor right after {@link #createContents(Composite)}. + */ + private void postCreate() { + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java index 5bb9e8a38..3f32ddc18 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java @@ -16,9 +16,12 @@ package com.android.sdkuilib.internal.repository; +import com.android.sdklib.internal.repository.IDescription; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; @@ -38,8 +41,6 @@ import org.eclipse.swt.widgets.Text; /* * TODO list - * - parse local repo - * - create entries * - select => update desc, enable update + delete, enable home page if url * - home page callback * - update callback @@ -89,6 +90,12 @@ public class LocalPackagesPage extends Composite { mTablePackages = mTableViewerPackages.getTable(); mTablePackages.setHeaderVisible(true); mTablePackages.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1)); + mTablePackages.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onTreeSelected(); //$hide$ + } + }); mColumnPackages = new TableColumn(mTablePackages, SWT.NONE); mColumnPackages.setWidth(377); @@ -164,11 +171,23 @@ public class LocalPackagesPage extends Composite { // Hide everything down-below from SWT designer //$hide>>$ + /** + * Must be called once to set the adapter input for the package table viewer. + */ + public void setInput(LocalSdkAdapter localSdkAdapter) { + mTableViewerPackages.setLabelProvider( localSdkAdapter.getLabelProvider()); + mTableViewerPackages.setContentProvider(localSdkAdapter.getContentProvider()); + mTableViewerPackages.setInput(localSdkAdapter); + onTreeSelected(); + } + + /** + * Called by the constructor right after {@link #createContents(Composite)}. + */ private void postCreate() { adjustColumnsWidth(); } - /** * Adds a listener to adjust the columns width when the parent is resized. *

@@ -186,13 +205,26 @@ public class LocalPackagesPage extends Composite { }); } - public void setInput(LocalSdkAdapter localSdkAdapter) { - mTableViewerPackages.setLabelProvider( localSdkAdapter.getLabelProvider()); - mTableViewerPackages.setContentProvider(localSdkAdapter.getContentProvider()); - mTableViewerPackages.setInput(localSdkAdapter); + /** + * Called when an item in the package table viewer is selected. + * If the items is an {@link IDescription} (as it should), this will display its long + * description in the description area. Otherwise when the item is not of the expected + * type or there is no selection, it empties the description area. + */ + private void onTreeSelected() { + ISelection sel = mTableViewerPackages.getSelection(); + if (sel instanceof IStructuredSelection) { + Object elem = ((IStructuredSelection) sel).getFirstElement(); + if (elem instanceof IDescription) { + mDescriptionLabel.setText(((IDescription) elem).getLongDescription()); + mDescriptionContainer.layout(true); + return; + } + } + mDescriptionLabel.setText(""); //$NON-NLS1-$ } - protected void onUpdateInstalledPackage() { + private void onUpdateInstalledPackage() { // TODO just a test, needs to be removed later. ProgressTask.start(getShell(), "Test", new ITask() { public void run(ITaskMonitor monitor) { diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java index 384ff5717..47ce3acc9 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java @@ -16,6 +16,7 @@ package com.android.sdkuilib.internal.repository; +import com.android.sdklib.ISdkLog; import com.android.sdklib.internal.repository.LocalSdkParser; import com.android.sdklib.internal.repository.RepoSources; @@ -23,6 +24,7 @@ import com.android.sdklib.internal.repository.RepoSources; * Data shared between {@link UpdaterWindowImpl} and its pages. */ class UpdaterData { + private ISdkLog mSdkLog; private String mOsSdkRoot; private boolean mUserCanChangeSdkRoot; @@ -64,4 +66,11 @@ class UpdaterData { return mLocalSdkAdapter; } + public void setSdkLog(ISdkLog sdkLog) { + mSdkLog = sdkLog; + } + + public ISdkLog getSdkLog() { + return mSdkLog; + } } diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java index 2a3ff9039..8ab37bb29 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java @@ -17,6 +17,7 @@ package com.android.sdkuilib.internal.repository; +import com.android.sdklib.ISdkLog; import com.android.sdklib.internal.repository.Archive; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; @@ -45,6 +46,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Constructor; import java.net.URL; import java.security.MessageDigest; import java.util.ArrayList; @@ -57,9 +59,18 @@ public class UpdaterWindowImpl { private static final int NUM_FETCH_URL_MONITOR_INC = 10; + /** Internal data shared between the window and its pages. */ private final UpdaterData mUpdaterData = new UpdaterData(); + /** The array of pages instances. Only one is visible at a time. */ private ArrayList mPages = new ArrayList(); + /** Indicates a page change is due to an internal request. Prevents callbacks from looping. */ private boolean mInternalPageChange; + /** A list of extra pages to instantiate. Each entry is an object array with 2 elements: + * the string title and the Composite class to instantiate to create the page. */ + private ArrayList mExtraPages; + /** A factory to create progress task dialogs. */ + private ProgressTaskFactory mTaskFactory; + // --- UI members --- @@ -71,9 +82,9 @@ public class UpdaterWindowImpl { private RemotePackagesPage mRemotePackagesPage; private StackLayout mStackLayout; private Image mIconImage; - private ProgressTaskFactory mTaskFactory; - public UpdaterWindowImpl(String osSdkRoot, boolean userCanChangeSdkRoot) { + public UpdaterWindowImpl(ISdkLog sdkLog, String osSdkRoot, boolean userCanChangeSdkRoot) { + mUpdaterData.setSdkLog(sdkLog); mUpdaterData.setOsSdkRoot(osSdkRoot); mUpdaterData.setUserCanChangeSdkRoot(userCanChangeSdkRoot); } @@ -140,6 +151,35 @@ public class UpdaterWindowImpl { // --- UI Callbacks ----------- + + /** + * Registers an extra page for the updater window. + *

+ * Pages must derive from {@link Composite} and implement a constructor that takes + * a single parent {@link Composite} argument. + *

+ * All pages must be registered before the call to {@link #open()}. + * + * @param title The title of the page. + * @param pageClass The {@link Composite}-derived class that will implement the page. + */ + public void registerExtraPage(String title, Class pageClass) { + if (mExtraPages == null) { + mExtraPages = new ArrayList(); + } + mExtraPages.add(new Object[]{ title, pageClass }); + } + + /** + * Helper to return the SWT shell. + */ + private Shell getShell() { + return mAndroidSdkUpdater; + } + + /** + * Callback called when the window shell is disposed. + */ private void onAndroidSdkUpdaterDispose() { if (mIconImage != null) { mIconImage.dispose(); @@ -147,6 +187,10 @@ public class UpdaterWindowImpl { } } + /** + * Creates the icon of the window shell. + * The icon is disposed by {@link #onAndroidSdkUpdaterDispose()}. + */ private void setWindowImage(Shell androidSdkUpdater) { InputStream stream = getClass().getResourceAsStream("android_icon_16.png"); //$NON-NLS-1$ if (stream != null) { @@ -157,40 +201,84 @@ public class UpdaterWindowImpl { imgData.getTransparencyMask()); mAndroidSdkUpdater.setImage(mIconImage); } catch (SWTException e) { - // ignore + mUpdaterData.getSdkLog().error(e, "Failed to set window icon"); //$NON-NLS-1$ } catch (IllegalArgumentException e) { - // ignore + mUpdaterData.getSdkLog().error(e, "Failed to set window icon"); //$NON-NLS-1$ } } } - private Shell getShell() { - return mAndroidSdkUpdater; - } - /** - * Once the UI has been created, initialize the content + * Once the UI has been created, initializes the content. + * This creates the pages, selects the first one, setup sources and scan for local folders. */ private void firstInit() { mTaskFactory = new ProgressTaskFactory(getShell()); addPage(mLocalPackagePage, "Installed Packages"); addPage(mRemotePackagesPage, "Available Packages"); + addExtraPages(); + displayPage(0); mPageList.setSelection(0); + // TODO read and apply settings + // TODO read add-on sources from some file setupSources(); scanLocalSdkFolders(); } // --- page switching --- + /** + * Adds an instance of a page to the page list. + *

+ * Each page is a {@link Composite}. The title of the page is stored in the + * {@link Composite#getData()} field. + */ private void addPage(Composite page, String title) { page.setData(title); mPages.add(page); mPageList.add(title); } + /** + * Adds all extra pages. For each page, instantiates an instance of the {@link Composite} + * using the constructor that takes a single {@link Composite} argument and then adds it + * to the page list. + */ + @SuppressWarnings("unchecked") + private void addExtraPages() { + for (Object[] extraPage : mExtraPages) { + String title = (String) extraPage[0]; + Class clazz = (Class) extraPage[1]; + + // We want the constructor that takes a single Composite as parameter + Constructor cons; + try { + cons = clazz.getConstructor(new Class[] { Composite.class }); + Composite instance = cons.newInstance(new Object[] { mPagesRootComposite }); + addPage(instance, title); + + } catch (NoSuchMethodException e) { + // There is no such constructor. + mUpdaterData.getSdkLog().error(e, + "Failed to add extra page %1$s. Constructor args must be (Composite parent).", //$NON-NLS-1$ + clazz.getSimpleName()); + + } catch (Exception e) { + // Log this instead of crashing the whole app. + mUpdaterData.getSdkLog().error(e, + "Failed to add extra page %1$s.", //$NON-NLS-1$ + clazz.getSimpleName()); + } + } + } + + /** + * Callback invoked when an item is selected in the page list. + * If this is not an internal page change, displays the given page. + */ private void onPageListSelected() { if (mInternalPageChange == false) { int index = mPageList.getSelectionIndex(); @@ -200,6 +288,11 @@ public class UpdaterWindowImpl { } } + /** + * Displays the page at the given index. + * + * @param index An index between 0 and {@link #mPages}'s length - 1. + */ private void displayPage(int index) { Composite page = mPages.get(index); if (page != null) { @@ -214,6 +307,9 @@ public class UpdaterWindowImpl { } } + /** + * Used to initialize the sources. + */ private void setupSources() { mUpdaterData.getSources().setTaskFactory(mTaskFactory); @@ -228,12 +324,19 @@ public class UpdaterWindowImpl { mRemotePackagesPage.setInput(mUpdaterData.getSourcesAdapter()); } + /** + * Used to scan the local SDK folders the first time. + */ private void scanLocalSdkFolders() { mUpdaterData.getLocalSdkAdapter().setSdkRoot(mUpdaterData.getOsSdkRoot()); mLocalPackagePage.setInput(mUpdaterData.getLocalSdkAdapter()); } + /** + * Install the list of given {@link Archive}s. + * @param archives The archives to install. Incompatible ones will be skipped. + */ public void installArchives(final Collection archives) { // TODO move most parts to SdkLib, maybe as part of Archive, making archives self-installing. mTaskFactory.start("Installing Archives", new ITask() { diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java index 0d067aec1..8ca332413 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java @@ -16,8 +16,11 @@ package com.android.sdkuilib.repository; +import com.android.sdklib.ISdkLog; import com.android.sdkuilib.internal.repository.UpdaterWindowImpl; +import org.eclipse.swt.widgets.Composite; + /** * Opens an SDK Updater Window. * @@ -29,12 +32,28 @@ public class UpdaterWindow { /** * Creates a new window. Caller must call open(), which will block. + * @param sdkLog * @param osSdkRoot The OS path to the SDK root. * @param userCanChangeSdkRoot If true, the window lets the user change the SDK path * being browsed. */ - public UpdaterWindow(String osSdkRoot, boolean userCanChangeSdkRoot) { - mWindow = new UpdaterWindowImpl(osSdkRoot, userCanChangeSdkRoot); + public UpdaterWindow(ISdkLog sdkLog, String osSdkRoot, boolean userCanChangeSdkRoot) { + mWindow = new UpdaterWindowImpl(sdkLog, osSdkRoot, userCanChangeSdkRoot); + } + + /** + * Registers an extra page for the updater window. + *

+ * Pages must derive from {@link Composite} and implement a constructor that takes + * a single parent {@link Composite} argument. + *

+ * All pages must be registered before the call to {@link #open()}. + * + * @param title The title of the page. + * @param pageClass The {@link Composite}-derived class that will implement the page. + */ + public void registerPage(String title, Class pageClass) { + mWindow.registerExtraPage(title, pageClass); } /**