am ba29296c: Merge change 8438 into donut

Merge commit 'ba29296c080103aa945f83e795c97bd45ed06b3e'

* commit 'ba29296c080103aa945f83e795c97bd45ed06b3e':
  Add a checkbox to only show packages that are updates or new.
This commit is contained in:
Android (Google) Code Review
2009-07-23 23:59:13 -07:00
committed by Android Git Automerger
13 changed files with 202 additions and 167 deletions

View File

@@ -44,7 +44,6 @@ public final class SdkManager {
public final static String PROP_VERSION_SDK = "ro.build.version.sdk";
public final static String PROP_VERSION_CODENAME = "ro.build.version.codename";
public final static String PROP_VERSION_RELEASE = "ro.build.version.release";
public final static String PROP_VERSION_REVISION = "ro.build.version.incremental";
private final static String ADDON_NAME = "name";
private final static String ADDON_VENDOR = "vendor";
@@ -62,9 +61,6 @@ public final class SdkManager {
private final static Pattern PATTERN_LIB_DATA = Pattern.compile(
"^([a-zA-Z0-9._-]+\\.jar);(.*)$", Pattern.CASE_INSENSITIVE);
private final static Pattern PATTERN_LOCAL_BUILD_PATTERN = Pattern.compile(
"^\\S+\\.\\S+\\.(\\d+)\\.\\d+$");
// usb ids are 16-bit hexadecimal values.
private final static Pattern PATTERN_USB_IDS = Pattern.compile(
"^0x[a-f0-9]{4}$", Pattern.CASE_INSENSITIVE);
@@ -312,45 +308,7 @@ public final class SdkManager {
}
int revision = 1;
stringValue = map.get(PROP_VERSION_REVISION);
if (stringValue == null) {
if (log != null) {
log.error(null,
"Ignoring platform '%1$s': %2$s is missing from '%3$s'",
platform.getName(), PROP_VERSION_REVISION,
SdkConstants.FN_BUILD_PROP);
}
return null;
} else {
try {
revision = Integer.parseInt(stringValue);
} catch (NumberFormatException e) {
// looks like the revision does not parse to a number.
// we look if it's a local build in the format
// <buildtype>.<username>.<date>.<time>
Matcher m = PATTERN_LOCAL_BUILD_PATTERN.matcher(stringValue);
boolean valid = false;
if (m.matches()) {
String date = m.group(1);
try {
revision = Integer.parseInt(date);
valid = true;
} catch (NumberFormatException e2) {
// do nothing, we'll display an error and return below
}
}
if (valid == false) {
if (log != null) {
log.error(null,
"Ignoring platform '%1$s': %2$s is not a valid number in %3$s.",
platform.getName(), PROP_VERSION_SDK,
SdkConstants.FN_BUILD_PROP);
}
return null;
}
}
}
// FIXME: properly parse the platform revision.
// api number and name look valid, perform a few more checks
if (checkPlatformContent(platform, log) == false) {

View File

@@ -250,31 +250,17 @@ public class AddonPackage extends Package {
return null;
}
/**
* Computes whether the given addon package is a suitable update for the current package.
* The base method checks the class type.
* The addon package also tests that the name+vendor is the same and
* the revision number is greater.
* <p/>
* An update is just that: a new package that supersedes the current one. If the new
* package has the same revision as the current one, it's not an update.
*
* @param replacementPackage The potential replacement package.
* @return True if the replacement package is a suitable update for this one.
*/
@Override
public boolean canBeUpdatedBy(Package replacementPackage) {
if (!super.canBeUpdatedBy(replacementPackage)) {
return false;
public boolean sameItemAs(Package pkg) {
if (pkg instanceof AddonPackage) {
AddonPackage newPkg = (AddonPackage)pkg;
// check they are the same add-on.
return getName().equals(newPkg.getName()) &&
getVendor().equals(newPkg.getVendor()) &&
getVersion().equals(newPkg.getVersion());
}
AddonPackage newPkg = (AddonPackage) replacementPackage;
String thisId = getName() + "+" + getVendor(); //$NON-NLS-1$
String newId = newPkg.getName() + "+" + newPkg.getVendor(); //$NON-NLS-1$
return thisId.equalsIgnoreCase(newId) &&
mVersion.equals(newPkg.getVersion()) &&
newPkg.getRevision() > this.getRevision();
return false;
}
}

View File

@@ -135,26 +135,9 @@ public class DocPackage extends Package {
return new File(osSdkRoot, SdkConstants.FD_DOCS);
}
/**
* Computes whether the given doc package is a suitable update for the current package.
* The base method checks the class type.
* The doc package also tests the API level and revision number: the revision number must
* always be bumped. The API level can be the same or greater.
* <p/>
* An update is just that: a new package that supersedes the current one. If the new
* package has the same revision as the current one, it's not an update.
*
* @param replacementPackage The potential replacement package.
* @return True if the replacement package is a suitable update for this one.
*/
@Override
public boolean canBeUpdatedBy(Package replacementPackage) {
if (!super.canBeUpdatedBy(replacementPackage)) {
return false;
}
DocPackage newPkg = (DocPackage) replacementPackage;
return newPkg.getRevision() > this.getRevision() &&
newPkg.getVersion().equals(this.getVersion());
public boolean sameItemAs(Package pkg) {
// only one doc package so any doc package is the same item.
return pkg instanceof DocPackage;
}
}

View File

@@ -141,25 +141,9 @@ public class ExtraPackage extends Package {
return new File(osSdkRoot, getPath());
}
/**
* Computes whether the given extra package is a suitable update for the current package.
* The base method checks the class type.
* The tools package also tests that the revision number is greater and the path is the
* same.
* <p/>
* An update is just that: a new package that supersedes the current one. If the new
* package has the same revision as the current one, it's not an update.
*
* @param replacementPackage The potential replacement package.
* @return True if the replacement package is a suitable update for this one.
*/
@Override
public boolean canBeUpdatedBy(Package replacementPackage) {
if (!super.canBeUpdatedBy(replacementPackage)) {
return false;
}
ExtraPackage newPkg = (ExtraPackage) replacementPackage;
return newPkg.getRevision() > this.getRevision() && newPkg.getPath().equals(this.getPath());
public boolean sameItemAs(Package pkg) {
// Extra packages are similar if they have the same path.
return pkg instanceof ExtraPackage && ((ExtraPackage)pkg).mPath.equals(mPath);
}
}

View File

@@ -55,6 +55,21 @@ public abstract class Package implements IDescription {
private final Archive[] mArchives;
private final RepoSource mSource;
/**
* Enum for the result of {@link Package#canBeUpdatedBy(Package)}. This used so that we can
* differentiate between a package that is totally incompatible, and one that is the same item
* but just not an update.
* @see #canBeUpdatedBy(Package)
*/
public static enum UpdateInfo {
/** Means that the 2 packages are not the same thing */
INCOMPATIBLE,
/** Means that the 2 packages are the same thing but one does not upgrade the other */
NOT_UPDATE,
/** Means that the 2 packages are the same thing, and one is the upgrade of the other */
UPDATE;
}
/**
* Creates a new package from the attributes and elements of the given XML node.
* <p/>
@@ -245,6 +260,20 @@ public abstract class Package implements IDescription {
return mArchives;
}
/**
* Returns whether the {@link Package} has at least one {@link Archive} compatible with
* the host platform.
*/
public boolean hasCompatibleArchive() {
for (Archive archive : mArchives) {
if (archive.isCompatible()) {
return true;
}
}
return false;
}
/**
* Returns a short description for an {@link IDescription}.
* Can be empty but not null.
@@ -279,21 +308,44 @@ public abstract class Package implements IDescription {
public abstract File getInstallFolder(
String osSdkRoot, String suggestedDir, SdkManager sdkManager);
/**
* Returns whether the give package represents the same item as the current package.
* <p/>
* Two packages are considered the same if they represent the same thing, except for the
* revision number.
* @param pkg the package to compare
* @return true if the item
*/
public abstract boolean sameItemAs(Package pkg);
/**
* Computes whether the given package is a suitable update for the current package.
* The base class method only checks that the {@link Package} class type is the same.
* Derived classes must add more specific checks, including the revision number.
* <p/>
* An update is just that: a new package that supersedes the current one. If the new
* package has the same revision as the current one, it's not an update.
* package does not represent the same item or if it has the same or lower revision as the
* current one, it's not an update.
*
* @param replacementPackage The potential replacement package.
* @return True if the replacement package is a suitable update for this one.
* @return
*
* @see #sameItemAs(Package)
*/
public boolean canBeUpdatedBy(Package replacementPackage) {
return replacementPackage != null &&
replacementPackage.getClass() == this.getClass() &&
replacementPackage.getRevision() > this.getRevision();
}
public final UpdateInfo canBeUpdatedBy(Package replacementPackage) {
if (replacementPackage == null) {
return UpdateInfo.INCOMPATIBLE;
}
// check they are the same item.
if (sameItemAs(replacementPackage) == false) {
return UpdateInfo.INCOMPATIBLE;
}
// check revision number
if (replacementPackage.getRevision() > this.getRevision()) {
return UpdateInfo.UPDATE;
}
// not an upgrade but not incompatible either.
return UpdateInfo.NOT_UPDATE;
}
}

View File

@@ -153,27 +153,15 @@ public class PlatformPackage extends Package {
return folder;
}
/**
* Computes whether the given platform package is a suitable update for the current package.
* The base method checks the class type.
* The platform package also tests that the version and API level are the same and
* the revision number is greater
* <p/>
* An update is just that: a new package that supersedes the current one. If the new
* package has the same revision as the current one, it's not an update.
*
* @param replacementPackage The potential replacement package.
* @return True if the replacement package is a suitable update for this one.
*/
@Override
public boolean canBeUpdatedBy(Package replacementPackage) {
if (!super.canBeUpdatedBy(replacementPackage)) {
return false;
public boolean sameItemAs(Package pkg) {
if (pkg instanceof PlatformPackage) {
PlatformPackage newPkg = (PlatformPackage)pkg;
// check they are the same platform.
return newPkg.getVersion().equals(this.getVersion());
}
PlatformPackage newPkg = (PlatformPackage) replacementPackage;
return newPkg.getVersionName().equalsIgnoreCase(this.getVersionName()) &&
newPkg.getVersion().equals(this.getVersion()) &&
newPkg.getRevision() > this.getRevision();
return false;
}
}

View File

@@ -98,24 +98,9 @@ public class ToolPackage extends Package {
return new File(osSdkRoot, SdkConstants.FD_TOOLS);
}
/**
* Computes whether the given tools package is a suitable update for the current package.
* The base method checks the class type.
* The tools package also tests that the revision number is greater.
* <p/>
* An update is just that: a new package that supersedes the current one. If the new
* package has the same revision as the current one, it's not an update.
*
* @param replacementPackage The potential replacement package.
* @return True if the replacement package is a suitable update for this one.
*/
@Override
public boolean canBeUpdatedBy(Package replacementPackage) {
if (!super.canBeUpdatedBy(replacementPackage)) {
return false;
}
ToolPackage newPkg = (ToolPackage) replacementPackage;
return newPkg.getRevision() > this.getRevision();
public boolean sameItemAs(Package pkg) {
// only one tool package so any tool package is the same item.
return pkg instanceof ToolPackage;
}
}

View File

@@ -25,11 +25,13 @@ import java.util.Properties;
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$
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$
public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$
/** Setting to force using http:// instead of https:// connections. Type: Boolean. */
public static final String KEY_FORCE_HTTP = "sdkman.force.http"; //$NON-NLS-1$
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. */
public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$
/** Loads settings from the given {@link Properties} container and update the page UI. */
public abstract void loadSettings(Properties in_settings);

View File

@@ -17,7 +17,6 @@
package com.android.sdkuilib.internal.repository;
import com.android.sdklib.internal.repository.IDescription;
import com.android.sdklib.internal.repository.LocalSdkParser;
import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.RepoSource;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
@@ -95,17 +94,7 @@ class LocalSdkAdapter {
*/
public Object[] getElements(Object inputElement) {
if (inputElement == LocalSdkAdapter.this) {
LocalSdkParser parser = mUpdaterData.getLocalSdkParser();
Package[] packages = parser.getPackages();
if (packages == null) {
// load on demand the first time
packages = parser.parseSdk(
mUpdaterData.getOsSdkRoot(),
mUpdaterData.getSdkManager(),
mUpdaterData.getSdkLog());
}
Package[] packages = mUpdaterData.getInstalledPackage();
if (packages != null) {
return packages;

View File

@@ -58,6 +58,7 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
private CheckboxTreeViewer mTreeViewerSources;
private Tree mTreeSources;
private TreeColumn mColumnSource;
private Button mUpdateOnlyCheckBox;
private Group mDescriptionContainer;
private Button mAddSiteButton;
private Button mDeleteSiteButton;
@@ -67,6 +68,7 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
private Label mDescriptionLabel;
/**
* Create the composite.
* @param parent The parent of the composite.
@@ -104,6 +106,28 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
mColumnSource.setWidth(289);
mColumnSource.setText("Sources, Packages and Archives");
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayoutData(
new GridData(SWT.FILL, SWT.BEGINNING, false, false, 5, 1));
GridLayout gl;
composite.setLayout(gl = new GridLayout(2, false));
gl.marginHeight = gl.marginWidth = 0;
// add an empty composite
Composite spacer = new Composite(composite, SWT.NONE);
GridData gd;
spacer.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
gd.heightHint = 0;
mUpdateOnlyCheckBox = new Button(composite, SWT.CHECK);
mUpdateOnlyCheckBox.setText("Display updates only");
mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
onShowUpdateOnly(); //$hide$
}
});
mDescriptionContainer = new Group(parent, SWT.NONE);
mDescriptionContainer.setLayout(new GridLayout(1, false));
mDescriptionContainer.setText("Description");
@@ -263,6 +287,11 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
}
}
private void onShowUpdateOnly() {
mUpdaterData.getSettingsController().setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());
mTreeViewerSources.refresh();
}
private void onInstallSelectedArchives() {
ArrayList<Archive> archives = new ArrayList<Archive>();
for (Object element : mTreeViewerSources.getCheckedElements()) {

View File

@@ -22,6 +22,7 @@ import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.RepoSource;
import com.android.sdklib.internal.repository.Package.UpdateInfo;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import org.eclipse.jface.viewers.IContentProvider;
@@ -31,6 +32,8 @@ import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;
import java.util.ArrayList;
/**
* A list of sdk-repository sources.
*
@@ -149,6 +152,10 @@ public class RepoSourcesAdapter {
packages = source.getPackages();
}
if (packages != null) {
// filter out only the packages that are new/upgrade.
if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {
return filteredPackages(packages);
}
return packages;
} else if (source.getFetchError() != null) {
// Return a dummy entry to display the fetch error
@@ -188,4 +195,46 @@ public class RepoSourcesAdapter {
}
}
/**
* Filters out a list of remote packages to only keep the ones that are either new or
* updates of existing package.
* @param remotePackages the list of packages to filter.
* @return a non null (but maybe empty) list of new or update packages.
*/
private Object[] filteredPackages(Package[] remotePackages) {
// get the installed packages
Package[] installedPackages = mUpdaterData.getInstalledPackage();
ArrayList<Package> filteredList = new ArrayList<Package>();
// for each remote packages, we look for an existing version.
// If no existing version -> add to the list
// if existing version but with older revision -> add it to the list
for (Package remotePkg : remotePackages) {
boolean newPkg = true;
// For all potential packages, we also make sure that there's an archive for the current
// platform, or we simply skip them.
if (remotePkg.hasCompatibleArchive()) {
for (Package installedPkg : installedPackages) {
UpdateInfo info = installedPkg.canBeUpdatedBy(remotePkg);
if (info == UpdateInfo.UPDATE) {
filteredList.add(remotePkg);
newPkg = false;
break; // there shouldn't be 2 revision of the same package
} else if (info != UpdateInfo.INCOMPATIBLE) {
newPkg = false;
break; // there shouldn't be 2 revision of the same package
}
}
// if we have not found the same package, then we add it (it's a new package)
if (newPkg) {
filteredList.add(remotePkg);
}
}
}
return filteredList.toArray();
}
}

View File

@@ -45,6 +45,18 @@ public class SettingsController {
return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));
}
public boolean getShowUpdateOnly() {
String value = mProperties.getProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY);
if (value == null) {
return true;
}
return Boolean.parseBoolean(value);
}
public void setShowUpdateOnly(boolean enabled) {
mProperties.setProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY, Boolean.toString(enabled));
}
//--- Controller methods -------------
/**

View File

@@ -30,6 +30,7 @@ import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.RepoSource;
import com.android.sdklib.internal.repository.RepoSources;
import com.android.sdklib.internal.repository.ToolPackage;
import com.android.sdklib.internal.repository.Package.UpdateInfo;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import org.eclipse.swt.widgets.Shell;
@@ -226,6 +227,22 @@ class UpdaterData {
}
}
/**
* Returns the list of installed packages, parsing them if this has not yet been done.
*/
public Package[] getInstalledPackage() {
LocalSdkParser parser = getLocalSdkParser();
Package[] packages = parser.getPackages();
if (packages == null) {
// load on demand the first time
packages = parser.parseSdk(getOsSdkRoot(), getSdkManager(), getSdkLog());
}
return packages;
}
/**
* Notify the listeners ({@link ISdkListener}) that the SDK was reloaded.
* <p/>This can be called from any thread.
@@ -491,7 +508,8 @@ class UpdaterData {
// local package?
for (Archive availArchive : list) {
if (localPkg.canBeUpdatedBy(availArchive.getParentPackage())) {
UpdateInfo info = localPkg.canBeUpdatedBy(availArchive.getParentPackage());
if (info == UpdateInfo.UPDATE) {
// Found one!
result.put(availArchive, localArchive);
break;