/* | |
* Copyright (C) 2009 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.android.sdkuilib.internal.repository; | |
import com.android.sdklib.AndroidVersion; | |
import com.android.sdklib.internal.repository.AddonPackage; | |
import com.android.sdklib.internal.repository.Archive; | |
import com.android.sdklib.internal.repository.DocPackage; | |
import com.android.sdklib.internal.repository.ExtraPackage; | |
import com.android.sdklib.internal.repository.IMinApiLevelDependency; | |
import com.android.sdklib.internal.repository.IMinToolsDependency; | |
import com.android.sdklib.internal.repository.IPackageVersion; | |
import com.android.sdklib.internal.repository.IPlatformDependency; | |
import com.android.sdklib.internal.repository.MinToolsPackage; | |
import com.android.sdklib.internal.repository.Package; | |
import com.android.sdklib.internal.repository.PlatformPackage; | |
import com.android.sdklib.internal.repository.RepoSource; | |
import com.android.sdklib.internal.repository.RepoSources; | |
import com.android.sdklib.internal.repository.SamplePackage; | |
import com.android.sdklib.internal.repository.ToolPackage; | |
import com.android.sdklib.internal.repository.Package.UpdateInfo; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
/** | |
* The logic to compute which packages to install, based on the choices | |
* made by the user. This adds dependent packages as needed. | |
* <p/> | |
* When the user doesn't provide a selection, looks at local package to find | |
* those that can be updated and compute dependencies too. | |
*/ | |
class UpdaterLogic { | |
/** | |
* Compute which packages to install by taking the user selection | |
* and adding dependent packages as needed. | |
* | |
* When the user doesn't provide a selection, looks at local packages to find | |
* those that can be updated and compute dependencies too. | |
*/ | |
public ArrayList<ArchiveInfo> computeUpdates( | |
Collection<Archive> selectedArchives, | |
RepoSources sources, | |
Package[] localPkgs) { | |
ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>(); | |
ArrayList<Package> remotePkgs = new ArrayList<Package>(); | |
RepoSource[] remoteSources = sources.getSources(); | |
// Create ArchiveInfos out of local (installed) packages. | |
ArchiveInfo[] localArchives = createLocalArchives(localPkgs); | |
if (selectedArchives == null) { | |
selectedArchives = findUpdates(localArchives, remotePkgs, remoteSources); | |
} | |
for (Archive a : selectedArchives) { | |
insertArchive(a, | |
archives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
false /*automated*/); | |
} | |
return archives; | |
} | |
/** | |
* Finds new packages that the user does not have in his/her local SDK | |
* and adds them to the list of archives to install. | |
*/ | |
public void addNewPlatforms(ArrayList<ArchiveInfo> archives, | |
RepoSources sources, | |
Package[] localPkgs) { | |
// Create ArchiveInfos out of local (installed) packages. | |
ArchiveInfo[] localArchives = createLocalArchives(localPkgs); | |
// Find the highest platform installed | |
float currentPlatformScore = 0; | |
float currentSampleScore = 0; | |
float currentAddonScore = 0; | |
float currentDocScore = 0; | |
HashMap<String, Float> currentExtraScore = new HashMap<String, Float>(); | |
for (Package p : localPkgs) { | |
int rev = p.getRevision(); | |
int api = 0; | |
boolean isPreview = false; | |
if (p instanceof IPackageVersion) { | |
AndroidVersion vers = ((IPackageVersion) p).getVersion(); | |
api = vers.getApiLevel(); | |
isPreview = vers.isPreview(); | |
} | |
// The score is 10*api + (1 if preview) + rev/100 | |
// This allows previews to rank above a non-preview and | |
// allows revisions to rank appropriately. | |
float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f; | |
if (p instanceof PlatformPackage) { | |
currentPlatformScore = Math.max(currentPlatformScore, score); | |
} else if (p instanceof SamplePackage) { | |
currentSampleScore = Math.max(currentSampleScore, score); | |
} else if (p instanceof AddonPackage) { | |
currentAddonScore = Math.max(currentAddonScore, score); | |
} else if (p instanceof ExtraPackage) { | |
currentExtraScore.put(((ExtraPackage) p).getPath(), score); | |
} else if (p instanceof DocPackage) { | |
currentDocScore = Math.max(currentDocScore, score); | |
} | |
} | |
RepoSource[] remoteSources = sources.getSources(); | |
ArrayList<Package> remotePkgs = new ArrayList<Package>(); | |
fetchRemotePackages(remotePkgs, remoteSources); | |
Package suggestedDoc = null; | |
for (Package p : remotePkgs) { | |
int rev = p.getRevision(); | |
int api = 0; | |
boolean isPreview = false; | |
if (p instanceof IPackageVersion) { | |
AndroidVersion vers = ((IPackageVersion) p).getVersion(); | |
api = vers.getApiLevel(); | |
isPreview = vers.isPreview(); | |
} | |
float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f; | |
boolean shouldAdd = false; | |
if (p instanceof PlatformPackage) { | |
shouldAdd = score > currentPlatformScore; | |
} else if (p instanceof SamplePackage) { | |
shouldAdd = score > currentSampleScore; | |
} else if (p instanceof AddonPackage) { | |
shouldAdd = score > currentAddonScore; | |
} else if (p instanceof ExtraPackage) { | |
String key = ((ExtraPackage) p).getPath(); | |
shouldAdd = !currentExtraScore.containsKey(key) || | |
score > currentExtraScore.get(key).floatValue(); | |
} else if (p instanceof DocPackage) { | |
// We don't want all the doc, only the most recent one | |
if (score > currentDocScore) { | |
suggestedDoc = p; | |
currentDocScore = score; | |
} | |
} | |
if (shouldAdd) { | |
// We should suggest this package for installation. | |
for (Archive a : p.getArchives()) { | |
if (a.isCompatible()) { | |
insertArchive(a, | |
archives, | |
null /*selectedArchives*/, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
} | |
} | |
} | |
if (suggestedDoc != null) { | |
// We should suggest this package for installation. | |
for (Archive a : suggestedDoc.getArchives()) { | |
if (a.isCompatible()) { | |
insertArchive(a, | |
archives, | |
null /*selectedArchives*/, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
} | |
} | |
} | |
/** | |
* Create a array of {@link ArchiveInfo} based on all local (already installed) | |
* packages. The array is always non-null but may be empty. | |
* <p/> | |
* The local {@link ArchiveInfo} are guaranteed to have one non-null archive | |
* that you can retrieve using {@link ArchiveInfo#getNewArchive()}. | |
*/ | |
protected ArchiveInfo[] createLocalArchives(Package[] localPkgs) { | |
if (localPkgs != null) { | |
ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>(); | |
for (Package p : localPkgs) { | |
// Only accept packages that have one compatible archive. | |
// Local package should have 1 and only 1 compatible archive anyway. | |
for (Archive a : p.getArchives()) { | |
if (a != null && a.isCompatible()) { | |
// We create an "installed" archive info to wrap the local package. | |
// Note that dependencies are not computed since right now we don't | |
// deal with more than one level of dependencies and installed archives | |
// are deemed implicitly accepted anyway. | |
list.add(new LocalArchiveInfo(a)); | |
} | |
} | |
} | |
return list.toArray(new ArchiveInfo[list.size()]); | |
} | |
return new ArchiveInfo[0]; | |
} | |
/** | |
* Find suitable updates to all current local packages. | |
*/ | |
private Collection<Archive> findUpdates(ArchiveInfo[] localArchives, | |
ArrayList<Package> remotePkgs, | |
RepoSource[] remoteSources) { | |
ArrayList<Archive> updates = new ArrayList<Archive>(); | |
fetchRemotePackages(remotePkgs, remoteSources); | |
for (ArchiveInfo ai : localArchives) { | |
Archive na = ai.getNewArchive(); | |
if (na == null) { | |
continue; | |
} | |
Package localPkg = na.getParentPackage(); | |
for (Package remotePkg : remotePkgs) { | |
if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { | |
// Found a suitable update. Only accept the remote package | |
// if it provides at least one compatible archive. | |
for (Archive a : remotePkg.getArchives()) { | |
if (a.isCompatible()) { | |
updates.add(a); | |
break; | |
} | |
} | |
} | |
} | |
} | |
return updates; | |
} | |
private ArchiveInfo insertArchive(Archive archive, | |
ArrayList<ArchiveInfo> outArchives, | |
Collection<Archive> selectedArchives, | |
ArrayList<Package> remotePkgs, | |
RepoSource[] remoteSources, | |
ArchiveInfo[] localArchives, | |
boolean automated) { | |
Package p = archive.getParentPackage(); | |
// Is this an update? | |
Archive updatedArchive = null; | |
for (ArchiveInfo ai : localArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package lp = a.getParentPackage(); | |
if (lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) { | |
updatedArchive = a; | |
} | |
} | |
} | |
// Find dependencies | |
ArchiveInfo[] deps = findDependency(p, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives); | |
// Make sure it's not a dup | |
ArchiveInfo ai = null; | |
for (ArchiveInfo ai2 : outArchives) { | |
Archive a2 = ai2.getNewArchive(); | |
if (a2 != null && a2.getParentPackage().sameItemAs(archive.getParentPackage())) { | |
ai = ai2; | |
break; | |
} | |
} | |
if (ai == null) { | |
ai = new ArchiveInfo( | |
archive, //newArchive | |
updatedArchive, //replaced | |
deps //dependsOn | |
); | |
outArchives.add(ai); | |
} | |
if (deps != null) { | |
for (ArchiveInfo d : deps) { | |
d.addDependencyFor(ai); | |
} | |
} | |
return ai; | |
} | |
/** | |
* Resolves dependencies for a given package. | |
* | |
* Returns null if no dependencies were found. | |
* Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have | |
* at least size 1 and contain no null elements. | |
*/ | |
private ArchiveInfo[] findDependency(Package pkg, | |
ArrayList<ArchiveInfo> outArchives, | |
Collection<Archive> selectedArchives, | |
ArrayList<Package> remotePkgs, | |
RepoSource[] remoteSources, | |
ArchiveInfo[] localArchives) { | |
// Current dependencies can be: | |
// - addon: *always* depends on platform of same API level | |
// - platform: *might* depends on tools of rev >= min-tools-rev | |
// - extra: *might* depends on platform with api >= min-api-level | |
ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>(); | |
if (pkg instanceof IPlatformDependency) { | |
ArchiveInfo ai = findPlatformDependency( | |
(IPlatformDependency) pkg, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives); | |
if (ai != null) { | |
list.add(ai); | |
} | |
} | |
if (pkg instanceof IMinToolsDependency) { | |
ArchiveInfo ai = findToolsDependency( | |
(IMinToolsDependency) pkg, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives); | |
if (ai != null) { | |
list.add(ai); | |
} | |
} | |
if (pkg instanceof IMinApiLevelDependency) { | |
ArchiveInfo ai = findMinApiLevelDependency( | |
(IMinApiLevelDependency) pkg, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives); | |
if (ai != null) { | |
list.add(ai); | |
} | |
} | |
if (list.size() > 0) { | |
return list.toArray(new ArchiveInfo[list.size()]); | |
} | |
return null; | |
} | |
/** | |
* Resolves dependencies on tools. | |
* | |
* A platform or an extra package can both have a min-tools-rev, in which case it | |
* depends on having a tools package of the requested revision. | |
* Finds the tools dependency. If found, add it to the list of things to install. | |
* Returns the archive info dependency, if any. | |
*/ | |
protected ArchiveInfo findToolsDependency( | |
IMinToolsDependency pkg, | |
ArrayList<ArchiveInfo> outArchives, | |
Collection<Archive> selectedArchives, | |
ArrayList<Package> remotePkgs, | |
RepoSource[] remoteSources, | |
ArchiveInfo[] localArchives) { | |
// This is the requirement to match. | |
int rev = pkg.getMinToolsRevision(); | |
if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) { | |
// Well actually there's no requirement. | |
return null; | |
} | |
// First look in locally installed packages. | |
for (ArchiveInfo ai : localArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package p = a.getParentPackage(); | |
if (p instanceof ToolPackage) { | |
if (((ToolPackage) p).getRevision() >= rev) { | |
// We found one already installed. | |
return ai; | |
} | |
} | |
} | |
} | |
// Look in archives already scheduled for install | |
for (ArchiveInfo ai : outArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package p = a.getParentPackage(); | |
if (p instanceof ToolPackage) { | |
if (((ToolPackage) p).getRevision() >= rev) { | |
// The dependency is already scheduled for install, nothing else to do. | |
return ai; | |
} | |
} | |
} | |
} | |
// Otherwise look in the selected archives. | |
if (selectedArchives != null) { | |
for (Archive a : selectedArchives) { | |
Package p = a.getParentPackage(); | |
if (p instanceof ToolPackage) { | |
if (((ToolPackage) p).getRevision() >= rev) { | |
// It's not already in the list of things to install, so add it now | |
return insertArchive(a, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
} | |
} | |
} | |
// Finally nothing matched, so let's look at all available remote packages | |
fetchRemotePackages(remotePkgs, remoteSources); | |
for (Package p : remotePkgs) { | |
if (p instanceof ToolPackage) { | |
if (((ToolPackage) p).getRevision() >= rev) { | |
// It's not already in the list of things to install, so add the | |
// first compatible archive we can find. | |
for (Archive a : p.getArchives()) { | |
if (a.isCompatible()) { | |
return insertArchive(a, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
} | |
} | |
} | |
} | |
// We end up here if nothing matches. We don't have a good platform to match. | |
// We need to indicate this extra depends on a missing platform archive | |
// so that it can be impossible to install later on. | |
return new MissingToolArchiveInfo(rev); | |
} | |
/** | |
* Resolves dependencies on platform for an addon. | |
* | |
* An addon depends on having a platform with the same API level. | |
* | |
* Finds the platform dependency. If found, add it to the list of things to install. | |
* Returns the archive info dependency, if any. | |
*/ | |
protected ArchiveInfo findPlatformDependency( | |
IPlatformDependency pkg, | |
ArrayList<ArchiveInfo> outArchives, | |
Collection<Archive> selectedArchives, | |
ArrayList<Package> remotePkgs, | |
RepoSource[] remoteSources, | |
ArchiveInfo[] localArchives) { | |
// This is the requirement to match. | |
AndroidVersion v = pkg.getVersion(); | |
// Find a platform that would satisfy the requirement. | |
// First look in locally installed packages. | |
for (ArchiveInfo ai : localArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package p = a.getParentPackage(); | |
if (p instanceof PlatformPackage) { | |
if (v.equals(((PlatformPackage) p).getVersion())) { | |
// We found one already installed. | |
return ai; | |
} | |
} | |
} | |
} | |
// Look in archives already scheduled for install | |
for (ArchiveInfo ai : outArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package p = a.getParentPackage(); | |
if (p instanceof PlatformPackage) { | |
if (v.equals(((PlatformPackage) p).getVersion())) { | |
// The dependency is already scheduled for install, nothing else to do. | |
return ai; | |
} | |
} | |
} | |
} | |
// Otherwise look in the selected archives. | |
if (selectedArchives != null) { | |
for (Archive a : selectedArchives) { | |
Package p = a.getParentPackage(); | |
if (p instanceof PlatformPackage) { | |
if (v.equals(((PlatformPackage) p).getVersion())) { | |
// It's not already in the list of things to install, so add it now | |
return insertArchive(a, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
} | |
} | |
} | |
// Finally nothing matched, so let's look at all available remote packages | |
fetchRemotePackages(remotePkgs, remoteSources); | |
for (Package p : remotePkgs) { | |
if (p instanceof PlatformPackage) { | |
if (v.equals(((PlatformPackage) p).getVersion())) { | |
// It's not already in the list of things to install, so add the | |
// first compatible archive we can find. | |
for (Archive a : p.getArchives()) { | |
if (a.isCompatible()) { | |
return insertArchive(a, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
} | |
} | |
} | |
} | |
// We end up here if nothing matches. We don't have a good platform to match. | |
// We need to indicate this addon depends on a missing platform archive | |
// so that it can be impossible to install later on. | |
return new MissingPlatformArchiveInfo(pkg.getVersion()); | |
} | |
/** | |
* Resolves platform dependencies for extras. | |
* An extra depends on having a platform with a minimun API level. | |
* | |
* We try to return the highest API level available above the specified minimum. | |
* Note that installed packages have priority so if one installed platform satisfies | |
* the dependency, we'll use it even if there's a higher API platform available but | |
* not installed yet. | |
* | |
* Finds the platform dependency. If found, add it to the list of things to install. | |
* Returns the archive info dependency, if any. | |
*/ | |
protected ArchiveInfo findMinApiLevelDependency( | |
IMinApiLevelDependency pkg, | |
ArrayList<ArchiveInfo> outArchives, | |
Collection<Archive> selectedArchives, | |
ArrayList<Package> remotePkgs, | |
RepoSource[] remoteSources, | |
ArchiveInfo[] localArchives) { | |
int api = pkg.getMinApiLevel(); | |
if (api == ExtraPackage.MIN_API_LEVEL_NOT_SPECIFIED) { | |
return null; | |
} | |
// Find a platform that would satisfy the requirement. | |
// First look in locally installed packages. | |
for (ArchiveInfo ai : localArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package p = a.getParentPackage(); | |
if (p instanceof PlatformPackage) { | |
if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) { | |
// We found one already installed. | |
return ai; | |
} | |
} | |
} | |
} | |
// Look in archives already scheduled for install | |
int foundApi = 0; | |
ArchiveInfo foundAi = null; | |
for (ArchiveInfo ai : outArchives) { | |
Archive a = ai.getNewArchive(); | |
if (a != null) { | |
Package p = a.getParentPackage(); | |
if (p instanceof PlatformPackage) { | |
if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) { | |
if (api > foundApi) { | |
foundApi = api; | |
foundAi = ai; | |
} | |
} | |
} | |
} | |
} | |
if (foundAi != null) { | |
// The dependency is already scheduled for install, nothing else to do. | |
return foundAi; | |
} | |
// Otherwise look in the selected archives *or* available remote packages | |
// and takes the best out of the two sets. | |
foundApi = 0; | |
Archive foundArchive = null; | |
if (selectedArchives != null) { | |
for (Archive a : selectedArchives) { | |
Package p = a.getParentPackage(); | |
if (p instanceof PlatformPackage) { | |
if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) { | |
if (api > foundApi) { | |
foundApi = api; | |
foundArchive = a; | |
} | |
} | |
} | |
} | |
} | |
// Finally nothing matched, so let's look at all available remote packages | |
fetchRemotePackages(remotePkgs, remoteSources); | |
for (Package p : remotePkgs) { | |
if (p instanceof PlatformPackage) { | |
if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) { | |
if (api > foundApi) { | |
// It's not already in the list of things to install, so add the | |
// first compatible archive we can find. | |
for (Archive a : p.getArchives()) { | |
if (a.isCompatible()) { | |
foundApi = api; | |
foundArchive = a; | |
} | |
} | |
} | |
} | |
} | |
} | |
if (foundArchive != null) { | |
// It's not already in the list of things to install, so add it now | |
return insertArchive(foundArchive, | |
outArchives, | |
selectedArchives, | |
remotePkgs, | |
remoteSources, | |
localArchives, | |
true /*automated*/); | |
} | |
// We end up here if nothing matches. We don't have a good platform to match. | |
// We need to indicate this extra depends on a missing platform archive | |
// so that it can be impossible to install later on. | |
return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/)); | |
} | |
/** Fetch all remote packages only if really needed. */ | |
protected void fetchRemotePackages(ArrayList<Package> remotePkgs, RepoSource[] remoteSources) { | |
if (remotePkgs.size() > 0) { | |
return; | |
} | |
for (RepoSource remoteSrc : remoteSources) { | |
Package[] pkgs = remoteSrc.getPackages(); | |
if (pkgs != null) { | |
nextPackage: for (Package pkg : pkgs) { | |
for (Archive a : pkg.getArchives()) { | |
// Only add a package if it contains at least one compatible archive | |
if (a.isCompatible()) { | |
remotePkgs.add(pkg); | |
continue nextPackage; | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* A {@link LocalArchiveInfo} is an {@link ArchiveInfo} that wraps an already installed | |
* "local" package/archive. | |
* <p/> | |
* In this case, the "new Archive" is still expected to be non null and the | |
* "replaced Archive" isnull. Installed archives are always accepted and never | |
* rejected. | |
* <p/> | |
* Dependencies are not set. | |
*/ | |
private static class LocalArchiveInfo extends ArchiveInfo { | |
public LocalArchiveInfo(Archive localArchive) { | |
super(localArchive, null /*replaced*/, null /*dependsOn*/); | |
} | |
/** Installed archives are always accepted. */ | |
@Override | |
public boolean isAccepted() { | |
return true; | |
} | |
/** Installed archives are never rejected. */ | |
@Override | |
public boolean isRejected() { | |
return false; | |
} | |
} | |
/** | |
* A {@link MissingPlatformArchiveInfo} is an {@link ArchiveInfo} that represents a | |
* package/archive that we <em>really</em> need as a dependency but that we don't have. | |
* <p/> | |
* This is currently used for addons and extras in case we can't find a matching base platform. | |
* <p/> | |
* This kind of archive has specific properties: the new archive to install is null, | |
* there are no dependencies and no archive is being replaced. The info can never be | |
* accepted and is always rejected. | |
*/ | |
private static class MissingPlatformArchiveInfo extends ArchiveInfo { | |
private final AndroidVersion mVersion; | |
/** | |
* Constructs a {@link MissingPlatformArchiveInfo} that will indicate the | |
* given platform version is missing. | |
*/ | |
public MissingPlatformArchiveInfo(AndroidVersion version) { | |
super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/); | |
mVersion = version; | |
} | |
/** Missing archives are never accepted. */ | |
@Override | |
public boolean isAccepted() { | |
return false; | |
} | |
/** Missing archives are always rejected. */ | |
@Override | |
public boolean isRejected() { | |
return true; | |
} | |
@Override | |
public String getShortDescription() { | |
return String.format("Missing SDK Platform Android%1$s, API %2$d", | |
mVersion.isPreview() ? " Preview" : "", | |
mVersion.getApiLevel()); | |
} | |
} | |
/** | |
* A {@link MissingToolArchiveInfo} is an {@link ArchiveInfo} that represents a | |
* package/archive that we <em>really</em> need as a dependency but that we don't have. | |
* <p/> | |
* This is currently used for extras in case we can't find a matching tool revision. | |
* <p/> | |
* This kind of archive has specific properties: the new archive to install is null, | |
* there are no dependencies and no archive is being replaced. The info can never be | |
* accepted and is always rejected. | |
*/ | |
private static class MissingToolArchiveInfo extends ArchiveInfo { | |
private final int mRevision; | |
/** | |
* Constructs a {@link MissingPlatformArchiveInfo} that will indicate the | |
* given platform version is missing. | |
*/ | |
public MissingToolArchiveInfo(int revision) { | |
super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/); | |
mRevision = revision; | |
} | |
/** Missing archives are never accepted. */ | |
@Override | |
public boolean isAccepted() { | |
return false; | |
} | |
/** Missing archives are always rejected. */ | |
@Override | |
public boolean isRejected() { | |
return true; | |
} | |
@Override | |
public String getShortDescription() { | |
return String.format("Missing Android SDK Tools, revision %1$d", mRevision); | |
} | |
} | |
} |