blob: 24d88da9b5e266b7498613dd291f9d910920bc21 [file] [log] [blame]
/*
* Copyright (C) 2014 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.welcome.install;
import com.android.sdklib.SdkManager;
import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.sdklib.repository.descriptors.PkgType;
import com.android.sdklib.repository.local.LocalPkgInfo;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.idea.sdk.remote.RemotePkgInfo;
import com.android.tools.idea.sdk.remote.UpdatablePkgInfo;
import com.android.tools.idea.sdk.remote.Update;
import com.android.tools.idea.sdk.remote.UpdateResult;
import com.android.tools.idea.sdk.remote.internal.updater.SdkUpdaterNoWindow;
import com.android.utils.ILogger;
import com.android.utils.NullLogger;
import com.google.common.base.Function;
import com.google.common.collect.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Installs SDK components.
*/
public final class ComponentInstaller {
@Nullable private final Multimap<PkgType, RemotePkgInfo> myRemotePackages;
public ComponentInstaller(@Nullable Multimap<PkgType, RemotePkgInfo> remotePackages) {
myRemotePackages = remotePackages;
}
private static Set<String> getPackageIds(Iterable<LocalPkgInfo> localPackages) {
Set<String> toUpdate = Sets.newHashSet();
for (LocalPkgInfo localPkgInfo : localPackages) {
toUpdate.add(localPkgInfo.getDesc().getInstallId());
}
return toUpdate;
}
private static List<LocalPkgInfo> getInstalledPackages(@NotNull SdkManager manager, @NotNull Set<String> toInstall) {
LocalSdk localSdk = manager.getLocalSdk();
LocalPkgInfo[] installed = localSdk.getPkgsInfos(EnumSet.allOf(PkgType.class));
List<LocalPkgInfo> toCheckForUpdate = Lists.newArrayListWithCapacity(installed.length);
for (LocalPkgInfo info : installed) {
if (toInstall.contains(info.getDesc().getInstallId())) {
toCheckForUpdate.add(info);
}
}
return toCheckForUpdate;
}
private Iterable<LocalPkgInfo> getOldPackages(Collection<LocalPkgInfo> installed) {
if (myRemotePackages != null) {
LocalPkgInfo[] packagesArray = ArrayUtil.toObjectArray(installed, LocalPkgInfo.class);
UpdateResult result = Update.computeUpdates(packagesArray, myRemotePackages);
return Iterables.transform(result.getUpdatedPkgs(), new Function<UpdatablePkgInfo, LocalPkgInfo>() {
@Override
public LocalPkgInfo apply(@Nullable UpdatablePkgInfo input) {
return input.getLocalInfo();
}
});
}
else {
return installed; // We should try reinstalling all...
}
}
private Set<String> getRequiredPackages(@NotNull Iterable<? extends InstallableComponent> components) {
// TODO: Prompt about connection in handoff case?
Set<String> packages = Sets.newHashSet();
for (InstallableComponent component : components) {
for (IPkgDesc pkg : component.getRequiredSdkPackages(myRemotePackages)) {
if (pkg != null) {
packages.add(pkg.getInstallId());
}
}
}
return packages;
}
/**
* Returns a list of package install IDs for packages that are missing or outdated.
*
* @param manager SDK manager instance or <code>null</code> if this is a new install.
* @param defaultUpdateAvailable If true, and if remote package information is not available, assume each package may have an update and
* try to reinstall. If false and remote package information not available, assume no updates are available.
*/
public ArrayList<String> getPackagesToInstall(@Nullable SdkManager manager, @NotNull Iterable<? extends InstallableComponent> components,
boolean defaultUpdateAvailable) {
Set<String> toInstall = getRequiredPackages(components);
if (manager == null) {
return Lists.newArrayList(toInstall);
}
else {
List<LocalPkgInfo> installed = getInstalledPackages(manager, toInstall);
if (!installed.isEmpty()) {
toInstall.removeAll(getPackageIds(installed));
if (myRemotePackages != null || defaultUpdateAvailable) {
toInstall.addAll(getPackageIds(getOldPackages(installed)));
}
}
return Lists.newArrayList(toInstall);
}
}
/**
* Returns a collection of package info objects for packages that will be installed.
*/
public Collection<RemotePkgInfo> getPackagesToInstallInfos(@Nullable String sdkPath,
@NotNull Iterable<? extends InstallableComponent> components) {
if (!StringUtil.isEmptyOrSpaces(sdkPath) && myRemotePackages != null) {
SdkManager sdkManager = SdkManager.createManager(sdkPath, new NullLogger());
if (sdkManager != null) {
Set<String> packagesToInstall = ImmutableSet
.copyOf(new ComponentInstaller(myRemotePackages).getPackagesToInstall(sdkManager, components, true));
Set<RemotePkgInfo> remotePackages = Sets.newHashSetWithExpectedSize(packagesToInstall.size());
for (RemotePkgInfo remotePkgInfo : myRemotePackages.values()) {
if (packagesToInstall.contains(remotePkgInfo.getPkgDesc().getInstallId())) {
remotePackages.add(remotePkgInfo);
}
}
return remotePackages;
}
}
return ImmutableSet.of();
}
public void installPackages(@NotNull SdkManager manager, @NotNull ArrayList<String> packages, ILogger logger) throws WizardException {
SdkUpdaterNoWindow updater = new SdkUpdaterNoWindow(manager.getLocation(), manager, logger, false, true, null, null);
updater.updateAll(packages, true, false, null, false);
}
}