| /* |
| * Copyright (C) 2013 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.gradle; |
| |
| import com.android.builder.model.*; |
| import com.android.sdklib.AndroidVersion; |
| import com.android.sdklib.repository.FullRevision; |
| import com.android.tools.idea.model.AndroidModel; |
| import com.android.tools.lint.detector.api.LintUtils; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.externalSystem.model.DataNode; |
| import com.intellij.openapi.externalSystem.model.ProjectSystemId; |
| import com.intellij.openapi.externalSystem.model.project.ModuleData; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.pom.java.LanguageLevel; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.*; |
| import java.util.*; |
| import java.util.concurrent.CountDownLatch; |
| |
| import static com.android.builder.model.AndroidProject.*; |
| import static com.android.tools.idea.gradle.AndroidProjectKeys.IDE_ANDROID_PROJECT; |
| import static com.android.tools.idea.gradle.customizer.android.ContentRootModuleCustomizer.EXCLUDED_OUTPUT_FOLDER_NAMES; |
| import static com.android.tools.idea.gradle.util.ProxyUtil.reproxy; |
| import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.find; |
| import static com.intellij.openapi.util.io.FileUtil.isAncestor; |
| import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile; |
| |
| /** |
| * Contains Android-Gradle related state necessary for configuring an IDEA project based on a user-selected build variant. |
| */ |
| public class IdeaAndroidProject implements AndroidModel, Serializable { |
| // Increase the value when adding/removing fields or when changing the serialization/deserialization mechanism. |
| private static final long serialVersionUID = 1L; |
| private static final Logger LOG = Logger.getInstance(IdeaAndroidProject.class); |
| |
| @NotNull private ProjectSystemId myProjectSystemId; |
| @NotNull private String myModuleName; |
| @NotNull private File myRootDirPath; |
| @NotNull private AndroidProject myAndroidProject; |
| |
| @Nullable private transient CountDownLatch myProxyDelegateLatch; |
| @Nullable private AndroidProject myProxyDelegate; |
| |
| @SuppressWarnings("NullableProblems") // Set in the constructor. |
| @NotNull private String mySelectedVariantName; |
| |
| private transient VirtualFile myRootDir; |
| |
| @SuppressWarnings("NullableProblems") // Set in the constructor. |
| @NotNull private String mySelectedTestArtifactName; |
| |
| @Nullable private Boolean myOverridesManifestPackage; |
| @Nullable private transient AndroidVersion myMinSdkVersion; |
| |
| @NotNull private Map<String, BuildTypeContainer> myBuildTypesByName = Maps.newHashMap(); |
| @NotNull private Map<String, ProductFlavorContainer> myProductFlavorsByName = Maps.newHashMap(); |
| @NotNull private Map<String, Variant> myVariantsByName = Maps.newHashMap(); |
| |
| @NotNull private Set<File> myExtraGeneratedSourceFolders = Sets.newHashSet(); |
| |
| /** |
| * Creates a new {@link IdeaAndroidProject}. |
| * |
| * @param projectSystemId the external system used to build the project (e.g. Gradle). |
| * @param moduleName the name of the IDEA module, created from {@code delegate}. |
| * @param rootDirPath the root directory of the imported Android-Gradle project. |
| * @param androidProject imported Android-Gradle project. |
| * @param selectedVariantName name of the selected build variant. |
| */ |
| public IdeaAndroidProject(@NotNull ProjectSystemId projectSystemId, |
| @NotNull String moduleName, |
| @NotNull File rootDirPath, |
| @NotNull AndroidProject androidProject, |
| @NotNull String selectedVariantName, |
| @NotNull String selectedTestArtifactName) { |
| myProjectSystemId = projectSystemId; |
| myModuleName = moduleName; |
| myRootDirPath = rootDirPath; |
| myAndroidProject = androidProject; |
| |
| // Compute the proxy object to avoid re-proxying the model during every serialization operation and also schedule it to run |
| // asynchronously to avoid blocking the project sync operation for reproxying to complete. |
| ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { |
| @Override |
| public void run() { |
| myProxyDelegateLatch = new CountDownLatch(1); |
| myProxyDelegate = reproxy(AndroidProject.class, myAndroidProject); |
| myProxyDelegateLatch.countDown(); |
| } |
| }); |
| |
| populateBuildTypesByName(); |
| populateProductFlavorsByName(); |
| populateVariantsByName(); |
| |
| setSelectedVariantName(selectedVariantName); |
| setSelectedTestArtifactName(selectedTestArtifactName); |
| } |
| |
| private void populateBuildTypesByName() { |
| for (BuildTypeContainer container : myAndroidProject.getBuildTypes()) { |
| String name = container.getBuildType().getName(); |
| myBuildTypesByName.put(name, container); |
| } |
| } |
| |
| private void populateProductFlavorsByName() { |
| for (ProductFlavorContainer container : myAndroidProject.getProductFlavors()) { |
| String name = container.getProductFlavor().getName(); |
| myProductFlavorsByName.put(name, container); |
| } |
| } |
| |
| private void populateVariantsByName() { |
| for (Variant variant : myAndroidProject.getVariants()) { |
| myVariantsByName.put(variant.getName(), variant); |
| } |
| } |
| |
| @NotNull |
| @Override |
| public AndroidArtifact getMainArtifact() { |
| return getSelectedVariant().getMainArtifact(); |
| } |
| |
| @NotNull |
| @Override |
| public SourceProvider getDefaultSourceProvider() { |
| return getAndroidProject().getDefaultConfig().getSourceProvider(); |
| } |
| |
| @NotNull |
| @Override |
| public List<SourceProvider> getActiveSourceProviders() { |
| List<SourceProvider> providers = Lists.newArrayList(); |
| // Main source provider. |
| providers.add(getDefaultSourceProvider()); |
| |
| Variant selectedVariant = getSelectedVariant(); |
| |
| // Flavor source providers. |
| for (String flavor : selectedVariant.getProductFlavors()) { |
| ProductFlavorContainer productFlavor = findProductFlavor(flavor); |
| assert productFlavor != null; |
| providers.add(productFlavor.getSourceProvider()); |
| } |
| |
| // Multi-flavor source provider. |
| AndroidArtifact mainArtifact = selectedVariant.getMainArtifact(); |
| SourceProvider multiFlavorProvider = mainArtifact.getMultiFlavorSourceProvider(); |
| if (multiFlavorProvider != null) { |
| providers.add(multiFlavorProvider); |
| } |
| |
| // Build type source provider. |
| BuildTypeContainer buildType = findBuildType(selectedVariant.getBuildType()); |
| assert buildType != null; |
| providers.add(buildType.getSourceProvider()); |
| |
| // Variant source provider. |
| SourceProvider variantProvider = mainArtifact.getVariantSourceProvider(); |
| if (variantProvider != null) { |
| providers.add(variantProvider); |
| } |
| return providers; |
| } |
| |
| @NotNull |
| @Override |
| public List<SourceProvider> getTestSourceProviders() { |
| List<SourceProvider> providers = Lists.newArrayList(); |
| |
| // Collect the default config test source providers. |
| Collection<SourceProviderContainer> extraSourceProviders = |
| getAndroidProject().getDefaultConfig().getExtraSourceProviders(); |
| providers.addAll(getSourceProvidersForSelectedTestArtifact(extraSourceProviders)); |
| |
| // Collect the product flavor test source providers. |
| Variant selectedVariant = getSelectedVariant(); |
| for (String flavor : selectedVariant.getProductFlavors()) { |
| ProductFlavorContainer productFlavor = findProductFlavor(flavor); |
| assert productFlavor != null; |
| providers.addAll( |
| getSourceProvidersForSelectedTestArtifact(productFlavor.getExtraSourceProviders())); |
| } |
| |
| // TODO: Does it make sense to add multi-flavor test source providers? |
| |
| // Collect the build type test source providers. |
| BuildTypeContainer buildType = findBuildType(selectedVariant.getBuildType()); |
| assert buildType != null; |
| providers.addAll( |
| getSourceProvidersForSelectedTestArtifact(buildType.getExtraSourceProviders())); |
| |
| // TODO: Does it make sense to add variant test source providers? |
| |
| return providers; |
| } |
| |
| @NotNull |
| @Override |
| public List<SourceProvider> getAllSourceProviders() { |
| Collection<Variant> variants = myAndroidProject.getVariants(); |
| List<SourceProvider> providers = Lists.newArrayList(); |
| |
| // Add main source set |
| providers.add(getDefaultSourceProvider()); |
| |
| // Add all flavors |
| Collection<ProductFlavorContainer> flavors = myAndroidProject.getProductFlavors(); |
| for (ProductFlavorContainer pfc : flavors) { |
| providers.add(pfc.getSourceProvider()); |
| } |
| |
| // Add the multi-flavor source providers |
| for (Variant v : variants) { |
| SourceProvider provider = v.getMainArtifact().getMultiFlavorSourceProvider(); |
| if (provider != null) { |
| providers.add(provider); |
| } |
| } |
| |
| // Add all the build types |
| Collection<BuildTypeContainer> buildTypes = myAndroidProject.getBuildTypes(); |
| for (BuildTypeContainer btc : buildTypes) { |
| providers.add(btc.getSourceProvider()); |
| } |
| |
| // Add all the variant source providers |
| for (Variant v : variants) { |
| SourceProvider provider = v.getMainArtifact().getVariantSourceProvider(); |
| if (provider != null) { |
| providers.add(provider); |
| } |
| } |
| |
| return providers; |
| } |
| |
| @NotNull |
| @Override |
| public String getApplicationId() { |
| return getSelectedVariant().getMainArtifact().getApplicationId(); |
| } |
| |
| @NotNull |
| @Override |
| public Set<String> getAllApplicationIds() { |
| Set<String> ids = Sets.newHashSet(); |
| for (Variant v : myAndroidProject.getVariants()) { |
| String applicationId = v.getMergedFlavor().getApplicationId(); |
| if (applicationId != null) { |
| ids.add(applicationId); |
| } |
| } |
| return ids; |
| } |
| |
| @Override |
| public Boolean isDebuggable() { |
| BuildTypeContainer buildTypeContainer = findBuildType(getSelectedVariant().getBuildType()); |
| if (buildTypeContainer != null) { |
| return buildTypeContainer.getBuildType().isDebuggable(); |
| } |
| return null; |
| } |
| |
| private static final AndroidVersion NOT_SPECIFIED = new AndroidVersion(0, null); |
| |
| /** |
| * Returns the {@code }minSdkVersion} specified by the user (in the default config or product flavors). |
| * This is normally the merged value, but for example when using preview platforms, the Gradle plugin |
| * will set minSdkVersion and targetSdkVersion to match the level of the compileSdkVersion; in this case |
| * we want tools like lint's API check to continue to look for the intended minSdkVersion specified in |
| * the build.gradle file |
| * |
| * @return the {@link AndroidVersion} to use for this Gradle project, or null if not specified |
| */ |
| @Nullable |
| @Override |
| public AndroidVersion getMinSdkVersion() { |
| if (myMinSdkVersion == null) { |
| ApiVersion minSdkVersion = getSelectedVariant().getMergedFlavor().getMinSdkVersion(); |
| if (minSdkVersion != null && minSdkVersion.getCodename() != null) { |
| ApiVersion defaultConfigVersion = getAndroidProject().getDefaultConfig().getProductFlavor().getMinSdkVersion(); |
| if (defaultConfigVersion != null) { |
| minSdkVersion = defaultConfigVersion; |
| } |
| |
| List<String> flavors = getSelectedVariant().getProductFlavors(); |
| for (String flavor : flavors) { |
| ProductFlavorContainer productFlavor = findProductFlavor(flavor); |
| assert productFlavor != null; |
| ApiVersion flavorVersion = productFlavor.getProductFlavor().getMinSdkVersion(); |
| if (flavorVersion != null) { |
| minSdkVersion = flavorVersion; |
| break; |
| } |
| } |
| } |
| |
| if (minSdkVersion != null) { |
| myMinSdkVersion = LintUtils.convertVersion(minSdkVersion, null); |
| } else { |
| myMinSdkVersion = NOT_SPECIFIED; |
| } |
| } |
| |
| return myMinSdkVersion != NOT_SPECIFIED ? myMinSdkVersion : null; |
| } |
| |
| @Nullable |
| @Override |
| public AndroidVersion getTargetSdkVersion() { |
| ApiVersion targetSdkVersion = getSelectedVariant().getMergedFlavor().getTargetSdkVersion(); |
| if (targetSdkVersion != null) { |
| return new AndroidVersion(targetSdkVersion.getApiLevel(), targetSdkVersion.getCodename()); |
| } |
| return null; |
| } |
| |
| @NotNull |
| public ProjectSystemId getProjectSystemId() { |
| return myProjectSystemId; |
| } |
| |
| @Nullable |
| public BuildTypeContainer findBuildType(@NotNull String name) { |
| return myBuildTypesByName.get(name); |
| } |
| |
| @NotNull |
| public Set<String> getBuildTypes() { |
| return myBuildTypesByName.keySet(); |
| } |
| |
| @NotNull |
| public Set<String> getProductFlavors() { |
| return myProductFlavorsByName.keySet(); |
| } |
| |
| @Nullable |
| public ProductFlavorContainer findProductFlavor(@NotNull String name) { |
| return myProductFlavorsByName.get(name); |
| } |
| |
| @Nullable |
| public BaseArtifact findSelectedTestArtifact(@NotNull Variant variant) { |
| BaseArtifact artifact = getBaseArtifact(variant.getExtraAndroidArtifacts()); |
| if (artifact != null) { |
| return artifact; |
| } |
| return getBaseArtifact(variant.getExtraJavaArtifacts()); |
| } |
| |
| @Nullable |
| private BaseArtifact getBaseArtifact(@NotNull Iterable<? extends BaseArtifact> artifacts) { |
| for (BaseArtifact artifact : artifacts) { |
| if (getSelectedTestArtifactName().equals(artifact.getName())) { |
| return artifact; |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public BaseArtifact findSelectedTestArtifactInSelectedVariant() { |
| return findSelectedTestArtifact(getSelectedVariant()); |
| } |
| |
| @NotNull |
| public String getModuleName() { |
| return myModuleName; |
| } |
| |
| /** |
| * @return the path of the root directory of the imported Android-Gradle project. The returned path belongs to the IDEA module containing |
| * the build.gradle file. |
| */ |
| @NotNull |
| public File getRootDirPath() { |
| return myRootDirPath; |
| } |
| |
| /** |
| * @return the root directory of the imported Android-Gradle project. The returned path belongs to the IDEA module containing the |
| * build.gradle file. |
| */ |
| @NotNull |
| public VirtualFile getRootDir() { |
| if (myRootDir == null) { |
| VirtualFile found = findFileByIoFile(myRootDirPath, true); |
| // the module's root directory can never be null. |
| assert found != null; |
| myRootDir = found; |
| } |
| return myRootDir; |
| } |
| |
| /** |
| * @return the imported Android-Gradle project. |
| */ |
| @NotNull |
| public AndroidProject getAndroidProject() { |
| return myAndroidProject; |
| } |
| |
| /** |
| * @return the selected build variant. |
| */ |
| @NotNull |
| public Variant getSelectedVariant() { |
| Variant selected = myVariantsByName.get(mySelectedVariantName); |
| assert selected != null; |
| return selected; |
| } |
| |
| /** |
| * Updates the name of the selected build variant. If the given name does not belong to an existing variant, this method will pick up |
| * the first variant, in alphabetical order. |
| * |
| * @param name the new name. |
| */ |
| public void setSelectedVariantName(@NotNull String name) { |
| Collection<String> variantNames = getVariantNames(); |
| String newVariantName; |
| if (variantNames.contains(name)) { |
| newVariantName = name; |
| } |
| else { |
| List<String> sorted = Lists.newArrayList(variantNames); |
| Collections.sort(sorted); |
| // AndroidProject has always at least 2 variants (debug and release.) |
| newVariantName = sorted.get(0); |
| } |
| mySelectedVariantName = newVariantName; |
| |
| // force lazy recompute |
| myOverridesManifestPackage = null; |
| myMinSdkVersion = null; |
| } |
| |
| public void setSelectedTestArtifactName(@NotNull String selectedTestArtifactName) { |
| assert selectedTestArtifactName.equals(ARTIFACT_ANDROID_TEST) || selectedTestArtifactName.equals(ARTIFACT_UNIT_TEST); |
| mySelectedTestArtifactName = selectedTestArtifactName; |
| } |
| |
| @NotNull |
| public String getSelectedTestArtifactName() { |
| return mySelectedTestArtifactName; |
| } |
| |
| @NotNull |
| public Collection<SourceProvider> getSourceProvidersForSelectedTestArtifact(@NotNull Iterable<SourceProviderContainer> containers) { |
| Set<SourceProvider> providers = Sets.newHashSet(); |
| |
| for (SourceProviderContainer container : containers) { |
| if (mySelectedTestArtifactName.equals(container.getArtifactName())) { |
| providers.add(container.getSourceProvider()); |
| } |
| } |
| |
| return providers; |
| } |
| |
| @NotNull |
| public Collection<String> getBuildTypeNames() { |
| return myBuildTypesByName.keySet(); |
| } |
| |
| @NotNull |
| public Collection<String> getProductFlavorNames() { |
| return myProductFlavorsByName.keySet(); |
| } |
| |
| @NotNull |
| public Collection<String> getVariantNames() { |
| return myVariantsByName.keySet(); |
| } |
| |
| @Nullable |
| public LanguageLevel getJavaLanguageLevel() { |
| JavaCompileOptions compileOptions = myAndroidProject.getJavaCompileOptions(); |
| String sourceCompatibility = compileOptions.getSourceCompatibility(); |
| return LanguageLevel.parse(sourceCompatibility); |
| } |
| |
| public boolean isLibrary() { |
| return getAndroidProject().isLibrary(); |
| } |
| |
| /** |
| * Returns whether this project fully overrides the manifest package (with applicationId in the |
| * default config or one of the product flavors) in the current variant. |
| * |
| * @return true if the manifest package is overridden |
| */ |
| @Override |
| public boolean overridesManifestPackage() { |
| if (myOverridesManifestPackage == null) { |
| myOverridesManifestPackage = getAndroidProject().getDefaultConfig().getProductFlavor().getApplicationId() != null; |
| |
| Variant variant = getSelectedVariant(); |
| |
| List<String> flavors = variant.getProductFlavors(); |
| for (String flavor : flavors) { |
| ProductFlavorContainer productFlavor = findProductFlavor(flavor); |
| assert productFlavor != null; |
| if (productFlavor.getProductFlavor().getApplicationId() != null) { |
| myOverridesManifestPackage = true; |
| break; |
| } |
| } |
| // The build type can specify a suffix, but it will be merged with the manifest |
| // value if not specified in a flavor/default config, so only flavors count |
| } |
| |
| return myOverridesManifestPackage.booleanValue(); |
| } |
| |
| /** |
| * Registers the path of a source folder that has been incorrectly generated outside of the default location (${buildDir}/generated.) |
| * |
| * @param folderPath the path of the generated source folder. |
| */ |
| public void registerExtraGeneratedSourceFolder(@NotNull File folderPath) { |
| myExtraGeneratedSourceFolders.add(folderPath); |
| } |
| |
| /** |
| * Indicates whether the given path should be manually excluded in the IDE, to minimize file indexing. |
| * <p> |
| * This method returns {@code false} if: |
| * <ul> |
| * <li>the given path does not belong to a folder</li> |
| * <li>the path belongs to the "generated sources" root folder (${buildDir}/generated)</li> |
| * <li>the path belongs to the standard output folders (${buildDir}/intermediates and ${buildDir}/outputs)</li> |
| * <li>or if the path belongs to a generated source folder that has been placed at the wrong location (e.g. by a 3rd-party Gradle |
| * plug-in)</li> |
| * </ul> |
| * </p> |
| * |
| * @param path the given path |
| * @return {@code true} if the path should be manually excluded in the IDE, {@code false otherwise}. |
| */ |
| public boolean shouldManuallyExclude(@NotNull File path) { |
| if (!path.isDirectory()) { |
| return false; |
| } |
| String name = path.getName(); |
| if (FD_INTERMEDIATES.equals(name) || EXCLUDED_OUTPUT_FOLDER_NAMES.contains(name)) { |
| // already excluded. |
| return false; |
| } |
| boolean hasGeneratedFolders = FD_GENERATED.equals(name) || containsExtraGeneratedSourceFolder(path); |
| return !hasGeneratedFolders; |
| } |
| |
| private boolean containsExtraGeneratedSourceFolder(@NotNull File folderPath) { |
| if (!folderPath.isDirectory()) { |
| return false; |
| } |
| for (File generatedSourceFolder : myExtraGeneratedSourceFolders) { |
| if (isAncestor(folderPath, generatedSourceFolder, false)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @return the paths of generated sources placed at the wrong location (not in ${build}/generated.) |
| */ |
| @NotNull |
| public File[] getExtraGeneratedSourceFolders() { |
| return myExtraGeneratedSourceFolders.toArray(new File[myExtraGeneratedSourceFolders.size()]); |
| } |
| |
| @Nullable |
| public Collection<SyncIssue> getSyncIssues() { |
| if (supportsIssueReporting()) { |
| return myAndroidProject.getSyncIssues(); |
| } |
| return null; |
| } |
| |
| private boolean supportsIssueReporting() { |
| String original = myAndroidProject.getModelVersion(); |
| FullRevision modelVersion; |
| try { |
| modelVersion = FullRevision.parseRevision(original); |
| } catch (NumberFormatException e) { |
| Logger.getInstance(IdeaAndroidProject.class).warn("Failed to parse '" + original + "'", e); |
| return false; |
| } |
| return modelVersion.compareTo(FullRevision.parseRevision("1.1.0")) >= 0; |
| } |
| |
| @Nullable |
| public SourceFileContainerInfo containsSourceFile(@NotNull File file) { |
| ProductFlavorContainer defaultConfig = myAndroidProject.getDefaultConfig(); |
| if (containsSourceFile(defaultConfig, file)) { |
| return new SourceFileContainerInfo(); |
| } |
| for (Variant variant : myAndroidProject.getVariants()) { |
| AndroidArtifact artifact = variant.getMainArtifact(); |
| if (containsSourceFile(artifact, file)) { |
| return new SourceFileContainerInfo(variant, artifact); |
| } |
| for (AndroidArtifact extraArtifact : variant.getExtraAndroidArtifacts()) { |
| if (containsSourceFile(extraArtifact, file)) { |
| return new SourceFileContainerInfo(variant, extraArtifact); |
| } |
| } |
| String buildTypeName = variant.getBuildType(); |
| BuildTypeContainer buildTypeContainer = findBuildType(buildTypeName); |
| if (buildTypeContainer != null) { |
| if (containsFile(buildTypeContainer.getSourceProvider(), file)) { |
| return new SourceFileContainerInfo(variant); |
| } |
| for (SourceProviderContainer extraSourceProvider : buildTypeContainer.getExtraSourceProviders()) { |
| if (containsFile(extraSourceProvider.getSourceProvider(), file)) { |
| return new SourceFileContainerInfo(variant); |
| } |
| } |
| } |
| for (String flavorName : variant.getProductFlavors()) { |
| ProductFlavorContainer flavor = findProductFlavor(flavorName); |
| if (flavor != null && containsSourceFile(flavor, file)) { |
| return new SourceFileContainerInfo(variant); |
| } |
| } |
| |
| } |
| |
| return null; // not found. |
| } |
| |
| private static boolean containsSourceFile(@NotNull ProductFlavorContainer flavorContainer, @NotNull File file) { |
| if (containsFile(flavorContainer.getSourceProvider(), file)) { |
| return true; |
| } |
| // Test source roots |
| for (SourceProviderContainer extraSourceProvider : flavorContainer.getExtraSourceProviders()) { |
| if (containsFile(extraSourceProvider.getSourceProvider(), file)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean containsSourceFile(@NotNull BaseArtifact artifact, @NotNull File file) { |
| if (artifact instanceof AndroidArtifact) { |
| AndroidArtifact android = (AndroidArtifact)artifact; |
| if (containsFile(android.getGeneratedSourceFolders(), file) || containsFile(android.getGeneratedResourceFolders(), file)) { |
| return true; |
| } |
| } |
| SourceProvider sourceProvider = artifact.getVariantSourceProvider(); |
| if (sourceProvider != null && containsFile(sourceProvider, file)) { |
| return true; |
| } |
| sourceProvider = artifact.getMultiFlavorSourceProvider(); |
| return sourceProvider != null && containsFile(sourceProvider, file); |
| } |
| |
| private static boolean containsFile(@NotNull SourceProvider sourceProvider, @NotNull File file) { |
| return containsFile(sourceProvider.getAidlDirectories(), file) || |
| containsFile(sourceProvider.getAssetsDirectories(), file) || |
| containsFile(sourceProvider.getCDirectories(), file) || |
| containsFile(sourceProvider.getCppDirectories(), file) || |
| containsFile(sourceProvider.getJavaDirectories(), file) || |
| containsFile(sourceProvider.getRenderscriptDirectories(), file) || |
| containsFile(sourceProvider.getResDirectories(), file) || |
| containsFile(sourceProvider.getResourcesDirectories(), file); |
| } |
| |
| private static boolean containsFile(@NotNull Collection<File> directories, @NotNull File file) { |
| for (File directory : directories) { |
| if (isAncestor(directory, file, false)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static class SourceFileContainerInfo { |
| @Nullable public final Variant variant; |
| @Nullable public final BaseArtifact artifact; |
| |
| SourceFileContainerInfo() { |
| this(null); |
| } |
| |
| SourceFileContainerInfo(@Nullable Variant variant) { |
| this(variant, null); |
| } |
| |
| SourceFileContainerInfo(@Nullable Variant variant, @Nullable BaseArtifact artifact) { |
| this.variant = variant; |
| this.artifact = artifact; |
| } |
| |
| public void updateSelectedVariantIn(@NotNull DataNode<ModuleData> moduleNode) { |
| if (variant != null) { |
| DataNode<IdeaAndroidProject> androidProjectNode = find(moduleNode, IDE_ANDROID_PROJECT); |
| if (androidProjectNode != null) { |
| androidProjectNode.getData().setSelectedVariantName(variant.getName()); |
| } |
| } |
| } |
| } |
| |
| private void writeObject(ObjectOutputStream out) throws IOException { |
| if (myProxyDelegateLatch != null) { |
| try { |
| // If required, wait for the proxy operation to complete. |
| myProxyDelegateLatch.await(); |
| } |
| catch (InterruptedException e) { |
| LOG.error(e); |
| Thread.currentThread().interrupt(); |
| } |
| } |
| |
| out.writeObject(myProjectSystemId); |
| out.writeObject(myModuleName); |
| out.writeObject(myRootDirPath); |
| out.writeObject(myProxyDelegate); |
| out.writeObject(mySelectedVariantName); |
| out.writeObject(mySelectedTestArtifactName); |
| } |
| |
| private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { |
| myProjectSystemId = (ProjectSystemId)in.readObject(); |
| myModuleName = (String)in.readObject(); |
| myRootDirPath = (File)in.readObject(); |
| myAndroidProject = (AndroidProject)in.readObject(); |
| myProxyDelegate = myAndroidProject; |
| |
| myBuildTypesByName = Maps.newHashMap(); |
| myProductFlavorsByName = Maps.newHashMap(); |
| myVariantsByName = Maps.newHashMap(); |
| myExtraGeneratedSourceFolders = Sets.newHashSet(); |
| |
| populateBuildTypesByName(); |
| populateProductFlavorsByName(); |
| populateVariantsByName(); |
| |
| setSelectedVariantName((String)in.readObject()); |
| setSelectedTestArtifactName((String)in.readObject()); |
| } |
| } |