blob: f78221fe6e0970ad05c92517b44ab1b1bb9793dd [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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 org.jetbrains.android.sdk;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.IAndroidTarget.OptionalLibrary;
import com.android.sdklib.SdkVersionInfo;
import com.android.sdklib.repository.descriptors.PkgType;
import com.android.tools.idea.ddms.adb.AdbService;
import com.android.tools.idea.sdk.IdeSdks;
import com.android.tools.idea.sdk.SelectSdkDialog;
import com.android.tools.idea.sdk.VersionCheck;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.CommonBundle;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.OSProcessManager;
import com.intellij.facet.ProjectFacetManager;
import com.intellij.ide.highlighter.ArchiveFileType;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.roots.JavadocOrderRootType;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.*;
import org.jetbrains.android.actions.AndroidEnableAdbServiceAction;
import org.jetbrains.android.actions.AndroidRunDdmsAction;
import org.jetbrains.android.actions.RunAndroidSdkManagerAction;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.logcat.AdbErrors;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static com.android.SdkConstants.*;
import static com.android.sdklib.IAndroidTarget.RESOURCES;
import static com.android.tools.idea.sdk.Jdks.chooseOrCreateJavaSdk;
import static com.android.tools.idea.sdk.Jdks.createJdk;
import static com.android.tools.idea.startup.AndroidStudioSpecificInitializer.isAndroidStudio;
import static com.android.tools.idea.startup.ExternalAnnotationsSupport.attachJdkAnnotations;
import static com.google.common.base.Joiner.on;
import static com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil.createUniqueSdkName;
import static com.intellij.openapi.roots.ModuleRootModificationUtil.setModuleSdk;
import static com.intellij.openapi.roots.OrderRootType.CLASSES;
import static com.intellij.openapi.roots.OrderRootType.SOURCES;
import static com.intellij.openapi.util.io.FileUtil.toSystemDependentName;
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static com.intellij.openapi.vfs.JarFileSystem.JAR_SEPARATOR;
import static org.jetbrains.android.actions.AndroidEnableAdbServiceAction.setAdbServiceEnabled;
import static org.jetbrains.android.facet.AndroidRootUtil.getProjectPropertyValue;
import static org.jetbrains.android.facet.AndroidRootUtil.getPropertyValue;
import static org.jetbrains.android.sdk.AndroidSdkData.getSdkData;
import static org.jetbrains.android.sdk.AndroidSdkType.DEFAULT_EXTERNAL_DOCUMENTATION_URL;
import static org.jetbrains.android.sdk.AndroidSdkType.SDK_NAME;
import static org.jetbrains.android.util.AndroidCommonUtils.ANNOTATIONS_JAR_RELATIVE_PATH;
import static org.jetbrains.android.util.AndroidCommonUtils.platformToolPath;
import static org.jetbrains.android.util.AndroidUtils.ANDROID_TARGET_PROPERTY;
import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile;
/**
* @author Eugene.Kudelevsky
*/
public final class AndroidSdkUtils {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.sdk.AndroidSdkUtils");
public static final String DEFAULT_PLATFORM_NAME_PROPERTY = "AndroidPlatformName";
public static final String SDK_NAME_PREFIX = "Android ";
public static final String DEFAULT_JDK_NAME = "JDK";
private static AndroidSdkData ourSdkData;
private AndroidSdkUtils() {
}
@NotNull
public static List<VirtualFile> getPlatformAndAddOnJars(@NotNull IAndroidTarget target) {
VirtualFile platformDir = getPlatformDir(target);
if (platformDir == null) {
return Collections.emptyList();
}
VirtualFile androidJar = platformDir.findChild(FN_FRAMEWORK_LIBRARY);
if (androidJar == null) {
return Collections.emptyList();
}
List<VirtualFile> result = Lists.newArrayList();
VirtualFile androidJarRoot = findFileInJarFileSystem(androidJar.getPath());
if (androidJarRoot != null) {
result.add(androidJarRoot);
}
List<OptionalLibrary> libs = target.getAdditionalLibraries();
for (OptionalLibrary lib : libs) {
VirtualFile libRoot = findFileInJarFileSystem(lib.getJar().getAbsolutePath());
if (libRoot != null) {
result.add(libRoot);
}
}
return result;
}
@NotNull
public static List<OrderRoot> getLibraryRootsForTarget(@NotNull IAndroidTarget target,
@Nullable String sdkPath,
boolean addPlatformAndAddOnJars) {
List<OrderRoot> result = Lists.newArrayList();
if (addPlatformAndAddOnJars) {
for (VirtualFile file : getPlatformAndAddOnJars(target)) {
result.add(new OrderRoot(file, CLASSES));
}
}
VirtualFile platformDir = getPlatformDir(target);
if (platformDir == null) return result;
VirtualFile targetDir = platformDir;
if (!target.isPlatform()) {
targetDir = findFileInLocalFileSystem(target.getLocation());
}
boolean docsOrSourcesFound = false;
if (targetDir != null) {
docsOrSourcesFound = addJavaDocAndSources(result, targetDir);
}
VirtualFile sdkDir = sdkPath != null ? findFileInLocalFileSystem(sdkPath) : null;
VirtualFile sourcesDir = null;
if (sdkDir != null) {
docsOrSourcesFound = addJavaDocAndSources(result, sdkDir) || docsOrSourcesFound;
sourcesDir = sdkDir.findChild(FD_PKG_SOURCES);
}
// todo: replace it by target.getPath(SOURCES) when it'll be up to date
if (sourcesDir != null && sourcesDir.isDirectory()) {
VirtualFile platformSourcesDir = sourcesDir.findChild(platformDir.getName());
if (platformSourcesDir != null && platformSourcesDir.isDirectory()) {
result.add(new OrderRoot(platformSourcesDir, SOURCES));
docsOrSourcesFound = true;
}
}
if (!docsOrSourcesFound) {
VirtualFile javadoc = VirtualFileManager.getInstance().findFileByUrl(DEFAULT_EXTERNAL_DOCUMENTATION_URL);
if (javadoc != null) {
result.add(new OrderRoot(javadoc, JavadocOrderRootType.getInstance()));
}
}
String resFolderPath = target.getPath(RESOURCES);
if (resFolderPath != null) {
VirtualFile resFolder = findFileInLocalFileSystem(resFolderPath);
if (resFolder != null) {
result.add(new OrderRoot(resFolder, CLASSES));
}
}
// Explicitly add annotations.jar unless the target platform already provides it (API16+).
if (sdkPath != null && needsAnnotationsJarInClasspath(target)) {
String annotationsJarPath = toSystemIndependentName(sdkPath) + ANNOTATIONS_JAR_RELATIVE_PATH;
VirtualFile annotationsJar = findFileInJarFileSystem(annotationsJarPath);
if (annotationsJar != null) {
result.add(new OrderRoot(annotationsJar, CLASSES));
}
}
return result;
}
@Nullable
private static VirtualFile getPlatformDir(@NotNull IAndroidTarget target) {
String platformPath = target.isPlatform() ? target.getLocation() : target.getParent().getLocation();
return LocalFileSystem.getInstance().refreshAndFindFileByPath(toSystemIndependentName((platformPath)));
}
@Nullable
private static VirtualFile findFileInLocalFileSystem(@NotNull String path) {
return LocalFileSystem.getInstance().findFileByPath(toSystemIndependentName(path));
}
@Nullable
private static VirtualFile findFileInJarFileSystem(@NotNull String path) {
return JarFileSystem.getInstance().findFileByPath(path + JAR_SEPARATOR);
}
/**
* Indicates whether annotations.jar needs to be added to the classpath of an Android SDK. annotations.jar is not needed for API 16
* or newer. The annotations are already included in android.jar.
*/
public static boolean needsAnnotationsJarInClasspath(@NotNull IAndroidTarget target) {
return target.getVersion().getApiLevel() <= 15;
}
@Nullable
private static VirtualFile findJavadocDir(@NotNull VirtualFile dir) {
VirtualFile docsDir = dir.findChild(FD_DOCS);
if (docsDir != null) {
return docsDir.findChild(FD_DOCS_REFERENCE);
}
return null;
}
private static boolean addJavaDocAndSources(@NotNull List<OrderRoot> orderRoots, @NotNull VirtualFile dir) {
boolean found = false;
VirtualFile javadocDir = findJavadocDir(dir);
if (javadocDir != null) {
orderRoots.add(new OrderRoot(javadocDir, JavadocOrderRootType.getInstance()));
found = true;
}
VirtualFile sourcesDir = dir.findChild(FD_SOURCES);
if (sourcesDir != null) {
orderRoots.add(new OrderRoot(sourcesDir, SOURCES));
found = true;
}
return found;
}
@NotNull
public static String getPresentableTargetName(@NotNull IAndroidTarget target) {
IAndroidTarget parentTarget = target.getParent();
String name = target.getName();
if (parentTarget != null) {
name = name + " (" + parentTarget.getVersionName() + ')';
}
return name;
}
/**
* Creates a new IDEA Android SDK. User is prompt for the paths of the Android SDK and JDK if necessary.
*
* @param sdkPath the path of Android SDK.
* @return the created IDEA Android SDK, or {@null} if it was not possible to create it.
*/
@Nullable
public static Sdk createNewAndroidPlatform(@Nullable String sdkPath, boolean promptUser) {
Sdk jdk = chooseOrCreateJavaSdk();
if (sdkPath != null && jdk != null) {
sdkPath = toSystemIndependentName(sdkPath);
IAndroidTarget target = findBestTarget(sdkPath);
if (target != null) {
Sdk sdk = createNewAndroidPlatform(target, sdkPath, chooseNameForNewLibrary(target), jdk, true);
if (sdk != null) {
return sdk;
}
}
}
String jdkPath = jdk == null ? null : jdk.getHomePath();
return promptUser ? promptUserForSdkCreation(null, sdkPath, jdkPath) : null;
}
@Nullable
private static IAndroidTarget findBestTarget(@NotNull String sdkPath) {
AndroidSdkData sdkData = getSdkData(sdkPath);
if (sdkData != null) {
IAndroidTarget[] targets = sdkData.getTargets();
if (targets.length == 1) {
return targets[0];
}
return findBestTarget(targets);
}
return null;
}
@Nullable
private static IAndroidTarget findBestTarget(@NotNull IAndroidTarget[] targets) {
IAndroidTarget bestTarget = null;
int maxApiLevel = -1;
for (IAndroidTarget target : targets) {
AndroidVersion version = target.getVersion();
if (target.isPlatform() && !version.isPreview() && version.getApiLevel() > maxApiLevel) {
bestTarget = target;
maxApiLevel = version.getApiLevel();
}
}
return bestTarget;
}
@Nullable
public static Sdk createNewAndroidPlatform(@NotNull IAndroidTarget target, @NotNull String sdkPath, boolean addRoots) {
return createNewAndroidPlatform(target, sdkPath, chooseNameForNewLibrary(target), addRoots);
}
@Nullable
public static Sdk createNewAndroidPlatform(@NotNull IAndroidTarget target,
@NotNull String sdkPath,
@NotNull String sdkName,
boolean addRoots) {
Sdk jdk = chooseOrCreateJavaSdk();
return jdk != null ? createNewAndroidPlatform(target, sdkPath, sdkName, jdk, addRoots) : null;
}
@Nullable
public static Sdk createNewAndroidPlatform(@NotNull IAndroidTarget target,
@NotNull String sdkPath,
@NotNull String sdkName,
@Nullable Sdk jdk,
boolean addRoots) {
ProjectJdkTable table = ProjectJdkTable.getInstance();
String tmpName = createUniqueSdkName(SDK_NAME, Arrays.asList(table.getAllJdks()));
final Sdk sdk = table.createSdk(tmpName, AndroidSdkType.getInstance());
SdkModificator sdkModificator = sdk.getSdkModificator();
sdkModificator.setHomePath(sdkPath);
sdkModificator.commitChanges();
setUpSdk(sdk, sdkName, table.getAllJdks(), target, jdk, addRoots);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ProjectJdkTable.getInstance().addJdk(sdk);
}
});
return sdk;
}
@NotNull
public static String chooseNameForNewLibrary(@NotNull IAndroidTarget target) {
if (target.isPlatform()) {
return SDK_NAME_PREFIX + target.getVersion().toString() + " Platform";
}
IAndroidTarget parentTarget = target.getParent();
String name = SDK_NAME_PREFIX;
if (parentTarget != null) {
name = name + parentTarget.getVersionName() + ' ';
}
return name + target.getName();
}
public static String getTargetPresentableName(@NotNull IAndroidTarget target) {
return target.isPlatform() ? target.getName() : target.getName() + " (" + target.getVersionName() + ')';
}
public static void setUpSdk(@NotNull Sdk androidSdk,
@NotNull String sdkName,
@NotNull Sdk[] allSdks,
@NotNull IAndroidTarget target,
@Nullable Sdk jdk,
boolean addRoots) {
AndroidSdkAdditionalData data = new AndroidSdkAdditionalData(androidSdk, jdk);
data.setBuildTarget(target);
String name = createUniqueSdkName(sdkName, Arrays.asList(allSdks));
SdkModificator sdkModificator = androidSdk.getSdkModificator();
findAndSetPlatformSources(target, sdkModificator);
sdkModificator.setName(name);
if (jdk != null) {
sdkModificator.setVersionString(jdk.getVersionString());
}
sdkModificator.setSdkAdditionalData(data);
if (addRoots) {
sdkModificator.removeAllRoots();
for (OrderRoot orderRoot : getLibraryRootsForTarget(target, androidSdk.getHomePath(), true)) {
sdkModificator.addRoot(orderRoot.getFile(), orderRoot.getType());
}
attachJdkAnnotations(sdkModificator);
}
sdkModificator.commitChanges();
}
public static void findAndSetPlatformSources(@NotNull IAndroidTarget target, @NotNull SdkModificator sdkModificator) {
File sources = findPlatformSources(target);
if (sources != null) {
VirtualFile virtualFile = findFileByIoFile(sources, true);
if (virtualFile != null) {
for (VirtualFile file : sdkModificator.getRoots(SOURCES)) {
if (file.equals(virtualFile)) {
return;
}
}
sdkModificator.addRoot(virtualFile, SOURCES);
}
}
}
public static boolean targetHasId(@NotNull IAndroidTarget target, @NotNull String id) {
return id.equals(target.getVersion().getApiString()) || id.equals(target.getVersionName());
}
@NotNull
public static Collection<String> getAndroidSdkPathsFromExistingPlatforms() {
List<String> result = Lists.newArrayList();
for (Sdk androidSdk : getAllAndroidSdks()) {
AndroidPlatform androidPlatform = AndroidPlatform.getInstance(androidSdk);
if (androidPlatform != null) {
// Put default platforms in the list before non-default ones so they'll be looked at first.
String sdkPath = toSystemIndependentName(androidPlatform.getSdkData().getLocation().getPath());
if (result.contains(sdkPath)) continue;
if (androidSdk.getName().startsWith(SDK_NAME_PREFIX)) {
result.add(0, sdkPath);
}
else {
result.add(sdkPath);
}
}
}
return result;
}
@NotNull
public static List<Sdk> getAllAndroidSdks() {
List<Sdk> allSdks = ProjectJdkTable.getInstance().getSdksOfType(AndroidSdkType.getInstance());
return allSdks != null ? allSdks : Collections.<Sdk>emptyList();
}
private static boolean tryToSetAndroidPlatform(@NotNull Module module, @NotNull Sdk sdk) {
AndroidPlatform platform = AndroidPlatform.parse(sdk);
if (platform != null) {
setModuleSdk(module, sdk);
return true;
}
return false;
}
private static void setupPlatform(@NotNull Module module) {
String targetHashString = getTargetHashStringFromPropertyFile(module);
if (targetHashString != null && findAndSetSdkWithHashString(module, targetHashString)) {
return;
}
PropertiesComponent component = PropertiesComponent.getInstance();
if (component.isValueSet(DEFAULT_PLATFORM_NAME_PROPERTY)) {
String defaultPlatformName = component.getValue(DEFAULT_PLATFORM_NAME_PROPERTY);
Sdk defaultLib = ProjectJdkTable.getInstance().findJdk(defaultPlatformName, AndroidSdkType.getInstance().getName());
if (defaultLib != null && tryToSetAndroidPlatform(module, defaultLib)) {
return;
}
}
for (Sdk sdk : getAllAndroidSdks()) {
AndroidPlatform platform = AndroidPlatform.getInstance(sdk);
if (platform != null &&
checkSdkRoots(sdk, platform.getTarget(), false) &&
tryToSetAndroidPlatform(module, sdk)) {
component.setValue(DEFAULT_PLATFORM_NAME_PROPERTY, sdk.getName());
return;
}
}
}
@Nullable
public static Sdk findSuitableAndroidSdk(@NotNull String targetHash) {
Set<String> foundSdkHomePaths = Sets.newHashSet();
List<Sdk> notCompatibleSdks = Lists.newArrayList();
for (Sdk sdk : getAllAndroidSdks()) {
AndroidSdkAdditionalData originalData = getAndroidSdkAdditionalData(sdk);
if (originalData == null) {
continue;
}
String sdkHomePath = sdk.getHomePath();
if (!foundSdkHomePaths.contains(sdkHomePath) && targetHash.equals(originalData.getBuildTargetHashString())) {
if (VersionCheck.isCompatibleVersion(sdkHomePath)) {
return sdk;
}
notCompatibleSdks.add(sdk);
if (sdkHomePath != null) {
foundSdkHomePaths.add(sdkHomePath);
}
}
}
return notCompatibleSdks.isEmpty() ? null : notCompatibleSdks.get(0);
}
@Nullable
private static String getTargetHashStringFromPropertyFile(@NotNull Module module) {
Pair<String, VirtualFile> targetProp = getProjectPropertyValue(module, ANDROID_TARGET_PROPERTY);
return targetProp != null ? targetProp.getFirst() : null;
}
private static boolean findAndSetSdkWithHashString(@NotNull Module module, @NotNull String targetHashString) {
Pair<String, VirtualFile> sdkDirProperty = getPropertyValue(module, FN_LOCAL_PROPERTIES, "sdk.dir");
String sdkDir = sdkDirProperty != null ? sdkDirProperty.getFirst() : null;
return findAndSetSdk(module, targetHashString, sdkDir);
}
/**
* Finds a matching Android SDK and sets it in the given module.
*
* @param module the module to set the found SDK to.
* @param targetHashString compile target.
* @param sdkPath path, in the file system, of the Android SDK.
* @return {@code true} if a matching Android SDK was found and set in the module; {@code false} otherwise.
*/
public static boolean findAndSetSdk(@NotNull Module module, @NotNull String targetHashString, @Nullable String sdkPath) {
if (sdkPath != null) {
sdkPath = toSystemIndependentName(sdkPath);
}
Sdk sdk = findSuitableAndroidSdk(targetHashString);
if (sdk != null) {
setModuleSdk(module, sdk);
return true;
}
if (sdkPath != null && tryToCreateAndSetAndroidSdk(module, sdkPath, targetHashString)) {
return true;
}
String androidHomeValue = System.getenv(ANDROID_HOME_ENV);
if (androidHomeValue != null && tryToCreateAndSetAndroidSdk(module, toSystemIndependentName(androidHomeValue), targetHashString)) {
return true;
}
for (String dir : getAndroidSdkPathsFromExistingPlatforms()) {
if (tryToCreateAndSetAndroidSdk(module, dir, targetHashString)) {
return true;
}
}
return false;
}
public static void clearLocalPkgInfo(@NotNull Sdk sdk) {
AndroidSdkAdditionalData data = getAndroidSdkAdditionalData(sdk);
if (data == null) {
return;
}
if (data.getAndroidPlatform() != null) {
data.getAndroidPlatform().getSdkData().getLocalSdk().clearLocalPkg(PkgType.PKG_ALL);
}
data.clearAndroidPlatform();
}
/**
* Reload SDK information and update the source root of the SDK.
*/
public static void updateSdkSourceRoot(@NotNull Sdk sdk) {
clearLocalPkgInfo(sdk);
AndroidPlatform platform = AndroidPlatform.getInstance(sdk);
if (platform != null) {
IAndroidTarget target = platform.getTarget();
SdkModificator sdkModificator = sdk.getSdkModificator();
sdkModificator.removeRoots(SOURCES);
findAndSetPlatformSources(target, sdkModificator);
sdkModificator.commitChanges();
}
}
@VisibleForTesting
static boolean tryToCreateAndSetAndroidSdk(@NotNull Module module, @NotNull String sdkPath, @NotNull String targetHashString) {
File path = new File(toSystemDependentName(sdkPath));
Sdk sdk = tryToCreateAndroidSdk(path, targetHashString);
if (sdk != null) {
setModuleSdk(module, sdk);
return true;
}
return false;
}
@Nullable
public static Sdk tryToCreateAndroidSdk(@NotNull File sdkPath, @NotNull String targetHashString) {
AndroidSdkData sdkData = getSdkData(sdkPath);
if (sdkData != null) {
sdkData.getLocalSdk().clearLocalPkg(PkgType.PKG_ALL);
IAndroidTarget target = sdkData.findTargetByHashString(targetHashString);
if (target != null) {
return createNewAndroidPlatform(target, sdkData.getLocation().getPath(), true);
}
}
return null;
}
@Nullable
private static Sdk promptUserForSdkCreation(@Nullable final IAndroidTarget target,
@Nullable final String androidSdkPath,
@Nullable final String jdkPath) {
final Ref<Sdk> sdkRef = new Ref<Sdk>();
Runnable task = new Runnable() {
@Override
public void run() {
SelectSdkDialog dlg = new SelectSdkDialog(jdkPath, androidSdkPath);
dlg.setModal(true);
if (dlg.showAndGet()) {
Sdk sdk = createNewAndroidPlatform(target, dlg.getAndroidHome(), dlg.getJdkHome());
sdkRef.set(sdk);
if (sdk != null) {
RunAndroidSdkManagerAction.updateInWelcomePage(dlg.getContentPanel());
}
}
}
};
Application application = ApplicationManager.getApplication();
if (application.isDispatchThread()) {
task.run();
return sdkRef.get();
}
application.invokeAndWait(task, ModalityState.any());
return sdkRef.get();
}
@Nullable
private static Sdk createNewAndroidPlatform(@Nullable IAndroidTarget target, @NotNull String androidSdkPath, @NotNull String jdkPath) {
if (isNotEmpty(jdkPath)) {
jdkPath = toSystemIndependentName(jdkPath);
Sdk jdk = createJdk(jdkPath);
if (jdk != null) {
androidSdkPath = toSystemIndependentName(androidSdkPath);
if (target == null) {
target = findBestTarget(androidSdkPath);
}
if (target != null) {
return createNewAndroidPlatform(target, androidSdkPath, chooseNameForNewLibrary(target), jdk, true);
}
}
}
return null;
}
public static void setupAndroidPlatformIfNecessary(@NotNull Module module, boolean forceImportFromProperties) {
Sdk currentSdk = ModuleRootManager.getInstance(module).getSdk();
if (currentSdk == null || !isAndroidSdk(currentSdk)) {
setupPlatform(module);
return;
}
if (forceImportFromProperties) {
AndroidPlatform platform = AndroidPlatform.getInstance(currentSdk);
if (platform != null) {
String targetHashString = getTargetHashStringFromPropertyFile(module);
String currentTargetHashString = platform.getTarget().hashString();
if (targetHashString != null && !targetHashString.equals(currentTargetHashString)) {
findAndSetSdkWithHashString(module, targetHashString);
}
}
}
}
public static void openModuleDependenciesConfigurable(@NotNull Module module) {
ProjectSettingsService.getInstance(module.getProject()).openModuleDependenciesSettings(module, null);
}
@Nullable
public static Sdk findAppropriateAndroidPlatform(@NotNull IAndroidTarget target, @NotNull AndroidSdkData sdkData, boolean forMaven) {
for (Sdk sdk : ProjectJdkTable.getInstance().getAllJdks()) {
String homePath = sdk.getHomePath();
if (homePath != null && isAndroidSdk(sdk)) {
AndroidSdkData currentSdkData = getSdkData(homePath);
if (sdkData.equals(currentSdkData)) {
AndroidSdkAdditionalData data = getAndroidSdkAdditionalData(sdk);
if (data != null) {
IAndroidTarget currentTarget = data.getBuildTarget(currentSdkData);
if (currentTarget != null &&
target.hashString().equals(currentTarget.hashString()) &&
checkSdkRoots(sdk, target, forMaven)) {
return sdk;
}
}
}
}
}
return null;
}
public static boolean isAndroidSdk(@NotNull Sdk sdk) {
return sdk.getSdkType() == AndroidSdkType.getInstance();
}
public static boolean checkSdkRoots(@NotNull Sdk sdk, @NotNull IAndroidTarget target, boolean forMaven) {
String homePath = sdk.getHomePath();
if (homePath == null) {
return false;
}
AndroidSdkAdditionalData sdkAdditionalData = getAndroidSdkAdditionalData(sdk);
Sdk javaSdk = sdkAdditionalData != null ? sdkAdditionalData.getJavaSdk() : null;
if (javaSdk == null) {
return false;
}
Set<VirtualFile> filesInSdk = Sets.newHashSet(sdk.getRootProvider().getFiles(CLASSES));
List<VirtualFile> platformAndAddOnJars = getPlatformAndAddOnJars(target);
for (VirtualFile file : platformAndAddOnJars) {
if (filesInSdk.contains(file) == forMaven) {
return false;
}
}
boolean containsJarFromJdk = false;
for (VirtualFile file : javaSdk.getRootProvider().getFiles(CLASSES)) {
if (file.getFileType() instanceof ArchiveFileType && filesInSdk.contains(file)) {
containsJarFromJdk = true;
}
}
return containsJarFromJdk == forMaven;
}
@Nullable
public static File getAdb(@NotNull Project project) {
AndroidSdkData data = getProjectSdkData(project);
if (data == null) {
data = getFirstAndroidModuleSdkData(project);
}
File adb = data == null ? null : new File(data.getLocation(), platformToolPath(FN_ADB));
return adb != null && adb.exists() ? adb : null;
}
@Nullable
private static AndroidSdkData getFirstAndroidModuleSdkData(Project project) {
List<AndroidFacet> facets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID);
for (AndroidFacet facet : facets) {
AndroidPlatform androidPlatform = facet.getConfiguration().getAndroidPlatform();
if (androidPlatform != null) {
return androidPlatform.getSdkData();
}
}
return null;
}
@Nullable
private static AndroidSdkData getProjectSdkData(Project project) {
Sdk projectSdk = ProjectRootManager.getInstance(project).getProjectSdk();
if (projectSdk != null) {
AndroidPlatform platform = AndroidPlatform.getInstance(projectSdk);
return platform != null ? platform.getSdkData() : null;
}
return null;
}
@Nullable
public static AndroidSdkAdditionalData getAndroidSdkAdditionalData(@NotNull Sdk sdk) {
SdkAdditionalData sdkAdditionalData = sdk.getSdkAdditionalData();
return sdkAdditionalData instanceof AndroidSdkAdditionalData ? (AndroidSdkAdditionalData)sdkAdditionalData : null;
}
public static boolean activateDdmsIfNecessary(@NotNull Project project) {
if (AndroidEnableAdbServiceAction.isAdbServiceEnabled()) {
AndroidDebugBridge bridge = getDebugBridge(project);
if (bridge != null && AdbService.isDdmsCorrupted(bridge)) {
LOG.info("DDMLIB is corrupted and will be restarted");
AdbService.getInstance().restartDdmlib(project);
}
}
else {
final OSProcessHandler ddmsProcessHandler = AndroidRunDdmsAction.getDdmsProcessHandler();
if (ddmsProcessHandler != null) {
String message = "Monitor will be closed to enable ADB integration. Continue?";
int result = Messages.showYesNoDialog(project, message, "ADB Integration", Messages.getQuestionIcon());
if (result != Messages.YES) {
return false;
}
Runnable destroyingRunnable = new Runnable() {
@Override
public void run() {
if (!ddmsProcessHandler.isProcessTerminated()) {
OSProcessManager.getInstance().killProcessTree(ddmsProcessHandler.getProcess());
ddmsProcessHandler.waitFor();
}
}
};
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(destroyingRunnable, "Closing Monitor", true, project)) {
return false;
}
setAdbServiceEnabled(project, true);
return true;
}
int result = Messages.showYesNoDialog(project, AndroidBundle.message("android.ddms.disabled.error"),
AndroidBundle.message("android.ddms.disabled.dialog.title"), Messages.getQuestionIcon());
if (result != Messages.YES) {
return false;
}
setAdbServiceEnabled(project, true);
}
return true;
}
public static boolean isAndroidSdkAvailable() {
return tryToChooseAndroidSdk() != null;
}
/**
* @return AndroidSdkData for the current SDK. In the normal case, this should always be set up. During the first run or if for some
* reason we can't find the SDK this can be null.
*/
@Nullable
public static AndroidSdkData tryToChooseAndroidSdk() {
if (ourSdkData == null) {
if (isAndroidStudio()) {
File path = IdeSdks.getAndroidSdkPath();
if (path != null) {
ourSdkData = getSdkData(path.getPath());
if (ourSdkData != null) {
return ourSdkData;
}
}
}
for (String s : getAndroidSdkPathsFromExistingPlatforms()) {
ourSdkData = getSdkData(s);
if (ourSdkData != null) {
break;
}
}
}
return ourSdkData;
}
public static void setSdkData(@Nullable AndroidSdkData data) {
ourSdkData = data;
}
/**
* Finds the root source code folder for the given android target, if any
*/
@Nullable
public static File findPlatformSources(@NotNull IAndroidTarget target) {
String path = target.getPath(IAndroidTarget.SOURCES);
if (path != null) {
File platformSource = new File(path);
if (platformSource.isDirectory()) {
return platformSource;
}
}
return null;
}
/**
* For a given target, returns a brief user-facing string that describes the platform, including the API level, platform version number,
* and codename. Does the right thing with pre-release platforms.
*/
@NotNull
public static String getTargetLabel(@NotNull IAndroidTarget target) {
if (!target.isPlatform()) {
return String.format("%1$s (API %2$s)", target.getFullName(), target.getVersion().getApiString());
}
AndroidVersion version = target.getVersion();
if (version.isPreview()) {
return String.format("API %d+: %s", target.getVersion().getApiLevel(), target.getName());
}
String name = SdkVersionInfo.getAndroidName(target.getVersion().getApiLevel());
if (name != null) {
return name;
}
String release = target.getProperty("ro.build.version.release"); //$NON-NLS-1$
if (release != null) {
return String.format("API %1$d: Android %2$s", version.getApiLevel(), release);
}
return String.format("API %1$d", version.getApiLevel());
}
@Nullable
public static AndroidDebugBridge getDebugBridge(@NotNull Project project) {
ApplicationManager.getApplication().assertIsDispatchThread();
AndroidSdkData data = getProjectSdkData(project);
if (data == null) {
data = getFirstAndroidModuleSdkData(project);
}
if (data == null) {
return null;
}
AndroidDebugBridge bridge = null;
boolean retry;
do {
File adb = getAdb(project);
if (adb == null) {
LOG.error("Unable to locate adb within SDK");
return null;
}
Future<AndroidDebugBridge> future = AdbService.getInstance().getDebugBridge(adb);
MyMonitorBridgeConnectionTask task = new MyMonitorBridgeConnectionTask(project, future);
ProgressManager.getInstance().run(task);
if (task.wasCanceled()) { // if the user cancelled the dialog
return null;
}
retry = false;
try {
bridge = future.get();
}
catch (InterruptedException e) {
break;
}
catch (ExecutionException e) {
// timed out waiting for bridge, ask the user what to do
String adbErrors = on('\n').join(AdbErrors.getErrors());
String message = "ADB not responding. If you'd like to retry, then please manually kill \"" + FN_ADB + "\" and click 'Restart'";
if (!adbErrors.isEmpty()) {
message += "\nErrors from ADB:\n" + adbErrors;
}
retry = Messages.showYesNoDialog(project, message, CommonBundle.getErrorTitle(), "&Restart", "&Cancel", Messages.getErrorIcon()) ==
Messages.YES;
}
}
while (retry);
return bridge;
}
/**
* @return URL for the Android SDK download for the current platform, or {@code null} if the platform was not recognized.
*/
@Nullable
public static String getSdkDownloadUrl() {
if (SystemInfo.isLinux) {
return "https://dl.google.com/android/android-sdk_r22.6.2-linux.tgz";
}
else if (SystemInfo.isWindows) {
return "https://dl.google.com/android/android-sdk_r22.6.2-windows.zip";
}
else if (SystemInfo.isMac) {
return "https://dl.google.com/android/android-sdk_r22.6.2-macosx.zip";
}
return null;
}
private static class MyMonitorBridgeConnectionTask extends Task.Modal {
private final Future<AndroidDebugBridge> myFuture;
private boolean myCancelled; // set/read only on EDT
public MyMonitorBridgeConnectionTask(@Nullable Project project, Future<AndroidDebugBridge> future) {
super(project, "Waiting for adb", true);
myFuture = future;
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
while (!myFuture.isDone()) {
try {
myFuture.get(200, TimeUnit.MILLISECONDS);
}
catch (Exception ignored) {
// all we need to know is whether the future completed or not..
}
if (indicator.isCanceled()) {
return;
}
}
}
@Override
public void onCancel() {
myCancelled = true;
}
public boolean wasCanceled() {
return myCancelled;
}
}
}