diff options
27 files changed, 7620 insertions, 7620 deletions
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AboutDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AboutDialog.java index fa3f688..6954c27 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AboutDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AboutDialog.java @@ -1,120 +1,120 @@ -/*
- * 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;
-
-
-import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.FileOp;
-import com.android.sdklib.repository.PkgProps;
-import com.android.sdklib.repository.SdkAddonConstants;
-import com.android.sdklib.repository.SdkRepoConstants;
-import com.android.sdkuilib.internal.repository.icons.ImageFactory;
-import com.android.sdkuilib.ui.GridDataBuilder;
-import com.android.sdkuilib.ui.GridLayoutBuilder;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.Properties;
-
-public class AboutDialog extends UpdaterBaseDialog {
-
- public AboutDialog(Shell parentShell, UpdaterData updaterData) {
- super(parentShell, updaterData, "About" /*title*/);
- assert updaterData != null;
- }
-
- @Override
- protected void createContents() {
- super.createContents();
- Shell shell = getShell();
- shell.setMinimumSize(new Point(450, 150));
- shell.setSize(450, 150);
-
- GridLayoutBuilder.create(shell).columns(3);
-
- Label logo = new Label(shell, SWT.NONE);
- ImageFactory imgf = getUpdaterData() == null ? null : getUpdaterData().getImageFactory();
- Image image = imgf == null ? null : imgf.getImageByName("sdkman_logo_128.png");
- if (image != null) logo.setImage(image);
-
- Label label = new Label(shell, SWT.NONE);
- GridDataBuilder.create(label).hFill().hGrab().hSpan(2);;
- label.setText(String.format(
- "Android SDK Manager.\n" +
- "Revision %1$s\n" +
- "Add-on XML Schema #%2$d\n" +
- "Repository XML Schema #%3$d\n" +
- // TODO: update with new year date (search this to find other occurrences to update)
- "Copyright (C) 2009-2012 The Android Open Source Project.",
- getRevision(),
- SdkAddonConstants.NS_LATEST_VERSION,
- SdkRepoConstants.NS_LATEST_VERSION));
-
- Label filler = new Label(shell, SWT.NONE);
- GridDataBuilder.create(filler).fill().grab().hSpan(2);
-
- createCloseButton();
- }
-
- @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>>$
-
- // End of hiding from SWT Designer
- //$hide<<$
-
- private String getRevision() {
- Properties p = new Properties();
- try{
- File sourceProp = FileOp.append(getUpdaterData().getOsSdkRoot(),
- SdkConstants.FD_TOOLS,
- SdkConstants.FN_SOURCE_PROP);
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(sourceProp);
- p.load(fis);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException ignore) {
- }
- }
- }
-
- String revision = p.getProperty(PkgProps.PKG_REVISION);
- if (revision != null) {
- return revision;
- }
- } catch (IOException e) {
- }
-
- return "?";
- }
-}
+/* + * 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; + + +import com.android.sdklib.SdkConstants; +import com.android.sdklib.io.FileOp; +import com.android.sdklib.repository.PkgProps; +import com.android.sdklib.repository.SdkAddonConstants; +import com.android.sdklib.repository.SdkRepoConstants; +import com.android.sdkuilib.internal.repository.icons.ImageFactory; +import com.android.sdkuilib.ui.GridDataBuilder; +import com.android.sdkuilib.ui.GridLayoutBuilder; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +public class AboutDialog extends UpdaterBaseDialog { + + public AboutDialog(Shell parentShell, UpdaterData updaterData) { + super(parentShell, updaterData, "About" /*title*/); + assert updaterData != null; + } + + @Override + protected void createContents() { + super.createContents(); + Shell shell = getShell(); + shell.setMinimumSize(new Point(450, 150)); + shell.setSize(450, 150); + + GridLayoutBuilder.create(shell).columns(3); + + Label logo = new Label(shell, SWT.NONE); + ImageFactory imgf = getUpdaterData() == null ? null : getUpdaterData().getImageFactory(); + Image image = imgf == null ? null : imgf.getImageByName("sdkman_logo_128.png"); + if (image != null) logo.setImage(image); + + Label label = new Label(shell, SWT.NONE); + GridDataBuilder.create(label).hFill().hGrab().hSpan(2);; + label.setText(String.format( + "Android SDK Manager.\n" + + "Revision %1$s\n" + + "Add-on XML Schema #%2$d\n" + + "Repository XML Schema #%3$d\n" + + // TODO: update with new year date (search this to find other occurrences to update) + "Copyright (C) 2009-2012 The Android Open Source Project.", + getRevision(), + SdkAddonConstants.NS_LATEST_VERSION, + SdkRepoConstants.NS_LATEST_VERSION)); + + Label filler = new Label(shell, SWT.NONE); + GridDataBuilder.create(filler).fill().grab().hSpan(2); + + createCloseButton(); + } + + @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>>$ + + // End of hiding from SWT Designer + //$hide<<$ + + private String getRevision() { + Properties p = new Properties(); + try{ + File sourceProp = FileOp.append(getUpdaterData().getOsSdkRoot(), + SdkConstants.FD_TOOLS, + SdkConstants.FN_SOURCE_PROP); + FileInputStream fis = null; + try { + fis = new FileInputStream(sourceProp); + p.load(fis); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException ignore) { + } + } + } + + String revision = p.getProperty(PkgProps.PKG_REVISION); + if (revision != null) { + return revision; + } + } catch (IOException e) { + } + + return "?"; + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ArchiveInfo.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ArchiveInfo.java index dafcc05..2c509ca 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ArchiveInfo.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ArchiveInfo.java @@ -1,160 +1,160 @@ -/*
- * 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.sdkuilib.internal.repository;
-
-import com.android.sdklib.internal.repository.archives.Archive;
-import com.android.sdklib.internal.repository.archives.ArchiveReplacement;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Represents an archive that we want to install.
- * Note that the installer deals with archives whereas the user mostly sees packages
- * but as far as we are concerned for installation there's a 1-to-1 mapping.
- * <p/>
- * A new archive is always a remote archive that needs to be downloaded and then
- * installed. It can replace an existing local one. It can also depends on another
- * (new or local) archive, which means the dependent archive needs to be successfully
- * installed first. Finally this archive can also be a dependency for another one.
- * <p/>
- * The accepted and rejected flags are used by {@link SdkUpdaterChooserDialog} to follow
- * user choices. The installer should never install something that is not accepted.
- * <p/>
- * <em>Note</em>: There is currently no logic to support more than one level of
- * dependency, either here or in the {@link SdkUpdaterChooserDialog}, since we currently
- * have no need for it.
- *
- * @see ArchiveInfo#ArchiveInfo(Archive, Archive, ArchiveInfo[])
- */
-class ArchiveInfo extends ArchiveReplacement implements Comparable<ArchiveInfo> {
-
- private final ArchiveInfo[] mDependsOn;
- private final ArrayList<ArchiveInfo> mDependencyFor = new ArrayList<ArchiveInfo>();
- private boolean mAccepted;
- private boolean mRejected;
-
- /**
- * Creates a new replacement where the {@code newArchive} will replace the
- * currently installed {@code replaced} archive.
- * When {@code newArchive} is not intended to replace anything (e.g. because
- * the user is installing a new package not present on her system yet), then
- * {@code replace} shall be null.
- *
- * @param newArchive A "new archive" to be installed. This is always an archive
- * that comes from a remote site. This <em>may</em> be null.
- * @param replaced An optional local archive that the new one will replace.
- * Can be null if this archive does not replace anything.
- * @param dependsOn An optional new or local dependency, that is an archive that
- * <em>this</em> archive depends upon. In other words, we can only install
- * this archive if the dependency has been successfully installed. It also
- * means we need to install the dependency first. Can be null or empty.
- * However it cannot contain nulls.
- */
- public ArchiveInfo(Archive newArchive, Archive replaced, ArchiveInfo[] dependsOn) {
- super(newArchive, replaced);
- mDependsOn = dependsOn;
- }
-
- /**
- * Returns an optional new or local dependency, that is an archive that <em>this</em>
- * archive depends upon. In other words, we can only install this archive if the
- * dependency has been successfully installed. It also means we need to install the
- * dependency first.
- * <p/>
- * This array can be null or empty. It can't contain nulls though.
- */
- public ArchiveInfo[] getDependsOn() {
- return mDependsOn;
- }
-
- /**
- * Returns true if this new archive is a dependency for <em>another</em> one that we
- * want to install.
- */
- public boolean isDependencyFor() {
- return mDependencyFor.size() > 0;
- }
-
- /**
- * Adds an {@link ArchiveInfo} for which <em>this</em> package is a dependency.
- * This means the package added here depends on this package.
- */
- public ArchiveInfo addDependencyFor(ArchiveInfo dependencyFor) {
- if (!mDependencyFor.contains(dependencyFor)) {
- mDependencyFor.add(dependencyFor);
- }
-
- return this;
- }
-
- /**
- * Returns the list of {@link ArchiveInfo} for which <em>this</em> package is a dependency.
- * This means the packages listed here depend on this package.
- * <p/>
- * Implementation detail: this is the internal mutable list. Callers should not modify it.
- * This list can be empty but is never null.
- */
- public Collection<ArchiveInfo> getDependenciesFor() {
- return mDependencyFor;
- }
-
- /**
- * Sets whether this archive was accepted (either manually by the user or
- * automatically if it doesn't have a license) for installation.
- */
- public void setAccepted(boolean accepted) {
- mAccepted = accepted;
- }
-
- /**
- * Returns whether this archive was accepted (either manually by the user or
- * automatically if it doesn't have a license) for installation.
- */
- public boolean isAccepted() {
- return mAccepted;
- }
-
- /**
- * Sets whether this archive was rejected manually by the user.
- * An archive can neither accepted nor rejected.
- */
- public void setRejected(boolean rejected) {
- mRejected = rejected;
- }
-
- /**
- * Returns whether this archive was rejected manually by the user.
- * An archive can neither accepted nor rejected.
- */
- public boolean isRejected() {
- return mRejected;
- }
-
- /**
- * ArchiveInfos are compared using ther "new archive" ordering.
- *
- * @see Archive#compareTo(Archive)
- */
- @Override
- public int compareTo(ArchiveInfo rhs) {
- if (getNewArchive() != null && rhs != null) {
- return getNewArchive().compareTo(rhs.getNewArchive());
- }
- return 0;
- }
-}
+/* + * 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.sdkuilib.internal.repository; + +import com.android.sdklib.internal.repository.archives.Archive; +import com.android.sdklib.internal.repository.archives.ArchiveReplacement; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Represents an archive that we want to install. + * Note that the installer deals with archives whereas the user mostly sees packages + * but as far as we are concerned for installation there's a 1-to-1 mapping. + * <p/> + * A new archive is always a remote archive that needs to be downloaded and then + * installed. It can replace an existing local one. It can also depends on another + * (new or local) archive, which means the dependent archive needs to be successfully + * installed first. Finally this archive can also be a dependency for another one. + * <p/> + * The accepted and rejected flags are used by {@link SdkUpdaterChooserDialog} to follow + * user choices. The installer should never install something that is not accepted. + * <p/> + * <em>Note</em>: There is currently no logic to support more than one level of + * dependency, either here or in the {@link SdkUpdaterChooserDialog}, since we currently + * have no need for it. + * + * @see ArchiveInfo#ArchiveInfo(Archive, Archive, ArchiveInfo[]) + */ +class ArchiveInfo extends ArchiveReplacement implements Comparable<ArchiveInfo> { + + private final ArchiveInfo[] mDependsOn; + private final ArrayList<ArchiveInfo> mDependencyFor = new ArrayList<ArchiveInfo>(); + private boolean mAccepted; + private boolean mRejected; + + /** + * Creates a new replacement where the {@code newArchive} will replace the + * currently installed {@code replaced} archive. + * When {@code newArchive} is not intended to replace anything (e.g. because + * the user is installing a new package not present on her system yet), then + * {@code replace} shall be null. + * + * @param newArchive A "new archive" to be installed. This is always an archive + * that comes from a remote site. This <em>may</em> be null. + * @param replaced An optional local archive that the new one will replace. + * Can be null if this archive does not replace anything. + * @param dependsOn An optional new or local dependency, that is an archive that + * <em>this</em> archive depends upon. In other words, we can only install + * this archive if the dependency has been successfully installed. It also + * means we need to install the dependency first. Can be null or empty. + * However it cannot contain nulls. + */ + public ArchiveInfo(Archive newArchive, Archive replaced, ArchiveInfo[] dependsOn) { + super(newArchive, replaced); + mDependsOn = dependsOn; + } + + /** + * Returns an optional new or local dependency, that is an archive that <em>this</em> + * archive depends upon. In other words, we can only install this archive if the + * dependency has been successfully installed. It also means we need to install the + * dependency first. + * <p/> + * This array can be null or empty. It can't contain nulls though. + */ + public ArchiveInfo[] getDependsOn() { + return mDependsOn; + } + + /** + * Returns true if this new archive is a dependency for <em>another</em> one that we + * want to install. + */ + public boolean isDependencyFor() { + return mDependencyFor.size() > 0; + } + + /** + * Adds an {@link ArchiveInfo} for which <em>this</em> package is a dependency. + * This means the package added here depends on this package. + */ + public ArchiveInfo addDependencyFor(ArchiveInfo dependencyFor) { + if (!mDependencyFor.contains(dependencyFor)) { + mDependencyFor.add(dependencyFor); + } + + return this; + } + + /** + * Returns the list of {@link ArchiveInfo} for which <em>this</em> package is a dependency. + * This means the packages listed here depend on this package. + * <p/> + * Implementation detail: this is the internal mutable list. Callers should not modify it. + * This list can be empty but is never null. + */ + public Collection<ArchiveInfo> getDependenciesFor() { + return mDependencyFor; + } + + /** + * Sets whether this archive was accepted (either manually by the user or + * automatically if it doesn't have a license) for installation. + */ + public void setAccepted(boolean accepted) { + mAccepted = accepted; + } + + /** + * Returns whether this archive was accepted (either manually by the user or + * automatically if it doesn't have a license) for installation. + */ + public boolean isAccepted() { + return mAccepted; + } + + /** + * Sets whether this archive was rejected manually by the user. + * An archive can neither accepted nor rejected. + */ + public void setRejected(boolean rejected) { + mRejected = rejected; + } + + /** + * Returns whether this archive was rejected manually by the user. + * An archive can neither accepted nor rejected. + */ + public boolean isRejected() { + return mRejected; + } + + /** + * ArchiveInfos are compared using ther "new archive" ordering. + * + * @see Archive#compareTo(Archive) + */ + @Override + public int compareTo(ArchiveInfo rhs) { + if (getNewArchive() != null && rhs != null) { + return getNewArchive().compareTo(rhs.getNewArchive()); + } + return 0; + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java index 6d15360..333644f 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java @@ -1,108 +1,108 @@ -/*
- * 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.sdkuilib.internal.repository;
-
-import com.android.sdklib.internal.repository.DownloadCache;
-
-import java.net.URL;
-import java.util.Properties;
-
-/**
- * Interface that a settings page must implement.
- */
-public interface ISettingsPage {
-
- /**
- * Java system setting picked up by {@link URL} for http proxy port.
- * Type: String.
- */
- public static final String KEY_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$
-
- /**
- * Java system setting picked up by {@link URL} for http proxy host.
- * Type: String.
- */
- public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$
-
- /**
- * Setting to force using http:// instead of https:// connections.
- * Type: Boolean.
- * Default: False.
- */
- public static final String KEY_FORCE_HTTP = "sdkman.force.http"; //$NON-NLS-1$
-
- /**
- * Setting to display only packages that are new or updates.
- * Type: Boolean.
- * Default: True.
- */
- public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$
-
- /**
- * Setting to ask for permission before restarting ADB.
- * Type: Boolean.
- * Default: False.
- */
- public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$
-
- /**
- * Setting to use the {@link DownloadCache}, for small manifest XML files.
- * Type: Boolean.
- * Default: True.
- */
- public static final String KEY_USE_DOWNLOAD_CACHE = "sdkman.use.dl.cache"; //$NON-NLS-1$
-
- /**
- * Setting to enabling previews in the package list
- * Type: Boolean.
- * Default: False.
- */
- public static final String KEY_ENABLE_PREVIEWS = "sdkman.enable.previews"; //$NON-NLS-1$
-
- /**
- * Setting to set the density of the monitor.
- * Type: Integer.
- * Default: -1
- */
- public static final String KEY_MONITOR_DENSITY = "sdkman.monitor.density"; //$NON-NLS-1$
-
- /** Loads settings from the given {@link Properties} container and update the page UI. */
- public abstract void loadSettings(Properties inSettings);
-
- /** Called by the application to retrieve settings from the UI and store them in
- * the given {@link Properties} container. */
- public abstract void retrieveSettings(Properties outSettings);
-
- /**
- * Called by the application to give a callback that the page should invoke when
- * settings have changed.
- */
- public abstract void setOnSettingsChanged(SettingsChangedCallback settingsChangedCallback);
-
- /**
- * Callback used to notify the application that settings have changed and need to be
- * applied.
- */
- public interface SettingsChangedCallback {
- /**
- * Invoked by the settings page when settings have changed and need to be
- * applied. The application will call {@link ISettingsPage#retrieveSettings(Properties)}
- * and apply the new settings.
- */
- public abstract void onSettingsChanged(ISettingsPage page);
- }
-}
+/* + * 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.sdkuilib.internal.repository; + +import com.android.sdklib.internal.repository.DownloadCache; + +import java.net.URL; +import java.util.Properties; + +/** + * Interface that a settings page must implement. + */ +public interface ISettingsPage { + + /** + * Java system setting picked up by {@link URL} for http proxy port. + * Type: String. + */ + public static final String KEY_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$ + + /** + * Java system setting picked up by {@link URL} for http proxy host. + * Type: String. + */ + public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$ + + /** + * Setting to force using http:// instead of https:// connections. + * Type: Boolean. + * Default: False. + */ + public static final String KEY_FORCE_HTTP = "sdkman.force.http"; //$NON-NLS-1$ + + /** + * Setting to display only packages that are new or updates. + * Type: Boolean. + * Default: True. + */ + public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$ + + /** + * Setting to ask for permission before restarting ADB. + * Type: Boolean. + * Default: False. + */ + public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$ + + /** + * Setting to use the {@link DownloadCache}, for small manifest XML files. + * Type: Boolean. + * Default: True. + */ + public static final String KEY_USE_DOWNLOAD_CACHE = "sdkman.use.dl.cache"; //$NON-NLS-1$ + + /** + * Setting to enabling previews in the package list + * Type: Boolean. + * Default: False. + */ + public static final String KEY_ENABLE_PREVIEWS = "sdkman.enable.previews"; //$NON-NLS-1$ + + /** + * Setting to set the density of the monitor. + * Type: Integer. + * Default: -1 + */ + public static final String KEY_MONITOR_DENSITY = "sdkman.monitor.density"; //$NON-NLS-1$ + + /** Loads settings from the given {@link Properties} container and update the page UI. */ + public abstract void loadSettings(Properties inSettings); + + /** Called by the application to retrieve settings from the UI and store them in + * the given {@link Properties} container. */ + public abstract void retrieveSettings(Properties outSettings); + + /** + * Called by the application to give a callback that the page should invoke when + * settings have changed. + */ + public abstract void setOnSettingsChanged(SettingsChangedCallback settingsChangedCallback); + + /** + * Callback used to notify the application that settings have changed and need to be + * applied. + */ + public interface SettingsChangedCallback { + /** + * Invoked by the settings page when settings have changed and need to be + * applied. The application will call {@link ISettingsPage#retrieveSettings(Properties)} + * and apply the new settings. + */ + public abstract void onSettingsChanged(ISettingsPage page); + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/MenuBarWrapper.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/MenuBarWrapper.java index f7bd97f..b23af12 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/MenuBarWrapper.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/MenuBarWrapper.java @@ -1,60 +1,60 @@ -/*
- * Copyright (C) 2011 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;
-
-
-import com.android.menubar.IMenuBarCallback;
-import com.android.menubar.MenuBarEnhancer;
-import com.android.sdkuilib.internal.repository.sdkman2.SdkUpdaterWindowImpl2;
-
-import org.eclipse.swt.widgets.Menu;
-
-/**
- * A simple wrapper/delegate around the {@link MenuBarEnhancer}.
- *
- * The {@link MenuBarEnhancer} and {@link IMenuBarCallback} classes are only
- * available when the SwtMenuBar library is available too. This wrapper helps
- * {@link SdkUpdaterWindowImpl2} make the call conditional, otherwise the updater
- * window class would fail to load when the SwtMenuBar library isn't found.
- */
-public abstract class MenuBarWrapper {
-
- public MenuBarWrapper(String appName, Menu menu) {
- MenuBarEnhancer.setupMenu(appName, menu, new IMenuBarCallback() {
- @Override
- public void onPreferencesMenuSelected() {
- MenuBarWrapper.this.onPreferencesMenuSelected();
- }
-
- @Override
- public void onAboutMenuSelected() {
- MenuBarWrapper.this.onAboutMenuSelected();
- }
-
- @Override
- public void printError(String format, Object... args) {
- MenuBarWrapper.this.printError(format, args);
- }
- });
- }
-
- abstract public void onPreferencesMenuSelected();
-
- abstract public void onAboutMenuSelected();
-
- abstract public void printError(String format, Object... args);
-}
+/* + * Copyright (C) 2011 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; + + +import com.android.menubar.IMenuBarCallback; +import com.android.menubar.MenuBarEnhancer; +import com.android.sdkuilib.internal.repository.sdkman2.SdkUpdaterWindowImpl2; + +import org.eclipse.swt.widgets.Menu; + +/** + * A simple wrapper/delegate around the {@link MenuBarEnhancer}. + * + * The {@link MenuBarEnhancer} and {@link IMenuBarCallback} classes are only + * available when the SwtMenuBar library is available too. This wrapper helps + * {@link SdkUpdaterWindowImpl2} make the call conditional, otherwise the updater + * window class would fail to load when the SwtMenuBar library isn't found. + */ +public abstract class MenuBarWrapper { + + public MenuBarWrapper(String appName, Menu menu) { + MenuBarEnhancer.setupMenu(appName, menu, new IMenuBarCallback() { + @Override + public void onPreferencesMenuSelected() { + MenuBarWrapper.this.onPreferencesMenuSelected(); + } + + @Override + public void onAboutMenuSelected() { + MenuBarWrapper.this.onAboutMenuSelected(); + } + + @Override + public void printError(String format, Object... args) { + MenuBarWrapper.this.printError(format, args); + } + }); + } + + abstract public void onPreferencesMenuSelected(); + + abstract public void onAboutMenuSelected(); + + abstract public void printError(String format, Object... args); +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterChooserDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterChooserDialog.java index a41a952..3b0c691 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterChooserDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterChooserDialog.java @@ -1,762 +1,762 @@ -/*
- * 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.sdkuilib.internal.repository;
-
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.SdkConstants;
-import com.android.sdklib.internal.repository.archives.Archive;
-import com.android.sdklib.internal.repository.packages.IAndroidVersionProvider;
-import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.packages.FullRevision;
-import com.android.sdklib.internal.repository.sources.SdkSource;
-import com.android.sdkuilib.internal.repository.icons.ImageFactory;
-import com.android.sdkuilib.ui.GridDialog;
-
-import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.LabelProvider;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.custom.StyleRange;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-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.Control;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-
-/**
- * Implements an {@link SdkUpdaterChooserDialog}.
- */
-final class SdkUpdaterChooserDialog extends GridDialog {
-
- /** Last dialog size for this session. */
- private static Point sLastSize;
- private boolean mLicenseAcceptAll;
- private boolean mInternalLicenseRadioUpdate;
-
- // UI fields
- private SashForm mSashForm;
- private Composite mPackageRootComposite;
- private TableViewer mTableViewPackage;
- private Table mTablePackage;
- private TableColumn mTableColum;
- private StyledText mPackageText;
- private Button mLicenseRadioAccept;
- private Button mLicenseRadioReject;
- private Button mLicenseRadioAcceptAll;
- private Group mPackageTextGroup;
- private final UpdaterData mUpdaterData;
- private Group mTableGroup;
- private Label mErrorLabel;
-
- /**
- * List of all archives to be installed with dependency information.
- * <p/>
- * Note: in a lot of cases, we need to find the archive info for a given archive. This
- * is currently done using a simple linear search, which is fine since we only have a very
- * limited number of archives to deal with (e.g. < 10 now). We might want to revisit
- * this later if it becomes an issue. Right now just do the simple thing.
- *<p/>
- * Typically we could add a map Archive=>ArchiveInfo later.
- */
- private final Collection<ArchiveInfo> mArchives;
-
-
-
- /**
- * Create the dialog.
- * @param parentShell The shell to use, typically updaterData.getWindowShell()
- * @param updaterData The updater data
- * @param archives The archives to be installed
- */
- public SdkUpdaterChooserDialog(Shell parentShell,
- UpdaterData updaterData,
- Collection<ArchiveInfo> archives) {
- super(parentShell, 3, false/*makeColumnsEqual*/);
- mUpdaterData = updaterData;
- mArchives = archives;
- }
-
- @Override
- protected boolean isResizable() {
- return true;
- }
-
- /**
- * Returns the results, i.e. the list of selected new archives to install.
- * This is similar to the {@link ArchiveInfo} list instance given to the constructor
- * except only accepted archives are present.
- *
- * An empty list is returned if cancel was choosen.
- */
- public ArrayList<ArchiveInfo> getResult() {
- ArrayList<ArchiveInfo> ais = new ArrayList<ArchiveInfo>();
-
- if (getReturnCode() == Window.OK) {
- for (ArchiveInfo ai : mArchives) {
- if (ai.isAccepted()) {
- ais.add(ai);
- }
- }
- }
-
- return ais;
- }
-
- /**
- * Create the main content of the dialog.
- * See also {@link #createButtonBar(Composite)} below.
- */
- @Override
- public void createDialogContent(Composite parent) {
- // Sash form
- mSashForm = new SashForm(parent, SWT.NONE);
- mSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
-
-
- // Left part of Sash Form
-
- mTableGroup = new Group(mSashForm, SWT.NONE);
- mTableGroup.setText("Packages");
- mTableGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/));
-
- mTableViewPackage = new TableViewer(mTableGroup, SWT.BORDER | SWT.V_SCROLL | SWT.SINGLE);
- mTablePackage = mTableViewPackage.getTable();
- mTablePackage.setHeaderVisible(false);
- mTablePackage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
-
- mTablePackage.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onPackageSelected(); //$hide$
- }
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- onPackageDoubleClick();
- }
- });
-
- mTableColum = new TableColumn(mTablePackage, SWT.NONE);
- mTableColum.setWidth(100);
- mTableColum.setText("Packages");
-
-
- // Right part of Sash form
- mPackageRootComposite = new Composite(mSashForm, SWT.NONE);
- mPackageRootComposite.setLayout(new GridLayout(4, false/*makeColumnsEqual*/));
- mPackageRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
- mPackageTextGroup = new Group(mPackageRootComposite, SWT.NONE);
- mPackageTextGroup.setText("Package Description && License");
- mPackageTextGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
- mPackageTextGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/));
-
- mPackageText = new StyledText(mPackageTextGroup,
- SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);
- mPackageText.setBackground(
- getParentShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
- mPackageText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
-
- mLicenseRadioAccept = new Button(mPackageRootComposite, SWT.RADIO);
- mLicenseRadioAccept.setText("Accept");
- mLicenseRadioAccept.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onLicenseRadioSelected();
- }
- });
-
- mLicenseRadioReject = new Button(mPackageRootComposite, SWT.RADIO);
- mLicenseRadioReject.setText("Reject");
- mLicenseRadioReject.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onLicenseRadioSelected();
- }
- });
-
- Label placeholder = new Label(mPackageRootComposite, SWT.NONE);
- placeholder.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
-
- mLicenseRadioAcceptAll = new Button(mPackageRootComposite, SWT.RADIO);
- mLicenseRadioAcceptAll.setText("Accept All");
- mLicenseRadioAcceptAll.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onLicenseRadioSelected();
- }
- });
-
- mSashForm.setWeights(new int[] {200, 300});
- }
-
- /**
- * Creates and returns the contents of this dialog's button bar.
- * <p/>
- * This reimplements most of the code from the base class with a few exceptions:
- * <ul>
- * <li>Enforces 3 columns.
- * <li>Inserts a full-width error label.
- * <li>Inserts a help label on the left of the first button.
- * <li>Renames the OK button into "Install"
- * </ul>
- */
- @Override
- protected Control createButtonBar(Composite parent) {
- Composite composite = new Composite(parent, SWT.NONE);
- GridLayout layout = new GridLayout();
- layout.numColumns = 0; // this is incremented by createButton
- layout.makeColumnsEqualWidth = false;
- layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
- layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
- layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
- layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
- composite.setLayout(layout);
- GridData data = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
- composite.setLayoutData(data);
- composite.setFont(parent.getFont());
-
- // Error message area
- mErrorLabel = new Label(composite, SWT.NONE);
- mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
-
- // Label at the left of the install/cancel buttons
- Label label = new Label(composite, SWT.NONE);
- label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
- label.setText("[*] Something depends on this package");
- label.setEnabled(false);
- layout.numColumns++;
-
- // Add the ok/cancel to the button bar.
- createButtonsForButtonBar(composite);
-
- // the ok button should be an "install" button
- Button button = getButton(IDialogConstants.OK_ID);
- button.setText("Install");
-
- return composite;
- }
-
- // -- End of UI, Start of internal logic ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- @Override
- public void create() {
- super.create();
-
- // set window title
- getShell().setText("Choose Packages to Install");
-
- setWindowImage();
-
- // Automatically accept those with an empty license or no license
- for (ArchiveInfo ai : mArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- String license = a.getParentPackage().getLicense();
- ai.setAccepted(license == null || license.trim().length() == 0);
- }
- }
-
- // Fill the list with the replacement packages
- mTableViewPackage.setLabelProvider(new NewArchivesLabelProvider());
- mTableViewPackage.setContentProvider(new NewArchivesContentProvider());
- mTableViewPackage.setInput(mArchives);
-
- adjustColumnsWidth();
-
- // select first item
- mTablePackage.select(0);
- onPackageSelected();
- }
-
- /**
- * Creates the icon of the window shell.
- */
- private void setWindowImage() {
- String imageName = "android_icon_16.png"; //$NON-NLS-1$
- if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
- imageName = "android_icon_128.png"; //$NON-NLS-1$
- }
-
- if (mUpdaterData != null) {
- ImageFactory imgFactory = mUpdaterData.getImageFactory();
- if (imgFactory != null) {
- getShell().setImage(imgFactory.getImageByName(imageName));
- }
- }
- }
-
- /**
- * Adds a listener to adjust the columns width when the parent is resized.
- * <p/>
- * If we need something more fancy, we might want to use this:
- * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
- */
- private void adjustColumnsWidth() {
- // Add a listener to resize the column to the full width of the table
- ControlAdapter resizer = new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = mTablePackage.getClientArea();
- mTableColum.setWidth(r.width);
- }
- };
- mTablePackage.addControlListener(resizer);
- resizer.controlResized(null);
- }
-
- /**
- * Captures the window size before closing this.
- * @see #getInitialSize()
- */
- @Override
- public boolean close() {
- sLastSize = getShell().getSize();
- return super.close();
- }
-
- /**
- * Tries to reuse the last window size during this session.
- * <p/>
- * Note: the alternative would be to implement {@link #getDialogBoundsSettings()}
- * since the default {@link #getDialogBoundsStrategy()} is to persist both location
- * and size.
- */
- @Override
- protected Point getInitialSize() {
- if (sLastSize != null) {
- return sLastSize;
- } else {
- // Arbitrary values that look good on my screen and fit on 800x600
- return new Point(740, 370);
- }
- }
-
- /**
- * Callback invoked when a package item is selected in the list.
- */
- private void onPackageSelected() {
- ArchiveInfo ai = getSelectedArchive();
- displayInformation(ai);
- displayMissingDependency(ai);
- updateLicenceRadios(ai);
- }
-
- /** Returns the currently selected {@link ArchiveInfo} or null. */
- private ArchiveInfo getSelectedArchive() {
- ISelection sel = mTableViewPackage.getSelection();
- if (sel instanceof IStructuredSelection) {
- Object elem = ((IStructuredSelection) sel).getFirstElement();
- if (elem instanceof ArchiveInfo) {
- return (ArchiveInfo) elem;
- }
- }
- return null;
- }
-
- /**
- * Updates the package description and license text depending on the selected package.
- * <p/>
- * Note that right now there is no logic to support more than one level of dependencies
- * (e.g. A <- B <- C and A is disabled so C should be disabled; currently C's state depends
- * solely on B's state). We currently don't need this. It would be straightforward to add
- * if we had a need for it, though. This would require changes to {@link ArchiveInfo} and
- * {@link SdkUpdaterLogic}.
- */
- private void displayInformation(ArchiveInfo ai) {
- if (ai == null) {
- mPackageText.setText("Please select a package.");
- return;
- }
-
- Archive aNew = ai.getNewArchive();
- if (aNew == null) {
- // Only missing archives have a null archive, so we shouldn't be here.
- return;
- }
-
- Package pNew = aNew.getParentPackage();
-
- mPackageText.setText(""); //$NON-NLS-1$
-
- addSectionTitle("Package Description\n");
- addText(pNew.getLongDescription(), "\n\n"); //$NON-NLS-1$
-
- Archive aOld = ai.getReplaced();
- if (aOld != null) {
- Package pOld = aOld.getParentPackage();
-
- FullRevision rOld = pOld.getRevision();
- FullRevision rNew = pNew.getRevision();
-
- boolean showRev = true;
-
- if (pNew instanceof IAndroidVersionProvider &&
- pOld instanceof IAndroidVersionProvider) {
- AndroidVersion vOld = ((IAndroidVersionProvider) pOld).getAndroidVersion();
- AndroidVersion vNew = ((IAndroidVersionProvider) pNew).getAndroidVersion();
-
- if (!vOld.equals(vNew)) {
- // Versions are different, so indicate more than just the revision.
- addText(String.format("This update will replace API %1$s revision %2$s with API %3$s revision %4$s.\n\n",
- vOld.getApiString(), rOld.toShortString(),
- vNew.getApiString(), rNew.toShortString()));
- showRev = false;
- }
- }
-
- if (showRev) {
- addText(String.format("This update will replace revision %1$s with revision %2$s.\n\n",
- rOld.toShortString(),
- rNew.toShortString()));
- }
- }
-
- ArchiveInfo[] aDeps = ai.getDependsOn();
- if ((aDeps != null && aDeps.length > 0) || ai.isDependencyFor()) {
- addSectionTitle("Dependencies\n");
-
- if (aDeps != null && aDeps.length > 0) {
- addText("Installing this package also requires installing:");
- for (ArchiveInfo aDep : aDeps) {
- addText(String.format("\n- %1$s",
- aDep.getShortDescription()));
- }
- addText("\n\n");
- }
-
- if (ai.isDependencyFor()) {
- addText("This package is a dependency for:");
- for (ArchiveInfo ai2 : ai.getDependenciesFor()) {
- addText(String.format("\n- %1$s",
- ai2.getShortDescription()));
- }
- addText("\n\n");
- }
- }
-
- addSectionTitle("Archive Description\n");
- addText(aNew.getLongDescription(), "\n\n"); //$NON-NLS-1$
-
- String license = pNew.getLicense();
- if (license != null) {
- addSectionTitle("License\n");
- addText(license.trim(), "\n\n"); //$NON-NLS-1$
- }
-
- addSectionTitle("Site\n");
- SdkSource source = pNew.getParentSource();
- if (source != null) {
- addText(source.getShortDescription());
- }
- }
-
- /**
- * Computes and displays missing dependencies.
- *
- * If there's a selected package, check the dependency for that one.
- * Otherwise display the first missing dependency of any other package.
- */
- private void displayMissingDependency(ArchiveInfo ai) {
- String error = null;
-
- try {
- if (ai != null) {
- if (ai.isAccepted()) {
- // Case where this package is accepted but blocked by another non-accepted one
- ArchiveInfo[] adeps = ai.getDependsOn();
- if (adeps != null) {
- for (ArchiveInfo adep : adeps) {
- if (!adep.isAccepted()) {
- error = String.format("This package depends on '%1$s'.",
- adep.getShortDescription());
- return;
- }
- }
- }
- } else {
- // Case where this package blocks another one when not accepted
- for (ArchiveInfo adep : ai.getDependenciesFor()) {
- // It only matters if the blocked one is accepted
- if (adep.isAccepted()) {
- error = String.format("Package '%1$s' depends on this one.",
- adep.getShortDescription());
- return;
- }
- }
- }
- }
-
- // If there is no missing dependency on the current selection,
- // just find the first missing dependency of any other package.
- for (ArchiveInfo ai2 : mArchives) {
- if (ai2 == ai) {
- // We already processed that one above.
- continue;
- }
- if (ai2.isAccepted()) {
- // The user requested to install this package.
- // Check if all its dependencies are met.
- ArchiveInfo[] adeps = ai2.getDependsOn();
- if (adeps != null) {
- for (ArchiveInfo adep : adeps) {
- if (!adep.isAccepted()) {
- error = String.format("Package '%1$s' depends on '%2$s'",
- ai2.getShortDescription(),
- adep.getShortDescription());
- return;
- }
- }
- }
- } else {
- // The user did not request to install this package.
- // Check whether this package blocks another one when not accepted.
- for (ArchiveInfo adep : ai2.getDependenciesFor()) {
- // It only matters if the blocked one is accepted
- // or if it's a local archive that is already installed (these
- // are marked as implicitly accepted, so it's the same test.)
- if (adep.isAccepted()) {
- error = String.format("Package '%1$s' depends on '%2$s'",
- adep.getShortDescription(),
- ai2.getShortDescription());
- return;
- }
- }
- }
- }
- } finally {
- mErrorLabel.setText(error == null ? "" : error); //$NON-NLS-1$
- }
- }
-
- private void addText(String...string) {
- for (String s : string) {
- mPackageText.append(s);
- }
- }
-
- private void addSectionTitle(String string) {
- String s = mPackageText.getText();
- int start = (s == null ? 0 : s.length());
- mPackageText.append(string);
-
- StyleRange sr = new StyleRange();
- sr.start = start;
- sr.length = string.length();
- sr.fontStyle = SWT.BOLD;
- sr.underline = true;
- mPackageText.setStyleRange(sr);
- }
-
- private void updateLicenceRadios(ArchiveInfo ai) {
- if (mInternalLicenseRadioUpdate) {
- return;
- }
- mInternalLicenseRadioUpdate = true;
-
- boolean oneAccepted = false;
-
- if (mLicenseAcceptAll) {
- mLicenseRadioAcceptAll.setSelection(true);
- mLicenseRadioAccept.setEnabled(true);
- mLicenseRadioReject.setEnabled(true);
- mLicenseRadioAccept.setSelection(false);
- mLicenseRadioReject.setSelection(false);
- } else {
- mLicenseRadioAcceptAll.setSelection(false);
- oneAccepted = ai != null && ai.isAccepted();
- mLicenseRadioAccept.setEnabled(ai != null);
- mLicenseRadioReject.setEnabled(ai != null);
- mLicenseRadioAccept.setSelection(oneAccepted);
- mLicenseRadioReject.setSelection(ai != null && ai.isRejected());
- }
-
- // The install button is enabled if there's at least one package accepted.
- // If the current one isn't, look for another one.
- boolean missing = mErrorLabel.getText() != null && mErrorLabel.getText().length() > 0;
- if (!missing && !oneAccepted) {
- for(ArchiveInfo ai2 : mArchives) {
- if (ai2.isAccepted()) {
- oneAccepted = true;
- break;
- }
- }
- }
-
- getButton(IDialogConstants.OK_ID).setEnabled(!missing && oneAccepted);
-
- mInternalLicenseRadioUpdate = false;
- }
-
- /**
- * Callback invoked when one of the radio license buttons is selected.
- *
- * - accept/refuse: toggle, update item checkbox
- * - accept all: set accept-all, check all items
- */
- private void onLicenseRadioSelected() {
- if (mInternalLicenseRadioUpdate) {
- return;
- }
- mInternalLicenseRadioUpdate = true;
-
- ArchiveInfo ai = getSelectedArchive();
-
- if (ai == null) {
- // Should never happen.
- return;
- }
-
- boolean needUpdate = true;
-
- if (!mLicenseAcceptAll && mLicenseRadioAcceptAll.getSelection()) {
- // Accept all has been switched on. Mark all packages as accepted
- mLicenseAcceptAll = true;
- for(ArchiveInfo ai2 : mArchives) {
- ai2.setAccepted(true);
- ai2.setRejected(false);
- }
-
- } else if (mLicenseRadioAccept.getSelection()) {
- // Accept only this one
- mLicenseAcceptAll = false;
- ai.setAccepted(true);
- ai.setRejected(false);
-
- } else if (mLicenseRadioReject.getSelection()) {
- // Reject only this one
- mLicenseAcceptAll = false;
- ai.setAccepted(false);
- ai.setRejected(true);
-
- } else {
- needUpdate = false;
- }
-
- mInternalLicenseRadioUpdate = false;
-
- if (needUpdate) {
- if (mLicenseAcceptAll) {
- mTableViewPackage.refresh();
- } else {
- mTableViewPackage.refresh(ai);
- }
- displayMissingDependency(ai);
- updateLicenceRadios(ai);
- }
- }
-
- /**
- * Callback invoked when a package item is double-clicked in the list.
- */
- private void onPackageDoubleClick() {
- ArchiveInfo ai = getSelectedArchive();
-
- if (ai == null) {
- // Should never happen.
- return;
- }
-
- boolean wasAccepted = ai.isAccepted();
- ai.setAccepted(!wasAccepted);
- ai.setRejected(wasAccepted);
-
- // update state
- mLicenseAcceptAll = false;
- mTableViewPackage.refresh(ai);
- displayMissingDependency(ai);
- updateLicenceRadios(ai);
- }
-
- private class NewArchivesLabelProvider extends LabelProvider {
- @Override
- public Image getImage(Object element) {
- assert element instanceof ArchiveInfo;
- ArchiveInfo ai = (ArchiveInfo) element;
-
- ImageFactory imgFactory = mUpdaterData.getImageFactory();
- if (imgFactory != null) {
- if (ai.isAccepted()) {
- return imgFactory.getImageByName("accept_icon16.png");
- } else if (ai.isRejected()) {
- return imgFactory.getImageByName("reject_icon16.png");
- }
- return imgFactory.getImageByName("unknown_icon16.png");
- }
- return super.getImage(element);
- }
-
- @Override
- public String getText(Object element) {
- assert element instanceof ArchiveInfo;
- ArchiveInfo ai = (ArchiveInfo) element;
-
- String desc = ai.getShortDescription();
-
- if (ai.isDependencyFor()) {
- desc += " [*]";
- }
-
- return desc;
- }
- }
-
- private class NewArchivesContentProvider implements IStructuredContentProvider {
-
- @Override
- public void dispose() {
- // pass
- }
-
- @Override
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- // Ignore. The input is always mArchives
- }
-
- @Override
- public Object[] getElements(Object inputElement) {
- return mArchives.toArray();
- }
- }
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/* + * 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.sdkuilib.internal.repository; + +import com.android.sdklib.AndroidVersion; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.repository.archives.Archive; +import com.android.sdklib.internal.repository.packages.IAndroidVersionProvider; +import com.android.sdklib.internal.repository.packages.Package; +import com.android.sdklib.internal.repository.packages.FullRevision; +import com.android.sdklib.internal.repository.sources.SdkSource; +import com.android.sdkuilib.internal.repository.icons.ImageFactory; +import com.android.sdkuilib.ui.GridDialog; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +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.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +import java.util.ArrayList; +import java.util.Collection; + + +/** + * Implements an {@link SdkUpdaterChooserDialog}. + */ +final class SdkUpdaterChooserDialog extends GridDialog { + + /** Last dialog size for this session. */ + private static Point sLastSize; + private boolean mLicenseAcceptAll; + private boolean mInternalLicenseRadioUpdate; + + // UI fields + private SashForm mSashForm; + private Composite mPackageRootComposite; + private TableViewer mTableViewPackage; + private Table mTablePackage; + private TableColumn mTableColum; + private StyledText mPackageText; + private Button mLicenseRadioAccept; + private Button mLicenseRadioReject; + private Button mLicenseRadioAcceptAll; + private Group mPackageTextGroup; + private final UpdaterData mUpdaterData; + private Group mTableGroup; + private Label mErrorLabel; + + /** + * List of all archives to be installed with dependency information. + * <p/> + * Note: in a lot of cases, we need to find the archive info for a given archive. This + * is currently done using a simple linear search, which is fine since we only have a very + * limited number of archives to deal with (e.g. < 10 now). We might want to revisit + * this later if it becomes an issue. Right now just do the simple thing. + *<p/> + * Typically we could add a map Archive=>ArchiveInfo later. + */ + private final Collection<ArchiveInfo> mArchives; + + + + /** + * Create the dialog. + * @param parentShell The shell to use, typically updaterData.getWindowShell() + * @param updaterData The updater data + * @param archives The archives to be installed + */ + public SdkUpdaterChooserDialog(Shell parentShell, + UpdaterData updaterData, + Collection<ArchiveInfo> archives) { + super(parentShell, 3, false/*makeColumnsEqual*/); + mUpdaterData = updaterData; + mArchives = archives; + } + + @Override + protected boolean isResizable() { + return true; + } + + /** + * Returns the results, i.e. the list of selected new archives to install. + * This is similar to the {@link ArchiveInfo} list instance given to the constructor + * except only accepted archives are present. + * + * An empty list is returned if cancel was choosen. + */ + public ArrayList<ArchiveInfo> getResult() { + ArrayList<ArchiveInfo> ais = new ArrayList<ArchiveInfo>(); + + if (getReturnCode() == Window.OK) { + for (ArchiveInfo ai : mArchives) { + if (ai.isAccepted()) { + ais.add(ai); + } + } + } + + return ais; + } + + /** + * Create the main content of the dialog. + * See also {@link #createButtonBar(Composite)} below. + */ + @Override + public void createDialogContent(Composite parent) { + // Sash form + mSashForm = new SashForm(parent, SWT.NONE); + mSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1)); + + + // Left part of Sash Form + + mTableGroup = new Group(mSashForm, SWT.NONE); + mTableGroup.setText("Packages"); + mTableGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/)); + + mTableViewPackage = new TableViewer(mTableGroup, SWT.BORDER | SWT.V_SCROLL | SWT.SINGLE); + mTablePackage = mTableViewPackage.getTable(); + mTablePackage.setHeaderVisible(false); + mTablePackage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + mTablePackage.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onPackageSelected(); //$hide$ + } + @Override + public void widgetDefaultSelected(SelectionEvent e) { + onPackageDoubleClick(); + } + }); + + mTableColum = new TableColumn(mTablePackage, SWT.NONE); + mTableColum.setWidth(100); + mTableColum.setText("Packages"); + + + // Right part of Sash form + mPackageRootComposite = new Composite(mSashForm, SWT.NONE); + mPackageRootComposite.setLayout(new GridLayout(4, false/*makeColumnsEqual*/)); + mPackageRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + mPackageTextGroup = new Group(mPackageRootComposite, SWT.NONE); + mPackageTextGroup.setText("Package Description && License"); + mPackageTextGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1)); + mPackageTextGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/)); + + mPackageText = new StyledText(mPackageTextGroup, + SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL); + mPackageText.setBackground( + getParentShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); + mPackageText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + mLicenseRadioAccept = new Button(mPackageRootComposite, SWT.RADIO); + mLicenseRadioAccept.setText("Accept"); + mLicenseRadioAccept.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onLicenseRadioSelected(); + } + }); + + mLicenseRadioReject = new Button(mPackageRootComposite, SWT.RADIO); + mLicenseRadioReject.setText("Reject"); + mLicenseRadioReject.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onLicenseRadioSelected(); + } + }); + + Label placeholder = new Label(mPackageRootComposite, SWT.NONE); + placeholder.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1)); + + mLicenseRadioAcceptAll = new Button(mPackageRootComposite, SWT.RADIO); + mLicenseRadioAcceptAll.setText("Accept All"); + mLicenseRadioAcceptAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onLicenseRadioSelected(); + } + }); + + mSashForm.setWeights(new int[] {200, 300}); + } + + /** + * Creates and returns the contents of this dialog's button bar. + * <p/> + * This reimplements most of the code from the base class with a few exceptions: + * <ul> + * <li>Enforces 3 columns. + * <li>Inserts a full-width error label. + * <li>Inserts a help label on the left of the first button. + * <li>Renames the OK button into "Install" + * </ul> + */ + @Override + protected Control createButtonBar(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = 0; // this is incremented by createButton + layout.makeColumnsEqualWidth = false; + layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + composite.setLayout(layout); + GridData data = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1); + composite.setLayoutData(data); + composite.setFont(parent.getFont()); + + // Error message area + mErrorLabel = new Label(composite, SWT.NONE); + mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); + + // Label at the left of the install/cancel buttons + Label label = new Label(composite, SWT.NONE); + label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + label.setText("[*] Something depends on this package"); + label.setEnabled(false); + layout.numColumns++; + + // Add the ok/cancel to the button bar. + createButtonsForButtonBar(composite); + + // the ok button should be an "install" button + Button button = getButton(IDialogConstants.OK_ID); + button.setText("Install"); + + return composite; + } + + // -- End of UI, Start of internal logic ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + @Override + public void create() { + super.create(); + + // set window title + getShell().setText("Choose Packages to Install"); + + setWindowImage(); + + // Automatically accept those with an empty license or no license + for (ArchiveInfo ai : mArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + String license = a.getParentPackage().getLicense(); + ai.setAccepted(license == null || license.trim().length() == 0); + } + } + + // Fill the list with the replacement packages + mTableViewPackage.setLabelProvider(new NewArchivesLabelProvider()); + mTableViewPackage.setContentProvider(new NewArchivesContentProvider()); + mTableViewPackage.setInput(mArchives); + + adjustColumnsWidth(); + + // select first item + mTablePackage.select(0); + onPackageSelected(); + } + + /** + * Creates the icon of the window shell. + */ + private void setWindowImage() { + String imageName = "android_icon_16.png"; //$NON-NLS-1$ + if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) { + imageName = "android_icon_128.png"; //$NON-NLS-1$ + } + + if (mUpdaterData != null) { + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (imgFactory != null) { + getShell().setImage(imgFactory.getImageByName(imageName)); + } + } + } + + /** + * Adds a listener to adjust the columns width when the parent is resized. + * <p/> + * If we need something more fancy, we might want to use this: + * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co + */ + private void adjustColumnsWidth() { + // Add a listener to resize the column to the full width of the table + ControlAdapter resizer = new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + Rectangle r = mTablePackage.getClientArea(); + mTableColum.setWidth(r.width); + } + }; + mTablePackage.addControlListener(resizer); + resizer.controlResized(null); + } + + /** + * Captures the window size before closing this. + * @see #getInitialSize() + */ + @Override + public boolean close() { + sLastSize = getShell().getSize(); + return super.close(); + } + + /** + * Tries to reuse the last window size during this session. + * <p/> + * Note: the alternative would be to implement {@link #getDialogBoundsSettings()} + * since the default {@link #getDialogBoundsStrategy()} is to persist both location + * and size. + */ + @Override + protected Point getInitialSize() { + if (sLastSize != null) { + return sLastSize; + } else { + // Arbitrary values that look good on my screen and fit on 800x600 + return new Point(740, 370); + } + } + + /** + * Callback invoked when a package item is selected in the list. + */ + private void onPackageSelected() { + ArchiveInfo ai = getSelectedArchive(); + displayInformation(ai); + displayMissingDependency(ai); + updateLicenceRadios(ai); + } + + /** Returns the currently selected {@link ArchiveInfo} or null. */ + private ArchiveInfo getSelectedArchive() { + ISelection sel = mTableViewPackage.getSelection(); + if (sel instanceof IStructuredSelection) { + Object elem = ((IStructuredSelection) sel).getFirstElement(); + if (elem instanceof ArchiveInfo) { + return (ArchiveInfo) elem; + } + } + return null; + } + + /** + * Updates the package description and license text depending on the selected package. + * <p/> + * Note that right now there is no logic to support more than one level of dependencies + * (e.g. A <- B <- C and A is disabled so C should be disabled; currently C's state depends + * solely on B's state). We currently don't need this. It would be straightforward to add + * if we had a need for it, though. This would require changes to {@link ArchiveInfo} and + * {@link SdkUpdaterLogic}. + */ + private void displayInformation(ArchiveInfo ai) { + if (ai == null) { + mPackageText.setText("Please select a package."); + return; + } + + Archive aNew = ai.getNewArchive(); + if (aNew == null) { + // Only missing archives have a null archive, so we shouldn't be here. + return; + } + + Package pNew = aNew.getParentPackage(); + + mPackageText.setText(""); //$NON-NLS-1$ + + addSectionTitle("Package Description\n"); + addText(pNew.getLongDescription(), "\n\n"); //$NON-NLS-1$ + + Archive aOld = ai.getReplaced(); + if (aOld != null) { + Package pOld = aOld.getParentPackage(); + + FullRevision rOld = pOld.getRevision(); + FullRevision rNew = pNew.getRevision(); + + boolean showRev = true; + + if (pNew instanceof IAndroidVersionProvider && + pOld instanceof IAndroidVersionProvider) { + AndroidVersion vOld = ((IAndroidVersionProvider) pOld).getAndroidVersion(); + AndroidVersion vNew = ((IAndroidVersionProvider) pNew).getAndroidVersion(); + + if (!vOld.equals(vNew)) { + // Versions are different, so indicate more than just the revision. + addText(String.format("This update will replace API %1$s revision %2$s with API %3$s revision %4$s.\n\n", + vOld.getApiString(), rOld.toShortString(), + vNew.getApiString(), rNew.toShortString())); + showRev = false; + } + } + + if (showRev) { + addText(String.format("This update will replace revision %1$s with revision %2$s.\n\n", + rOld.toShortString(), + rNew.toShortString())); + } + } + + ArchiveInfo[] aDeps = ai.getDependsOn(); + if ((aDeps != null && aDeps.length > 0) || ai.isDependencyFor()) { + addSectionTitle("Dependencies\n"); + + if (aDeps != null && aDeps.length > 0) { + addText("Installing this package also requires installing:"); + for (ArchiveInfo aDep : aDeps) { + addText(String.format("\n- %1$s", + aDep.getShortDescription())); + } + addText("\n\n"); + } + + if (ai.isDependencyFor()) { + addText("This package is a dependency for:"); + for (ArchiveInfo ai2 : ai.getDependenciesFor()) { + addText(String.format("\n- %1$s", + ai2.getShortDescription())); + } + addText("\n\n"); + } + } + + addSectionTitle("Archive Description\n"); + addText(aNew.getLongDescription(), "\n\n"); //$NON-NLS-1$ + + String license = pNew.getLicense(); + if (license != null) { + addSectionTitle("License\n"); + addText(license.trim(), "\n\n"); //$NON-NLS-1$ + } + + addSectionTitle("Site\n"); + SdkSource source = pNew.getParentSource(); + if (source != null) { + addText(source.getShortDescription()); + } + } + + /** + * Computes and displays missing dependencies. + * + * If there's a selected package, check the dependency for that one. + * Otherwise display the first missing dependency of any other package. + */ + private void displayMissingDependency(ArchiveInfo ai) { + String error = null; + + try { + if (ai != null) { + if (ai.isAccepted()) { + // Case where this package is accepted but blocked by another non-accepted one + ArchiveInfo[] adeps = ai.getDependsOn(); + if (adeps != null) { + for (ArchiveInfo adep : adeps) { + if (!adep.isAccepted()) { + error = String.format("This package depends on '%1$s'.", + adep.getShortDescription()); + return; + } + } + } + } else { + // Case where this package blocks another one when not accepted + for (ArchiveInfo adep : ai.getDependenciesFor()) { + // It only matters if the blocked one is accepted + if (adep.isAccepted()) { + error = String.format("Package '%1$s' depends on this one.", + adep.getShortDescription()); + return; + } + } + } + } + + // If there is no missing dependency on the current selection, + // just find the first missing dependency of any other package. + for (ArchiveInfo ai2 : mArchives) { + if (ai2 == ai) { + // We already processed that one above. + continue; + } + if (ai2.isAccepted()) { + // The user requested to install this package. + // Check if all its dependencies are met. + ArchiveInfo[] adeps = ai2.getDependsOn(); + if (adeps != null) { + for (ArchiveInfo adep : adeps) { + if (!adep.isAccepted()) { + error = String.format("Package '%1$s' depends on '%2$s'", + ai2.getShortDescription(), + adep.getShortDescription()); + return; + } + } + } + } else { + // The user did not request to install this package. + // Check whether this package blocks another one when not accepted. + for (ArchiveInfo adep : ai2.getDependenciesFor()) { + // It only matters if the blocked one is accepted + // or if it's a local archive that is already installed (these + // are marked as implicitly accepted, so it's the same test.) + if (adep.isAccepted()) { + error = String.format("Package '%1$s' depends on '%2$s'", + adep.getShortDescription(), + ai2.getShortDescription()); + return; + } + } + } + } + } finally { + mErrorLabel.setText(error == null ? "" : error); //$NON-NLS-1$ + } + } + + private void addText(String...string) { + for (String s : string) { + mPackageText.append(s); + } + } + + private void addSectionTitle(String string) { + String s = mPackageText.getText(); + int start = (s == null ? 0 : s.length()); + mPackageText.append(string); + + StyleRange sr = new StyleRange(); + sr.start = start; + sr.length = string.length(); + sr.fontStyle = SWT.BOLD; + sr.underline = true; + mPackageText.setStyleRange(sr); + } + + private void updateLicenceRadios(ArchiveInfo ai) { + if (mInternalLicenseRadioUpdate) { + return; + } + mInternalLicenseRadioUpdate = true; + + boolean oneAccepted = false; + + if (mLicenseAcceptAll) { + mLicenseRadioAcceptAll.setSelection(true); + mLicenseRadioAccept.setEnabled(true); + mLicenseRadioReject.setEnabled(true); + mLicenseRadioAccept.setSelection(false); + mLicenseRadioReject.setSelection(false); + } else { + mLicenseRadioAcceptAll.setSelection(false); + oneAccepted = ai != null && ai.isAccepted(); + mLicenseRadioAccept.setEnabled(ai != null); + mLicenseRadioReject.setEnabled(ai != null); + mLicenseRadioAccept.setSelection(oneAccepted); + mLicenseRadioReject.setSelection(ai != null && ai.isRejected()); + } + + // The install button is enabled if there's at least one package accepted. + // If the current one isn't, look for another one. + boolean missing = mErrorLabel.getText() != null && mErrorLabel.getText().length() > 0; + if (!missing && !oneAccepted) { + for(ArchiveInfo ai2 : mArchives) { + if (ai2.isAccepted()) { + oneAccepted = true; + break; + } + } + } + + getButton(IDialogConstants.OK_ID).setEnabled(!missing && oneAccepted); + + mInternalLicenseRadioUpdate = false; + } + + /** + * Callback invoked when one of the radio license buttons is selected. + * + * - accept/refuse: toggle, update item checkbox + * - accept all: set accept-all, check all items + */ + private void onLicenseRadioSelected() { + if (mInternalLicenseRadioUpdate) { + return; + } + mInternalLicenseRadioUpdate = true; + + ArchiveInfo ai = getSelectedArchive(); + + if (ai == null) { + // Should never happen. + return; + } + + boolean needUpdate = true; + + if (!mLicenseAcceptAll && mLicenseRadioAcceptAll.getSelection()) { + // Accept all has been switched on. Mark all packages as accepted + mLicenseAcceptAll = true; + for(ArchiveInfo ai2 : mArchives) { + ai2.setAccepted(true); + ai2.setRejected(false); + } + + } else if (mLicenseRadioAccept.getSelection()) { + // Accept only this one + mLicenseAcceptAll = false; + ai.setAccepted(true); + ai.setRejected(false); + + } else if (mLicenseRadioReject.getSelection()) { + // Reject only this one + mLicenseAcceptAll = false; + ai.setAccepted(false); + ai.setRejected(true); + + } else { + needUpdate = false; + } + + mInternalLicenseRadioUpdate = false; + + if (needUpdate) { + if (mLicenseAcceptAll) { + mTableViewPackage.refresh(); + } else { + mTableViewPackage.refresh(ai); + } + displayMissingDependency(ai); + updateLicenceRadios(ai); + } + } + + /** + * Callback invoked when a package item is double-clicked in the list. + */ + private void onPackageDoubleClick() { + ArchiveInfo ai = getSelectedArchive(); + + if (ai == null) { + // Should never happen. + return; + } + + boolean wasAccepted = ai.isAccepted(); + ai.setAccepted(!wasAccepted); + ai.setRejected(wasAccepted); + + // update state + mLicenseAcceptAll = false; + mTableViewPackage.refresh(ai); + displayMissingDependency(ai); + updateLicenceRadios(ai); + } + + private class NewArchivesLabelProvider extends LabelProvider { + @Override + public Image getImage(Object element) { + assert element instanceof ArchiveInfo; + ArchiveInfo ai = (ArchiveInfo) element; + + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (imgFactory != null) { + if (ai.isAccepted()) { + return imgFactory.getImageByName("accept_icon16.png"); + } else if (ai.isRejected()) { + return imgFactory.getImageByName("reject_icon16.png"); + } + return imgFactory.getImageByName("unknown_icon16.png"); + } + return super.getImage(element); + } + + @Override + public String getText(Object element) { + assert element instanceof ArchiveInfo; + ArchiveInfo ai = (ArchiveInfo) element; + + String desc = ai.getShortDescription(); + + if (ai.isDependencyFor()) { + desc += " [*]"; + } + + return desc; + } + } + + private class NewArchivesContentProvider implements IStructuredContentProvider { + + @Override + public void dispose() { + // pass + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Ignore. The input is always mArchives + } + + @Override + public Object[] getElements(Object inputElement) { + return mArchives.toArray(); + } + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterLogic.java index 62a16d1..da71115 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterLogic.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterLogic.java @@ -1,1435 +1,1435 @@ -/*
- * 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.sdkuilib.internal.repository;
-
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.internal.repository.ITask;
-import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.internal.repository.archives.Archive;
-import com.android.sdklib.internal.repository.packages.AddonPackage;
-import com.android.sdklib.internal.repository.packages.DocPackage;
-import com.android.sdklib.internal.repository.packages.ExtraPackage;
-import com.android.sdklib.internal.repository.packages.FullRevision;
-import com.android.sdklib.internal.repository.packages.IAndroidVersionProvider;
-import com.android.sdklib.internal.repository.packages.IExactApiLevelDependency;
-import com.android.sdklib.internal.repository.packages.IMinApiLevelDependency;
-import com.android.sdklib.internal.repository.packages.IMinPlatformToolsDependency;
-import com.android.sdklib.internal.repository.packages.IMinToolsDependency;
-import com.android.sdklib.internal.repository.packages.IPlatformDependency;
-import com.android.sdklib.internal.repository.packages.MinToolsPackage;
-import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.packages.Package.UpdateInfo;
-import com.android.sdklib.internal.repository.packages.PlatformPackage;
-import com.android.sdklib.internal.repository.packages.PlatformToolPackage;
-import com.android.sdklib.internal.repository.packages.SamplePackage;
-import com.android.sdklib.internal.repository.packages.SystemImagePackage;
-import com.android.sdklib.internal.repository.packages.ToolPackage;
-import com.android.sdklib.internal.repository.sources.SdkSource;
-import com.android.sdklib.internal.repository.sources.SdkSources;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The logic to compute which packages to install, based on the choices
- * made by the user. This adds required packages as needed.
- * <p/>
- * When the user doesn't provide a selection, looks at local package to find
- * those that can be updated and compute dependencies too.
- */
-class SdkUpdaterLogic {
-
- private final IUpdaterData mUpdaterData;
-
- public SdkUpdaterLogic(IUpdaterData updaterData) {
- mUpdaterData = updaterData;
- }
-
- /**
- * Retrieves an unfiltered list of all remote archives.
- * The archives are guaranteed to be compatible with the current platform.
- */
- public List<ArchiveInfo> getAllRemoteArchives(
- SdkSources sources,
- Package[] localPkgs,
- boolean includeAll) {
-
- List<Package> remotePkgs = new ArrayList<Package>();
- SdkSource[] remoteSources = sources.getAllSources();
- fetchRemotePackages(remotePkgs, remoteSources);
-
- ArrayList<Archive> archives = new ArrayList<Archive>();
- for (Package remotePkg : remotePkgs) {
- // Only look for non-obsolete updates unless requested to include them
- if (includeAll || !remotePkg.isObsolete()) {
- // Found a suitable update. Only accept the remote package
- // if it provides at least one compatible archive
-
- addArchives:
- for (Archive a : remotePkg.getArchives()) {
- if (a.isCompatible()) {
-
- // If we're trying to add a package for revision N,
- // make sure we don't also have a package for revision N-1.
- for (int i = archives.size() - 1; i >= 0; i--) {
- Package pkgFound = archives.get(i).getParentPackage();
- if (pkgFound.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
- // This package can update one we selected earlier.
- // Remove the one that can be updated by this new one.
- archives.remove(i);
- } else if (remotePkg.canBeUpdatedBy(pkgFound) == UpdateInfo.UPDATE) {
- // There is a package in the list that is already better
- // than the one we want to add, so don't add it.
- break addArchives;
- }
- }
-
- archives.add(a);
- break;
- }
- }
- }
- }
-
- ArrayList<ArchiveInfo> result = new ArrayList<ArchiveInfo>();
-
- ArchiveInfo[] localArchives = createLocalArchives(localPkgs);
-
- for (Archive a : archives) {
- insertArchive(a,
- result,
- archives,
- remotePkgs,
- remoteSources,
- localArchives,
- false /*automated*/);
- }
-
- return result;
- }
-
- /**
- * Compute which packages to install by taking the user selection
- * and adding required packages as needed.
- *
- * When the user doesn't provide a selection, looks at local packages to find
- * those that can be updated and compute dependencies too.
- */
- public List<ArchiveInfo> computeUpdates(
- Collection<Archive> selectedArchives,
- SdkSources sources,
- Package[] localPkgs,
- boolean includeAll) {
-
- List<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();
- List<Package> remotePkgs = new ArrayList<Package>();
- SdkSource[] remoteSources = sources.getAllSources();
-
- // Create ArchiveInfos out of local (installed) packages.
- ArchiveInfo[] localArchives = createLocalArchives(localPkgs);
-
- // If we do not have a specific list of archives to install (that is the user
- // selected "update all" rather than request specific packages), then we try to
- // find updates based on the *existing* packages.
- if (selectedArchives == null) {
- selectedArchives = findUpdates(
- localArchives,
- remotePkgs,
- remoteSources,
- includeAll);
- }
-
- // Once we have a list of packages to install, we try to solve all their
- // dependencies by automatically adding them to the list of things to install.
- // This works on the list provided either by the user directly or the list
- // computed from potential updates.
- for (Archive a : selectedArchives) {
- insertArchive(a,
- archives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- false /*automated*/);
- }
-
- // Finally we need to look at *existing* packages which are not being updated
- // and check if they have any missing dependencies and suggest how to fix
- // these dependencies.
- fixMissingLocalDependencies(
- archives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- return archives;
- }
-
- private double getRevisionRank(FullRevision rev) {
- int p = rev.isPreview() ? 999 : 999 - rev.getPreview();
- return rev.getMajor() +
- rev.getMinor() / 1000.d +
- rev.getMicro() / 1000000.d +
- p / 1000000000.d;
- }
-
- /**
- * Finds new packages that the user does not have in his/her local SDK
- * and adds them to the list of archives to install.
- * <p/>
- * The default is to only find "new" platforms, that is anything more
- * recent than the highest platform currently installed.
- * A side effect is that for an empty SDK install this will list *all*
- * platforms available (since there's no "highest" installed platform.)
- *
- * @param archives The in-out list of archives to install. Typically the
- * list is not empty at first as it should contain any archives that is
- * already scheduled for install. This method will add to the list.
- * @param sources The list of all sources, to fetch them as necessary.
- * @param localPkgs The list of all currently installed packages.
- * @param includeAll When true, this will list all platforms.
- * (included these lower than the highest installed one) as well as
- * all obsolete packages of these platforms.
- */
- public void addNewPlatforms(
- Collection<ArchiveInfo> archives,
- SdkSources sources,
- Package[] localPkgs,
- boolean includeAll) {
-
- // Create ArchiveInfos out of local (installed) packages.
- ArchiveInfo[] localArchives = createLocalArchives(localPkgs);
-
- // Find the highest platform installed
- double currentPlatformScore = 0;
- double currentSampleScore = 0;
- double currentAddonScore = 0;
- double currentDocScore = 0;
- HashMap<String, Double> currentExtraScore = new HashMap<String, Double>();
- if (!includeAll) {
- if (localPkgs != null) {
- for (Package p : localPkgs) {
- double rev = getRevisionRank(p.getRevision());
- int api = 0;
- boolean isPreview = false;
- if (p instanceof IAndroidVersionProvider) {
- AndroidVersion vers = ((IAndroidVersionProvider) p).getAndroidVersion();
- api = vers.getApiLevel();
- isPreview = vers.isPreview();
- }
-
- // The score is 1000*api + (999 if preview) + rev
- // This allows previews to rank above a non-preview and
- // allows revisions to rank appropriately.
- double score = api * 1000 + (isPreview ? 999 : 0) + rev;
-
- if (p instanceof PlatformPackage) {
- currentPlatformScore = Math.max(currentPlatformScore, score);
- } else if (p instanceof SamplePackage) {
- currentSampleScore = Math.max(currentSampleScore, score);
- } else if (p instanceof AddonPackage) {
- currentAddonScore = Math.max(currentAddonScore, score);
- } else if (p instanceof ExtraPackage) {
- currentExtraScore.put(((ExtraPackage) p).getPath(), score);
- } else if (p instanceof DocPackage) {
- currentDocScore = Math.max(currentDocScore, score);
- }
- }
- }
- }
-
- SdkSource[] remoteSources = sources.getAllSources();
- ArrayList<Package> remotePkgs = new ArrayList<Package>();
- fetchRemotePackages(remotePkgs, remoteSources);
-
- Package suggestedDoc = null;
-
- for (Package p : remotePkgs) {
- // Skip obsolete packages unless requested to include them.
- if (p.isObsolete() && !includeAll) {
- continue;
- }
-
- double rev = getRevisionRank(p.getRevision());
- int api = 0;
- boolean isPreview = false;
- if (p instanceof IAndroidVersionProvider) {
- AndroidVersion vers = ((IAndroidVersionProvider) p).getAndroidVersion();
- api = vers.getApiLevel();
- isPreview = vers.isPreview();
- }
-
- double score = api * 1000 + (isPreview ? 999 : 0) + rev;
-
- boolean shouldAdd = false;
- if (p instanceof PlatformPackage) {
- shouldAdd = score > currentPlatformScore;
- } else if (p instanceof SamplePackage) {
- shouldAdd = score > currentSampleScore;
- } else if (p instanceof AddonPackage) {
- shouldAdd = score > currentAddonScore;
- } else if (p instanceof ExtraPackage) {
- String key = ((ExtraPackage) p).getPath();
- shouldAdd = !currentExtraScore.containsKey(key) ||
- score > currentExtraScore.get(key).doubleValue();
- } else if (p instanceof DocPackage) {
- // We don't want all the doc, only the most recent one
- if (score > currentDocScore) {
- suggestedDoc = p;
- currentDocScore = score;
- }
- }
-
- if (shouldAdd) {
- // We should suggest this package for installation.
- for (Archive a : p.getArchives()) {
- if (a.isCompatible()) {
- insertArchive(a,
- archives,
- null /*selectedArchives*/,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
-
- if (p instanceof PlatformPackage && (score >= currentPlatformScore)) {
- // We just added a new platform *or* we are visiting the highest currently
- // installed platform. In either case we want to make sure it either has
- // its own system image or that we provide one by default.
- PlatformPackage pp = (PlatformPackage) p;
- if (pp.getIncludedAbi() == null) {
- for (Package p2 : remotePkgs) {
- if (!(p2 instanceof SystemImagePackage) ||
- (p2.isObsolete() && !includeAll)) {
- continue;
- }
- SystemImagePackage sip = (SystemImagePackage) p2;
- if (sip.getAndroidVersion().equals(pp.getAndroidVersion())) {
- for (Archive a : sip.getArchives()) {
- if (a.isCompatible()) {
- insertArchive(a,
- archives,
- null /*selectedArchives*/,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
- }
- }
- }
-
- if (suggestedDoc != null) {
- // We should suggest this package for installation.
- for (Archive a : suggestedDoc.getArchives()) {
- if (a.isCompatible()) {
- insertArchive(a,
- archives,
- null /*selectedArchives*/,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
-
- /**
- * Create a array of {@link ArchiveInfo} based on all local (already installed)
- * packages. The array is always non-null but may be empty.
- * <p/>
- * The local {@link ArchiveInfo} are guaranteed to have one non-null archive
- * that you can retrieve using {@link ArchiveInfo#getNewArchive()}.
- */
- protected ArchiveInfo[] createLocalArchives(Package[] localPkgs) {
-
- if (localPkgs != null) {
- ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();
- for (Package p : localPkgs) {
- // Only accept packages that have one compatible archive.
- // Local package should have 1 and only 1 compatible archive anyway.
- for (Archive a : p.getArchives()) {
- if (a != null && a.isCompatible()) {
- // We create an "installed" archive info to wrap the local package.
- // Note that dependencies are not computed since right now we don't
- // deal with more than one level of dependencies and installed archives
- // are deemed implicitly accepted anyway.
- list.add(new LocalArchiveInfo(a));
- }
- }
- }
-
- return list.toArray(new ArchiveInfo[list.size()]);
- }
-
- return new ArchiveInfo[0];
- }
-
- /**
- * Find suitable updates to all current local packages.
- * <p/>
- * Returns a list of potential updates for *existing* packages. This does NOT solve
- * dependencies for the new packages.
- * <p/>
- * Always returns a non-null collection, which can be empty.
- */
- private Collection<Archive> findUpdates(
- ArchiveInfo[] localArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- boolean includeAll) {
- ArrayList<Archive> updates = new ArrayList<Archive>();
-
- fetchRemotePackages(remotePkgs, remoteSources);
-
- for (ArchiveInfo ai : localArchives) {
- Archive na = ai.getNewArchive();
- if (na == null) {
- continue;
- }
- Package localPkg = na.getParentPackage();
-
- for (Package remotePkg : remotePkgs) {
- // Only look for non-obsolete updates unless requested to include them
- if ((includeAll || !remotePkg.isObsolete()) &&
- localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
- // Found a suitable update. Only accept the remote package
- // if it provides at least one compatible archive
-
- addArchives:
- for (Archive a : remotePkg.getArchives()) {
- if (a.isCompatible()) {
-
- // If we're trying to add a package for revision N,
- // make sure we don't also have a package for revision N-1.
- for (int i = updates.size() - 1; i >= 0; i--) {
- Package pkgFound = updates.get(i).getParentPackage();
- if (pkgFound.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
- // This package can update one we selected earlier.
- // Remove the one that can be updated by this new one.
- updates.remove(i);
- } else if (remotePkg.canBeUpdatedBy(pkgFound) ==
- UpdateInfo.UPDATE) {
- // There is a package in the list that is already better
- // than the one we want to add, so don't add it.
- break addArchives;
- }
- }
-
- updates.add(a);
- break;
- }
- }
- }
- }
- }
-
- return updates;
- }
-
- /**
- * Check all local archives which are NOT being updated and see if they
- * miss any dependency. If they do, try to fix that dependency by selecting
- * an appropriate package.
- */
- private void fixMissingLocalDependencies(
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
-
- nextLocalArchive: for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- Package p = a == null ? null : a.getParentPackage();
- if (p == null) {
- continue;
- }
-
- // Is this local archive being updated?
- for (ArchiveInfo ai2 : outArchives) {
- if (ai2.getReplaced() == a) {
- // this new archive will replace the current local one,
- // so we don't have to care about fixing dependencies (since the
- // new archive should already have had its dependencies resolved)
- continue nextLocalArchive;
- }
- }
-
- // find dependencies for the local archive and add them as needed
- // to the outArchives collection.
- ArchiveInfo[] deps = findDependency(p,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- if (deps != null) {
- // The already installed archive has a missing dependency, which we
- // just selected for install. Make sure we remember the dependency
- // so that we can enforce it later in the UI.
- for (ArchiveInfo aid : deps) {
- aid.addDependencyFor(ai);
- }
- }
- }
- }
-
- private ArchiveInfo insertArchive(Archive archive,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives,
- boolean automated) {
- Package p = archive.getParentPackage();
-
- // Is this an update?
- Archive updatedArchive = null;
- for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package lp = a.getParentPackage();
-
- if (lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {
- updatedArchive = a;
- }
- }
- }
-
- // Find dependencies and adds them as needed to outArchives
- ArchiveInfo[] deps = findDependency(p,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- // Make sure it's not a dup
- ArchiveInfo ai = null;
-
- for (ArchiveInfo ai2 : outArchives) {
- Archive a2 = ai2.getNewArchive();
- if (a2 != null && a2.getParentPackage().sameItemAs(archive.getParentPackage())) {
- ai = ai2;
- break;
- }
- }
-
- if (ai == null) {
- ai = new ArchiveInfo(
- archive, //newArchive
- updatedArchive, //replaced
- deps //dependsOn
- );
- outArchives.add(ai);
- }
-
- if (deps != null) {
- for (ArchiveInfo d : deps) {
- d.addDependencyFor(ai);
- }
- }
-
- return ai;
- }
-
- /**
- * Resolves dependencies for a given package.
- *
- * Returns null if no dependencies were found.
- * Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have
- * at least size 1 and contain no null elements.
- */
- private ArchiveInfo[] findDependency(Package pkg,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
-
- // Current dependencies can be:
- // - addon: *always* depends on platform of same API level
- // - platform: *might* depends on tools of rev >= min-tools-rev
- // - extra: *might* depends on platform with api >= min-api-level
-
- Set<ArchiveInfo> aiFound = new HashSet<ArchiveInfo>();
-
- if (pkg instanceof IPlatformDependency) {
- ArchiveInfo ai = findPlatformDependency(
- (IPlatformDependency) pkg,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- if (ai != null) {
- aiFound.add(ai);
- }
- }
-
- if (pkg instanceof IMinToolsDependency) {
-
- ArchiveInfo ai = findToolsDependency(
- (IMinToolsDependency) pkg,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- if (ai != null) {
- aiFound.add(ai);
- }
- }
-
- if (pkg instanceof IMinPlatformToolsDependency) {
-
- ArchiveInfo ai = findPlatformToolsDependency(
- (IMinPlatformToolsDependency) pkg,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- if (ai != null) {
- aiFound.add(ai);
- }
- }
-
- if (pkg instanceof IMinApiLevelDependency) {
-
- ArchiveInfo ai = findMinApiLevelDependency(
- (IMinApiLevelDependency) pkg,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- if (ai != null) {
- aiFound.add(ai);
- }
- }
-
- if (pkg instanceof IExactApiLevelDependency) {
-
- ArchiveInfo ai = findExactApiLevelDependency(
- (IExactApiLevelDependency) pkg,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives);
-
- if (ai != null) {
- aiFound.add(ai);
- }
- }
-
- if (aiFound.size() > 0) {
- ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]);
- Arrays.sort(result);
- return result;
- }
-
- return null;
- }
-
- /**
- * Resolves dependencies on tools.
- *
- * A platform or an extra package can both have a min-tools-rev, in which case it
- * depends on having a tools package of the requested revision.
- * Finds the tools dependency. If found, add it to the list of things to install.
- * Returns the archive info dependency, if any.
- */
- protected ArchiveInfo findToolsDependency(
- IMinToolsDependency pkg,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
- // This is the requirement to match.
- FullRevision rev = pkg.getMinToolsRevision();
-
- if (rev.equals(MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED)) {
- // Well actually there's no requirement.
- return null;
- }
-
- // First look in locally installed packages.
- for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof ToolPackage) {
- if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) {
- // We found one already installed.
- return null;
- }
- }
- }
- }
-
- // Look in archives already scheduled for install
- for (ArchiveInfo ai : outArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof ToolPackage) {
- if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) {
- // The dependency is already scheduled for install, nothing else to do.
- return ai;
- }
- }
- }
- }
-
- // Otherwise look in the selected archives.
- if (selectedArchives != null) {
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof ToolPackage) {
- if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
-
- // Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs, remoteSources);
- for (Package p : remotePkgs) {
- if (p instanceof ToolPackage) {
- if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) {
- // It's not already in the list of things to install, so add the
- // first compatible archive we can find.
- for (Archive a : p.getArchives()) {
- if (a.isCompatible()) {
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
- }
-
- // We end up here if nothing matches. We don't have a good platform to match.
- // We need to indicate this extra depends on a missing platform archive
- // so that it can be impossible to install later on.
- return new MissingArchiveInfo(MissingArchiveInfo.TITLE_TOOL, rev);
- }
-
- /**
- * Resolves dependencies on platform-tools.
- *
- * A tool package can have a min-platform-tools-rev, in which case it depends on
- * having a platform-tool package of the requested revision.
- * Finds the platform-tool dependency. If found, add it to the list of things to install.
- * Returns the archive info dependency, if any.
- */
- protected ArchiveInfo findPlatformToolsDependency(
- IMinPlatformToolsDependency pkg,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
- // This is the requirement to match.
- FullRevision rev = pkg.getMinPlatformToolsRevision();
- boolean findMax = false;
- ArchiveInfo aiMax = null;
- Archive aMax = null;
-
- if (rev.equals(IMinPlatformToolsDependency.MIN_PLATFORM_TOOLS_REV_INVALID)) {
- // The requirement is invalid, which is not supposed to happen since this
- // property is mandatory. However in a typical upgrade scenario we can end
- // up with the previous updater managing a new package and not dealing
- // correctly with the new unknown property.
- // So instead we parse all the existing and remote packages and try to find
- // the max available revision and we'll use it.
- findMax = true;
- }
-
- // First look in locally installed packages.
- for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformToolPackage) {
- FullRevision r = ((PlatformToolPackage) p).getRevision();
- if (findMax && r.compareTo(rev) > 0) {
- rev = r;
- aiMax = ai;
- } else if (!findMax && r.compareTo(rev) >= 0) {
- // We found one already installed.
- return null;
- }
- }
- }
- }
-
- // Look in archives already scheduled for install
- for (ArchiveInfo ai : outArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformToolPackage) {
- FullRevision r = ((PlatformToolPackage) p).getRevision();
- if (findMax && r.compareTo(rev) > 0) {
- rev = r;
- aiMax = ai;
- } else if (!findMax && r.compareTo(rev) >= 0) {
- // The dependency is already scheduled for install, nothing else to do.
- return ai;
- }
- }
- }
- }
-
- // Otherwise look in the selected archives.
- if (selectedArchives != null) {
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformToolPackage) {
- FullRevision r = ((PlatformToolPackage) p).getRevision();
- if (findMax && r.compareTo(rev) > 0) {
- rev = r;
- aiMax = null;
- aMax = a;
- } else if (!findMax && r.compareTo(rev) >= 0) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
-
- // Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs, remoteSources);
- for (Package p : remotePkgs) {
- if (p instanceof PlatformToolPackage) {
- FullRevision r = ((PlatformToolPackage) p).getRevision();
- if (r.compareTo(rev) >= 0) {
- // Make sure there's at least one valid archive here
- for (Archive a : p.getArchives()) {
- if (a.isCompatible()) {
- if (findMax && r.compareTo(rev) > 0) {
- rev = r;
- aiMax = null;
- aMax = a;
- } else if (!findMax && r.compareTo(rev) >= 0) {
- // It's not already in the list of things to install, so add the
- // first compatible archive we can find.
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
- }
- }
-
- if (findMax) {
- if (aMax != null) {
- return insertArchive(aMax,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- } else if (aiMax != null) {
- return aiMax;
- }
- }
-
- // We end up here if nothing matches. We don't have a good platform to match.
- // We need to indicate this package depends on a missing platform archive
- // so that it can be impossible to install later on.
- return new MissingArchiveInfo(MissingArchiveInfo.TITLE_PLATFORM_TOOL, rev);
- }
-
- /**
- * Resolves dependencies on platform for an addon.
- *
- * An addon depends on having a platform with the same API level.
- *
- * Finds the platform dependency. If found, add it to the list of things to install.
- * Returns the archive info dependency, if any.
- */
- protected ArchiveInfo findPlatformDependency(
- IPlatformDependency pkg,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
- // This is the requirement to match.
- AndroidVersion v = pkg.getAndroidVersion();
-
- // Find a platform that would satisfy the requirement.
-
- // First look in locally installed packages.
- for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (v.equals(((PlatformPackage) p).getAndroidVersion())) {
- // We found one already installed.
- return null;
- }
- }
- }
- }
-
- // Look in archives already scheduled for install
- for (ArchiveInfo ai : outArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (v.equals(((PlatformPackage) p).getAndroidVersion())) {
- // The dependency is already scheduled for install, nothing else to do.
- return ai;
- }
- }
- }
- }
-
- // Otherwise look in the selected archives.
- if (selectedArchives != null) {
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (v.equals(((PlatformPackage) p).getAndroidVersion())) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
-
- // Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs, remoteSources);
- for (Package p : remotePkgs) {
- if (p instanceof PlatformPackage) {
- if (v.equals(((PlatformPackage) p).getAndroidVersion())) {
- // It's not already in the list of things to install, so add the
- // first compatible archive we can find.
- for (Archive a : p.getArchives()) {
- if (a.isCompatible()) {
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
- }
-
- // We end up here if nothing matches. We don't have a good platform to match.
- // We need to indicate this addon depends on a missing platform archive
- // so that it can be impossible to install later on.
- return new MissingPlatformArchiveInfo(pkg.getAndroidVersion());
- }
-
- /**
- * Resolves platform dependencies for extras.
- * An extra depends on having a platform with a minimun API level.
- *
- * We try to return the highest API level available above the specified minimum.
- * Note that installed packages have priority so if one installed platform satisfies
- * the dependency, we'll use it even if there's a higher API platform available but
- * not installed yet.
- *
- * Finds the platform dependency. If found, add it to the list of things to install.
- * Returns the archive info dependency, if any.
- */
- protected ArchiveInfo findMinApiLevelDependency(
- IMinApiLevelDependency pkg,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
-
- int api = pkg.getMinApiLevel();
-
- if (api == IMinApiLevelDependency.MIN_API_LEVEL_NOT_SPECIFIED) {
- return null;
- }
-
- // Find a platform that would satisfy the requirement.
-
- // First look in locally installed packages.
- for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) {
- // We found one already installed.
- return null;
- }
- }
- }
- }
-
- // Look in archives already scheduled for install
- int foundApi = 0;
- ArchiveInfo foundAi = null;
-
- for (ArchiveInfo ai : outArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) {
- if (api > foundApi) {
- foundApi = api;
- foundAi = ai;
- }
- }
- }
- }
- }
-
- if (foundAi != null) {
- // The dependency is already scheduled for install, nothing else to do.
- return foundAi;
- }
-
- // Otherwise look in the selected archives *or* available remote packages
- // and takes the best out of the two sets.
- foundApi = 0;
- Archive foundArchive = null;
- if (selectedArchives != null) {
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) {
- if (api > foundApi) {
- foundApi = api;
- foundArchive = a;
- }
- }
- }
- }
- }
-
- // Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs, remoteSources);
- for (Package p : remotePkgs) {
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) {
- if (api > foundApi) {
- // It's not already in the list of things to install, so add the
- // first compatible archive we can find.
- for (Archive a : p.getArchives()) {
- if (a.isCompatible()) {
- foundApi = api;
- foundArchive = a;
- }
- }
- }
- }
- }
- }
-
- if (foundArchive != null) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(foundArchive,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
-
- // We end up here if nothing matches. We don't have a good platform to match.
- // We need to indicate this extra depends on a missing platform archive
- // so that it can be impossible to install later on.
- return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/));
- }
-
- /**
- * Resolves platform dependencies for add-ons.
- * An add-ons depends on having a platform with an exact specific API level.
- *
- * Finds the platform dependency. If found, add it to the list of things to install.
- * Returns the archive info dependency, if any.
- */
- protected ArchiveInfo findExactApiLevelDependency(
- IExactApiLevelDependency pkg,
- Collection<ArchiveInfo> outArchives,
- Collection<Archive> selectedArchives,
- Collection<Package> remotePkgs,
- SdkSource[] remoteSources,
- ArchiveInfo[] localArchives) {
-
- int api = pkg.getExactApiLevel();
-
- if (api == IExactApiLevelDependency.API_LEVEL_INVALID) {
- return null;
- }
-
- // Find a platform that would satisfy the requirement.
-
- // First look in locally installed packages.
- for (ArchiveInfo ai : localArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().equals(api)) {
- // We found one already installed.
- return null;
- }
- }
- }
- }
-
- // Look in archives already scheduled for install
-
- for (ArchiveInfo ai : outArchives) {
- Archive a = ai.getNewArchive();
- if (a != null) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().equals(api)) {
- return ai;
- }
- }
- }
- }
-
- // Otherwise look in the selected archives.
- if (selectedArchives != null) {
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().equals(api)) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
-
- // Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs, remoteSources);
- for (Package p : remotePkgs) {
- if (p instanceof PlatformPackage) {
- if (((PlatformPackage) p).getAndroidVersion().equals(api)) {
- // It's not already in the list of things to install, so add the
- // first compatible archive we can find.
- for (Archive a : p.getArchives()) {
- if (a.isCompatible()) {
- return insertArchive(a,
- outArchives,
- selectedArchives,
- remotePkgs,
- remoteSources,
- localArchives,
- true /*automated*/);
- }
- }
- }
- }
- }
-
- // We end up here if nothing matches. We don't have a good platform to match.
- // We need to indicate this extra depends on a missing platform archive
- // so that it can be impossible to install later on.
- return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/));
- }
-
- /**
- * Fetch all remote packages only if really needed.
- * <p/>
- * This method takes a list of sources. Each source is only fetched once -- that is each
- * source keeps the list of packages that we fetched from the remote XML file. If the list
- * is null, it means this source has never been fetched so we'll do it once here. Otherwise
- * we rely on the cached list of packages from this source.
- * <p/>
- * This method also takes a remote package list as input, which it will fill out.
- * If a source has already been fetched, we'll add its packages to the remote package list
- * if they are not already present. Otherwise, the source will be fetched and the packages
- * added to the list.
- *
- * @param remotePkgs An in-out list of packages available from remote sources.
- * This list must not be null.
- * It can be empty or already contain some packages.
- * @param remoteSources A list of available remote sources to fetch from.
- */
- protected void fetchRemotePackages(
- final Collection<Package> remotePkgs,
- final SdkSource[] remoteSources) {
- if (remotePkgs.size() > 0) {
- return;
- }
-
- // First check if there's any remote source we need to fetch.
- // This will bring the task window, so we rather not display it unless
- // necessary.
- boolean needsFetch = false;
- for (final SdkSource remoteSrc : remoteSources) {
- Package[] pkgs = remoteSrc.getPackages();
- if (pkgs == null) {
- // This source has never been fetched. We'll do it below.
- needsFetch = true;
- } else {
- // This source has already been fetched and we know its package list.
- // We still need to make sure all of its packages are present in the
- // remotePkgs list.
-
- nextPackage: for (Package pkg : pkgs) {
- for (Archive a : pkg.getArchives()) {
- // Only add a package if it contains at least one compatible archive
- // and is not already in the remote package list.
- if (a.isCompatible()) {
- if (!remotePkgs.contains(pkg)) {
- remotePkgs.add(pkg);
- continue nextPackage;
- }
- }
- }
- }
- }
- }
-
- if (!needsFetch) {
- return;
- }
-
- final boolean forceHttp = mUpdaterData.getSettingsController().getSettings().getForceHttp();
-
- mUpdaterData.getTaskFactory().start("Refresh Sources", new ITask() {
- @Override
- public void run(ITaskMonitor monitor) {
- for (SdkSource remoteSrc : remoteSources) {
- Package[] pkgs = remoteSrc.getPackages();
-
- if (pkgs == null) {
- remoteSrc.load(mUpdaterData.getDownloadCache(), monitor, forceHttp);
- pkgs = remoteSrc.getPackages();
- }
-
- if (pkgs != null) {
- nextPackage: for (Package pkg : pkgs) {
- for (Archive a : pkg.getArchives()) {
- // Only add a package if it contains at least one compatible archive
- // and is not already in the remote package list.
- if (a.isCompatible()) {
- if (!remotePkgs.contains(pkg)) {
- remotePkgs.add(pkg);
- continue nextPackage;
- }
- }
- }
- }
- }
- }
- }
- });
- }
-
-
- /**
- * A {@link LocalArchiveInfo} is an {@link ArchiveInfo} that wraps an already installed
- * "local" package/archive.
- * <p/>
- * In this case, the "new Archive" is still expected to be non null and the
- * "replaced Archive" is null. Installed archives are always accepted and never
- * rejected.
- * <p/>
- * Dependencies are not set.
- */
- private static class LocalArchiveInfo extends ArchiveInfo {
-
- public LocalArchiveInfo(Archive localArchive) {
- super(localArchive, null /*replaced*/, null /*dependsOn*/);
- }
-
- /** Installed archives are always accepted. */
- @Override
- public boolean isAccepted() {
- return true;
- }
-
- /** Installed archives are never rejected. */
- @Override
- public boolean isRejected() {
- return false;
- }
- }
-
- /**
- * A {@link MissingPlatformArchiveInfo} is an {@link ArchiveInfo} that represents a
- * package/archive that we <em>really</em> need as a dependency but that we don't have.
- * <p/>
- * This is currently used for addons and extras in case we can't find a matching base platform.
- * <p/>
- * This kind of archive has specific properties: the new archive to install is null,
- * there are no dependencies and no archive is being replaced. The info can never be
- * accepted and is always rejected.
- */
- private static class MissingPlatformArchiveInfo extends ArchiveInfo {
-
- private final AndroidVersion mVersion;
-
- /**
- * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the
- * given platform version is missing.
- */
- public MissingPlatformArchiveInfo(AndroidVersion version) {
- super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/);
- mVersion = version;
- }
-
- /** Missing archives are never accepted. */
- @Override
- public boolean isAccepted() {
- return false;
- }
-
- /** Missing archives are always rejected. */
- @Override
- public boolean isRejected() {
- return true;
- }
-
- @Override
- public String getShortDescription() {
- return String.format("Missing SDK Platform Android%1$s, API %2$d",
- mVersion.isPreview() ? " Preview" : "",
- mVersion.getApiLevel());
- }
- }
-
- /**
- * A {@link MissingArchiveInfo} is an {@link ArchiveInfo} that represents a
- * package/archive that we <em>really</em> need as a dependency but that we don't have.
- * <p/>
- * This is currently used for extras in case we can't find a matching tool revision
- * or when a platform-tool is missing.
- * <p/>
- * This kind of archive has specific properties: the new archive to install is null,
- * there are no dependencies and no archive is being replaced. The info can never be
- * accepted and is always rejected.
- */
- private static class MissingArchiveInfo extends ArchiveInfo {
-
- private final FullRevision mRevision;
- private final String mTitle;
-
- public static final String TITLE_TOOL = "Tools";
- public static final String TITLE_PLATFORM_TOOL = "Platform-tools";
-
- /**
- * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the
- * given platform version is missing.
- *
- * @param title Typically "Tools" or "Platform-tools".
- * @param revision The required revision.
- */
- public MissingArchiveInfo(String title, FullRevision revision) {
- super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/);
- mTitle = title;
- mRevision = revision;
- }
-
- /** Missing archives are never accepted. */
- @Override
- public boolean isAccepted() {
- return false;
- }
-
- /** Missing archives are always rejected. */
- @Override
- public boolean isRejected() {
- return true;
- }
-
- @Override
- public String getShortDescription() {
- return String.format("Missing Android SDK %1$s, revision %2$s",
- mTitle,
- mRevision.toShortString());
- }
- }
-}
+/* + * 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.sdkuilib.internal.repository; + +import com.android.sdklib.AndroidVersion; +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdklib.internal.repository.archives.Archive; +import com.android.sdklib.internal.repository.packages.AddonPackage; +import com.android.sdklib.internal.repository.packages.DocPackage; +import com.android.sdklib.internal.repository.packages.ExtraPackage; +import com.android.sdklib.internal.repository.packages.FullRevision; +import com.android.sdklib.internal.repository.packages.IAndroidVersionProvider; +import com.android.sdklib.internal.repository.packages.IExactApiLevelDependency; +import com.android.sdklib.internal.repository.packages.IMinApiLevelDependency; +import com.android.sdklib.internal.repository.packages.IMinPlatformToolsDependency; +import com.android.sdklib.internal.repository.packages.IMinToolsDependency; +import com.android.sdklib.internal.repository.packages.IPlatformDependency; +import com.android.sdklib.internal.repository.packages.MinToolsPackage; +import com.android.sdklib.internal.repository.packages.Package; +import com.android.sdklib.internal.repository.packages.Package.UpdateInfo; +import com.android.sdklib.internal.repository.packages.PlatformPackage; +import com.android.sdklib.internal.repository.packages.PlatformToolPackage; +import com.android.sdklib.internal.repository.packages.SamplePackage; +import com.android.sdklib.internal.repository.packages.SystemImagePackage; +import com.android.sdklib.internal.repository.packages.ToolPackage; +import com.android.sdklib.internal.repository.sources.SdkSource; +import com.android.sdklib.internal.repository.sources.SdkSources; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The logic to compute which packages to install, based on the choices + * made by the user. This adds required packages as needed. + * <p/> + * When the user doesn't provide a selection, looks at local package to find + * those that can be updated and compute dependencies too. + */ +class SdkUpdaterLogic { + + private final IUpdaterData mUpdaterData; + + public SdkUpdaterLogic(IUpdaterData updaterData) { + mUpdaterData = updaterData; + } + + /** + * Retrieves an unfiltered list of all remote archives. + * The archives are guaranteed to be compatible with the current platform. + */ + public List<ArchiveInfo> getAllRemoteArchives( + SdkSources sources, + Package[] localPkgs, + boolean includeAll) { + + List<Package> remotePkgs = new ArrayList<Package>(); + SdkSource[] remoteSources = sources.getAllSources(); + fetchRemotePackages(remotePkgs, remoteSources); + + ArrayList<Archive> archives = new ArrayList<Archive>(); + for (Package remotePkg : remotePkgs) { + // Only look for non-obsolete updates unless requested to include them + if (includeAll || !remotePkg.isObsolete()) { + // Found a suitable update. Only accept the remote package + // if it provides at least one compatible archive + + addArchives: + for (Archive a : remotePkg.getArchives()) { + if (a.isCompatible()) { + + // If we're trying to add a package for revision N, + // make sure we don't also have a package for revision N-1. + for (int i = archives.size() - 1; i >= 0; i--) { + Package pkgFound = archives.get(i).getParentPackage(); + if (pkgFound.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { + // This package can update one we selected earlier. + // Remove the one that can be updated by this new one. + archives.remove(i); + } else if (remotePkg.canBeUpdatedBy(pkgFound) == UpdateInfo.UPDATE) { + // There is a package in the list that is already better + // than the one we want to add, so don't add it. + break addArchives; + } + } + + archives.add(a); + break; + } + } + } + } + + ArrayList<ArchiveInfo> result = new ArrayList<ArchiveInfo>(); + + ArchiveInfo[] localArchives = createLocalArchives(localPkgs); + + for (Archive a : archives) { + insertArchive(a, + result, + archives, + remotePkgs, + remoteSources, + localArchives, + false /*automated*/); + } + + return result; + } + + /** + * Compute which packages to install by taking the user selection + * and adding required packages as needed. + * + * When the user doesn't provide a selection, looks at local packages to find + * those that can be updated and compute dependencies too. + */ + public List<ArchiveInfo> computeUpdates( + Collection<Archive> selectedArchives, + SdkSources sources, + Package[] localPkgs, + boolean includeAll) { + + List<ArchiveInfo> archives = new ArrayList<ArchiveInfo>(); + List<Package> remotePkgs = new ArrayList<Package>(); + SdkSource[] remoteSources = sources.getAllSources(); + + // Create ArchiveInfos out of local (installed) packages. + ArchiveInfo[] localArchives = createLocalArchives(localPkgs); + + // If we do not have a specific list of archives to install (that is the user + // selected "update all" rather than request specific packages), then we try to + // find updates based on the *existing* packages. + if (selectedArchives == null) { + selectedArchives = findUpdates( + localArchives, + remotePkgs, + remoteSources, + includeAll); + } + + // Once we have a list of packages to install, we try to solve all their + // dependencies by automatically adding them to the list of things to install. + // This works on the list provided either by the user directly or the list + // computed from potential updates. + for (Archive a : selectedArchives) { + insertArchive(a, + archives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + false /*automated*/); + } + + // Finally we need to look at *existing* packages which are not being updated + // and check if they have any missing dependencies and suggest how to fix + // these dependencies. + fixMissingLocalDependencies( + archives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + return archives; + } + + private double getRevisionRank(FullRevision rev) { + int p = rev.isPreview() ? 999 : 999 - rev.getPreview(); + return rev.getMajor() + + rev.getMinor() / 1000.d + + rev.getMicro() / 1000000.d + + p / 1000000000.d; + } + + /** + * Finds new packages that the user does not have in his/her local SDK + * and adds them to the list of archives to install. + * <p/> + * The default is to only find "new" platforms, that is anything more + * recent than the highest platform currently installed. + * A side effect is that for an empty SDK install this will list *all* + * platforms available (since there's no "highest" installed platform.) + * + * @param archives The in-out list of archives to install. Typically the + * list is not empty at first as it should contain any archives that is + * already scheduled for install. This method will add to the list. + * @param sources The list of all sources, to fetch them as necessary. + * @param localPkgs The list of all currently installed packages. + * @param includeAll When true, this will list all platforms. + * (included these lower than the highest installed one) as well as + * all obsolete packages of these platforms. + */ + public void addNewPlatforms( + Collection<ArchiveInfo> archives, + SdkSources sources, + Package[] localPkgs, + boolean includeAll) { + + // Create ArchiveInfos out of local (installed) packages. + ArchiveInfo[] localArchives = createLocalArchives(localPkgs); + + // Find the highest platform installed + double currentPlatformScore = 0; + double currentSampleScore = 0; + double currentAddonScore = 0; + double currentDocScore = 0; + HashMap<String, Double> currentExtraScore = new HashMap<String, Double>(); + if (!includeAll) { + if (localPkgs != null) { + for (Package p : localPkgs) { + double rev = getRevisionRank(p.getRevision()); + int api = 0; + boolean isPreview = false; + if (p instanceof IAndroidVersionProvider) { + AndroidVersion vers = ((IAndroidVersionProvider) p).getAndroidVersion(); + api = vers.getApiLevel(); + isPreview = vers.isPreview(); + } + + // The score is 1000*api + (999 if preview) + rev + // This allows previews to rank above a non-preview and + // allows revisions to rank appropriately. + double score = api * 1000 + (isPreview ? 999 : 0) + rev; + + if (p instanceof PlatformPackage) { + currentPlatformScore = Math.max(currentPlatformScore, score); + } else if (p instanceof SamplePackage) { + currentSampleScore = Math.max(currentSampleScore, score); + } else if (p instanceof AddonPackage) { + currentAddonScore = Math.max(currentAddonScore, score); + } else if (p instanceof ExtraPackage) { + currentExtraScore.put(((ExtraPackage) p).getPath(), score); + } else if (p instanceof DocPackage) { + currentDocScore = Math.max(currentDocScore, score); + } + } + } + } + + SdkSource[] remoteSources = sources.getAllSources(); + ArrayList<Package> remotePkgs = new ArrayList<Package>(); + fetchRemotePackages(remotePkgs, remoteSources); + + Package suggestedDoc = null; + + for (Package p : remotePkgs) { + // Skip obsolete packages unless requested to include them. + if (p.isObsolete() && !includeAll) { + continue; + } + + double rev = getRevisionRank(p.getRevision()); + int api = 0; + boolean isPreview = false; + if (p instanceof IAndroidVersionProvider) { + AndroidVersion vers = ((IAndroidVersionProvider) p).getAndroidVersion(); + api = vers.getApiLevel(); + isPreview = vers.isPreview(); + } + + double score = api * 1000 + (isPreview ? 999 : 0) + rev; + + boolean shouldAdd = false; + if (p instanceof PlatformPackage) { + shouldAdd = score > currentPlatformScore; + } else if (p instanceof SamplePackage) { + shouldAdd = score > currentSampleScore; + } else if (p instanceof AddonPackage) { + shouldAdd = score > currentAddonScore; + } else if (p instanceof ExtraPackage) { + String key = ((ExtraPackage) p).getPath(); + shouldAdd = !currentExtraScore.containsKey(key) || + score > currentExtraScore.get(key).doubleValue(); + } else if (p instanceof DocPackage) { + // We don't want all the doc, only the most recent one + if (score > currentDocScore) { + suggestedDoc = p; + currentDocScore = score; + } + } + + if (shouldAdd) { + // We should suggest this package for installation. + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + insertArchive(a, + archives, + null /*selectedArchives*/, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + + if (p instanceof PlatformPackage && (score >= currentPlatformScore)) { + // We just added a new platform *or* we are visiting the highest currently + // installed platform. In either case we want to make sure it either has + // its own system image or that we provide one by default. + PlatformPackage pp = (PlatformPackage) p; + if (pp.getIncludedAbi() == null) { + for (Package p2 : remotePkgs) { + if (!(p2 instanceof SystemImagePackage) || + (p2.isObsolete() && !includeAll)) { + continue; + } + SystemImagePackage sip = (SystemImagePackage) p2; + if (sip.getAndroidVersion().equals(pp.getAndroidVersion())) { + for (Archive a : sip.getArchives()) { + if (a.isCompatible()) { + insertArchive(a, + archives, + null /*selectedArchives*/, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + } + } + } + + if (suggestedDoc != null) { + // We should suggest this package for installation. + for (Archive a : suggestedDoc.getArchives()) { + if (a.isCompatible()) { + insertArchive(a, + archives, + null /*selectedArchives*/, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + + /** + * Create a array of {@link ArchiveInfo} based on all local (already installed) + * packages. The array is always non-null but may be empty. + * <p/> + * The local {@link ArchiveInfo} are guaranteed to have one non-null archive + * that you can retrieve using {@link ArchiveInfo#getNewArchive()}. + */ + protected ArchiveInfo[] createLocalArchives(Package[] localPkgs) { + + if (localPkgs != null) { + ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>(); + for (Package p : localPkgs) { + // Only accept packages that have one compatible archive. + // Local package should have 1 and only 1 compatible archive anyway. + for (Archive a : p.getArchives()) { + if (a != null && a.isCompatible()) { + // We create an "installed" archive info to wrap the local package. + // Note that dependencies are not computed since right now we don't + // deal with more than one level of dependencies and installed archives + // are deemed implicitly accepted anyway. + list.add(new LocalArchiveInfo(a)); + } + } + } + + return list.toArray(new ArchiveInfo[list.size()]); + } + + return new ArchiveInfo[0]; + } + + /** + * Find suitable updates to all current local packages. + * <p/> + * Returns a list of potential updates for *existing* packages. This does NOT solve + * dependencies for the new packages. + * <p/> + * Always returns a non-null collection, which can be empty. + */ + private Collection<Archive> findUpdates( + ArchiveInfo[] localArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + boolean includeAll) { + ArrayList<Archive> updates = new ArrayList<Archive>(); + + fetchRemotePackages(remotePkgs, remoteSources); + + for (ArchiveInfo ai : localArchives) { + Archive na = ai.getNewArchive(); + if (na == null) { + continue; + } + Package localPkg = na.getParentPackage(); + + for (Package remotePkg : remotePkgs) { + // Only look for non-obsolete updates unless requested to include them + if ((includeAll || !remotePkg.isObsolete()) && + localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { + // Found a suitable update. Only accept the remote package + // if it provides at least one compatible archive + + addArchives: + for (Archive a : remotePkg.getArchives()) { + if (a.isCompatible()) { + + // If we're trying to add a package for revision N, + // make sure we don't also have a package for revision N-1. + for (int i = updates.size() - 1; i >= 0; i--) { + Package pkgFound = updates.get(i).getParentPackage(); + if (pkgFound.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { + // This package can update one we selected earlier. + // Remove the one that can be updated by this new one. + updates.remove(i); + } else if (remotePkg.canBeUpdatedBy(pkgFound) == + UpdateInfo.UPDATE) { + // There is a package in the list that is already better + // than the one we want to add, so don't add it. + break addArchives; + } + } + + updates.add(a); + break; + } + } + } + } + } + + return updates; + } + + /** + * Check all local archives which are NOT being updated and see if they + * miss any dependency. If they do, try to fix that dependency by selecting + * an appropriate package. + */ + private void fixMissingLocalDependencies( + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + + nextLocalArchive: for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + Package p = a == null ? null : a.getParentPackage(); + if (p == null) { + continue; + } + + // Is this local archive being updated? + for (ArchiveInfo ai2 : outArchives) { + if (ai2.getReplaced() == a) { + // this new archive will replace the current local one, + // so we don't have to care about fixing dependencies (since the + // new archive should already have had its dependencies resolved) + continue nextLocalArchive; + } + } + + // find dependencies for the local archive and add them as needed + // to the outArchives collection. + ArchiveInfo[] deps = findDependency(p, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + if (deps != null) { + // The already installed archive has a missing dependency, which we + // just selected for install. Make sure we remember the dependency + // so that we can enforce it later in the UI. + for (ArchiveInfo aid : deps) { + aid.addDependencyFor(ai); + } + } + } + } + + private ArchiveInfo insertArchive(Archive archive, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives, + boolean automated) { + Package p = archive.getParentPackage(); + + // Is this an update? + Archive updatedArchive = null; + for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package lp = a.getParentPackage(); + + if (lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) { + updatedArchive = a; + } + } + } + + // Find dependencies and adds them as needed to outArchives + ArchiveInfo[] deps = findDependency(p, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + // Make sure it's not a dup + ArchiveInfo ai = null; + + for (ArchiveInfo ai2 : outArchives) { + Archive a2 = ai2.getNewArchive(); + if (a2 != null && a2.getParentPackage().sameItemAs(archive.getParentPackage())) { + ai = ai2; + break; + } + } + + if (ai == null) { + ai = new ArchiveInfo( + archive, //newArchive + updatedArchive, //replaced + deps //dependsOn + ); + outArchives.add(ai); + } + + if (deps != null) { + for (ArchiveInfo d : deps) { + d.addDependencyFor(ai); + } + } + + return ai; + } + + /** + * Resolves dependencies for a given package. + * + * Returns null if no dependencies were found. + * Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have + * at least size 1 and contain no null elements. + */ + private ArchiveInfo[] findDependency(Package pkg, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + + // Current dependencies can be: + // - addon: *always* depends on platform of same API level + // - platform: *might* depends on tools of rev >= min-tools-rev + // - extra: *might* depends on platform with api >= min-api-level + + Set<ArchiveInfo> aiFound = new HashSet<ArchiveInfo>(); + + if (pkg instanceof IPlatformDependency) { + ArchiveInfo ai = findPlatformDependency( + (IPlatformDependency) pkg, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + if (ai != null) { + aiFound.add(ai); + } + } + + if (pkg instanceof IMinToolsDependency) { + + ArchiveInfo ai = findToolsDependency( + (IMinToolsDependency) pkg, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + if (ai != null) { + aiFound.add(ai); + } + } + + if (pkg instanceof IMinPlatformToolsDependency) { + + ArchiveInfo ai = findPlatformToolsDependency( + (IMinPlatformToolsDependency) pkg, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + if (ai != null) { + aiFound.add(ai); + } + } + + if (pkg instanceof IMinApiLevelDependency) { + + ArchiveInfo ai = findMinApiLevelDependency( + (IMinApiLevelDependency) pkg, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + if (ai != null) { + aiFound.add(ai); + } + } + + if (pkg instanceof IExactApiLevelDependency) { + + ArchiveInfo ai = findExactApiLevelDependency( + (IExactApiLevelDependency) pkg, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives); + + if (ai != null) { + aiFound.add(ai); + } + } + + if (aiFound.size() > 0) { + ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]); + Arrays.sort(result); + return result; + } + + return null; + } + + /** + * Resolves dependencies on tools. + * + * A platform or an extra package can both have a min-tools-rev, in which case it + * depends on having a tools package of the requested revision. + * Finds the tools dependency. If found, add it to the list of things to install. + * Returns the archive info dependency, if any. + */ + protected ArchiveInfo findToolsDependency( + IMinToolsDependency pkg, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + // This is the requirement to match. + FullRevision rev = pkg.getMinToolsRevision(); + + if (rev.equals(MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED)) { + // Well actually there's no requirement. + return null; + } + + // First look in locally installed packages. + for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof ToolPackage) { + if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { + // We found one already installed. + return null; + } + } + } + } + + // Look in archives already scheduled for install + for (ArchiveInfo ai : outArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof ToolPackage) { + if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { + // The dependency is already scheduled for install, nothing else to do. + return ai; + } + } + } + } + + // Otherwise look in the selected archives. + if (selectedArchives != null) { + for (Archive a : selectedArchives) { + Package p = a.getParentPackage(); + if (p instanceof ToolPackage) { + if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { + // It's not already in the list of things to install, so add it now + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + + // Finally nothing matched, so let's look at all available remote packages + fetchRemotePackages(remotePkgs, remoteSources); + for (Package p : remotePkgs) { + if (p instanceof ToolPackage) { + if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { + // It's not already in the list of things to install, so add the + // first compatible archive we can find. + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + } + + // We end up here if nothing matches. We don't have a good platform to match. + // We need to indicate this extra depends on a missing platform archive + // so that it can be impossible to install later on. + return new MissingArchiveInfo(MissingArchiveInfo.TITLE_TOOL, rev); + } + + /** + * Resolves dependencies on platform-tools. + * + * A tool package can have a min-platform-tools-rev, in which case it depends on + * having a platform-tool package of the requested revision. + * Finds the platform-tool dependency. If found, add it to the list of things to install. + * Returns the archive info dependency, if any. + */ + protected ArchiveInfo findPlatformToolsDependency( + IMinPlatformToolsDependency pkg, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + // This is the requirement to match. + FullRevision rev = pkg.getMinPlatformToolsRevision(); + boolean findMax = false; + ArchiveInfo aiMax = null; + Archive aMax = null; + + if (rev.equals(IMinPlatformToolsDependency.MIN_PLATFORM_TOOLS_REV_INVALID)) { + // The requirement is invalid, which is not supposed to happen since this + // property is mandatory. However in a typical upgrade scenario we can end + // up with the previous updater managing a new package and not dealing + // correctly with the new unknown property. + // So instead we parse all the existing and remote packages and try to find + // the max available revision and we'll use it. + findMax = true; + } + + // First look in locally installed packages. + for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformToolPackage) { + FullRevision r = ((PlatformToolPackage) p).getRevision(); + if (findMax && r.compareTo(rev) > 0) { + rev = r; + aiMax = ai; + } else if (!findMax && r.compareTo(rev) >= 0) { + // We found one already installed. + return null; + } + } + } + } + + // Look in archives already scheduled for install + for (ArchiveInfo ai : outArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformToolPackage) { + FullRevision r = ((PlatformToolPackage) p).getRevision(); + if (findMax && r.compareTo(rev) > 0) { + rev = r; + aiMax = ai; + } else if (!findMax && r.compareTo(rev) >= 0) { + // The dependency is already scheduled for install, nothing else to do. + return ai; + } + } + } + } + + // Otherwise look in the selected archives. + if (selectedArchives != null) { + for (Archive a : selectedArchives) { + Package p = a.getParentPackage(); + if (p instanceof PlatformToolPackage) { + FullRevision r = ((PlatformToolPackage) p).getRevision(); + if (findMax && r.compareTo(rev) > 0) { + rev = r; + aiMax = null; + aMax = a; + } else if (!findMax && r.compareTo(rev) >= 0) { + // It's not already in the list of things to install, so add it now + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + + // Finally nothing matched, so let's look at all available remote packages + fetchRemotePackages(remotePkgs, remoteSources); + for (Package p : remotePkgs) { + if (p instanceof PlatformToolPackage) { + FullRevision r = ((PlatformToolPackage) p).getRevision(); + if (r.compareTo(rev) >= 0) { + // Make sure there's at least one valid archive here + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + if (findMax && r.compareTo(rev) > 0) { + rev = r; + aiMax = null; + aMax = a; + } else if (!findMax && r.compareTo(rev) >= 0) { + // It's not already in the list of things to install, so add the + // first compatible archive we can find. + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + } + } + + if (findMax) { + if (aMax != null) { + return insertArchive(aMax, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } else if (aiMax != null) { + return aiMax; + } + } + + // We end up here if nothing matches. We don't have a good platform to match. + // We need to indicate this package depends on a missing platform archive + // so that it can be impossible to install later on. + return new MissingArchiveInfo(MissingArchiveInfo.TITLE_PLATFORM_TOOL, rev); + } + + /** + * Resolves dependencies on platform for an addon. + * + * An addon depends on having a platform with the same API level. + * + * Finds the platform dependency. If found, add it to the list of things to install. + * Returns the archive info dependency, if any. + */ + protected ArchiveInfo findPlatformDependency( + IPlatformDependency pkg, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + // This is the requirement to match. + AndroidVersion v = pkg.getAndroidVersion(); + + // Find a platform that would satisfy the requirement. + + // First look in locally installed packages. + for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (v.equals(((PlatformPackage) p).getAndroidVersion())) { + // We found one already installed. + return null; + } + } + } + } + + // Look in archives already scheduled for install + for (ArchiveInfo ai : outArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (v.equals(((PlatformPackage) p).getAndroidVersion())) { + // The dependency is already scheduled for install, nothing else to do. + return ai; + } + } + } + } + + // Otherwise look in the selected archives. + if (selectedArchives != null) { + for (Archive a : selectedArchives) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (v.equals(((PlatformPackage) p).getAndroidVersion())) { + // It's not already in the list of things to install, so add it now + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + + // Finally nothing matched, so let's look at all available remote packages + fetchRemotePackages(remotePkgs, remoteSources); + for (Package p : remotePkgs) { + if (p instanceof PlatformPackage) { + if (v.equals(((PlatformPackage) p).getAndroidVersion())) { + // It's not already in the list of things to install, so add the + // first compatible archive we can find. + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + } + + // We end up here if nothing matches. We don't have a good platform to match. + // We need to indicate this addon depends on a missing platform archive + // so that it can be impossible to install later on. + return new MissingPlatformArchiveInfo(pkg.getAndroidVersion()); + } + + /** + * Resolves platform dependencies for extras. + * An extra depends on having a platform with a minimun API level. + * + * We try to return the highest API level available above the specified minimum. + * Note that installed packages have priority so if one installed platform satisfies + * the dependency, we'll use it even if there's a higher API platform available but + * not installed yet. + * + * Finds the platform dependency. If found, add it to the list of things to install. + * Returns the archive info dependency, if any. + */ + protected ArchiveInfo findMinApiLevelDependency( + IMinApiLevelDependency pkg, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + + int api = pkg.getMinApiLevel(); + + if (api == IMinApiLevelDependency.MIN_API_LEVEL_NOT_SPECIFIED) { + return null; + } + + // Find a platform that would satisfy the requirement. + + // First look in locally installed packages. + for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { + // We found one already installed. + return null; + } + } + } + } + + // Look in archives already scheduled for install + int foundApi = 0; + ArchiveInfo foundAi = null; + + for (ArchiveInfo ai : outArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { + if (api > foundApi) { + foundApi = api; + foundAi = ai; + } + } + } + } + } + + if (foundAi != null) { + // The dependency is already scheduled for install, nothing else to do. + return foundAi; + } + + // Otherwise look in the selected archives *or* available remote packages + // and takes the best out of the two sets. + foundApi = 0; + Archive foundArchive = null; + if (selectedArchives != null) { + for (Archive a : selectedArchives) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { + if (api > foundApi) { + foundApi = api; + foundArchive = a; + } + } + } + } + } + + // Finally nothing matched, so let's look at all available remote packages + fetchRemotePackages(remotePkgs, remoteSources); + for (Package p : remotePkgs) { + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { + if (api > foundApi) { + // It's not already in the list of things to install, so add the + // first compatible archive we can find. + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + foundApi = api; + foundArchive = a; + } + } + } + } + } + } + + if (foundArchive != null) { + // It's not already in the list of things to install, so add it now + return insertArchive(foundArchive, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + + // We end up here if nothing matches. We don't have a good platform to match. + // We need to indicate this extra depends on a missing platform archive + // so that it can be impossible to install later on. + return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/)); + } + + /** + * Resolves platform dependencies for add-ons. + * An add-ons depends on having a platform with an exact specific API level. + * + * Finds the platform dependency. If found, add it to the list of things to install. + * Returns the archive info dependency, if any. + */ + protected ArchiveInfo findExactApiLevelDependency( + IExactApiLevelDependency pkg, + Collection<ArchiveInfo> outArchives, + Collection<Archive> selectedArchives, + Collection<Package> remotePkgs, + SdkSource[] remoteSources, + ArchiveInfo[] localArchives) { + + int api = pkg.getExactApiLevel(); + + if (api == IExactApiLevelDependency.API_LEVEL_INVALID) { + return null; + } + + // Find a platform that would satisfy the requirement. + + // First look in locally installed packages. + for (ArchiveInfo ai : localArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().equals(api)) { + // We found one already installed. + return null; + } + } + } + } + + // Look in archives already scheduled for install + + for (ArchiveInfo ai : outArchives) { + Archive a = ai.getNewArchive(); + if (a != null) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().equals(api)) { + return ai; + } + } + } + } + + // Otherwise look in the selected archives. + if (selectedArchives != null) { + for (Archive a : selectedArchives) { + Package p = a.getParentPackage(); + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().equals(api)) { + // It's not already in the list of things to install, so add it now + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + + // Finally nothing matched, so let's look at all available remote packages + fetchRemotePackages(remotePkgs, remoteSources); + for (Package p : remotePkgs) { + if (p instanceof PlatformPackage) { + if (((PlatformPackage) p).getAndroidVersion().equals(api)) { + // It's not already in the list of things to install, so add the + // first compatible archive we can find. + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + return insertArchive(a, + outArchives, + selectedArchives, + remotePkgs, + remoteSources, + localArchives, + true /*automated*/); + } + } + } + } + } + + // We end up here if nothing matches. We don't have a good platform to match. + // We need to indicate this extra depends on a missing platform archive + // so that it can be impossible to install later on. + return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/)); + } + + /** + * Fetch all remote packages only if really needed. + * <p/> + * This method takes a list of sources. Each source is only fetched once -- that is each + * source keeps the list of packages that we fetched from the remote XML file. If the list + * is null, it means this source has never been fetched so we'll do it once here. Otherwise + * we rely on the cached list of packages from this source. + * <p/> + * This method also takes a remote package list as input, which it will fill out. + * If a source has already been fetched, we'll add its packages to the remote package list + * if they are not already present. Otherwise, the source will be fetched and the packages + * added to the list. + * + * @param remotePkgs An in-out list of packages available from remote sources. + * This list must not be null. + * It can be empty or already contain some packages. + * @param remoteSources A list of available remote sources to fetch from. + */ + protected void fetchRemotePackages( + final Collection<Package> remotePkgs, + final SdkSource[] remoteSources) { + if (remotePkgs.size() > 0) { + return; + } + + // First check if there's any remote source we need to fetch. + // This will bring the task window, so we rather not display it unless + // necessary. + boolean needsFetch = false; + for (final SdkSource remoteSrc : remoteSources) { + Package[] pkgs = remoteSrc.getPackages(); + if (pkgs == null) { + // This source has never been fetched. We'll do it below. + needsFetch = true; + } else { + // This source has already been fetched and we know its package list. + // We still need to make sure all of its packages are present in the + // remotePkgs list. + + nextPackage: for (Package pkg : pkgs) { + for (Archive a : pkg.getArchives()) { + // Only add a package if it contains at least one compatible archive + // and is not already in the remote package list. + if (a.isCompatible()) { + if (!remotePkgs.contains(pkg)) { + remotePkgs.add(pkg); + continue nextPackage; + } + } + } + } + } + } + + if (!needsFetch) { + return; + } + + final boolean forceHttp = mUpdaterData.getSettingsController().getSettings().getForceHttp(); + + mUpdaterData.getTaskFactory().start("Refresh Sources", new ITask() { + @Override + public void run(ITaskMonitor monitor) { + for (SdkSource remoteSrc : remoteSources) { + Package[] pkgs = remoteSrc.getPackages(); + + if (pkgs == null) { + remoteSrc.load(mUpdaterData.getDownloadCache(), monitor, forceHttp); + pkgs = remoteSrc.getPackages(); + } + + if (pkgs != null) { + nextPackage: for (Package pkg : pkgs) { + for (Archive a : pkg.getArchives()) { + // Only add a package if it contains at least one compatible archive + // and is not already in the remote package list. + if (a.isCompatible()) { + if (!remotePkgs.contains(pkg)) { + remotePkgs.add(pkg); + continue nextPackage; + } + } + } + } + } + } + } + }); + } + + + /** + * A {@link LocalArchiveInfo} is an {@link ArchiveInfo} that wraps an already installed + * "local" package/archive. + * <p/> + * In this case, the "new Archive" is still expected to be non null and the + * "replaced Archive" is null. Installed archives are always accepted and never + * rejected. + * <p/> + * Dependencies are not set. + */ + private static class LocalArchiveInfo extends ArchiveInfo { + + public LocalArchiveInfo(Archive localArchive) { + super(localArchive, null /*replaced*/, null /*dependsOn*/); + } + + /** Installed archives are always accepted. */ + @Override + public boolean isAccepted() { + return true; + } + + /** Installed archives are never rejected. */ + @Override + public boolean isRejected() { + return false; + } + } + + /** + * A {@link MissingPlatformArchiveInfo} is an {@link ArchiveInfo} that represents a + * package/archive that we <em>really</em> need as a dependency but that we don't have. + * <p/> + * This is currently used for addons and extras in case we can't find a matching base platform. + * <p/> + * This kind of archive has specific properties: the new archive to install is null, + * there are no dependencies and no archive is being replaced. The info can never be + * accepted and is always rejected. + */ + private static class MissingPlatformArchiveInfo extends ArchiveInfo { + + private final AndroidVersion mVersion; + + /** + * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the + * given platform version is missing. + */ + public MissingPlatformArchiveInfo(AndroidVersion version) { + super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/); + mVersion = version; + } + + /** Missing archives are never accepted. */ + @Override + public boolean isAccepted() { + return false; + } + + /** Missing archives are always rejected. */ + @Override + public boolean isRejected() { + return true; + } + + @Override + public String getShortDescription() { + return String.format("Missing SDK Platform Android%1$s, API %2$d", + mVersion.isPreview() ? " Preview" : "", + mVersion.getApiLevel()); + } + } + + /** + * A {@link MissingArchiveInfo} is an {@link ArchiveInfo} that represents a + * package/archive that we <em>really</em> need as a dependency but that we don't have. + * <p/> + * This is currently used for extras in case we can't find a matching tool revision + * or when a platform-tool is missing. + * <p/> + * This kind of archive has specific properties: the new archive to install is null, + * there are no dependencies and no archive is being replaced. The info can never be + * accepted and is always rejected. + */ + private static class MissingArchiveInfo extends ArchiveInfo { + + private final FullRevision mRevision; + private final String mTitle; + + public static final String TITLE_TOOL = "Tools"; + public static final String TITLE_PLATFORM_TOOL = "Platform-tools"; + + /** + * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the + * given platform version is missing. + * + * @param title Typically "Tools" or "Platform-tools". + * @param revision The required revision. + */ + public MissingArchiveInfo(String title, FullRevision revision) { + super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/); + mTitle = title; + mRevision = revision; + } + + /** Missing archives are never accepted. */ + @Override + public boolean isAccepted() { + return false; + } + + /** Missing archives are always rejected. */ + @Override + public boolean isRejected() { + return true; + } + + @Override + public String getShortDescription() { + return String.format("Missing Android SDK %1$s, revision %2$s", + mTitle, + mRevision.toShortString()); + } + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java index 09edca8..513d159 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java @@ -1,382 +1,382 @@ -/*
- * 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.sdkuilib.internal.repository;
-
-import com.android.annotations.NonNull;
-import com.android.prefs.AndroidLocation;
-import com.android.prefs.AndroidLocation.AndroidLocationException;
-import com.android.sdklib.ISdkLog;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Properties;
-
-/**
- * Controller class to get settings values. Settings are kept in-memory.
- * Users of this class must first load the settings before changing them and save
- * them when modified.
- * <p/>
- * Settings are enumerated by constants in {@link ISettingsPage}.
- */
-public class SettingsController {
-
- private static final String SETTINGS_FILENAME = "androidtool.cfg"; //$NON-NLS-1$
-
- private final ISdkLog mSdkLog;
- private final Settings mSettings;
-
- public interface OnChangedListener {
- public void onSettingsChanged(SettingsController controller, Settings oldSettings);
- }
- private final List<OnChangedListener> mChangedListeners = new ArrayList<OnChangedListener>(1);
-
- /** The currently associated {@link ISettingsPage}. Can be null. */
- private ISettingsPage mSettingsPage;
-
- /**
- * Constructs a new default {@link SettingsController}.
- *
- * @param sdkLog A non-null logger to use.
- */
- public SettingsController(@NonNull ISdkLog sdkLog) {
- mSdkLog = sdkLog;
- mSettings = new Settings();
- }
-
- /**
- * Specialized constructor that wraps an existing {@link Settings} instance.
- * This is mostly used in unit-tests to override settings that are being used.
- * Normal usage should NOT need to call this constructor.
- *
- * @param sdkLog A non-null logger to use.
- * @param settings A non-null {@link Settings} to use as-is. It is not duplicated.
- */
- protected SettingsController(@NonNull ISdkLog sdkLog, @NonNull Settings settings) {
- mSdkLog = sdkLog;
- mSettings = settings;
- }
-
- public Settings getSettings() {
- return mSettings;
- }
-
- public void registerOnChangedListener(OnChangedListener listener) {
- if (listener != null && !mChangedListeners.contains(listener)) {
- mChangedListeners.add(listener);
- }
- }
-
- public void unregisterOnChangedListener(OnChangedListener listener) {
- if (listener != null) {
- mChangedListeners.remove(listener);
- }
- }
-
- //--- Access to settings ------------
-
-
- public static class Settings {
- private final Properties mProperties;
-
- /** Initialize an empty set of settings. */
- public Settings() {
- mProperties = new Properties();
- }
-
- /** Duplicates a set of settings. */
- public Settings(Settings settings) {
- this();
- for (Entry<Object, Object> entry : settings.mProperties.entrySet()) {
- mProperties.put(entry.getKey(), entry.getValue());
- }
- }
-
- /**
- * Specialized constructor for unit-tests that wraps an existing
- * {@link Properties} instance. The properties instance is not duplicated,
- * it's merely used as-is and changes will be reflected directly.
- */
- protected Settings(Properties properties) {
- mProperties = properties;
- }
-
- /**
- * Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting.
- *
- * @see ISettingsPage#KEY_FORCE_HTTP
- */
- public boolean getForceHttp() {
- return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));
- }
-
- /**
- * Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting.
- *
- * @see ISettingsPage#KEY_ASK_ADB_RESTART
- */
- public boolean getAskBeforeAdbRestart() {
- return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART));
- }
-
- /**
- * Returns the value of the {@link ISettingsPage#KEY_USE_DOWNLOAD_CACHE} setting.
- *
- * @see ISettingsPage#KEY_USE_DOWNLOAD_CACHE
- */
- public boolean getUseDownloadCache() {
- return Boolean.parseBoolean(
- mProperties.getProperty(
- ISettingsPage.KEY_USE_DOWNLOAD_CACHE,
- Boolean.TRUE.toString()));
- }
-
- /**
- * Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting.
- *
- * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
- */
- public boolean getShowUpdateOnly() {
- return Boolean.parseBoolean(
- mProperties.getProperty(
- ISettingsPage.KEY_SHOW_UPDATE_ONLY,
- Boolean.TRUE.toString()));
- }
-
- /**
- * Returns the value of the {@link ISettingsPage#KEY_ENABLE_PREVIEWS} setting.
- *
- * @see ISettingsPage#KEY_ENABLE_PREVIEWS
- */
- public boolean getEnablePreviews() {
- return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ENABLE_PREVIEWS));
- }
-
- /**
- * Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting
- * @see ISettingsPage#KEY_MONITOR_DENSITY
- */
- public int getMonitorDensity() {
- String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null);
- if (value == null) {
- return -1;
- }
-
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return -1;
- }
- }
- }
-
- /**
- * Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting.
- *
- * @param enabled True if only compatible non-obsolete update items should be shown.
- * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
- */
- public void setShowUpdateOnly(boolean enabled) {
- setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);
- }
-
- /**
- * Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting.
- *
- * @param density the density of the monitor
- * @see ISettingsPage#KEY_MONITOR_DENSITY
- */
- public void setMonitorDensity(int density) {
- mSettings.mProperties.setProperty(
- ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density));
- }
-
- /**
- * Internal helper to set a boolean setting.
- */
- void setSetting(String key, boolean value) {
- mSettings.mProperties.setProperty(key, Boolean.toString(value));
- }
-
- //--- Controller methods -------------
-
- /**
- * Associate the given {@link ISettingsPage} with this {@link SettingsController}.
- * <p/>
- * This loads the current properties into the setting page UI.
- * It then associates the SettingsChanged callback with this controller.
- * <p/>
- * If the setting page given is null, it will be unlinked from controller.
- *
- * @param settingsPage An {@link ISettingsPage} to associate with the controller.
- */
- public void setSettingsPage(ISettingsPage settingsPage) {
- mSettingsPage = settingsPage;
-
- if (settingsPage != null) {
- settingsPage.loadSettings(mSettings.mProperties);
-
- settingsPage.setOnSettingsChanged(new ISettingsPage.SettingsChangedCallback() {
- @Override
- public void onSettingsChanged(ISettingsPage page) {
- SettingsController.this.onSettingsChanged();
- }
- });
- }
- }
-
- /**
- * Load settings from the settings file.
- */
- public void loadSettings() {
- FileInputStream fis = null;
- String path = null;
- try {
- String folder = AndroidLocation.getFolder();
- File f = new File(folder, SETTINGS_FILENAME);
- path = f.getPath();
- if (f.exists()) {
- fis = new FileInputStream(f);
-
- mSettings.mProperties.load(fis);
-
- // Properly reformat some settings to enforce their default value when missing.
- setShowUpdateOnly(mSettings.getShowUpdateOnly());
- setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, mSettings.getAskBeforeAdbRestart());
- setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, mSettings.getUseDownloadCache());
- }
-
- } catch (Exception e) {
- if (mSdkLog != null) {
- mSdkLog.error(e,
- "Failed to load settings from .android folder. Path is '%1$s'.",
- path);
- }
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- /**
- * Saves settings to the settings file.
- */
- public void saveSettings() {
-
- FileOutputStream fos = null;
- String path = null;
- try {
- String folder = AndroidLocation.getFolder();
- File f = new File(folder, SETTINGS_FILENAME);
- path = f.getPath();
-
- fos = new FileOutputStream(f);
-
- mSettings.mProperties.store(fos, "## Settings for Android Tool"); //$NON-NLS-1$
-
- } catch (Exception e) {
- if (mSdkLog != null) {
- // This is important enough that we want to really nag the user about it
- String reason = null;
-
- if (e instanceof FileNotFoundException) {
- reason = "File not found";
- } else if (e instanceof AndroidLocationException) {
- reason = ".android folder not found, please define ANDROID_SDK_HOME";
- } else if (e.getMessage() != null) {
- reason = String.format("%1$s: %2$s",
- e.getClass().getSimpleName(),
- e.getMessage());
- } else {
- reason = e.getClass().getName();
- }
-
- mSdkLog.error(e, "Failed to save settings file '%1$s': %2$s", path, reason);
- }
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- /**
- * When settings have changed: retrieve the new settings, apply them, save them
- * and notify on-settings-changed listeners.
- */
- private void onSettingsChanged() {
- if (mSettingsPage == null) {
- return;
- }
-
- Settings oldSettings = new Settings(mSettings);
- mSettingsPage.retrieveSettings(mSettings.mProperties);
- applySettings();
- saveSettings();
-
- for (OnChangedListener listener : mChangedListeners) {
- try {
- listener.onSettingsChanged(this, oldSettings);
- } catch (Throwable ignore) {}
- }
- }
-
- /**
- * Applies the current settings.
- */
- public void applySettings() {
- Properties props = System.getProperties();
-
- // Get the configured HTTP proxy settings
- String proxyHost = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_HOST,
- ""); //$NON-NLS-1$
- String proxyPort = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_PORT,
- ""); //$NON-NLS-1$
-
- // Set both the HTTP and HTTPS proxy system properties.
- // The system property constants can be found in the Java SE documentation at
- // http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
- final String JAVA_PROP_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$
- final String JAVA_PROP_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$
- final String JAVA_PROP_HTTPS_PROXY_HOST = "https.proxyHost"; //$NON-NLS-1$
- final String JAVA_PROP_HTTPS_PROXY_PORT = "https.proxyPort"; //$NON-NLS-1$
-
- // Only change the proxy if have something in the preferences.
- // Do not erase the default settings by empty values.
- if (proxyHost != null && proxyHost.length() > 0) {
- props.setProperty(JAVA_PROP_HTTP_PROXY_HOST, proxyHost);
- props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost);
- }
- if (proxyPort != null && proxyPort.length() > 0) {
- props.setProperty(JAVA_PROP_HTTP_PROXY_PORT, proxyPort);
- props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort);
- }
- }
-
-}
+/* + * 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.sdkuilib.internal.repository; + +import com.android.annotations.NonNull; +import com.android.prefs.AndroidLocation; +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.ISdkLog; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Properties; + +/** + * Controller class to get settings values. Settings are kept in-memory. + * Users of this class must first load the settings before changing them and save + * them when modified. + * <p/> + * Settings are enumerated by constants in {@link ISettingsPage}. + */ +public class SettingsController { + + private static final String SETTINGS_FILENAME = "androidtool.cfg"; //$NON-NLS-1$ + + private final ISdkLog mSdkLog; + private final Settings mSettings; + + public interface OnChangedListener { + public void onSettingsChanged(SettingsController controller, Settings oldSettings); + } + private final List<OnChangedListener> mChangedListeners = new ArrayList<OnChangedListener>(1); + + /** The currently associated {@link ISettingsPage}. Can be null. */ + private ISettingsPage mSettingsPage; + + /** + * Constructs a new default {@link SettingsController}. + * + * @param sdkLog A non-null logger to use. + */ + public SettingsController(@NonNull ISdkLog sdkLog) { + mSdkLog = sdkLog; + mSettings = new Settings(); + } + + /** + * Specialized constructor that wraps an existing {@link Settings} instance. + * This is mostly used in unit-tests to override settings that are being used. + * Normal usage should NOT need to call this constructor. + * + * @param sdkLog A non-null logger to use. + * @param settings A non-null {@link Settings} to use as-is. It is not duplicated. + */ + protected SettingsController(@NonNull ISdkLog sdkLog, @NonNull Settings settings) { + mSdkLog = sdkLog; + mSettings = settings; + } + + public Settings getSettings() { + return mSettings; + } + + public void registerOnChangedListener(OnChangedListener listener) { + if (listener != null && !mChangedListeners.contains(listener)) { + mChangedListeners.add(listener); + } + } + + public void unregisterOnChangedListener(OnChangedListener listener) { + if (listener != null) { + mChangedListeners.remove(listener); + } + } + + //--- Access to settings ------------ + + + public static class Settings { + private final Properties mProperties; + + /** Initialize an empty set of settings. */ + public Settings() { + mProperties = new Properties(); + } + + /** Duplicates a set of settings. */ + public Settings(Settings settings) { + this(); + for (Entry<Object, Object> entry : settings.mProperties.entrySet()) { + mProperties.put(entry.getKey(), entry.getValue()); + } + } + + /** + * Specialized constructor for unit-tests that wraps an existing + * {@link Properties} instance. The properties instance is not duplicated, + * it's merely used as-is and changes will be reflected directly. + */ + protected Settings(Properties properties) { + mProperties = properties; + } + + /** + * Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting. + * + * @see ISettingsPage#KEY_FORCE_HTTP + */ + public boolean getForceHttp() { + return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP)); + } + + /** + * Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting. + * + * @see ISettingsPage#KEY_ASK_ADB_RESTART + */ + public boolean getAskBeforeAdbRestart() { + return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART)); + } + + /** + * Returns the value of the {@link ISettingsPage#KEY_USE_DOWNLOAD_CACHE} setting. + * + * @see ISettingsPage#KEY_USE_DOWNLOAD_CACHE + */ + public boolean getUseDownloadCache() { + return Boolean.parseBoolean( + mProperties.getProperty( + ISettingsPage.KEY_USE_DOWNLOAD_CACHE, + Boolean.TRUE.toString())); + } + + /** + * Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. + * + * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY + */ + public boolean getShowUpdateOnly() { + return Boolean.parseBoolean( + mProperties.getProperty( + ISettingsPage.KEY_SHOW_UPDATE_ONLY, + Boolean.TRUE.toString())); + } + + /** + * Returns the value of the {@link ISettingsPage#KEY_ENABLE_PREVIEWS} setting. + * + * @see ISettingsPage#KEY_ENABLE_PREVIEWS + */ + public boolean getEnablePreviews() { + return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ENABLE_PREVIEWS)); + } + + /** + * Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting + * @see ISettingsPage#KEY_MONITOR_DENSITY + */ + public int getMonitorDensity() { + String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null); + if (value == null) { + return -1; + } + + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return -1; + } + } + } + + /** + * Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. + * + * @param enabled True if only compatible non-obsolete update items should be shown. + * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY + */ + public void setShowUpdateOnly(boolean enabled) { + setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled); + } + + /** + * Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting. + * + * @param density the density of the monitor + * @see ISettingsPage#KEY_MONITOR_DENSITY + */ + public void setMonitorDensity(int density) { + mSettings.mProperties.setProperty( + ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density)); + } + + /** + * Internal helper to set a boolean setting. + */ + void setSetting(String key, boolean value) { + mSettings.mProperties.setProperty(key, Boolean.toString(value)); + } + + //--- Controller methods ------------- + + /** + * Associate the given {@link ISettingsPage} with this {@link SettingsController}. + * <p/> + * This loads the current properties into the setting page UI. + * It then associates the SettingsChanged callback with this controller. + * <p/> + * If the setting page given is null, it will be unlinked from controller. + * + * @param settingsPage An {@link ISettingsPage} to associate with the controller. + */ + public void setSettingsPage(ISettingsPage settingsPage) { + mSettingsPage = settingsPage; + + if (settingsPage != null) { + settingsPage.loadSettings(mSettings.mProperties); + + settingsPage.setOnSettingsChanged(new ISettingsPage.SettingsChangedCallback() { + @Override + public void onSettingsChanged(ISettingsPage page) { + SettingsController.this.onSettingsChanged(); + } + }); + } + } + + /** + * Load settings from the settings file. + */ + public void loadSettings() { + FileInputStream fis = null; + String path = null; + try { + String folder = AndroidLocation.getFolder(); + File f = new File(folder, SETTINGS_FILENAME); + path = f.getPath(); + if (f.exists()) { + fis = new FileInputStream(f); + + mSettings.mProperties.load(fis); + + // Properly reformat some settings to enforce their default value when missing. + setShowUpdateOnly(mSettings.getShowUpdateOnly()); + setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, mSettings.getAskBeforeAdbRestart()); + setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, mSettings.getUseDownloadCache()); + } + + } catch (Exception e) { + if (mSdkLog != null) { + mSdkLog.error(e, + "Failed to load settings from .android folder. Path is '%1$s'.", + path); + } + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + } + } + } + } + + /** + * Saves settings to the settings file. + */ + public void saveSettings() { + + FileOutputStream fos = null; + String path = null; + try { + String folder = AndroidLocation.getFolder(); + File f = new File(folder, SETTINGS_FILENAME); + path = f.getPath(); + + fos = new FileOutputStream(f); + + mSettings.mProperties.store(fos, "## Settings for Android Tool"); //$NON-NLS-1$ + + } catch (Exception e) { + if (mSdkLog != null) { + // This is important enough that we want to really nag the user about it + String reason = null; + + if (e instanceof FileNotFoundException) { + reason = "File not found"; + } else if (e instanceof AndroidLocationException) { + reason = ".android folder not found, please define ANDROID_SDK_HOME"; + } else if (e.getMessage() != null) { + reason = String.format("%1$s: %2$s", + e.getClass().getSimpleName(), + e.getMessage()); + } else { + reason = e.getClass().getName(); + } + + mSdkLog.error(e, "Failed to save settings file '%1$s': %2$s", path, reason); + } + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + } + } + } + } + + /** + * When settings have changed: retrieve the new settings, apply them, save them + * and notify on-settings-changed listeners. + */ + private void onSettingsChanged() { + if (mSettingsPage == null) { + return; + } + + Settings oldSettings = new Settings(mSettings); + mSettingsPage.retrieveSettings(mSettings.mProperties); + applySettings(); + saveSettings(); + + for (OnChangedListener listener : mChangedListeners) { + try { + listener.onSettingsChanged(this, oldSettings); + } catch (Throwable ignore) {} + } + } + + /** + * Applies the current settings. + */ + public void applySettings() { + Properties props = System.getProperties(); + + // Get the configured HTTP proxy settings + String proxyHost = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_HOST, + ""); //$NON-NLS-1$ + String proxyPort = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_PORT, + ""); //$NON-NLS-1$ + + // Set both the HTTP and HTTPS proxy system properties. + // The system property constants can be found in the Java SE documentation at + // http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html + final String JAVA_PROP_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$ + final String JAVA_PROP_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$ + final String JAVA_PROP_HTTPS_PROXY_HOST = "https.proxyHost"; //$NON-NLS-1$ + final String JAVA_PROP_HTTPS_PROXY_PORT = "https.proxyPort"; //$NON-NLS-1$ + + // Only change the proxy if have something in the preferences. + // Do not erase the default settings by empty values. + if (proxyHost != null && proxyHost.length() > 0) { + props.setProperty(JAVA_PROP_HTTP_PROXY_HOST, proxyHost); + props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost); + } + if (proxyPort != null && proxyPort.length() > 0) { + props.setProperty(JAVA_PROP_HTTP_PROXY_PORT, proxyPort); + props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort); + } + } + +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsDialog.java index 350022e..7f44b7c 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsDialog.java @@ -1,275 +1,275 @@ -/*
- * 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;
-
-import com.android.sdklib.internal.repository.DownloadCache;
-import com.android.sdklib.internal.repository.DownloadCache.Strategy;
-import com.android.sdklib.util.FormatUtils;
-import com.android.sdkuilib.ui.GridDataBuilder;
-import com.android.sdkuilib.ui.GridLayoutBuilder;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-import java.util.Properties;
-
-
-public class SettingsDialog extends UpdaterBaseDialog implements ISettingsPage {
-
-
- // data members
- private final DownloadCache mDownloadCache = new DownloadCache(Strategy.SERVE_CACHE);
- private final SettingsController mSettingsController;
- private SettingsChangedCallback mSettingsChangedCallback;
-
- // UI widgets
- private Text mTextProxyServer;
- private Text mTextProxyPort;
- private Text mTextCacheSize;
- private Button mCheckUseCache;
- private Button mCheckForceHttp;
- private Button mCheckAskAdbRestart;
- private Button mCheckEnablePreviews;
-
- private SelectionAdapter mApplyOnSelected = new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- applyNewSettings(); //$hide$
- }
- };
-
- private ModifyListener mApplyOnModified = new ModifyListener() {
- @Override
- public void modifyText(ModifyEvent e) {
- applyNewSettings(); //$hide$
- }
- };
-
- public SettingsDialog(Shell parentShell, UpdaterData updaterData) {
- super(parentShell, updaterData, "Settings" /*title*/);
- assert updaterData != null;
- mSettingsController = updaterData.getSettingsController();
- }
-
- @Override
- protected void createContents() {
- super.createContents();
- Shell shell = getShell();
-
- Group group = new Group(shell, SWT.NONE);
- group.setText("Proxy Settings");
- GridDataBuilder.create(group).fill().grab().hSpan(2);
- GridLayoutBuilder.create(group).columns(2);
-
- Label label = new Label(group, SWT.NONE);
- GridDataBuilder.create(label).hRight().vCenter();
- label.setText("HTTP Proxy Server");
- String tooltip = "The hostname or IP of the HTTP & HTTPS proxy server to use (e.g. proxy.example.com).\n" +
- "When empty, the default Java proxy setting is used.";
- label.setToolTipText(tooltip);
-
- mTextProxyServer = new Text(group, SWT.BORDER);
- GridDataBuilder.create(mTextProxyServer).hFill().hGrab().vCenter();
- mTextProxyServer.addModifyListener(mApplyOnModified);
- mTextProxyServer.setToolTipText(tooltip);
-
- label = new Label(group, SWT.NONE);
- GridDataBuilder.create(label).hRight().vCenter();
- label.setText("HTTP Proxy Port");
- tooltip = "The port of the HTTP & HTTPS proxy server to use (e.g. 3128).\n" +
- "When empty, the default Java proxy setting is used.";
- label.setToolTipText(tooltip);
-
- mTextProxyPort = new Text(group, SWT.BORDER);
- GridDataBuilder.create(mTextProxyPort).hFill().hGrab().vCenter();
- mTextProxyPort.addModifyListener(mApplyOnModified);
- mTextProxyPort.setToolTipText(tooltip);
-
- // ----
- group = new Group(shell, SWT.NONE);
- group.setText("Manifest Cache");
- GridDataBuilder.create(group).fill().grab().hSpan(2);
- GridLayoutBuilder.create(group).columns(3);
-
- label = new Label(group, SWT.NONE);
- GridDataBuilder.create(label).hRight().vCenter();
- label.setText("Directory:");
-
- Text text = new Text(group, SWT.NONE);
- GridDataBuilder.create(text).hFill().hGrab().vCenter().hSpan(2);
- text.setEnabled(false);
- text.setText(mDownloadCache.getCacheRoot().getAbsolutePath());
-
- label = new Label(group, SWT.NONE);
- GridDataBuilder.create(label).hRight().vCenter();
- label.setText("Current Size:");
-
- mTextCacheSize = new Text(group, SWT.NONE);
- GridDataBuilder.create(mTextCacheSize).hFill().hGrab().vCenter().hSpan(2);
- mTextCacheSize.setEnabled(false);
- updateDownloadCacheSize();
-
- mCheckUseCache = new Button(group, SWT.CHECK);
- GridDataBuilder.create(mCheckUseCache).vCenter().hSpan(1);
- mCheckUseCache.setText("Use download cache");
- mCheckUseCache.setToolTipText("When checked, small manifest files are cached locally.\n" +
- "Large binary files are never cached locally.");
- mCheckUseCache.addSelectionListener(mApplyOnSelected);
-
- label = new Label(group, SWT.NONE);
- GridDataBuilder.create(label).hFill().hGrab().hSpan(1);
-
- Button button = new Button(group, SWT.PUSH);
- GridDataBuilder.create(button).vCenter().hSpan(1);
- button.setText("Clear Cache");
- button.setToolTipText("Deletes all cached files.");
- button.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- mDownloadCache.clearCache();
- updateDownloadCacheSize();
- }
- });
-
- // ----
- group = new Group(shell, SWT.NONE);
- group.setText("Others");
- GridDataBuilder.create(group).fill().grab().hSpan(2);
- GridLayoutBuilder.create(group).columns(2);
-
- mCheckForceHttp = new Button(group, SWT.CHECK);
- GridDataBuilder.create(mCheckForceHttp).hFill().hGrab().vCenter().hSpan(2);
- mCheckForceHttp.setText("Force https://... sources to be fetched using http://...");
- mCheckForceHttp.setToolTipText(
- "If you are not able to connect to the official Android repository using HTTPS,\n" +
- "enable this setting to force accessing it via HTTP.");
- mCheckForceHttp.addSelectionListener(mApplyOnSelected);
-
- mCheckAskAdbRestart = new Button(group, SWT.CHECK);
- GridDataBuilder.create(mCheckAskAdbRestart).hFill().hGrab().vCenter().hSpan(2);
- mCheckAskAdbRestart.setText("Ask before restarting ADB");
- mCheckAskAdbRestart.setToolTipText(
- "When checked, the user will be asked for permission to restart ADB\n" +
- "after updating an addon-on package or a tool package.");
- mCheckAskAdbRestart.addSelectionListener(mApplyOnSelected);
-
- mCheckEnablePreviews = new Button(group, SWT.CHECK);
- GridDataBuilder.create(mCheckEnablePreviews).hFill().hGrab().vCenter().hSpan(2);
- mCheckEnablePreviews.setText("Enable Preview Tools");
- mCheckEnablePreviews.setToolTipText(
- "When checked, the package list will also display preview versions of the tools.\n" +
- "These are optional future release candidates that the Android tools team\n" +
- "publishes from time to time for early feedback.");
- mCheckEnablePreviews.addSelectionListener(mApplyOnSelected);
-
- Label filler = new Label(shell, SWT.NONE);
- GridDataBuilder.create(filler).hFill().hGrab();
-
- createCloseButton();
- }
-
- @Override
- protected void postCreate() {
- super.postCreate();
- // This tells the controller to load the settings into the page UI.
- mSettingsController.setSettingsPage(this);
- }
-
- @Override
- protected void close() {
- // Dissociate this page from the controller
- mSettingsController.setSettingsPage(null);
- super.close();
- }
-
-
- // -- Start of internal part ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- /** Loads settings from the given {@link Properties} container and update the page UI. */
- @Override
- public void loadSettings(Properties inSettings) {
- mTextProxyServer.setText(inSettings.getProperty(KEY_HTTP_PROXY_HOST, "")); //$NON-NLS-1$
- mTextProxyPort.setText( inSettings.getProperty(KEY_HTTP_PROXY_PORT, "")); //$NON-NLS-1$
- mCheckForceHttp.setSelection(
- Boolean.parseBoolean(inSettings.getProperty(KEY_FORCE_HTTP)));
- mCheckAskAdbRestart.setSelection(
- Boolean.parseBoolean(inSettings.getProperty(KEY_ASK_ADB_RESTART)));
- mCheckUseCache.setSelection(
- Boolean.parseBoolean(inSettings.getProperty(KEY_USE_DOWNLOAD_CACHE)));
- mCheckEnablePreviews.setSelection(
- Boolean.parseBoolean(inSettings.getProperty(KEY_ENABLE_PREVIEWS)));
-
- }
-
- /** Called by the application to retrieve settings from the UI and store them in
- * the given {@link Properties} container. */
- @Override
- public void retrieveSettings(Properties outSettings) {
- outSettings.setProperty(KEY_HTTP_PROXY_HOST, mTextProxyServer.getText());
- outSettings.setProperty(KEY_HTTP_PROXY_PORT, mTextProxyPort.getText());
- outSettings.setProperty(KEY_FORCE_HTTP,
- Boolean.toString(mCheckForceHttp.getSelection()));
- outSettings.setProperty(KEY_ASK_ADB_RESTART,
- Boolean.toString(mCheckAskAdbRestart.getSelection()));
- outSettings.setProperty(KEY_USE_DOWNLOAD_CACHE,
- Boolean.toString(mCheckUseCache.getSelection()));
- outSettings.setProperty(KEY_ENABLE_PREVIEWS,
- Boolean.toString(mCheckEnablePreviews.getSelection()));
-
- }
-
- /**
- * Called by the application to give a callback that the page should invoke when
- * settings must be applied. The page does not apply the settings itself, instead
- * it notifies the application.
- */
- @Override
- public void setOnSettingsChanged(SettingsChangedCallback settingsChangedCallback) {
- mSettingsChangedCallback = settingsChangedCallback;
- }
-
- /**
- * Callback invoked when user touches one of the settings.
- * There is no "Apply" button, settings are applied immediately as they are changed.
- * Notify the application that settings have changed.
- */
- private void applyNewSettings() {
- if (mSettingsChangedCallback != null) {
- mSettingsChangedCallback.onSettingsChanged(this);
- }
- }
-
- private void updateDownloadCacheSize() {
- long size = mDownloadCache.getCurrentSize();
- String str = FormatUtils.byteSizeToString(size);
- mTextCacheSize.setText(str);
- }
-
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/* + * 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; + +import com.android.sdklib.internal.repository.DownloadCache; +import com.android.sdklib.internal.repository.DownloadCache.Strategy; +import com.android.sdklib.util.FormatUtils; +import com.android.sdkuilib.ui.GridDataBuilder; +import com.android.sdkuilib.ui.GridLayoutBuilder; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import java.util.Properties; + + +public class SettingsDialog extends UpdaterBaseDialog implements ISettingsPage { + + + // data members + private final DownloadCache mDownloadCache = new DownloadCache(Strategy.SERVE_CACHE); + private final SettingsController mSettingsController; + private SettingsChangedCallback mSettingsChangedCallback; + + // UI widgets + private Text mTextProxyServer; + private Text mTextProxyPort; + private Text mTextCacheSize; + private Button mCheckUseCache; + private Button mCheckForceHttp; + private Button mCheckAskAdbRestart; + private Button mCheckEnablePreviews; + + private SelectionAdapter mApplyOnSelected = new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + applyNewSettings(); //$hide$ + } + }; + + private ModifyListener mApplyOnModified = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + applyNewSettings(); //$hide$ + } + }; + + public SettingsDialog(Shell parentShell, UpdaterData updaterData) { + super(parentShell, updaterData, "Settings" /*title*/); + assert updaterData != null; + mSettingsController = updaterData.getSettingsController(); + } + + @Override + protected void createContents() { + super.createContents(); + Shell shell = getShell(); + + Group group = new Group(shell, SWT.NONE); + group.setText("Proxy Settings"); + GridDataBuilder.create(group).fill().grab().hSpan(2); + GridLayoutBuilder.create(group).columns(2); + + Label label = new Label(group, SWT.NONE); + GridDataBuilder.create(label).hRight().vCenter(); + label.setText("HTTP Proxy Server"); + String tooltip = "The hostname or IP of the HTTP & HTTPS proxy server to use (e.g. proxy.example.com).\n" + + "When empty, the default Java proxy setting is used."; + label.setToolTipText(tooltip); + + mTextProxyServer = new Text(group, SWT.BORDER); + GridDataBuilder.create(mTextProxyServer).hFill().hGrab().vCenter(); + mTextProxyServer.addModifyListener(mApplyOnModified); + mTextProxyServer.setToolTipText(tooltip); + + label = new Label(group, SWT.NONE); + GridDataBuilder.create(label).hRight().vCenter(); + label.setText("HTTP Proxy Port"); + tooltip = "The port of the HTTP & HTTPS proxy server to use (e.g. 3128).\n" + + "When empty, the default Java proxy setting is used."; + label.setToolTipText(tooltip); + + mTextProxyPort = new Text(group, SWT.BORDER); + GridDataBuilder.create(mTextProxyPort).hFill().hGrab().vCenter(); + mTextProxyPort.addModifyListener(mApplyOnModified); + mTextProxyPort.setToolTipText(tooltip); + + // ---- + group = new Group(shell, SWT.NONE); + group.setText("Manifest Cache"); + GridDataBuilder.create(group).fill().grab().hSpan(2); + GridLayoutBuilder.create(group).columns(3); + + label = new Label(group, SWT.NONE); + GridDataBuilder.create(label).hRight().vCenter(); + label.setText("Directory:"); + + Text text = new Text(group, SWT.NONE); + GridDataBuilder.create(text).hFill().hGrab().vCenter().hSpan(2); + text.setEnabled(false); + text.setText(mDownloadCache.getCacheRoot().getAbsolutePath()); + + label = new Label(group, SWT.NONE); + GridDataBuilder.create(label).hRight().vCenter(); + label.setText("Current Size:"); + + mTextCacheSize = new Text(group, SWT.NONE); + GridDataBuilder.create(mTextCacheSize).hFill().hGrab().vCenter().hSpan(2); + mTextCacheSize.setEnabled(false); + updateDownloadCacheSize(); + + mCheckUseCache = new Button(group, SWT.CHECK); + GridDataBuilder.create(mCheckUseCache).vCenter().hSpan(1); + mCheckUseCache.setText("Use download cache"); + mCheckUseCache.setToolTipText("When checked, small manifest files are cached locally.\n" + + "Large binary files are never cached locally."); + mCheckUseCache.addSelectionListener(mApplyOnSelected); + + label = new Label(group, SWT.NONE); + GridDataBuilder.create(label).hFill().hGrab().hSpan(1); + + Button button = new Button(group, SWT.PUSH); + GridDataBuilder.create(button).vCenter().hSpan(1); + button.setText("Clear Cache"); + button.setToolTipText("Deletes all cached files."); + button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent arg0) { + mDownloadCache.clearCache(); + updateDownloadCacheSize(); + } + }); + + // ---- + group = new Group(shell, SWT.NONE); + group.setText("Others"); + GridDataBuilder.create(group).fill().grab().hSpan(2); + GridLayoutBuilder.create(group).columns(2); + + mCheckForceHttp = new Button(group, SWT.CHECK); + GridDataBuilder.create(mCheckForceHttp).hFill().hGrab().vCenter().hSpan(2); + mCheckForceHttp.setText("Force https://... sources to be fetched using http://..."); + mCheckForceHttp.setToolTipText( + "If you are not able to connect to the official Android repository using HTTPS,\n" + + "enable this setting to force accessing it via HTTP."); + mCheckForceHttp.addSelectionListener(mApplyOnSelected); + + mCheckAskAdbRestart = new Button(group, SWT.CHECK); + GridDataBuilder.create(mCheckAskAdbRestart).hFill().hGrab().vCenter().hSpan(2); + mCheckAskAdbRestart.setText("Ask before restarting ADB"); + mCheckAskAdbRestart.setToolTipText( + "When checked, the user will be asked for permission to restart ADB\n" + + "after updating an addon-on package or a tool package."); + mCheckAskAdbRestart.addSelectionListener(mApplyOnSelected); + + mCheckEnablePreviews = new Button(group, SWT.CHECK); + GridDataBuilder.create(mCheckEnablePreviews).hFill().hGrab().vCenter().hSpan(2); + mCheckEnablePreviews.setText("Enable Preview Tools"); + mCheckEnablePreviews.setToolTipText( + "When checked, the package list will also display preview versions of the tools.\n" + + "These are optional future release candidates that the Android tools team\n" + + "publishes from time to time for early feedback."); + mCheckEnablePreviews.addSelectionListener(mApplyOnSelected); + + Label filler = new Label(shell, SWT.NONE); + GridDataBuilder.create(filler).hFill().hGrab(); + + createCloseButton(); + } + + @Override + protected void postCreate() { + super.postCreate(); + // This tells the controller to load the settings into the page UI. + mSettingsController.setSettingsPage(this); + } + + @Override + protected void close() { + // Dissociate this page from the controller + mSettingsController.setSettingsPage(null); + super.close(); + } + + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + /** Loads settings from the given {@link Properties} container and update the page UI. */ + @Override + public void loadSettings(Properties inSettings) { + mTextProxyServer.setText(inSettings.getProperty(KEY_HTTP_PROXY_HOST, "")); //$NON-NLS-1$ + mTextProxyPort.setText( inSettings.getProperty(KEY_HTTP_PROXY_PORT, "")); //$NON-NLS-1$ + mCheckForceHttp.setSelection( + Boolean.parseBoolean(inSettings.getProperty(KEY_FORCE_HTTP))); + mCheckAskAdbRestart.setSelection( + Boolean.parseBoolean(inSettings.getProperty(KEY_ASK_ADB_RESTART))); + mCheckUseCache.setSelection( + Boolean.parseBoolean(inSettings.getProperty(KEY_USE_DOWNLOAD_CACHE))); + mCheckEnablePreviews.setSelection( + Boolean.parseBoolean(inSettings.getProperty(KEY_ENABLE_PREVIEWS))); + + } + + /** Called by the application to retrieve settings from the UI and store them in + * the given {@link Properties} container. */ + @Override + public void retrieveSettings(Properties outSettings) { + outSettings.setProperty(KEY_HTTP_PROXY_HOST, mTextProxyServer.getText()); + outSettings.setProperty(KEY_HTTP_PROXY_PORT, mTextProxyPort.getText()); + outSettings.setProperty(KEY_FORCE_HTTP, + Boolean.toString(mCheckForceHttp.getSelection())); + outSettings.setProperty(KEY_ASK_ADB_RESTART, + Boolean.toString(mCheckAskAdbRestart.getSelection())); + outSettings.setProperty(KEY_USE_DOWNLOAD_CACHE, + Boolean.toString(mCheckUseCache.getSelection())); + outSettings.setProperty(KEY_ENABLE_PREVIEWS, + Boolean.toString(mCheckEnablePreviews.getSelection())); + + } + + /** + * Called by the application to give a callback that the page should invoke when + * settings must be applied. The page does not apply the settings itself, instead + * it notifies the application. + */ + @Override + public void setOnSettingsChanged(SettingsChangedCallback settingsChangedCallback) { + mSettingsChangedCallback = settingsChangedCallback; + } + + /** + * Callback invoked when user touches one of the settings. + * There is no "Apply" button, settings are applied immediately as they are changed. + * Notify the application that settings have changed. + */ + private void applyNewSettings() { + if (mSettingsChangedCallback != null) { + mSettingsChangedCallback.onSettingsChanged(this); + } + } + + private void updateDownloadCacheSize() { + long size = mDownloadCache.getCurrentSize(); + String str = FormatUtils.byteSizeToString(size); + mTextCacheSize.setText(str); + } + + + // End of hiding from SWT Designer + //$hide<<$ +} 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 aaa190c..c30f2fd 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 @@ -1,159 +1,159 @@ -/*
- * 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.sdkuilib.internal.repository.icons;
-
-import com.android.sdklib.internal.repository.archives.Archive;
-import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.sources.SdkSource;
-import com.android.sdklib.internal.repository.sources.SdkSourceCategory;
-import com.android.sdkuilib.internal.repository.sdkman2.PkgContentProvider;
-
-import org.eclipse.swt.SWTException;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Display;
-
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-
-
-/**
- * An utility class to serve {@link Image} correspond to the various icons
- * present in this package and dispose of them correctly at the end.
- */
-public class ImageFactory {
-
- private final Display mDisplay;
- private final Map<String, Image> mImages = new HashMap<String, Image>();
-
- public ImageFactory(Display display) {
- mDisplay = display;
- }
-
- /**
- * Loads an image given its filename (with its extension).
- * Might return null if the image cannot be loaded.
- * The image is cached. Successive calls will return the <em>same</em> object.
- *
- * @param imageName The filename (with extension) of the image to load.
- * @return A new or existing {@link Image}. The caller must NOT dispose the image (the
- * image will disposed by {@link #dispose()}). The returned image can be null if the
- * expected file is missing.
- */
- public Image getImageByName(String imageName) {
-
- Image image = mImages.get(imageName);
- if (image != null) {
- return image;
- }
-
- InputStream stream = getClass().getResourceAsStream(imageName);
- if (stream != null) {
- try {
- image = new Image(mDisplay, stream);
- } catch (SWTException e) {
- // ignore
- } catch (IllegalArgumentException e) {
- // ignore
- }
- }
-
- // Store the image in the hash, even if this failed. If it fails now, it will fail later.
- mImages.put(imageName, image);
-
- return image;
- }
-
- /**
- * Loads and returns the appropriate image for a given package, archive or source object.
- * The image is cached. Successive calls will return the <em>same</em> object.
- *
- * @param object A {@link SdkSource} or {@link Package} or {@link Archive}.
- * @return A new or existing {@link Image}. The caller must NOT dispose the image (the
- * image will disposed by {@link #dispose()}). The returned image can be null if the
- * object is of an unknown type.
- */
- public Image getImageForObject(Object object) {
-
- if (object == null) {
- return null;
- }
-
- if (object instanceof Image) {
- return (Image) object;
- }
-
- String clz = object.getClass().getSimpleName();
- if (clz.endsWith(Package.class.getSimpleName())) {
- String name = clz.replaceFirst(Package.class.getSimpleName(), "") //$NON-NLS-1$
- .replace("SystemImage", "sysimg") //$NON-NLS-1$ //$NON-NLS-2$
- .toLowerCase(Locale.US);
- name += "_pkg_16.png"; //$NON-NLS-1$
- return getImageByName(name);
- }
-
- if (object instanceof SdkSourceCategory) {
- return getImageByName("source_cat_icon_16.png"); //$NON-NLS-1$
-
- } else if (object instanceof SdkSource) {
- 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) {
- if (((Archive) object).isCompatible()) {
- return getImageByName("archive_icon16.png"); //$NON-NLS-1$
- } else {
- return getImageByName("incompat_icon16.png"); //$NON-NLS-1$
- }
- }
-
- if (object instanceof String) {
- return getImageByName((String) object);
- }
-
-
- if (object != null) {
- // For debugging
- // System.out.println("No image for object " + object.getClass().getSimpleName());
- }
-
- return null;
- }
-
- /**
- * Dispose all the images created by this factory so far.
- */
- public void dispose() {
- Iterator<Image> it = mImages.values().iterator();
- while(it.hasNext()) {
- Image img = it.next();
- if (img != null && img.isDisposed() == false) {
- img.dispose();
- }
- it.remove();
- }
- }
-
-}
+/* + * 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.sdkuilib.internal.repository.icons; + +import com.android.sdklib.internal.repository.archives.Archive; +import com.android.sdklib.internal.repository.packages.Package; +import com.android.sdklib.internal.repository.sources.SdkSource; +import com.android.sdklib.internal.repository.sources.SdkSourceCategory; +import com.android.sdkuilib.internal.repository.sdkman2.PkgContentProvider; + +import org.eclipse.swt.SWTException; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + + +/** + * An utility class to serve {@link Image} correspond to the various icons + * present in this package and dispose of them correctly at the end. + */ +public class ImageFactory { + + private final Display mDisplay; + private final Map<String, Image> mImages = new HashMap<String, Image>(); + + public ImageFactory(Display display) { + mDisplay = display; + } + + /** + * Loads an image given its filename (with its extension). + * Might return null if the image cannot be loaded. + * The image is cached. Successive calls will return the <em>same</em> object. + * + * @param imageName The filename (with extension) of the image to load. + * @return A new or existing {@link Image}. The caller must NOT dispose the image (the + * image will disposed by {@link #dispose()}). The returned image can be null if the + * expected file is missing. + */ + public Image getImageByName(String imageName) { + + Image image = mImages.get(imageName); + if (image != null) { + return image; + } + + InputStream stream = getClass().getResourceAsStream(imageName); + if (stream != null) { + try { + image = new Image(mDisplay, stream); + } catch (SWTException e) { + // ignore + } catch (IllegalArgumentException e) { + // ignore + } + } + + // Store the image in the hash, even if this failed. If it fails now, it will fail later. + mImages.put(imageName, image); + + return image; + } + + /** + * Loads and returns the appropriate image for a given package, archive or source object. + * The image is cached. Successive calls will return the <em>same</em> object. + * + * @param object A {@link SdkSource} or {@link Package} or {@link Archive}. + * @return A new or existing {@link Image}. The caller must NOT dispose the image (the + * image will disposed by {@link #dispose()}). The returned image can be null if the + * object is of an unknown type. + */ + public Image getImageForObject(Object object) { + + if (object == null) { + return null; + } + + if (object instanceof Image) { + return (Image) object; + } + + String clz = object.getClass().getSimpleName(); + if (clz.endsWith(Package.class.getSimpleName())) { + String name = clz.replaceFirst(Package.class.getSimpleName(), "") //$NON-NLS-1$ + .replace("SystemImage", "sysimg") //$NON-NLS-1$ //$NON-NLS-2$ + .toLowerCase(Locale.US); + name += "_pkg_16.png"; //$NON-NLS-1$ + return getImageByName(name); + } + + if (object instanceof SdkSourceCategory) { + return getImageByName("source_cat_icon_16.png"); //$NON-NLS-1$ + + } else if (object instanceof SdkSource) { + 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) { + if (((Archive) object).isCompatible()) { + return getImageByName("archive_icon16.png"); //$NON-NLS-1$ + } else { + return getImageByName("incompat_icon16.png"); //$NON-NLS-1$ + } + } + + if (object instanceof String) { + return getImageByName((String) object); + } + + + if (object != null) { + // For debugging + // System.out.println("No image for object " + object.getClass().getSimpleName()); + } + + return null; + } + + /** + * Dispose all the images created by this factory so far. + */ + public void dispose() { + Iterator<Image> it = mImages.values().iterator(); + while(it.hasNext()) { + Image img = it.next(); + if (img != null && img.isDisposed() == false) { + img.dispose(); + } + it.remove(); + } + } + +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman1/AvdManagerPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman1/AvdManagerPage.java index 84a549b..63c53e0 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman1/AvdManagerPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman1/AvdManagerPage.java @@ -1,125 +1,125 @@ -/*
- * 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.sdkuilib.internal.repository.sdkman1;
-
-import com.android.prefs.AndroidLocation.AndroidLocationException;
-import com.android.sdkuilib.internal.repository.UpdaterData;
-import com.android.sdkuilib.internal.repository.UpdaterPage;
-import com.android.sdkuilib.internal.widgets.AvdSelector;
-import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
-import com.android.sdkuilib.repository.ISdkChangeListener;
-
-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;
-
-public class AvdManagerPage extends UpdaterPage implements ISdkChangeListener {
-
- private AvdSelector mAvdSelector;
-
- private final UpdaterData mUpdaterData;
-
- /**
- * Create the composite.
- * @param parent The parent of the composite.
- * @param updaterData An instance of {@link UpdaterData}.
- */
- public AvdManagerPage(Composite parent, int swtStyle, UpdaterData updaterData) {
- super(parent, swtStyle);
-
- mUpdaterData = updaterData;
- mUpdaterData.addListeners(this);
-
- createContents(this);
- postCreate(); //$hide$
- }
-
- private void createContents(Composite parent) {
- parent.setLayout(new GridLayout(1, false));
-
- Label label = new Label(parent, SWT.NONE);
- label.setLayoutData(new GridData());
-
- try {
- if (mUpdaterData != null && mUpdaterData.getAvdManager() != null) {
- label.setText(String.format(
- "List of existing Android Virtual Devices located at %s",
- mUpdaterData.getAvdManager().getBaseAvdFolder()));
- } else {
- label.setText("Error: cannot find the AVD folder location.\r\n Please set the 'ANDROID_SDK_HOME' env variable.");
- }
- } catch (AndroidLocationException e) {
- label.setText(e.getMessage());
- }
-
- mAvdSelector = new AvdSelector(parent,
- mUpdaterData.getOsSdkRoot(),
- mUpdaterData.getAvdManager(),
- DisplayMode.MANAGER,
- mUpdaterData.getSdkLog());
- mAvdSelector.setSettingsController(mUpdaterData.getSettingsController());
- }
-
- @Override
- public void dispose() {
- mUpdaterData.removeListener(this);
- super.dispose();
- }
-
- @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() {
- // nothing to be done for now.
- }
-
- // --- Implementation of ISdkChangeListener ---
-
- @Override
- public void onSdkLoaded() {
- onSdkReload();
- }
-
- @Override
- public void onSdkReload() {
- mAvdSelector.refresh(false /*reload*/);
- }
-
- @Override
- public void preInstallHook() {
- // nothing to be done for now.
- }
-
- @Override
- public void postInstallHook() {
- // nothing to be done for now.
- }
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/* + * 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.sdkuilib.internal.repository.sdkman1; + +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdkuilib.internal.repository.UpdaterData; +import com.android.sdkuilib.internal.repository.UpdaterPage; +import com.android.sdkuilib.internal.widgets.AvdSelector; +import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode; +import com.android.sdkuilib.repository.ISdkChangeListener; + +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; + +public class AvdManagerPage extends UpdaterPage implements ISdkChangeListener { + + private AvdSelector mAvdSelector; + + private final UpdaterData mUpdaterData; + + /** + * Create the composite. + * @param parent The parent of the composite. + * @param updaterData An instance of {@link UpdaterData}. + */ + public AvdManagerPage(Composite parent, int swtStyle, UpdaterData updaterData) { + super(parent, swtStyle); + + mUpdaterData = updaterData; + mUpdaterData.addListeners(this); + + createContents(this); + postCreate(); //$hide$ + } + + private void createContents(Composite parent) { + parent.setLayout(new GridLayout(1, false)); + + Label label = new Label(parent, SWT.NONE); + label.setLayoutData(new GridData()); + + try { + if (mUpdaterData != null && mUpdaterData.getAvdManager() != null) { + label.setText(String.format( + "List of existing Android Virtual Devices located at %s", + mUpdaterData.getAvdManager().getBaseAvdFolder())); + } else { + label.setText("Error: cannot find the AVD folder location.\r\n Please set the 'ANDROID_SDK_HOME' env variable."); + } + } catch (AndroidLocationException e) { + label.setText(e.getMessage()); + } + + mAvdSelector = new AvdSelector(parent, + mUpdaterData.getOsSdkRoot(), + mUpdaterData.getAvdManager(), + DisplayMode.MANAGER, + mUpdaterData.getSdkLog()); + mAvdSelector.setSettingsController(mUpdaterData.getSettingsController()); + } + + @Override + public void dispose() { + mUpdaterData.removeListener(this); + super.dispose(); + } + + @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() { + // nothing to be done for now. + } + + // --- Implementation of ISdkChangeListener --- + + @Override + public void onSdkLoaded() { + onSdkReload(); + } + + @Override + public void onSdkReload() { + mAvdSelector.refresh(false /*reload*/); + } + + @Override + public void preInstallHook() { + // nothing to be done for now. + } + + @Override + public void postInstallHook() { + // nothing to be done for now. + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java index 8e66c63..95c16f1 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java @@ -1,492 +1,492 @@ -/*
- * Copyright (C) 2011 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.AndroidVersion;
-import com.android.sdklib.ISdkLog;
-import com.android.sdklib.internal.repository.packages.ExtraPackage;
-import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.packages.PlatformPackage;
-import com.android.sdklib.internal.repository.packages.PlatformToolPackage;
-import com.android.sdklib.internal.repository.packages.ToolPackage;
-import com.android.sdklib.internal.repository.sources.SdkSource;
-import com.android.sdkuilib.internal.repository.SettingsController;
-import com.android.sdkuilib.internal.repository.UpdaterData;
-import com.android.sdkuilib.internal.repository.sdkman2.PackageLoader.IAutoInstallTask;
-import com.android.sdkuilib.internal.tasks.ProgressView;
-import com.android.sdkuilib.internal.tasks.ProgressViewFactory;
-import com.android.sdkuilib.ui.GridDataBuilder;
-import com.android.sdkuilib.ui.GridLayoutBuilder;
-import com.android.sdkuilib.ui.SwtBaseDialog;
-import com.android.util.Pair;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.ProgressBar;
-import org.eclipse.swt.widgets.Shell;
-
-import java.io.File;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * This is a private implementation of UpdateWindow for ADT,
- * designed to install a very specific package.
- * <p/>
- * Example of usage:
- * <pre>
- * AdtUpdateDialog dialog = new AdtUpdateDialog(
- * AdtPlugin.getDisplay().getActiveShell(),
- * new AdtConsoleSdkLog(),
- * sdk.getSdkLocation());
- *
- * Pair<Boolean, File> result = dialog.installExtraPackage(
- * "android", "compatibility"); //$NON-NLS-1$ //$NON-NLS-2$
- * or
- * Pair<Boolean, File> result = dialog.installPlatformPackage(11);
- * </pre>
- */
-public class AdtUpdateDialog extends SwtBaseDialog {
-
- public static final int USE_MAX_REMOTE_API_LEVEL = 0;
-
- private static final String APP_NAME = "Android SDK Manager";
- private final UpdaterData mUpdaterData;
-
- private Boolean mResultCode = Boolean.FALSE;
- private Map<Package, File> mResultPaths = null;
- private SettingsController mSettingsController;
- private PackageFilter mPackageFilter;
- private PackageLoader mPackageLoader;
-
- private ProgressBar mProgressBar;
- private Label mStatusText;
-
- /**
- * Creates a new {@link AdtUpdateDialog}.
- * Callers will want to call {@link #installExtraPackage} or
- * {@link #installPlatformPackage} after this.
- *
- * @param parentShell The existing parent shell. Must not be null.
- * @param sdkLog An SDK logger. Must not be null.
- * @param osSdkRoot The current SDK root OS path. Must not be null or empty.
- */
- public AdtUpdateDialog(
- Shell parentShell,
- ISdkLog sdkLog,
- String osSdkRoot) {
- super(parentShell, SWT.NONE, APP_NAME);
- mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
- }
-
- /**
- * Displays the update dialog and triggers installation of the requested {@code extra}
- * package with the specified vendor and path attributes.
- * <p/>
- * Callers must not try to reuse this dialog after this call.
- *
- * @param vendor The extra package vendor string to match.
- * @param path The extra package path string to match.
- * @return A boolean indicating whether the installation was successful (meaning the package
- * was either already present, or got installed or updated properly) and a {@link File}
- * with the path to the root folder of the package. The file is null when the boolean
- * is false, otherwise it should point to an existing valid folder.
- * @wbp.parser.entryPoint
- */
- public Pair<Boolean, File> installExtraPackage(String vendor, String path) {
- mPackageFilter = createExtraFilter(vendor, path);
- open();
-
- File installPath = null;
- if (mResultPaths != null) {
- for (Entry<Package, File> entry : mResultPaths.entrySet()) {
- if (entry.getKey() instanceof ExtraPackage) {
- installPath = entry.getValue();
- break;
- }
- }
- }
-
- return Pair.of(mResultCode, installPath);
- }
-
- /**
- * Displays the update dialog and triggers installation of platform-tools package.
- * <p/>
- * Callers must not try to reuse this dialog after this call.
- *
- * @return A boolean indicating whether the installation was successful (meaning the package
- * was either already present, or got installed or updated properly) and a {@link File}
- * with the path to the root folder of the package. The file is null when the boolean
- * is false, otherwise it should point to an existing valid folder.
- * @wbp.parser.entryPoint
- */
- public Pair<Boolean, File> installPlatformTools() {
- mPackageFilter = createPlatformToolsFilter();
- open();
-
- File installPath = null;
- if (mResultPaths != null) {
- for (Entry<Package, File> entry : mResultPaths.entrySet()) {
- if (entry.getKey() instanceof ExtraPackage) {
- installPath = entry.getValue();
- break;
- }
- }
- }
-
- return Pair.of(mResultCode, installPath);
- }
-
- /**
- * Displays the update dialog and triggers installation of the requested platform
- * package with the specified API level.
- * <p/>
- * Callers must not try to reuse this dialog after this call.
- *
- * @param apiLevel The platform API level to match.
- * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use
- * the highest API level available on the remote repository.
- * @return A boolean indicating whether the installation was successful (meaning the package
- * was either already present, or got installed or updated properly) and a {@link File}
- * with the path to the root folder of the package. The file is null when the boolean
- * is false, otherwise it should point to an existing valid folder.
- */
- public Pair<Boolean, File> installPlatformPackage(int apiLevel) {
- mPackageFilter = createPlatformFilter(apiLevel);
- open();
-
- File installPath = null;
- if (mResultPaths != null) {
- for (Entry<Package, File> entry : mResultPaths.entrySet()) {
- if (entry.getKey() instanceof PlatformPackage) {
- installPath = entry.getValue();
- break;
- }
- }
- }
-
- return Pair.of(mResultCode, installPath);
- }
-
- /**
- * Displays the update dialog and triggers installation of a new SDK. This works by
- * requesting a remote platform package with the specified API levels as well as
- * the first tools or platform-tools packages available.
- * <p/>
- * Callers must not try to reuse this dialog after this call.
- *
- * @param apiLevels A set of platform API levels to match.
- * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use
- * the highest API level available in the repository.
- * @return A boolean indicating whether the installation was successful (meaning the packages
- * were either already present, or got installed or updated properly).
- */
- public boolean installNewSdk(Set<Integer> apiLevels) {
- mPackageFilter = createNewSdkFilter(apiLevels);
- open();
- return mResultCode.booleanValue();
- }
-
- @Override
- protected void createContents() {
- Shell shell = getShell();
- shell.setMinimumSize(new Point(450, 100));
- shell.setSize(450, 100);
-
- mUpdaterData.setWindowShell(shell);
-
- GridLayoutBuilder.create(shell).columns(1);
-
- Composite composite1 = new Composite(shell, SWT.NONE);
- composite1.setLayout(new GridLayout(1, false));
- GridDataBuilder.create(composite1).fill().grab();
-
- mProgressBar = new ProgressBar(composite1, SWT.NONE);
- GridDataBuilder.create(mProgressBar).hFill().hGrab();
-
- mStatusText = new Label(composite1, SWT.NONE);
- mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder
- GridDataBuilder.create(mStatusText).hFill().hGrab();
- }
-
- @Override
- protected void postCreate() {
- ProgressViewFactory factory = new ProgressViewFactory();
- factory.setProgressView(new ProgressView(
- mStatusText,
- mProgressBar,
- null /*buttonStop*/,
- new SdkLogAdapter(mUpdaterData.getSdkLog())));
- mUpdaterData.setTaskFactory(factory);
-
- setupSources();
- initializeSettings();
-
- if (mUpdaterData.checkIfInitFailed()) {
- close();
- return;
- }
-
- mUpdaterData.broadcastOnSdkLoaded();
-
- mPackageLoader = new PackageLoader(mUpdaterData);
- }
-
- @Override
- protected void eventLoop() {
- mPackageLoader.loadPackagesWithInstallTask(
- mPackageFilter.installFlags(),
- new IAutoInstallTask() {
- @Override
- public Package[] filterLoadedSource(SdkSource source, Package[] packages) {
- for (Package pkg : packages) {
- mPackageFilter.visit(pkg);
- }
- return packages;
- }
-
- @Override
- public boolean acceptPackage(Package pkg) {
- // Is this the package we want to install?
- return mPackageFilter.accept(pkg);
- }
-
- @Override
- public void setResult(boolean success, Map<Package, File> installPaths) {
- // Capture the result from the installation.
- mResultCode = Boolean.valueOf(success);
- mResultPaths = installPaths;
- }
-
- @Override
- public void taskCompleted() {
- // We can close that window now.
- close();
- }
- });
-
- super.eventLoop();
- }
-
- // -- Start of internal part ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- // --- Public API -----------
-
-
- // --- Internals & UI Callbacks -----------
-
- /**
- * Used to initialize the sources.
- */
- private void setupSources() {
- mUpdaterData.setupDefaultSources();
- }
-
- /**
- * Initializes settings.
- */
- private void initializeSettings() {
- mSettingsController = mUpdaterData.getSettingsController();
- mSettingsController.loadSettings();
- mSettingsController.applySettings();
- }
-
- // ----
-
- private static abstract class PackageFilter {
- /** Returns the installer flags for the corresponding mode. */
- abstract int installFlags();
-
- /** Visit a new package definition, in case we need to adjust the filter dynamically. */
- abstract void visit(Package pkg);
-
- /** Checks whether this is the package we've been looking for. */
- abstract boolean accept(Package pkg);
- }
-
- public static PackageFilter createExtraFilter(
- final String vendor,
- final String path) {
- return new PackageFilter() {
- String mVendor = vendor;
- String mPath = path;
-
- @Override
- boolean accept(Package pkg) {
- if (pkg instanceof ExtraPackage) {
- ExtraPackage ep = (ExtraPackage) pkg;
- if (ep.getVendorId().equals(mVendor)) {
- // Check actual extra <path> field first
- if (ep.getPath().equals(mPath)) {
- return true;
- }
- // If not, check whether this is one of the <old-paths> values.
- for (String oldPath : ep.getOldPaths()) {
- if (oldPath.equals(mPath)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- @Override
- void visit(Package pkg) {
- // nop
- }
-
- @Override
- int installFlags() {
- return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT;
- }
- };
- }
-
- private PackageFilter createPlatformToolsFilter() {
- return new PackageFilter() {
- @Override
- boolean accept(Package pkg) {
- return pkg instanceof PlatformToolPackage;
- }
-
- @Override
- void visit(Package pkg) {
- // nop
- }
-
- @Override
- int installFlags() {
- return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT;
- }
- };
- }
-
- public static PackageFilter createPlatformFilter(final int apiLevel) {
- return new PackageFilter() {
- int mApiLevel = apiLevel;
- boolean mFindMaxApi = apiLevel == USE_MAX_REMOTE_API_LEVEL;
-
- @Override
- boolean accept(Package pkg) {
- if (pkg instanceof PlatformPackage) {
- PlatformPackage pp = (PlatformPackage) pkg;
- AndroidVersion v = pp.getAndroidVersion();
- return !v.isPreview() && v.getApiLevel() == mApiLevel;
- }
- return false;
- }
-
- @Override
- void visit(Package pkg) {
- // Try to find the max API in all remote packages
- if (mFindMaxApi &&
- pkg instanceof PlatformPackage &&
- !pkg.isLocal()) {
- PlatformPackage pp = (PlatformPackage) pkg;
- AndroidVersion v = pp.getAndroidVersion();
- if (!v.isPreview()) {
- int api = v.getApiLevel();
- if (api > mApiLevel) {
- mApiLevel = api;
- }
- }
- }
- }
-
- @Override
- int installFlags() {
- return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT;
- }
- };
- }
-
- public static PackageFilter createNewSdkFilter(final Set<Integer> apiLevels) {
- return new PackageFilter() {
- int mMaxApiLevel;
- boolean mFindMaxApi = apiLevels.contains(USE_MAX_REMOTE_API_LEVEL);
- boolean mNeedTools = true;
- boolean mNeedPlatformTools = true;
-
- @Override
- boolean accept(Package pkg) {
- if (!pkg.isLocal()) {
- if (pkg instanceof PlatformPackage) {
- PlatformPackage pp = (PlatformPackage) pkg;
- AndroidVersion v = pp.getAndroidVersion();
- if (!v.isPreview()) {
- int level = v.getApiLevel();
- if ((mFindMaxApi && level == mMaxApiLevel) ||
- (level > 0 && apiLevels.contains(level))) {
- return true;
- }
- }
- } else if (mNeedTools && pkg instanceof ToolPackage) {
- // We want a tool package. There should be only one,
- // but in case of error just take the first one.
- mNeedTools = false;
- return true;
- } else if (mNeedPlatformTools && pkg instanceof PlatformToolPackage) {
- // We want a platform-tool package. There should be only one,
- // but in case of error just take the first one.
- mNeedPlatformTools = false;
- return true;
- }
- }
- return false;
- }
-
- @Override
- void visit(Package pkg) {
- // Try to find the max API in all remote packages
- if (mFindMaxApi &&
- pkg instanceof PlatformPackage &&
- !pkg.isLocal()) {
- PlatformPackage pp = (PlatformPackage) pkg;
- AndroidVersion v = pp.getAndroidVersion();
- if (!v.isPreview()) {
- int api = v.getApiLevel();
- if (api > mMaxApiLevel) {
- mMaxApiLevel = api;
- }
- }
- }
- }
-
- @Override
- int installFlags() {
- return UpdaterData.NO_TOOLS_MSG;
- }
- };
- }
-
-
-
- // End of hiding from SWT Designer
- //$hide<<$
-
- // -----
-
-}
+/* + * Copyright (C) 2011 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.AndroidVersion; +import com.android.sdklib.ISdkLog; +import com.android.sdklib.internal.repository.packages.ExtraPackage; +import com.android.sdklib.internal.repository.packages.Package; +import com.android.sdklib.internal.repository.packages.PlatformPackage; +import com.android.sdklib.internal.repository.packages.PlatformToolPackage; +import com.android.sdklib.internal.repository.packages.ToolPackage; +import com.android.sdklib.internal.repository.sources.SdkSource; +import com.android.sdkuilib.internal.repository.SettingsController; +import com.android.sdkuilib.internal.repository.UpdaterData; +import com.android.sdkuilib.internal.repository.sdkman2.PackageLoader.IAutoInstallTask; +import com.android.sdkuilib.internal.tasks.ProgressView; +import com.android.sdkuilib.internal.tasks.ProgressViewFactory; +import com.android.sdkuilib.ui.GridDataBuilder; +import com.android.sdkuilib.ui.GridLayoutBuilder; +import com.android.sdkuilib.ui.SwtBaseDialog; +import com.android.util.Pair; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Shell; + +import java.io.File; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * This is a private implementation of UpdateWindow for ADT, + * designed to install a very specific package. + * <p/> + * Example of usage: + * <pre> + * AdtUpdateDialog dialog = new AdtUpdateDialog( + * AdtPlugin.getDisplay().getActiveShell(), + * new AdtConsoleSdkLog(), + * sdk.getSdkLocation()); + * + * Pair<Boolean, File> result = dialog.installExtraPackage( + * "android", "compatibility"); //$NON-NLS-1$ //$NON-NLS-2$ + * or + * Pair<Boolean, File> result = dialog.installPlatformPackage(11); + * </pre> + */ +public class AdtUpdateDialog extends SwtBaseDialog { + + public static final int USE_MAX_REMOTE_API_LEVEL = 0; + + private static final String APP_NAME = "Android SDK Manager"; + private final UpdaterData mUpdaterData; + + private Boolean mResultCode = Boolean.FALSE; + private Map<Package, File> mResultPaths = null; + private SettingsController mSettingsController; + private PackageFilter mPackageFilter; + private PackageLoader mPackageLoader; + + private ProgressBar mProgressBar; + private Label mStatusText; + + /** + * Creates a new {@link AdtUpdateDialog}. + * Callers will want to call {@link #installExtraPackage} or + * {@link #installPlatformPackage} after this. + * + * @param parentShell The existing parent shell. Must not be null. + * @param sdkLog An SDK logger. Must not be null. + * @param osSdkRoot The current SDK root OS path. Must not be null or empty. + */ + public AdtUpdateDialog( + Shell parentShell, + ISdkLog sdkLog, + String osSdkRoot) { + super(parentShell, SWT.NONE, APP_NAME); + mUpdaterData = new UpdaterData(osSdkRoot, sdkLog); + } + + /** + * Displays the update dialog and triggers installation of the requested {@code extra} + * package with the specified vendor and path attributes. + * <p/> + * Callers must not try to reuse this dialog after this call. + * + * @param vendor The extra package vendor string to match. + * @param path The extra package path string to match. + * @return A boolean indicating whether the installation was successful (meaning the package + * was either already present, or got installed or updated properly) and a {@link File} + * with the path to the root folder of the package. The file is null when the boolean + * is false, otherwise it should point to an existing valid folder. + * @wbp.parser.entryPoint + */ + public Pair<Boolean, File> installExtraPackage(String vendor, String path) { + mPackageFilter = createExtraFilter(vendor, path); + open(); + + File installPath = null; + if (mResultPaths != null) { + for (Entry<Package, File> entry : mResultPaths.entrySet()) { + if (entry.getKey() instanceof ExtraPackage) { + installPath = entry.getValue(); + break; + } + } + } + + return Pair.of(mResultCode, installPath); + } + + /** + * Displays the update dialog and triggers installation of platform-tools package. + * <p/> + * Callers must not try to reuse this dialog after this call. + * + * @return A boolean indicating whether the installation was successful (meaning the package + * was either already present, or got installed or updated properly) and a {@link File} + * with the path to the root folder of the package. The file is null when the boolean + * is false, otherwise it should point to an existing valid folder. + * @wbp.parser.entryPoint + */ + public Pair<Boolean, File> installPlatformTools() { + mPackageFilter = createPlatformToolsFilter(); + open(); + + File installPath = null; + if (mResultPaths != null) { + for (Entry<Package, File> entry : mResultPaths.entrySet()) { + if (entry.getKey() instanceof ExtraPackage) { + installPath = entry.getValue(); + break; + } + } + } + + return Pair.of(mResultCode, installPath); + } + + /** + * Displays the update dialog and triggers installation of the requested platform + * package with the specified API level. + * <p/> + * Callers must not try to reuse this dialog after this call. + * + * @param apiLevel The platform API level to match. + * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use + * the highest API level available on the remote repository. + * @return A boolean indicating whether the installation was successful (meaning the package + * was either already present, or got installed or updated properly) and a {@link File} + * with the path to the root folder of the package. The file is null when the boolean + * is false, otherwise it should point to an existing valid folder. + */ + public Pair<Boolean, File> installPlatformPackage(int apiLevel) { + mPackageFilter = createPlatformFilter(apiLevel); + open(); + + File installPath = null; + if (mResultPaths != null) { + for (Entry<Package, File> entry : mResultPaths.entrySet()) { + if (entry.getKey() instanceof PlatformPackage) { + installPath = entry.getValue(); + break; + } + } + } + + return Pair.of(mResultCode, installPath); + } + + /** + * Displays the update dialog and triggers installation of a new SDK. This works by + * requesting a remote platform package with the specified API levels as well as + * the first tools or platform-tools packages available. + * <p/> + * Callers must not try to reuse this dialog after this call. + * + * @param apiLevels A set of platform API levels to match. + * The special value {@link #USE_MAX_REMOTE_API_LEVEL} means to use + * the highest API level available in the repository. + * @return A boolean indicating whether the installation was successful (meaning the packages + * were either already present, or got installed or updated properly). + */ + public boolean installNewSdk(Set<Integer> apiLevels) { + mPackageFilter = createNewSdkFilter(apiLevels); + open(); + return mResultCode.booleanValue(); + } + + @Override + protected void createContents() { + Shell shell = getShell(); + shell.setMinimumSize(new Point(450, 100)); + shell.setSize(450, 100); + + mUpdaterData.setWindowShell(shell); + + GridLayoutBuilder.create(shell).columns(1); + + Composite composite1 = new Composite(shell, SWT.NONE); + composite1.setLayout(new GridLayout(1, false)); + GridDataBuilder.create(composite1).fill().grab(); + + mProgressBar = new ProgressBar(composite1, SWT.NONE); + GridDataBuilder.create(mProgressBar).hFill().hGrab(); + + mStatusText = new Label(composite1, SWT.NONE); + mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder + GridDataBuilder.create(mStatusText).hFill().hGrab(); + } + + @Override + protected void postCreate() { + ProgressViewFactory factory = new ProgressViewFactory(); + factory.setProgressView(new ProgressView( + mStatusText, + mProgressBar, + null /*buttonStop*/, + new SdkLogAdapter(mUpdaterData.getSdkLog()))); + mUpdaterData.setTaskFactory(factory); + + setupSources(); + initializeSettings(); + + if (mUpdaterData.checkIfInitFailed()) { + close(); + return; + } + + mUpdaterData.broadcastOnSdkLoaded(); + + mPackageLoader = new PackageLoader(mUpdaterData); + } + + @Override + protected void eventLoop() { + mPackageLoader.loadPackagesWithInstallTask( + mPackageFilter.installFlags(), + new IAutoInstallTask() { + @Override + public Package[] filterLoadedSource(SdkSource source, Package[] packages) { + for (Package pkg : packages) { + mPackageFilter.visit(pkg); + } + return packages; + } + + @Override + public boolean acceptPackage(Package pkg) { + // Is this the package we want to install? + return mPackageFilter.accept(pkg); + } + + @Override + public void setResult(boolean success, Map<Package, File> installPaths) { + // Capture the result from the installation. + mResultCode = Boolean.valueOf(success); + mResultPaths = installPaths; + } + + @Override + public void taskCompleted() { + // We can close that window now. + close(); + } + }); + + super.eventLoop(); + } + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + // --- Public API ----------- + + + // --- Internals & UI Callbacks ----------- + + /** + * Used to initialize the sources. + */ + private void setupSources() { + mUpdaterData.setupDefaultSources(); + } + + /** + * Initializes settings. + */ + private void initializeSettings() { + mSettingsController = mUpdaterData.getSettingsController(); + mSettingsController.loadSettings(); + mSettingsController.applySettings(); + } + + // ---- + + private static abstract class PackageFilter { + /** Returns the installer flags for the corresponding mode. */ + abstract int installFlags(); + + /** Visit a new package definition, in case we need to adjust the filter dynamically. */ + abstract void visit(Package pkg); + + /** Checks whether this is the package we've been looking for. */ + abstract boolean accept(Package pkg); + } + + public static PackageFilter createExtraFilter( + final String vendor, + final String path) { + return new PackageFilter() { + String mVendor = vendor; + String mPath = path; + + @Override + boolean accept(Package pkg) { + if (pkg instanceof ExtraPackage) { + ExtraPackage ep = (ExtraPackage) pkg; + if (ep.getVendorId().equals(mVendor)) { + // Check actual extra <path> field first + if (ep.getPath().equals(mPath)) { + return true; + } + // If not, check whether this is one of the <old-paths> values. + for (String oldPath : ep.getOldPaths()) { + if (oldPath.equals(mPath)) { + return true; + } + } + } + } + return false; + } + + @Override + void visit(Package pkg) { + // nop + } + + @Override + int installFlags() { + return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; + } + }; + } + + private PackageFilter createPlatformToolsFilter() { + return new PackageFilter() { + @Override + boolean accept(Package pkg) { + return pkg instanceof PlatformToolPackage; + } + + @Override + void visit(Package pkg) { + // nop + } + + @Override + int installFlags() { + return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; + } + }; + } + + public static PackageFilter createPlatformFilter(final int apiLevel) { + return new PackageFilter() { + int mApiLevel = apiLevel; + boolean mFindMaxApi = apiLevel == USE_MAX_REMOTE_API_LEVEL; + + @Override + boolean accept(Package pkg) { + if (pkg instanceof PlatformPackage) { + PlatformPackage pp = (PlatformPackage) pkg; + AndroidVersion v = pp.getAndroidVersion(); + return !v.isPreview() && v.getApiLevel() == mApiLevel; + } + return false; + } + + @Override + void visit(Package pkg) { + // Try to find the max API in all remote packages + if (mFindMaxApi && + pkg instanceof PlatformPackage && + !pkg.isLocal()) { + PlatformPackage pp = (PlatformPackage) pkg; + AndroidVersion v = pp.getAndroidVersion(); + if (!v.isPreview()) { + int api = v.getApiLevel(); + if (api > mApiLevel) { + mApiLevel = api; + } + } + } + } + + @Override + int installFlags() { + return UpdaterData.TOOLS_MSG_UPDATED_FROM_ADT; + } + }; + } + + public static PackageFilter createNewSdkFilter(final Set<Integer> apiLevels) { + return new PackageFilter() { + int mMaxApiLevel; + boolean mFindMaxApi = apiLevels.contains(USE_MAX_REMOTE_API_LEVEL); + boolean mNeedTools = true; + boolean mNeedPlatformTools = true; + + @Override + boolean accept(Package pkg) { + if (!pkg.isLocal()) { + if (pkg instanceof PlatformPackage) { + PlatformPackage pp = (PlatformPackage) pkg; + AndroidVersion v = pp.getAndroidVersion(); + if (!v.isPreview()) { + int level = v.getApiLevel(); + if ((mFindMaxApi && level == mMaxApiLevel) || + (level > 0 && apiLevels.contains(level))) { + return true; + } + } + } else if (mNeedTools && pkg instanceof ToolPackage) { + // We want a tool package. There should be only one, + // but in case of error just take the first one. + mNeedTools = false; + return true; + } else if (mNeedPlatformTools && pkg instanceof PlatformToolPackage) { + // We want a platform-tool package. There should be only one, + // but in case of error just take the first one. + mNeedPlatformTools = false; + return true; + } + } + return false; + } + + @Override + void visit(Package pkg) { + // Try to find the max API in all remote packages + if (mFindMaxApi && + pkg instanceof PlatformPackage && + !pkg.isLocal()) { + PlatformPackage pp = (PlatformPackage) pkg; + AndroidVersion v = pp.getAndroidVersion(); + if (!v.isPreview()) { + int api = v.getApiLevel(); + if (api > mMaxApiLevel) { + mMaxApiLevel = api; + } + } + } + } + + @Override + int installFlags() { + return UpdaterData.NO_TOOLS_MSG; + } + }; + } + + + + // End of hiding from SWT Designer + //$hide<<$ + + // ----- + +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/LogWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/LogWindow.java index 699edda..4cdd198 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/LogWindow.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/LogWindow.java @@ -1,379 +1,379 @@ -/*
- * Copyright (C) 2011 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.ISdkLog;
-import com.android.sdkuilib.internal.tasks.ILogUiProvider;
-import com.android.sdkuilib.ui.GridDataBuilder;
-import com.android.sdkuilib.ui.GridLayoutBuilder;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StyleRange;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-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.Shell;
-import org.eclipse.swt.widgets.Widget;
-
-
-/**
- * A floating log window that can be displayed or hidden by the main SDK Manager 2 window.
- * It displays a log of the sdk manager operation (listing, install, delete) including
- * any errors (e.g. network error or install/delete errors.)
- * <p/>
- * Since the SDK Manager will direct all log to this window, its purpose is to be
- * opened by the main window at startup and left open all the time. When not needed
- * the floating window is hidden but not closed. This way it can easily accumulate
- * all the log.
- */
-class LogWindow implements ILogUiProvider {
-
- private Shell mParentShell;
- private Shell mShell;
- private Composite mRootComposite;
- private StyledText mStyledText;
- private Label mLogDescription;
- private Button mCloseButton;
-
- private final ISdkLog mSecondaryLog;
- private boolean mCloseRequested;
- private boolean mInitPosition = true;
- private String mLastLogMsg = null;
-
- private enum TextStyle {
- DEFAULT,
- TITLE,
- ERROR
- }
-
- /**
- * Creates the floating window. Callers should use {@link #open()} later.
- *
- * @param parentShell Parent container
- * @param secondaryLog An optional logger where messages will <em>also</em> be output.
- */
- public LogWindow(Shell parentShell, ISdkLog secondaryLog) {
- mParentShell = parentShell;
- mSecondaryLog = secondaryLog;
- }
-
- /**
- * For testing only. See {@link #open()} and {@link #close()} for normal usage.
- * @wbp.parser.entryPoint
- */
- void openBlocking() {
- open();
- Display display = Display.getDefault();
- while (!mShell.isDisposed()) {
- if (!display.readAndDispatch()) {
- display.sleep();
- }
- }
- close();
- }
-
- /**
- * Opens the window.
- * This call does not block and relies on the fact that the main window is
- * already running an SWT event dispatch loop.
- * Caller should use {@link #close()} later.
- */
- public void open() {
- createShell();
- createContents();
- mShell.open();
- mShell.layout();
- mShell.setVisible(false);
- }
-
- /**
- * Closes and <em>destroys</em> the window.
- * This must be called just before quitting the app.
- * <p/>
- * To simply hide/show the window, use {@link #setVisible(boolean)} instead.
- */
- public void close() {
- if (mShell != null && !mShell.isDisposed()) {
- mCloseRequested = true;
- mShell.close();
- mShell = null;
- }
- }
-
- /**
- * Determines whether the window is currently shown or not.
- *
- * @return True if the window is shown.
- */
- public boolean isVisible() {
- return mShell != null && !mShell.isDisposed() && mShell.isVisible();
- }
-
- /**
- * Toggles the window visibility.
- *
- * @param visible True to make the window visible, false to hide it.
- */
- public void setVisible(boolean visible) {
- if (mShell != null && !mShell.isDisposed()) {
- mShell.setVisible(visible);
- if (visible && mInitPosition) {
- mInitPosition = false;
- positionWindow();
- }
- }
- }
-
- private void createShell() {
- mShell = new Shell(mParentShell, SWT.SHELL_TRIM | SWT.TOOL);
- mShell.setMinimumSize(new Point(600, 300));
- mShell.setSize(450, 300);
- mShell.setText("Android SDK Manager Log");
- GridLayoutBuilder.create(mShell);
-
- mShell.addShellListener(new ShellAdapter() {
- @Override
- public void shellClosed(ShellEvent e) {
- if (!mCloseRequested) {
- e.doit = false;
- setVisible(false);
- }
- }
- });
- }
-
- /**
- * Create contents of the dialog.
- */
- private void createContents() {
- mRootComposite = new Composite(mShell, SWT.NONE);
- GridLayoutBuilder.create(mRootComposite).columns(2);
- GridDataBuilder.create(mRootComposite).fill().grab();
-
- mStyledText = new StyledText(mRootComposite,
- SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);
- GridDataBuilder.create(mStyledText).hSpan(2).fill().grab();
-
- mLogDescription = new Label(mRootComposite, SWT.NONE);
- GridDataBuilder.create(mLogDescription).hFill().hGrab();
-
- mCloseButton = new Button(mRootComposite, SWT.NONE);
- mCloseButton.setText("Close");
- mCloseButton.setToolTipText("Closes the log window");
- mCloseButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- setVisible(false); //$hide$
- }
- });
- }
-
- // --- Implementation of ILogUiProvider ---
-
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setDescription(final String description) {
- syncExec(mLogDescription, new Runnable() {
- @Override
- public void run() {
- mLogDescription.setText(description);
-
- if (acceptLog(description, true /*isDescription*/)) {
- appendLine(TextStyle.TITLE, description);
-
- if (mSecondaryLog != null) {
- mSecondaryLog.printf("%1$s", description); //$NON-NLS-1$
- }
- }
- }
- });
- }
-
- /**
- * Logs a "normal" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void log(final String log) {
- if (acceptLog(log, false /*isDescription*/)) {
- syncExec(mLogDescription, new Runnable() {
- @Override
- public void run() {
- appendLine(TextStyle.DEFAULT, log);
- }
- });
-
- if (mSecondaryLog != null) {
- mSecondaryLog.printf(" %1$s", log); //$NON-NLS-1$
- }
- }
- }
-
- /**
- * Logs an "error" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logError(final String log) {
- if (acceptLog(log, false /*isDescription*/)) {
- syncExec(mLogDescription, new Runnable() {
- @Override
- public void run() {
- appendLine(TextStyle.ERROR, log);
- }
- });
-
- if (mSecondaryLog != null) {
- mSecondaryLog.error(null, "%1$s", log); //$NON-NLS-1$
- }
- }
- }
-
- /**
- * Logs a "verbose" information line, that is extra details which are typically
- * not that useful for the end-user and might be hidden until explicitly shown.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logVerbose(final String log) {
- if (acceptLog(log, false /*isDescription*/)) {
- syncExec(mLogDescription, new Runnable() {
- @Override
- public void run() {
- appendLine(TextStyle.DEFAULT, " " + log); //$NON-NLS-1$
- }
- });
-
- if (mSecondaryLog != null) {
- mSecondaryLog.printf(" %1$s", log); //$NON-NLS-1$
- }
- }
- }
-
-
- // ----
-
-
- /**
- * Centers the dialog in its parent shell.
- */
- private void positionWindow() {
- // Centers the dialog in its parent shell
- Shell child = mShell;
- if (child != null && mParentShell != null) {
- // get the parent client area with a location relative to the display
- Rectangle parentArea = mParentShell.getClientArea();
- Point parentLoc = mParentShell.getLocation();
- int px = parentLoc.x;
- int py = parentLoc.y;
- int pw = parentArea.width;
- int ph = parentArea.height;
-
- Point childSize = child.getSize();
- int cw = Math.max(childSize.x, pw);
- int ch = childSize.y;
-
- int x = 30 + px + (pw - cw) / 2;
- if (x < 0) x = 0;
-
- int y = py + (ph - ch) / 2;
- if (y < py) y = py;
-
- child.setLocation(x, y);
- child.setSize(cw, ch);
- }
- }
-
- private void appendLine(TextStyle style, String text) {
- if (!text.endsWith("\n")) { //$NON-NLS-1$
- text += '\n';
- }
-
- int start = mStyledText.getCharCount();
-
- if (style == TextStyle.DEFAULT) {
- mStyledText.append(text);
-
- } else {
- mStyledText.append(text);
-
- StyleRange sr = new StyleRange();
- sr.start = start;
- sr.length = text.length();
- sr.fontStyle = SWT.BOLD;
- if (style == TextStyle.ERROR) {
- sr.foreground = mStyledText.getDisplay().getSystemColor(SWT.COLOR_DARK_RED);
- }
- sr.underline = false;
- mStyledText.setStyleRange(sr);
- }
-
- // Scroll caret if it was already at the end before we added new text.
- // Ideally we would scroll if the scrollbar is at the bottom but we don't
- // have direct access to the scrollbar without overriding the SWT impl.
- if (mStyledText.getCaretOffset() >= start) {
- mStyledText.setSelection(mStyledText.getCharCount());
- }
- }
-
-
- private void syncExec(final Widget widget, final Runnable runnable) {
- if (widget != null && !widget.isDisposed()) {
- widget.getDisplay().syncExec(runnable);
- }
- }
-
- /**
- * Filter messages displayed in the log: <br/>
- * - Messages with a % are typical part of a progress update and shouldn't be in the log. <br/>
- * - Messages that are the same as the same output message should be output a second time.
- *
- * @param msg The potential log line to print.
- * @return True if the log line should be printed, false otherwise.
- */
- private boolean acceptLog(String msg, boolean isDescription) {
- if (msg == null) {
- return false;
- }
-
- msg = msg.trim();
-
- // Descriptions also have the download progress status (0..100%) which we want to avoid
- if (isDescription && msg.indexOf('%') != -1) {
- return false;
- }
-
- if (msg.equals(mLastLogMsg)) {
- return false;
- }
-
- mLastLogMsg = msg;
- return true;
- }
-}
+/* + * Copyright (C) 2011 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.ISdkLog; +import com.android.sdkuilib.internal.tasks.ILogUiProvider; +import com.android.sdkuilib.ui.GridDataBuilder; +import com.android.sdkuilib.ui.GridLayoutBuilder; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +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.Shell; +import org.eclipse.swt.widgets.Widget; + + +/** + * A floating log window that can be displayed or hidden by the main SDK Manager 2 window. + * It displays a log of the sdk manager operation (listing, install, delete) including + * any errors (e.g. network error or install/delete errors.) + * <p/> + * Since the SDK Manager will direct all log to this window, its purpose is to be + * opened by the main window at startup and left open all the time. When not needed + * the floating window is hidden but not closed. This way it can easily accumulate + * all the log. + */ +class LogWindow implements ILogUiProvider { + + private Shell mParentShell; + private Shell mShell; + private Composite mRootComposite; + private StyledText mStyledText; + private Label mLogDescription; + private Button mCloseButton; + + private final ISdkLog mSecondaryLog; + private boolean mCloseRequested; + private boolean mInitPosition = true; + private String mLastLogMsg = null; + + private enum TextStyle { + DEFAULT, + TITLE, + ERROR + } + + /** + * Creates the floating window. Callers should use {@link #open()} later. + * + * @param parentShell Parent container + * @param secondaryLog An optional logger where messages will <em>also</em> be output. + */ + public LogWindow(Shell parentShell, ISdkLog secondaryLog) { + mParentShell = parentShell; + mSecondaryLog = secondaryLog; + } + + /** + * For testing only. See {@link #open()} and {@link #close()} for normal usage. + * @wbp.parser.entryPoint + */ + void openBlocking() { + open(); + Display display = Display.getDefault(); + while (!mShell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + close(); + } + + /** + * Opens the window. + * This call does not block and relies on the fact that the main window is + * already running an SWT event dispatch loop. + * Caller should use {@link #close()} later. + */ + public void open() { + createShell(); + createContents(); + mShell.open(); + mShell.layout(); + mShell.setVisible(false); + } + + /** + * Closes and <em>destroys</em> the window. + * This must be called just before quitting the app. + * <p/> + * To simply hide/show the window, use {@link #setVisible(boolean)} instead. + */ + public void close() { + if (mShell != null && !mShell.isDisposed()) { + mCloseRequested = true; + mShell.close(); + mShell = null; + } + } + + /** + * Determines whether the window is currently shown or not. + * + * @return True if the window is shown. + */ + public boolean isVisible() { + return mShell != null && !mShell.isDisposed() && mShell.isVisible(); + } + + /** + * Toggles the window visibility. + * + * @param visible True to make the window visible, false to hide it. + */ + public void setVisible(boolean visible) { + if (mShell != null && !mShell.isDisposed()) { + mShell.setVisible(visible); + if (visible && mInitPosition) { + mInitPosition = false; + positionWindow(); + } + } + } + + private void createShell() { + mShell = new Shell(mParentShell, SWT.SHELL_TRIM | SWT.TOOL); + mShell.setMinimumSize(new Point(600, 300)); + mShell.setSize(450, 300); + mShell.setText("Android SDK Manager Log"); + GridLayoutBuilder.create(mShell); + + mShell.addShellListener(new ShellAdapter() { + @Override + public void shellClosed(ShellEvent e) { + if (!mCloseRequested) { + e.doit = false; + setVisible(false); + } + } + }); + } + + /** + * Create contents of the dialog. + */ + private void createContents() { + mRootComposite = new Composite(mShell, SWT.NONE); + GridLayoutBuilder.create(mRootComposite).columns(2); + GridDataBuilder.create(mRootComposite).fill().grab(); + + mStyledText = new StyledText(mRootComposite, + SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL); + GridDataBuilder.create(mStyledText).hSpan(2).fill().grab(); + + mLogDescription = new Label(mRootComposite, SWT.NONE); + GridDataBuilder.create(mLogDescription).hFill().hGrab(); + + mCloseButton = new Button(mRootComposite, SWT.NONE); + mCloseButton.setText("Close"); + mCloseButton.setToolTipText("Closes the log window"); + mCloseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + setVisible(false); //$hide$ + } + }); + } + + // --- Implementation of ILogUiProvider --- + + + /** + * Sets the description in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setDescription(final String description) { + syncExec(mLogDescription, new Runnable() { + @Override + public void run() { + mLogDescription.setText(description); + + if (acceptLog(description, true /*isDescription*/)) { + appendLine(TextStyle.TITLE, description); + + if (mSecondaryLog != null) { + mSecondaryLog.printf("%1$s", description); //$NON-NLS-1$ + } + } + } + }); + } + + /** + * Logs a "normal" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void log(final String log) { + if (acceptLog(log, false /*isDescription*/)) { + syncExec(mLogDescription, new Runnable() { + @Override + public void run() { + appendLine(TextStyle.DEFAULT, log); + } + }); + + if (mSecondaryLog != null) { + mSecondaryLog.printf(" %1$s", log); //$NON-NLS-1$ + } + } + } + + /** + * Logs an "error" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logError(final String log) { + if (acceptLog(log, false /*isDescription*/)) { + syncExec(mLogDescription, new Runnable() { + @Override + public void run() { + appendLine(TextStyle.ERROR, log); + } + }); + + if (mSecondaryLog != null) { + mSecondaryLog.error(null, "%1$s", log); //$NON-NLS-1$ + } + } + } + + /** + * Logs a "verbose" information line, that is extra details which are typically + * not that useful for the end-user and might be hidden until explicitly shown. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logVerbose(final String log) { + if (acceptLog(log, false /*isDescription*/)) { + syncExec(mLogDescription, new Runnable() { + @Override + public void run() { + appendLine(TextStyle.DEFAULT, " " + log); //$NON-NLS-1$ + } + }); + + if (mSecondaryLog != null) { + mSecondaryLog.printf(" %1$s", log); //$NON-NLS-1$ + } + } + } + + + // ---- + + + /** + * Centers the dialog in its parent shell. + */ + private void positionWindow() { + // Centers the dialog in its parent shell + Shell child = mShell; + if (child != null && mParentShell != null) { + // get the parent client area with a location relative to the display + Rectangle parentArea = mParentShell.getClientArea(); + Point parentLoc = mParentShell.getLocation(); + int px = parentLoc.x; + int py = parentLoc.y; + int pw = parentArea.width; + int ph = parentArea.height; + + Point childSize = child.getSize(); + int cw = Math.max(childSize.x, pw); + int ch = childSize.y; + + int x = 30 + px + (pw - cw) / 2; + if (x < 0) x = 0; + + int y = py + (ph - ch) / 2; + if (y < py) y = py; + + child.setLocation(x, y); + child.setSize(cw, ch); + } + } + + private void appendLine(TextStyle style, String text) { + if (!text.endsWith("\n")) { //$NON-NLS-1$ + text += '\n'; + } + + int start = mStyledText.getCharCount(); + + if (style == TextStyle.DEFAULT) { + mStyledText.append(text); + + } else { + mStyledText.append(text); + + StyleRange sr = new StyleRange(); + sr.start = start; + sr.length = text.length(); + sr.fontStyle = SWT.BOLD; + if (style == TextStyle.ERROR) { + sr.foreground = mStyledText.getDisplay().getSystemColor(SWT.COLOR_DARK_RED); + } + sr.underline = false; + mStyledText.setStyleRange(sr); + } + + // Scroll caret if it was already at the end before we added new text. + // Ideally we would scroll if the scrollbar is at the bottom but we don't + // have direct access to the scrollbar without overriding the SWT impl. + if (mStyledText.getCaretOffset() >= start) { + mStyledText.setSelection(mStyledText.getCharCount()); + } + } + + + private void syncExec(final Widget widget, final Runnable runnable) { + if (widget != null && !widget.isDisposed()) { + widget.getDisplay().syncExec(runnable); + } + } + + /** + * Filter messages displayed in the log: <br/> + * - Messages with a % are typical part of a progress update and shouldn't be in the log. <br/> + * - Messages that are the same as the same output message should be output a second time. + * + * @param msg The potential log line to print. + * @return True if the log line should be printed, false otherwise. + */ + private boolean acceptLog(String msg, boolean isDescription) { + if (msg == null) { + return false; + } + + msg = msg.trim(); + + // Descriptions also have the download progress status (0..100%) which we want to avoid + if (isDescription && msg.indexOf('%') != -1) { + return false; + } + + if (msg.equals(mLastLogMsg)) { + return false; + } + + mLastLogMsg = msg; + return true; + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkLogAdapter.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkLogAdapter.java index 969d930..aa4bc6c 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkLogAdapter.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkLogAdapter.java @@ -1,112 +1,112 @@ -/*
- * Copyright (C) 2011 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.ISdkLog;
-import com.android.sdkuilib.internal.tasks.ILogUiProvider;
-
-
-/**
- * Adapter that transform log from an {@link ILogUiProvider} to an {@link ISdkLog}.
- */
-public final class SdkLogAdapter implements ILogUiProvider {
-
- private ISdkLog mSdkLog;
- private String mLastLogMsg;
-
- /**
- * Creates a new adapter to output log on the given {@code sdkLog}.
- *
- * @param sdkLog The logger to output to. Must not be null.
- */
- public SdkLogAdapter(ISdkLog sdkLog) {
- mSdkLog = sdkLog;
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setDescription(final String description) {
- if (acceptLog(description)) {
- mSdkLog.printf("%1$s", description); //$NON-NLS-1$
- }
- }
-
- /**
- * Logs a "normal" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void log(String log) {
- if (acceptLog(log)) {
- mSdkLog.printf(" %1$s", log); //$NON-NLS-1$
- }
- }
-
- /**
- * Logs an "error" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logError(String log) {
- if (acceptLog(log)) {
- mSdkLog.error(null, " %1$s", log); //$NON-NLS-1$
- }
- }
-
- /**
- * Logs a "verbose" information line, that is extra details which are typically
- * not that useful for the end-user and might be hidden until explicitly shown.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logVerbose(String log) {
- if (acceptLog(log)) {
- mSdkLog.printf(" %1$s", log); //$NON-NLS-1$
- }
- }
-
- // ----
-
- /**
- * Filter messages displayed in the log: <br/>
- * - Messages with a % are typical part of a progress update and shouldn't be in the log. <br/>
- * - Messages that are the same as the same output message should be output a second time.
- *
- * @param msg The potential log line to print.
- * @return True if the log line should be printed, false otherwise.
- */
- private boolean acceptLog(String msg) {
- if (msg == null) {
- return false;
- }
-
- msg = msg.trim();
- if (msg.indexOf('%') != -1) {
- return false;
- }
-
- if (msg.equals(mLastLogMsg)) {
- return false;
- }
-
- mLastLogMsg = msg;
- return true;
- }
-}
+/* + * Copyright (C) 2011 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.ISdkLog; +import com.android.sdkuilib.internal.tasks.ILogUiProvider; + + +/** + * Adapter that transform log from an {@link ILogUiProvider} to an {@link ISdkLog}. + */ +public final class SdkLogAdapter implements ILogUiProvider { + + private ISdkLog mSdkLog; + private String mLastLogMsg; + + /** + * Creates a new adapter to output log on the given {@code sdkLog}. + * + * @param sdkLog The logger to output to. Must not be null. + */ + public SdkLogAdapter(ISdkLog sdkLog) { + mSdkLog = sdkLog; + } + + /** + * Sets the description in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setDescription(final String description) { + if (acceptLog(description)) { + mSdkLog.printf("%1$s", description); //$NON-NLS-1$ + } + } + + /** + * Logs a "normal" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void log(String log) { + if (acceptLog(log)) { + mSdkLog.printf(" %1$s", log); //$NON-NLS-1$ + } + } + + /** + * Logs an "error" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logError(String log) { + if (acceptLog(log)) { + mSdkLog.error(null, " %1$s", log); //$NON-NLS-1$ + } + } + + /** + * Logs a "verbose" information line, that is extra details which are typically + * not that useful for the end-user and might be hidden until explicitly shown. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logVerbose(String log) { + if (acceptLog(log)) { + mSdkLog.printf(" %1$s", log); //$NON-NLS-1$ + } + } + + // ---- + + /** + * Filter messages displayed in the log: <br/> + * - Messages with a % are typical part of a progress update and shouldn't be in the log. <br/> + * - Messages that are the same as the same output message should be output a second time. + * + * @param msg The potential log line to print. + * @return True if the log line should be printed, false otherwise. + */ + private boolean acceptLog(String msg) { + if (msg == null) { + return false; + } + + msg = msg.trim(); + if (msg.indexOf('%') != -1) { + return false; + } + + if (msg.equals(mLastLogMsg)) { + return false; + } + + mLastLogMsg = msg; + return true; + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkUpdaterWindowImpl2.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkUpdaterWindowImpl2.java index 4376ec8..3542c5f 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkUpdaterWindowImpl2.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkUpdaterWindowImpl2.java @@ -1,590 +1,590 @@ -/*
- * Copyright (C) 2011 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.ISdkLog;
-import com.android.sdklib.SdkConstants;
-import com.android.sdklib.internal.repository.ITaskFactory;
-import com.android.sdklib.internal.repository.sources.SdkSourceProperties;
-import com.android.sdkuilib.internal.repository.AboutDialog;
-import com.android.sdkuilib.internal.repository.ISdkUpdaterWindow;
-import com.android.sdkuilib.internal.repository.MenuBarWrapper;
-import com.android.sdkuilib.internal.repository.SettingsController;
-import com.android.sdkuilib.internal.repository.SettingsController.Settings;
-import com.android.sdkuilib.internal.repository.SettingsDialog;
-import com.android.sdkuilib.internal.repository.UpdaterData;
-import com.android.sdkuilib.internal.repository.icons.ImageFactory;
-import com.android.sdkuilib.internal.repository.sdkman2.PackagesPage.MenuAction;
-import com.android.sdkuilib.internal.tasks.ILogUiProvider;
-import com.android.sdkuilib.internal.tasks.ProgressView;
-import com.android.sdkuilib.internal.tasks.ProgressViewFactory;
-import com.android.sdkuilib.internal.widgets.ImgDisabledButton;
-import com.android.sdkuilib.internal.widgets.ToggleButton;
-import com.android.sdkuilib.repository.AvdManagerWindow.AvdInvocationContext;
-import com.android.sdkuilib.repository.ISdkChangeListener;
-import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext;
-
-import org.eclipse.swt.SWT;
-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.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.MenuItem;
-import org.eclipse.swt.widgets.ProgressBar;
-import org.eclipse.swt.widgets.Shell;
-
-/**
- * This is the private implementation of the UpdateWindow
- * for the second version of the SDK Manager.
- * <p/>
- * This window features only one embedded page, the combined installed+available package list.
- */
-public class SdkUpdaterWindowImpl2 implements ISdkUpdaterWindow {
-
- public static final String APP_NAME = "Android SDK Manager";
- private static final String SIZE_POS_PREFIX = "sdkman2"; //$NON-NLS-1$
-
- private final Shell mParentShell;
- private final SdkInvocationContext mContext;
- /** Internal data shared between the window and its pages. */
- private final UpdaterData mUpdaterData;
-
- // --- UI members ---
-
- protected Shell mShell;
- private PackagesPage mPkgPage;
- private ProgressBar mProgressBar;
- private Label mStatusText;
- private ImgDisabledButton mButtonStop;
- private ToggleButton mButtonShowLog;
- private SettingsController mSettingsController;
- private LogWindow mLogWindow;
-
- /**
- * Creates a new window. Caller must call open(), which will block.
- *
- * @param parentShell Parent shell.
- * @param sdkLog Logger. Cannot be null.
- * @param osSdkRoot The OS path to the SDK root.
- * @param context The {@link SdkInvocationContext} to change the behavior depending on who's
- * opening the SDK Manager.
- */
- public SdkUpdaterWindowImpl2(
- Shell parentShell,
- ISdkLog sdkLog,
- String osSdkRoot,
- SdkInvocationContext context) {
- mParentShell = parentShell;
- mContext = context;
- mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
- }
-
- /**
- * Creates a new window. Caller must call open(), which will block.
- * <p/>
- * This is to be used when the window is opened from {@link AvdManagerWindowImpl1}
- * to share the same {@link UpdaterData} structure.
- *
- * @param parentShell Parent shell.
- * @param updaterData The parent's updater data.
- * @param context The {@link SdkInvocationContext} to change the behavior depending on who's
- * opening the SDK Manager.
- */
- public SdkUpdaterWindowImpl2(
- Shell parentShell,
- UpdaterData updaterData,
- SdkInvocationContext context) {
- mParentShell = parentShell;
- mContext = context;
- mUpdaterData = updaterData;
- }
-
- /**
- * Opens the window.
- * @wbp.parser.entryPoint
- */
- @Override
- public void open() {
- if (mParentShell == null) {
- Display.setAppName(APP_NAME); //$hide$ (hide from SWT designer)
- }
-
- createShell();
- preCreateContent();
- createContents();
- createMenuBar();
- createLogWindow();
- mShell.open();
- mShell.layout();
-
- if (postCreateContent()) { //$hide$ (hide from SWT designer)
- Display display = Display.getDefault();
- while (!mShell.isDisposed()) {
- if (!display.readAndDispatch()) {
- display.sleep();
- }
- }
- }
-
- SdkSourceProperties p = new SdkSourceProperties();
- p.save();
-
- dispose(); //$hide$
- }
-
- private void createShell() {
- // The SDK Manager must use a shell trim when standalone
- // or a dialog trim when invoked from somewhere else.
- int style = SWT.SHELL_TRIM;
- if (mContext != SdkInvocationContext.STANDALONE) {
- style |= SWT.APPLICATION_MODAL;
- }
-
- mShell = new Shell(mParentShell, style);
- mShell.addDisposeListener(new DisposeListener() {
- @Override
- public void widgetDisposed(DisposeEvent e) {
- ShellSizeAndPos.saveSizeAndPos(mShell, SIZE_POS_PREFIX);
- onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)
- }
- });
-
- GridLayout glShell = new GridLayout(2, false);
- glShell.verticalSpacing = 0;
- glShell.horizontalSpacing = 0;
- glShell.marginWidth = 0;
- glShell.marginHeight = 0;
- mShell.setLayout(glShell);
-
- mShell.setMinimumSize(new Point(500, 300));
- mShell.setSize(700, 500);
- mShell.setText(APP_NAME);
-
- ShellSizeAndPos.loadSizeAndPos(mShell, SIZE_POS_PREFIX);
- }
-
- private void createContents() {
- mPkgPage = new PackagesPage(mShell, SWT.NONE, mUpdaterData, mContext);
- mPkgPage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
-
- Composite composite1 = new Composite(mShell, SWT.NONE);
- composite1.setLayout(new GridLayout(1, false));
- composite1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
-
- mProgressBar = new ProgressBar(composite1, SWT.NONE);
- mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
-
- mStatusText = new Label(composite1, SWT.NONE);
- mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder
- mStatusText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
-
- Composite composite2 = new Composite(mShell, SWT.NONE);
- composite2.setLayout(new GridLayout(2, false));
-
- mButtonStop = new ImgDisabledButton(composite2, SWT.NONE,
- getImage("stop_enabled_16.png"), //$NON-NLS-1$
- getImage("stop_disabled_16.png"), //$NON-NLS-1$
- "Click to abort the current task",
- ""); //$NON-NLS-1$ nothing to abort
- mButtonStop.addListener(SWT.Selection, new Listener() {
- @Override
- public void handleEvent(Event event) {
- onStopSelected();
- }
- });
-
- mButtonShowLog = new ToggleButton(composite2, SWT.NONE,
- getImage("log_off_16.png"), //$NON-NLS-1$
- getImage("log_on_16.png"), //$NON-NLS-1$
- "Click to show the log window", // tooltip for state hidden=>shown
- "Click to hide the log window"); // tooltip for state shown=>hidden
- mButtonShowLog.addListener(SWT.Selection, new Listener() {
- @Override
- public void handleEvent(Event event) {
- onToggleLogWindow();
- }
- });
- }
-
- @SuppressWarnings("unused") // MenuItem works using side effects
- private void createMenuBar() {
-
- Menu menuBar = new Menu(mShell, SWT.BAR);
- mShell.setMenuBar(menuBar);
-
- MenuItem menuBarPackages = new MenuItem(menuBar, SWT.CASCADE);
- menuBarPackages.setText("Packages");
-
- Menu menuPkgs = new Menu(menuBarPackages);
- menuBarPackages.setMenu(menuPkgs);
-
- MenuItem showUpdatesNew = new MenuItem(menuPkgs,
- MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuStyle());
- showUpdatesNew.setText(
- MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG, showUpdatesNew);
-
- MenuItem showInstalled = new MenuItem(menuPkgs,
- MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuStyle());
- showInstalled.setText(
- MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.TOGGLE_SHOW_INSTALLED_PKG, showInstalled);
-
- MenuItem showObsoletePackages = new MenuItem(menuPkgs,
- MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuStyle());
- showObsoletePackages.setText(
- MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.TOGGLE_SHOW_OBSOLETE_PKG, showObsoletePackages);
-
- MenuItem showArchives = new MenuItem(menuPkgs,
- MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuStyle());
- showArchives.setText(
- MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.TOGGLE_SHOW_ARCHIVES, showArchives);
-
- new MenuItem(menuPkgs, SWT.SEPARATOR);
-
- MenuItem sortByApi = new MenuItem(menuPkgs,
- MenuAction.SORT_API_LEVEL.getMenuStyle());
- sortByApi.setText(
- MenuAction.SORT_API_LEVEL.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.SORT_API_LEVEL, sortByApi);
-
- MenuItem sortBySource = new MenuItem(menuPkgs,
- MenuAction.SORT_SOURCE.getMenuStyle());
- sortBySource.setText(
- MenuAction.SORT_SOURCE.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.SORT_SOURCE, sortBySource);
-
- new MenuItem(menuPkgs, SWT.SEPARATOR);
-
- MenuItem reload = new MenuItem(menuPkgs,
- MenuAction.RELOAD.getMenuStyle());
- reload.setText(
- MenuAction.RELOAD.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.RELOAD, reload);
-
- MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE);
- menuBarTools.setText("Tools");
-
- Menu menuTools = new Menu(menuBarTools);
- menuBarTools.setMenu(menuTools);
-
- if (mContext == SdkInvocationContext.STANDALONE) {
- MenuItem manageAvds = new MenuItem(menuTools, SWT.NONE);
- manageAvds.setText("Manage AVDs...");
- manageAvds.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent event) {
- onAvdManager();
- }
- });
- }
-
- MenuItem manageSources = new MenuItem(menuTools,
- MenuAction.SHOW_ADDON_SITES.getMenuStyle());
- manageSources.setText(
- MenuAction.SHOW_ADDON_SITES.getMenuTitle());
- mPkgPage.registerMenuAction(
- MenuAction.SHOW_ADDON_SITES, manageSources);
-
- if (mContext == SdkInvocationContext.STANDALONE || mContext == SdkInvocationContext.IDE) {
- try {
- new MenuBarWrapper(APP_NAME, menuTools) {
- @Override
- public void onPreferencesMenuSelected() {
-
- // capture a copy of the initial settings
- Settings settings1 = new Settings(mSettingsController.getSettings());
-
- // open the dialog and wait for it to close
- SettingsDialog sd = new SettingsDialog(mShell, mUpdaterData);
- sd.open();
-
- // get the new settings
- Settings settings2 = mSettingsController.getSettings();
-
- // We need to reload the package list if the http mode or the preview
- // modes have changed.
- if (settings1.getForceHttp() != settings2.getForceHttp() ||
- settings1.getEnablePreviews() != settings2.getEnablePreviews()) {
- mPkgPage.onSdkReload();
- }
- }
-
- @Override
- public void onAboutMenuSelected() {
- AboutDialog ad = new AboutDialog(mShell, mUpdaterData);
- ad.open();
- }
-
- @Override
- public void printError(String format, Object... args) {
- if (mUpdaterData != null) {
- mUpdaterData.getSdkLog().error(null, format, args);
- }
- }
- };
- } catch (Throwable e) {
- mUpdaterData.getSdkLog().error(e, "Failed to setup menu bar");
- e.printStackTrace();
- }
- }
- }
-
- private Image getImage(String filename) {
- if (mUpdaterData != null) {
- ImageFactory imgFactory = mUpdaterData.getImageFactory();
- if (imgFactory != null) {
- return imgFactory.getImageByName(filename);
- }
- }
- return null;
- }
-
- /**
- * Creates the log window.
- * <p/>
- * If this is invoked from an IDE, we also define a secondary logger so that all
- * messages flow to the IDE log. This may or may not be what we want in the end
- * (e.g. a middle ground would be to repeat error, and ignore normal/verbose)
- */
- private void createLogWindow() {
- mLogWindow = new LogWindow(mShell,
- mContext == SdkInvocationContext.IDE ? mUpdaterData.getSdkLog() : null);
- mLogWindow.open();
- }
-
-
- // -- Start of internal part ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- // --- Public API -----------
-
- /**
- * Adds a new listener to be notified when a change is made to the content of the SDK.
- */
- @Override
- public void addListener(ISdkChangeListener listener) {
- mUpdaterData.addListeners(listener);
- }
-
- /**
- * Removes a new listener to be notified anymore when a change is made to the content of
- * the SDK.
- */
- @Override
- public void removeListener(ISdkChangeListener listener) {
- mUpdaterData.removeListener(listener);
- }
-
- // --- Internals & UI Callbacks -----------
-
- /**
- * Called before the UI is created.
- */
- private void preCreateContent() {
- mUpdaterData.setWindowShell(mShell);
- // We need the UI factory to create the UI
- mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay()));
- // Note: we can't create the TaskFactory yet because we need the UI
- // to be created first, so this is done in postCreateContent().
- }
-
- /**
- * Once the UI has been created, initializes the content.
- * This creates the pages, selects the first one, setups sources and scans for local folders.
- *
- * Returns true if we should show the window.
- */
- private boolean postCreateContent() {
- ProgressViewFactory factory = new ProgressViewFactory();
-
- // This class delegates all logging to the mLogWindow window
- // and filters errors to make sure the window is visible when
- // an error is logged.
- ILogUiProvider logAdapter = new ILogUiProvider() {
- @Override
- public void setDescription(String description) {
- mLogWindow.setDescription(description);
- }
-
- @Override
- public void log(String log) {
- mLogWindow.log(log);
- }
-
- @Override
- public void logVerbose(String log) {
- mLogWindow.logVerbose(log);
- }
-
- @Override
- public void logError(String log) {
- mLogWindow.logError(log);
-
- // Run the window visibility check/toggle on the UI thread.
- // Note: at least on Windows, it seems ok to check for the window visibility
- // on a sub-thread but that doesn't seem cross-platform safe. We shouldn't
- // have a lot of error logging, so this should be acceptable. If not, we could
- // cache the visibility state.
- if (mShell != null && !mShell.isDisposed()) {
- mShell.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mLogWindow.isVisible()) {
- // Don't toggle the window visibility directly.
- // Instead use the same action as the log-toggle button
- // so that the button's state be kept in sync.
- onToggleLogWindow();
- }
- }
- });
- }
- }
- };
-
- factory.setProgressView(
- new ProgressView(mStatusText, mProgressBar, mButtonStop, logAdapter));
- mUpdaterData.setTaskFactory(factory);
-
- setWindowImage(mShell);
-
- setupSources();
- initializeSettings();
-
- if (mUpdaterData.checkIfInitFailed()) {
- return false;
- }
-
- mUpdaterData.broadcastOnSdkLoaded();
-
- // Tell the one page its the selected one
- mPkgPage.performFirstLoad();
-
- return true;
- }
-
- /**
- * Creates the icon of the window shell.
- *
- * @param shell The shell on which to put the icon
- */
- private void setWindowImage(Shell shell) {
- String imageName = "android_icon_16.png"; //$NON-NLS-1$
- if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
- imageName = "android_icon_128.png"; //$NON-NLS-1$
- }
-
- if (mUpdaterData != null) {
- ImageFactory imgFactory = mUpdaterData.getImageFactory();
- if (imgFactory != null) {
- shell.setImage(imgFactory.getImageByName(imageName));
- }
- }
- }
-
- /**
- * Called by the main loop when the window has been disposed.
- */
- private void dispose() {
- mLogWindow.close();
- mUpdaterData.getSources().saveUserAddons(mUpdaterData.getSdkLog());
- }
-
- /**
- * Callback called when the window shell is disposed.
- */
- private void onAndroidSdkUpdaterDispose() {
- if (mUpdaterData != null) {
- ImageFactory imgFactory = mUpdaterData.getImageFactory();
- if (imgFactory != null) {
- imgFactory.dispose();
- }
- }
- }
-
- /**
- * Used to initialize the sources.
- */
- private void setupSources() {
- mUpdaterData.setupDefaultSources();
- }
-
- /**
- * Initializes settings.
- * This must be called after addExtraPages(), which created a settings page.
- * Iterate through all the pages to find the first (and supposedly unique) setting page,
- * and use it to load and apply these settings.
- */
- private void initializeSettings() {
- mSettingsController = mUpdaterData.getSettingsController();
- mSettingsController.loadSettings();
- mSettingsController.applySettings();
- }
-
- private void onToggleLogWindow() {
- // toggle visibility
- if (!mButtonShowLog.isDisposed()) {
- mLogWindow.setVisible(!mLogWindow.isVisible());
- mButtonShowLog.setState(mLogWindow.isVisible() ? 1 : 0);
- }
- }
-
- private void onStopSelected() {
- // TODO
- }
-
- private void onAvdManager() {
- ITaskFactory oldFactory = mUpdaterData.getTaskFactory();
-
- try {
- AvdManagerWindowImpl1 win = new AvdManagerWindowImpl1(
- mShell,
- mUpdaterData,
- AvdInvocationContext.DIALOG);
-
- win.open();
- } catch (Exception e) {
- mUpdaterData.getSdkLog().error(e, "AVD Manager window error");
- } finally {
- mUpdaterData.setTaskFactory(oldFactory);
- }
- }
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/* + * Copyright (C) 2011 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.ISdkLog; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.repository.ITaskFactory; +import com.android.sdklib.internal.repository.sources.SdkSourceProperties; +import com.android.sdkuilib.internal.repository.AboutDialog; +import com.android.sdkuilib.internal.repository.ISdkUpdaterWindow; +import com.android.sdkuilib.internal.repository.MenuBarWrapper; +import com.android.sdkuilib.internal.repository.SettingsController; +import com.android.sdkuilib.internal.repository.SettingsController.Settings; +import com.android.sdkuilib.internal.repository.SettingsDialog; +import com.android.sdkuilib.internal.repository.UpdaterData; +import com.android.sdkuilib.internal.repository.icons.ImageFactory; +import com.android.sdkuilib.internal.repository.sdkman2.PackagesPage.MenuAction; +import com.android.sdkuilib.internal.tasks.ILogUiProvider; +import com.android.sdkuilib.internal.tasks.ProgressView; +import com.android.sdkuilib.internal.tasks.ProgressViewFactory; +import com.android.sdkuilib.internal.widgets.ImgDisabledButton; +import com.android.sdkuilib.internal.widgets.ToggleButton; +import com.android.sdkuilib.repository.AvdManagerWindow.AvdInvocationContext; +import com.android.sdkuilib.repository.ISdkChangeListener; +import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext; + +import org.eclipse.swt.SWT; +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.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Shell; + +/** + * This is the private implementation of the UpdateWindow + * for the second version of the SDK Manager. + * <p/> + * This window features only one embedded page, the combined installed+available package list. + */ +public class SdkUpdaterWindowImpl2 implements ISdkUpdaterWindow { + + public static final String APP_NAME = "Android SDK Manager"; + private static final String SIZE_POS_PREFIX = "sdkman2"; //$NON-NLS-1$ + + private final Shell mParentShell; + private final SdkInvocationContext mContext; + /** Internal data shared between the window and its pages. */ + private final UpdaterData mUpdaterData; + + // --- UI members --- + + protected Shell mShell; + private PackagesPage mPkgPage; + private ProgressBar mProgressBar; + private Label mStatusText; + private ImgDisabledButton mButtonStop; + private ToggleButton mButtonShowLog; + private SettingsController mSettingsController; + private LogWindow mLogWindow; + + /** + * Creates a new window. Caller must call open(), which will block. + * + * @param parentShell Parent shell. + * @param sdkLog Logger. Cannot be null. + * @param osSdkRoot The OS path to the SDK root. + * @param context The {@link SdkInvocationContext} to change the behavior depending on who's + * opening the SDK Manager. + */ + public SdkUpdaterWindowImpl2( + Shell parentShell, + ISdkLog sdkLog, + String osSdkRoot, + SdkInvocationContext context) { + mParentShell = parentShell; + mContext = context; + mUpdaterData = new UpdaterData(osSdkRoot, sdkLog); + } + + /** + * Creates a new window. Caller must call open(), which will block. + * <p/> + * This is to be used when the window is opened from {@link AvdManagerWindowImpl1} + * to share the same {@link UpdaterData} structure. + * + * @param parentShell Parent shell. + * @param updaterData The parent's updater data. + * @param context The {@link SdkInvocationContext} to change the behavior depending on who's + * opening the SDK Manager. + */ + public SdkUpdaterWindowImpl2( + Shell parentShell, + UpdaterData updaterData, + SdkInvocationContext context) { + mParentShell = parentShell; + mContext = context; + mUpdaterData = updaterData; + } + + /** + * Opens the window. + * @wbp.parser.entryPoint + */ + @Override + public void open() { + if (mParentShell == null) { + Display.setAppName(APP_NAME); //$hide$ (hide from SWT designer) + } + + createShell(); + preCreateContent(); + createContents(); + createMenuBar(); + createLogWindow(); + mShell.open(); + mShell.layout(); + + if (postCreateContent()) { //$hide$ (hide from SWT designer) + Display display = Display.getDefault(); + while (!mShell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + } + + SdkSourceProperties p = new SdkSourceProperties(); + p.save(); + + dispose(); //$hide$ + } + + private void createShell() { + // The SDK Manager must use a shell trim when standalone + // or a dialog trim when invoked from somewhere else. + int style = SWT.SHELL_TRIM; + if (mContext != SdkInvocationContext.STANDALONE) { + style |= SWT.APPLICATION_MODAL; + } + + mShell = new Shell(mParentShell, style); + mShell.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + ShellSizeAndPos.saveSizeAndPos(mShell, SIZE_POS_PREFIX); + onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer) + } + }); + + GridLayout glShell = new GridLayout(2, false); + glShell.verticalSpacing = 0; + glShell.horizontalSpacing = 0; + glShell.marginWidth = 0; + glShell.marginHeight = 0; + mShell.setLayout(glShell); + + mShell.setMinimumSize(new Point(500, 300)); + mShell.setSize(700, 500); + mShell.setText(APP_NAME); + + ShellSizeAndPos.loadSizeAndPos(mShell, SIZE_POS_PREFIX); + } + + private void createContents() { + mPkgPage = new PackagesPage(mShell, SWT.NONE, mUpdaterData, mContext); + mPkgPage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + + Composite composite1 = new Composite(mShell, SWT.NONE); + composite1.setLayout(new GridLayout(1, false)); + composite1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + mProgressBar = new ProgressBar(composite1, SWT.NONE); + mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + mStatusText = new Label(composite1, SWT.NONE); + mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder + mStatusText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + Composite composite2 = new Composite(mShell, SWT.NONE); + composite2.setLayout(new GridLayout(2, false)); + + mButtonStop = new ImgDisabledButton(composite2, SWT.NONE, + getImage("stop_enabled_16.png"), //$NON-NLS-1$ + getImage("stop_disabled_16.png"), //$NON-NLS-1$ + "Click to abort the current task", + ""); //$NON-NLS-1$ nothing to abort + mButtonStop.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + onStopSelected(); + } + }); + + mButtonShowLog = new ToggleButton(composite2, SWT.NONE, + getImage("log_off_16.png"), //$NON-NLS-1$ + getImage("log_on_16.png"), //$NON-NLS-1$ + "Click to show the log window", // tooltip for state hidden=>shown + "Click to hide the log window"); // tooltip for state shown=>hidden + mButtonShowLog.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + onToggleLogWindow(); + } + }); + } + + @SuppressWarnings("unused") // MenuItem works using side effects + private void createMenuBar() { + + Menu menuBar = new Menu(mShell, SWT.BAR); + mShell.setMenuBar(menuBar); + + MenuItem menuBarPackages = new MenuItem(menuBar, SWT.CASCADE); + menuBarPackages.setText("Packages"); + + Menu menuPkgs = new Menu(menuBarPackages); + menuBarPackages.setMenu(menuPkgs); + + MenuItem showUpdatesNew = new MenuItem(menuPkgs, + MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuStyle()); + showUpdatesNew.setText( + MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG, showUpdatesNew); + + MenuItem showInstalled = new MenuItem(menuPkgs, + MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuStyle()); + showInstalled.setText( + MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.TOGGLE_SHOW_INSTALLED_PKG, showInstalled); + + MenuItem showObsoletePackages = new MenuItem(menuPkgs, + MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuStyle()); + showObsoletePackages.setText( + MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.TOGGLE_SHOW_OBSOLETE_PKG, showObsoletePackages); + + MenuItem showArchives = new MenuItem(menuPkgs, + MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuStyle()); + showArchives.setText( + MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.TOGGLE_SHOW_ARCHIVES, showArchives); + + new MenuItem(menuPkgs, SWT.SEPARATOR); + + MenuItem sortByApi = new MenuItem(menuPkgs, + MenuAction.SORT_API_LEVEL.getMenuStyle()); + sortByApi.setText( + MenuAction.SORT_API_LEVEL.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.SORT_API_LEVEL, sortByApi); + + MenuItem sortBySource = new MenuItem(menuPkgs, + MenuAction.SORT_SOURCE.getMenuStyle()); + sortBySource.setText( + MenuAction.SORT_SOURCE.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.SORT_SOURCE, sortBySource); + + new MenuItem(menuPkgs, SWT.SEPARATOR); + + MenuItem reload = new MenuItem(menuPkgs, + MenuAction.RELOAD.getMenuStyle()); + reload.setText( + MenuAction.RELOAD.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.RELOAD, reload); + + MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE); + menuBarTools.setText("Tools"); + + Menu menuTools = new Menu(menuBarTools); + menuBarTools.setMenu(menuTools); + + if (mContext == SdkInvocationContext.STANDALONE) { + MenuItem manageAvds = new MenuItem(menuTools, SWT.NONE); + manageAvds.setText("Manage AVDs..."); + manageAvds.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + onAvdManager(); + } + }); + } + + MenuItem manageSources = new MenuItem(menuTools, + MenuAction.SHOW_ADDON_SITES.getMenuStyle()); + manageSources.setText( + MenuAction.SHOW_ADDON_SITES.getMenuTitle()); + mPkgPage.registerMenuAction( + MenuAction.SHOW_ADDON_SITES, manageSources); + + if (mContext == SdkInvocationContext.STANDALONE || mContext == SdkInvocationContext.IDE) { + try { + new MenuBarWrapper(APP_NAME, menuTools) { + @Override + public void onPreferencesMenuSelected() { + + // capture a copy of the initial settings + Settings settings1 = new Settings(mSettingsController.getSettings()); + + // open the dialog and wait for it to close + SettingsDialog sd = new SettingsDialog(mShell, mUpdaterData); + sd.open(); + + // get the new settings + Settings settings2 = mSettingsController.getSettings(); + + // We need to reload the package list if the http mode or the preview + // modes have changed. + if (settings1.getForceHttp() != settings2.getForceHttp() || + settings1.getEnablePreviews() != settings2.getEnablePreviews()) { + mPkgPage.onSdkReload(); + } + } + + @Override + public void onAboutMenuSelected() { + AboutDialog ad = new AboutDialog(mShell, mUpdaterData); + ad.open(); + } + + @Override + public void printError(String format, Object... args) { + if (mUpdaterData != null) { + mUpdaterData.getSdkLog().error(null, format, args); + } + } + }; + } catch (Throwable e) { + mUpdaterData.getSdkLog().error(e, "Failed to setup menu bar"); + e.printStackTrace(); + } + } + } + + private Image getImage(String filename) { + if (mUpdaterData != null) { + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (imgFactory != null) { + return imgFactory.getImageByName(filename); + } + } + return null; + } + + /** + * Creates the log window. + * <p/> + * If this is invoked from an IDE, we also define a secondary logger so that all + * messages flow to the IDE log. This may or may not be what we want in the end + * (e.g. a middle ground would be to repeat error, and ignore normal/verbose) + */ + private void createLogWindow() { + mLogWindow = new LogWindow(mShell, + mContext == SdkInvocationContext.IDE ? mUpdaterData.getSdkLog() : null); + mLogWindow.open(); + } + + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + // --- Public API ----------- + + /** + * Adds a new listener to be notified when a change is made to the content of the SDK. + */ + @Override + public void addListener(ISdkChangeListener listener) { + mUpdaterData.addListeners(listener); + } + + /** + * Removes a new listener to be notified anymore when a change is made to the content of + * the SDK. + */ + @Override + public void removeListener(ISdkChangeListener listener) { + mUpdaterData.removeListener(listener); + } + + // --- Internals & UI Callbacks ----------- + + /** + * Called before the UI is created. + */ + private void preCreateContent() { + mUpdaterData.setWindowShell(mShell); + // We need the UI factory to create the UI + mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay())); + // Note: we can't create the TaskFactory yet because we need the UI + // to be created first, so this is done in postCreateContent(). + } + + /** + * Once the UI has been created, initializes the content. + * This creates the pages, selects the first one, setups sources and scans for local folders. + * + * Returns true if we should show the window. + */ + private boolean postCreateContent() { + ProgressViewFactory factory = new ProgressViewFactory(); + + // This class delegates all logging to the mLogWindow window + // and filters errors to make sure the window is visible when + // an error is logged. + ILogUiProvider logAdapter = new ILogUiProvider() { + @Override + public void setDescription(String description) { + mLogWindow.setDescription(description); + } + + @Override + public void log(String log) { + mLogWindow.log(log); + } + + @Override + public void logVerbose(String log) { + mLogWindow.logVerbose(log); + } + + @Override + public void logError(String log) { + mLogWindow.logError(log); + + // Run the window visibility check/toggle on the UI thread. + // Note: at least on Windows, it seems ok to check for the window visibility + // on a sub-thread but that doesn't seem cross-platform safe. We shouldn't + // have a lot of error logging, so this should be acceptable. If not, we could + // cache the visibility state. + if (mShell != null && !mShell.isDisposed()) { + mShell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mLogWindow.isVisible()) { + // Don't toggle the window visibility directly. + // Instead use the same action as the log-toggle button + // so that the button's state be kept in sync. + onToggleLogWindow(); + } + } + }); + } + } + }; + + factory.setProgressView( + new ProgressView(mStatusText, mProgressBar, mButtonStop, logAdapter)); + mUpdaterData.setTaskFactory(factory); + + setWindowImage(mShell); + + setupSources(); + initializeSettings(); + + if (mUpdaterData.checkIfInitFailed()) { + return false; + } + + mUpdaterData.broadcastOnSdkLoaded(); + + // Tell the one page its the selected one + mPkgPage.performFirstLoad(); + + return true; + } + + /** + * Creates the icon of the window shell. + * + * @param shell The shell on which to put the icon + */ + private void setWindowImage(Shell shell) { + String imageName = "android_icon_16.png"; //$NON-NLS-1$ + if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) { + imageName = "android_icon_128.png"; //$NON-NLS-1$ + } + + if (mUpdaterData != null) { + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (imgFactory != null) { + shell.setImage(imgFactory.getImageByName(imageName)); + } + } + } + + /** + * Called by the main loop when the window has been disposed. + */ + private void dispose() { + mLogWindow.close(); + mUpdaterData.getSources().saveUserAddons(mUpdaterData.getSdkLog()); + } + + /** + * Callback called when the window shell is disposed. + */ + private void onAndroidSdkUpdaterDispose() { + if (mUpdaterData != null) { + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (imgFactory != null) { + imgFactory.dispose(); + } + } + } + + /** + * Used to initialize the sources. + */ + private void setupSources() { + mUpdaterData.setupDefaultSources(); + } + + /** + * Initializes settings. + * This must be called after addExtraPages(), which created a settings page. + * Iterate through all the pages to find the first (and supposedly unique) setting page, + * and use it to load and apply these settings. + */ + private void initializeSettings() { + mSettingsController = mUpdaterData.getSettingsController(); + mSettingsController.loadSettings(); + mSettingsController.applySettings(); + } + + private void onToggleLogWindow() { + // toggle visibility + if (!mButtonShowLog.isDisposed()) { + mLogWindow.setVisible(!mLogWindow.isVisible()); + mButtonShowLog.setState(mLogWindow.isVisible() ? 1 : 0); + } + } + + private void onStopSelected() { + // TODO + } + + private void onAvdManager() { + ITaskFactory oldFactory = mUpdaterData.getTaskFactory(); + + try { + AvdManagerWindowImpl1 win = new AvdManagerWindowImpl1( + mShell, + mUpdaterData, + AvdInvocationContext.DIALOG); + + win.open(); + } catch (Exception e) { + mUpdaterData.getSdkLog().error(e, "AVD Manager window error"); + } finally { + mUpdaterData.setTaskFactory(oldFactory); + } + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java index e53c8ae..57161b4 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java @@ -1,166 +1,166 @@ -/*
- * Copyright (C) 2011 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.prefs.AndroidLocation;
-
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Monitor;
-import org.eclipse.swt.widgets.Shell;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Properties;
-
-/**
- * Utility to save & restore the size and position on a window
- * using a common config file.
- */
-public class ShellSizeAndPos {
-
- private static final String SETTINGS_FILENAME = "androidwin.cfg"; //$NON-NLS-1$
- private static final String PX = "_px"; //$NON-NLS-1$
- private static final String PY = "_py"; //$NON-NLS-1$
- private static final String SX = "_sx"; //$NON-NLS-1$
- private static final String SY = "_sy"; //$NON-NLS-1$
-
- public static void loadSizeAndPos(Shell shell, String prefix) {
- Properties props = loadProperties();
-
- try {
- int px = Integer.parseInt(props.getProperty(prefix + PX));
- int py = Integer.parseInt(props.getProperty(prefix + PY));
- int sx = Integer.parseInt(props.getProperty(prefix + SX));
- int sy = Integer.parseInt(props.getProperty(prefix + SY));
-
- Point p1 = new Point(px, py);
- Point p2 = new Point(px + sx, py + sy);
- Rectangle r = new Rectangle(px, py, sy, sy);
-
- Monitor bestMatch = null;
- int bestSurface = -1;
- for (Monitor monitor : shell.getDisplay().getMonitors()) {
- Rectangle area = monitor.getClientArea();
- if (area.contains(p1) && area.contains(p2)) {
- // The shell is fully visible on this monitor. Just use that.
- bestMatch = monitor;
- bestSurface = Integer.MAX_VALUE;
- break;
- } else {
- // Find which monitor displays the largest surface of the window.
- // We'll use this one to center the window there, to make sure we're not
- // starting split between several monitors.
- Rectangle i = area.intersection(r);
- int surface = i.width * i.height;
- if (surface > bestSurface) {
- bestSurface = surface;
- bestMatch = monitor;
- }
- }
- }
-
- if (bestMatch != null && bestSurface != Integer.MAX_VALUE) {
- // Recenter the window on this monitor and make sure it fits
- Rectangle area = bestMatch.getClientArea();
-
- sx = Math.min(sx, area.width);
- sy = Math.min(sy, area.height);
- px = area.x + (area.width - sx) / 2;
- py = area.y + (area.height - sy) / 2;
- }
-
- shell.setLocation(px, py);
- shell.setSize(sx, sy);
-
- } catch ( Exception e) {
- // Ignore exception. We could typically get NPE from the getProperty
- // or NumberFormatException from parseInt calls. Either way, do
- // nothing if anything goes wrong.
- }
- }
-
- public static void saveSizeAndPos(Shell shell, String prefix) {
- Properties props = loadProperties();
-
- Point loc = shell.getLocation();
- Point size = shell.getSize();
-
- props.setProperty(prefix + PX, Integer.toString(loc.x));
- props.setProperty(prefix + PY, Integer.toString(loc.y));
- props.setProperty(prefix + SX, Integer.toString(size.x));
- props.setProperty(prefix + SY, Integer.toString(size.y));
-
- saveProperties(props);
- }
-
- /**
- * Load properties saved in {@link #SETTINGS_FILENAME}.
- * If the file does not exists or doesn't load properly, just return an
- * empty set of properties.
- */
- private static Properties loadProperties() {
- Properties props = new Properties();
- FileInputStream fis = null;
-
- try {
- String folder = AndroidLocation.getFolder();
- File f = new File(folder, SETTINGS_FILENAME);
- if (f.exists()) {
- fis = new FileInputStream(f);
-
- props.load(fis);
- }
- } catch (Exception e) {
- // Ignore
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
- }
-
- return props;
- }
-
- private static void saveProperties(Properties props) {
- FileOutputStream fos = null;
-
- try {
- String folder = AndroidLocation.getFolder();
- File f = new File(folder, SETTINGS_FILENAME);
- fos = new FileOutputStream(f);
-
- props.store(fos, "## Size and Pos for SDK Manager Windows"); //$NON-NLS-1$
-
- } catch (Exception e) {
- // ignore
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- }
- }
- }
- }
-}
+/* + * Copyright (C) 2011 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.prefs.AndroidLocation; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Monitor; +import org.eclipse.swt.widgets.Shell; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +/** + * Utility to save & restore the size and position on a window + * using a common config file. + */ +public class ShellSizeAndPos { + + private static final String SETTINGS_FILENAME = "androidwin.cfg"; //$NON-NLS-1$ + private static final String PX = "_px"; //$NON-NLS-1$ + private static final String PY = "_py"; //$NON-NLS-1$ + private static final String SX = "_sx"; //$NON-NLS-1$ + private static final String SY = "_sy"; //$NON-NLS-1$ + + public static void loadSizeAndPos(Shell shell, String prefix) { + Properties props = loadProperties(); + + try { + int px = Integer.parseInt(props.getProperty(prefix + PX)); + int py = Integer.parseInt(props.getProperty(prefix + PY)); + int sx = Integer.parseInt(props.getProperty(prefix + SX)); + int sy = Integer.parseInt(props.getProperty(prefix + SY)); + + Point p1 = new Point(px, py); + Point p2 = new Point(px + sx, py + sy); + Rectangle r = new Rectangle(px, py, sy, sy); + + Monitor bestMatch = null; + int bestSurface = -1; + for (Monitor monitor : shell.getDisplay().getMonitors()) { + Rectangle area = monitor.getClientArea(); + if (area.contains(p1) && area.contains(p2)) { + // The shell is fully visible on this monitor. Just use that. + bestMatch = monitor; + bestSurface = Integer.MAX_VALUE; + break; + } else { + // Find which monitor displays the largest surface of the window. + // We'll use this one to center the window there, to make sure we're not + // starting split between several monitors. + Rectangle i = area.intersection(r); + int surface = i.width * i.height; + if (surface > bestSurface) { + bestSurface = surface; + bestMatch = monitor; + } + } + } + + if (bestMatch != null && bestSurface != Integer.MAX_VALUE) { + // Recenter the window on this monitor and make sure it fits + Rectangle area = bestMatch.getClientArea(); + + sx = Math.min(sx, area.width); + sy = Math.min(sy, area.height); + px = area.x + (area.width - sx) / 2; + py = area.y + (area.height - sy) / 2; + } + + shell.setLocation(px, py); + shell.setSize(sx, sy); + + } catch ( Exception e) { + // Ignore exception. We could typically get NPE from the getProperty + // or NumberFormatException from parseInt calls. Either way, do + // nothing if anything goes wrong. + } + } + + public static void saveSizeAndPos(Shell shell, String prefix) { + Properties props = loadProperties(); + + Point loc = shell.getLocation(); + Point size = shell.getSize(); + + props.setProperty(prefix + PX, Integer.toString(loc.x)); + props.setProperty(prefix + PY, Integer.toString(loc.y)); + props.setProperty(prefix + SX, Integer.toString(size.x)); + props.setProperty(prefix + SY, Integer.toString(size.y)); + + saveProperties(props); + } + + /** + * Load properties saved in {@link #SETTINGS_FILENAME}. + * If the file does not exists or doesn't load properly, just return an + * empty set of properties. + */ + private static Properties loadProperties() { + Properties props = new Properties(); + FileInputStream fis = null; + + try { + String folder = AndroidLocation.getFolder(); + File f = new File(folder, SETTINGS_FILENAME); + if (f.exists()) { + fis = new FileInputStream(f); + + props.load(fis); + } + } catch (Exception e) { + // Ignore + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + } + } + } + + return props; + } + + private static void saveProperties(Properties props) { + FileOutputStream fos = null; + + try { + String folder = AndroidLocation.getFolder(); + File f = new File(folder, SETTINGS_FILENAME); + fos = new FileOutputStream(f); + + props.store(fos, "## Size and Pos for SDK Manager Windows"); //$NON-NLS-1$ + + } catch (Exception e) { + // ignore + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + } + } + } + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java index 2563a5f..d5404ae 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java @@ -1,108 +1,108 @@ -/*
- * 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.sdkuilib.internal.tasks;
-
-import com.android.sdklib.internal.repository.ITask;
-import com.android.sdklib.internal.repository.ITaskMonitor;
-
-import org.eclipse.swt.widgets.Shell;
-
-
-/**
- * An {@link ITaskMonitor} that displays a {@link ProgressTaskDialog}.
- */
-public final class ProgressTask extends TaskMonitorImpl {
-
- private final String mTitle;
- private final ProgressTaskDialog mDialog;
- private volatile boolean mAutoClose = true;
-
-
- /**
- * Creates a new {@link ProgressTask} with the given title.
- * This does NOT start the task. The caller must invoke {@link #start(ITask)}.
- */
- public ProgressTask(Shell parent, String title) {
- super(new ProgressTaskDialog(parent));
- mTitle = title;
- mDialog = (ProgressTaskDialog) getUiProvider();
- mDialog.setText(mTitle);
- }
-
- /**
- * Execute the given task in a separate thread (not the UI thread).
- * This blocks till the thread ends.
- * <p/>
- * The {@link ProgressTask} must not be reused after this call.
- */
- public void start(ITask task) {
- assert mDialog != null;
- mDialog.open(createTaskThread(mTitle, task));
- }
-
- /**
- * Changes the auto-close behavior of the dialog on task completion.
- *
- * @param autoClose True if the dialog should be closed automatically when the task
- * has completed.
- */
- public void setAutoClose(boolean autoClose) {
- if (autoClose != mAutoClose) {
- if (autoClose) {
- mDialog.setAutoCloseRequested();
- } else {
- mDialog.setManualCloseRequested();
- }
- mAutoClose = autoClose;
- }
- }
-
- /**
- * Creates a thread to run the task. The thread has not been started yet.
- * When the task completes, requests to close the dialog.
- *
- * @return A new thread that will run the task. The thread has not been started yet.
- */
- private Thread createTaskThread(String title, final ITask task) {
- if (task != null) {
- return new Thread(title) {
- @Override
- public void run() {
- task.run(ProgressTask.this);
- if (mAutoClose) {
- mDialog.setAutoCloseRequested();
- } else {
- mDialog.setManualCloseRequested();
- }
- }
- };
- }
- return null;
- }
-
- /**
- * {@inheritDoc}
- * <p/>
- * Sets the dialog to not auto-close since we want the user to see the error
- * (this is equivalent to calling {@code setAutoClose(false)}).
- */
- @Override
- public void logError(String format, Object...args) {
- setAutoClose(false);
- super.logError(format, args);
- }
-}
+/* + * 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.sdkuilib.internal.tasks; + +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskMonitor; + +import org.eclipse.swt.widgets.Shell; + + +/** + * An {@link ITaskMonitor} that displays a {@link ProgressTaskDialog}. + */ +public final class ProgressTask extends TaskMonitorImpl { + + private final String mTitle; + private final ProgressTaskDialog mDialog; + private volatile boolean mAutoClose = true; + + + /** + * Creates a new {@link ProgressTask} with the given title. + * This does NOT start the task. The caller must invoke {@link #start(ITask)}. + */ + public ProgressTask(Shell parent, String title) { + super(new ProgressTaskDialog(parent)); + mTitle = title; + mDialog = (ProgressTaskDialog) getUiProvider(); + mDialog.setText(mTitle); + } + + /** + * Execute the given task in a separate thread (not the UI thread). + * This blocks till the thread ends. + * <p/> + * The {@link ProgressTask} must not be reused after this call. + */ + public void start(ITask task) { + assert mDialog != null; + mDialog.open(createTaskThread(mTitle, task)); + } + + /** + * Changes the auto-close behavior of the dialog on task completion. + * + * @param autoClose True if the dialog should be closed automatically when the task + * has completed. + */ + public void setAutoClose(boolean autoClose) { + if (autoClose != mAutoClose) { + if (autoClose) { + mDialog.setAutoCloseRequested(); + } else { + mDialog.setManualCloseRequested(); + } + mAutoClose = autoClose; + } + } + + /** + * Creates a thread to run the task. The thread has not been started yet. + * When the task completes, requests to close the dialog. + * + * @return A new thread that will run the task. The thread has not been started yet. + */ + private Thread createTaskThread(String title, final ITask task) { + if (task != null) { + return new Thread(title) { + @Override + public void run() { + task.run(ProgressTask.this); + if (mAutoClose) { + mDialog.setAutoCloseRequested(); + } else { + mDialog.setManualCloseRequested(); + } + } + }; + } + return null; + } + + /** + * {@inheritDoc} + * <p/> + * Sets the dialog to not auto-close since we want the user to see the error + * (this is equivalent to calling {@code setAutoClose(false)}). + */ + @Override + public void logError(String format, Object...args) { + setAutoClose(false); + super.logError(format, args); + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java index c6ba8b7..8eee146 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java @@ -1,518 +1,518 @@ -/*
- * 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.sdkuilib.internal.tasks;
-
-import com.android.sdklib.SdkConstants;
-import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.internal.repository.UserCredentials;
-import com.android.sdkuilib.ui.AuthenticationDialog;
-import com.android.sdkuilib.ui.GridDialog;
-import com.android.util.Pair;
-
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-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.Dialog;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.ProgressBar;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-
-/**
- * Implements a {@link ProgressTaskDialog}, used by the {@link ProgressTask} class.
- * This separates the dialog UI from the task logic.
- *
- * Note: this does not implement the {@link ITaskMonitor} interface to avoid confusing
- * SWT Designer.
- */
-final class ProgressTaskDialog extends Dialog implements IProgressUiProvider {
-
- /**
- * Min Y location for dialog. Need to deal with the menu bar on mac os.
- */
- private final static int MIN_Y = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ?
- 20 : 0;
-
- private static enum CancelMode {
- /** Cancel button says "Cancel" and is enabled. Waiting for user to cancel. */
- ACTIVE,
- /** Cancel button has been clicked. Waiting for thread to finish. */
- CANCEL_PENDING,
- /** Close pending. Close button clicked or thread finished but there were some
- * messages so the user needs to manually close. */
- CLOSE_MANUAL,
- /** Close button clicked or thread finished. The window will automatically close. */
- CLOSE_AUTO
- }
-
- /** The current mode of operation of the dialog. */
- private CancelMode mCancelMode = CancelMode.ACTIVE;
-
- /** Last dialog size for this session. */
- private static Point sLastSize;
-
-
- // UI fields
- private Shell mDialogShell;
- private Composite mRootComposite;
- private Label mLabel;
- private ProgressBar mProgressBar;
- private Button mCancelButton;
- private Text mResultText;
-
-
- /**
- * Create the dialog.
- * @param parent Parent container
- */
- public ProgressTaskDialog(Shell parent) {
- super(parent, SWT.APPLICATION_MODAL);
- }
-
- /**
- * Open the dialog and blocks till it gets closed
- * @param taskThread The thread to run the task. Cannot be null.
- */
- public void open(Thread taskThread) {
- createContents();
- positionShell(); //$hide$ (hide from SWT designer)
- mDialogShell.open();
- mDialogShell.layout();
-
- startThread(taskThread); //$hide$ (hide from SWT designer)
-
- Display display = getParent().getDisplay();
- while (!mDialogShell.isDisposed() && mCancelMode != CancelMode.CLOSE_AUTO) {
- if (!display.readAndDispatch()) {
- display.sleep();
- }
- }
-
- setCancelRequested(); //$hide$ (hide from SWT designer)
-
- if (!mDialogShell.isDisposed()) {
- sLastSize = mDialogShell.getSize();
- mDialogShell.close();
- }
- }
-
- /**
- * Create contents of the dialog.
- */
- private void createContents() {
- mDialogShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE);
- mDialogShell.addShellListener(new ShellAdapter() {
- @Override
- public void shellClosed(ShellEvent e) {
- onShellClosed(e);
- }
- });
- mDialogShell.setLayout(new GridLayout(1, false));
- mDialogShell.setSize(450, 300);
- mDialogShell.setText(getText());
-
- mRootComposite = new Composite(mDialogShell, SWT.NONE);
- mRootComposite.setLayout(new GridLayout(2, false));
- mRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
-
- mLabel = new Label(mRootComposite, SWT.NONE);
- mLabel.setText("Task");
- mLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
-
- mProgressBar = new ProgressBar(mRootComposite, SWT.NONE);
- mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
- mCancelButton = new Button(mRootComposite, SWT.NONE);
- mCancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- mCancelButton.setText("Cancel");
-
- mCancelButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onCancelSelected(); //$hide$
- }
- });
-
- mResultText = new Text(mRootComposite,
- SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |
- SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
- mResultText.setEditable(true);
- mResultText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
- }
-
- // -- End of UI, Start of internal logic ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- @Override
- public boolean isCancelRequested() {
- return mCancelMode != CancelMode.ACTIVE;
- }
-
- /**
- * Sets the mode to cancel pending.
- * The first time this grays the cancel button, to let the user know that the
- * cancel operation is pending.
- */
- public void setCancelRequested() {
- if (!mDialogShell.isDisposed()) {
- // The dialog is not disposed, make sure to run all this in the UI thread
- // and lock on the cancel button mode.
- mDialogShell.getDisplay().syncExec(new Runnable() {
-
- @Override
- public void run() {
- synchronized (mCancelMode) {
- if (mCancelMode == CancelMode.ACTIVE) {
- mCancelMode = CancelMode.CANCEL_PENDING;
-
- if (!mCancelButton.isDisposed()) {
- mCancelButton.setEnabled(false);
- }
- }
- }
- }
- });
- } else {
- // The dialog is disposed. Just set the boolean. We shouldn't be here.
- if (mCancelMode == CancelMode.ACTIVE) {
- mCancelMode = CancelMode.CANCEL_PENDING;
- }
- }
- }
-
- /**
- * Sets the mode to close manual.
- * The first time, this also ungrays the pause button and converts it to a close button.
- */
- public void setManualCloseRequested() {
- if (!mDialogShell.isDisposed()) {
- // The dialog is not disposed, make sure to run all this in the UI thread
- // and lock on the cancel button mode.
- mDialogShell.getDisplay().syncExec(new Runnable() {
-
- @Override
- public void run() {
- synchronized (mCancelMode) {
- if (mCancelMode != CancelMode.CLOSE_MANUAL &&
- mCancelMode != CancelMode.CLOSE_AUTO) {
- mCancelMode = CancelMode.CLOSE_MANUAL;
-
- if (!mCancelButton.isDisposed()) {
- mCancelButton.setEnabled(true);
- mCancelButton.setText("Close");
- }
- }
- }
- }
- });
- } else {
- // The dialog is disposed. Just set the booleans. We shouldn't be here.
- if (mCancelMode != CancelMode.CLOSE_MANUAL &&
- mCancelMode != CancelMode.CLOSE_AUTO) {
- mCancelMode = CancelMode.CLOSE_MANUAL;
- }
- }
- }
-
- /**
- * Sets the mode to close auto.
- * The main loop will just exit and close the shell at the first opportunity.
- */
- public void setAutoCloseRequested() {
- synchronized (mCancelMode) {
- if (mCancelMode != CancelMode.CLOSE_AUTO) {
- mCancelMode = CancelMode.CLOSE_AUTO;
- }
- }
- }
-
- /**
- * Callback invoked when the cancel button is selected.
- * When in closing mode, this simply closes the shell. Otherwise triggers a cancel.
- */
- private void onCancelSelected() {
- if (mCancelMode == CancelMode.CLOSE_MANUAL) {
- setAutoCloseRequested();
- } else {
- setCancelRequested();
- }
- }
-
- /**
- * Callback invoked when the shell is closed either by clicking the close button
- * on by calling shell.close().
- * This does the same thing as clicking the cancel/close button unless the mode is
- * to auto close in which case we should do nothing to let the shell close normally.
- */
- private void onShellClosed(ShellEvent e) {
- if (mCancelMode != CancelMode.CLOSE_AUTO) {
- e.doit = false; // don't close directly
- onCancelSelected();
- }
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setDescription(final String description) {
- mDialogShell.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mLabel.isDisposed()) {
- mLabel.setText(description);
- }
- }
- });
- }
-
- /**
- * Adds to the log in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void log(final String info) {
- if (!mDialogShell.isDisposed()) {
- mDialogShell.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mResultText.isDisposed()) {
- mResultText.setVisible(true);
- String lastText = mResultText.getText();
- if (lastText != null &&
- lastText.length() > 0 &&
- !lastText.endsWith("\n") && //$NON-NLS-1$
- !info.startsWith("\n")) { //$NON-NLS-1$
- mResultText.append("\n"); //$NON-NLS-1$
- }
- mResultText.append(info);
- }
- }
- });
- }
- }
-
- @Override
- public void logError(String info) {
- log(info);
- }
-
- @Override
- public void logVerbose(String info) {
- log(info);
- }
-
- /**
- * Sets the max value of the progress bar.
- * This method can be invoked from a non-UI thread.
- *
- * @see ProgressBar#setMaximum(int)
- */
- @Override
- public void setProgressMax(final int max) {
- if (!mDialogShell.isDisposed()) {
- mDialogShell.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mProgressBar.isDisposed()) {
- mProgressBar.setMaximum(max);
- }
- }
- });
- }
- }
-
- /**
- * Sets the current value of the progress bar.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setProgress(final int value) {
- if (!mDialogShell.isDisposed()) {
- mDialogShell.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mProgressBar.isDisposed()) {
- mProgressBar.setSelection(value);
- }
- }
- });
- }
- }
-
- /**
- * Returns the current value of the progress bar,
- * between 0 and up to {@link #setProgressMax(int)} - 1.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public int getProgress() {
- final int[] result = new int[] { 0 };
-
- if (!mDialogShell.isDisposed()) {
- mDialogShell.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mProgressBar.isDisposed()) {
- result[0] = mProgressBar.getSelection();
- }
- }
- });
- }
-
- return result[0];
- }
-
- /**
- * Display a yes/no question dialog box.
- *
- * This implementation allow this to be called from any thread, it
- * makes sure the dialog is opened synchronously in the ui thread.
- *
- * @param title The title of the dialog box
- * @param message The error message
- * @return true if YES was clicked.
- */
- @Override
- public boolean displayPrompt(final String title, final String message) {
- Display display = mDialogShell.getDisplay();
-
- // we need to ask the user what he wants to do.
- final boolean[] result = new boolean[] { false };
- display.syncExec(new Runnable() {
- @Override
- public void run() {
- result[0] = MessageDialog.openQuestion(mDialogShell, title, message);
- }
- });
- return result[0];
- }
-
- /**
- * This method opens a pop-up window which requests for User Login and
- * password.
- *
- * @param title The title of the window.
- * @param message The message to displayed in the login/password window.
- * @return Returns a {@link Pair} holding the entered login and password.
- * The information must always be in the following order:
- * Login,Password. So in order to retrieve the <b>login</b> callers
- * should retrieve the first element, and the second value for the
- * <b>password</b>.
- * If operation is <b>canceled</b> by user the return value must be <b>null</b>.
- * @see ITaskMonitor#displayLoginCredentialsPrompt(String, String)
- */
- @Override
- public UserCredentials displayLoginCredentialsPrompt(
- final String title, final String message) {
- Display display = mDialogShell.getDisplay();
-
- // open dialog and request login and password
- GetUserCredentialsTask task = new GetUserCredentialsTask(mDialogShell, title, message);
- display.syncExec(task);
-
- return new UserCredentials(task.userName, task.password, task.workstation, task.domain);
- }
-
- private static class GetUserCredentialsTask implements Runnable {
- public String userName = null;
- public String password = null;
- public String workstation = null;
- public String domain = null;
-
- private Shell mShell;
- private String mTitle;
- private String mMessage;
-
- public GetUserCredentialsTask(Shell shell, String title, String message) {
- mShell = shell;
- mTitle = title;
- mMessage = message;
- }
-
- @Override
- public void run() {
- AuthenticationDialog authenticationDialog = new AuthenticationDialog(mShell,
- mTitle, mMessage);
- int dlgResult= authenticationDialog.open();
- if(dlgResult == GridDialog.OK) {
- userName = authenticationDialog.getLogin();
- password = authenticationDialog.getPassword();
- workstation = authenticationDialog.getWorkstation();
- domain = authenticationDialog.getDomain();
- }
- }
- }
-
- /**
- * Starts the thread that runs the task.
- * This is deferred till the UI is created.
- */
- private void startThread(Thread taskThread) {
- if (taskThread != null) {
- taskThread.start();
- }
- }
-
- /**
- * Centers the dialog in its parent shell.
- */
- private void positionShell() {
- // Centers the dialog in its parent shell
- Shell child = mDialogShell;
- Shell parent = getParent();
- if (child != null && parent != null) {
-
- // get the parent client area with a location relative to the display
- Rectangle parentArea = parent.getClientArea();
- Point parentLoc = parent.getLocation();
- int px = parentLoc.x;
- int py = parentLoc.y;
- int pw = parentArea.width;
- int ph = parentArea.height;
-
- // Reuse the last size if there's one, otherwise use the default
- Point childSize = sLastSize != null ? sLastSize : child.getSize();
- int cw = childSize.x;
- int ch = childSize.y;
-
- int x = px + (pw - cw) / 2;
- if (x < 0) x = 0;
-
- int y = py + (ph - ch) / 2;
- if (y < MIN_Y) y = MIN_Y;
-
- child.setLocation(x, y);
- child.setSize(cw, ch);
- }
- }
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/* + * 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.sdkuilib.internal.tasks; + +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdklib.internal.repository.UserCredentials; +import com.android.sdkuilib.ui.AuthenticationDialog; +import com.android.sdkuilib.ui.GridDialog; +import com.android.util.Pair; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +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.Dialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + + +/** + * Implements a {@link ProgressTaskDialog}, used by the {@link ProgressTask} class. + * This separates the dialog UI from the task logic. + * + * Note: this does not implement the {@link ITaskMonitor} interface to avoid confusing + * SWT Designer. + */ +final class ProgressTaskDialog extends Dialog implements IProgressUiProvider { + + /** + * Min Y location for dialog. Need to deal with the menu bar on mac os. + */ + private final static int MIN_Y = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ? + 20 : 0; + + private static enum CancelMode { + /** Cancel button says "Cancel" and is enabled. Waiting for user to cancel. */ + ACTIVE, + /** Cancel button has been clicked. Waiting for thread to finish. */ + CANCEL_PENDING, + /** Close pending. Close button clicked or thread finished but there were some + * messages so the user needs to manually close. */ + CLOSE_MANUAL, + /** Close button clicked or thread finished. The window will automatically close. */ + CLOSE_AUTO + } + + /** The current mode of operation of the dialog. */ + private CancelMode mCancelMode = CancelMode.ACTIVE; + + /** Last dialog size for this session. */ + private static Point sLastSize; + + + // UI fields + private Shell mDialogShell; + private Composite mRootComposite; + private Label mLabel; + private ProgressBar mProgressBar; + private Button mCancelButton; + private Text mResultText; + + + /** + * Create the dialog. + * @param parent Parent container + */ + public ProgressTaskDialog(Shell parent) { + super(parent, SWT.APPLICATION_MODAL); + } + + /** + * Open the dialog and blocks till it gets closed + * @param taskThread The thread to run the task. Cannot be null. + */ + public void open(Thread taskThread) { + createContents(); + positionShell(); //$hide$ (hide from SWT designer) + mDialogShell.open(); + mDialogShell.layout(); + + startThread(taskThread); //$hide$ (hide from SWT designer) + + Display display = getParent().getDisplay(); + while (!mDialogShell.isDisposed() && mCancelMode != CancelMode.CLOSE_AUTO) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + + setCancelRequested(); //$hide$ (hide from SWT designer) + + if (!mDialogShell.isDisposed()) { + sLastSize = mDialogShell.getSize(); + mDialogShell.close(); + } + } + + /** + * Create contents of the dialog. + */ + private void createContents() { + mDialogShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE); + mDialogShell.addShellListener(new ShellAdapter() { + @Override + public void shellClosed(ShellEvent e) { + onShellClosed(e); + } + }); + mDialogShell.setLayout(new GridLayout(1, false)); + mDialogShell.setSize(450, 300); + mDialogShell.setText(getText()); + + mRootComposite = new Composite(mDialogShell, SWT.NONE); + mRootComposite.setLayout(new GridLayout(2, false)); + mRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + mLabel = new Label(mRootComposite, SWT.NONE); + mLabel.setText("Task"); + mLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + mProgressBar = new ProgressBar(mRootComposite, SWT.NONE); + mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mCancelButton = new Button(mRootComposite, SWT.NONE); + mCancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mCancelButton.setText("Cancel"); + + mCancelButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onCancelSelected(); //$hide$ + } + }); + + mResultText = new Text(mRootComposite, + SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | + SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI); + mResultText.setEditable(true); + mResultText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + } + + // -- End of UI, Start of internal logic ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + @Override + public boolean isCancelRequested() { + return mCancelMode != CancelMode.ACTIVE; + } + + /** + * Sets the mode to cancel pending. + * The first time this grays the cancel button, to let the user know that the + * cancel operation is pending. + */ + public void setCancelRequested() { + if (!mDialogShell.isDisposed()) { + // The dialog is not disposed, make sure to run all this in the UI thread + // and lock on the cancel button mode. + mDialogShell.getDisplay().syncExec(new Runnable() { + + @Override + public void run() { + synchronized (mCancelMode) { + if (mCancelMode == CancelMode.ACTIVE) { + mCancelMode = CancelMode.CANCEL_PENDING; + + if (!mCancelButton.isDisposed()) { + mCancelButton.setEnabled(false); + } + } + } + } + }); + } else { + // The dialog is disposed. Just set the boolean. We shouldn't be here. + if (mCancelMode == CancelMode.ACTIVE) { + mCancelMode = CancelMode.CANCEL_PENDING; + } + } + } + + /** + * Sets the mode to close manual. + * The first time, this also ungrays the pause button and converts it to a close button. + */ + public void setManualCloseRequested() { + if (!mDialogShell.isDisposed()) { + // The dialog is not disposed, make sure to run all this in the UI thread + // and lock on the cancel button mode. + mDialogShell.getDisplay().syncExec(new Runnable() { + + @Override + public void run() { + synchronized (mCancelMode) { + if (mCancelMode != CancelMode.CLOSE_MANUAL && + mCancelMode != CancelMode.CLOSE_AUTO) { + mCancelMode = CancelMode.CLOSE_MANUAL; + + if (!mCancelButton.isDisposed()) { + mCancelButton.setEnabled(true); + mCancelButton.setText("Close"); + } + } + } + } + }); + } else { + // The dialog is disposed. Just set the booleans. We shouldn't be here. + if (mCancelMode != CancelMode.CLOSE_MANUAL && + mCancelMode != CancelMode.CLOSE_AUTO) { + mCancelMode = CancelMode.CLOSE_MANUAL; + } + } + } + + /** + * Sets the mode to close auto. + * The main loop will just exit and close the shell at the first opportunity. + */ + public void setAutoCloseRequested() { + synchronized (mCancelMode) { + if (mCancelMode != CancelMode.CLOSE_AUTO) { + mCancelMode = CancelMode.CLOSE_AUTO; + } + } + } + + /** + * Callback invoked when the cancel button is selected. + * When in closing mode, this simply closes the shell. Otherwise triggers a cancel. + */ + private void onCancelSelected() { + if (mCancelMode == CancelMode.CLOSE_MANUAL) { + setAutoCloseRequested(); + } else { + setCancelRequested(); + } + } + + /** + * Callback invoked when the shell is closed either by clicking the close button + * on by calling shell.close(). + * This does the same thing as clicking the cancel/close button unless the mode is + * to auto close in which case we should do nothing to let the shell close normally. + */ + private void onShellClosed(ShellEvent e) { + if (mCancelMode != CancelMode.CLOSE_AUTO) { + e.doit = false; // don't close directly + onCancelSelected(); + } + } + + /** + * Sets the description in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setDescription(final String description) { + mDialogShell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mLabel.isDisposed()) { + mLabel.setText(description); + } + } + }); + } + + /** + * Adds to the log in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + @Override + public void log(final String info) { + if (!mDialogShell.isDisposed()) { + mDialogShell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mResultText.isDisposed()) { + mResultText.setVisible(true); + String lastText = mResultText.getText(); + if (lastText != null && + lastText.length() > 0 && + !lastText.endsWith("\n") && //$NON-NLS-1$ + !info.startsWith("\n")) { //$NON-NLS-1$ + mResultText.append("\n"); //$NON-NLS-1$ + } + mResultText.append(info); + } + } + }); + } + } + + @Override + public void logError(String info) { + log(info); + } + + @Override + public void logVerbose(String info) { + log(info); + } + + /** + * Sets the max value of the progress bar. + * This method can be invoked from a non-UI thread. + * + * @see ProgressBar#setMaximum(int) + */ + @Override + public void setProgressMax(final int max) { + if (!mDialogShell.isDisposed()) { + mDialogShell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mProgressBar.isDisposed()) { + mProgressBar.setMaximum(max); + } + } + }); + } + } + + /** + * Sets the current value of the progress bar. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setProgress(final int value) { + if (!mDialogShell.isDisposed()) { + mDialogShell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mProgressBar.isDisposed()) { + mProgressBar.setSelection(value); + } + } + }); + } + } + + /** + * Returns the current value of the progress bar, + * between 0 and up to {@link #setProgressMax(int)} - 1. + * This method can be invoked from a non-UI thread. + */ + @Override + public int getProgress() { + final int[] result = new int[] { 0 }; + + if (!mDialogShell.isDisposed()) { + mDialogShell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mProgressBar.isDisposed()) { + result[0] = mProgressBar.getSelection(); + } + } + }); + } + + return result[0]; + } + + /** + * Display a yes/no question dialog box. + * + * This implementation allow this to be called from any thread, it + * makes sure the dialog is opened synchronously in the ui thread. + * + * @param title The title of the dialog box + * @param message The error message + * @return true if YES was clicked. + */ + @Override + public boolean displayPrompt(final String title, final String message) { + Display display = mDialogShell.getDisplay(); + + // we need to ask the user what he wants to do. + final boolean[] result = new boolean[] { false }; + display.syncExec(new Runnable() { + @Override + public void run() { + result[0] = MessageDialog.openQuestion(mDialogShell, title, message); + } + }); + return result[0]; + } + + /** + * This method opens a pop-up window which requests for User Login and + * password. + * + * @param title The title of the window. + * @param message The message to displayed in the login/password window. + * @return Returns a {@link Pair} holding the entered login and password. + * The information must always be in the following order: + * Login,Password. So in order to retrieve the <b>login</b> callers + * should retrieve the first element, and the second value for the + * <b>password</b>. + * If operation is <b>canceled</b> by user the return value must be <b>null</b>. + * @see ITaskMonitor#displayLoginCredentialsPrompt(String, String) + */ + @Override + public UserCredentials displayLoginCredentialsPrompt( + final String title, final String message) { + Display display = mDialogShell.getDisplay(); + + // open dialog and request login and password + GetUserCredentialsTask task = new GetUserCredentialsTask(mDialogShell, title, message); + display.syncExec(task); + + return new UserCredentials(task.userName, task.password, task.workstation, task.domain); + } + + private static class GetUserCredentialsTask implements Runnable { + public String userName = null; + public String password = null; + public String workstation = null; + public String domain = null; + + private Shell mShell; + private String mTitle; + private String mMessage; + + public GetUserCredentialsTask(Shell shell, String title, String message) { + mShell = shell; + mTitle = title; + mMessage = message; + } + + @Override + public void run() { + AuthenticationDialog authenticationDialog = new AuthenticationDialog(mShell, + mTitle, mMessage); + int dlgResult= authenticationDialog.open(); + if(dlgResult == GridDialog.OK) { + userName = authenticationDialog.getLogin(); + password = authenticationDialog.getPassword(); + workstation = authenticationDialog.getWorkstation(); + domain = authenticationDialog.getDomain(); + } + } + } + + /** + * Starts the thread that runs the task. + * This is deferred till the UI is created. + */ + private void startThread(Thread taskThread) { + if (taskThread != null) { + taskThread.start(); + } + } + + /** + * Centers the dialog in its parent shell. + */ + private void positionShell() { + // Centers the dialog in its parent shell + Shell child = mDialogShell; + Shell parent = getParent(); + if (child != null && parent != null) { + + // get the parent client area with a location relative to the display + Rectangle parentArea = parent.getClientArea(); + Point parentLoc = parent.getLocation(); + int px = parentLoc.x; + int py = parentLoc.y; + int pw = parentArea.width; + int ph = parentArea.height; + + // Reuse the last size if there's one, otherwise use the default + Point childSize = sLastSize != null ? sLastSize : child.getSize(); + int cw = childSize.x; + int ch = childSize.y; + + int x = px + (pw - cw) / 2; + if (x < 0) x = 0; + + int y = py + (ph - ch) / 2; + if (y < MIN_Y) y = MIN_Y; + + child.setLocation(x, y); + child.setSize(cw, ch); + } + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskFactory.java index bd2cc14..17cba7a 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskFactory.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskFactory.java @@ -1,67 +1,67 @@ -/*
- * 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.sdkuilib.internal.tasks;
-
-import com.android.sdklib.internal.repository.ITask;
-import com.android.sdklib.internal.repository.ITaskFactory;
-import com.android.sdklib.internal.repository.ITaskMonitor;
-
-import org.eclipse.swt.widgets.Shell;
-
-/**
- * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog
- * for each new task.
- */
-public final class ProgressTaskFactory implements ITaskFactory {
-
- private final Shell mShell;
-
- public ProgressTaskFactory(Shell shell) {
- mShell = shell;
- }
-
- @Override
- public void start(String title, ITask task) {
- start(title, null /*parentMonitor*/, task);
- }
-
- @Override
- public void start(String title, ITaskMonitor parentMonitor, ITask task) {
-
- if (parentMonitor == null) {
- ProgressTask p = new ProgressTask(mShell, title);
- p.start(task);
- } else {
- // Use all the reminder of the parent monitor.
- if (parentMonitor.getProgressMax() == 0) {
- parentMonitor.setProgressMax(1);
- }
-
- ITaskMonitor sub = parentMonitor.createSubMonitor(
- parentMonitor.getProgressMax() - parentMonitor.getProgress());
- try {
- task.run(sub);
- } finally {
- int delta =
- sub.getProgressMax() - sub.getProgress();
- if (delta > 0) {
- sub.incProgress(delta);
- }
- }
- }
- }
-}
+/* + * 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.sdkuilib.internal.tasks; + +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskFactory; +import com.android.sdklib.internal.repository.ITaskMonitor; + +import org.eclipse.swt.widgets.Shell; + +/** + * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog + * for each new task. + */ +public final class ProgressTaskFactory implements ITaskFactory { + + private final Shell mShell; + + public ProgressTaskFactory(Shell shell) { + mShell = shell; + } + + @Override + public void start(String title, ITask task) { + start(title, null /*parentMonitor*/, task); + } + + @Override + public void start(String title, ITaskMonitor parentMonitor, ITask task) { + + if (parentMonitor == null) { + ProgressTask p = new ProgressTask(mShell, title); + p.start(task); + } else { + // Use all the reminder of the parent monitor. + if (parentMonitor.getProgressMax() == 0) { + parentMonitor.setProgressMax(1); + } + + ITaskMonitor sub = parentMonitor.createSubMonitor( + parentMonitor.getProgressMax() - parentMonitor.getProgress()); + try { + task.run(sub); + } finally { + int delta = + sub.getProgressMax() - sub.getProgress(); + if (delta > 0) { + sub.incProgress(delta); + } + } + } + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java index 3448852..4691ec6 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java @@ -1,375 +1,375 @@ -/*
- * Copyright (C) 2011 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.tasks;
-
-import com.android.sdklib.internal.repository.ITask;
-import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.internal.repository.UserCredentials;
-import com.android.sdkuilib.ui.AuthenticationDialog;
-import com.android.sdkuilib.ui.GridDialog;
-
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.ProgressBar;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Widget;
-
-
-/**
- * Implements a "view" that uses an existing progress bar, status button and
- * status text to display a {@link ITaskMonitor}.
- */
-public final class ProgressView implements IProgressUiProvider {
-
- private static enum State {
- /** View created but there's no task running. Next state can only be ACTIVE. */
- IDLE,
- /** A task is currently running. Next state is either STOP_PENDING or IDLE. */
- ACTIVE,
- /** Stop button has been clicked. Waiting for thread to finish. Next state is IDLE. */
- STOP_PENDING,
- }
-
- /** The current mode of operation of the dialog. */
- private State mState = State.IDLE;
-
-
-
- // UI fields
- private final Label mLabel;
- private final Control mStopButton;
- private final ProgressBar mProgressBar;
-
- /** Logger object. Cannot not be null. */
- private final ILogUiProvider mLog;
-
- /**
- * Creates a new {@link ProgressView} object, a simple "holder" for the various
- * widgets used to display and update a progress + status bar.
- *
- * @param label The label to display titles of status updates (e.g. task titles and
- * calls to {@link #setDescription(String)}.) Must not be null.
- * @param progressBar The progress bar to update during a task. Must not be null.
- * @param stopButton The stop button. It will be disabled when there's no task that can
- * be interrupted. A selection listener will be attached to it. Optional. Can be null.
- * @param log A <em>mandatory</em> logger object that will be used to report all the log.
- * Must not be null.
- */
- public ProgressView(
- Label label,
- ProgressBar progressBar,
- Control stopButton,
- ILogUiProvider log) {
- mLabel = label;
- mProgressBar = progressBar;
- mLog = log;
- mProgressBar.setEnabled(false);
-
- mStopButton = stopButton;
- if (mStopButton != null) {
- mStopButton.addListener(SWT.Selection, new Listener() {
- @Override
- public void handleEvent(Event event) {
- if (mState == State.ACTIVE) {
- changeState(State.STOP_PENDING);
- }
- }
- });
- }
- }
-
- /**
- * Starts the task and block till it's either finished or canceled.
- * This can be called from a non-UI thread safely.
- * <p/>
- * When a task is started from within a monitor, it reuses the thread
- * from the parent. Otherwise it starts a new thread and runs it own
- * UI loop. This means the task can perform UI operations using
- * {@link Display#asyncExec(Runnable)}.
- * <p/>
- * In either case, the method only returns when the task has finished.
- */
- public void startTask(
- final String title,
- final ITaskMonitor parentMonitor,
- final ITask task) {
- if (task != null) {
- try {
- if (parentMonitor == null && !mProgressBar.isDisposed()) {
- mLabel.setText(title);
- mProgressBar.setSelection(0);
- mProgressBar.setEnabled(true);
- changeState(ProgressView.State.ACTIVE);
- }
-
- Runnable r = new Runnable() {
- @Override
- public void run() {
- if (parentMonitor == null) {
- task.run(new TaskMonitorImpl(ProgressView.this));
-
- } else {
- // Use all the reminder of the parent monitor.
- if (parentMonitor.getProgressMax() == 0) {
- parentMonitor.setProgressMax(1);
- }
- ITaskMonitor sub = parentMonitor.createSubMonitor(
- parentMonitor.getProgressMax() - parentMonitor.getProgress());
- try {
- task.run(sub);
- } finally {
- int delta =
- sub.getProgressMax() - sub.getProgress();
- if (delta > 0) {
- sub.incProgress(delta);
- }
- }
- }
- }
- };
-
- // If for some reason the UI has been disposed, just abort the thread.
- if (mProgressBar.isDisposed()) {
- return;
- }
-
- if (TaskMonitorImpl.isTaskMonitorImpl(parentMonitor)) {
- // If there's a parent monitor and it's our own class, we know this parent
- // is already running a thread and the base one is running an event loop.
- // We should thus not run a second event loop and we can process the
- // runnable right here instead of spawning a thread inside the thread.
- r.run();
-
- } else {
- // No parent monitor. This is the first one so we need a thread and
- // we need to process UI events.
-
- final Thread t = new Thread(r, title);
- t.start();
-
- // Process the app's event loop whilst we wait for the thread to finish
- while (!mProgressBar.isDisposed() && t.isAlive()) {
- Display display = mProgressBar.getDisplay();
- if (!mProgressBar.isDisposed() && !display.readAndDispatch()) {
- display.sleep();
- }
- }
- }
- } catch (Exception e) {
- // TODO log
-
- } finally {
- if (parentMonitor == null && !mProgressBar.isDisposed()) {
- changeState(ProgressView.State.IDLE);
- mProgressBar.setSelection(0);
- mProgressBar.setEnabled(false);
- }
- }
- }
- }
-
- private void syncExec(final Widget widget, final Runnable runnable) {
- if (widget != null && !widget.isDisposed()) {
- widget.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- // Check again whether the widget got disposed between the time where
- // we requested the syncExec and the time it actually happened.
- if (!widget.isDisposed()) {
- runnable.run();
- }
- }
- });
- }
- }
-
- private void changeState(State state) {
- if (mState != null ) {
- mState = state;
- }
-
- syncExec(mStopButton, new Runnable() {
- @Override
- public void run() {
- mStopButton.setEnabled(mState == State.ACTIVE);
- }
- });
-
- }
-
- // --- Implementation of ITaskUiProvider ---
-
- @Override
- public boolean isCancelRequested() {
- return mState != State.ACTIVE;
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setDescription(final String description) {
- syncExec(mLabel, new Runnable() {
- @Override
- public void run() {
- mLabel.setText(description);
- }
- });
-
- mLog.setDescription(description);
- }
-
- /**
- * Logs a "normal" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void log(String log) {
- mLog.log(log);
- }
-
- /**
- * Logs an "error" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logError(String log) {
- mLog.logError(log);
- }
-
- /**
- * Logs a "verbose" information line, that is extra details which are typically
- * not that useful for the end-user and might be hidden until explicitly shown.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logVerbose(String log) {
- mLog.logVerbose(log);
- }
-
- /**
- * Sets the max value of the progress bar.
- * This method can be invoked from a non-UI thread.
- *
- * @see ProgressBar#setMaximum(int)
- */
- @Override
- public void setProgressMax(final int max) {
- syncExec(mProgressBar, new Runnable() {
- @Override
- public void run() {
- mProgressBar.setMaximum(max);
- }
- });
- }
-
- /**
- * Sets the current value of the progress bar.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setProgress(final int value) {
- syncExec(mProgressBar, new Runnable() {
- @Override
- public void run() {
- mProgressBar.setSelection(value);
- }
- });
- }
-
- /**
- * Returns the current value of the progress bar,
- * between 0 and up to {@link #setProgressMax(int)} - 1.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public int getProgress() {
- final int[] result = new int[] { 0 };
-
- if (!mProgressBar.isDisposed()) {
- mProgressBar.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- if (!mProgressBar.isDisposed()) {
- result[0] = mProgressBar.getSelection();
- }
- }
- });
- }
-
- return result[0];
- }
-
- @Override
- public boolean displayPrompt(final String title, final String message) {
- final boolean[] result = new boolean[] { false };
-
- syncExec(mProgressBar, new Runnable() {
- @Override
- public void run() {
- Shell shell = mProgressBar.getShell();
- result[0] = MessageDialog.openQuestion(shell, title, message);
- }
- });
-
- return result[0];
+/* + * Copyright (C) 2011 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.tasks; + +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdklib.internal.repository.UserCredentials; +import com.android.sdkuilib.ui.AuthenticationDialog; +import com.android.sdkuilib.ui.GridDialog; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Widget; + + +/** + * Implements a "view" that uses an existing progress bar, status button and + * status text to display a {@link ITaskMonitor}. + */ +public final class ProgressView implements IProgressUiProvider { + + private static enum State { + /** View created but there's no task running. Next state can only be ACTIVE. */ + IDLE, + /** A task is currently running. Next state is either STOP_PENDING or IDLE. */ + ACTIVE, + /** Stop button has been clicked. Waiting for thread to finish. Next state is IDLE. */ + STOP_PENDING, } -
- /**
- * This method opens a pop-up window which requests for User Credentials.
- *
- * @param title The title of the window.
- * @param message The message to displayed in the login/password window.
- * @return Returns user provided credentials.
- * If operation is <b>canceled</b> by user the return value must be <b>null</b>.
- * @see ITaskMonitor#displayLoginCredentialsPrompt(String, String)
- */
- @Override
- public UserCredentials
- displayLoginCredentialsPrompt(final String title, final String message) {
- final String[] resultArray = new String[] {"", "", "", ""};
- // open dialog and request login and password
- syncExec(mProgressBar, new Runnable() {
- @Override
- public void run() {
- Shell shell = mProgressBar.getShell();
- AuthenticationDialog authenticationDialog = new AuthenticationDialog(shell,
- title,
- message);
- int dlgResult = authenticationDialog.open();
- if (dlgResult == GridDialog.OK) {
- resultArray[0] = authenticationDialog.getLogin();
- resultArray[1] = authenticationDialog.getPassword();
- resultArray[2] = authenticationDialog.getWorkstation();
- resultArray[3] = authenticationDialog.getDomain();
- }
- }
- });
-
- return new UserCredentials(resultArray[0],
- resultArray[1],
- resultArray[2],
- resultArray[3]);
- }
-}
-
+ + /** The current mode of operation of the dialog. */ + private State mState = State.IDLE; + + + + // UI fields + private final Label mLabel; + private final Control mStopButton; + private final ProgressBar mProgressBar; + + /** Logger object. Cannot not be null. */ + private final ILogUiProvider mLog; + + /** + * Creates a new {@link ProgressView} object, a simple "holder" for the various + * widgets used to display and update a progress + status bar. + * + * @param label The label to display titles of status updates (e.g. task titles and + * calls to {@link #setDescription(String)}.) Must not be null. + * @param progressBar The progress bar to update during a task. Must not be null. + * @param stopButton The stop button. It will be disabled when there's no task that can + * be interrupted. A selection listener will be attached to it. Optional. Can be null. + * @param log A <em>mandatory</em> logger object that will be used to report all the log. + * Must not be null. + */ + public ProgressView( + Label label, + ProgressBar progressBar, + Control stopButton, + ILogUiProvider log) { + mLabel = label; + mProgressBar = progressBar; + mLog = log; + mProgressBar.setEnabled(false); + + mStopButton = stopButton; + if (mStopButton != null) { + mStopButton.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + if (mState == State.ACTIVE) { + changeState(State.STOP_PENDING); + } + } + }); + } + } + + /** + * Starts the task and block till it's either finished or canceled. + * This can be called from a non-UI thread safely. + * <p/> + * When a task is started from within a monitor, it reuses the thread + * from the parent. Otherwise it starts a new thread and runs it own + * UI loop. This means the task can perform UI operations using + * {@link Display#asyncExec(Runnable)}. + * <p/> + * In either case, the method only returns when the task has finished. + */ + public void startTask( + final String title, + final ITaskMonitor parentMonitor, + final ITask task) { + if (task != null) { + try { + if (parentMonitor == null && !mProgressBar.isDisposed()) { + mLabel.setText(title); + mProgressBar.setSelection(0); + mProgressBar.setEnabled(true); + changeState(ProgressView.State.ACTIVE); + } + + Runnable r = new Runnable() { + @Override + public void run() { + if (parentMonitor == null) { + task.run(new TaskMonitorImpl(ProgressView.this)); + + } else { + // Use all the reminder of the parent monitor. + if (parentMonitor.getProgressMax() == 0) { + parentMonitor.setProgressMax(1); + } + ITaskMonitor sub = parentMonitor.createSubMonitor( + parentMonitor.getProgressMax() - parentMonitor.getProgress()); + try { + task.run(sub); + } finally { + int delta = + sub.getProgressMax() - sub.getProgress(); + if (delta > 0) { + sub.incProgress(delta); + } + } + } + } + }; + + // If for some reason the UI has been disposed, just abort the thread. + if (mProgressBar.isDisposed()) { + return; + } + + if (TaskMonitorImpl.isTaskMonitorImpl(parentMonitor)) { + // If there's a parent monitor and it's our own class, we know this parent + // is already running a thread and the base one is running an event loop. + // We should thus not run a second event loop and we can process the + // runnable right here instead of spawning a thread inside the thread. + r.run(); + + } else { + // No parent monitor. This is the first one so we need a thread and + // we need to process UI events. + + final Thread t = new Thread(r, title); + t.start(); + + // Process the app's event loop whilst we wait for the thread to finish + while (!mProgressBar.isDisposed() && t.isAlive()) { + Display display = mProgressBar.getDisplay(); + if (!mProgressBar.isDisposed() && !display.readAndDispatch()) { + display.sleep(); + } + } + } + } catch (Exception e) { + // TODO log + + } finally { + if (parentMonitor == null && !mProgressBar.isDisposed()) { + changeState(ProgressView.State.IDLE); + mProgressBar.setSelection(0); + mProgressBar.setEnabled(false); + } + } + } + } + + private void syncExec(final Widget widget, final Runnable runnable) { + if (widget != null && !widget.isDisposed()) { + widget.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + // Check again whether the widget got disposed between the time where + // we requested the syncExec and the time it actually happened. + if (!widget.isDisposed()) { + runnable.run(); + } + } + }); + } + } + + private void changeState(State state) { + if (mState != null ) { + mState = state; + } + + syncExec(mStopButton, new Runnable() { + @Override + public void run() { + mStopButton.setEnabled(mState == State.ACTIVE); + } + }); + + } + + // --- Implementation of ITaskUiProvider --- + + @Override + public boolean isCancelRequested() { + return mState != State.ACTIVE; + } + + /** + * Sets the description in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setDescription(final String description) { + syncExec(mLabel, new Runnable() { + @Override + public void run() { + mLabel.setText(description); + } + }); + + mLog.setDescription(description); + } + + /** + * Logs a "normal" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void log(String log) { + mLog.log(log); + } + + /** + * Logs an "error" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logError(String log) { + mLog.logError(log); + } + + /** + * Logs a "verbose" information line, that is extra details which are typically + * not that useful for the end-user and might be hidden until explicitly shown. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logVerbose(String log) { + mLog.logVerbose(log); + } + + /** + * Sets the max value of the progress bar. + * This method can be invoked from a non-UI thread. + * + * @see ProgressBar#setMaximum(int) + */ + @Override + public void setProgressMax(final int max) { + syncExec(mProgressBar, new Runnable() { + @Override + public void run() { + mProgressBar.setMaximum(max); + } + }); + } + + /** + * Sets the current value of the progress bar. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setProgress(final int value) { + syncExec(mProgressBar, new Runnable() { + @Override + public void run() { + mProgressBar.setSelection(value); + } + }); + } + + /** + * Returns the current value of the progress bar, + * between 0 and up to {@link #setProgressMax(int)} - 1. + * This method can be invoked from a non-UI thread. + */ + @Override + public int getProgress() { + final int[] result = new int[] { 0 }; + + if (!mProgressBar.isDisposed()) { + mProgressBar.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (!mProgressBar.isDisposed()) { + result[0] = mProgressBar.getSelection(); + } + } + }); + } + + return result[0]; + } + + @Override + public boolean displayPrompt(final String title, final String message) { + final boolean[] result = new boolean[] { false }; + + syncExec(mProgressBar, new Runnable() { + @Override + public void run() { + Shell shell = mProgressBar.getShell(); + result[0] = MessageDialog.openQuestion(shell, title, message); + } + }); + + return result[0]; + } + + /** + * This method opens a pop-up window which requests for User Credentials. + * + * @param title The title of the window. + * @param message The message to displayed in the login/password window. + * @return Returns user provided credentials. + * If operation is <b>canceled</b> by user the return value must be <b>null</b>. + * @see ITaskMonitor#displayLoginCredentialsPrompt(String, String) + */ + @Override + public UserCredentials + displayLoginCredentialsPrompt(final String title, final String message) { + final String[] resultArray = new String[] {"", "", "", ""}; + // open dialog and request login and password + syncExec(mProgressBar, new Runnable() { + @Override + public void run() { + Shell shell = mProgressBar.getShell(); + AuthenticationDialog authenticationDialog = new AuthenticationDialog(shell, + title, + message); + int dlgResult = authenticationDialog.open(); + if (dlgResult == GridDialog.OK) { + resultArray[0] = authenticationDialog.getLogin(); + resultArray[1] = authenticationDialog.getPassword(); + resultArray[2] = authenticationDialog.getWorkstation(); + resultArray[3] = authenticationDialog.getDomain(); + } + } + }); + + return new UserCredentials(resultArray[0], + resultArray[1], + resultArray[2], + resultArray[3]); + } +} + diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java index 1d39c59..2590169 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java @@ -1,48 +1,48 @@ -/*
- * 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.sdkuilib.internal.tasks;
-
-import com.android.sdklib.internal.repository.ITask;
-import com.android.sdklib.internal.repository.ITaskFactory;
-import com.android.sdklib.internal.repository.ITaskMonitor;
-
-/**
- * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog
- * for each new task.
- */
-public final class ProgressViewFactory implements ITaskFactory {
-
- private ProgressView mProgressView;
-
- public ProgressViewFactory() {
- }
-
- public void setProgressView(ProgressView progressView) {
- mProgressView = progressView;
- }
-
- @Override
- public void start(String title, ITask task) {
- start(title, null /*monitor*/, task);
- }
-
- @Override
- public void start(String title, ITaskMonitor parentMonitor, ITask task) {
- assert mProgressView != null;
- mProgressView.startTask(title, parentMonitor, task);
- }
-}
+/* + * 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.sdkuilib.internal.tasks; + +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskFactory; +import com.android.sdklib.internal.repository.ITaskMonitor; + +/** + * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog + * for each new task. + */ +public final class ProgressViewFactory implements ITaskFactory { + + private ProgressView mProgressView; + + public ProgressViewFactory() { + } + + public void setProgressView(ProgressView progressView) { + mProgressView = progressView; + } + + @Override + public void start(String title, ITask task) { + start(title, null /*monitor*/, task); + } + + @Override + public void start(String title, ITaskMonitor parentMonitor, ITask task) { + assert mProgressView != null; + mProgressView.startTask(title, parentMonitor, task); + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java index 9a796b7..9958920 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java @@ -1,358 +1,358 @@ -/*
- * Copyright (C) 2011 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.tasks;
-
-import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.internal.repository.UserCredentials;
-
-/**
- * Internal class that implements the logic of an {@link ITaskMonitor}.
- * It doesn't deal with any UI directly. Instead it delegates the UI to
- * the provided {@link IProgressUiProvider}.
- */
-class TaskMonitorImpl implements ITaskMonitor {
-
- private static final double MAX_COUNT = 10000.0;
-
- private interface ISubTaskMonitor extends ITaskMonitor {
- public void subIncProgress(double realDelta);
- }
-
- private double mIncCoef = 0;
- private double mValue = 0;
- private final IProgressUiProvider mUi;
-
- /**
- * Returns true if the given {@code monitor} is an instance of {@link TaskMonitorImpl}
- * or its private SubTaskMonitor.
- */
- public static boolean isTaskMonitorImpl(ITaskMonitor monitor) {
- return monitor instanceof TaskMonitorImpl || monitor instanceof SubTaskMonitor;
- }
-
- /**
- * Constructs a new {@link TaskMonitorImpl} that relies on the given
- * {@link IProgressUiProvider} to change the user interface.
- * @param ui The {@link IProgressUiProvider}. Cannot be null.
- */
- public TaskMonitorImpl(IProgressUiProvider ui) {
- mUi = ui;
- }
-
- /** Returns the {@link IProgressUiProvider} passed to the constructor. */
- public IProgressUiProvider getUiProvider() {
- return mUi;
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void setDescription(String format, Object... args) {
- final String text = String.format(format, args);
- mUi.setDescription(text);
- }
-
- /**
- * Logs a "normal" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void log(String format, Object... args) {
- String text = String.format(format, args);
- mUi.log(text);
- }
-
- /**
- * Logs an "error" information line.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logError(String format, Object... args) {
- String text = String.format(format, args);
- mUi.logError(text);
- }
-
- /**
- * Logs a "verbose" information line, that is extra details which are typically
- * not that useful for the end-user and might be hidden until explicitly shown.
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void logVerbose(String format, Object... args) {
- String text = String.format(format, args);
- mUi.logVerbose(text);
- }
-
- /**
- * Sets the max value of the progress bar.
- * This method can be invoked from a non-UI thread.
- *
- * Weird things will happen if setProgressMax is called multiple times
- * *after* {@link #incProgress(int)}: we don't try to adjust it on the
- * fly.
- */
- @Override
- public void setProgressMax(int max) {
- assert max > 0;
- // Always set the dialog's progress max to 10k since it only handles
- // integers and we want to have a better inner granularity. Instead
- // we use the max to compute a coefficient for inc deltas.
- mUi.setProgressMax((int) MAX_COUNT);
- mIncCoef = max > 0 ? MAX_COUNT / max : 0;
- assert mIncCoef > 0;
- }
-
- @Override
- public int getProgressMax() {
- return mIncCoef > 0 ? (int) (MAX_COUNT / mIncCoef) : 0;
- }
-
- /**
- * Increments the current value of the progress bar.
- *
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public void incProgress(int delta) {
- if (delta > 0 && mIncCoef > 0) {
- internalIncProgress(delta * mIncCoef);
- }
- }
-
- private void internalIncProgress(double realDelta) {
- mValue += realDelta;
- mUi.setProgress((int)mValue);
- }
-
- /**
- * Returns the current value of the progress bar,
- * between 0 and up to {@link #setProgressMax(int)} - 1.
- *
- * This method can be invoked from a non-UI thread.
- */
- @Override
- public int getProgress() {
- // mIncCoef is 0 if setProgressMax hasn't been used yet.
- return mIncCoef > 0 ? (int)(mUi.getProgress() / mIncCoef) : 0;
- }
-
- /**
- * Returns true if the "Cancel" button was selected.
- * It is up to the task thread to pool this and exit.
- */
- @Override
- public boolean isCancelRequested() {
- return mUi.isCancelRequested();
- }
-
- /**
- * Displays a yes/no question dialog box.
- *
- * This implementation allow this to be called from any thread, it
- * makes sure the dialog is opened synchronously in the ui thread.
- *
- * @param title The title of the dialog box
- * @param message The error message
- * @return true if YES was clicked.
- */
- @Override
- public boolean displayPrompt(final String title, final String message) {
- return mUi.displayPrompt(title, message);
- }
-
- /**
- * Displays a Login/Password dialog. This implementation allows this method to be
- * called from any thread, it makes sure the dialog is opened synchronously
- * in the ui thread.
- *
- * @param title The title of the dialog box
- * @param message Message to be displayed
- * @return Pair with entered login/password. Login is always the first
- * element and Password is always the second. If any error occurs a
- * pair with empty strings is returned.
- */
- @Override
- public UserCredentials displayLoginCredentialsPrompt(String title, String message) {
- return mUi.displayLoginCredentialsPrompt(title, message);
- }
-
- /**
- * Creates a sub-monitor that will use up to tickCount on the progress bar.
- * tickCount must be 1 or more.
- */
- @Override
- public ITaskMonitor createSubMonitor(int tickCount) {
- assert mIncCoef > 0;
- assert tickCount > 0;
- return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef);
- }
-
- // ----- ISdkLog interface ----
-
- @Override
- public void error(Throwable throwable, String errorFormat, Object... arg) {
- if (errorFormat != null) {
- logError("Error: " + errorFormat, arg);
- }
-
- if (throwable != null) {
- logError("%s", throwable.getMessage()); //$NON-NLS-1$
- }
- }
-
- @Override
- public void warning(String warningFormat, Object... arg) {
- log("Warning: " + warningFormat, arg);
- }
-
- @Override
- public void printf(String msgFormat, Object... arg) {
- log(msgFormat, arg);
- }
-
- // ----- Sub Monitor -----
-
- private static class SubTaskMonitor implements ISubTaskMonitor {
-
- private final TaskMonitorImpl mRoot;
- private final ISubTaskMonitor mParent;
- private final double mStart;
- private final double mSpan;
- private double mSubValue;
- private double mSubCoef;
-
- /**
- * Creates a new sub task monitor which will work for the given range [start, start+span]
- * in its parent.
- *
- * @param taskMonitor The ProgressTask root
- * @param parent The immediate parent. Can be the null or another sub task monitor.
- * @param start The start value in the root's coordinates
- * @param span The span value in the root's coordinates
- */
- public SubTaskMonitor(TaskMonitorImpl taskMonitor,
- ISubTaskMonitor parent,
- double start,
- double span) {
- mRoot = taskMonitor;
- mParent = parent;
- mStart = start;
- mSpan = span;
- mSubValue = start;
- }
-
- @Override
- public boolean isCancelRequested() {
- return mRoot.isCancelRequested();
- }
-
- @Override
- public void setDescription(String format, Object... args) {
- mRoot.setDescription(format, args);
- }
-
- @Override
- public void log(String format, Object... args) {
- mRoot.log(format, args);
- }
-
- @Override
- public void logError(String format, Object... args) {
- mRoot.logError(format, args);
- }
-
- @Override
- public void logVerbose(String format, Object... args) {
- mRoot.logVerbose(format, args);
- }
-
- @Override
- public void setProgressMax(int max) {
- assert max > 0;
- mSubCoef = max > 0 ? mSpan / max : 0;
- assert mSubCoef > 0;
- }
-
- @Override
- public int getProgressMax() {
- return mSubCoef > 0 ? (int) (mSpan / mSubCoef) : 0;
- }
-
- @Override
- public int getProgress() {
- // subCoef can be 0 if setProgressMax() and incProgress() haven't been called yet
- assert mSubValue == mStart || mSubCoef > 0;
- return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;
- }
-
- @Override
- public void incProgress(int delta) {
- if (delta > 0 && mSubCoef > 0) {
- subIncProgress(delta * mSubCoef);
- }
- }
-
- @Override
- public void subIncProgress(double realDelta) {
- mSubValue += realDelta;
- if (mParent != null) {
- mParent.subIncProgress(realDelta);
- } else {
- mRoot.internalIncProgress(realDelta);
- }
- }
-
- @Override
- public boolean displayPrompt(String title, String message) {
- return mRoot.displayPrompt(title, message);
- }
-
- @Override
- public UserCredentials displayLoginCredentialsPrompt(String title, String message) {
- return mRoot.displayLoginCredentialsPrompt(title, message);
- }
-
- @Override
- public ITaskMonitor createSubMonitor(int tickCount) {
- assert mSubCoef > 0;
- assert tickCount > 0;
- return new SubTaskMonitor(mRoot,
- this,
- mSubValue,
- tickCount * mSubCoef);
- }
-
- // ----- ISdkLog interface ----
-
- @Override
- public void error(Throwable throwable, String errorFormat, Object... arg) {
- mRoot.error(throwable, errorFormat, arg);
- }
-
- @Override
- public void warning(String warningFormat, Object... arg) {
- mRoot.warning(warningFormat, arg);
- }
-
- @Override
- public void printf(String msgFormat, Object... arg) {
- mRoot.printf(msgFormat, arg);
- }
- }
-}
+/* + * Copyright (C) 2011 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.tasks; + +import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdklib.internal.repository.UserCredentials; + +/** + * Internal class that implements the logic of an {@link ITaskMonitor}. + * It doesn't deal with any UI directly. Instead it delegates the UI to + * the provided {@link IProgressUiProvider}. + */ +class TaskMonitorImpl implements ITaskMonitor { + + private static final double MAX_COUNT = 10000.0; + + private interface ISubTaskMonitor extends ITaskMonitor { + public void subIncProgress(double realDelta); + } + + private double mIncCoef = 0; + private double mValue = 0; + private final IProgressUiProvider mUi; + + /** + * Returns true if the given {@code monitor} is an instance of {@link TaskMonitorImpl} + * or its private SubTaskMonitor. + */ + public static boolean isTaskMonitorImpl(ITaskMonitor monitor) { + return monitor instanceof TaskMonitorImpl || monitor instanceof SubTaskMonitor; + } + + /** + * Constructs a new {@link TaskMonitorImpl} that relies on the given + * {@link IProgressUiProvider} to change the user interface. + * @param ui The {@link IProgressUiProvider}. Cannot be null. + */ + public TaskMonitorImpl(IProgressUiProvider ui) { + mUi = ui; + } + + /** Returns the {@link IProgressUiProvider} passed to the constructor. */ + public IProgressUiProvider getUiProvider() { + return mUi; + } + + /** + * Sets the description in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + @Override + public void setDescription(String format, Object... args) { + final String text = String.format(format, args); + mUi.setDescription(text); + } + + /** + * Logs a "normal" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void log(String format, Object... args) { + String text = String.format(format, args); + mUi.log(text); + } + + /** + * Logs an "error" information line. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logError(String format, Object... args) { + String text = String.format(format, args); + mUi.logError(text); + } + + /** + * Logs a "verbose" information line, that is extra details which are typically + * not that useful for the end-user and might be hidden until explicitly shown. + * This method can be invoked from a non-UI thread. + */ + @Override + public void logVerbose(String format, Object... args) { + String text = String.format(format, args); + mUi.logVerbose(text); + } + + /** + * Sets the max value of the progress bar. + * This method can be invoked from a non-UI thread. + * + * Weird things will happen if setProgressMax is called multiple times + * *after* {@link #incProgress(int)}: we don't try to adjust it on the + * fly. + */ + @Override + public void setProgressMax(int max) { + assert max > 0; + // Always set the dialog's progress max to 10k since it only handles + // integers and we want to have a better inner granularity. Instead + // we use the max to compute a coefficient for inc deltas. + mUi.setProgressMax((int) MAX_COUNT); + mIncCoef = max > 0 ? MAX_COUNT / max : 0; + assert mIncCoef > 0; + } + + @Override + public int getProgressMax() { + return mIncCoef > 0 ? (int) (MAX_COUNT / mIncCoef) : 0; + } + + /** + * Increments the current value of the progress bar. + * + * This method can be invoked from a non-UI thread. + */ + @Override + public void incProgress(int delta) { + if (delta > 0 && mIncCoef > 0) { + internalIncProgress(delta * mIncCoef); + } + } + + private void internalIncProgress(double realDelta) { + mValue += realDelta; + mUi.setProgress((int)mValue); + } + + /** + * Returns the current value of the progress bar, + * between 0 and up to {@link #setProgressMax(int)} - 1. + * + * This method can be invoked from a non-UI thread. + */ + @Override + public int getProgress() { + // mIncCoef is 0 if setProgressMax hasn't been used yet. + return mIncCoef > 0 ? (int)(mUi.getProgress() / mIncCoef) : 0; + } + + /** + * Returns true if the "Cancel" button was selected. + * It is up to the task thread to pool this and exit. + */ + @Override + public boolean isCancelRequested() { + return mUi.isCancelRequested(); + } + + /** + * Displays a yes/no question dialog box. + * + * This implementation allow this to be called from any thread, it + * makes sure the dialog is opened synchronously in the ui thread. + * + * @param title The title of the dialog box + * @param message The error message + * @return true if YES was clicked. + */ + @Override + public boolean displayPrompt(final String title, final String message) { + return mUi.displayPrompt(title, message); + } + + /** + * Displays a Login/Password dialog. This implementation allows this method to be + * called from any thread, it makes sure the dialog is opened synchronously + * in the ui thread. + * + * @param title The title of the dialog box + * @param message Message to be displayed + * @return Pair with entered login/password. Login is always the first + * element and Password is always the second. If any error occurs a + * pair with empty strings is returned. + */ + @Override + public UserCredentials displayLoginCredentialsPrompt(String title, String message) { + return mUi.displayLoginCredentialsPrompt(title, message); + } + + /** + * Creates a sub-monitor that will use up to tickCount on the progress bar. + * tickCount must be 1 or more. + */ + @Override + public ITaskMonitor createSubMonitor(int tickCount) { + assert mIncCoef > 0; + assert tickCount > 0; + return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef); + } + + // ----- ISdkLog interface ---- + + @Override + public void error(Throwable throwable, String errorFormat, Object... arg) { + if (errorFormat != null) { + logError("Error: " + errorFormat, arg); + } + + if (throwable != null) { + logError("%s", throwable.getMessage()); //$NON-NLS-1$ + } + } + + @Override + public void warning(String warningFormat, Object... arg) { + log("Warning: " + warningFormat, arg); + } + + @Override + public void printf(String msgFormat, Object... arg) { + log(msgFormat, arg); + } + + // ----- Sub Monitor ----- + + private static class SubTaskMonitor implements ISubTaskMonitor { + + private final TaskMonitorImpl mRoot; + private final ISubTaskMonitor mParent; + private final double mStart; + private final double mSpan; + private double mSubValue; + private double mSubCoef; + + /** + * Creates a new sub task monitor which will work for the given range [start, start+span] + * in its parent. + * + * @param taskMonitor The ProgressTask root + * @param parent The immediate parent. Can be the null or another sub task monitor. + * @param start The start value in the root's coordinates + * @param span The span value in the root's coordinates + */ + public SubTaskMonitor(TaskMonitorImpl taskMonitor, + ISubTaskMonitor parent, + double start, + double span) { + mRoot = taskMonitor; + mParent = parent; + mStart = start; + mSpan = span; + mSubValue = start; + } + + @Override + public boolean isCancelRequested() { + return mRoot.isCancelRequested(); + } + + @Override + public void setDescription(String format, Object... args) { + mRoot.setDescription(format, args); + } + + @Override + public void log(String format, Object... args) { + mRoot.log(format, args); + } + + @Override + public void logError(String format, Object... args) { + mRoot.logError(format, args); + } + + @Override + public void logVerbose(String format, Object... args) { + mRoot.logVerbose(format, args); + } + + @Override + public void setProgressMax(int max) { + assert max > 0; + mSubCoef = max > 0 ? mSpan / max : 0; + assert mSubCoef > 0; + } + + @Override + public int getProgressMax() { + return mSubCoef > 0 ? (int) (mSpan / mSubCoef) : 0; + } + + @Override + public int getProgress() { + // subCoef can be 0 if setProgressMax() and incProgress() haven't been called yet + assert mSubValue == mStart || mSubCoef > 0; + return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0; + } + + @Override + public void incProgress(int delta) { + if (delta > 0 && mSubCoef > 0) { + subIncProgress(delta * mSubCoef); + } + } + + @Override + public void subIncProgress(double realDelta) { + mSubValue += realDelta; + if (mParent != null) { + mParent.subIncProgress(realDelta); + } else { + mRoot.internalIncProgress(realDelta); + } + } + + @Override + public boolean displayPrompt(String title, String message) { + return mRoot.displayPrompt(title, message); + } + + @Override + public UserCredentials displayLoginCredentialsPrompt(String title, String message) { + return mRoot.displayLoginCredentialsPrompt(title, message); + } + + @Override + public ITaskMonitor createSubMonitor(int tickCount) { + assert mSubCoef > 0; + assert tickCount > 0; + return new SubTaskMonitor(mRoot, + this, + mSubValue, + tickCount * mSubCoef); + } + + // ----- ISdkLog interface ---- + + @Override + public void error(Throwable throwable, String errorFormat, Object... arg) { + mRoot.error(throwable, errorFormat, arg); + } + + @Override + public void warning(String warningFormat, Object... arg) { + mRoot.warning(warningFormat, arg); + } + + @Override + public void printf(String msgFormat, Object... arg) { + mRoot.printf(msgFormat, arg); + } + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ImgDisabledButton.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ImgDisabledButton.java index 7f1361e..62973a4 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ImgDisabledButton.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ImgDisabledButton.java @@ -1,60 +1,60 @@ -/*
- * Copyright (C) 2011 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.widgets;
-
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * A label that can display 2 images depending on its enabled/disabled state.
- * This acts as a button by firing the {@link SWT#Selection} listener.
- */
-public class ImgDisabledButton extends ToggleButton {
- public ImgDisabledButton(
- Composite parent,
- int style,
- Image imageEnabled,
- Image imageDisabled,
- String tooltipEnabled,
- String tooltipDisabled) {
- super(parent,
- style,
- imageEnabled,
- imageDisabled,
- tooltipEnabled,
- tooltipDisabled);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- updateImageAndTooltip();
- redraw();
- }
-
- @Override
- public void setState(int state) {
- throw new UnsupportedOperationException(); // not available for this type of button
- }
-
- @Override
- public int getState() {
- return (isDisposed() || !isEnabled()) ? 1 : 0;
- }
-}
+/* + * Copyright (C) 2011 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.widgets; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; + +/** + * A label that can display 2 images depending on its enabled/disabled state. + * This acts as a button by firing the {@link SWT#Selection} listener. + */ +public class ImgDisabledButton extends ToggleButton { + public ImgDisabledButton( + Composite parent, + int style, + Image imageEnabled, + Image imageDisabled, + String tooltipEnabled, + String tooltipDisabled) { + super(parent, + style, + imageEnabled, + imageDisabled, + tooltipEnabled, + tooltipDisabled); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + updateImageAndTooltip(); + redraw(); + } + + @Override + public void setState(int state) { + throw new UnsupportedOperationException(); // not available for this type of button + } + + @Override + public int getState() { + return (isDisposed() || !isEnabled()) ? 1 : 0; + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ToggleButton.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ToggleButton.java index 24138bc..7c66bcf 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ToggleButton.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ToggleButton.java @@ -1,134 +1,134 @@ -/*
- * Copyright (C) 2011 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.widgets;
-
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Event;
-
-/**
- * A label that can display 2 images depending on its internal state.
- * This acts as a button by firing the {@link SWT#Selection} listener.
- */
-public class ToggleButton extends CLabel {
- private Image[] mImage = new Image[2];
- private String[] mTooltip = new String[2];
- private boolean mMouseIn;
- private int mState = 0;
-
-
- public ToggleButton(
- Composite parent,
- int style,
- Image image1,
- Image image2,
- String tooltip1,
- String tooltip2) {
- super(parent, style);
- mImage[0] = image1;
- mImage[1] = image2;
- mTooltip[0] = tooltip1;
- mTooltip[1] = tooltip2;
- updateImageAndTooltip();
-
- addMouseListener(new MouseListener() {
- @Override
- public void mouseDown(MouseEvent e) {
- // pass
- }
-
- @Override
- public void mouseUp(MouseEvent e) {
- // We select on mouse-up, as it should be properly done since this is the
- // only way a user can cancel a button click by moving out of the button.
- if (mMouseIn && e.button == 1) {
- notifyListeners(SWT.Selection, new Event());
- }
- }
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- if (mMouseIn && e.button == 1) {
- notifyListeners(SWT.DefaultSelection, new Event());
- }
- }
- });
-
- addMouseTrackListener(new MouseTrackListener() {
- @Override
- public void mouseExit(MouseEvent e) {
- if (mMouseIn) {
- mMouseIn = false;
- redraw();
- }
- }
-
- @Override
- public void mouseEnter(MouseEvent e) {
- if (!mMouseIn) {
- mMouseIn = true;
- redraw();
- }
- }
-
- @Override
- public void mouseHover(MouseEvent e) {
- // pass
- }
- });
- }
-
- @Override
- public int getStyle() {
- int style = super.getStyle();
- if (mMouseIn) {
- style |= SWT.SHADOW_IN;
- }
- return style;
- }
-
- /**
- * Sets current state.
- * @param state A value 0 or 1.
- */
- public void setState(int state) {
- assert state == 0 || state == 1;
- mState = state;
- updateImageAndTooltip();
- redraw();
- }
-
- /**
- * Returns the current state
- * @return Returns the current state, either 0 or 1.
- */
- public int getState() {
- return mState;
- }
-
- protected void updateImageAndTooltip() {
- setImage(mImage[getState()]);
- setToolTipText(mTooltip[getState()]);
- }
-}
-
+/* + * Copyright (C) 2011 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.widgets; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; + +/** + * A label that can display 2 images depending on its internal state. + * This acts as a button by firing the {@link SWT#Selection} listener. + */ +public class ToggleButton extends CLabel { + private Image[] mImage = new Image[2]; + private String[] mTooltip = new String[2]; + private boolean mMouseIn; + private int mState = 0; + + + public ToggleButton( + Composite parent, + int style, + Image image1, + Image image2, + String tooltip1, + String tooltip2) { + super(parent, style); + mImage[0] = image1; + mImage[1] = image2; + mTooltip[0] = tooltip1; + mTooltip[1] = tooltip2; + updateImageAndTooltip(); + + addMouseListener(new MouseListener() { + @Override + public void mouseDown(MouseEvent e) { + // pass + } + + @Override + public void mouseUp(MouseEvent e) { + // We select on mouse-up, as it should be properly done since this is the + // only way a user can cancel a button click by moving out of the button. + if (mMouseIn && e.button == 1) { + notifyListeners(SWT.Selection, new Event()); + } + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + if (mMouseIn && e.button == 1) { + notifyListeners(SWT.DefaultSelection, new Event()); + } + } + }); + + addMouseTrackListener(new MouseTrackListener() { + @Override + public void mouseExit(MouseEvent e) { + if (mMouseIn) { + mMouseIn = false; + redraw(); + } + } + + @Override + public void mouseEnter(MouseEvent e) { + if (!mMouseIn) { + mMouseIn = true; + redraw(); + } + } + + @Override + public void mouseHover(MouseEvent e) { + // pass + } + }); + } + + @Override + public int getStyle() { + int style = super.getStyle(); + if (mMouseIn) { + style |= SWT.SHADOW_IN; + } + return style; + } + + /** + * Sets current state. + * @param state A value 0 or 1. + */ + public void setState(int state) { + assert state == 0 || state == 1; + mState = state; + updateImageAndTooltip(); + redraw(); + } + + /** + * Returns the current state + * @return Returns the current state, either 0 or 1. + */ + public int getState() { + return mState; + } + + protected void updateImageAndTooltip() { + setImage(mImage[getState()]); + setToolTipText(mTooltip[getState()]); + } +} + diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvdManagerWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvdManagerWindow.java index 5ad5fc2..17816b8 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvdManagerWindow.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvdManagerWindow.java @@ -1,96 +1,96 @@ -/*
- * Copyright (C) 2011 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.repository;
-
-import com.android.sdklib.ISdkLog;
-import com.android.sdkuilib.internal.repository.sdkman2.AvdManagerWindowImpl1;
-import com.android.sdkuilib.internal.widgets.AvdSelector;
-
-import org.eclipse.swt.widgets.Shell;
-
-/**
- * Opens an AVD Manager Window.
- *
- * This is the public entry point for using the window.
- */
-public class AvdManagerWindow {
-
- /** The actual window implementation to which this class delegates. */
- private AvdManagerWindowImpl1 mWindow;
-
- /**
- * Enum giving some indication of what is invoking this window.
- * The behavior and UI will change slightly depending on the context.
- * <p/>
- * Note: if you add Android support to your specific IDE, you might want
- * to specialize this context enum.
- */
- public enum AvdInvocationContext {
- /**
- * The AVD Manager is invoked from the stand-alone 'android' tool.
- * In this mode, we present an about box, a settings page.
- * For SdkMan2, we also have a menu bar and link to the SDK Manager 2.
- */
- STANDALONE,
-
- /**
- * The AVD Manager is embedded as a dialog in the SDK Manager
- * or in the {@link AvdSelector}.
- * This is similar to the {@link #STANDALONE} mode except we don't need
- * to display a menu bar at all since we don't want a menu item linking
- * back to the SDK Manager and we don't need to redisplay the options
- * and about which are already on the root window.
- */
- DIALOG,
-
- /**
- * The AVD Manager is invoked from an IDE.
- * In this mode, we do not modify the menu bar.
- * There is no about box and no settings.
- */
- IDE,
- }
-
-
- /**
- * Creates a new window. Caller must call open(), which will block.
- *
- * @param parentShell Parent shell.
- * @param sdkLog Logger. Cannot be null.
- * @param osSdkRoot The OS path to the SDK root.
- * @param context The {@link AvdInvocationContext} to change the behavior depending on who's
- * opening the SDK Manager.
- */
- public AvdManagerWindow(
- Shell parentShell,
- ISdkLog sdkLog,
- String osSdkRoot,
- AvdInvocationContext context) {
- mWindow = new AvdManagerWindowImpl1(
- parentShell,
- sdkLog,
- osSdkRoot,
- context);
- }
-
- /**
- * Opens the window.
- */
- public void open() {
- mWindow.open();
- }
-}
+/* + * Copyright (C) 2011 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.repository; + +import com.android.sdklib.ISdkLog; +import com.android.sdkuilib.internal.repository.sdkman2.AvdManagerWindowImpl1; +import com.android.sdkuilib.internal.widgets.AvdSelector; + +import org.eclipse.swt.widgets.Shell; + +/** + * Opens an AVD Manager Window. + * + * This is the public entry point for using the window. + */ +public class AvdManagerWindow { + + /** The actual window implementation to which this class delegates. */ + private AvdManagerWindowImpl1 mWindow; + + /** + * Enum giving some indication of what is invoking this window. + * The behavior and UI will change slightly depending on the context. + * <p/> + * Note: if you add Android support to your specific IDE, you might want + * to specialize this context enum. + */ + public enum AvdInvocationContext { + /** + * The AVD Manager is invoked from the stand-alone 'android' tool. + * In this mode, we present an about box, a settings page. + * For SdkMan2, we also have a menu bar and link to the SDK Manager 2. + */ + STANDALONE, + + /** + * The AVD Manager is embedded as a dialog in the SDK Manager + * or in the {@link AvdSelector}. + * This is similar to the {@link #STANDALONE} mode except we don't need + * to display a menu bar at all since we don't want a menu item linking + * back to the SDK Manager and we don't need to redisplay the options + * and about which are already on the root window. + */ + DIALOG, + + /** + * The AVD Manager is invoked from an IDE. + * In this mode, we do not modify the menu bar. + * There is no about box and no settings. + */ + IDE, + } + + + /** + * Creates a new window. Caller must call open(), which will block. + * + * @param parentShell Parent shell. + * @param sdkLog Logger. Cannot be null. + * @param osSdkRoot The OS path to the SDK root. + * @param context The {@link AvdInvocationContext} to change the behavior depending on who's + * opening the SDK Manager. + */ + public AvdManagerWindow( + Shell parentShell, + ISdkLog sdkLog, + String osSdkRoot, + AvdInvocationContext context) { + mWindow = new AvdManagerWindowImpl1( + parentShell, + sdkLog, + osSdkRoot, + context); + } + + /** + * Opens the window. + */ + public void open() { + mWindow.open(); + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ISdkChangeListener.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ISdkChangeListener.java index 9cca7aa..e221f98 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ISdkChangeListener.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ISdkChangeListener.java @@ -1,54 +1,54 @@ -/*
- * Copyright (C) 2010 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.repository;
-
-
-/**
- * Interface for listeners on SDK modifications by the SDK Manager UI.
- * This notifies when the SDK manager is first loading the SDK or before/after it installed
- * a package.
- */
-public interface ISdkChangeListener {
- /**
- * Invoked when the content of the SDK is being loaded by the SDK Manager UI
- * for the first time.
- * This is generally followed by a call to {@link #onSdkReload()}
- * or by a call to {@link #preInstallHook()}.
- */
- void onSdkLoaded();
-
- /**
- * Invoked when the SDK Manager UI is about to start installing packages.
- * This will be followed by a call to {@link #postInstallHook()}.
- */
- void preInstallHook();
-
- /**
- * Invoked when the SDK Manager UI is done installing packages.
- * Some new packages might have been installed or the user might have cancelled the operation.
- * This is generally followed by a call to {@link #onSdkReload()}.
- */
- void postInstallHook();
-
- /**
- * Invoked when the content of the SDK is being reloaded by the SDK Manager UI,
- * typically after a package was installed. The SDK content might or might not
- * have changed.
- */
- void onSdkReload();
-}
-
+/* + * Copyright (C) 2010 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.repository; + + +/** + * Interface for listeners on SDK modifications by the SDK Manager UI. + * This notifies when the SDK manager is first loading the SDK or before/after it installed + * a package. + */ +public interface ISdkChangeListener { + /** + * Invoked when the content of the SDK is being loaded by the SDK Manager UI + * for the first time. + * This is generally followed by a call to {@link #onSdkReload()} + * or by a call to {@link #preInstallHook()}. + */ + void onSdkLoaded(); + + /** + * Invoked when the SDK Manager UI is about to start installing packages. + * This will be followed by a call to {@link #postInstallHook()}. + */ + void preInstallHook(); + + /** + * Invoked when the SDK Manager UI is done installing packages. + * Some new packages might have been installed or the user might have cancelled the operation. + * This is generally followed by a call to {@link #onSdkReload()}. + */ + void postInstallHook(); + + /** + * Invoked when the content of the SDK is being reloaded by the SDK Manager UI, + * typically after a package was installed. The SDK content might or might not + * have changed. + */ + void onSdkReload(); +} + diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/SdkUpdaterWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/SdkUpdaterWindow.java index 06fc387..d796f47 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/SdkUpdaterWindow.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/SdkUpdaterWindow.java @@ -1,112 +1,112 @@ -/*
- * 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.sdkuilib.repository;
-
-import com.android.sdklib.ISdkLog;
-import com.android.sdkuilib.internal.repository.ISdkUpdaterWindow;
-import com.android.sdkuilib.internal.repository.sdkman2.SdkUpdaterWindowImpl2;
-
-import org.eclipse.swt.widgets.Shell;
-
-/**
- * Opens an SDK Manager Window.
- *
- * This is the public entry point for using the window.
- */
-public class SdkUpdaterWindow {
-
- /** The actual window implementation to which this class delegates. */
- private ISdkUpdaterWindow mWindow;
-
- /**
- * Enum giving some indication of what is invoking this window.
- * The behavior and UI will change slightly depending on the context.
- * <p/>
- * Note: if you add Android support to your specific IDE, you might want
- * to specialize this context enum.
- */
- public enum SdkInvocationContext {
- /**
- * The SDK Manager is invoked from the stand-alone 'android' tool.
- * In this mode, we present an about box, a settings page.
- * For SdkMan2, we also have a menu bar and link to the AVD manager.
- */
- STANDALONE,
-
- /**
- * The SDK Manager is invoked from the standalone AVD Manager.
- * This is similar to the standalone mode except that in this case we
- * don't display a menu item linking to the AVD Manager.
- */
- AVD_MANAGER,
-
- /**
- * The SDK Manager is invoked from an IDE.
- * In this mode, we do not modify the menu bar. There is no about box
- * and no settings (e.g. HTTP proxy settings are inherited from Eclipse.)
- */
- IDE,
-
- /**
- * The SDK Manager is invoked from the AVD Selector.
- * For SdkMan1, this means the AVD page will be displayed first.
- * For SdkMan2, we won't be using this.
- */
- AVD_SELECTOR
- }
-
- /**
- * Creates a new window. Caller must call open(), which will block.
- *
- * @param parentShell Parent shell.
- * @param sdkLog Logger. Cannot be null.
- * @param osSdkRoot The OS path to the SDK root.
- * @param context The {@link SdkInvocationContext} to change the behavior depending on who's
- * opening the SDK Manager.
- */
- public SdkUpdaterWindow(
- Shell parentShell,
- ISdkLog sdkLog,
- String osSdkRoot,
- SdkInvocationContext context) {
-
- mWindow = new SdkUpdaterWindowImpl2(parentShell, sdkLog, osSdkRoot, context);
- }
-
- /**
- * Adds a new listener to be notified when a change is made to the content of the SDK.
- * This should be called before {@link #open()}.
- */
- public void addListener(ISdkChangeListener listener) {
- mWindow.addListener(listener);
- }
-
- /**
- * Removes a new listener to be notified anymore when a change is made to the content of
- * the SDK.
- */
- public void removeListener(ISdkChangeListener listener) {
- mWindow.removeListener(listener);
- }
-
- /**
- * Opens the window.
- */
- public void open() {
- mWindow.open();
- }
-}
+/* + * 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.sdkuilib.repository; + +import com.android.sdklib.ISdkLog; +import com.android.sdkuilib.internal.repository.ISdkUpdaterWindow; +import com.android.sdkuilib.internal.repository.sdkman2.SdkUpdaterWindowImpl2; + +import org.eclipse.swt.widgets.Shell; + +/** + * Opens an SDK Manager Window. + * + * This is the public entry point for using the window. + */ +public class SdkUpdaterWindow { + + /** The actual window implementation to which this class delegates. */ + private ISdkUpdaterWindow mWindow; + + /** + * Enum giving some indication of what is invoking this window. + * The behavior and UI will change slightly depending on the context. + * <p/> + * Note: if you add Android support to your specific IDE, you might want + * to specialize this context enum. + */ + public enum SdkInvocationContext { + /** + * The SDK Manager is invoked from the stand-alone 'android' tool. + * In this mode, we present an about box, a settings page. + * For SdkMan2, we also have a menu bar and link to the AVD manager. + */ + STANDALONE, + + /** + * The SDK Manager is invoked from the standalone AVD Manager. + * This is similar to the standalone mode except that in this case we + * don't display a menu item linking to the AVD Manager. + */ + AVD_MANAGER, + + /** + * The SDK Manager is invoked from an IDE. + * In this mode, we do not modify the menu bar. There is no about box + * and no settings (e.g. HTTP proxy settings are inherited from Eclipse.) + */ + IDE, + + /** + * The SDK Manager is invoked from the AVD Selector. + * For SdkMan1, this means the AVD page will be displayed first. + * For SdkMan2, we won't be using this. + */ + AVD_SELECTOR + } + + /** + * Creates a new window. Caller must call open(), which will block. + * + * @param parentShell Parent shell. + * @param sdkLog Logger. Cannot be null. + * @param osSdkRoot The OS path to the SDK root. + * @param context The {@link SdkInvocationContext} to change the behavior depending on who's + * opening the SDK Manager. + */ + public SdkUpdaterWindow( + Shell parentShell, + ISdkLog sdkLog, + String osSdkRoot, + SdkInvocationContext context) { + + mWindow = new SdkUpdaterWindowImpl2(parentShell, sdkLog, osSdkRoot, context); + } + + /** + * Adds a new listener to be notified when a change is made to the content of the SDK. + * This should be called before {@link #open()}. + */ + public void addListener(ISdkChangeListener listener) { + mWindow.addListener(listener); + } + + /** + * Removes a new listener to be notified anymore when a change is made to the content of + * the SDK. + */ + public void removeListener(ISdkChangeListener listener) { + mWindow.removeListener(listener); + } + + /** + * Opens the window. + */ + public void open() { + mWindow.open(); + } +} diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java index f186c84..a23b921 100755 --- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java +++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java @@ -1,366 +1,366 @@ -/*
- * 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.sdkuilib.internal.repository;
-
-import com.android.sdklib.ISdkLog;
-import com.android.sdklib.SdkManager;
-import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.repository.ITaskFactory;
-import com.android.sdklib.internal.repository.DownloadCache;
-import com.android.sdklib.internal.repository.archives.Archive;
-import com.android.sdklib.internal.repository.packages.MockAddonPackage;
-import com.android.sdklib.internal.repository.packages.MockBrokenPackage;
-import com.android.sdklib.internal.repository.packages.MockPlatformPackage;
-import com.android.sdklib.internal.repository.packages.MockPlatformToolPackage;
-import com.android.sdklib.internal.repository.packages.MockToolPackage;
-import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.sources.SdkSource;
-import com.android.sdklib.internal.repository.sources.SdkSources;
-import com.android.sdkuilib.internal.repository.icons.ImageFactory;
-
-import org.eclipse.swt.widgets.Shell;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import junit.framework.TestCase;
-
-public class UpdaterLogicTest extends TestCase {
-
- private static class NullUpdaterData implements IUpdaterData {
-
- @Override
- public AvdManager getAvdManager() {
- return null;
- }
-
- @Override
- public ImageFactory getImageFactory() {
- return null;
- }
-
- @Override
- public ISdkLog getSdkLog() {
- return null;
- }
-
- @Override
- public DownloadCache getDownloadCache() {
- return null;
- }
-
- @Override
- public SdkManager getSdkManager() {
- return null;
- }
-
- @Override
- public SettingsController getSettingsController() {
- return null;
- }
-
- @Override
- public ITaskFactory getTaskFactory() {
- return null;
- }
-
- @Override
- public Shell getWindowShell() {
- return null;
- }
-
- }
-
- private static class MockUpdaterLogic extends SdkUpdaterLogic {
- private final Package[] mRemotePackages;
-
- public MockUpdaterLogic(IUpdaterData updaterData, Package[] remotePackages) {
- super(updaterData);
- mRemotePackages = remotePackages;
- }
-
- @Override
- protected void fetchRemotePackages(Collection<Package> remotePkgs,
- SdkSource[] remoteSources) {
- // Ignore remoteSources and instead uses the remotePackages list given to the
- // constructor.
- if (mRemotePackages != null) {
- remotePkgs.addAll(Arrays.asList(mRemotePackages));
- }
- }
- }
-
- /**
- * Addon packages depend on a base platform package.
- * This test checks that UpdaterLogic.findPlatformToolsDependency(...)
- * can find the base platform for a given addon.
- */
- public void testFindAddonDependency() {
- MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null);
-
- MockPlatformPackage p1 = new MockPlatformPackage(1, 1);
- MockPlatformPackage p2 = new MockPlatformPackage(2, 1);
-
- MockAddonPackage a1 = new MockAddonPackage(p1, 1);
- MockAddonPackage a2 = new MockAddonPackage(p2, 2);
-
- ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>();
- ArrayList<Archive> selected = new ArrayList<Archive>();
- ArrayList<Package> remote = new ArrayList<Package>();
-
- // a2 depends on p2, which is not in the locals
- Package[] localPkgs = { p1, a1 };
- ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);
-
- SdkSource[] sources = null;
-
- // a2 now depends on a "fake" archive info with no newArchive that wraps the missing
- // underlying platform.
- ArchiveInfo fai = mul.findPlatformDependency(a2, out, selected, remote, sources, locals);
- assertNotNull(fai);
- assertNull(fai.getNewArchive());
- assertTrue(fai.isRejected());
- assertEquals(0, out.size());
-
- // p2 is now selected, and should be scheduled for install in out
- Archive p2_archive = p2.getArchives()[0];
- selected.add(p2_archive);
- ArchiveInfo ai2 = mul.findPlatformDependency(a2, out, selected, remote, sources, locals);
- assertNotNull(ai2);
- assertSame(p2_archive, ai2.getNewArchive());
- assertEquals(1, out.size());
- assertSame(p2_archive, out.get(0).getNewArchive());
- }
-
- /**
- * Broken add-on packages require an exact platform package to be present or installed.
- * This tests checks that findExactApiLevelDependency() can find a base
- * platform package for a given broken add-on package.
- */
- public void testFindExactApiLevelDependency() {
- MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null);
-
- MockPlatformPackage p1 = new MockPlatformPackage(1, 1);
- MockPlatformPackage p2 = new MockPlatformPackage(2, 1);
-
- MockBrokenPackage a1 = new MockBrokenPackage(0, 1);
- MockBrokenPackage a2 = new MockBrokenPackage(0, 2);
-
- ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>();
- ArrayList<Archive> selected = new ArrayList<Archive>();
- ArrayList<Package> remote = new ArrayList<Package>();
-
- // a2 depends on p2, which is not in the locals
- Package[] localPkgs = { p1, a1 };
- ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);
-
- SdkSource[] sources = null;
-
- // a1 depends on p1, which can be found in the locals. p1 is already "installed"
- // so we donn't need to suggest it as a dependency to solve any problem.
- ArchiveInfo found = mul.findExactApiLevelDependency(
- a1, out, selected, remote, sources, locals);
- assertNull(found);
-
- // a2 now depends on a "fake" archive info with no newArchive that wraps the missing
- // underlying platform.
- found = mul.findExactApiLevelDependency(a2, out, selected, remote, sources, locals);
- assertNotNull(found);
- assertNull(found.getNewArchive());
- assertTrue(found.isRejected());
- assertEquals(0, out.size());
-
- // p2 is now selected, and should be scheduled for install in out
- Archive p2_archive = p2.getArchives()[0];
- selected.add(p2_archive);
- found = mul.findExactApiLevelDependency(a2, out, selected, remote, sources, locals);
- assertNotNull(found);
- assertSame(p2_archive, found.getNewArchive());
- assertEquals(1, out.size());
- assertSame(p2_archive, out.get(0).getNewArchive());
- }
-
- /**
- * Platform packages depend on a tool package.
- * This tests checks that UpdaterLogic.findToolsDependency() can find a base
- * tool package for a given platform package.
- */
- public void testFindPlatformDependency() {
- MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null);
-
- MockPlatformToolPackage pt1 = new MockPlatformToolPackage(1);
-
- MockToolPackage t1 = new MockToolPackage(1, 1);
- MockToolPackage t2 = new MockToolPackage(2, 1);
-
- MockPlatformPackage p2 = new MockPlatformPackage(2, 1, 2);
-
- ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>();
- ArrayList<Archive> selected = new ArrayList<Archive>();
- ArrayList<Package> remote = new ArrayList<Package>();
-
- // p2 depends on t2, which is not locally installed
- Package[] localPkgs = { t1, pt1 };
- ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);
-
- SdkSource[] sources = null;
-
- // p2 now depends on a "fake" archive info with no newArchive that wraps the missing
- // underlying tool
- ArchiveInfo fai = mul.findToolsDependency(p2, out, selected, remote, sources, locals);
- assertNotNull(fai);
- assertNull(fai.getNewArchive());
- assertTrue(fai.isRejected());
- assertEquals(0, out.size());
-
- // t2 is now selected and can be used as a dependency
- Archive t2_archive = t2.getArchives()[0];
- selected.add(t2_archive);
- ArchiveInfo ai2 = mul.findToolsDependency(p2, out, selected, remote, sources, locals);
- assertNotNull(ai2);
- assertSame(t2_archive, ai2.getNewArchive());
- assertEquals(1, out.size());
- assertSame(t2_archive, out.get(0).getNewArchive());
- }
-
- /**
- * Tool packages require a platform-tool package to be present or installed.
- * This tests checks that UpdaterLogic.findPlatformToolsDependency() can find a base
- * platform-tool package for a given tool package.
- */
- public void testFindPlatformToolDependency() {
- MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null);
-
- MockPlatformToolPackage t1 = new MockPlatformToolPackage(1);
- MockPlatformToolPackage t2 = new MockPlatformToolPackage(2);
-
- MockToolPackage p2 = new MockToolPackage(2, 2);
-
- ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>();
- ArrayList<Archive> selected = new ArrayList<Archive>();
- ArrayList<Package> remote = new ArrayList<Package>();
-
- // p2 depends on t2, which is not locally installed
- Package[] localPkgs = { t1 };
- ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);
-
- SdkSource[] sources = null;
-
- // p2 now depends on a "fake" archive info with no newArchive that wraps the missing
- // underlying tool
- ArchiveInfo fai = mul.findPlatformToolsDependency(
- p2, out, selected, remote, sources, locals);
- assertNotNull(fai);
- assertNull(fai.getNewArchive());
- assertTrue(fai.isRejected());
- assertEquals(0, out.size());
-
- // t2 is now selected and can be used as a dependency
- Archive t2_archive = t2.getArchives()[0];
- selected.add(t2_archive);
- ArchiveInfo ai2 = mul.findPlatformToolsDependency(
- p2, out, selected, remote, sources, locals);
- assertNotNull(ai2);
- assertSame(t2_archive, ai2.getNewArchive());
- assertEquals(1, out.size());
- assertSame(t2_archive, out.get(0).getNewArchive());
- }
-
- public void testComputeRevisionUpdate() {
- // Scenario:
- // - user has tools rev 7 installed + plat-tools rev 1 installed
- // - server has tools rev 8, depending on plat-tools rev 2
- // - server has tools rev 9, depending on plat-tools rev 3
- // - server has platform 9 that requires min-tools-rev 9
- //
- // If we do an update all, we want to the installer to pick up:
- // - the new platform 9
- // - the tools rev 9 (required by platform 9)
- // - the plat-tools rev 3 (required by tools rev 9)
-
- final MockPlatformToolPackage pt1 = new MockPlatformToolPackage(1);
- final MockPlatformToolPackage pt2 = new MockPlatformToolPackage(2);
- final MockPlatformToolPackage pt3 = new MockPlatformToolPackage(3);
-
- final MockToolPackage t7 = new MockToolPackage(7, 1 /*min-plat-tools*/);
- final MockToolPackage t8 = new MockToolPackage(8, 2 /*min-plat-tools*/);
- final MockToolPackage t9 = new MockToolPackage(9, 3 /*min-plat-tools*/);
-
- final MockPlatformPackage p9 = new MockPlatformPackage(9, 1, 9 /*min-tools*/);
-
- // Note: the mock updater logic gets the remotes packages from the array given
- // here and bypasses the source (to avoid fetching any actual URLs)
- MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(),
- new Package[] { t8, pt2, t9, pt3, p9 });
-
- SdkSources sources = new SdkSources();
- Package[] localPkgs = { t7, pt1 };
-
- List<ArchiveInfo> selected = mul.computeUpdates(
- null /*selectedArchives*/,
- sources,
- localPkgs,
- false /*includeObsoletes*/);
-
- assertEquals(
- "[Android SDK Platform-tools, revision 3, " +
- "Android SDK Tools, revision 9]",
- Arrays.toString(selected.toArray()));
-
- mul.addNewPlatforms(
- selected,
- sources,
- localPkgs,
- false /*includeObsoletes*/);
-
- assertEquals(
- "[Android SDK Platform-tools, revision 3, " +
- "Android SDK Tools, revision 9, " +
- "SDK Platform Android android-9, API 9, revision 1]",
- Arrays.toString(selected.toArray()));
-
- // Now try again but reverse the order of the remote package list.
-
- mul = new MockUpdaterLogic(new NullUpdaterData(),
- new Package[] { p9, t9, pt3, t8, pt2 });
-
- selected = mul.computeUpdates(
- null /*selectedArchives*/,
- sources,
- localPkgs,
- false /*includeObsoletes*/);
-
- assertEquals(
- "[Android SDK Platform-tools, revision 3, " +
- "Android SDK Tools, revision 9]",
- Arrays.toString(selected.toArray()));
-
- mul.addNewPlatforms(
- selected,
- sources,
- localPkgs,
- false /*includeObsoletes*/);
-
- assertEquals(
- "[Android SDK Platform-tools, revision 3, " +
- "Android SDK Tools, revision 9, " +
- "SDK Platform Android android-9, API 9, revision 1]",
- Arrays.toString(selected.toArray()));
- }
-}
+/* + * 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.sdkuilib.internal.repository; + +import com.android.sdklib.ISdkLog; +import com.android.sdklib.SdkManager; +import com.android.sdklib.internal.avd.AvdManager; +import com.android.sdklib.internal.repository.ITaskFactory; +import com.android.sdklib.internal.repository.DownloadCache; +import com.android.sdklib.internal.repository.archives.Archive; +import com.android.sdklib.internal.repository.packages.MockAddonPackage; +import com.android.sdklib.internal.repository.packages.MockBrokenPackage; +import com.android.sdklib.internal.repository.packages.MockPlatformPackage; +import com.android.sdklib.internal.repository.packages.MockPlatformToolPackage; +import com.android.sdklib.internal.repository.packages.MockToolPackage; +import com.android.sdklib.internal.repository.packages.Package; +import com.android.sdklib.internal.repository.sources.SdkSource; +import com.android.sdklib.internal.repository.sources.SdkSources; +import com.android.sdkuilib.internal.repository.icons.ImageFactory; + +import org.eclipse.swt.widgets.Shell; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import junit.framework.TestCase; + +public class UpdaterLogicTest extends TestCase { + + private static class NullUpdaterData implements IUpdaterData { + + @Override + public AvdManager getAvdManager() { + return null; + } + + @Override + public ImageFactory getImageFactory() { + return null; + } + + @Override + public ISdkLog getSdkLog() { + return null; + } + + @Override + public DownloadCache getDownloadCache() { + return null; + } + + @Override + public SdkManager getSdkManager() { + return null; + } + + @Override + public SettingsController getSettingsController() { + return null; + } + + @Override + public ITaskFactory getTaskFactory() { + return null; + } + + @Override + public Shell getWindowShell() { + return null; + } + + } + + private static class MockUpdaterLogic extends SdkUpdaterLogic { + private final Package[] mRemotePackages; + + public MockUpdaterLogic(IUpdaterData updaterData, Package[] remotePackages) { + super(updaterData); + mRemotePackages = remotePackages; + } + + @Override + protected void fetchRemotePackages(Collection<Package> remotePkgs, + SdkSource[] remoteSources) { + // Ignore remoteSources and instead uses the remotePackages list given to the + // constructor. + if (mRemotePackages != null) { + remotePkgs.addAll(Arrays.asList(mRemotePackages)); + } + } + } + + /** + * Addon packages depend on a base platform package. + * This test checks that UpdaterLogic.findPlatformToolsDependency(...) + * can find the base platform for a given addon. + */ + public void testFindAddonDependency() { + MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null); + + MockPlatformPackage p1 = new MockPlatformPackage(1, 1); + MockPlatformPackage p2 = new MockPlatformPackage(2, 1); + + MockAddonPackage a1 = new MockAddonPackage(p1, 1); + MockAddonPackage a2 = new MockAddonPackage(p2, 2); + + ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>(); + ArrayList<Archive> selected = new ArrayList<Archive>(); + ArrayList<Package> remote = new ArrayList<Package>(); + + // a2 depends on p2, which is not in the locals + Package[] localPkgs = { p1, a1 }; + ArchiveInfo[] locals = mul.createLocalArchives(localPkgs); + + SdkSource[] sources = null; + + // a2 now depends on a "fake" archive info with no newArchive that wraps the missing + // underlying platform. + ArchiveInfo fai = mul.findPlatformDependency(a2, out, selected, remote, sources, locals); + assertNotNull(fai); + assertNull(fai.getNewArchive()); + assertTrue(fai.isRejected()); + assertEquals(0, out.size()); + + // p2 is now selected, and should be scheduled for install in out + Archive p2_archive = p2.getArchives()[0]; + selected.add(p2_archive); + ArchiveInfo ai2 = mul.findPlatformDependency(a2, out, selected, remote, sources, locals); + assertNotNull(ai2); + assertSame(p2_archive, ai2.getNewArchive()); + assertEquals(1, out.size()); + assertSame(p2_archive, out.get(0).getNewArchive()); + } + + /** + * Broken add-on packages require an exact platform package to be present or installed. + * This tests checks that findExactApiLevelDependency() can find a base + * platform package for a given broken add-on package. + */ + public void testFindExactApiLevelDependency() { + MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null); + + MockPlatformPackage p1 = new MockPlatformPackage(1, 1); + MockPlatformPackage p2 = new MockPlatformPackage(2, 1); + + MockBrokenPackage a1 = new MockBrokenPackage(0, 1); + MockBrokenPackage a2 = new MockBrokenPackage(0, 2); + + ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>(); + ArrayList<Archive> selected = new ArrayList<Archive>(); + ArrayList<Package> remote = new ArrayList<Package>(); + + // a2 depends on p2, which is not in the locals + Package[] localPkgs = { p1, a1 }; + ArchiveInfo[] locals = mul.createLocalArchives(localPkgs); + + SdkSource[] sources = null; + + // a1 depends on p1, which can be found in the locals. p1 is already "installed" + // so we donn't need to suggest it as a dependency to solve any problem. + ArchiveInfo found = mul.findExactApiLevelDependency( + a1, out, selected, remote, sources, locals); + assertNull(found); + + // a2 now depends on a "fake" archive info with no newArchive that wraps the missing + // underlying platform. + found = mul.findExactApiLevelDependency(a2, out, selected, remote, sources, locals); + assertNotNull(found); + assertNull(found.getNewArchive()); + assertTrue(found.isRejected()); + assertEquals(0, out.size()); + + // p2 is now selected, and should be scheduled for install in out + Archive p2_archive = p2.getArchives()[0]; + selected.add(p2_archive); + found = mul.findExactApiLevelDependency(a2, out, selected, remote, sources, locals); + assertNotNull(found); + assertSame(p2_archive, found.getNewArchive()); + assertEquals(1, out.size()); + assertSame(p2_archive, out.get(0).getNewArchive()); + } + + /** + * Platform packages depend on a tool package. + * This tests checks that UpdaterLogic.findToolsDependency() can find a base + * tool package for a given platform package. + */ + public void testFindPlatformDependency() { + MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null); + + MockPlatformToolPackage pt1 = new MockPlatformToolPackage(1); + + MockToolPackage t1 = new MockToolPackage(1, 1); + MockToolPackage t2 = new MockToolPackage(2, 1); + + MockPlatformPackage p2 = new MockPlatformPackage(2, 1, 2); + + ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>(); + ArrayList<Archive> selected = new ArrayList<Archive>(); + ArrayList<Package> remote = new ArrayList<Package>(); + + // p2 depends on t2, which is not locally installed + Package[] localPkgs = { t1, pt1 }; + ArchiveInfo[] locals = mul.createLocalArchives(localPkgs); + + SdkSource[] sources = null; + + // p2 now depends on a "fake" archive info with no newArchive that wraps the missing + // underlying tool + ArchiveInfo fai = mul.findToolsDependency(p2, out, selected, remote, sources, locals); + assertNotNull(fai); + assertNull(fai.getNewArchive()); + assertTrue(fai.isRejected()); + assertEquals(0, out.size()); + + // t2 is now selected and can be used as a dependency + Archive t2_archive = t2.getArchives()[0]; + selected.add(t2_archive); + ArchiveInfo ai2 = mul.findToolsDependency(p2, out, selected, remote, sources, locals); + assertNotNull(ai2); + assertSame(t2_archive, ai2.getNewArchive()); + assertEquals(1, out.size()); + assertSame(t2_archive, out.get(0).getNewArchive()); + } + + /** + * Tool packages require a platform-tool package to be present or installed. + * This tests checks that UpdaterLogic.findPlatformToolsDependency() can find a base + * platform-tool package for a given tool package. + */ + public void testFindPlatformToolDependency() { + MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), null); + + MockPlatformToolPackage t1 = new MockPlatformToolPackage(1); + MockPlatformToolPackage t2 = new MockPlatformToolPackage(2); + + MockToolPackage p2 = new MockToolPackage(2, 2); + + ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>(); + ArrayList<Archive> selected = new ArrayList<Archive>(); + ArrayList<Package> remote = new ArrayList<Package>(); + + // p2 depends on t2, which is not locally installed + Package[] localPkgs = { t1 }; + ArchiveInfo[] locals = mul.createLocalArchives(localPkgs); + + SdkSource[] sources = null; + + // p2 now depends on a "fake" archive info with no newArchive that wraps the missing + // underlying tool + ArchiveInfo fai = mul.findPlatformToolsDependency( + p2, out, selected, remote, sources, locals); + assertNotNull(fai); + assertNull(fai.getNewArchive()); + assertTrue(fai.isRejected()); + assertEquals(0, out.size()); + + // t2 is now selected and can be used as a dependency + Archive t2_archive = t2.getArchives()[0]; + selected.add(t2_archive); + ArchiveInfo ai2 = mul.findPlatformToolsDependency( + p2, out, selected, remote, sources, locals); + assertNotNull(ai2); + assertSame(t2_archive, ai2.getNewArchive()); + assertEquals(1, out.size()); + assertSame(t2_archive, out.get(0).getNewArchive()); + } + + public void testComputeRevisionUpdate() { + // Scenario: + // - user has tools rev 7 installed + plat-tools rev 1 installed + // - server has tools rev 8, depending on plat-tools rev 2 + // - server has tools rev 9, depending on plat-tools rev 3 + // - server has platform 9 that requires min-tools-rev 9 + // + // If we do an update all, we want to the installer to pick up: + // - the new platform 9 + // - the tools rev 9 (required by platform 9) + // - the plat-tools rev 3 (required by tools rev 9) + + final MockPlatformToolPackage pt1 = new MockPlatformToolPackage(1); + final MockPlatformToolPackage pt2 = new MockPlatformToolPackage(2); + final MockPlatformToolPackage pt3 = new MockPlatformToolPackage(3); + + final MockToolPackage t7 = new MockToolPackage(7, 1 /*min-plat-tools*/); + final MockToolPackage t8 = new MockToolPackage(8, 2 /*min-plat-tools*/); + final MockToolPackage t9 = new MockToolPackage(9, 3 /*min-plat-tools*/); + + final MockPlatformPackage p9 = new MockPlatformPackage(9, 1, 9 /*min-tools*/); + + // Note: the mock updater logic gets the remotes packages from the array given + // here and bypasses the source (to avoid fetching any actual URLs) + MockUpdaterLogic mul = new MockUpdaterLogic(new NullUpdaterData(), + new Package[] { t8, pt2, t9, pt3, p9 }); + + SdkSources sources = new SdkSources(); + Package[] localPkgs = { t7, pt1 }; + + List<ArchiveInfo> selected = mul.computeUpdates( + null /*selectedArchives*/, + sources, + localPkgs, + false /*includeObsoletes*/); + + assertEquals( + "[Android SDK Platform-tools, revision 3, " + + "Android SDK Tools, revision 9]", + Arrays.toString(selected.toArray())); + + mul.addNewPlatforms( + selected, + sources, + localPkgs, + false /*includeObsoletes*/); + + assertEquals( + "[Android SDK Platform-tools, revision 3, " + + "Android SDK Tools, revision 9, " + + "SDK Platform Android android-9, API 9, revision 1]", + Arrays.toString(selected.toArray())); + + // Now try again but reverse the order of the remote package list. + + mul = new MockUpdaterLogic(new NullUpdaterData(), + new Package[] { p9, t9, pt3, t8, pt2 }); + + selected = mul.computeUpdates( + null /*selectedArchives*/, + sources, + localPkgs, + false /*includeObsoletes*/); + + assertEquals( + "[Android SDK Platform-tools, revision 3, " + + "Android SDK Tools, revision 9]", + Arrays.toString(selected.toArray())); + + mul.addNewPlatforms( + selected, + sources, + localPkgs, + false /*includeObsoletes*/); + + assertEquals( + "[Android SDK Platform-tools, revision 3, " + + "Android SDK Tools, revision 9, " + + "SDK Platform Android android-9, API 9, revision 1]", + Arrays.toString(selected.toArray())); + } +} |