/*
* 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.SdkConstants;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
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.DefaultSdks;
import com.android.tools.idea.sdk.Jdks;
import com.android.tools.idea.sdk.SelectSdkDialog;
import com.android.tools.idea.sdk.VersionCheck;
import com.android.tools.idea.startup.AndroidStudioSpecificInitializer;
import com.android.tools.idea.startup.ExternalAnnotationsSupport;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
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.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.roots.*;
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.io.FileUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.util.ObjectUtils;
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.facet.AndroidRootUtil;
import org.jetbrains.android.logcat.AdbErrors;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
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;
/**
* @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() {
}
@Nullable
private static VirtualFile getPlatformDir(@NotNull IAndroidTarget target) {
String platformPath = target.isPlatform() ? target.getLocation() : target.getParent().getLocation();
return LocalFileSystem.getInstance().refreshAndFindFileByPath(FileUtil.toSystemIndependentName((platformPath)));
}
@NotNull
public static List<VirtualFile> getPlatformAndAddOnJars(@NotNull IAndroidTarget target) {
VirtualFile platformDir = getPlatformDir(target);
if (platformDir == null) {
return Collections.emptyList();
}
VirtualFile androidJar = platformDir.findChild(SdkConstants.FN_FRAMEWORK_LIBRARY);
if (androidJar == null) {
return Collections.emptyList();
}
List<VirtualFile> result = Lists.newArrayList();
VirtualFile androidJarRoot = JarFileSystem.getInstance().findFileByPath(androidJar.getPath() + JarFileSystem.JAR_SEPARATOR);
if (androidJarRoot != null) {
result.add(androidJarRoot);
}
IAndroidTarget.IOptionalLibrary[] libs = target.getOptionalLibraries();
if (libs != null) {
for (IAndroidTarget.IOptionalLibrary lib : libs) {
VirtualFile libRoot = JarFileSystem.getInstance().findFileByPath(lib.getJarPath() + JarFileSystem.JAR_SEPARATOR);
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, OrderRootType.CLASSES));
}
}
VirtualFile platformDir = getPlatformDir(target);
if (platformDir == null) return result;
VirtualFile targetDir = platformDir;
if (!target.isPlatform()) {
targetDir = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName((target.getLocation())));
}
boolean docsOrSourcesFound = false;
if (targetDir != null) {
docsOrSourcesFound = addJavaDocAndSources(result, targetDir);
}
VirtualFile sdkDir = sdkPath != null ? LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName((sdkPath))) : null;
VirtualFile sourcesDir = null;
if (sdkDir != null) {
docsOrSourcesFound = addJavaDocAndSources(result, sdkDir) || docsOrSourcesFound;
sourcesDir = sdkDir.findChild(SdkConstants.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, OrderRootType.SOURCES));
docsOrSourcesFound = true;
}
}
if (!docsOrSourcesFound) {
VirtualFile f = VirtualFileManager.getInstance().findFileByUrl(AndroidSdkType.DEFAULT_EXTERNAL_DOCUMENTATION_URL);
if (f != null) {
result.add(new OrderRoot(f, JavadocOrderRootType.getInstance()));
}
}
String resFolderPath = target.getPath(IAndroidTarget.RESOURCES);
if (resFolderPath != null) {
VirtualFile resFolder = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName((resFolderPath)));
if (resFolder != null) {
result.add(new OrderRoot(resFolder, OrderRootType.CLASSES));
}
}
if (sdkPath != null) {
// todo: check if we should do it for new android platforms (api_level >= 15)
JarFileSystem jarFileSystem = JarFileSystem.getInstance();
String annotationsJarPath =
FileUtil.toSystemIndependentName(sdkPath) + AndroidCommonUtils.ANNOTATIONS_JAR_RELATIVE_PATH + JarFileSystem.JAR_SEPARATOR;
VirtualFile annotationsJar = jarFileSystem.findFileByPath(annotationsJarPath);
if (annotationsJar != null) {
result.add(new OrderRoot(annotationsJar, OrderRootType.CLASSES));
}
}
return result;
}
@Nullable
private static VirtualFile findJavadocDir(@NotNull VirtualFile dir) {
VirtualFile docsDir = dir.findChild(SdkConstants.FD_DOCS);
if (docsDir != null) {
return docsDir.findChild(SdkConstants.FD_DOCS_REFERENCE);
}
return null;
}
private static boolean addJavaDocAndSources(@NotNull List<OrderRoot> list, @NotNull VirtualFile dir) {
boolean found = false;
VirtualFile javadocDir = findJavadocDir(dir);
if (javadocDir != null) {
list.add(new OrderRoot(javadocDir, JavadocOrderRootType.getInstance()));
found = true;
}
VirtualFile sourcesDir = dir.findChild(SdkConstants.FD_SOURCES);
if (sourcesDir != null) {
list.add(new OrderRoot(sourcesDir, OrderRootType.SOURCES));
found = true;
}
return found;
}
public static String getPresentableTargetName(@NotNull IAndroidTarget target) {
IAndroidTarget parentTarget = target.getParent();
if (parentTarget != null) {
return target.getName() + " (" + parentTarget.getVersionName() + ')';
}
return target.getName();
}
/**
* 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 = Jdks.chooseOrCreateJavaSdk();
if (sdkPath != null && jdk != null) {
sdkPath = FileUtil.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 = AndroidSdkData.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 = Jdks.chooseOrCreateJavaSdk();
if (jdk == null) {
return null;
}
return createNewAndroidPlatform(target, sdkPath, sdkName, jdk, addRoots);
}
@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 = SdkConfigurationUtil.createUniqueSdkName(AndroidSdkType.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();
if (parentTarget != null) {
return SDK_NAME_PREFIX + parentTarget.getVersionName() + ' ' + target.getName();
}
return SDK_NAME_PREFIX + 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 = SdkConfigurationUtil.createUniqueSdkName(sdkName, Arrays.asList(allSdks));
SdkModificator sdkModificator = androidSdk.getSdkModificator();
findAndSetPlatformSources(target, sdkModificator);
sdkModificator.setName(name);
if (jdk != null) {
//noinspection ConstantConditions
sdkModificator.setVersionString(jdk.getVersionString());
}
sdkModificator.setSdkAdditionalData(data);
if (addRoots) {
for (OrderRoot orderRoot : getLibraryRootsForTarget(target, androidSdk.getHomePath(), true)) {
sdkModificator.addRoot(orderRoot.getFile(), orderRoot.getType());
}
ExternalAnnotationsSupport.attachJdkAnnotations(sdkModificator);
}
sdkModificator.commitChanges();
}
public static void findAndSetPlatformSources(@NotNull IAndroidTarget target, @NotNull SdkModificator sdkModificator) {
File sources = findPlatformSources(target);
if (sources != null) {
VirtualFile virtualFile = VfsUtil.findFileByIoFile(sources, true);
if (virtualFile != null) {
sdkModificator.addRoot(virtualFile, OrderRootType.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<Sdk> androidSdks = getAllAndroidSdks();
List<String> result = Lists.newArrayList();
for (Sdk androidSdk : androidSdks) {
AndroidSdkAdditionalData data = (AndroidSdkAdditionalData)androidSdk.getSdkAdditionalData();
if (data != null) {
AndroidPlatform androidPlatform = data.getAndroidPlatform();
if (androidPlatform != null) {
// Put default platforms in the list before non-default ones so they'll be looked at first.
String sdkPath = FileUtil.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 ObjectUtils.notNull(allSdks, Collections.<Sdk>emptyList());
}
private static boolean tryToSetAndroidPlatform(Module module, Sdk sdk) {
AndroidPlatform platform = AndroidPlatform.parse(sdk);
if (platform != null) {
ModuleRootModificationUtil.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()) {
AndroidSdkAdditionalData data = (AndroidSdkAdditionalData)sdk.getSdkAdditionalData();
if (data != null) {
AndroidPlatform platform = data.getAndroidPlatform();
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()) {
SdkAdditionalData originalData = sdk.getSdkAdditionalData();
if (!(originalData instanceof AndroidSdkAdditionalData)) {
continue;
}
AndroidSdkAdditionalData data = (AndroidSdkAdditionalData)originalData;
AndroidPlatform androidPlatform = data.getAndroidPlatform();
if (androidPlatform == null) {
continue;
}
String sdkHomePath = sdk.getHomePath();
if (!foundSdkHomePaths.contains(sdkHomePath) && targetHash.equals(androidPlatform.getTarget().hashString())) {
if (VersionCheck.isCompatibleVersion(sdkHomePath)) {
return sdk;
}
notCompatibleSdks.add(sdk);
if (sdkHomePath != null) {
foundSdkHomePaths.add(sdkHomePath);
}
}
}
if (!notCompatibleSdks.isEmpty()) {
// We got here because we have SDKs but none of them have a compatible Tools version. Pick the first one.
return notCompatibleSdks.get(0);
}
return null;
}
@Nullable
private static String getTargetHashStringFromPropertyFile(@NotNull Module module) {
Pair<String, VirtualFile> targetProp = AndroidRootUtil.getProjectPropertyValue(module, AndroidUtils.ANDROID_TARGET_PROPERTY);
return targetProp != null ? targetProp.getFirst() : null;
}
private static boolean findAndSetSdkWithHashString(@NotNull Module module, @NotNull String targetHashString) {
Pair<String, VirtualFile> sdkDirProperty = AndroidRootUtil.getPropertyValue(module, SdkConstants.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 = FileUtil.toSystemIndependentName(sdkPath);
}
Sdk sdk = findSuitableAndroidSdk(targetHashString);
if (sdk != null) {
ModuleRootModificationUtil.setModuleSdk(module, sdk);
return true;
}
if (sdkPath != null && tryToCreateAndSetAndroidSdk(module, sdkPath, targetHashString)) {
return true;
}
String androidHomeValue = System.getenv(SdkConstants.ANDROID_HOME_ENV);
if (androidHomeValue != null &&
tryToCreateAndSetAndroidSdk(module, FileUtil.toSystemIndependentName(androidHomeValue), targetHashString)) {
return true;
}
for (String dir : getAndroidSdkPathsFromExistingPlatforms()) {
if (tryToCreateAndSetAndroidSdk(module, dir, targetHashString)) {
return true;
}
}
return false;
}
@VisibleForTesting
static boolean tryToCreateAndSetAndroidSdk(@NotNull Module module, @NotNull String sdkPath, @NotNull String targetHashString) {
File path = new File(FileUtil.toSystemDependentName(sdkPath));
Sdk sdk = tryToCreateAndroidSdk(path, targetHashString);
if (sdk != null) {
ModuleRootModificationUtil.setModuleSdk(module, sdk);
return true;
}
return false;
}
@Nullable
public static Sdk tryToCreateAndroidSdk(@NotNull File sdkPath, @NotNull String targetHashString) {
AndroidSdkData sdkData = AndroidSdkData.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 (!Strings.isNullOrEmpty(jdkPath)) {
jdkPath = FileUtil.toSystemIndependentName(jdkPath);
Sdk jdk = Jdks.createJdk(jdkPath);
if (jdk != null) {
androidSdkPath = FileUtil.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) {
SdkAdditionalData data = currentSdk.getSdkAdditionalData();
if (data instanceof AndroidSdkAdditionalData) {
AndroidPlatform platform = ((AndroidSdkAdditionalData)data).getAndroidPlatform();
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(final 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 = AndroidSdkData.getSdkData(homePath);
if (currentSdkData != null && currentSdkData.equals(sdkData)) {
AndroidSdkAdditionalData data = (AndroidSdkAdditionalData)sdk.getSdkAdditionalData();
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 = (AndroidSdkAdditionalData)sdk.getSdkAdditionalData();
Sdk javaSdk = sdkAdditionalData != null ? sdkAdditionalData.getJavaSdk() : null;
if (javaSdk == null) {
return false;
}
Set<VirtualFile> filesInSdk = Sets.newHashSet(sdk.getRootProvider().getFiles(OrderRootType.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(OrderRootType.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(), AndroidCommonUtils.platformToolPath(SdkConstants.FN_ADB));
return adb != null && adb.exists() ? adb : null;
}
@Nullable
private static AndroidSdkData getFirstAndroidModuleSdkData(Project project) {
final 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 && projectSdk.getSdkType() == AndroidSdkType.getInstance()) {
AndroidSdkAdditionalData sdkData = (AndroidSdkAdditionalData)projectSdk.getSdkAdditionalData();
if (sdkData != null) {
AndroidPlatform platform = sdkData.getAndroidPlatform();
return platform != null ? platform.getSdkData() : null;
}
}
return 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.restartDdmlib(project);
}
}
else {
final OSProcessHandler ddmsProcessHandler = AndroidRunDdmsAction.getDdmsProcessHandler();
if (ddmsProcessHandler != null) {
int r = Messages.showYesNoDialog(project, "Monitor will be closed to enable ADB integration. Continue?", "ADB Integration",
Messages.getQuestionIcon());
if (r != 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;
}
AndroidEnableAdbServiceAction.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;
}
AndroidEnableAdbServiceAction.setAdbServiceEnabled(project, true);
}
return true;
}
public static boolean isAndroidSdkAvailable() {
return tryToChooseAndroidSdk() != null;
}
@Nullable
public static AndroidSdkData tryToChooseAndroidSdk() {
if (ourSdkData == null) {
if (AndroidStudioSpecificInitializer.isAndroidStudio()) {
File path = DefaultSdks.getDefaultAndroidHome();
if (path != null) {
ourSdkData = AndroidSdkData.getSdkData(path.getPath());
if (ourSdkData != null) {
return ourSdkData;
}
}
}
for (String s : getAndroidSdkPathsFromExistingPlatforms()) {
ourSdkData = AndroidSdkData.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 prerelease 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.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
final String adbErrors = Joiner.on('\n').join(AdbErrors.getErrors());
String message =
"ADB not responding. If you'd like to retry, then please manually kill \"" + SdkConstants.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;
}
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;
}
}
}