blob: 3ab46740ecd8ff7dc8ecf30a6013c73de51e061a [file] [log] [blame]
/*
* Copyright (C) 2015 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.tools.idea.sdk;
import com.android.annotations.NonNull;
import com.android.sdklib.repository.FullRevision;
import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.sdklib.repository.descriptors.PkgType;
import com.android.sdklib.repository.local.LocalPkgInfo;
import com.android.tools.idea.sdk.remote.RemotePkgInfo;
import com.android.tools.idea.sdk.remote.UpdatablePkgInfo;
import com.google.common.base.Predicate;
import com.google.common.collect.*;
import java.util.Map;
import java.util.Set;
/**
* Store of current local and remote packages, in convenient forms.
*/
public final class SdkPackages {
private final Set<UpdatablePkgInfo> myUpdatedPkgs = Sets.newTreeSet();
private final Set<RemotePkgInfo> myNewPkgs = Sets.newTreeSet();
private final long myTimestampMs;
private Map<String, UpdatablePkgInfo> myConsolidatedPkgs = Maps.newTreeMap();
private LocalPkgInfo[] myLocalPkgInfos = new LocalPkgInfo[0];
private Multimap<PkgType, RemotePkgInfo> myRemotePkgInfos = TreeMultimap.create();
SdkPackages() {
myTimestampMs = System.currentTimeMillis();
}
public SdkPackages(LocalPkgInfo[] localPkgs, Multimap<PkgType, RemotePkgInfo> remotePkgs) {
this();
setLocalPkgInfos(localPkgs);
setRemotePkgInfos(remotePkgs);
}
/**
* Returns the timestamp (in {@link System#currentTimeMillis()} time) when this object was created.
*/
public long getTimestampMs() {
return myTimestampMs;
}
/**
* Returns the set of packages that have local updates available.
* Use {@link LocalPkgInfo#getUpdate()} to retrieve the computed updated candidate.
*
* @return A non-null, possibly empty Set of update candidates.
*/
@NonNull
public Set<UpdatablePkgInfo> getUpdatedPkgs() {
return myUpdatedPkgs;
}
/**
* Returns the set of new remote packages that are not locally present
* and that the user could install.
*
* @return A non-null, possibly empty Set of new install candidates.
*/
@NonNull
public Set<RemotePkgInfo> getNewPkgs() {
return myNewPkgs;
}
/**
* Returns a map of package install ids to {@link UpdatablePackageInfo}s representing all known
* local and remote packages. Remote packages corresponding to local packages will be represented
* by a single item containing both the local and remote info.
* {@see IPkgDesc#getInstallId()}
*/
@NonNull
public Map<String, UpdatablePkgInfo> getConsolidatedPkgs() {
return myConsolidatedPkgs;
}
@NonNull
public LocalPkgInfo[] getLocalPkgInfos() {
return myLocalPkgInfos;
}
public Multimap<PkgType, RemotePkgInfo> getRemotePkgInfos() {
return myRemotePkgInfos;
}
void setLocalPkgInfos(LocalPkgInfo[] packages) {
myLocalPkgInfos = packages;
computeUpdates();
}
void setRemotePkgInfos(Multimap<PkgType, RemotePkgInfo> packages) {
myRemotePkgInfos = Multimaps.filterValues(packages, new Predicate<RemotePkgInfo>() {
@Override
public boolean apply(RemotePkgInfo input) {
return input.hasCompatibleArchive();
}
});
computeUpdates();
}
private void computeUpdates() {
Map<String, UpdatablePkgInfo> newConsolidatedPkgs = Maps.newTreeMap();
UpdatablePkgInfo[] updatablePkgInfos = new UpdatablePkgInfo[myLocalPkgInfos.length];
for (int i = 0; i < myLocalPkgInfos.length; i++) {
updatablePkgInfos[i] = new UpdatablePkgInfo(myLocalPkgInfos[i]);
}
Set<RemotePkgInfo> updates = Sets.newTreeSet();
// Find updates to locally installed packages
for (UpdatablePkgInfo info : updatablePkgInfos) {
IPkgDesc localDesc = info.getLocalInfo().getDesc();
for (RemotePkgInfo remote : myRemotePkgInfos.get(localDesc.getType())) {
if (remote.getPkgDesc().isUpdateFor(localDesc, FullRevision.PreviewComparison.IGNORE)) {
info.addRemote(remote);
myUpdatedPkgs.add(info);
updates.add(remote);
}
}
// the consolidated packages map is always keyed by the non-preview installid, whether or not the UpdatablePackage happens to
// contain a preview package.
newConsolidatedPkgs.put(info.getPkgDesc(true).getBaseInstallId(), info);
}
// Find new packages not yet installed
nextRemote: for (RemotePkgInfo remote : myRemotePkgInfos.values()) {
if (updates.contains(remote)) {
// if package is already a known update, it's not new.
continue nextRemote;
}
IPkgDesc remoteDesc = remote.getPkgDesc();
for (UpdatablePkgInfo info : updatablePkgInfos) {
IPkgDesc localDesc = info.getLocalInfo().getDesc();
if (remoteDesc.compareTo(localDesc) == 0 || remoteDesc.isUpdateFor(localDesc, FullRevision.PreviewComparison.IGNORE) ||
localDesc.isUpdateFor(remoteDesc, FullRevision.PreviewComparison.IGNORE) /* shouldn't happen in the normal case */) {
// if package is same as an installed or is an update for an installed
// one, then it's not new.
continue nextRemote;
}
}
myNewPkgs.add(remote);
String key = remoteDesc.getBaseInstallId();
UpdatablePkgInfo existing = newConsolidatedPkgs.get(key);
if (existing != null) {
existing.addRemote(remote);
}
else {
newConsolidatedPkgs.put(key, new UpdatablePkgInfo(remote));
}
}
myConsolidatedPkgs = newConsolidatedPkgs;
}
}