Added quick fix and link to "Build Variants" window to variant-selection errors.
This CL adds the following hyperlinks to error messages, shown in the
"Messages" window, that are related to variant-selection conflicts:
- "Quick fix." This hyperlink will fix the conflict and update the state
of the IDE.
- "Show conflict source in window." This hyperlink will select the source
of the conflict in the "Build Variants" window.
Change-Id: I78420fa2be6e6da94d8807f4b3d4c1b454dd324f
diff --git a/android/src/com/android/tools/idea/gradle/messages/ProjectSyncMessages.java b/android/src/com/android/tools/idea/gradle/messages/ProjectSyncMessages.java
index 6f170f9..effaef2 100644
--- a/android/src/com/android/tools/idea/gradle/messages/ProjectSyncMessages.java
+++ b/android/src/com/android/tools/idea/gradle/messages/ProjectSyncMessages.java
@@ -16,13 +16,17 @@
package com.android.tools.idea.gradle.messages;
import com.android.tools.idea.gradle.messages.navigatable.ProjectSyncErrorNavigatable;
+import com.android.tools.idea.gradle.service.notification.GradleNotificationExtension;
+import com.android.tools.idea.gradle.service.notification.NotificationHyperlink;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationManager;
import com.intellij.openapi.externalSystem.service.notification.NotificationCategory;
import com.intellij.openapi.externalSystem.service.notification.NotificationData;
import com.intellij.openapi.externalSystem.service.notification.NotificationSource;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.util.GradleConstants;
@@ -58,16 +62,26 @@
return myNotificationManager.getMessageCount(groupName, NotificationSource.PROJECT_SYNC, null, GradleConstants.SYSTEM_ID);
}
- public void add(@NotNull final Message message) {
+ public void add(@NotNull final Message message, @NotNull NotificationHyperlink... hyperlinks) {
Navigatable navigatable = message.getNavigatable();
if (navigatable instanceof ProjectSyncErrorNavigatable) {
((ProjectSyncErrorNavigatable)navigatable).setProject(myProject);
}
- NotificationData notification = new NotificationData(
- message.getGroupName(), StringUtil.join(message.getText(), "\n"),
- NotificationCategory.convert(message.getType().getValue()), NotificationSource.PROJECT_SYNC,
- message.getFile() != null ? message.getFile().getPath() : null, message.getLine(), message.getColumn(), false);
+ String title = message.getGroupName();
+ String errorMsg = StringUtil.join(message.getText(), "\n");
+
+ VirtualFile file = message.getFile();
+ String filePath = file != null ? FileUtil.toSystemDependentName(file.getPath()) : null;
+
+ NotificationData notification =
+ new NotificationData(title, errorMsg, NotificationCategory.convert(message.getType().getValue()), NotificationSource.PROJECT_SYNC,
+ filePath, message.getLine(), message.getColumn(), false);
notification.setNavigatable(navigatable);
+
+ if (hyperlinks.length > 0) {
+ GradleNotificationExtension.updateNotification(notification, myProject, title, errorMsg, hyperlinks);
+ }
+
myNotificationManager.showNotification(GradleConstants.SYSTEM_ID, notification);
}
diff --git a/android/src/com/android/tools/idea/gradle/project/PostProjectSyncTasksExecutor.java b/android/src/com/android/tools/idea/gradle/project/PostProjectSyncTasksExecutor.java
index 15e080e..cbc0318 100755
--- a/android/src/com/android/tools/idea/gradle/project/PostProjectSyncTasksExecutor.java
+++ b/android/src/com/android/tools/idea/gradle/project/PostProjectSyncTasksExecutor.java
@@ -28,17 +28,16 @@
import com.android.tools.idea.gradle.util.ProjectBuilder;
import com.android.tools.idea.gradle.util.Projects;
import com.android.tools.idea.gradle.variant.Conflict;
-import com.android.tools.idea.gradle.variant.ConflictDisplay;
-import com.android.tools.idea.gradle.variant.ConflictFinder;
+import com.android.tools.idea.gradle.variant.ConflictResolution;
import com.android.tools.idea.gradle.variant.ConflictSet;
import com.android.tools.idea.gradle.variant.profiles.ProjectProfileSelectionDialog;
+import com.android.tools.idea.gradle.variant.view.BuildVariantView;
import com.android.tools.idea.rendering.ProjectResourceRepository;
import com.android.tools.idea.sdk.DefaultSdks;
import com.android.tools.idea.startup.AndroidStudioSpecificInitializer;
import com.android.tools.idea.stats.StatsKeys;
import com.android.tools.idea.stats.StatsTimeCollector;
import com.android.tools.idea.templates.TemplateManager;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.intellij.jarFinder.InternetAttachSourceProvider;
@@ -106,7 +105,7 @@
AndroidGradleProjectComponent.getInstance(myProject).checkForSupportedModules();
}
- findAndShowVariantSelectionConflicts();
+ findAndShowVariantConflicts();
ProjectResourceRepository.moduleRootsChanged(myProject);
@@ -337,16 +336,21 @@
}
}
- private void findAndShowVariantSelectionConflicts() {
- ConflictSet conflicts = ConflictFinder.findConflicts(myProject);
+ private void findAndShowVariantConflicts() {
+ ConflictSet conflicts = ConflictResolution.findConflicts(myProject);
- ImmutableList<Conflict> structureConflicts = conflicts.getStructureConflicts();
- if (!structureConflicts.isEmpty() && SystemProperties.getBooleanProperty("enable.project.profiles", false)) {
- ProjectProfileSelectionDialog dialog = new ProjectProfileSelectionDialog(myProject, structureConflicts);
- dialog.show();
+ List<Conflict> structureConflicts = conflicts.getStructureConflicts();
+
+ if (!structureConflicts.isEmpty()) {
+ ConflictResolution.displayStructureConflicts(myProject, structureConflicts);
+ if (SystemProperties.getBooleanProperty("enable.project.profiles", false)) {
+ ProjectProfileSelectionDialog dialog = new ProjectProfileSelectionDialog(myProject, structureConflicts);
+ dialog.show();
+ }
}
- ConflictDisplay.displayConflicts(myProject, conflicts);
+ List<Conflict> selectionConflicts = conflicts.getSelectionConflicts();
+ ConflictResolution.displaySelectionConflicts(myProject, selectionConflicts);
ProjectSyncMessages messages = ProjectSyncMessages.getInstance(myProject);
if (!messages.isEmpty()) {
diff --git a/android/src/com/android/tools/idea/gradle/service/notification/GradleNotificationExtension.java b/android/src/com/android/tools/idea/gradle/service/notification/GradleNotificationExtension.java
index c36ed51..8f1d3c8 100644
--- a/android/src/com/android/tools/idea/gradle/service/notification/GradleNotificationExtension.java
+++ b/android/src/com/android/tools/idea/gradle/service/notification/GradleNotificationExtension.java
@@ -345,6 +345,15 @@
@NotNull final Project project,
@NotNull String errorMsg,
@NotNull NotificationHyperlink... hyperlinks) {
+ String title = String.format("Failed to refresh Gradle project '%1$s'", project.getName());
+ updateNotification(notification, project, title, errorMsg, hyperlinks);
+ }
+
+ public static void updateNotification(@NotNull NotificationData notification,
+ @NotNull final Project project,
+ @NotNull String title,
+ @NotNull String errorMsg,
+ @NotNull NotificationHyperlink... hyperlinks) {
String text = errorMsg;
int hyperlinkCount = hyperlinks.length;
if (hyperlinkCount > 0) {
@@ -357,7 +366,6 @@
}
text += ('\n' + b.toString());
}
- String title = String.format("Failed to refresh Gradle project '%1$s'", project.getName());
notification.setTitle(title);
notification.setMessage(text);
diff --git a/android/src/com/android/tools/idea/gradle/variant/ConflictDisplay.java b/android/src/com/android/tools/idea/gradle/variant/ConflictDisplay.java
deleted file mode 100644
index adc3086..0000000
--- a/android/src/com/android/tools/idea/gradle/variant/ConflictDisplay.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.gradle.variant;
-
-import com.android.tools.idea.gradle.messages.AbstractNavigatable;
-import com.android.tools.idea.gradle.messages.Message;
-import com.android.tools.idea.gradle.messages.ProjectSyncMessages;
-import com.android.tools.idea.gradle.variant.view.BuildVariantView;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.project.Project;
-import com.intellij.pom.Navigatable;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.PROJECT_STRUCTURE_CONFLICTS;
-import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.VARIANT_SELECTION_CONFLICTS;
-
-public final class ConflictDisplay {
- private ConflictDisplay() {
- }
-
- /**
- * Displays in the "Messages" window the given variant-related conflicts of the given project.
- *
- * @param project the given project.
- * @param conflicts the conflicts to display.
- */
- public static void displayConflicts(@NotNull Project project, @NotNull ConflictSet conflicts) {
- ImmutableList<Conflict> selectionConflicts = conflicts.getSelectionConflicts();
- displaySelectionConflicts(project, selectionConflicts, VARIANT_SELECTION_CONFLICTS);
- BuildVariantView.getInstance(project).updateNotification(selectionConflicts);
- displaySelectionConflicts(project, conflicts.getStructureConflicts(), PROJECT_STRUCTURE_CONFLICTS);
- }
-
- private static void displaySelectionConflicts(@NotNull final Project project,
- @NotNull ImmutableList<Conflict> conflicts,
- @NotNull String groupName) {
- ProjectSyncMessages messages = ProjectSyncMessages.getInstance(project);
- messages.removeMessages(groupName);
-
- for (Conflict conflict : conflicts) {
- List<String> text = Lists.newArrayList();
- final Module conflictSource = conflict.getSource();
- text.add(String.format("Module '%1$s' has variant '%2$s' selected.", conflictSource.getName(), conflict.getSelectedVariant()));
-
- List<String> expectedVariants = Lists.newArrayList(conflict.getVariants());
- Collections.sort(expectedVariants);
-
- for (String expectedVariant : expectedVariants) {
- List<String> modules = Lists.newArrayList();
- for (Conflict.AffectedModule affected : conflict.getModulesExpectingVariant(expectedVariant)) {
- modules.add("'" + affected.getTarget().getName() + "'");
-
- }
- Collections.sort(modules);
- text.add(String.format("- Variant '%1$s' expected by module(s) %2$s.", expectedVariant, modules));
- text.add("");
- }
-
- Navigatable navigateToConflictSource = new AbstractNavigatable() {
- @Override
- public void navigate(boolean requestFocus) {
- BuildVariantView.getInstance(project).selectAndScrollTo(conflictSource);
- }
-
- @Override
- public boolean canNavigate() {
- return true;
- }
- };
-
- messages.add(new Message(groupName, Message.Type.ERROR, navigateToConflictSource, text.toArray(new String[text.size()])));
- }
- }
-}
diff --git a/android/src/com/android/tools/idea/gradle/variant/ConflictFinder.java b/android/src/com/android/tools/idea/gradle/variant/ConflictFinder.java
deleted file mode 100644
index adcb876..0000000
--- a/android/src/com/android/tools/idea/gradle/variant/ConflictFinder.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.gradle.variant;
-
-import com.android.builder.model.AndroidLibrary;
-import com.android.tools.idea.gradle.IdeaAndroidProject;
-import com.android.tools.idea.gradle.util.GradleUtil;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleManager;
-import com.intellij.openapi.module.ModuleUtilCore;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-import java.util.Map;
-
-public class ConflictFinder {
- @NotNull
- public static ConflictSet findConflicts(@NotNull Project project) {
- Map<String, Conflict> selectionConflicts = Maps.newHashMap();
- Map<String, Conflict> structureConflicts = Maps.newHashMap();
-
- ModuleManager moduleManager = ModuleManager.getInstance(project);
- for (Module module : moduleManager.getModules()) {
- IdeaAndroidProject currentProject = getAndroidProject(module);
- if (currentProject == null || !currentProject.isLibrary()) {
- continue;
- }
- String gradlePath = GradleUtil.getGradlePath(module);
- assert gradlePath != null;
-
- String selectedVariant = currentProject.getSelectedVariant().getName();
-
- for (Module dependent : ModuleUtilCore.getAllDependentModules(module)) {
- IdeaAndroidProject dependentProject = getAndroidProject(dependent);
- if (dependentProject == null) {
- continue;
- }
- String expectedVariant = getExpectedVariant(dependentProject, gradlePath);
- if (StringUtil.isEmpty(expectedVariant)) {
- continue;
- }
- addConflict(structureConflicts, module, selectedVariant, dependent, expectedVariant);
-
- if (!selectedVariant.equals(expectedVariant)) {
- addConflict(selectionConflicts, module, selectedVariant, dependent, expectedVariant);
- }
- }
- }
-
- // Structural conflicts are the ones that have more than one group of modules depending on different variants of another module.
- List<Conflict> structure = Lists.newArrayList();
- for (Conflict conflict : structureConflicts.values()) {
- if (conflict.getVariants().size() > 1) {
- structure.add(conflict);
- }
- }
-
- List<Conflict> selection = Lists.newArrayList();
- for (Conflict conflict : selectionConflicts.values()) {
- if (conflict.getVariants().size() == 1) {
- selection.add(conflict);
- }
- }
-
- return new ConflictSet(selection, structure);
- }
-
- private static void addConflict(@NotNull Map<String, Conflict> allConflicts,
- @NotNull Module source,
- @NotNull String selectedVariant,
- @NotNull Module affected,
- @NotNull String expectedVariant) {
- String causeName = source.getName();
- Conflict conflict = allConflicts.get(causeName);
- if (conflict == null) {
- conflict = new Conflict(source, selectedVariant);
- allConflicts.put(causeName, conflict);
- }
- conflict.addAffectedModule(affected, expectedVariant);
- }
-
- @Nullable
- private static String getExpectedVariant(@NotNull IdeaAndroidProject dependentProject, @NotNull String dependencyGradlePath) {
- List<AndroidLibrary> dependencies = GradleUtil.getDirectLibraryDependencies(dependentProject.getSelectedVariant());
- for (AndroidLibrary dependency : dependencies) {
- if (!dependencyGradlePath.equals(dependency.getProject())) {
- continue;
- }
- return dependency.getProjectVariant();
- }
- return null;
- }
-
- @Nullable
- private static IdeaAndroidProject getAndroidProject(@NotNull Module module) {
- AndroidFacet facet = AndroidFacet.getInstance(module);
- if (facet == null || !facet.isGradleProject()) {
- return null;
- }
- return facet.getIdeaAndroidProject();
- }
-}
diff --git a/android/src/com/android/tools/idea/gradle/variant/ConflictResolution.java b/android/src/com/android/tools/idea/gradle/variant/ConflictResolution.java
new file mode 100644
index 0000000..34b7628
--- /dev/null
+++ b/android/src/com/android/tools/idea/gradle/variant/ConflictResolution.java
@@ -0,0 +1,239 @@
+/*
+ * 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.gradle.variant;
+
+import com.android.builder.model.AndroidLibrary;
+import com.android.tools.idea.gradle.IdeaAndroidProject;
+import com.android.tools.idea.gradle.messages.Message;
+import com.android.tools.idea.gradle.messages.ProjectSyncMessages;
+import com.android.tools.idea.gradle.service.notification.NotificationHyperlink;
+import com.android.tools.idea.gradle.util.GradleUtil;
+import com.android.tools.idea.gradle.variant.view.BuildVariantView;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.PROJECT_STRUCTURE_CONFLICTS;
+import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.VARIANT_SELECTION_CONFLICTS;
+
+public final class ConflictResolution {
+ private ConflictResolution() {
+ }
+
+ public static void updateConflicts(@NotNull Project project) {
+ ConflictSet conflicts = findConflicts(project);
+ List<Conflict> selectionConflicts = conflicts.getSelectionConflicts();
+ displaySelectionConflicts(project, selectionConflicts);
+ BuildVariantView view = BuildVariantView.getInstance(project);
+ view.updateNotification(selectionConflicts);
+ view.updateContents();
+ }
+
+ @NotNull
+ public static ConflictSet findConflicts(@NotNull Project project) {
+ Map<String, Conflict> selectionConflicts = Maps.newHashMap();
+ Map<String, Conflict> structureConflicts = Maps.newHashMap();
+
+ ModuleManager moduleManager = ModuleManager.getInstance(project);
+ for (Module module : moduleManager.getModules()) {
+ IdeaAndroidProject currentProject = getAndroidProject(module);
+ if (currentProject == null || !currentProject.isLibrary()) {
+ continue;
+ }
+ String gradlePath = GradleUtil.getGradlePath(module);
+ assert gradlePath != null;
+
+ String selectedVariant = currentProject.getSelectedVariant().getName();
+
+ for (Module dependent : ModuleUtilCore.getAllDependentModules(module)) {
+ IdeaAndroidProject dependentProject = getAndroidProject(dependent);
+ if (dependentProject == null) {
+ continue;
+ }
+ String expectedVariant = getExpectedVariant(dependentProject, gradlePath);
+ if (StringUtil.isEmpty(expectedVariant)) {
+ continue;
+ }
+ addConflict(structureConflicts, module, selectedVariant, dependent, expectedVariant);
+
+ if (!selectedVariant.equals(expectedVariant)) {
+ addConflict(selectionConflicts, module, selectedVariant, dependent, expectedVariant);
+ }
+ }
+ }
+
+ // Structural conflicts are the ones that have more than one group of modules depending on different variants of another module.
+ List<Conflict> structure = Lists.newArrayList();
+ for (Conflict conflict : structureConflicts.values()) {
+ if (conflict.getVariants().size() > 1) {
+ structure.add(conflict);
+ }
+ }
+
+ List<Conflict> selection = Lists.newArrayList();
+ for (Conflict conflict : selectionConflicts.values()) {
+ if (conflict.getVariants().size() == 1) {
+ selection.add(conflict);
+ }
+ }
+
+ return new ConflictSet(selection, structure);
+ }
+
+ private static void addConflict(@NotNull Map<String, Conflict> allConflicts,
+ @NotNull Module source,
+ @NotNull String selectedVariant,
+ @NotNull Module affected,
+ @NotNull String expectedVariant) {
+ String causeName = source.getName();
+ Conflict conflict = allConflicts.get(causeName);
+ if (conflict == null) {
+ conflict = new Conflict(source, selectedVariant);
+ allConflicts.put(causeName, conflict);
+ }
+ conflict.addAffectedModule(affected, expectedVariant);
+ }
+
+ @Nullable
+ private static String getExpectedVariant(@NotNull IdeaAndroidProject dependentProject, @NotNull String dependencyGradlePath) {
+ List<AndroidLibrary> dependencies = GradleUtil.getDirectLibraryDependencies(dependentProject.getSelectedVariant());
+ for (AndroidLibrary dependency : dependencies) {
+ if (!dependencyGradlePath.equals(dependency.getProject())) {
+ continue;
+ }
+ return dependency.getProjectVariant();
+ }
+ return null;
+ }
+
+ public static boolean solveSelectionConflict(@NotNull Conflict conflict) {
+ AndroidFacet facet = AndroidFacet.getInstance(conflict.getSource());
+ if (facet == null || !facet.isGradleProject()) {
+ // project structure may have changed and the conflict is not longer applicable.
+ return true;
+ }
+ IdeaAndroidProject source = facet.getIdeaAndroidProject();
+ if (source == null) {
+ return false;
+ }
+ Collection<String> variants = conflict.getVariants();
+ assert variants.size() == 1;
+ String expectedVariant = ContainerUtil.getFirstItem(variants);
+ if (StringUtil.isNotEmpty(expectedVariant)) {
+ source.setSelectedVariantName(expectedVariant);
+ facet.syncSelectedVariant();
+ return true;
+ }
+ return false;
+ }
+
+ @Nullable
+ private static IdeaAndroidProject getAndroidProject(@NotNull Module module) {
+ AndroidFacet facet = AndroidFacet.getInstance(module);
+ if (facet == null || !facet.isGradleProject()) {
+ return null;
+ }
+ return facet.getIdeaAndroidProject();
+ }
+
+ public static void displaySelectionConflicts(@NotNull final Project project, @NotNull List<Conflict> conflicts) {
+ String groupName = VARIANT_SELECTION_CONFLICTS;
+
+ ProjectSyncMessages messages = ProjectSyncMessages.getInstance(project);
+ messages.removeMessages(groupName);
+
+ for (final Conflict conflict : conflicts) {
+ final Module source = conflict.getSource();
+ String text = String.format("Module '%1$s' has variant '%2$s' selected, ", source.getName(), conflict.getSelectedVariant());
+
+ List<String> expectedVariants = Lists.newArrayList(conflict.getVariants());
+ assert expectedVariants.size() == 1;
+
+ String expectedVariant = expectedVariants.get(0);
+ List<String> modules = Lists.newArrayList();
+ for (Conflict.AffectedModule affected : conflict.getModulesExpectingVariant(expectedVariant)) {
+ modules.add("'" + affected.getTarget().getName() + "'");
+
+ }
+ Collections.sort(modules);
+ text += String.format("but variant '%1$s' is expected by module(s) %2$s.", expectedVariant, modules);
+
+ String hyperlinkText = String.format("Select '%1$s' in \"Build Variants\" window", source.getName());
+ NotificationHyperlink selectInBuildVariantsWindowHyperlink =
+ new NotificationHyperlink("select.conflict.in.variants.window", hyperlinkText) {
+ @Override
+ protected void execute(@NotNull Project project) {
+ BuildVariantView.getInstance(project).selectAndScrollTo(source);
+ }
+ };
+
+ NotificationHyperlink quickFixHyperlink = new NotificationHyperlink("fix.conflict", "Fix problem") {
+ @Override
+ protected void execute(@NotNull Project project) {
+ boolean solved = solveSelectionConflict(conflict);
+ if (solved) {
+ updateConflicts(project);
+ }
+ }
+ };
+
+ Message msg = new Message(groupName, Message.Type.ERROR, text);
+ messages.add(msg, selectInBuildVariantsWindowHyperlink, quickFixHyperlink);
+ }
+ }
+
+ public static void displayStructureConflicts(@NotNull final Project project, @NotNull List<Conflict> conflicts) {
+ String groupName = PROJECT_STRUCTURE_CONFLICTS;
+
+ ProjectSyncMessages messages = ProjectSyncMessages.getInstance(project);
+ messages.removeMessages(groupName);
+
+ for (Conflict conflict : conflicts) {
+ List<String> text = Lists.newArrayList();
+ final Module conflictSource = conflict.getSource();
+ text.add(String.format("Module '%1$s' has variant '%2$s' selected.", conflictSource.getName(), conflict.getSelectedVariant()));
+
+ List<String> expectedVariants = Lists.newArrayList(conflict.getVariants());
+ Collections.sort(expectedVariants);
+
+ for (String expectedVariant : expectedVariants) {
+ List<String> modules = Lists.newArrayList();
+ for (Conflict.AffectedModule affected : conflict.getModulesExpectingVariant(expectedVariant)) {
+ modules.add("'" + affected.getTarget().getName() + "'");
+
+ }
+ Collections.sort(modules);
+ text.add(String.format("- Variant '%1$s' expected by module(s) %2$s.", expectedVariant, modules));
+ text.add("");
+ }
+
+ messages.add(new Message(groupName, Message.Type.ERROR, text.toArray(new String[text.size()])));
+ }
+ }
+}
diff --git a/android/src/com/android/tools/idea/gradle/variant/ConflictSet.java b/android/src/com/android/tools/idea/gradle/variant/ConflictSet.java
index b0263dc..a86a13c 100644
--- a/android/src/com/android/tools/idea/gradle/variant/ConflictSet.java
+++ b/android/src/com/android/tools/idea/gradle/variant/ConflictSet.java
@@ -19,6 +19,7 @@
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
+import java.util.List;
/**
* Set of all variant-selection-related conflicts. We classify these conflicts in 2 groups:
@@ -44,12 +45,12 @@
}
@NotNull
- public ImmutableList<Conflict> getSelectionConflicts() {
+ public List<Conflict> getSelectionConflicts() {
return mySelectionConflicts;
}
@NotNull
- public ImmutableList<Conflict> getStructureConflicts() {
+ public List<Conflict> getStructureConflicts() {
return myStructureConflicts;
}
}
diff --git a/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantUpdater.java b/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantUpdater.java
index 923ec10..6925303 100644
--- a/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantUpdater.java
+++ b/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantUpdater.java
@@ -24,9 +24,7 @@
import com.android.tools.idea.gradle.customizer.android.DependenciesModuleCustomizer;
import com.android.tools.idea.gradle.util.GradleUtil;
import com.android.tools.idea.gradle.util.ProjectBuilder;
-import com.android.tools.idea.gradle.variant.ConflictDisplay;
-import com.android.tools.idea.gradle.variant.ConflictFinder;
-import com.android.tools.idea.gradle.variant.ConflictSet;
+import com.android.tools.idea.gradle.variant.ConflictResolution;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
@@ -69,8 +67,7 @@
public void execute() {
Module updatedModule = doUpdate(project, moduleName, buildVariantName, facets);
if (updatedModule != null) {
- ConflictSet conflicts = ConflictFinder.findConflicts(project);
- ConflictDisplay.displayConflicts(project, conflicts);
+ ConflictResolution.updateConflicts(project);
}
if (!facets.isEmpty()) {
diff --git a/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantView.java b/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantView.java
index cda91b4..e96f66d 100644
--- a/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantView.java
+++ b/android/src/com/android/tools/idea/gradle/variant/view/BuildVariantView.java
@@ -211,7 +211,7 @@
return androidFacet != null ? androidFacet.getIdeaAndroidProject() : null;
}
- public void updateNotification(ImmutableList<Conflict> conflicts) {
+ public void updateNotification(List<Conflict> conflicts) {
myErrorPanel.removeAll();
myConflictSources.clear();
@@ -234,7 +234,8 @@
public void selectAndScrollTo(@NotNull Module module) {
String name = module.getName();
- for (int row = 0; row < myVariantsTable.getRowCount() - 1; row++) {
+ int rowCount = myVariantsTable.getRowCount();
+ for (int row = 0; row < rowCount; row++) {
if (name.equals(myVariantsTable.getValueAt(row, MODULE_COLUMN_INDEX))) {
myVariantsTable.getSelectionModel().setSelectionInterval(row, row);
myVariantsTable.getColumnModel().getSelectionModel().setSelectionInterval(MODULE_COLUMN_INDEX, MODULE_COLUMN_INDEX);
diff --git a/android/testSrc/com/android/tools/idea/gradle/variant/ConflictFinderTest.java b/android/testSrc/com/android/tools/idea/gradle/variant/ConflictResolutionTest.java
similarity index 91%
rename from android/testSrc/com/android/tools/idea/gradle/variant/ConflictFinderTest.java
rename to android/testSrc/com/android/tools/idea/gradle/variant/ConflictResolutionTest.java
index e0c9a93..2548fd3 100644
--- a/android/testSrc/com/android/tools/idea/gradle/variant/ConflictFinderTest.java
+++ b/android/testSrc/com/android/tools/idea/gradle/variant/ConflictResolutionTest.java
@@ -18,7 +18,6 @@
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.android.tools.idea.gradle.facet.AndroidGradleFacet;
import com.android.tools.idea.gradle.stubs.android.*;
-import com.google.common.collect.ImmutableList;
import com.intellij.facet.FacetManager;
import com.intellij.facet.ModifiableFacetModel;
import com.intellij.openapi.application.ApplicationManager;
@@ -35,9 +34,9 @@
import java.util.List;
/**
- * Tests for {@link ConflictFinder}
+ * Tests for {@link ConflictResolution}
*/
-public class ConflictFinderTest extends IdeaTestCase {
+public class ConflictResolutionTest extends IdeaTestCase {
private Module myLibModule;
private IdeaAndroidProject myApp;
private IdeaAndroidProject myLib;
@@ -135,25 +134,25 @@
public void testFindSelectionConflictsWithoutConflict() {
setUpDependencyOnLibrary("debug");
- ImmutableList<Conflict> conflicts = ConflictFinder.findConflicts(myProject).getSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertTrue(conflicts.isEmpty());
}
public void testFindSelectionConflictsWithoutEmptyVariantDependency() {
setUpDependencyOnLibrary("");
- ImmutableList<Conflict> conflicts = ConflictFinder.findConflicts(myProject).getSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertTrue(conflicts.isEmpty());
}
public void testFindSelectionConflictsWithoutNullVariantDependency() {
setUpDependencyOnLibrary(null);
- ImmutableList<Conflict> conflicts = ConflictFinder.findConflicts(myProject).getSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertTrue(conflicts.isEmpty());
}
public void testFindSelectionConflictsWithConflict() {
setUpDependencyOnLibrary("release");
- ImmutableList<Conflict> conflicts = ConflictFinder.findConflicts(myProject).getSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertEquals(1, conflicts.size());
Conflict conflict = conflicts.get(0);