\r
import java.io.File;\r
import java.util.ArrayList;\r
+import java.util.Arrays;\r
import java.util.Map;\r
import java.util.Properties;\r
\r
public String getDescription() {\r
return mDescription;\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = 1;\r
+ result = prime * result + ((mDescription == null) ? 0 : mDescription.hashCode());\r
+ result = prime * result + ((mName == null) ? 0 : mName.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (obj == null) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof Lib)) {\r
+ return false;\r
+ }\r
+ Lib other = (Lib) obj;\r
+ if (mDescription == null) {\r
+ if (other.mDescription != null) {\r
+ return false;\r
+ }\r
+ } else if (!mDescription.equals(other.mDescription)) {\r
+ return false;\r
+ }\r
+ if (mName == null) {\r
+ if (other.mName != null) {\r
+ return false;\r
+ }\r
+ } else if (!mName.equals(other.mName)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
\r
private final Lib[] mLibs;\r
\r
return false;\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result + ((mLayoutlibVersion == null) ? 0 : mLayoutlibVersion.hashCode());\r
+ result = prime * result + Arrays.hashCode(mLibs);\r
+ result = prime * result + ((mName == null) ? 0 : mName.hashCode());\r
+ result = prime * result + ((mVendor == null) ? 0 : mVendor.hashCode());\r
+ result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof AddonPackage)) {\r
+ return false;\r
+ }\r
+ AddonPackage other = (AddonPackage) obj;\r
+ if (mLayoutlibVersion == null) {\r
+ if (other.mLayoutlibVersion != null) {\r
+ return false;\r
+ }\r
+ } else if (!mLayoutlibVersion.equals(other.mLayoutlibVersion)) {\r
+ return false;\r
+ }\r
+ if (!Arrays.equals(mLibs, other.mLibs)) {\r
+ return false;\r
+ }\r
+ if (mName == null) {\r
+ if (other.mName != null) {\r
+ return false;\r
+ }\r
+ } else if (!mName.equals(other.mName)) {\r
+ return false;\r
+ }\r
+ if (mVendor == null) {\r
+ if (other.mVendor != null) {\r
+ return false;\r
+ }\r
+ } else if (!mVendor.equals(other.mVendor)) {\r
+ return false;\r
+ }\r
+ if (mVersion == null) {\r
+ if (other.mVersion != null) {\r
+ return false;\r
+ }\r
+ } else if (!mVersion.equals(other.mVersion)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
}\r
return 0;\r
}\r
+\r
+ /**\r
+ * Note: An {@link Archive}'s hash code does NOT depend on the parent {@link Package} hash code.\r
+ * <p/>\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = 1;\r
+ result = prime * result + ((mArch == null) ? 0 : mArch.hashCode());\r
+ result = prime * result + ((mChecksum == null) ? 0 : mChecksum.hashCode());\r
+ result = prime * result + ((mChecksumType == null) ? 0 : mChecksumType.hashCode());\r
+ result = prime * result + (mIsLocal ? 1231 : 1237);\r
+ result = prime * result + ((mLocalOsPath == null) ? 0 : mLocalOsPath.hashCode());\r
+ result = prime * result + ((mOs == null) ? 0 : mOs.hashCode());\r
+ result = prime * result + (int) (mSize ^ (mSize >>> 32));\r
+ result = prime * result + ((mUrl == null) ? 0 : mUrl.hashCode());\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Note: An {@link Archive}'s equality does NOT depend on the parent {@link Package} equality.\r
+ * <p/>\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (obj == null) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof Archive)) {\r
+ return false;\r
+ }\r
+ Archive other = (Archive) obj;\r
+ if (mArch == null) {\r
+ if (other.mArch != null) {\r
+ return false;\r
+ }\r
+ } else if (!mArch.equals(other.mArch)) {\r
+ return false;\r
+ }\r
+ if (mChecksum == null) {\r
+ if (other.mChecksum != null) {\r
+ return false;\r
+ }\r
+ } else if (!mChecksum.equals(other.mChecksum)) {\r
+ return false;\r
+ }\r
+ if (mChecksumType == null) {\r
+ if (other.mChecksumType != null) {\r
+ return false;\r
+ }\r
+ } else if (!mChecksumType.equals(other.mChecksumType)) {\r
+ return false;\r
+ }\r
+ if (mIsLocal != other.mIsLocal) {\r
+ return false;\r
+ }\r
+ if (mLocalOsPath == null) {\r
+ if (other.mLocalOsPath != null) {\r
+ return false;\r
+ }\r
+ } else if (!mLocalOsPath.equals(other.mLocalOsPath)) {\r
+ return false;\r
+ }\r
+ if (mOs == null) {\r
+ if (other.mOs != null) {\r
+ return false;\r
+ }\r
+ } else if (!mOs.equals(other.mOs)) {\r
+ return false;\r
+ }\r
+ if (mSize != other.mSize) {\r
+ return false;\r
+ }\r
+ if (mUrl == null) {\r
+ if (other.mUrl != null) {\r
+ return false;\r
+ }\r
+ } else if (!mUrl.equals(other.mUrl)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
// not an upgrade but not incompatible either.\r
return UpdateInfo.NOT_UPDATE;\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof DocPackage)) {\r
+ return false;\r
+ }\r
+ DocPackage other = (DocPackage) obj;\r
+ if (mVersion == null) {\r
+ if (other.mVersion != null) {\r
+ return false;\r
+ }\r
+ } else if (!mVersion.equals(other.mVersion)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
\r
import java.io.File;\r
import java.util.ArrayList;\r
+import java.util.Arrays;\r
import java.util.Map;\r
import java.util.Properties;\r
import java.util.regex.Pattern;\r
\r
return null;\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result + mMinApiLevel;\r
+ result = prime * result + ((mPath == null) ? 0 : mPath.hashCode());\r
+ result = prime * result + Arrays.hashCode(mProjectFiles);\r
+ result = prime * result + ((mVendor == null) ? 0 : mVendor.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof ExtraPackage)) {\r
+ return false;\r
+ }\r
+ ExtraPackage other = (ExtraPackage) obj;\r
+ if (mMinApiLevel != other.mMinApiLevel) {\r
+ return false;\r
+ }\r
+ if (mPath == null) {\r
+ if (other.mPath != null) {\r
+ return false;\r
+ }\r
+ } else if (!mPath.equals(other.mPath)) {\r
+ return false;\r
+ }\r
+ if (!Arrays.equals(mProjectFiles, other.mProjectFiles)) {\r
+ return false;\r
+ }\r
+ if (mVendor == null) {\r
+ if (other.mVendor != null) {\r
+ return false;\r
+ }\r
+ } else if (!mVendor.equals(other.mVendor)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
public Pair<Integer, Integer> getLayoutlibVersion() {\r
return mLayoutlibVersion;\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = 1;\r
+ result = prime * result + ((mLayoutlibVersion == null) ? 0 : mLayoutlibVersion.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (obj == null) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof LayoutlibVersionMixin)) {\r
+ return false;\r
+ }\r
+ LayoutlibVersionMixin other = (LayoutlibVersionMixin) obj;\r
+ if (mLayoutlibVersion == null) {\r
+ if (other.mLayoutlibVersion != null) {\r
+ return false;\r
+ }\r
+ } else if (!mLayoutlibVersion.equals(other.mLayoutlibVersion)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(getMinToolsRevision()));\r
}\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result + mMinToolsRevision;\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof MinToolsPackage)) {\r
+ return false;\r
+ }\r
+ MinToolsPackage other = (MinToolsPackage) obj;\r
+ if (mMinToolsRevision != other.mMinToolsRevision) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
\r
import java.io.File;\r
import java.util.ArrayList;\r
+import java.util.Arrays;\r
import java.util.Map;\r
import java.util.Properties;\r
\r
}\r
\r
/**\r
+ * A package is local (that is 'installed locally') if it contains a single\r
+ * archive that is local. If not local, it's a remote package, only available\r
+ * on a remote source for download and installation.\r
+ */\r
+ public boolean isLocal() {\r
+ return mArchives.length == 1 && mArchives[0].isLocal();\r
+ }\r
+\r
+ /**\r
* Computes a potential installation folder if an archive of this package were\r
* to be installed right away in the given SDK root.\r
* <p/>\r
return sb.toString();\r
}\r
\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = 1;\r
+ result = prime * result + Arrays.hashCode(mArchives);\r
+ result = prime * result + ((mObsolete == null) ? 0 : mObsolete.hashCode());\r
+ result = prime * result + mRevision;\r
+ result = prime * result + ((mSource == null) ? 0 : mSource.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (obj == null) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof Package)) {\r
+ return false;\r
+ }\r
+ Package other = (Package) obj;\r
+ if (!Arrays.equals(mArchives, other.mArchives)) {\r
+ return false;\r
+ }\r
+ if (mObsolete == null) {\r
+ if (other.mObsolete != null) {\r
+ return false;\r
+ }\r
+ } else if (!mObsolete.equals(other.mObsolete)) {\r
+ return false;\r
+ }\r
+ if (mRevision != other.mRevision) {\r
+ return false;\r
+ }\r
+ if (mSource == null) {\r
+ if (other.mSource != null) {\r
+ return false;\r
+ }\r
+ } else if (!mSource.equals(other.mSource)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
\r
return false;\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result +\r
+ ((mLayoutlibVersion == null) ? 0 : mLayoutlibVersion.hashCode());\r
+ result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());\r
+ result = prime * result + ((mVersionName == null) ? 0 : mVersionName.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof PlatformPackage)) {\r
+ return false;\r
+ }\r
+ PlatformPackage other = (PlatformPackage) obj;\r
+ if (mLayoutlibVersion == null) {\r
+ if (other.mLayoutlibVersion != null) {\r
+ return false;\r
+ }\r
+ } else if (!mLayoutlibVersion.equals(other.mLayoutlibVersion)) {\r
+ return false;\r
+ }\r
+ if (mVersion == null) {\r
+ if (other.mVersion != null) {\r
+ return false;\r
+ }\r
+ } else if (!mVersion.equals(other.mVersion)) {\r
+ return false;\r
+ }\r
+ if (mVersionName == null) {\r
+ if (other.mVersionName != null) {\r
+ return false;\r
+ }\r
+ } else if (!mVersionName.equals(other.mVersionName)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
// get the return code from the process\r
return process.waitFor();\r
}\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result + mMinPlatformToolsRevision;\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (!(obj instanceof ToolPackage)) {\r
+ return false;\r
+ }\r
+ ToolPackage other = (ToolPackage) obj;\r
+ if (mMinPlatformToolsRevision != other.mMinPlatformToolsRevision) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
import com.android.sdklib.internal.repository.IPackageVersion;
import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.internal.repository.LocalSdkParser;
import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.SdkSource;
import com.android.sdklib.internal.repository.Package.UpdateInfo;
* Interface for the callback called by
* {@link PackageLoader#loadPackages(ISourceLoadedCallback)}.
* <p/>
- * After processing each source, the package loader calls {@link #onSourceLoaded(List)}
- * with the list of package items found in that source. The client should process that
- * list as it want, typically by accumulating the package items in a list of its own.
- * By returning true from {@link #onSourceLoaded(List)}, the client tells the loader to
+ * After processing each source, the package loader calls {@link #onUpdateSource}
+ * with the list of packages found in that source.
+ * By returning true from {@link #onUpdateSource}, the client tells the loader to
* continue and process the next source. By returning false, it tells to stop loading.
* <p/>
* The {@link #onLoadCompleted()} method is guaranteed to be called at the end, no
public interface ISourceLoadedCallback {
/**
* After processing each source, the package loader calls this method with the
- * list of package items found in that source. The client should process that
- * list as it want, typically by accumulating the package items in a list of its own.
- * By returning true from {@link #onSourceLoaded(List)}, the client tells the loader to
- * continue and process the next source. By returning false, it tells to stop loading.
+ * list of packages found in that source.
+ * By returning true from {@link #onUpdateSource}, the client tells
+ * the loader to continue and process the next source.
+ * By returning false, it tells to stop loading.
* <p/>
- * <em>Important</em>: This method is called from a sub-thread, so clients who try
- * to access any UI widgets must wrap their calls into {@link Display#syncExec(Runnable)}
- * or {@link Display#asyncExec(Runnable)}.
+ * <em>Important</em>: This method is called from a sub-thread, so clients which
+ * try to access any UI widgets must wrap their calls into
+ * {@link Display#syncExec(Runnable)} or {@link Display#asyncExec(Runnable)}.
*
- * @param pkgItems All the package items loaded from the last processed source.
- * This is a copy and the client can hold to this list or modify it in any way.
+ * @param packages All the packages loaded from the source. Never null.
* @return True if the load operation should continue, false if it should stop.
*/
- public boolean onSourceLoaded(List<PkgItem> pkgItems);
+ public boolean onUpdateSource(SdkSource source, Package[] packages);
/**
* This method is guaranteed to be called at the end, no matter how the
}
// get local packages and offer them to the callback
- List<PkgItem> localPkgItems = loadLocalPackages();
- if (!localPkgItems.isEmpty()) {
- if (!sourceLoadedCallback.onSourceLoaded(localPkgItems)) {
- return;
- }
+ Package[] localPkgs = mUpdaterData.getInstalledPackages();
+ if (localPkgs == null) {
+ localPkgs = new Package[0];
+ }
+ if (!sourceLoadedCallback.onUpdateSource(null, localPkgs)) {
+ return;
}
- final int[] numPackages = { localPkgItems.size() };
+ final int[] numPackages = { localPkgs == null ? 0 : localPkgs.length };
// get remote packages
final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();
continue;
}
- List<PkgItem> sourcePkgItems = new ArrayList<PkgItem>();
- for(Package pkg : pkgs) {
- PkgItem pi = new PkgItem(pkg, PkgState.NEW);
- sourcePkgItems.add(pi);
- }
-
- numPackages[0] += sourcePkgItems.size();
+ numPackages[0] += pkgs.length;
// Notify the callback a new source has finished loading.
// If the callback requests so, stop right away.
- if (!sourceLoadedCallback.onSourceLoaded(sourcePkgItems)) {
+ if (!sourceLoadedCallback.onUpdateSource(source, pkgs)) {
return;
}
}
}
/**
- * Internal method that returns all installed packages from the {@link LocalSdkParser}
- * associated with the {@link UpdaterData}.
- * <p/>
- * Note that the {@link LocalSdkParser} maintains a cache, so callers need to clear
- * it if they know they changed the local installation.
- *
- * @return A new list of {@link PkgItem}. May be empty but never null.
- */
- private List<PkgItem> loadLocalPackages() {
- List<PkgItem> pkgItems = new ArrayList<PkgItem>();
-
- for (Package pkg : mUpdaterData.getInstalledPackages()) {
- PkgItem pi = new PkgItem(pkg, PkgState.INSTALLED);
- pkgItems.add(pi);
- }
-
- return pkgItems;
- }
-
- /**
* Load packages, source by source using {@link #loadPackages(ISourceLoadedCallback)},
* and executes the given {@link IAutoInstallTask} on the current package list.
* That is for each package known, the install task is queried to find if
public void loadPackagesWithInstallTask(final IAutoInstallTask installTask) {
loadPackages(new ISourceLoadedCallback() {
- public boolean onSourceLoaded(List<PkgItem> pkgItems) {
- for (PkgItem item : pkgItems) {
- Package acceptedPkg = null;
- switch(item.getState()) {
- case NEW:
- if (installTask.acceptPackage(item.getMainPackage())) {
- acceptedPkg = item.getMainPackage();
- }
- if (item.hasUpdatePkg() && installTask.acceptPackage(item.getUpdatePkg())) {
- acceptedPkg = item.getUpdatePkg();
- }
- break;
- case INSTALLED:
- if (installTask.acceptPackage(item.getMainPackage())) {
+ public boolean onUpdateSource(SdkSource source, Package[] packages) {
+ for (Package pkg : packages) {
+ if (pkg.isLocal()) {
+ // This is a local (aka installed) package
+ if (installTask.acceptPackage(pkg)) {
// If the caller is accepting an installed package,
// return a success and give the package's install path
- acceptedPkg = item.getMainPackage();
- Archive[] a = acceptedPkg.getArchives();
+ Archive[] a = pkg.getArchives();
// an installed package should have one local compatible archive
if (a.length == 1 && a[0].isCompatible()) {
installTask.setResult(
- acceptedPkg,
+ pkg,
true /*success*/,
new File(a[0].getLocalOsPath()));
-
- // return false to tell loadPackages() that we don't
- // need to continue processing any more sources.
- return false;
}
+ // return false to tell loadPackages() that we don't
+ // need to continue processing any more sources.
+ return false;
}
- }
- if (acceptedPkg != null) {
- // Try to install this package if it has one compatible archive.
- Archive archiveToInstall = null;
+ } else {
+ // This is a remote package
+ if (installTask.acceptPackage(pkg)) {
+ // The caller is accepting this remote package. Let's try to install it.
- for (Archive a2 : acceptedPkg.getArchives()) {
- if (a2.isCompatible()) {
- archiveToInstall = a2;
- break;
+ for (Archive archive : pkg.getArchives()) {
+ if (archive.isCompatible()) {
+ installArchive(archive);
+ break;
+ }
}
+ // return false to tell loadPackages() that we don't
+ // need to continue processing any more sources.
+ return false;
}
-
- if (archiveToInstall != null) {
- installArchive(archiveToInstall);
- }
-
- // return false to tell loadPackages() that we don't
- // need to continue processing any more sources.
- return false;
}
-
}
+
// Tell loadPackages() to process the next source.
return true;
}
// The local package list has changed, make sure to refresh it
mUpdaterData.getLocalSdkParser().clearPackages();
- final List<PkgItem> localPkgItems = loadLocalPackages();
+ final Package[] localPkgs = mUpdaterData.getInstalledPackages();
// Try to locate the installed package in the new package list
- for (PkgItem localItem : localPkgItems) {
- Package localPkg = localItem.getMainPackage();
+ for (Package localPkg : localPkgs) {
if (localPkg.canBeUpdatedBy(packageToInstall) == UpdateInfo.NOT_UPDATE) {
Archive[] localArchive = localPkg.getArchives();
if (localArchive.length == 1 && localArchive[0].isCompatible()) {
}
public boolean hasUpdatePkg() {
- return mState == PkgState.INSTALLED && mUpdatePkg != null;
+ return mUpdatePkg != null;
}
public String getName() {
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.repository.Archive;
import com.android.sdklib.internal.repository.IDescription;
+import com.android.sdklib.internal.repository.IPackageVersion;
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.PlatformPackage;
import com.android.sdklib.internal.repository.PlatformToolPackage;
+import com.android.sdklib.internal.repository.SdkRepoSource;
import com.android.sdklib.internal.repository.SdkSource;
import com.android.sdklib.internal.repository.ToolPackage;
import com.android.sdkuilib.internal.repository.PackageLoader.ISourceLoadedCallback;
import com.android.sdkuilib.internal.repository.PackageLoader.PkgItem;
import com.android.sdkuilib.internal.repository.PackageLoader.PkgState;
+import com.android.sdkuilib.internal.repository.PackagesPage.PackagesDiffLogic.UpdateOp;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.repository.ISdkChangeListener;
import com.android.sdkuilib.ui.GridDataBuilder;
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
private final Map<MenuAction, MenuItem> mMenuActions = new HashMap<MenuAction, MenuItem>();
- private final PackagesPageLogic mLogic;
+ private final PackagesDiffLogic mDiffLogic;
private boolean mDisplayArchives = false;
private Text mTextSdkOsPath;
public PackagesPage(Composite parent, int swtStyle, UpdaterData updaterData) {
super(parent, swtStyle);
- mLogic = new PackagesPageLogic(updaterData) {
- @Override
- boolean keepItem(PkgItem item) {
- return PackagesPage.this.keepItem(item);
- }
- };
+ mDiffLogic = new PackagesDiffLogic(updaterData);
createContents(this);
postCreate(); //$hide$
}
public void onPageSelected() {
- if (mLogic.mAllPkgItems.isEmpty()) {
+ if (mDiffLogic.mCurrentCategories == null || mDiffLogic.mCurrentCategories.isEmpty()) {
// Initialize the package list the first time the page is shown.
loadPackages();
}
mTree.setHeaderVisible(true);
GridDataBuilder.create(mTree).fill().grab();
- // column name icon is set in sortPackages() depending on the current filter type
+ // column name icon is set when loading depending on the current filter type
// (e.g. API level or source)
mColumnName = new TreeViewerColumn(mTreeViewer, SWT.NONE);
mTreeColumnName = mColumnName.getColumn();
mCheckFilterNew.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- sortPackages(true /*updateButtons*/);
+ loadPackages();
}
});
mCheckFilterNew.setSelection(true);
mCheckFilterInstalled.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- sortPackages(true /*updateButtons*/);
+ loadPackages();
}
});
mCheckFilterInstalled.setSelection(true);
mCheckFilterObsolete.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- sortPackages(true /*updateButtons*/);
+ loadPackages();
}
});
mCheckFilterObsolete.setSelection(false);
mCheckSortApi.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- sortPackages(true /*updateButtons*/);
+ loadPackages();
// Reset the expanded state when changing sort algorithm
- expandInitial(mLogic.mCurrentCategories);
+ expandInitial(mDiffLogic.mCurrentCategories);
}
});
mCheckSortApi.setText("API level");
mCheckSortSource.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- sortPackages(true /*updateButtons*/);
+ loadPackages();
// Reset the expanded state when changing sort algorithm
- expandInitial(mLogic.mCurrentCategories);
+ expandInitial(mDiffLogic.mCurrentCategories);
}
});
}
private Image getImage(String filename) {
- if (mLogic.mUpdaterData != null) {
- ImageFactory imgFactory = mLogic.mUpdaterData.getImageFactory();
+ if (mDiffLogic.mUpdaterData != null) {
+ ImageFactory imgFactory = mDiffLogic.mUpdaterData.getImageFactory();
if (imgFactory != null) {
return imgFactory.getImageByName(filename);
}
loadPackages();
break;
case SHOW_ADDON_SITES:
- AddonSitesDialog d = new AddonSitesDialog(getShell(), mLogic.mUpdaterData);
+ AddonSitesDialog d = new AddonSitesDialog(getShell(), mDiffLogic.mUpdaterData);
if (d.open()) {
loadPackages();
}
break;
case TOGGLE_SHOW_ARCHIVES:
mDisplayArchives = !mDisplayArchives;
- sortPackages(true /*updateButtons*/);
+ loadPackages();
break;
case TOGGLE_SHOW_INSTALLED_PKG:
button = mCheckFilterInstalled;
}
private void postCreate() {
- if (mLogic.mUpdaterData != null) {
- mTextSdkOsPath.setText(mLogic.mUpdaterData.getOsSdkRoot());
+ if (mDiffLogic.mUpdaterData != null) {
+ mTextSdkOsPath.setText(mDiffLogic.mUpdaterData.getOsSdkRoot());
}
mTreeViewer.setContentProvider(new PkgContentProvider());
}
private void loadPackages() {
- if (mLogic.mUpdaterData == null) {
+ if (mDiffLogic.mUpdaterData == null) {
return;
}
- final boolean firstLoad = mLogic.mAllPkgItems.isEmpty();
-
// LoadPackage is synchronous but does not block the UI.
// Consequently it's entirely possible for the user
// to request the app to close whilst the packages are loading. Any
// action done after loadPackages must check the UI hasn't been
// disposed yet. Otherwise hilarity ensues.
- mLogic.mPackageLoader.loadPackages(new ISourceLoadedCallback() {
- public boolean onSourceLoaded(List<PkgItem> newPkgItems) {
- boolean somethingNew = false;
-
- synchronized(mLogic.mAllPkgItems) {
- nextNewItem: for (PkgItem newItem : newPkgItems) {
- for (PkgItem existingItem : mLogic.mAllPkgItems) {
- if (existingItem.isSameItemAs(newItem)) {
- // This isn't a new package, we already have it.
- continue nextNewItem;
- }
- }
- mLogic.mAllPkgItems.add(newItem);
- somethingNew = true;
- }
- }
+ final boolean useSortByApi = isSortByApi();
+ final UpdateOp op = mDiffLogic.updateStart(useSortByApi);
+ mDiffLogic.mPackageLoader.loadPackages(new ISourceLoadedCallback() {
+ boolean needsRefresh = mDiffLogic.isSortByApi() == useSortByApi;
- if (somethingNew) {
- // Dynamically update the table while we load after each source.
- // Since the official Android source gets loaded first, it makes the
- // window look non-empty a lot sooner.
- if (!mGroupPackages.isDisposed()) {
- mGroupPackages.getDisplay().syncExec(new Runnable() {
- public void run() {
- sortPackages(true /* updateButtons */);
-
- if (!mGroupPackages.isDisposed()) {
- if (firstLoad) {
- // set the initial expanded state
- expandInitial(mLogic.mCurrentCategories);
- }
- updateButtonsState();
- updateMenuCheckmarks();
- }
- }
- });
- }
+ public boolean onUpdateSource(SdkSource source, Package[] newPackages) {
+ if (mDiffLogic.updateSourcePackages(op, source, newPackages) || needsRefresh) {
+ refreshViewerSync();
+ needsRefresh = false;
}
// Return true to tell the loader to continue with the next source.
}
public void onLoadCompleted() {
- if (firstLoad && !mGroupPackages.isDisposed()) {
- updateButtonsState();
- updateMenuCheckmarks();
+ if (mDiffLogic.updateEnd(op) || needsRefresh) {
+ refreshViewerSync();
+ needsRefresh = false;
}
}
});
}
- private void sortPackages(boolean updateButtons) {
- if (isSortByApi()) {
- sortByApiLevel();
- } else {
- sortBySource();
- }
- if (updateButtons) {
- updateButtonsState();
- updateMenuCheckmarks();
- }
- }
-
- private boolean isSortByApi() {
- return mCheckSortApi != null && !mCheckSortApi.isDisposed() && mCheckSortApi.getSelection();
- }
-
- /**
- * Recompute the tree by sorting all the packages by API.
- * This does an update in-place of the mApiCategories list so that the table
- * can preserve its state (checked / expanded / selected) properly.
- */
- private void sortByApiLevel() {
+ private void refreshViewerSync() {
+ // Dynamically update the table while we load after each source.
+ // Since the official Android source gets loaded first, it makes the
+ // window look non-empty a lot sooner.
+ if (!mGroupPackages.isDisposed()) {
+ mGroupPackages.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ if (!mGroupPackages.isDisposed()) {
- if (!mTreeColumnName.isDisposed()) {
- mTreeColumnName.setImage(getImage(ICON_SORT_BY_API));
- }
+ if (mTreeViewer.getInput() != mDiffLogic.mCurrentCategories) {
+ // set initial input
+ mTreeViewer.setInput(mDiffLogic.mCurrentCategories);
+ } else {
+ // refresh existing, which preserves the expanded state, the selection
+ // and the checked state.
+ mTreeViewer.refresh();
+ }
- mLogic.sortByApiLevel();
+ // set the initial expanded state
+ expandInitial(mDiffLogic.mCurrentCategories);
- if (mTreeViewer.getInput() != mLogic.mCurrentCategories) {
- // set initial input
- mTreeViewer.setInput(mLogic.mCurrentCategories);
- } else {
- // refresh existing, which preserves the expanded state, the selection
- // and the checked state.
- mTreeViewer.refresh();
+ updateButtonsState();
+ updateMenuCheckmarks();
+ }
+ }
+ });
}
}
- /**
- * Recompute the tree by sorting all packages by source.
- */
- private void sortBySource() {
-
- if (!mTreeColumnName.isDisposed()) {
- mTreeColumnName.setImage(getImage(ICON_SORT_BY_SOURCE));
- }
-
- mLogic.sortBySource();
-
- // We don't support in-place incremental updates so the table gets reset
- // each time we load when sorted by source.
- if (mTreeViewer.getInput() != mLogic.mCurrentCategories) {
- mTreeViewer.setInput(mLogic.mCurrentCategories);
- } else {
- // refresh existing, which preserves the expanded state, the selection
- // and the checked state.
- mTreeViewer.refresh();
- }
+ private boolean isSortByApi() {
+ return mCheckSortApi != null && !mCheckSortApi.isDisposed() && mCheckSortApi.getSelection();
}
/**
private void onSelectNewUpdates() {
ITreeContentProvider provider = (ITreeContentProvider) mTreeViewer.getContentProvider();
- synchronized(mLogic.mAllPkgItems) {
- for (PkgCategory cat : mLogic.mCurrentCategories) {
+ synchronized(mDiffLogic.mCurrentCategories) {
+ for (PkgCategory cat : mDiffLogic.mCurrentCategories) {
boolean selected = false;
for (PkgItem item : cat.getItems()) {
if (item.getState() == PkgState.NEW || item.hasUpdatePkg()) {
}
}
- if (mLogic.mUpdaterData != null) {
+ if (mDiffLogic.mUpdaterData != null) {
try {
beginOperationPending();
- mLogic.mUpdaterData.updateOrInstallAll_WithGUI(
+ mDiffLogic.mUpdaterData.updateOrInstallAll_WithGUI(
archives,
mCheckFilterObsolete.getSelection() /* includeObsoletes */);
} finally {
endOperationPending();
- // Remove any pkg item matching anything we potentially installed
- // then request the package list to be updated. This will prevent
- // from having stale entries.
- synchronized(mLogic.mAllPkgItems) {
- for (Archive a : archives) {
- for (Iterator<PkgItem> it = mLogic.mAllPkgItems.iterator();
- it.hasNext(); ) {
- PkgItem pi = it.next();
- if (pi.hasArchive(a)) {
- it.remove();
- break;
- }
- }
- }
- }
-
// The local package list has changed, make sure to refresh it
- mLogic.mUpdaterData.getLocalSdkParser().clearPackages();
+ mDiffLogic.mUpdaterData.getLocalSdkParser().clearPackages();
loadPackages();
}
}
try {
beginOperationPending();
- mLogic.mUpdaterData.getTaskFactory().start("Delete Package", new ITask() {
+ mDiffLogic.mUpdaterData.getTaskFactory().start("Delete Package", new ITask() {
public void run(ITaskMonitor monitor) {
monitor.setProgressMax(archives.size() + 1);
for (Entry<Archive, PkgItem> entry : archives.entrySet()) {
a.getParentPackage().getShortDescription(),
a.getLocalOsPath());
- // Delete the actual package and its internal representation
+ // Delete the actual package
a.deleteLocal();
- synchronized(mLogic.mAllPkgItems) {
- mLogic.mAllPkgItems.remove(entry.getValue());
- }
-
monitor.incProgress(1);
if (monitor.isCancelRequested()) {
break;
endOperationPending();
// The local package list has changed, make sure to refresh it
- mLogic.mUpdaterData.getLocalSdkParser().clearPackages();
+ mDiffLogic.mUpdaterData.getLocalSdkParser().clearPackages();
loadPackages();
}
}
if (element instanceof PkgCategory) {
return ((PkgCategory) element).getLabel();
} else if (element instanceof PkgItem) {
- return getPkgItemname((PkgItem) element);
+ return getPkgItemName((PkgItem) element);
} else if (element instanceof IDescription) {
return ((IDescription) element).getShortDescription();
}
return "";
}
- private String getPkgItemname(PkgItem item) {
+ private String getPkgItemName(PkgItem item) {
String name = item.getName().trim();
if (isSortByApi()) {
// When sorting by API, the package name might contains the API number
// or the platform name at the end. If we find it, cut it out since it's
// redundant.
+ // TODO deal with obsolete packages
PkgApiCategory cat = (PkgApiCategory) findCategoryForItem(item);
String apiLabel = cat.getApiLabel();
}
private PkgCategory findCategoryForItem(PkgItem item) {
- for (PkgCategory cat : mLogic.mCurrentCategories) {
+ for (PkgCategory cat : mDiffLogic.mCurrentCategories) {
for (PkgItem i : cat.getItems()) {
if (i == item) {
return cat;
@Override
public Image getImage(Object element) {
- ImageFactory imgFactory = mLogic.mUpdaterData.getImageFactory();
+ ImageFactory imgFactory = mDiffLogic.mUpdaterData.getImageFactory();
if (imgFactory != null) {
if (mColumn == mColumnName) {
private final Object mIconRef;
private final List<PkgItem> mItems = new ArrayList<PkgItem>();
private String mLabel;
+ /** Transient flag used during incremental updates. */
+ private boolean mUnused;
public PkgCategory(Object key, String label, Object iconRef) {
mKey = key;
return mItems;
}
+ public void setUnused(boolean unused) {
+ mUnused = unused;
+ }
+
+ public boolean isUnused() {
+ return mUnused;
+ }
+
@Override
public String toString() {
return String.format("%s <key=%08x, label=%s, #items=%d>",
/**
* A special {@link SdkSource} object that represents the locally installed
- * items, or more exactly a lack of remote source. Value is {@code null}.
+ * items, or more exactly a lack of remote source.
*/
- public final static SdkSource UNKNOWN_SOURCE = null;
+ public final static SdkSource UNKNOWN_SOURCE =
+ new SdkRepoSource("http://no.source", "Local Packages");
private final SdkSource mSource;
public PkgSourceCategory(SdkSource source, UpdaterData updaterData) {
public String toString() {
return String.format("%s <source=%s, #items=%d>",
this.getClass().getSimpleName(),
- mSource == UNKNOWN_SOURCE ? "Local" : mSource.toString(),
+ mSource.toString(),
getItems().size());
}
/**
- * Helper class that separate the logic of package management from the UI
+ * Helper class that separates the logic of package management from the UI
* so that we can test it using head-less unit tests.
*/
- static abstract class PackagesPageLogic {
+ static class PackagesDiffLogic {
final PackageLoader mPackageLoader;
final UpdaterData mUpdaterData;
final List<PkgCategory> mApiCategories = new ArrayList<PkgCategory>();
final List<PkgCategory> mSourceCategories = new ArrayList<PkgCategory>();
List<PkgCategory> mCurrentCategories = mApiCategories;
- /** Access to this list must be synchronized on {@link #mAllPkgItems}. */
- final List<PkgItem> mAllPkgItems = new ArrayList<PkgItem>();
- public PackagesPageLogic(UpdaterData updaterData) {
+ public PackagesDiffLogic(UpdaterData updaterData) {
mUpdaterData = updaterData;
mPackageLoader = new PackageLoader(updaterData);
}
/**
- * Private interface used by {@link PackagesPageLogic#sort(ISortOperation)}.
- * The sort() method only focuses on the incremental update part of the sort.
- * The operation interface tells what to sort, how to extract keys from items
- * to bucket them in categeories, how to create categories, what categories are
- * by default and finally how to sort these categories.
+ * An update operation, customized to either sort by API or sort by source.
*/
- private interface ISortOperation {
- /** The list of categories to update in-place. */
- List<PkgCategory> getCategories();
-
- /** Create all the default categories (e.g. local installed packages, tools. etc.) */
- void addDefaultCategories(
- List<PkgCategory> currentCategories,
- Map<Object, PkgCategory> categoryKeyMap,
- Set<Object> unusedCategoryKey,
- ImageFactory imgFactory);
-
- /** Extracts the category key from a given item. */
- public Object getCategoryKey(PkgItem item);
-
- /** Creates a new category object for the given key. */
- PkgCategory createCategory(Object catKey, ImageFactory imgFactory);
-
- /**
- * Process a new item and merge it into the existing categories,
- * return true if the item was already in the category items list.
- *
- * @return True if the {@code newItem} was found in the category's item list.
- * When returning true, the method should remove the package(s) from
- * {@code unusedPackages}.
- * If the method return false, the caller will add {@code newItem} to the
- * category's items list.
- */
- boolean mergeNewItem(
- PkgItem newItem,
- PkgCategory cat,
- List<PkgCategory> cats,
- Set<Package> unusedPackages);
-
- /**
- * Post process items/categories after an item from mAllPkgItems as been filtered.
- * Used by the API sort to see if we can infer the category name from a platform
- * package when we don't have this info in the package manager.
- */
- void postProcessItem(Object catKey, PkgCategory category, PkgItem item);
-
- /** Final step, sort the list of categories. */
- void sortCategoryList(List<PkgCategory> categoryList);
+ abstract class UpdateOp {
+ public final Set<SdkSource> mVisitedSources = new HashSet<SdkSource>();
+
+ /** Retrieve the category key for the given package, either local or remote. */
+ public abstract Object getCategoryKey(Package pkg);
+ /** Modified {@code currentCategories} to add default categories. */
+ public abstract void addDefaultCategories(List<PkgCategory> currentCategories);
+ /** Creates the category for the given key and returns it. */
+ public abstract PkgCategory createCategory(Object catKey);
+ /** Sorts the category list (but not the items within the categories.) */
+ public abstract void sortCategoryList(List<PkgCategory> categoryList);
+ /** Called after items of a given category have changed. Used to sort the
+ * items and/or adjust the category name. */
+ public abstract void postCategoryItemsChanged(List<PkgCategory> categoryList);
+ /** Add the new package or merge it as an update or does nothing if this package
+ * is already part of the category items.
+ * Returns true if the category item list has changed. */
+ public abstract boolean mergeNewPackage(Package newPackage, PkgCategory cat);
+ }
+
+ public boolean isSortByApi() {
+ return mCurrentCategories == mApiCategories;
+ }
+
+ public UpdateOp updateStart(boolean sortByApi) {
+ mCurrentCategories = sortByApi ? mApiCategories : mSourceCategories;
+
+ UpdateOp info = sortByApi ? (new UpdateOpApi()) : (new UpdateOpSource());
+
+ // Note that default categories are created after the unused ones so that
+ // the callback can decide whether they should be marked as unused or not.
+ for (PkgCategory cat : mCurrentCategories) {
+ cat.setUnused(true);
+ }
+
+ info.addDefaultCategories(mCurrentCategories);
+
+ return info;
}
- /**
- * Recompute the tree by sorting all the {@link PkgItem}s into the category buckets.
- * This does an update in-place of the mCurrentCategories list so that the table
- * can preserve its state (checked / expanded / selected) properly.
- * <p/>
- * Since this is shared between both the per-API and the per-source sort, care must
- * be taken to either not change the displayed PkgItem or make the change compatible
- * with both displays. Otherwise it looks odd when the displayed items change
- * when changing the sorting mode in the UI.
- * FIXME currently there's an issue about that wrt items that have available updates.
- *
- * @param op The actual details of the sort, which allows us to
- * reuse the same method for both sorting by API or by SdkSource.
- * @see ISortOperation
- */
- private void sort(ISortOperation op) {
- ImageFactory imgFactory = mUpdaterData.getImageFactory();
-
- List<PkgCategory> cats = mCurrentCategories = op.getCategories();
-
- // We'll do an in-place update: first make a map of existing categories and
- // whatever pkg items they contain. Then prepare the new categories we want
- // which is all the existing categories + tools & extra (creating them the first time).
- // We mark all the existing items as "unused" then remove from the unused set the
- // items that we want to keep. At the end, whatever is left in the unused maps
- // are obsolete items we remove from the tree.
-
- // Keep a map of the initial state so that we can detect which items or categories are
- // no longer being used, so that we can remove them at the end of the in-place update.
-
- final Map<Object, PkgCategory> categoryKeyMap = new HashMap<Object, PkgCategory>();
- final Set<Object> unusedCategoryKey = new HashSet<Object>();
- final Set<Package> unusedPackages = new HashSet<Package>();
-
- // Get existing categories and packages
- for (PkgCategory cat : cats) {
- categoryKeyMap.put(cat.getKey(), cat);
- unusedCategoryKey.add(cat.getKey());
-
- for (PkgItem pi : cat.getItems()) {
- unusedPackages.add(pi.getMainPackage());
- if (pi.hasUpdatePkg()) {
- unusedPackages.add(pi.getUpdatePkg());
+ public boolean updateSourcePackages(UpdateOp op, SdkSource source, Package[] newPackages) {
+ if (newPackages.length > 0) {
+ op.mVisitedSources.add(source);
+ }
+ if (source == null) {
+ return processLocals(op, newPackages);
+ } else {
+ return processSource(op, source, newPackages);
+ }
+ }
+
+ public boolean updateEnd(UpdateOp op) {
+ boolean hasChanged = false;
+
+ // Remove unused categories
+ for (Iterator<PkgCategory> catIt = mCurrentCategories.iterator(); catIt.hasNext(); ) {
+ PkgCategory cat = catIt.next();
+ if (cat.isUnused()) {
+ catIt.remove();
+ hasChanged = true;
+ continue;
+ }
+
+ // Remove all items which source we have not been visited. They are obsolete.
+ for (Iterator<PkgItem> itemIt = cat.getItems().iterator(); itemIt.hasNext(); ) {
+ PkgItem item = itemIt.next();
+ if (!op.mVisitedSources.contains(item.getSource())) {
+ itemIt.remove();
+ hasChanged = true;
}
}
}
+ return hasChanged;
+ }
- op.addDefaultCategories(cats, categoryKeyMap, unusedCategoryKey, imgFactory);
+ /** Process all local packages. Returns true if something changed.
+ * @param op */
+ private boolean processLocals(UpdateOp op, Package[] packages) {
+ boolean hasChanged = false;
+ Set<Package> newPackages = new HashSet<Package>(Arrays.asList(packages));
+ Set<Package> unusedPackages = new HashSet<Package>(newPackages);
- // Go through the new package item list
- synchronized (mAllPkgItems) {
- for (PkgItem newItem : mAllPkgItems) {
- // Is this a package we want to display? That may change depending on the
- // display filter (obsolete, new/updates, etc.)
- if (!keepItem(newItem)) {
- continue;
- }
+ assert newPackages.size() == packages.length;
- Object catKey = op.getCategoryKey(newItem);
- PkgCategory cat = categoryKeyMap.get(catKey);
+ // Upgrade 'new' items to 'installed' for any local package we already know about
+ for (PkgCategory cat : mCurrentCategories) {
+ List<PkgItem> items = cat.getItems();
+ for (int i = 0; i < items.size(); i++) {
+ PkgItem item = items.get(i);
- if (cat == null) {
- // This is a new category. Create it and add it to the list.
- cat = op.createCategory(catKey, imgFactory);
- // It should not matter where we add to the list since we'll sort
- // the categories at the end.
- cats.add(cat);
- categoryKeyMap.put(cat.getKey(), cat);
- } else {
- // Remove the category key from the unused list.
- unusedCategoryKey.remove(catKey);
+ if (item.hasUpdatePkg() && newPackages.contains(item.getUpdatePkg())) {
+ // This item has an update package that is now installed.
+ PkgItem installed = new PkgItem(item.getUpdatePkg(), PkgState.INSTALLED);
+ unusedPackages.remove(item.getUpdatePkg());
+ item.removeUpdate();
+ items.add(installed);
+ cat.setUnused(false);
+ hasChanged = true;
}
- // Check whether the item is already present or merge it if it's an update
- boolean found = op.mergeNewItem(newItem, cat, cats, unusedPackages);
-
- if (!found) {
- cat.getItems().add(newItem);
- unusedPackages.remove(newItem.getMainPackage());
- unusedPackages.remove(newItem.getUpdatePkg());
+ if (newPackages.contains(item.getMainPackage())) {
+ unusedPackages.remove(item.getMainPackage());
+ if (item.getState() == PkgState.NEW) {
+ // This item has a main package that is now installed.
+ item.setState(PkgState.INSTALLED);
+ cat.setUnused(false);
+ hasChanged = true;
+ }
}
+ }
+ }
- op.postProcessItem(catKey, cat, newItem);
+ // Downgrade 'installed' items to 'new' if their package isn't listed anymore
+ for (PkgCategory cat : mCurrentCategories) {
+ for (PkgItem item : cat.getItems()) {
+ if (item.getState() == PkgState.INSTALLED &&
+ !newPackages.contains(item.getMainPackage())) {
+ item.setState(PkgState.NEW);
+ hasChanged = true;
+ }
}
}
- // Now go through all the remaining categories used for the tree and clear unused items.
- for (Iterator<PkgCategory> iterCat = cats.iterator(); iterCat.hasNext(); ) {
- PkgCategory cat = iterCat.next();
+ // Create new 'installed' items for any local package we haven't processed yet
+ for (Package newPackage : unusedPackages) {
+ Object catKey = op.getCategoryKey(newPackage);
+ PkgCategory cat = findCurrentCategory(mCurrentCategories, catKey);
- // Remove any unused categories.
- if (unusedCategoryKey.contains(cat.getKey())) {
- iterCat.remove();
- continue;
+ if (cat == null) {
+ // This is a new category. Create it and add it to the list.
+ cat = op.createCategory(catKey);
+ mCurrentCategories.add(cat);
+ op.sortCategoryList(mCurrentCategories);
}
- // Remove any unused items in the category.
- for (Iterator<PkgItem> iterItem = cat.getItems().iterator(); iterItem.hasNext(); ) {
- PkgItem item = iterItem.next();
+ cat.getItems().add(new PkgItem(newPackage, PkgState.INSTALLED));
+ cat.setUnused(false);
+ hasChanged = true;
+ }
- if (unusedPackages.contains(item.getMainPackage())) {
- iterItem.remove();
- } else if (item.hasUpdatePkg() &&
- unusedPackages.contains(item.getUpdatePkg())) {
- item.removeUpdate();
+ if (hasChanged) {
+ op.postCategoryItemsChanged(mCurrentCategories);
+ }
+
+ return hasChanged;
+ }
+
+ /** Process all remote packages. Returns true if something changed.
+ * @param op */
+ private boolean processSource(UpdateOp op, SdkSource source, Package[] packages) {
+ boolean hasChanged = false;
+ // Note: unusedPackages must respect the original packages order. It can't be a set.
+ List<Package> unusedPackages = new ArrayList<Package>(Arrays.asList(packages));
+ Set<Package> newPackages = new HashSet<Package>(unusedPackages);
+
+ assert newPackages.size() == packages.length;
+
+ // Remove any items or updates that are no longer in the source's packages
+ for (PkgCategory cat : mCurrentCategories) {
+ List<PkgItem> items = cat.getItems();
+ for (int i = 0; i < items.size(); i++) {
+ PkgItem item = items.get(i);
+ SdkSource itemSource = item.getSource();
+
+ // Only process items matching the current source
+ if (!(itemSource == source || (source != null && source.equals(itemSource)))) {
+ continue;
+ }
+ // Installed items have been dealt with the local source,
+ // so only change new items here
+ if (item.getState() == PkgState.NEW &&
+ !newPackages.contains(item.getMainPackage())) {
+ // This package is no longer part of the source.
+ items.remove(i--);
+ hasChanged = true;
+ continue;
+ }
+
+ cat.setUnused(false);
+ unusedPackages.remove(item.getMainPackage());
+
+ if (item.hasUpdatePkg()) {
+ if (newPackages.contains(item.getUpdatePkg())) {
+ unusedPackages.remove(item.getUpdatePkg());
+ } else {
+ // This update is no longer part of the source
+ item.removeUpdate();
+ hasChanged = true;
+ }
}
}
+ }
- // Sort the items
- Collections.sort(cat.getItems());
+ // Add any new unknown packages
+ for (Package newPackage : unusedPackages) {
+ Object catKey = op.getCategoryKey(newPackage);
+ PkgCategory cat = findCurrentCategory(mCurrentCategories, catKey);
+
+ if (cat == null) {
+ // This is a new category. Create it and add it to the list.
+ cat = op.createCategory(catKey);
+ mCurrentCategories.add(cat);
+ op.sortCategoryList(mCurrentCategories);
+ }
+
+ // Add the new package or merge it as an update
+ hasChanged |= op.mergeNewPackage(newPackage, cat);
}
- op.sortCategoryList(cats);
+ if (hasChanged) {
+ op.postCategoryItemsChanged(mCurrentCategories);
+ }
+
+ return hasChanged;
}
+ private PkgCategory findCurrentCategory(
+ List<PkgCategory> currentCategories,
+ Object categoryKey) {
+ for (PkgCategory cat : currentCategories) {
+ if (cat.getKey().equals(categoryKey)) {
+ return cat;
+ }
+ }
+ return null;
+ }
/**
- * Recompute the tree by sorting all the packages by API.
+ * {@link UpdateOp} describing the Sort-by-API operation.
*/
- void sortByApiLevel() {
- sort(new ISortOperation() {
- public List<PkgCategory> getCategories() {
- return mApiCategories;
- }
+ private class UpdateOpApi extends UpdateOp {
+ @Override
+ public Object getCategoryKey(Package pkg) {
+ // Sort by API
- public void addDefaultCategories(
- List<PkgCategory> currentCategories,
- Map<Object, PkgCategory> categoryKeyMap,
- Set<Object> unusedCategoryKey,
- ImageFactory imgFactory) {
- // Always add the tools & extras categories, even if empty (unlikely anyway)
- if (!unusedCategoryKey.contains(PkgApiCategory.KEY_TOOLS)) {
- PkgApiCategory acat = new PkgApiCategory(
- PkgApiCategory.KEY_TOOLS,
- null,
- imgFactory.getImageByName(ICON_CAT_OTHER));
- currentCategories.add(acat);
- categoryKeyMap.put(acat.getKey(), acat);
- unusedCategoryKey.add(acat.getKey());
- }
+ if (pkg instanceof IPackageVersion) {
+ return ((IPackageVersion) pkg).getVersion().getApiLevel();
+
+ } else if (pkg instanceof ToolPackage || pkg instanceof PlatformToolPackage) {
+ return PkgApiCategory.KEY_TOOLS;
- if (!unusedCategoryKey.contains(PkgApiCategory.KEY_EXTRA)) {
- PkgApiCategory acat = new PkgApiCategory(
- PkgApiCategory.KEY_EXTRA,
- null,
- imgFactory.getImageByName(ICON_CAT_OTHER));
- currentCategories.add(acat);
- categoryKeyMap.put(acat.getKey(), acat);
- unusedCategoryKey.add(acat.getKey());
+ } else {
+ return PkgApiCategory.KEY_EXTRA;
+ }
+ }
+
+ @Override
+ public void addDefaultCategories(List<PkgCategory> currentCategories) {
+ boolean needTools = true;
+ boolean needExtras = true;
+
+ for (PkgCategory cat : currentCategories) {
+ if (cat.getKey().equals(PkgApiCategory.KEY_TOOLS)) {
+ // Mark them as no unused to prevent their removal in updateEnd().
+ cat.setUnused(false);
+ needTools = false;
+ } else if (cat.getKey().equals(PkgApiCategory.KEY_EXTRA)) {
+ cat.setUnused(false);
+ needExtras = false;
}
}
- public Object getCategoryKey(PkgItem item) {
- // Get the category for this item.
- int apiKey = item.getApi();
+ // Always add the tools & extras categories, even if empty (unlikely anyway)
+ if (needTools) {
+ PkgApiCategory acat = new PkgApiCategory(
+ PkgApiCategory.KEY_TOOLS,
+ null,
+ mUpdaterData.getImageFactory().getImageByName(ICON_CAT_OTHER));
+ currentCategories.add(acat);
+ }
- if (apiKey < 1) {
- Package p = item.getMainPackage();
- if (p instanceof ToolPackage || p instanceof PlatformToolPackage) {
- apiKey = PkgApiCategory.KEY_TOOLS;
- } else {
- apiKey = PkgApiCategory.KEY_EXTRA;
- }
- }
- return apiKey;
+ if (needExtras) {
+ PkgApiCategory acat = new PkgApiCategory(
+ PkgApiCategory.KEY_EXTRA,
+ null,
+ mUpdaterData.getImageFactory().getImageByName(ICON_CAT_OTHER));
+ currentCategories.add(acat);
}
+ }
- public PkgCategory createCategory(
- Object catKey,
- ImageFactory imgFactory) {
-
- PkgCategory cat = null;
-
- assert catKey instanceof Integer;
- int apiKey = ((Integer) catKey).intValue();
-
- // We need a label for the category.
- // If we have an API level, try to get the info from the SDK Manager.
- // If we don't (e.g. when installing a new platform that isn't yet available
- // locally in the SDK Manager), it's OK we'll try to find the first platform
- // package available.
- String platformName = null;
- if (apiKey >= 1 && apiKey != PkgApiCategory.KEY_TOOLS) {
- for (IAndroidTarget target :
- mUpdaterData.getSdkManager().getTargets()) {
- if (target.isPlatform() &&
- target.getVersion().getApiLevel() == apiKey) {
- platformName = target.getVersionName();
- break;
- }
+ @Override
+ public PkgCategory createCategory(Object catKey) {
+ // Create API category.
+ PkgCategory cat = null;
+
+ assert catKey instanceof Integer;
+ int apiKey = ((Integer) catKey).intValue();
+
+ // We need a label for the category.
+ // If we have an API level, try to get the info from the SDK Manager.
+ // If we don't (e.g. when installing a new platform that isn't yet available
+ // locally in the SDK Manager), it's OK we'll try to find the first platform
+ // package available.
+ String platformName = null;
+ if (apiKey >= 1 && apiKey != PkgApiCategory.KEY_TOOLS) {
+ for (IAndroidTarget target :
+ mUpdaterData.getSdkManager().getTargets()) {
+ if (target.isPlatform() &&
+ target.getVersion().getApiLevel() == apiKey) {
+ platformName = target.getVersionName();
+ break;
}
}
+ }
- cat = new PkgApiCategory(
- apiKey,
- platformName,
- imgFactory.getImageByName(ICON_CAT_PLATFORM));
+ cat = new PkgApiCategory(
+ apiKey,
+ platformName,
+ mUpdaterData.getImageFactory().getImageByName(ICON_CAT_PLATFORM));
- return cat;
- }
+ return cat;
+ }
- public boolean mergeNewItem(
- PkgItem newItem,
- PkgCategory cat,
- List<PkgCategory> cats,
- Set<Package> unusedPackages) {
-
- // Behavior for a merge when sorting by API:
- // - New items can only be merged with their own category.
- // - Normally we expect installed items to be processed first (before new
- // item which will update them), by design (since the local list is always
- // processed first.) If for any reason this isn't the case, we'll show a
- // duplicate right now. That means if we're processing an installed item,
- // we won't try to merge it.
-
- for (PkgItem pi : cat.getItems()) {
- Package p = newItem.getMainPackage();
- if (pi.isSameItemAs(newItem) || pi.isSameMainPackageAs(p)) {
- // It's the same item or
- // it's not exactly the same item but the main package
- // is the same.
- unusedPackages.remove(pi.getMainPackage());
- return true;
- } else if (newItem.getState() == PkgState.NEW && pi.mergeUpdate(p)) {
- // The new package is an update for the existing package.
- unusedPackages.remove(pi.getMainPackage());
- unusedPackages.remove(pi.getUpdatePkg());
- return true;
- }
+ @Override
+ public boolean mergeNewPackage(Package newPackage, PkgCategory cat) {
+ // First check if the new package could be an update
+ // to an existing package
+ for (PkgItem item : cat.getItems()) {
+ if (item.isSameMainPackageAs(newPackage)) {
+ // Seems like this isn't really a new item after all.
+ cat.setUnused(false);
+ // Return false since we're not changing anything.
+ return false;
+ } else if (item.mergeUpdate(newPackage)) {
+ // The new package is an update for the existing package
+ // and has been merged in the PkgItem as such.
+ cat.setUnused(false);
+ // Return true to indicate we changed something.
+ return true;
}
- return false;
}
- public void postProcessItem(
- Object catKey,
- PkgCategory category,
- PkgItem item) {
- assert catKey instanceof Integer;
- int apiKey = ((Integer) catKey).intValue();
+ // This is truly a new item.
+ cat.getItems().add(new PkgItem(newPackage, PkgState.NEW));
+ cat.setUnused(false);
+ return true; // something has changed
+ }
- assert category instanceof PkgApiCategory;
- PkgApiCategory cat = (PkgApiCategory) category;
+ @Override
+ public void sortCategoryList(List<PkgCategory> categoryList) {
+ // Sort the categories list.
+ // We always want categories in order tools..platforms..extras.
+ // For platform, we compare in descending order (o2-o1).
+ // This order is achieved by having the category keys ordered as
+ // needed for the sort to just do what we expect.
+
+ Collections.sort(categoryList, new Comparator<PkgCategory>() {
+ public int compare(PkgCategory cat1, PkgCategory cat2) {
+ assert cat1 instanceof PkgApiCategory;
+ assert cat2 instanceof PkgApiCategory;
+ int api1 = ((Integer) cat1.getKey()).intValue();
+ int api2 = ((Integer) cat2.getKey()).intValue();
+ return api2 - api1;
+ }
+ });
+ }
- if (apiKey != -1 && cat.getPlatformName() == null) {
+ @Override
+ public void postCategoryItemsChanged(List<PkgCategory> categoryList) {
+ // Sort the items
+ for (PkgCategory cat : mCurrentCategories) {
+ Collections.sort(cat.getItems());
+
+ // When sorting by API, we can't always get the platform name
+ // from the package manager. In this case at the very end we
+ // look for a potential platform package we can use to extract
+ // the platform version name (e.g. '1.5') from the first suitable
+ // platform package we can find.
+
+ assert cat instanceof PkgApiCategory;
+ PkgApiCategory pac = (PkgApiCategory) cat;
+ if (pac.getPlatformName() == null) {
// Check whether we can get the actual platform version name (e.g. "1.5")
// from the first Platform package we find in this category.
- Package p = item.getMainPackage();
- if (p instanceof PlatformPackage) {
- String platformName = ((PlatformPackage) p).getVersionName();
- cat.setPlatformName(platformName);
+
+ for (PkgItem item : cat.getItems()) {
+ Package p = item.getMainPackage();
+ if (p instanceof PlatformPackage) {
+ String platformName = ((PlatformPackage) p).getVersionName();
+ if (platformName != null) {
+ pac.setPlatformName(platformName);
+ break;
+ }
+ }
}
}
}
- public void sortCategoryList(List<PkgCategory> categoryList) {
- // Sort the categories list.
- // We always want categories in order tools..platforms..extras.
- // For platform, we compare in descending order (o2-o1).
- // This order is achieved by having the category keys ordered as
- // needed for the sort to just do what we expect.
-
- Collections.sort(categoryList, new Comparator<PkgCategory>() {
- public int compare(PkgCategory cat1, PkgCategory cat2) {
- assert cat1 instanceof PkgApiCategory;
- assert cat2 instanceof PkgApiCategory;
- int api1 = ((Integer) cat1.getKey()).intValue();
- int api2 = ((Integer) cat2.getKey()).intValue();
- return api2 - api1;
- }
- });
- }
- });
+ }
}
/**
- * Recompute the tree by sorting all packages by source.
- *
- * Behavior for a merge when sorting by source:
- * - Items are grouped under their source even if installed.
- * The 'local' source is only for installed items with no source.
+ * {@link UpdateOp} describing the Sort-by-Source operation.
*/
- void sortBySource() {
- sort(new ISortOperation() {
- public List<PkgCategory> getCategories() {
- return mSourceCategories;
+ private class UpdateOpSource extends UpdateOp {
+ @Override
+ public Object getCategoryKey(Package pkg) {
+ // Sort by source
+ SdkSource source = pkg.getParentSource();
+ if (source == null) {
+ return PkgSourceCategory.UNKNOWN_SOURCE;
}
+ return source;
+ }
- public void addDefaultCategories(
- List<PkgCategory> currentCategories,
- Map<Object, PkgCategory> categoryKeyMap,
- Set<Object> unusedCategoryKey,
- ImageFactory imgFactory) {
-
- // Always add the local categories, even if empty (unlikely anyway)
- if (!unusedCategoryKey.contains(PkgSourceCategory.UNKNOWN_SOURCE)) {
- PkgSourceCategory cat = new PkgSourceCategory(
- PkgSourceCategory.UNKNOWN_SOURCE,
- mUpdaterData);
- currentCategories.add(cat);
- categoryKeyMap.put(cat.getKey(), cat);
- unusedCategoryKey.add(cat.getKey());
+ @Override
+ public void addDefaultCategories(List<PkgCategory> currentCategories) {
+ for (PkgCategory cat : currentCategories) {
+ if (cat.getKey().equals(PkgSourceCategory.UNKNOWN_SOURCE)) {
+ // Already present.
+ return;
}
}
- public Object getCategoryKey(PkgItem item) {
- return item.getSource();
- }
-
- public PkgCategory createCategory(
- Object catKey,
- ImageFactory imgFactory) {
+ // Always add the local categories, even if empty (unlikely anyway)
+ PkgSourceCategory cat = new PkgSourceCategory(
+ PkgSourceCategory.UNKNOWN_SOURCE,
+ mUpdaterData);
+ // Mark it as unused so that it can be cleared in updateEnd() if not used.
+ cat.setUnused(true);
+ currentCategories.add(cat);
+ }
- assert catKey instanceof SdkSource;
+ @Override
+ public PkgCategory createCategory(Object catKey) {
+ assert catKey instanceof SdkSource;
+ PkgCategory cat = new PkgSourceCategory((SdkSource) catKey, mUpdaterData);
+ return cat;
- PkgCategory cat = new PkgSourceCategory(
- (SdkSource) catKey,
- mUpdaterData);
+ }
- return cat;
+ @Override
+ public boolean mergeNewPackage(Package newPackage, PkgCategory cat) {
+ // First check if the new package could be an update
+ // to an existing package
+ for (PkgItem item : cat.getItems()) {
+ if (item.isSameMainPackageAs(newPackage)) {
+ // Seems like this isn't really a new item after all.
+ cat.setUnused(false);
+ // Return false since we're not changing anything.
+ return false;
+ } else if (item.mergeUpdate(newPackage)) {
+ // The new package is an update for the existing package
+ // and has been merged in the PkgItem as such.
+ cat.setUnused(false);
+ // Return true to indicate we changed something.
+ return true;
+ }
}
- public boolean mergeNewItem(
- PkgItem newItem,
- PkgCategory cat,
- List<PkgCategory> cats,
- Set<Package> unusedPackages) {
-
- for (PkgItem pi : cat.getItems()) {
- Package p = newItem.getMainPackage();
- if (pi.isSameItemAs(newItem)) {
- // It's the same item, keep it.
- unusedPackages.remove(pi.getMainPackage());
- return true;
- } else if (pi.isSameMainPackageAs(p)) {
- // It's not exactly the same item but the main package is the same.
- // This happens when trying to merge an item which state has changed
- // or its update list has changed.
-
- if (newItem.getState() == PkgState.INSTALLED &&
- pi.getState() == PkgState.NEW) {
- // In source-list mode, installed items 'hide' new items.
- // In this case, return false so that the caller add the newItem
- // to the category.
- return false;
- }
+ // This is truly a new item.
+ cat.getItems().add(new PkgItem(newPackage, PkgState.NEW));
+ cat.setUnused(false);
+ return true; // something has changed
+ }
- unusedPackages.remove(pi.getMainPackage());
- return true;
- } else if (newItem.getState() == PkgState.NEW && pi.mergeUpdate(p)) {
- // The new package is an update for the existing package.
- unusedPackages.remove(pi.getMainPackage());
- unusedPackages.remove(pi.getUpdatePkg());
- return true;
+ @Override
+ public void sortCategoryList(List<PkgCategory> categoryList) {
+ // Sort the sources in ascending source name order,
+ // with the local packages always first.
+
+ Collections.sort(categoryList, new Comparator<PkgCategory>() {
+ public int compare(PkgCategory cat1, PkgCategory cat2) {
+ assert cat1 instanceof PkgSourceCategory;
+ assert cat2 instanceof PkgSourceCategory;
+
+ SdkSource src1 = ((PkgSourceCategory) cat1).getSource();
+ SdkSource src2 = ((PkgSourceCategory) cat2).getSource();
+
+ if (src1 == src2) {
+ return 0;
+ } else if (src1 == PkgSourceCategory.UNKNOWN_SOURCE) {
+ return -1;
+ } else if (src2 == PkgSourceCategory.UNKNOWN_SOURCE) {
+ return 1;
}
+ assert src1 != null; // true because LOCAL_SOURCE==null
+ assert src2 != null;
+ return src1.toString().compareTo(src2.toString());
}
+ });
+ }
- return false;
- }
-
- public void postProcessItem(
- Object catKey,
- PkgCategory category,
- PkgItem item) {
- // pass
- }
-
- public void sortCategoryList(List<PkgCategory> categoryList) {
-
- // Sort the sources in ascending source name order,
- // with the local packages always first.
-
- Collections.sort(categoryList, new Comparator<PkgCategory>() {
- public int compare(PkgCategory cat1, PkgCategory cat2) {
- assert cat1 instanceof PkgSourceCategory;
- assert cat2 instanceof PkgSourceCategory;
-
- SdkSource src1 = ((PkgSourceCategory) cat1).getSource();
- SdkSource src2 = ((PkgSourceCategory) cat2).getSource();
-
- if (src1 == src2) {
- return 0;
- } else if (src1 == PkgSourceCategory.UNKNOWN_SOURCE) {
- return -1;
- } else if (src2 == PkgSourceCategory.UNKNOWN_SOURCE) {
- return 1;
- }
- assert src1 != null; // true because LOCAL_SOURCE==null
- assert src2 != null;
- return src1.toString().compareTo(src2.toString());
- }
- });
+ @Override
+ public void postCategoryItemsChanged(List<PkgCategory> categoryList) {
+ // Sort the items
+ for (PkgCategory cat : mCurrentCategories) {
+ Collections.sort(cat.getItems());
}
- });
+ }
}
-
- /**
- * Used by {@link #sort(ISortOperation)} to determine if a given item from
- * the input {@link #mAllPkgItems} should be displayed or not. This is what
- * allows us to filter items in our out of the tree displayed depending on
- * user flags, without actually reloading anything.
- */
- abstract boolean keepItem(PkgItem item);
}
mTestHandle.equals(((MockEmptyPackage) pkg).mTestHandle);
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((mTestHandle == null) ? 0 : mTestHandle.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof MockEmptyPackage)) {
+ return false;
+ }
+ MockEmptyPackage other = (MockEmptyPackage) obj;
+ if (mTestHandle == null) {
+ if (other.mTestHandle != null) {
+ return false;
+ }
+ } else if (!mTestHandle.equals(other.mTestHandle)) {
+ return false;
+ }
+ return true;
+ }
}
--- /dev/null
+/*
+ * 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.sdklib.internal.repository.MockAddonPackage;
+import com.android.sdklib.internal.repository.MockExtraPackage;
+import com.android.sdklib.internal.repository.MockPlatformPackage;
+import com.android.sdklib.internal.repository.MockPlatformToolPackage;
+import com.android.sdklib.internal.repository.MockToolPackage;
+import com.android.sdklib.internal.repository.Package;
+import com.android.sdklib.internal.repository.SdkRepoSource;
+import com.android.sdklib.internal.repository.SdkSource;
+import com.android.sdkuilib.internal.repository.PackageLoader.PkgItem;
+import com.android.sdkuilib.internal.repository.PackagesPage.PackagesDiffLogic;
+import com.android.sdkuilib.internal.repository.PackagesPage.PkgCategory;
+import com.android.sdkuilib.internal.repository.PackagesPage.PackagesDiffLogic.UpdateOp;
+
+import junit.framework.TestCase;
+
+public class PackagesDiffLogicTest extends TestCase {
+
+ private PackagesDiffLogic m;
+ private MockUpdaterData u;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ u = new MockUpdaterData();
+ m = new PackagesDiffLogic(u);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ // ----
+ //
+ // Test Details Note: the way load is implemented in PackageLoader, the
+ // loader processes each source and then for each source the packages are added
+ // to a list and the sorting algorithm is called with that list. Thus for
+ // one load, many calls to the sortByX/Y happen, with the list progressively
+ // being populated.
+ // However when the user switches sorting algorithm, the package list is not
+ // reloaded and is processed at once.
+
+ public void testSortByApi_Empty() {
+ UpdateOp op = m.updateStart(true /*sortByApi*/);
+ assertFalse(m.updateSourcePackages(op, null /*locals*/, new Package[0]));
+ assertFalse(m.updateEnd(op));
+
+ assertSame(m.mCurrentCategories, m.mApiCategories);
+
+ // We also keep these 2 categories even if they contain nothing
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=0>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_AddSamePackage() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ UpdateOp op = m.updateStart(true /*sortByApi*/);
+ // First insert local packages
+ assertTrue(m.updateSourcePackages(op, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "some pkg", 1)
+ }));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'some pkg' rev=1>\n",
+ getTree(m));
+
+ // Insert the next source
+ // Same package as the one installed, so we don't display it
+ assertFalse(m.updateSourcePackages(op, src1, new Package[] {
+ new MockEmptyPackage(src1, "some pkg", 1)
+ }));
+
+ assertFalse(m.updateEnd(op));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'some pkg' rev=1>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_AddOtherPackage() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ UpdateOp op = m.updateStart(true /*sortByApi*/);
+ // First insert local packages
+ assertTrue(m.updateSourcePackages(op, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "some pkg", 1)
+ }));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'some pkg' rev=1>\n",
+ getTree(m));
+
+ // Insert the next source
+ // Not the same package as the one installed, so we'll display it
+ assertTrue(m.updateSourcePackages(op, src1, new Package[] {
+ new MockEmptyPackage(src1, "other pkg", 1)
+ }));
+
+ assertFalse(m.updateEnd(op));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'some pkg' rev=1>\n" +
+ "-- <NEW, pkg:MockEmptyPackage 'other pkg' rev=1>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_Update1() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ // Typical case: user has a locally installed package in revision 1
+ // The display list after sort should show that installed package.
+ UpdateOp op = m.updateStart(true /*sortByApi*/);
+ // First insert local packages
+ assertTrue(m.updateSourcePackages(op, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+ assertTrue(m.updateSourcePackages(op, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 4),
+ new MockEmptyPackage(src1, "type1", 2)
+ }));
+
+ assertFalse(m.updateEnd(op));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=4>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_Reload() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ // First load reveals a package local package and its update
+ UpdateOp op1 = m.updateStart(true /*sortByApi*/);
+ // First insert local packages
+ assertTrue(m.updateSourcePackages(op1, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2)
+ }));
+
+ assertFalse(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+
+ // Now simulate a reload that clears the package list and create similar
+ // objects but not the same references. The only difference is that updateXyz
+ // returns false since they don't change anything.
+
+ UpdateOp op2 = m.updateStart(true /*sortByApi*/);
+ // First insert local packages
+ assertFalse(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+ assertFalse(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2)
+ }));
+
+ assertFalse(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_InstallPackage() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ // First load reveals a new package
+ UpdateOp op1 = m.updateStart(true /*sortByApi*/);
+ // No local packages at first
+ assertFalse(m.updateSourcePackages(op1, null /*locals*/, new Package[0]));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+
+ assertFalse(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+ // Install it.
+ UpdateOp op2 = m.updateStart(true /*sortByApi*/);
+ // local packages
+ assertTrue(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+ assertFalse(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+
+ assertFalse(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+ // Load reveals an update
+ UpdateOp op3 = m.updateStart(true /*sortByApi*/);
+ // local packages
+ assertFalse(m.updateSourcePackages(op3, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+ assertTrue(m.updateSourcePackages(op3, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2)
+ }));
+
+ assertFalse(m.updateEnd(op3));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_DeletePackage() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ // We have an installed package
+ UpdateOp op2 = m.updateStart(true /*sortByApi*/);
+ // local packages
+ assertTrue(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+ assertTrue(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2)
+ }));
+
+ assertFalse(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+
+ // User now deletes the installed package.
+ UpdateOp op1 = m.updateStart(true /*sortByApi*/);
+ // No local packages
+ assertTrue(m.updateSourcePackages(op1, null /*locals*/, new Package[0]));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1)
+ }));
+
+ assertFalse(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=0>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
+ "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+ }
+
+ public void testSortByApi_CompleteUpdate() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url1", "repo1");
+ SdkSource src2 = new SdkRepoSource("http://repo.com/url2", "repo2");
+
+ // Resulting categories are sorted by Tools, descending platform API and finally Extras.
+ // Addons are sorted by name within their API.
+ // Extras are sorted by vendor name.
+ // The order packages are added to the mAllPkgItems list is purposedly different from
+ // the final order we get.
+
+ // First update has the typical tools and a couple extras
+ UpdateOp op1 = m.updateStart(true /*sortByApi*/);
+
+ assertTrue(m.updateSourcePackages(op1, null /*locals*/, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "android", "usb_driver", 4, 3),
+ }));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0),
+ new MockExtraPackage(src1, "android", "usb_driver", 5, 3),
+ }));
+ assertFalse(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
+ "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
+ "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
+ "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
+ "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+ getTree(m));
+
+ // Next update adds platforms and addon, sorted in a category based on their API level
+ UpdateOp op2 = m.updateStart(true /*sortByApi*/);
+ MockPlatformPackage p1;
+ MockPlatformPackage p2;
+
+ assertTrue(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "android", "usb_driver", 4, 3),
+ // second update
+ p1 = new MockPlatformPackage(src1, 1, 2, 3), // API 1
+ new MockPlatformPackage(src1, 3, 6, 3),
+ new MockAddonPackage(src2, "addon A", p1, 5),
+ }));
+ assertTrue(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0),
+ new MockExtraPackage(src1, "android", "usb_driver", 5, 3),
+ // second update
+ p2 = new MockPlatformPackage(src1, 2, 4, 3), // API 2
+ }));
+ assertTrue(m.updateSourcePackages(op2, src2, new Package[] {
+ new MockAddonPackage(src2, "addon C", p2, 9),
+ new MockAddonPackage(src2, "addon A", p1, 6),
+ new MockAddonPackage(src2, "addon B", p2, 7),
+ // the rev 8 update will be ignored since there's a rev 9 coming after
+ new MockAddonPackage(src2, "addon B", p2, 8),
+ new MockAddonPackage(src2, "addon B", p2, 9),
+ }));
+ assertFalse(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
+ "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
+ "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
+ "PkgApiCategory <API=API 3, label=Android android-3 (API 3), #items=1>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
+ "PkgApiCategory <API=API 2, label=Android android-2 (API 2), #items=3>\n" +
+ "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
+ "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
+ "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
+ "PkgApiCategory <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
+ "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
+ "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
+ "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+ getTree(m));
+
+ // Reloading the same thing should have no impact except for the update methods
+ // returning false when they don't change the current list.
+ UpdateOp op3 = m.updateStart(true /*sortByApi*/);
+
+ assertFalse(m.updateSourcePackages(op3, null /*locals*/, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "android", "usb_driver", 4, 3),
+ // second update
+ p1 = new MockPlatformPackage(src1, 1, 2, 3),
+ new MockPlatformPackage(src1, 3, 6, 3),
+ new MockAddonPackage(src2, "addon A", p1, 5),
+ }));
+ assertFalse(m.updateSourcePackages(op3, src1, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0),
+ new MockExtraPackage(src1, "android", "usb_driver", 5, 3),
+ // second update
+ p2 = new MockPlatformPackage(src1, 2, 4, 3),
+ }));
+ assertTrue(m.updateSourcePackages(op3, src2, new Package[] {
+ new MockAddonPackage(src2, "addon C", p2, 9),
+ new MockAddonPackage(src2, "addon A", p1, 6),
+ new MockAddonPackage(src2, "addon B", p2, 7),
+ // the rev 8 update will be ignored since there's a rev 9 coming after
+ // however as a side effect it makes the update method return true as it
+ // incorporated the update.
+ new MockAddonPackage(src2, "addon B", p2, 8),
+ new MockAddonPackage(src2, "addon B", p2, 9),
+ }));
+ assertFalse(m.updateEnd(op3));
+
+ assertEquals(
+ "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
+ "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
+ "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
+ "PkgApiCategory <API=API 3, label=Android android-3 (API 3), #items=1>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
+ "PkgApiCategory <API=API 2, label=Android android-2 (API 2), #items=3>\n" +
+ "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
+ "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
+ "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
+ "PkgApiCategory <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
+ "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
+ "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
+ "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
+ "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+ getTree(m));
+ }
+
+ // ----
+
+ public void testSortBySource_Empty() {
+ UpdateOp op = m.updateStart(false /*sortByApi*/);
+ assertFalse(m.updateSourcePackages(op, null /*locals*/, new Package[0]));
+ // UpdateEnd returns true since it removed the synthetic "unknown source" category
+ assertTrue(m.updateEnd(op));
+
+ assertSame(m.mCurrentCategories, m.mSourceCategories);
+ assertTrue(m.mApiCategories.isEmpty());
+
+ assertEquals(
+ "",
+ getTree(m));
+ }
+
+ public void testSortBySource_AddPackages() {
+ // Since we're sorting by source, items are grouped under their source
+ // even if installed. The 'local' source is only for installed items for
+ // which we don't know the source.
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ UpdateOp op = m.updateStart(false /*sortByApi*/);
+ assertTrue(m.updateSourcePackages(op, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "known source", 2),
+ new MockEmptyPackage(null, "unknown source", 3),
+ }));
+
+ assertEquals(
+ "PkgSourceCategory <source=Local Packages (no.source), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'unknown source' rev=3>\n" +
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'known source' rev=2>\n",
+ getTree(m));
+
+ assertTrue(m.updateSourcePackages(op, src1, new Package[] {
+ new MockEmptyPackage(src1, "new", 1),
+ }));
+
+ assertFalse(m.updateEnd(op));
+
+ assertEquals(
+ "PkgSourceCategory <source=Local Packages (no.source), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'unknown source' rev=3>\n" +
+ "PkgSourceCategory <source=repo1 (repo.com), #items=2>\n" +
+ "-- <NEW, pkg:MockEmptyPackage 'new' rev=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'known source' rev=2>\n",
+ getTree(m));
+ }
+
+ public void testSortBySource_Update1() {
+
+ // Typical case: user has a locally installed package in revision 1
+ // The display list after sort should show that instaled package.
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+ UpdateOp op = m.updateStart(false /*sortByApi*/);
+ assertTrue(m.updateSourcePackages(op, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+
+ assertEquals(
+ "PkgSourceCategory <source=Local Packages (no.source), #items=0>\n" +
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+ // Edge case: the source reveals an update in revision 2. It is ignored since
+ // we already have a package in rev 4.
+
+ assertTrue(m.updateSourcePackages(op, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 4),
+ new MockEmptyPackage(src1, "type1", 2),
+ }));
+
+ assertTrue(m.updateEnd(op));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=4>\n",
+ getTree(m));
+ }
+
+ public void testSortBySource_Reload() {
+
+ // First load reveals a package local package and its update
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+ UpdateOp op1 = m.updateStart(false /*sortByApi*/);
+ assertTrue(m.updateSourcePackages(op1, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2),
+ }));
+ assertTrue(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+
+ // Now simulate a reload that clears the package list and creates similar
+ // objects but not the same references. Update methods return false since
+ // they don't change anything.
+ UpdateOp op2 = m.updateStart(false /*sortByApi*/);
+ assertFalse(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertFalse(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2),
+ }));
+ assertTrue(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+ }
+
+ public void testSortBySource_InstallPackage() {
+
+ // First load reveals a new package
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+ UpdateOp op1 = m.updateStart(false /*sortByApi*/);
+ // no local package
+ assertFalse(m.updateSourcePackages(op1, null /*locals*/, new Package[0]));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertTrue(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+
+ // Install it. The display only shows the installed one, 'hiding' the remote package
+ UpdateOp op2 = m.updateStart(false /*sortByApi*/);
+ assertTrue(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertFalse(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertTrue(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+ // Now we have an update
+ UpdateOp op3 = m.updateStart(false /*sortByApi*/);
+ assertFalse(m.updateSourcePackages(op3, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertTrue(m.updateSourcePackages(op3, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 2),
+ }));
+ assertTrue(m.updateEnd(op3));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
+ getTree(m));
+ }
+
+ public void testSortBySource_DeletePackage() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+
+ // Start with an installed package and its matching remote package
+ UpdateOp op2 = m.updateStart(false /*sortByApi*/);
+ assertTrue(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertFalse(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertTrue(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+
+ // User now deletes the installed package.
+ UpdateOp op1 = m.updateStart(false /*sortByApi*/);
+ // no local package
+ assertTrue(m.updateSourcePackages(op1, null /*locals*/, new Package[0]));
+ assertFalse(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockEmptyPackage(src1, "type1", 1),
+ }));
+ assertTrue(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
+ "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
+ getTree(m));
+ }
+
+ public void testSortBySource_CompleteUpdate() {
+ SdkSource src1 = new SdkRepoSource("http://repo.com/url1", "repo1");
+ SdkSource src2 = new SdkRepoSource("http://repo.com/url2", "repo2");
+
+ // First update has the typical tools and a couple extras
+ UpdateOp op1 = m.updateStart(false /*sortByApi*/);
+
+ assertTrue(m.updateSourcePackages(op1, null /*locals*/, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "android", "usb_driver", 4, 3),
+ }));
+ assertTrue(m.updateSourcePackages(op1, src1, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0),
+ new MockExtraPackage(src1, "android", "usb_driver", 5, 3),
+ }));
+ assertTrue(m.updateEnd(op1));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=4>\n" +
+ "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
+ "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
+ "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
+ "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+ getTree(m));
+
+ // Next update adds platforms and addon, sorted in a category based on their API level
+ UpdateOp op2 = m.updateStart(false /*sortByApi*/);
+ MockPlatformPackage p1;
+ MockPlatformPackage p2;
+
+ assertTrue(m.updateSourcePackages(op2, null /*locals*/, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "android", "usb_driver", 4, 3),
+ // second update
+ p1 = new MockPlatformPackage(src1, 1, 2, 3), // API 1
+ new MockPlatformPackage(src1, 3, 6, 3), // API 3
+ new MockAddonPackage(src2, "addon A", p1, 5),
+ }));
+ assertTrue(m.updateSourcePackages(op2, src1, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0),
+ new MockExtraPackage(src1, "android", "usb_driver", 5, 3),
+ // second update
+ p2 = new MockPlatformPackage(src1, 2, 4, 3), // API 2
+ }));
+ assertTrue(m.updateSourcePackages(op2, src2, new Package[] {
+ new MockAddonPackage(src2, "addon C", p2, 9),
+ new MockAddonPackage(src2, "addon A", p1, 6),
+ new MockAddonPackage(src2, "addon B", p2, 7),
+ // the rev 8 update will be ignored since there's a rev 9 coming after
+ new MockAddonPackage(src2, "addon B", p2, 8),
+ new MockAddonPackage(src2, "addon B", p2, 9),
+ }));
+ assertTrue(m.updateEnd(op2));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=7>\n" +
+ "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
+ "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
+ "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
+ "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
+ "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
+ "PkgSourceCategory <source=repo2 (repo.com), #items=3>\n" +
+ "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
+ "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
+ "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n",
+ getTree(m));
+
+ // Reloading the same thing should have no impact except for the update methods
+ // returning false when they don't change the current list.
+ UpdateOp op3 = m.updateStart(false /*sortByApi*/);
+
+ assertFalse(m.updateSourcePackages(op3, null /*locals*/, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "android", "usb_driver", 4, 3),
+ // second update
+ p1 = new MockPlatformPackage(src1, 1, 2, 3),
+ new MockPlatformPackage(src1, 3, 6, 3),
+ new MockAddonPackage(src2, "addon A", p1, 5),
+ }));
+ assertFalse(m.updateSourcePackages(op3, src1, new Package[] {
+ new MockToolPackage(src1, 10, 3),
+ new MockPlatformToolPackage(src1, 3),
+ new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0),
+ new MockExtraPackage(src1, "android", "usb_driver", 5, 3),
+ // second update
+ p2 = new MockPlatformPackage(src1, 2, 4, 3),
+ }));
+ assertTrue(m.updateSourcePackages(op3, src2, new Package[] {
+ new MockAddonPackage(src2, "addon C", p2, 9),
+ new MockAddonPackage(src2, "addon A", p1, 6),
+ new MockAddonPackage(src2, "addon B", p2, 7),
+ // the rev 8 update will be ignored since there's a rev 9 coming after
+ // however as a side effect it makes the update method return true as it
+ // incorporated the update.
+ new MockAddonPackage(src2, "addon B", p2, 8),
+ new MockAddonPackage(src2, "addon B", p2, 9),
+ }));
+ assertTrue(m.updateEnd(op3));
+
+ assertEquals(
+ "PkgSourceCategory <source=repo1 (repo.com), #items=7>\n" +
+ "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
+ "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
+ "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
+ "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
+ "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
+ "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
+ "PkgSourceCategory <source=repo2 (repo.com), #items=3>\n" +
+ "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
+ "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
+ "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n",
+ getTree(m));
+ }
+
+ // ----
+
+ /**
+ * Simulates the display we would have in the Packages Tree.
+ * This always depends on mCurrentCategories like the tree does.
+ * The display format is something like:
+ * <pre>
+ * PkgCategory <description>
+ * -- <PkgItem description>
+ * </pre>
+ */
+ public String getTree(PackagesDiffLogic l) {
+ StringBuilder sb = new StringBuilder();
+
+ for (PkgCategory cat : l.mCurrentCategories) {
+ sb.append(cat.toString()).append('\n');
+ for (PkgItem item : cat.getItems()) {
+ sb.append("-- ").append(item.toString()).append('\n');
+ }
+ }
+
+ return sb.toString();
+ }
+}
+++ /dev/null
-/*
- * 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.sdklib.internal.repository.MockAddonPackage;
-import com.android.sdklib.internal.repository.MockExtraPackage;
-import com.android.sdklib.internal.repository.MockPlatformPackage;
-import com.android.sdklib.internal.repository.MockPlatformToolPackage;
-import com.android.sdklib.internal.repository.MockToolPackage;
-import com.android.sdklib.internal.repository.SdkRepoSource;
-import com.android.sdklib.internal.repository.SdkSource;
-import com.android.sdkuilib.internal.repository.PackageLoader.PkgItem;
-import com.android.sdkuilib.internal.repository.PackageLoader.PkgState;
-import com.android.sdkuilib.internal.repository.PackagesPage.PackagesPageLogic;
-import com.android.sdkuilib.internal.repository.PackagesPage.PkgCategory;
-
-import junit.framework.TestCase;
-
-public class PackagesPageLogicTest extends TestCase {
-
- private PackagesPageLogic m;
- private MockUpdaterData u;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- u = new MockUpdaterData();
- m = new PackagesPageLogic(u) {
- @Override
- boolean keepItem(PkgItem item) {
- return true;
- }
- };
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
- // ----
- //
- // Test Details Note: the way load is implemented in PackageLoader, the
- // loader processes each source and then for each source the packages are added
- // to a list and the sorting algorithm is called with that list. Thus for
- // one load, many calls to the sortByX/Y happen, with the list progressively
- // being populated.
- // However when the user switches sorting algorithm, the package list is not
- // reloaded and is processed at once.
-
- public void testSortByApi_Empty() {
- assertTrue(m.mAllPkgItems.isEmpty());
- m.sortByApiLevel();
- assertSame(m.mCurrentCategories, m.mApiCategories);
- assertTrue(m.mApiCategories.isEmpty());
- }
-
- public void testSortByApi_SamePackage() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "some pkg", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'some pkg' rev=1>\n",
- getTree(m));
-
- // Same package as the one installed, so we don't display it
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "some pkg", 1), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'some pkg' rev=1>\n",
- getTree(m));
- }
-
- public void testSortByApi_AddPackages() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "that pkg", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "this pkg", 1), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'that pkg' rev=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'this pkg' rev=1>\n",
- getTree(m));
- }
-
- public void testSortByApi_Update1() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- // Typical case: user has a locally installed package in revision 1
- // The display list after sort should show that instaled package.
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // Then loading sources reveals an update in revision 4
- // Edge case: another source reveals an update in revision 2.
- // The display list after sort should show an update as available with rev 4
- // and rev 2 should be ignored since we have a better one.
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 4), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=4>\n",
- getTree(m));
- }
-
- public void testSortByApi_Reload() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- // First load reveals a package local package and its update
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
-
- // Now simulate a reload that clears the package list and create similar
- // objects but not the same references.
- m.mAllPkgItems.clear();
- assertTrue(m.mAllPkgItems.isEmpty());
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
- }
-
- public void testSortByApi_InstallAfterNew() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- // We expect updates to appear AFTER the packages the installed items will update.
- // (This is pretty much guaranteed since local packages are processed first.)
- // The reverse order is not supported by the sorting algorithm and both will be shown.
-
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
- }
-
- public void testSortByApi_InstallPackage() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- // First load reveals a new package
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // Install it. Load reveals a package local package and its update
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // Now we have an update
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
- }
-
- public void testSortByApi_DeletePackage() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- // We have an installed package
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortByApiLevel();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // User now deletes the installed package.
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
- }
-
- public void testSortByApi_CompleteUpdate() {
- assertTrue(m.mAllPkgItems.isEmpty());
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- // Resulting categories are sorted by Tools, descending platform API and finally Extras.
- // Addons are sorted by name within their API.
- // Extras are sorted by vendor name.
- // The order packages are added to the mAllPkgItems list is purposedly different from
- // the final order we get.
-
- // Typical case is to have these 2 tools, which should get sorted in their own category
- m.mAllPkgItems.add(new PkgItem(new MockToolPackage(10, 3), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(new MockPlatformToolPackage(3), PkgState.INSTALLED));
- // We'll typically see installed items twice, first as installed then as new packages
- // coming from the source that delivered them. The new ones should be ignored.
- m.mAllPkgItems.add(new PkgItem(new MockToolPackage(10, 3), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockPlatformToolPackage(3), PkgState.NEW));
-
- // Load a few extra packages
- m.mAllPkgItems.add(new PkgItem(
- new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0), PkgState.NEW));
-
- // We call sortByApiLevel() multiple times to simulate the fact it works as an
- // incremental diff. In real usage, it is called after each source is loaded so
- // that we can progressively update the display.
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=1>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
- getTree(m));
-
- m.mAllPkgItems.add(new PkgItem(
- new MockExtraPackage(src1, "android", "usb_driver", 4, 3), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(
- new MockExtraPackage(src1, "android", "usb_driver", 5, 3), PkgState.NEW));
-
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
- getTree(m));
-
- // Platforms and addon are sorted in a category based on their API level
- MockPlatformPackage p1;
- MockPlatformPackage p2;
- m.mAllPkgItems.add(new PkgItem(p1 = new MockPlatformPackage(src1, 1, 2, 3), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(p2 = new MockPlatformPackage(src1, 2, 4, 3), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem( new MockPlatformPackage(src1, 3, 6, 3), PkgState.INSTALLED));
-
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "PkgApiCategory <API=API 3, label=Android android-3 (API 3), #items=1>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
- "PkgApiCategory <API=API 2, label=Android android-2 (API 2), #items=1>\n" +
- "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
- "PkgApiCategory <API=API 1, label=Android android-1 (API 1), #items=1>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
- getTree(m));
-
- m.mAllPkgItems.add(new PkgItem(new MockAddonPackage(src1, "addon C", p2, 9), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockAddonPackage(src1, "addon A", p1, 5), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(new MockAddonPackage(src1, "addon A", p1, 6), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockAddonPackage(src1, "addon B", p2, 7), PkgState.NEW));
- // the rev 8 update will be ignored since there's a rev 9 coming after
- m.mAllPkgItems.add(new PkgItem(new MockAddonPackage(src1, "addon B", p2, 8), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockAddonPackage(src1, "addon B", p2, 9), PkgState.NEW));
-
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "PkgApiCategory <API=API 3, label=Android android-3 (API 3), #items=1>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
- "PkgApiCategory <API=API 2, label=Android android-2 (API 2), #items=3>\n" +
- "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
- "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
- "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
- "PkgApiCategory <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
- "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
- getTree(m));
-
- // Now simulate a change of sorting algorithm: sort by source then by API again.
-
- m.sortBySource();
- m.sortByApiLevel();
-
- assertEquals(
- "PkgApiCategory <API=TOOLS, label=Tools, #items=2>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "PkgApiCategory <API=API 3, label=Android android-3 (API 3), #items=1>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
- "PkgApiCategory <API=API 2, label=Android android-2 (API 2), #items=3>\n" +
- "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
- "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
- "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
- "PkgApiCategory <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
- "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
- "PkgApiCategory <API=EXTRAS, label=Extras, #items=2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
- getTree(m));
-}
-
- // ----
-
- public void testSortBySource_Empty() {
- assertTrue(m.mAllPkgItems.isEmpty());
- m.sortBySource();
- assertSame(m.mCurrentCategories, m.mSourceCategories);
- assertTrue(m.mApiCategories.isEmpty());
- }
-
-
- public void testSortBySource_AddPackages() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // Since we're sorting by source, items are grouped under their source
- // even if installed. The 'local' source is only for installed items for
- // which we don't know the source.
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
-
- m.mAllPkgItems.add(new PkgItem(
- new MockEmptyPackage(src1, "new", 1), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(
- new MockEmptyPackage(src1, "known source", 2), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(
- new MockEmptyPackage(null, "unknown source", 3), PkgState.INSTALLED));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=Local, #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'unknown source' rev=3>\n" +
- "PkgSourceCategory <source=repo1 (repo.com), #items=2>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'new' rev=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'known source' rev=2>\n",
- getTree(m));
- }
-
- public void testSortBySource_Update1() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // Typical case: user has a locally installed package in revision 1
- // The display list after sort should show that instaled package.
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // Edge case: the source reveals an update in revision 2. It is ignored since
- // we already have a package in rev 4.
-
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 4), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=4>\n",
- getTree(m));
- }
-
- public void testSortBySource_Reload() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // First load reveals a package local package and its update
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
-
- // Now simulate a reload that clears the package list and create similar
- // objects but not the same references.
- m.mAllPkgItems.clear();
- assertTrue(m.mAllPkgItems.isEmpty());
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
- }
-
- public void testSortBySource_InstallAfterNew() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // We expect updates to appear AFTER the packages the installed items will update.
- // (This is pretty much guaranteed since local packages are processed first.)
- // The reverse order is not supported by the sorting algorithm and both will be shown.
-
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=2>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
- }
-
- public void testSortBySource_InstallPackage() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // First load reveals a new package
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // Install it. The display only shows the installed one, 'hiding' the remote package
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortBySource();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // Now we have an update
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortBySource();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 2), PkgState.NEW));
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1, updated by:MockEmptyPackage 'type1' rev=2>\n",
- getTree(m));
- }
-
- public void testSortBySource_DeletePackage() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // Start with an installed package and its matching remote package
- SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.INSTALLED));
- m.sortBySource();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
-
- // User now deletes the installed package.
- m.mAllPkgItems.clear();
- m.mAllPkgItems.add(new PkgItem(new MockEmptyPackage(src1, "type1", 1), PkgState.NEW));
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=1>\n" +
- "-- <NEW, pkg:MockEmptyPackage 'type1' rev=1>\n",
- getTree(m));
- }
-
- public void testSortBySource_CompleteUpdate() {
- assertTrue(m.mAllPkgItems.isEmpty());
-
- // Typical case is to have these 2 tools
- SdkSource src1 = new SdkRepoSource("http://repo.com/url1", "repo1");
- m.mAllPkgItems.add(new PkgItem(new MockToolPackage(src1, 10, 3), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(new MockPlatformToolPackage(src1, 3), PkgState.INSTALLED));
-
- // Load a few extra packages
- m.mAllPkgItems.add(
- new PkgItem(new MockExtraPackage(src1, "carrier", "custom_rom", 1, 0), PkgState.NEW));
-
- // We call sortBySource() multiple times to simulate the fact it works as an
- // incremental diff. In real usage, it is called after each source is loaded so
- // that we can progressively update the display.
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
- getTree(m));
-
- // Source 2 only provides the addon, which is already installed so the source
- // should be empty.
- SdkSource src2 = new SdkRepoSource("http://repo.com/url2", "repo2");
- m.mAllPkgItems.add(new PkgItem(
- new MockExtraPackage(src2, "android", "usb_driver", 4, 3), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(
- new MockExtraPackage(src2, "android", "usb_driver", 4, 3), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
- "PkgSourceCategory <source=repo2 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4>\n",
- getTree(m));
-
- // When an update is available, it is still merged with the installed item
- m.mAllPkgItems.add(new PkgItem(
- new MockExtraPackage(src2, "android", "usb_driver", 6, 4), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
- "PkgSourceCategory <source=repo2 (repo.com), #items=1>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 6>\n" ,
- getTree(m));
-
-
- // Now add a few Platforms
-
- SdkSource src3 = new SdkRepoSource("http://repo.com/url3", "repo3");
- MockPlatformPackage p1;
- MockPlatformPackage p2;
- m.mAllPkgItems.add(new PkgItem(
- p1 = new MockPlatformPackage(src2, 1, 2, 3), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(
- p2 = new MockPlatformPackage(src3, 2, 4, 3), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(
- new MockPlatformPackage(src2, 3, 6, 3), PkgState.INSTALLED));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
- "PkgSourceCategory <source=repo2 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n"+
- "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 6>\n" +
- "PkgSourceCategory <source=repo3 (repo.com), #items=1>\n" +
- "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n",
- getTree(m));
-
- // Add a bunch of add-ons and sort them.
- // Note that for source 4, the order is BCA since we order first by decreasing API
- // and then by increasing add-on name.
- SdkSource src4 = new SdkRepoSource("http://repo.com/url4", "repo4");
- m.mAllPkgItems.add(new PkgItem(
- new MockAddonPackage(src4, "addon C", p2, 9), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(
- new MockAddonPackage(src4, "addon A", p1, 5), PkgState.INSTALLED));
- m.mAllPkgItems.add(new PkgItem(
- new MockAddonPackage(src4, "addon A", p1, 6), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(
- new MockAddonPackage(src4, "addon B", p2, 7), PkgState.NEW));
- // the rev 8 update will be ignored since there's a rev 9 coming after
- m.mAllPkgItems.add(new PkgItem(
- new MockAddonPackage(src4, "addon B", p2, 8), PkgState.NEW));
- m.mAllPkgItems.add(new PkgItem(
- new MockAddonPackage(src4, "addon B", p2, 9), PkgState.NEW));
-
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
- "PkgSourceCategory <source=repo2 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n"+
- "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 6>\n" +
- "PkgSourceCategory <source=repo3 (repo.com), #items=1>\n" +
- "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
- "PkgSourceCategory <source=repo4 (repo.com), #items=3>\n" +
- "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
- "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
- "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n",
- getTree(m));
-
- // Now simulate a change of sorting algorithm: sort by source then by API again.
- m.sortByApiLevel();
- m.sortBySource();
-
- assertEquals(
- "PkgSourceCategory <source=repo1 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
- "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
- "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
- "PkgSourceCategory <source=repo2 (repo.com), #items=3>\n" +
- "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n"+
- "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
- "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 6>\n" +
- "PkgSourceCategory <source=repo3 (repo.com), #items=1>\n" +
- "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
- "PkgSourceCategory <source=repo4 (repo.com), #items=3>\n" +
- "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 7, updated by:addon B by vendor 2, Android API 2, revision 9>\n" +
- "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
- "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n",
- getTree(m));
- }
-
- // ----
-
- /**
- * Simulates the display we would have in the Packages Tree.
- * This always depends on mCurrentCategories like the tree does.
- * The display format is something like:
- * <pre>
- * PkgCategory <description>
- * -- <PkgItem description>
- * </pre>
- */
- public String getTree(PackagesPageLogic l) {
- StringBuilder sb = new StringBuilder();
-
- for (PkgCategory cat : l.mCurrentCategories) {
- sb.append(cat.toString()).append('\n');
- for (PkgItem item : cat.getItems()) {
- sb.append("-- ").append(item.toString()).append('\n');
- }
- }
-
- return sb.toString();
- }
-}