diff options
author | Raphael Moll <ralf@android.com> | 2012-03-28 16:43:24 -0700 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2012-04-03 16:00:01 -0700 |
commit | 6462650a787c96cbb371d6aca201af3d613aad24 (patch) | |
tree | d5df3571837f38332db68f8f5b44c21a8fba50fe /sdkmanager/libs | |
parent | da3dd4690065f3bb547c8827e4efc6e24af4c61d (diff) | |
download | sdk-6462650a787c96cbb371d6aca201af3d613aad24.zip sdk-6462650a787c96cbb371d6aca201af3d613aad24.tar.gz sdk-6462650a787c96cbb371d6aca201af3d613aad24.tar.bz2 |
SDK Manager dialog to enable 3rd party addons.
- Change AddonUser dialog: transform current dialog in
2 pages. One is for user to add custom addon site
URLs (like the dialog was doing before, unchanged.)
- Other tab is to select which official addon sites
should be enabled.
- Support enable/disable state for each source.
- Display disabled sources as such in the tree when
in "per-repository" view.
- Persist the enable bit state via local pref file.
- Refactor a few inner classes out of PackagesPage.
Change-Id: Icc8e392d90550e53f1c76dd7aefb31669219973b
Diffstat (limited to 'sdkmanager/libs')
12 files changed, 991 insertions, 380 deletions
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java index 7933602..9a04226 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java @@ -19,6 +19,8 @@ package com.android.sdklib.internal.repository; import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.annotations.VisibleForTesting.Visibility;
+import com.android.prefs.AndroidLocation;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.internal.repository.UrlOpener.CanceledByUserException;
import com.android.sdklib.repository.RepoConstants;
import com.android.sdklib.repository.SdkAddonConstants;
@@ -33,7 +35,10 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException;
import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -41,6 +46,7 @@ import java.net.URL; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -68,6 +74,9 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> { private String mFetchError;
private final String mUiName;
+ private static final Properties sDisabledSourceUrls = new Properties();
+ private static final String SRC_FILENAME = "sites-settings.cfg"; //$NON-NLS-1$
+
/**
* Constructs a new source for the given repository URL.
* @param url The source URL. Cannot be null. If the URL ends with a /, the default
@@ -183,7 +192,8 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> { /**
* Returns the list of known packages found by the last call to load().
- * This is null when the source hasn't been loaded yet.
+ * This is null when the source hasn't been loaded yet -- caller should
+ * then call {@link #load(ITaskMonitor, boolean)} to load the packages.
*/
public Package[] getPackages() {
return mPackages;
@@ -208,6 +218,110 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> { }
/**
+ * Indicates if the source is enabled.
+ * <p/>
+ * A 3rd-party add-on source can be disabled by the user to prevent from loading it.
+ * This loads the persistent state from a settings file when first called.
+ *
+ * @return True if the source is enabled (default is true).
+ */
+ public boolean isEnabled() {
+ synchronized (sDisabledSourceUrls) {
+ if (sDisabledSourceUrls.isEmpty()) {
+ // Load state from persistent file
+
+ FileInputStream fis = null;
+ try {
+ String folder = AndroidLocation.getFolder();
+ File f = new File(folder, SRC_FILENAME);
+ if (f.exists()) {
+ fis = new FileInputStream(f);
+
+ sDisabledSourceUrls.load(fis);
+ }
+ } catch (IOException ignore) {
+ // nop
+ } catch (AndroidLocationException ignore) {
+ // nop
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException ignore) {}
+ }
+ }
+
+ if (sDisabledSourceUrls.isEmpty()) {
+ // Nothing was loaded. Initialize the storage with a version
+ // identified. This isn't currently checked back, but we might
+ // want it later if we decide to change the way this works.
+ // The version key is choosen on purpose to not match any valid URL.
+ sDisabledSourceUrls.setProperty("@version", "1"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ // A URL is enabled if it's not in the disabled list.
+ return sDisabledSourceUrls.getProperty(mUrl) == null;
+ }
+ }
+
+ /**
+ * Changes whether the source is marked as enabled.
+ * <p/>
+ * When <em>changing> the enable state, the current package list is purged
+ * and the next {@code load} will either return an empty list (if disabled) or
+ * the actual package list (if enabled.)
+ * <p/>
+ * This also persistent the change by updating a settings file.
+ *
+ * @param enabled True for the source to be enabled (can be loaded), false otherwise.
+ */
+ public void setEnabled(boolean enabled) {
+ // Comparing using isEnabled() has the voluntary side-effect of also
+ // loading the map from the persistent file the first time.
+ if (enabled != isEnabled()) {
+ // First we clear the current package list, which will force the
+ // next load() to actually set the package list as desired.
+ clearPackages();
+
+ synchronized (sDisabledSourceUrls) {
+ // Change the map
+ if (enabled) {
+ sDisabledSourceUrls.remove(mUrl);
+ } else {
+ // The "disabled" value is not being checked when reloading the map.
+ // We might want to do something with it later if a URL can have
+ // more attributes than just disabled.
+ sDisabledSourceUrls.setProperty(mUrl, "disabled"); //$NON-NLS-1$
+ }
+
+ // Persist it to the file
+ FileOutputStream fos = null;
+ try {
+ String folder = AndroidLocation.getFolder();
+ File f = new File(folder, SRC_FILENAME);
+
+ fos = new FileOutputStream(f);
+
+ sDisabledSourceUrls.store(fos,
+ "## Disabled Sources for Android SDK Manager"); //$NON-NLS-1$
+
+ } catch (AndroidLocationException ignore) {
+ // nop
+ } catch (IOException ignore) {
+ // nop
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ignore) {}
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Returns the short description of the source, if not null.
* Otherwise returns the default Object toString result.
* <p/>
@@ -258,13 +372,24 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> { }
/**
- * Tries to fetch the repository index for the given URL.
+ * Tries to fetch the repository index for the given URL and updates the package list.
+ * When a source is disabled, this create an empty non-null package list.
+ * <p/>
+ * Callers can get the package list using {@link #getPackages()} after this. It will be
+ * null in case of error, in which case {@link #getFetchError()} can be used to an
+ * error message.
*/
public void load(ITaskMonitor monitor, boolean forceHttp) {
+ setDefaultDescription();
monitor.setProgressMax(7);
- setDefaultDescription();
+ if (!isEnabled()) {
+ setPackages(new Package[0]);
+ mDescription += "\nSource is disabled.";
+ monitor.incProgress(7);
+ return;
+ }
String url = mUrl;
if (forceHttp) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java index 22311a7..dc33966 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java @@ -44,11 +44,19 @@ public class SdkSources { private final EnumMap<SdkSourceCategory, ArrayList<SdkSource>> mSources =
new EnumMap<SdkSourceCategory, ArrayList<SdkSource>>(SdkSourceCategory.class);
+ private ArrayList<Runnable> mChangeListeners; // lazily initialized
+
+
public SdkSources() {
}
/**
* Adds a new source to the Sources list.
+ * <p/>
+ * Implementation detail: {@link SdkSources} doesn't invoke {@link #notifyChangeListeners()}
+ * directly. Callers who use {@code add()} are responsible for notifying the listeners once
+ * they are done modifying the sources list. The intent is to notify the listeners only once
+ * at the end, not for every single addition.
*/
public void add(SdkSourceCategory category, SdkSource source) {
synchronized (mSources) {
@@ -64,6 +72,9 @@ public class SdkSources { /**
* Removes a source from the Sources list.
+ * <p/>
+ * Callers who remove entries are responsible for notifying the listeners using
+ * {@link #notifyChangeListeners()} once they are done modifying the sources list.
*/
public void remove(SdkSource source) {
synchronized (mSources) {
@@ -85,6 +96,9 @@ public class SdkSources { /**
* Removes all the sources in the given category.
+ * <p/>
+ * Callers who remove entries are responsible for notifying the listeners using
+ * {@link #notifyChangeListeners()} once they are done modifying the sources list.
*/
public void removeAll(SdkSourceCategory category) {
synchronized (mSources) {
@@ -243,53 +257,63 @@ public class SdkSources { /**
* Loads all user sources. This <em>replaces</em> all existing user sources
* by the ones from the property file.
+ * <p/>
+ * This calls {@link #notifyChangeListeners()} at the end of the operation.
*/
public void loadUserAddons(ISdkLog log) {
-
- // Remove all existing user sources
- removeAll(SdkSourceCategory.USER_ADDONS);
-
- // Load new user sources from property file
- FileInputStream fis = null;
- try {
- String folder = AndroidLocation.getFolder();
- File f = new File(folder, SRC_FILENAME);
- if (f.exists()) {
- fis = new FileInputStream(f);
-
- Properties props = new Properties();
- props.load(fis);
-
- int count = Integer.parseInt(props.getProperty(KEY_COUNT, "0"));
-
- for (int i = 0; i < count; i++) {
- String url = props.getProperty(String.format("%s%02d", KEY_SRC, i)); //$NON-NLS-1$
- if (url != null) {
- SdkSource s = new SdkAddonSource(url, null/*uiName*/);
- if (!hasSourceUrl(s)) {
- add(SdkSourceCategory.USER_ADDONS, s);
+ // Implementation detail: synchronize on the sources list to make sure that
+ // a- the source list doesn't change while we load/save it, and most important
+ // b- to make sure it's not being saved while loaded or the reverse.
+ // In most cases we do these operation from the UI thread so it's not really
+ // that necessary. This is more a protection in case of someone calls this
+ // from a worker thread by mistake.
+ synchronized (mSources) {
+ // Remove all existing user sources
+ removeAll(SdkSourceCategory.USER_ADDONS);
+
+ // Load new user sources from property file
+ FileInputStream fis = null;
+ try {
+ String folder = AndroidLocation.getFolder();
+ File f = new File(folder, SRC_FILENAME);
+ if (f.exists()) {
+ fis = new FileInputStream(f);
+
+ Properties props = new Properties();
+ props.load(fis);
+
+ int count = Integer.parseInt(props.getProperty(KEY_COUNT, "0"));
+
+ for (int i = 0; i < count; i++) {
+ String url = props.getProperty(String.format("%s%02d", KEY_SRC, i)); //$NON-NLS-1$
+ if (url != null) {
+ SdkSource s = new SdkAddonSource(url, null/*uiName*/);
+ if (!hasSourceUrl(s)) {
+ add(SdkSourceCategory.USER_ADDONS, s);
+ }
}
}
}
- }
- } catch (NumberFormatException e) {
- log.error(e, null);
+ } catch (NumberFormatException e) {
+ log.error(e, null);
- } catch (AndroidLocationException e) {
- log.error(e, null);
+ } catch (AndroidLocationException e) {
+ log.error(e, null);
- } catch (IOException e) {
- log.error(e, null);
+ } catch (IOException e) {
+ log.error(e, null);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
}
}
}
+ notifyChangeListeners();
}
/**
@@ -297,38 +321,93 @@ public class SdkSources { * @param log Logger. Cannot be null.
*/
public void saveUserAddons(ISdkLog log) {
- FileOutputStream fos = null;
- try {
- String folder = AndroidLocation.getFolder();
- File f = new File(folder, SRC_FILENAME);
+ // See the implementation detail note in loadUserAddons() about the synchronization.
+ synchronized (mSources) {
+ FileOutputStream fos = null;
+ try {
+ String folder = AndroidLocation.getFolder();
+ File f = new File(folder, SRC_FILENAME);
- fos = new FileOutputStream(f);
+ fos = new FileOutputStream(f);
- Properties props = new Properties();
+ Properties props = new Properties();
- int count = 0;
- for (SdkSource s : getSources(SdkSourceCategory.USER_ADDONS)) {
- props.setProperty(String.format("%s%02d", KEY_SRC, count), s.getUrl()); //$NON-NLS-1$
- count++;
- }
- props.setProperty(KEY_COUNT, Integer.toString(count));
+ int count = 0;
+ for (SdkSource s : getSources(SdkSourceCategory.USER_ADDONS)) {
+ props.setProperty(String.format("%s%02d", KEY_SRC, count), //$NON-NLS-1$
+ s.getUrl());
+ count++;
+ }
+ props.setProperty(KEY_COUNT, Integer.toString(count));
- props.store( fos, "## User Sources for Android tool"); //$NON-NLS-1$
+ props.store( fos, "## User Sources for Android SDK Manager"); //$NON-NLS-1$
- } catch (AndroidLocationException e) {
- log.error(e, null);
+ } catch (AndroidLocationException e) {
+ log.error(e, null);
- } catch (IOException e) {
- log.error(e, null);
+ } catch (IOException e) {
+ log.error(e, null);
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ }
}
}
}
+ }
+
+ /**
+ * Adds a listener that will be notified when the sources list has changed.
+ *
+ * @param changeListener A non-null listener to add. Ignored if already present.
+ * @see SdkSources#notifyChangeListeners()
+ */
+ public void addChangeListener(Runnable changeListener) {
+ assert changeListener != null;
+ if (mChangeListeners == null) {
+ mChangeListeners = new ArrayList<Runnable>();
+ }
+ synchronized (mChangeListeners) {
+ if (changeListener != null && !mChangeListeners.contains(changeListener)) {
+ mChangeListeners.add(changeListener);
+ }
+ }
+ }
+
+ /**
+ * Removes a listener from the list of listeners to notify when the sources change.
+ *
+ * @param changeListener A listener to remove. Ignored if not previously added.
+ */
+ public void removeChangeListener(Runnable changeListener) {
+ if (mChangeListeners != null && changeListener != null) {
+ synchronized (mChangeListeners) {
+ mChangeListeners.remove(changeListener);
+ }
+ }
+ }
+ /**
+ * Invoke all the registered change listeners, if any.
+ * <p/>
+ * This <em>may</em> be called from a worker thread, in which case the runnable
+ * should take care of only updating UI from a main thread.
+ */
+ public void notifyChangeListeners() {
+ if (mChangeListeners == null) {
+ return;
+ }
+ synchronized (mChangeListeners) {
+ for (Runnable runnable : mChangeListeners) {
+ try {
+ runnable.run();
+ } catch (Throwable ignore) {
+ assert ignore == null : "A SdkSource.ChangeListener failed with an exception.";
+ }
+ }
+ }
}
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java index 4f39b71..dd51a59 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java @@ -328,7 +328,8 @@ public class UpdaterData implements IUpdaterData { new SdkRepoSource(baseUrl,
SdkSourceCategory.ANDROID_REPO.getUiName()));
- // Load user sources
+ // Load user sources (this will also notify change listeners but this operation is
+ // done early enough that there shouldn't be any anyway.)
sources.loadUserAddons(getSdkLog());
}
@@ -1063,6 +1064,8 @@ public class UpdaterData implements IUpdaterData { }
}
+ mSources.notifyChangeListeners();
+
mStateFetchRemoteAddonsList = 1;
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java index fa2e360..1d58541 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java @@ -20,6 +20,7 @@ import com.android.sdklib.internal.repository.Archive; import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.SdkSource;
import com.android.sdklib.internal.repository.SdkSourceCategory;
+import com.android.sdkuilib.internal.repository.sdkman2.PkgContentProvider;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Image;
@@ -108,17 +109,16 @@ public class ImageFactory { }
if (object instanceof SdkSourceCategory) {
- return getImageByName("source_cat_icon16.png"); //$NON-NLS-1$
+ return getImageByName("source_cat_icon_16.png"); //$NON-NLS-1$
} else if (object instanceof SdkSource) {
- return getImageByName("source_icon16.png"); //$NON-NLS-1$
-
- // TODO reintroduce this in SDK Manager 2 in repository view
- // } else if (object instanceof RepoSourcesAdapter.RepoSourceError) {
- // return getImageByName("error_icon16.png"); //$NON-NLS-1$
- //
- // } else if (object instanceof RepoSourcesAdapter.RepoSourceEmpty) {
- // return getImageByName("nopkg_icon16.png"); //$NON-NLS-1$
+ return getImageByName("source_icon_16.png"); //$NON-NLS-1$
+
+ } else if (object instanceof PkgContentProvider.RepoSourceError) {
+ return getImageByName("error_icon_16.png"); //$NON-NLS-1$
+
+ } else if (object instanceof PkgContentProvider.RepoSourceNotification) {
+ return getImageByName("nopkg_icon_16.png"); //$NON-NLS-1$
}
if (object instanceof Archive) {
@@ -133,6 +133,12 @@ public class ImageFactory { return getImageByName((String) object);
}
+
+ if (object != null) {
+ // For debugging
+ // System.out.println("No image for object " + object.getClass().getSimpleName());
+ }
+
return null;
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/error_icon16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/error_icon_16.png Binary files differindex ccb4d0a..ccb4d0a 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/error_icon16.png +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/error_icon_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_cat_icon16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_cat_icon_16.png Binary files differindex 13c8bb3..13c8bb3 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_cat_icon16.png +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_cat_icon_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_icon16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_icon_16.png Binary files differindex 5eb1ead..5eb1ead 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_icon16.png +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/source_icon_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AddonSitesDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AddonSitesDialog.java index 28a065a..e02d20e 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AddonSitesDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AddonSitesDialog.java @@ -23,9 +23,14 @@ import com.android.sdklib.internal.repository.SdkSources; import com.android.sdkuilib.internal.repository.UpdaterBaseDialog; import com.android.sdkuilib.internal.repository.UpdaterData; import com.android.sdkuilib.ui.GridDataBuilder; +import com.android.sdkuilib.ui.GridLayoutBuilder; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; @@ -46,110 +51,246 @@ import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; +import java.util.ArrayList; import java.util.Arrays; public class AddonSitesDialog extends UpdaterBaseDialog { - private Table mTable; - private TableViewer mTableViewer; - private Button mButtonNew; - private Button mButtonDelete; - private Button mButtonEdit; - private TableColumn mColumnUrl; + private final SdkSources mSources; + private Table mUserTable; + private TableViewer mUserTableViewer; + private CheckboxTableViewer mSitesTableViewer; + private Button mUserButtonNew; + private Button mUserButtonDelete; + private Button mUserButtonEdit; + private Runnable mSourcesChangeListener; /** * Create the dialog. * * @param parent The parent's shell + * @wbp.parser.entryPoint */ public AddonSitesDialog(Shell parent, UpdaterData updaterData) { super(parent, updaterData, "Add-on Sites"); + mSources = updaterData.getSources(); + assert mSources != null; } /** * Create contents of the dialog. + * @wbp.parser.entryPoint */ - @SuppressWarnings("unused") @Override protected void createContents() { super.createContents(); Shell shell = getShell(); - shell.setMinimumSize(new Point(450, 300)); - shell.setSize(450, 300); + shell.setMinimumSize(new Point(300, 300)); + shell.setSize(600, 400); + TabFolder tabFolder = new TabFolder(shell, SWT.NONE); + tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + GridDataBuilder.create(tabFolder).fill().grab().hSpan(2); + + TabItem sitesTabItem = new TabItem(tabFolder, SWT.NONE); + sitesTabItem.setText("Official Add-on Sites"); + createTabOfficialSites(tabFolder, sitesTabItem); + + TabItem userTabItem = new TabItem(tabFolder, SWT.NONE); + userTabItem.setText("User Defined Sites"); + createTabUserSites(tabFolder, userTabItem); + + // placeholder for aligning close button Label label = new Label(shell, SWT.NONE); + GridDataBuilder.create(label).hFill().hGrab(); + + createCloseButton(); + } + + void createTabOfficialSites(TabFolder tabFolder, TabItem sitesTabItem) { + Composite root = new Composite(tabFolder, SWT.NONE); + sitesTabItem.setControl(root); + GridLayoutBuilder.create(root).columns(3); + + Label label = new Label(root, SWT.NONE); + GridDataBuilder.create(label).hLeft().vCenter().hSpan(3); + label.setText( + "This lets select which official 3rd-party sites you want to load.\n" + + "\n" + + "These sites are managed by non-Android vendors to provide add-ons and extra packages.\n" + + "They are by default all enabled. When you disable one, the SDK Manager will not check the site for new packages." + ); + + mSitesTableViewer = CheckboxTableViewer.newCheckList(root, SWT.BORDER | SWT.FULL_SELECTION); + mSitesTableViewer.setContentProvider(new SourcesContentProvider()); + + Table sitesTable = mSitesTableViewer.getTable(); + sitesTable.setToolTipText("Enable 3rd-Party Site"); + sitesTable.setLinesVisible(true); + sitesTable.setHeaderVisible(true); + GridDataBuilder.create(sitesTable).fill().grab().hSpan(3); + + TableViewerColumn columnViewer = new TableViewerColumn(mSitesTableViewer, SWT.NONE); + TableColumn column = columnViewer.getColumn(); + column.setResizable(true); + column.setWidth(150); + column.setText("Name"); + columnViewer.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + if (element instanceof SdkSource) { + String name = ((SdkSource) element).getUiName(); + if (name != null) { + return name; + } + return ((SdkSource) element).getShortDescription(); + } + return super.getText(element); + } + }); + + columnViewer = new TableViewerColumn(mSitesTableViewer, SWT.NONE); + column = columnViewer.getColumn(); + column.setResizable(true); + column.setWidth(400); + column.setText("URL"); + columnViewer.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + if (element instanceof SdkSource) { + return ((SdkSource) element).getUrl(); + } + return super.getText(element); + } + }); + + mSitesTableViewer.addCheckStateListener(new ICheckStateListener() { + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + on_SitesTableViewer_checkStateChanged(event); + } + }); + + // "enable all" and "disable all" buttons under the table + Button selectAll = new Button(root, SWT.NONE); + selectAll.setText("Enable All"); + GridDataBuilder.create(selectAll).hLeft(); + selectAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + on_SitesTableViewer_selectAll(); + } + }); + + // placeholder between both buttons + label = new Label(root, SWT.NONE); + GridDataBuilder.create(label).hFill().hGrab(); + + Button deselectAll = new Button(root, SWT.NONE); + deselectAll.setText("Disable All"); + GridDataBuilder.create(deselectAll).hRight(); + deselectAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + on_SitesTableViewer_deselectAll(); + } + }); + } + + void createTabUserSites(TabFolder tabFolder, TabItem userTabItem) { + Composite root = new Composite(tabFolder, SWT.NONE); + userTabItem.setControl(root); + GridLayoutBuilder.create(root).columns(2); + + Label label = new Label(root, SWT.NONE); GridDataBuilder.create(label).hLeft().vCenter().hSpan(2); label.setText( - "This dialog lets you manage the URLs of external add-on sites to be used.\n" + + "This lets you manage a list of user-contributed external add-on sites URLs.\n" + "\n" + - "Add-on sites can provide new add-ons or \"user\" packages.\n" + - "They cannot provide standard Android platforms, docs or samples packages.\n" + + "Add-on sites can provide new add-ons and extra packages.\n" + + "They cannot provide standard Android platforms, system images or docs.\n" + "Adding a URL here will not allow you to clone an official Android repository." ); - mTableViewer = new TableViewer(shell, SWT.BORDER | SWT.FULL_SELECTION); - mTableViewer.addPostSelectionChangedListener(new ISelectionChangedListener() { + mUserTableViewer = new TableViewer(root, SWT.BORDER | SWT.FULL_SELECTION); + mUserTableViewer.setContentProvider(new SourcesContentProvider()); + + mUserTableViewer.addPostSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { - on_TableViewer_selectionChanged(event); + on_UserTableViewer_selectionChanged(event); } }); - mTable = mTableViewer.getTable(); - mTable.setLinesVisible(false); - mTable.addMouseListener(new MouseAdapter() { + mUserTable = mUserTableViewer.getTable(); + mUserTable.setLinesVisible(true); + mUserTable.addMouseListener(new MouseAdapter() { @Override - public void mouseUp(MouseEvent e) { - on_Table_mouseUp(e); + public void mouseUp(MouseEvent event) { + on_UserTable_mouseUp(event); } }); - GridDataBuilder.create(mTable).fill().grab().vSpan(5); + GridDataBuilder.create(mUserTable).fill().grab().vSpan(5); - TableViewerColumn tableViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE); - mColumnUrl = tableViewerColumn.getColumn(); - mColumnUrl.setWidth(100); - mColumnUrl.setText("New Column"); + TableViewerColumn tableViewerColumn = new TableViewerColumn(mUserTableViewer, SWT.NONE); + TableColumn userColumnUrl = tableViewerColumn.getColumn(); + userColumnUrl.setWidth(100); - mButtonNew = new Button(shell, SWT.NONE); - mButtonNew.addSelectionListener(new SelectionAdapter() { + // Implementation detail: set the label provider on the table viewer *after* associating + // a column. This will set the label provider on the column for us. + mUserTableViewer.setLabelProvider(new LabelProvider()); + + + mUserButtonNew = new Button(root, SWT.NONE); + mUserButtonNew.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - newOrEdit(false /*isEdit*/); + userNewOrEdit(false /*isEdit*/); } }); - GridDataBuilder.create(mButtonNew).hFill().vCenter(); - mButtonNew.setText("New..."); + GridDataBuilder.create(mUserButtonNew).hFill().vCenter(); + mUserButtonNew.setText("New..."); - mButtonEdit = new Button(shell, SWT.NONE); - mButtonEdit.addSelectionListener(new SelectionAdapter() { + mUserButtonEdit = new Button(root, SWT.NONE); + mUserButtonEdit.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - newOrEdit(true /*isEdit*/); + userNewOrEdit(true /*isEdit*/); } }); - GridDataBuilder.create(mButtonEdit).hFill().vCenter(); - mButtonEdit.setText("Edit..."); + GridDataBuilder.create(mUserButtonEdit).hFill().vCenter(); + mUserButtonEdit.setText("Edit..."); - mButtonDelete = new Button(shell, SWT.NONE); - mButtonDelete.addSelectionListener(new SelectionAdapter() { + mUserButtonDelete = new Button(root, SWT.NONE); + mUserButtonDelete.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - on_ButtonDelete_widgetSelected(e); + on_UserButtonDelete_widgetSelected(e); } }); - GridDataBuilder.create(mButtonDelete).hFill().vCenter(); - mButtonDelete.setText("Delete..."); - new Label(shell, SWT.NONE); + GridDataBuilder.create(mUserButtonDelete).hFill().vCenter(); + mUserButtonDelete.setText("Delete..."); - createCloseButton(); + adjustColumnsWidth(mUserTable, userColumnUrl); + } - adjustColumnsWidth(mTable, mColumnUrl); + @Override + protected void close() { + if (mSources != null && mSourcesChangeListener != null) { + mSources.removeChangeListener(mSourcesChangeListener); + } + super.close(); } /** @@ -166,12 +307,11 @@ public class AddonSitesDialog extends UpdaterBaseDialog { }); } - private void newOrEdit(final boolean isEdit) { - SdkSources sources = getUpdaterData().getSources(); - final SdkSource[] knownSources = sources.getAllSources(); + private void userNewOrEdit(final boolean isEdit) { + final SdkSource[] knownSources = mSources.getAllSources(); String title = isEdit ? "Edit Add-on Site URL" : "Add Add-on Site URL"; String msg = "Please enter the URL of the addon.xml:"; - IStructuredSelection sel = (IStructuredSelection) mTableViewer.getSelection(); + IStructuredSelection sel = (IStructuredSelection) mUserTableViewer.getSelection(); final String initialValue = !isEdit || sel.isEmpty() ? null : sel.getFirstElement().toString(); @@ -228,9 +368,9 @@ public class AddonSitesDialog extends UpdaterBaseDialog { if (isEdit && initialValue != null) { // Remove the old value before we add the new one, which is we just // asserted will be different. - for (SdkSource source : sources.getSources(SdkSourceCategory.USER_ADDONS)) { + for (SdkSource source : mSources.getSources(SdkSourceCategory.USER_ADDONS)) { if (initialValue.equals(source.getUrl())) { - sources.remove(source); + mSources.remove(source); break; } } @@ -239,21 +379,22 @@ public class AddonSitesDialog extends UpdaterBaseDialog { // create the source, store it and update the list SdkAddonSource newSource = new SdkAddonSource(url, null/*uiName*/); - sources.add( + mSources.add( SdkSourceCategory.USER_ADDONS, newSource); setReturnValue(true); - loadList(); + // notify sources change listeners. This will invoke our own loadUserUrlsList(). + mSources.notifyChangeListeners(); // select the new source IStructuredSelection newSel = new StructuredSelection(newSource); - mTableViewer.setSelection(newSel, true /*reveal*/); + mUserTableViewer.setSelection(newSel, true /*reveal*/); } } } - private void on_ButtonDelete_widgetSelected(SelectionEvent e) { - IStructuredSelection sel = (IStructuredSelection) mTableViewer.getSelection(); + private void on_UserButtonDelete_widgetSelected(SelectionEvent e) { + IStructuredSelection sel = (IStructuredSelection) mUserTableViewer.getSelection(); String selectedUrl = sel.isEmpty() ? null : sel.getFirstElement().toString(); if (selectedUrl == null) { @@ -265,60 +406,138 @@ public class AddonSitesDialog extends UpdaterBaseDialog { mb.setText("Delete add-on site"); mb.setMessage(String.format("Do you want to delete the URL %1$s?", selectedUrl)); if (mb.open() == SWT.YES) { - SdkSources sources = getUpdaterData().getSources(); - for (SdkSource source : sources.getSources(SdkSourceCategory.USER_ADDONS)) { + for (SdkSource source : mSources.getSources(SdkSourceCategory.USER_ADDONS)) { if (selectedUrl.equals(source.getUrl())) { - sources.remove(source); + mSources.remove(source); setReturnValue(true); - loadList(); + mSources.notifyChangeListeners(); + break; } } } } - private void on_Table_mouseUp(MouseEvent e) { - Point p = new Point(e.x, e.y); - if (mTable.getItem(p) == null) { - mTable.deselectAll(); - on_TableViewer_selectionChanged(null /*event*/); + private void on_UserTable_mouseUp(MouseEvent event) { + Point p = new Point(event.x, event.y); + if (mUserTable.getItem(p) == null) { + mUserTable.deselectAll(); + on_UserTableViewer_selectionChanged(null /*event*/); + } + } + + private void on_UserTableViewer_selectionChanged(SelectionChangedEvent event) { + ISelection sel = mUserTableViewer.getSelection(); + mUserButtonDelete.setEnabled(!sel.isEmpty()); + mUserButtonEdit.setEnabled(!sel.isEmpty()); + } + + private void on_SitesTableViewer_checkStateChanged(CheckStateChangedEvent event) { + Object element = event.getElement(); + if (element instanceof SdkSource) { + SdkSource source = (SdkSource) element; + boolean isChecked = event.getChecked(); + if (source.isEnabled() != isChecked) { + setReturnValue(true); + source.setEnabled(isChecked); + mSources.notifyChangeListeners(); + } + } + } + + private void on_SitesTableViewer_selectAll() { + for (Object item : (Object[]) mSitesTableViewer.getInput()) { + if (!mSitesTableViewer.getChecked(item)) { + mSitesTableViewer.setChecked(item, true); + on_SitesTableViewer_checkStateChanged( + new CheckStateChangedEvent(mSitesTableViewer, item, true)); + } } } - private void on_TableViewer_selectionChanged(SelectionChangedEvent event) { - ISelection sel = mTableViewer.getSelection(); - mButtonDelete.setEnabled(!sel.isEmpty()); - mButtonEdit.setEnabled(!sel.isEmpty()); + private void on_SitesTableViewer_deselectAll() { + for (Object item : (Object[]) mSitesTableViewer.getInput()) { + if (mSitesTableViewer.getChecked(item)) { + mSitesTableViewer.setChecked(item, false); + on_SitesTableViewer_checkStateChanged( + new CheckStateChangedEvent(mSitesTableViewer, item, false)); + } + } } + @Override protected void postCreate() { + // A runnable to initially load and then update the user urls & sites lists. + final Runnable updateInUiThread = new Runnable() { + @Override + public void run() { + loadUserUrlsList(); + loadSiteUrlsList(); + } + }; + + // A listener that runs when the sources have changed. + // This is most likely called on a worker thread. + mSourcesChangeListener = new Runnable() { + @Override + public void run() { + Shell shell = getShell(); + if (shell != null) { + Display display = shell.getDisplay(); + if (display != null) { + display.syncExec(updateInUiThread); + } + } + } + }; + + mSources.addChangeListener(mSourcesChangeListener); + // initialize the list - mTableViewer.setLabelProvider(new LabelProvider()); - mTableViewer.setContentProvider(new SourcesContentProvider()); - loadList(); + updateInUiThread.run(); } - private void loadList() { - if (getUpdaterData() != null) { - SdkSource[] knownSources = - getUpdaterData().getSources().getSources(SdkSourceCategory.USER_ADDONS); - Arrays.sort(knownSources); + private void loadUserUrlsList() { + SdkSource[] knownSources = mSources.getSources(SdkSourceCategory.USER_ADDONS); + Arrays.sort(knownSources); - ISelection oldSelection = mTableViewer.getSelection(); + ISelection oldSelection = mUserTableViewer.getSelection(); - mTableViewer.setInput(knownSources); - mTableViewer.refresh(); - // initialize buttons' state that depend on the list - on_TableViewer_selectionChanged(null /*event*/); + mUserTableViewer.setInput(knownSources); + mUserTableViewer.refresh(); + // initialize buttons' state that depend on the list + on_UserTableViewer_selectionChanged(null /*event*/); - if (oldSelection != null && !oldSelection.isEmpty()) { - mTableViewer.setSelection(oldSelection, true /*reveal*/); + if (oldSelection != null && !oldSelection.isEmpty()) { + mUserTableViewer.setSelection(oldSelection, true /*reveal*/); + } + } + + private void loadSiteUrlsList() { + SdkSource[] knownSources = mSources.getSources(SdkSourceCategory.ADDONS_3RD_PARTY); + Arrays.sort(knownSources); + + ISelection oldSelection = mSitesTableViewer.getSelection(); + + mSitesTableViewer.setInput(knownSources); + mSitesTableViewer.refresh(); + + if (oldSelection != null && !oldSelection.isEmpty()) { + mSitesTableViewer.setSelection(oldSelection, true /*reveal*/); + } + + // Check the sources which are currently enabled. + ArrayList<SdkSource> disabled = new ArrayList<SdkSource>(knownSources.length); + for (SdkSource source : knownSources) { + if (source.isEnabled()) { + disabled.add(source); } } + mSitesTableViewer.setCheckedElements(disabled.toArray()); } - private static class SourcesContentProvider implements IStructuredContentProvider { + private static class SourcesContentProvider implements IStructuredContentProvider { @Override public void dispose() { // pass diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java index 3d7cdb0..0774905 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java @@ -302,9 +302,7 @@ class PackagesDiffLogic { } public boolean updateSourcePackages(SdkSource source, Package[] newPackages) { - if (newPackages.length > 0) { - mVisitedSources.add(source); - } + mVisitedSources.add(source); if (source == null) { return processLocals(this, newPackages); } else { @@ -759,6 +757,31 @@ class PackagesDiffLogic { * {@link UpdateOp} describing the Sort-by-Source operation. */ private class UpdateOpSource extends UpdateOp { + + @Override + public boolean updateSourcePackages(SdkSource source, Package[] newPackages) { + // When displaying the repo by source, we want to create all the + // categories so that they can appear on the UI even if empty. + if (source != null) { + List<PkgCategory> cats = getCategories(); + Object catKey = source; + PkgCategory cat = findCurrentCategory(cats, catKey); + + if (cat == null) { + // This is a new category. Create it and add it to the list. + cat = createCategory(catKey); + synchronized (cats) { + cats.add(cat); + } + sortCategoryList(); + } + + keep(cat); + } + + return super.updateSourcePackages(source, newPackages); + } + @Override public Object getCategoryKey(Package pkg) { // Sort by source diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java index d6eb73e..99801b0 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java @@ -36,7 +36,6 @@ import com.android.sdkuilib.ui.GridDataBuilder; import com.android.sdkuilib.ui.GridLayoutBuilder; import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ColumnLabelProvider; @@ -48,8 +47,6 @@ import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ITableFontProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.jface.viewers.TreeColumnViewerLabelProvider; -import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; @@ -59,7 +56,6 @@ import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; @@ -424,6 +420,8 @@ public class PackagesPage extends UpdaterPage case TOGGLE_SHOW_ARCHIVES: mDisplayArchives = !mDisplayArchives; // Force the viewer to be refreshed + ((PkgContentProvider) mTreeViewer.getContentProvider()).setDisplayArchives( + mDisplayArchives); mTreeViewer.setInput(null); refreshViewerInput(); syncViewerSelection(); @@ -523,7 +521,6 @@ public class PackagesPage extends UpdaterPage item.setSelection(value); } } - } private void postCreate() { @@ -531,13 +528,19 @@ public class PackagesPage extends UpdaterPage mTextSdkOsPath.setText(mUpdaterData.getOsSdkRoot()); } - mTreeViewer.setContentProvider(new PkgContentProvider()); + mTreeViewer.setContentProvider(new PkgContentProvider(mTreeViewer)); + ((PkgContentProvider) mTreeViewer.getContentProvider()).setDisplayArchives( + mDisplayArchives); ColumnViewerToolTipSupport.enableFor(mTreeViewer, ToolTip.NO_RECREATE); - mColumnApi.setLabelProvider (new PkgTreeColumnViewerLabelProvider(mColumnApi)); - mColumnName.setLabelProvider (new PkgTreeColumnViewerLabelProvider(mColumnName)); - mColumnStatus.setLabelProvider (new PkgTreeColumnViewerLabelProvider(mColumnStatus)); - mColumnRevision.setLabelProvider(new PkgTreeColumnViewerLabelProvider(mColumnRevision)); + mColumnApi.setLabelProvider( + new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnApi))); + mColumnName.setLabelProvider( + new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnName))); + mColumnStatus.setLabelProvider( + new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnStatus))); + mColumnRevision.setLabelProvider( + new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnRevision))); FontData fontData = mTree.getFont().getFontData()[0]; fontData.setStyle(SWT.ITALIC); @@ -1277,116 +1280,6 @@ public class PackagesPage extends UpdaterPage // ---------------------- - /** - * A custom version of {@link TreeColumnViewerLabelProvider} which - * handles {@link TreePath}s and delegates content to a base - * {@link PkgCellLabelProvider} for the given {@link TreeViewerColumn}. - * <p/> - * The implementation handles a variety of providers (table label, table - * color, table font) but does not implement a tooltip provider, so we - * delegate the calls here to the appropriate {@link PkgCellLabelProvider}. - * <p/> - * Only {@link #getToolTipText(Object)} is really useful for us but we - * delegate all the tooltip calls for completeness and avoid surprises later - * if we ever decide to override more things in the label provider. - */ - public class PkgTreeColumnViewerLabelProvider extends TreeColumnViewerLabelProvider { - - private CellLabelProvider mTooltipProvider; - - public PkgTreeColumnViewerLabelProvider(TreeViewerColumn column) { - super(new PkgCellLabelProvider(column)); - } - - @Override - public void setProviders(Object provider) { - super.setProviders(provider); - if (provider instanceof CellLabelProvider) { - mTooltipProvider = (CellLabelProvider) provider; - } - } - - @Override - public Image getToolTipImage(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipImage(object); - } - return super.getToolTipImage(object); - } - - @Override - public String getToolTipText(Object element) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipText(element); - } - return super.getToolTipText(element); - } - - @Override - public Color getToolTipBackgroundColor(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipBackgroundColor(object); - } - return super.getToolTipBackgroundColor(object); - } - - @Override - public Color getToolTipForegroundColor(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipForegroundColor(object); - } - return super.getToolTipForegroundColor(object); - } - - @Override - public Font getToolTipFont(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipFont(object); - } - return super.getToolTipFont(object); - } - - @Override - public Point getToolTipShift(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipShift(object); - } - return super.getToolTipShift(object); - } - - @Override - public boolean useNativeToolTip(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.useNativeToolTip(object); - } - return super.useNativeToolTip(object); - } - - @Override - public int getToolTipTimeDisplayed(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipTimeDisplayed(object); - } - return super.getToolTipTimeDisplayed(object); - } - - @Override - public int getToolTipDisplayDelayTime(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipDisplayDelayTime(object); - } - return super.getToolTipDisplayDelayTime(object); - } - - @Override - public int getToolTipStyle(Object object) { - if (mTooltipProvider != null) { - return mTooltipProvider.getToolTipStyle(object); - } - return super.getToolTipStyle(object); - } - } - public class PkgCellLabelProvider extends ColumnLabelProvider implements ITableFontProvider { private final TreeViewerColumn mColumn; @@ -1400,7 +1293,6 @@ public class PackagesPage extends UpdaterPage public String getText(Object element) { if (mColumn == mColumnName) { - if (element instanceof PkgCategory) { return ((PkgCategory) element).getLabel(); } else if (element instanceof PkgItem) { @@ -1410,7 +1302,6 @@ public class PackagesPage extends UpdaterPage } } else if (mColumn == mColumnApi) { - int api = -1; if (element instanceof PkgItem) { api = ((PkgItem) element).getApi(); @@ -1420,14 +1311,12 @@ public class PackagesPage extends UpdaterPage } } else if (mColumn == mColumnRevision) { - if (element instanceof PkgItem) { PkgItem pkg = (PkgItem) element; return Integer.toString(pkg.getRevision()); } } else if (mColumn == mColumnStatus) { - if (element instanceof PkgItem) { PkgItem pkg = (PkgItem) element; @@ -1458,7 +1347,7 @@ public class PackagesPage extends UpdaterPage } } - return ""; + return ""; //$NON-NLS-1$ } private String getPkgItemName(PkgItem item) { @@ -1622,105 +1511,6 @@ public class PackagesPage extends UpdaterPage } } - private class PkgContentProvider implements ITreeContentProvider { - - @Override - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof ArrayList<?>) { - return ((ArrayList<?>) parentElement).toArray(); - - } else if (parentElement instanceof PkgCategory) { - return ((PkgCategory) parentElement).getItems().toArray(); - - } else if (parentElement instanceof PkgItem) { - if (mDisplayArchives) { - - Package pkg = ((PkgItem) parentElement).getUpdatePkg(); - - // Display update packages as sub-items if the details mode is activated. - if (pkg != null) { - return new Object[] { pkg }; - } - - return ((PkgItem) parentElement).getArchives(); - } - - } else if (parentElement instanceof Package) { - if (mDisplayArchives) { - return ((Package) parentElement).getArchives(); - } - - } - - return new Object[0]; - } - - @Override - @SuppressWarnings("unchecked") - public Object getParent(Object element) { - // This operation is expensive, so we do the minimum - // and don't try to cover all cases. - - if (element instanceof PkgItem) { - Object input = mTreeViewer.getInput(); - if (input != null) { - for (PkgCategory cat : (List<PkgCategory>) input) { - if (cat.getItems().contains(element)) { - return cat; - } - } - } - } - - return null; - } - - @Override - public boolean hasChildren(Object parentElement) { - if (parentElement instanceof ArrayList<?>) { - return true; - - } else if (parentElement instanceof PkgCategory) { - return true; - - } else if (parentElement instanceof PkgItem) { - if (mDisplayArchives) { - Package pkg = ((PkgItem) parentElement).getUpdatePkg(); - - // Display update packages as sub-items if the details mode is activated. - if (pkg != null) { - return true; - } - - Archive[] archives = ((PkgItem) parentElement).getArchives(); - return archives.length > 0; - } - } else if (parentElement instanceof Package) { - if (mDisplayArchives) { - return ((Package) parentElement).getArchives().length > 0; - } - } - - return false; - } - - @Override - public Object[] getElements(Object inputElement) { - return getChildren(inputElement); - } - - @Override - public void dispose() { - // unused - - } - - @Override - public void inputChanged(Viewer arg0, Object arg1, Object arg2) { - // unused - } - } - // --- Implementation of ISdkChangeListener --- @Override diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgContentProvider.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgContentProvider.java new file mode 100755 index 0000000..bd3bd0e --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgContentProvider.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2012 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.sdkuilib.internal.repository.sdkman2; + +import com.android.sdklib.internal.repository.Archive; +import com.android.sdklib.internal.repository.IDescription; +import com.android.sdklib.internal.repository.Package; +import com.android.sdklib.internal.repository.SdkSource; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Content provider for the main tree view in {@link PackagesPage}. + */ +public class PkgContentProvider implements ITreeContentProvider { + + private final Viewer mViewer; + private boolean mDisplayArchives; + + public PkgContentProvider(Viewer viewer) { + mViewer = viewer; + } + + public void setDisplayArchives(boolean displayArchives) { + mDisplayArchives = displayArchives; + } + + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof ArrayList<?>) { + return ((ArrayList<?>) parentElement).toArray(); + + } else if (parentElement instanceof PkgCategorySource) { + return getSourceChildren((PkgCategorySource) parentElement); + + } else if (parentElement instanceof PkgCategory) { + return ((PkgCategory) parentElement).getItems().toArray(); + + } else if (parentElement instanceof PkgItem) { + if (mDisplayArchives) { + + Package pkg = ((PkgItem) parentElement).getUpdatePkg(); + + // Display update packages as sub-items if the details mode is activated. + if (pkg != null) { + return new Object[] { pkg }; + } + + return ((PkgItem) parentElement).getArchives(); + } + + } else if (parentElement instanceof Package) { + if (mDisplayArchives) { + return ((Package) parentElement).getArchives(); + } + + } + + return new Object[0]; + } + + @Override + @SuppressWarnings("unchecked") + public Object getParent(Object element) { + // This operation is expensive, so we do the minimum + // and don't try to cover all cases. + + if (element instanceof PkgItem) { + Object input = mViewer.getInput(); + if (input != null) { + for (PkgCategory cat : (List<PkgCategory>) input) { + if (cat.getItems().contains(element)) { + return cat; + } + } + } + } + + return null; + } + + @Override + public boolean hasChildren(Object parentElement) { + if (parentElement instanceof ArrayList<?>) { + return true; + + } else if (parentElement instanceof PkgCategory) { + return true; + + } else if (parentElement instanceof PkgItem) { + if (mDisplayArchives) { + Package pkg = ((PkgItem) parentElement).getUpdatePkg(); + + // Display update packages as sub-items if the details mode is activated. + if (pkg != null) { + return true; + } + + Archive[] archives = ((PkgItem) parentElement).getArchives(); + return archives.length > 0; + } + } else if (parentElement instanceof Package) { + if (mDisplayArchives) { + return ((Package) parentElement).getArchives().length > 0; + } + } + + return false; + } + + @Override + public Object[] getElements(Object inputElement) { + return getChildren(inputElement); + } + + @Override + public void dispose() { + // unused + + } + + @Override + public void inputChanged(Viewer arg0, Object arg1, Object arg2) { + // unused + } + + + private Object[] getSourceChildren(PkgCategorySource parentElement) { + List<?> children = parentElement.getItems(); + + SdkSource source = parentElement.getSource(); + IDescription error = null; + IDescription empty = null; + + String errStr = source.getFetchError(); + if (errStr != null) { + error = new RepoSourceError(source); + } + if (!source.isEnabled() || children.isEmpty()) { + empty = new RepoSourceNotification(source); + } + + if (error != null || empty != null) { + ArrayList<Object> children2 = new ArrayList<Object>(); + if (error != null) { + children2.add(error); + } + if (empty != null) { + children2.add(empty); + } + children2.addAll(children); + children = children2; + } + + return children.toArray(); + } + + + /** + * A dummy entry returned for sources which had load errors. + * It displays a summary of the error as its short description or + * it displays the source's long description. + */ + public static class RepoSourceError implements IDescription { + + private final SdkSource mSource; + + public RepoSourceError(SdkSource source) { + mSource = source; + } + + @Override + public String getLongDescription() { + return mSource.getLongDescription(); + } + + @Override + public String getShortDescription() { + return mSource.getFetchError(); + } + } + + /** + * A dummy entry returned for sources with no packages. + * We need that to force the SWT tree to display an open/close triangle + * even for empty sources. + */ + public static class RepoSourceNotification implements IDescription { + + private final SdkSource mSource; + + public RepoSourceNotification(SdkSource source) { + mSource = source; + } + + @Override + public String getLongDescription() { + return mSource.getLongDescription(); + } + + @Override + public String getShortDescription() { + if (mSource.isEnabled()) { + return "No packages found."; + } else { + return "This site is disabled."; + } + } + } + +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgTreeColumnViewerLabelProvider.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgTreeColumnViewerLabelProvider.java new file mode 100755 index 0000000..392c87e --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PkgTreeColumnViewerLabelProvider.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 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.sdkuilib.internal.repository.sdkman2; + +import org.eclipse.jface.viewers.CellLabelProvider; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.TreeColumnViewerLabelProvider; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +/** + * A custom version of {@link TreeColumnViewerLabelProvider} which + * handles {@link TreePath}s and delegates content to the given + * {@link ColumnLabelProvider} for a given {@link TreeViewerColumn}. + * <p/> + * The implementation handles a variety of providers (table label, table + * color, table font) but does not implement a tooltip provider, so we + * delegate the calls here to the appropriate {@link ColumnLabelProvider}. + * <p/> + * Only {@link #getToolTipText(Object)} is really useful for us but we + * delegate all the tooltip calls for completeness and avoid surprises later + * if we ever decide to override more things in the label provider. + */ +class PkgTreeColumnViewerLabelProvider extends TreeColumnViewerLabelProvider { + + private CellLabelProvider mTooltipProvider; + + public PkgTreeColumnViewerLabelProvider(ColumnLabelProvider columnLabelProvider) { + super(columnLabelProvider); + } + + @Override + public void setProviders(Object provider) { + super.setProviders(provider); + if (provider instanceof CellLabelProvider) { + mTooltipProvider = (CellLabelProvider) provider; + } + } + + @Override + public Image getToolTipImage(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipImage(object); + } + return super.getToolTipImage(object); + } + + @Override + public String getToolTipText(Object element) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipText(element); + } + return super.getToolTipText(element); + } + + @Override + public Color getToolTipBackgroundColor(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipBackgroundColor(object); + } + return super.getToolTipBackgroundColor(object); + } + + @Override + public Color getToolTipForegroundColor(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipForegroundColor(object); + } + return super.getToolTipForegroundColor(object); + } + + @Override + public Font getToolTipFont(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipFont(object); + } + return super.getToolTipFont(object); + } + + @Override + public Point getToolTipShift(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipShift(object); + } + return super.getToolTipShift(object); + } + + @Override + public boolean useNativeToolTip(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.useNativeToolTip(object); + } + return super.useNativeToolTip(object); + } + + @Override + public int getToolTipTimeDisplayed(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipTimeDisplayed(object); + } + return super.getToolTipTimeDisplayed(object); + } + + @Override + public int getToolTipDisplayDelayTime(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipDisplayDelayTime(object); + } + return super.getToolTipDisplayDelayTime(object); + } + + @Override + public int getToolTipStyle(Object object) { + if (mTooltipProvider != null) { + return mTooltipProvider.getToolTipStyle(object); + } + return super.getToolTipStyle(object); + } +} |