Merge "device panel: don't trigger selection listeners on certain updates" into idea133
diff --git a/android/src/com/android/tools/idea/gradle/IdeaAndroidProject.java b/android/src/com/android/tools/idea/gradle/IdeaAndroidProject.java
index 86c0958..7d5bd0d 100644
--- a/android/src/com/android/tools/idea/gradle/IdeaAndroidProject.java
+++ b/android/src/com/android/tools/idea/gradle/IdeaAndroidProject.java
@@ -195,4 +195,8 @@
public String computePackageName() {
return getSelectedVariant().getMainArtifact().getPackageName();
}
+
+ public boolean isLibrary() {
+ return getDelegate().isLibrary();
+ }
}
diff --git a/android/src/com/android/tools/idea/gradle/messages/CommonMessageGroupNames.java b/android/src/com/android/tools/idea/gradle/messages/CommonMessageGroupNames.java
index ba23135..98a36d2 100644
--- a/android/src/com/android/tools/idea/gradle/messages/CommonMessageGroupNames.java
+++ b/android/src/com/android/tools/idea/gradle/messages/CommonMessageGroupNames.java
@@ -25,6 +25,7 @@
public static final String UNRESOLVED_ANDROID_DEPENDENCIES = "Unresolved Android dependencies";
public static final String UNRESOLVED_DEPENDENCIES = "Unresolved dependencies";
public static final String VARIANT_SELECTION_CONFLICTS = "Variant selection conflicts";
+ public static final String PROJECT_STRUCTURE_CONFLICTS = "Project structure conflicts";
private CommonMessageGroupNames() {
}
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/AndroidGradleProjectComponent.java b/android/src/com/android/tools/idea/gradle/project/AndroidGradleProjectComponent.java
index ca09d60..eac5ff1 100755
--- a/android/src/com/android/tools/idea/gradle/project/AndroidGradleProjectComponent.java
+++ b/android/src/com/android/tools/idea/gradle/project/AndroidGradleProjectComponent.java
@@ -37,7 +37,6 @@
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.module.JavaModuleType;
@@ -60,8 +59,6 @@
import java.util.List;
public class AndroidGradleProjectComponent extends AbstractProjectComponent {
- private static final Logger LOG = Logger.getInstance(AndroidGradleProjectComponent.class);
-
@NonNls private static final String SHOW_MIGRATE_TO_GRADLE_POPUP = "show.migrate.to.gradle.popup";
@Nullable private Disposable myDisposable;
@@ -117,8 +114,8 @@
StudioBuildStatsPersistenceComponent stats = StudioBuildStatsPersistenceComponent.getInstance();
if (stats != null) {
- BuildRecord b = new BuildRecord(StatsKeys.PROJECT_OPENED, isGradleProject ? "gradle" : "not-gradle");
- stats.addBuildRecord(b);
+ BuildRecord record = new BuildRecord(StatsKeys.PROJECT_OPENED, isGradleProject ? "gradle" : "not-gradle");
+ stats.addBuildRecord(record);
}
}
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 deef48a..cbc0318 100755
--- a/android/src/com/android/tools/idea/gradle/project/PostProjectSyncTasksExecutor.java
+++ b/android/src/com/android/tools/idea/gradle/project/PostProjectSyncTasksExecutor.java
@@ -27,7 +27,11 @@
import com.android.tools.idea.gradle.service.notification.NotificationHyperlink;
import com.android.tools.idea.gradle.util.ProjectBuilder;
import com.android.tools.idea.gradle.util.Projects;
-import com.android.tools.idea.gradle.variant.VariantSelectionVerifier;
+import com.android.tools.idea.gradle.variant.Conflict;
+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;
@@ -56,6 +60,7 @@
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
+import com.intellij.util.SystemProperties;
import com.intellij.util.io.URLUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -100,7 +105,7 @@
AndroidGradleProjectComponent.getInstance(myProject).checkForSupportedModules();
}
- findAndShowVariantSelectionConflicts();
+ findAndShowVariantConflicts();
ProjectResourceRepository.moduleRootsChanged(myProject);
@@ -331,16 +336,21 @@
}
}
- private void findAndShowVariantSelectionConflicts() {
- // TODO: enable this code to allow creation of project profiles.
- //ImmutableList<SelectionConflict> conflicts = VariantSelectionVerifier.getInstance(myProject).findSelectionConflicts();
- //if (!conflicts.isEmpty()) {
- // ProjectProfileSelectionDialog dialog = new ProjectProfileSelectionDialog(myProject, conflicts);
- // dialog.show();
- //}
+ private void findAndShowVariantConflicts() {
+ ConflictSet conflicts = ConflictResolution.findConflicts(myProject);
- VariantSelectionVerifier verifier = new VariantSelectionVerifier(myProject);
- verifier.findAndShowSelectionConflicts();
+ 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();
+ }
+ }
+
+ 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/SelectionConflict.java b/android/src/com/android/tools/idea/gradle/variant/Conflict.java
similarity index 91%
rename from android/src/com/android/tools/idea/gradle/variant/SelectionConflict.java
rename to android/src/com/android/tools/idea/gradle/variant/Conflict.java
index 0b0b42b..db64c68 100644
--- a/android/src/com/android/tools/idea/gradle/variant/SelectionConflict.java
+++ b/android/src/com/android/tools/idea/gradle/variant/Conflict.java
@@ -24,7 +24,7 @@
import java.util.Collection;
import java.util.List;
-public class SelectionConflict {
+public class Conflict {
@NotNull private final Module mySource;
@NotNull private final String mySelectedVariant;
@@ -35,7 +35,7 @@
private boolean myResolved;
- public SelectionConflict(@NotNull Module source, @NotNull String selectedVariant) {
+ public Conflict(@NotNull Module source, @NotNull String selectedVariant) {
mySource = source;
mySelectedVariant = selectedVariant;
}
@@ -102,19 +102,19 @@
}
public static class AffectedModule {
- @NotNull private final SelectionConflict myConflict;
+ @NotNull private final Conflict myConflict;
@NotNull private final Module myTarget;
@NotNull private final String myExpectedVariant;
private boolean mySelected = true;
- AffectedModule(@NotNull SelectionConflict conflict, @NotNull Module target, @NotNull String expectedVariant) {
+ AffectedModule(@NotNull Conflict conflict, @NotNull Module target, @NotNull String expectedVariant) {
myConflict = conflict;
myTarget = target;
myExpectedVariant = expectedVariant;
}
@NotNull
- public SelectionConflict getConflict() {
+ public Conflict getConflict() {
return myConflict;
}
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
new file mode 100644
index 0000000..a86a13c
--- /dev/null
+++ b/android/src/com/android/tools/idea/gradle/variant/ConflictSet.java
@@ -0,0 +1,56 @@
+/*
+ * 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.google.common.collect.ImmutableList;
+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:
+ * <ol>
+ * <li>
+ * <b>Selection conflicts.</b> These conflicts occur when module A depends on module B/variant X but module B has variant Y selected
+ * instead. These conflicts can be easily fixed by selecting the right variant in the "Build Variants" tool window.
+ * </li>
+ * <b>Structure conflicts.</b> These conflicts occur when there are multiple modules depending on different variants of a single module.
+ * For example, module A depends on module E/variant X, module B depends on module E/variant Y and module C depends on module E/variant Z.
+ * These conflicts cannot be resolved through the "Build Variants" tool window because regardless of the variant is selected on module E,
+ * we will always have a selection conflict. These conflicts can be resolved by importing a subset of modules into the IDE (i.e. project
+ * profiles.)
+ * </ol>
+ */
+public class ConflictSet {
+ @NotNull private final ImmutableList<Conflict> mySelectionConflicts;
+ @NotNull private final ImmutableList<Conflict> myStructureConflicts;
+
+ ConflictSet(@NotNull Collection<Conflict> selectionConflicts, @NotNull Collection<Conflict> structureConflicts) {
+ mySelectionConflicts = ImmutableList.copyOf(selectionConflicts);
+ myStructureConflicts = ImmutableList.copyOf(structureConflicts);
+ }
+
+ @NotNull
+ public List<Conflict> getSelectionConflicts() {
+ return mySelectionConflicts;
+ }
+
+ @NotNull
+ public List<Conflict> getStructureConflicts() {
+ return myStructureConflicts;
+ }
+}
diff --git a/android/src/com/android/tools/idea/gradle/variant/VariantSelectionVerifier.java b/android/src/com/android/tools/idea/gradle/variant/VariantSelectionVerifier.java
deleted file mode 100644
index d07ad62..0000000
--- a/android/src/com/android/tools/idea/gradle/variant/VariantSelectionVerifier.java
+++ /dev/null
@@ -1,172 +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.messages.AbstractNavigatable;
-import com.android.tools.idea.gradle.messages.Message;
-import com.android.tools.idea.gradle.messages.ProjectSyncMessages;
-import com.android.tools.idea.gradle.util.GradleUtil;
-import com.android.tools.idea.gradle.variant.view.BuildVariantView;
-import com.google.common.collect.*;
-import com.intellij.openapi.components.ServiceManager;
-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.pom.Navigatable;
-import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-
-import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.VARIANT_SELECTION_CONFLICTS;
-
-public class VariantSelectionVerifier {
- @NotNull private final Project myProject;
-
- public VariantSelectionVerifier(@NotNull Project project) {
- myProject = project;
- }
-
- public void findAndShowSelectionConflicts() {
- ImmutableList<SelectionConflict> conflicts = findSelectionConflicts();
-
- ProjectSyncMessages messages = ProjectSyncMessages.getInstance(myProject);
- messages.removeMessages(VARIANT_SELECTION_CONFLICTS);
-
- for (SelectionConflict 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 (SelectionConflict.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(myProject).selectAndScrollTo(conflictSource);
- }
-
- @Override
- public boolean canNavigate() {
- return true;
- }
- };
-
- messages.add(new Message(VARIANT_SELECTION_CONFLICTS, Message.Type.ERROR, navigateToConflictSource, text.toArray(new String[text.size()])));
- }
-
- BuildVariantView.getInstance(myProject).updateNotification(conflicts);
- }
-
- @NotNull
- public ImmutableList<SelectionConflict> findSelectionConflicts() {
- Map<String, SelectionConflict> conflictsByModuleName = Maps.newHashMap();
-
- ModuleManager moduleManager = ModuleManager.getInstance(myProject);
- for (Module module : moduleManager.getModules()) {
- String gradlePath = GradleUtil.getGradlePath(module);
- if (StringUtil.isEmpty(gradlePath)) {
- continue;
- }
- IdeaAndroidProject libraryProject = getAndroidProject(module, true);
- if (libraryProject == null || !libraryProject.getDelegate().isLibrary()) {
- continue;
- }
-
- String selectedVariant = libraryProject.getSelectedVariant().getName();
-
- for (Module dependent : ModuleUtilCore.getAllDependentModules(module)) {
- IdeaAndroidProject androidProject = getAndroidProject(dependent, false);
- if (androidProject == null) {
- continue;
- }
-
- for (AndroidLibrary library : GradleUtil.getDirectLibraryDependencies(androidProject.getSelectedVariant())) {
- if (!gradlePath.equals(library.getProject())) {
- continue;
- }
- String expected = library.getProjectVariant();
- if (StringUtil.isNotEmpty(expected) && !selectedVariant.equals(expected)) {
- String causeName = module.getName();
- SelectionConflict conflict = conflictsByModuleName.get(causeName);
- if (conflict == null) {
- conflict = new SelectionConflict(module, selectedVariant);
- conflictsByModuleName.put(causeName, conflict);
- }
- conflict.addAffectedModule(dependent, expected);
- }
- }
- }
- }
-
- // Make sure conflict has all modules affected by it.
- Collection<SelectionConflict> conflicts = conflictsByModuleName.values();
- for (SelectionConflict conflict : conflicts) {
- Module source = conflict.getSource();
- String gradlePath = GradleUtil.getGradlePath(source);
- assert gradlePath != null;
-
- for (Module dependent : ModuleUtilCore.getAllDependentModules(source)) {
- IdeaAndroidProject androidProject = getAndroidProject(dependent, false);
- if (androidProject == null) {
- continue;
- }
- for (AndroidLibrary library : GradleUtil.getDirectLibraryDependencies(androidProject.getSelectedVariant())) {
- String expected = library.getProjectVariant();
- if (!gradlePath.equals(library.getProject())) {
- continue;
- }
- if (!StringUtil.isEmpty(expected) && !conflict.isAffectingDirectly(dependent)) {
- conflict.addAffectedModule(dependent, expected);
- }
- }
- }
- }
-
- return ImmutableList.copyOf(conflicts);
- }
-
- @Nullable
- private static IdeaAndroidProject getAndroidProject(@NotNull Module module, boolean libraryProject) {
- AndroidFacet facet = AndroidFacet.getInstance(module);
- if (facet == null || !facet.isGradleProject()) {
- return null;
- }
- IdeaAndroidProject androidProject = facet.getIdeaAndroidProject();
- if (androidProject != null && androidProject.getDelegate().isLibrary() == libraryProject) {
- return androidProject;
- }
- return null;
- }
-}
diff --git a/android/src/com/android/tools/idea/gradle/variant/profiles/ProjectProfileSelectionDialog.java b/android/src/com/android/tools/idea/gradle/variant/profiles/ProjectProfileSelectionDialog.java
index 7fd29b7..b4eed4b 100644
--- a/android/src/com/android/tools/idea/gradle/variant/profiles/ProjectProfileSelectionDialog.java
+++ b/android/src/com/android/tools/idea/gradle/variant/profiles/ProjectProfileSelectionDialog.java
@@ -19,7 +19,7 @@
import com.android.builder.model.Variant;
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.android.tools.idea.gradle.util.GradleUtil;
-import com.android.tools.idea.gradle.variant.SelectionConflict;
+import com.android.tools.idea.gradle.variant.Conflict;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.collect.HashMultimap;
@@ -71,7 +71,7 @@
new SimpleTextAttributes(SimpleTextAttributes.STYLE_STRIKEOUT, SimpleTextAttributes.GRAY_ATTRIBUTES.getFgColor());
@NotNull private final Project myProject;
- @NotNull private final List<SelectionConflict> myConflicts;
+ @NotNull private final List<Conflict> myConflicts;
@NotNull private final JPanel myPanel;
@@ -80,12 +80,12 @@
@NotNull private CheckboxTreeView myConflictTree;
@NotNull private DetailsComponent myConflictDetails;
- public ProjectProfileSelectionDialog(@NotNull Project project, @NotNull List<SelectionConflict> conflicts) {
+ public ProjectProfileSelectionDialog(@NotNull Project project, @NotNull List<Conflict> conflicts) {
super(project);
myProject = project;
myConflicts = conflicts;
- for (SelectionConflict conflict : conflicts) {
+ for (Conflict conflict : conflicts) {
conflict.refreshStatus();
}
@@ -129,7 +129,7 @@
if (!moduleElement.myConflicts.isEmpty()) {
boolean allResolved = true;
- for (SelectionConflict conflict : moduleElement.myConflicts) {
+ for (Conflict conflict : moduleElement.myConflicts) {
if (!conflict.isResolved()) {
allResolved = false;
break;
@@ -198,7 +198,7 @@
continue;
}
- SelectionConflict conflict = getConflict(dependency);
+ Conflict conflict = getConflict(dependency);
modulesByGradlePath.put(gradlePath, dependency);
DependencyTreeElement dependencyElement =
@@ -287,10 +287,10 @@
}
CheckedTreeNode moduleNode = (CheckedTreeNode)child;
data = moduleNode.getUserObject();
- if (!(data instanceof SelectionConflict.AffectedModule)) {
+ if (!(data instanceof Conflict.AffectedModule)) {
continue;
}
- SelectionConflict.AffectedModule affected = (SelectionConflict.AffectedModule)data;
+ Conflict.AffectedModule affected = (Conflict.AffectedModule)data;
boolean checked = node.isChecked();
if (module.equals(affected.getTarget()) && moduleNode.isChecked() != checked) {
affected.setSelected(checked);
@@ -317,8 +317,8 @@
}
@Nullable
- private SelectionConflict getConflict(@NotNull Module source) {
- for (SelectionConflict conflict : myConflicts) {
+ private Conflict getConflict(@NotNull Module source) {
+ for (Conflict conflict : myConflicts) {
if (source.equals(conflict.getSource())) {
return conflict;
}
@@ -424,7 +424,7 @@
ConflictsTableModel tableModel = (ConflictsTableModel)myConflictsTable.getModel();
ConflictTableRow row = tableModel.myRows.get(selectedIndex);
- SelectionConflict conflict = row.myConflict;
+ Conflict conflict = row.myConflict;
myConflictDetails.setText("Conflict Detail: " + conflict.getSource().getName());
List<String> variants = Lists.newArrayList(conflict.getVariants());
@@ -434,7 +434,7 @@
CheckedTreeNode variantNode = new CheckedTreeNode(variant);
myConflictTree.myRoot.add(variantNode);
- for (SelectionConflict.AffectedModule module : conflict.getModulesExpectingVariant(variant)) {
+ for (Conflict.AffectedModule module : conflict.getModulesExpectingVariant(variant)) {
CheckedTreeNode moduleNode = new CheckedTreeNode(module);
variantNode.add(moduleNode);
}
@@ -471,7 +471,7 @@
show = true;
}
else {
- for (SelectionConflict conflict : moduleElement.myConflicts) {
+ for (Conflict conflict : moduleElement.myConflicts) {
if (selectedConflictSources.contains(conflict.getSource())) {
show = true;
break;
@@ -493,8 +493,8 @@
if (value instanceof DefaultMutableTreeNode) {
Object data = ((DefaultMutableTreeNode)value).getUserObject();
ColoredTreeCellRenderer textRenderer = getTextRenderer();
- if (data instanceof SelectionConflict.AffectedModule) {
- textRenderer.append(((SelectionConflict.AffectedModule)data).getTarget().getName());
+ if (data instanceof Conflict.AffectedModule) {
+ textRenderer.append(((Conflict.AffectedModule)data).getTarget().getName());
textRenderer.setIcon(AllIcons.Actions.Module);
}
else if (data instanceof String) {
@@ -510,10 +510,10 @@
@Override
protected void onNodeStateChanged(@NotNull CheckedTreeNode node) {
Object data = node.getUserObject();
- if (!(data instanceof SelectionConflict.AffectedModule)) {
+ if (!(data instanceof Conflict.AffectedModule)) {
return;
}
- SelectionConflict.AffectedModule affected = (SelectionConflict.AffectedModule)data;
+ Conflict.AffectedModule affected = (Conflict.AffectedModule)data;
Module module = affected.getTarget();
Enumeration moduleNodes = myProjectStructureTree.myRoot.children();
@@ -564,10 +564,10 @@
final List<ConflictTableRow> myRows = Lists.newArrayList();
final Function<List<ConflictTableRow>, Void> myFilterFunction;
- ConflictsTableModel(@NotNull List<SelectionConflict> conflicts, Function<List<ConflictTableRow>, Void> filterFunction) {
+ ConflictsTableModel(@NotNull List<Conflict> conflicts, Function<List<ConflictTableRow>, Void> filterFunction) {
super(COLUMN_NAMES, conflicts.size());
myFilterFunction = filterFunction;
- for (SelectionConflict conflict : conflicts) {
+ for (Conflict conflict : conflicts) {
myRows.add(new ConflictTableRow(conflict));
}
Collections.sort(myRows);
@@ -604,10 +604,10 @@
}
private static class ConflictTableRow implements Comparable<ConflictTableRow> {
- final SelectionConflict myConflict;
+ final Conflict myConflict;
boolean myFilter;
- ConflictTableRow(SelectionConflict conflict) {
+ ConflictTableRow(Conflict conflict) {
myConflict = conflict;
}
@@ -717,13 +717,13 @@
private static class ModuleTreeElement {
@NotNull final Module myModule;
- @NotNull final List<SelectionConflict> myConflicts = Lists.newArrayList();
+ @NotNull final List<Conflict> myConflicts = Lists.newArrayList();
ModuleTreeElement(@NotNull Module module) {
myModule = module;
}
- void addConflict(@NotNull SelectionConflict conflict) {
+ void addConflict(@NotNull Conflict conflict) {
myConflicts.add(conflict);
}
}
@@ -733,12 +733,12 @@
@NotNull final String myGradlePath;
@Nullable final String myVariant;
- @Nullable final SelectionConflict myConflict;
+ @Nullable final Conflict myConflict;
DependencyTreeElement(@NotNull Module module,
@NotNull String gradlePath,
@Nullable String variant,
- @Nullable SelectionConflict conflict) {
+ @Nullable Conflict conflict) {
myGradlePath = gradlePath;
myVariant = variant;
myModule = module;
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 e3b6a30..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,7 +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.VariantSelectionVerifier;
+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;
@@ -67,7 +67,7 @@
public void execute() {
Module updatedModule = doUpdate(project, moduleName, buildVariantName, facets);
if (updatedModule != null) {
- new VariantSelectionVerifier(project).findAndShowSelectionConflicts();
+ 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 317b239..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
@@ -18,7 +18,7 @@
import com.android.tools.idea.gradle.GradleSyncState;
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.android.tools.idea.gradle.messages.ProjectSyncMessages;
-import com.android.tools.idea.gradle.variant.SelectionConflict;
+import com.android.tools.idea.gradle.variant.Conflict;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -211,7 +211,7 @@
return androidFacet != null ? androidFacet.getIdeaAndroidProject() : null;
}
- public void updateNotification(ImmutableList<SelectionConflict> conflicts) {
+ public void updateNotification(List<Conflict> conflicts) {
myErrorPanel.removeAll();
myConflictSources.clear();
@@ -227,14 +227,15 @@
myErrorPanel.add(notification);
}
- for (SelectionConflict conflict : conflicts) {
+ for (Conflict conflict : conflicts) {
myConflictSources.add(conflict.getSource().getName());
}
}
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/src/com/android/tools/idea/rendering/LayoutPsiPullParser.java b/android/src/com/android/tools/idea/rendering/LayoutPsiPullParser.java
index 36568d7..f75d0c8 100644
--- a/android/src/com/android/tools/idea/rendering/LayoutPsiPullParser.java
+++ b/android/src/com/android/tools/idea/rendering/LayoutPsiPullParser.java
@@ -19,6 +19,7 @@
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.res2.ValueXmlHelper;
import com.android.resources.Density;
+import com.android.resources.ResourceFolderType;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -64,7 +65,7 @@
@Nullable
protected String myAndroidPrefix;
- private boolean myProvideViewCookies = true;
+ protected boolean myProvideViewCookies = true;
/**
* Constructs a new {@link LayoutPsiPullParser}, a parser dedicated to the special case of
@@ -75,6 +76,30 @@
*/
@NotNull
public static LayoutPsiPullParser create(@NotNull XmlFile file, @NotNull RenderLogger logger) {
+ if (ResourceHelper.getFolderType(file) == ResourceFolderType.MENU) {
+ return new LayoutPsiPullParser(file, logger) {
+ @Nullable
+ @Override
+ public Object getViewCookie() {
+ if (myProvideViewCookies) {
+ Element element = getCurrentNode();
+ if (element != null) {
+ // <menu> tags means that we are adding a sub-menu. Since we don't show the submenu, we
+ // return the enclosing tag.
+ if (element.tag.equals(FD_RES_MENU)) {
+ Element previousElement = getPreviousNode();
+ if (previousElement != null) {
+ return previousElement.cookie;
+ }
+ }
+ return element.cookie;
+ }
+ }
+
+ return null;
+ }
+ };
+ }
return new LayoutPsiPullParser(file, logger);
}
@@ -183,6 +208,15 @@
}
@Nullable
+ protected final Element getPreviousNode() {
+ if (myNodeStack.size() > 1) {
+ return myNodeStack.get(myNodeStack.size() - 2);
+ }
+
+ return null;
+ }
+
+ @Nullable
protected final Attribute getAttribute(int i) {
if (myParsingState != START_TAG) {
throw new IndexOutOfBoundsException();
diff --git a/android/src/com/android/tools/idea/rendering/LayoutlibCallback.java b/android/src/com/android/tools/idea/rendering/LayoutlibCallback.java
index f09f7a4..a45c105 100644
--- a/android/src/com/android/tools/idea/rendering/LayoutlibCallback.java
+++ b/android/src/com/android/tools/idea/rendering/LayoutlibCallback.java
@@ -21,7 +21,6 @@
import com.android.ide.common.rendering.legacy.LegacyCallback;
import com.android.ide.common.resources.ResourceResolver;
import com.android.resources.ResourceType;
-import com.android.tools.idea.model.ManifestInfo;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.utils.HtmlBuilder;
import com.android.utils.SdkUtils;
@@ -76,11 +75,11 @@
@Nullable private final Object myCredential;
@Nullable private String myNamespace;
@Nullable private RenderLogger myLogger;
- @NotNull private ViewLoader myClassLoader;
+ @NotNull private final ViewLoader myClassLoader;
@Nullable private String myLayoutName;
@Nullable private ILayoutPullParser myLayoutEmbeddedParser;
@Nullable private ResourceResolver myResourceResolver;
- @NotNull private ActionBarHandler myActionBarHandler;
+ @NotNull private final ActionBarHandler myActionBarHandler;
private boolean myUsed = false;
private Set<File> myParserFiles;
private int myParserCount;
@@ -269,7 +268,7 @@
@Nullable
@Override
public ILayoutPullParser getParser(@NotNull ResourceValue layoutResource) {
- return getParser(layoutResource.getName(), new File(layoutResource.getName()));
+ return getParser(layoutResource.getName(), new File(layoutResource.getValue()));
}
@Nullable
@@ -307,17 +306,22 @@
// contents rather than the most recently saved file contents.
if (xml != null && xml.isFile()) {
File parent = xml.getParentFile();
- if (parent != null && parent.getName().startsWith(FD_RES_LAYOUT)) {
- VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(xml);
- if (file != null) {
- PsiManager psiManager = PsiManager.getInstance(myModule.getProject());
- PsiFile psiFile = psiManager.findFile(file);
- if (psiFile instanceof XmlFile) {
- assert myLogger != null;
- LayoutPsiPullParser parser = LayoutPsiPullParser.create((XmlFile)psiFile, myLogger);
- // For included layouts, don't see view cookies; we want the leaf to point back to the include tag
- parser.setProvideViewCookies(false);
- return parser;
+ if (parent != null) {
+ String parentName = parent.getName();
+ if (parentName.startsWith(FD_RES_LAYOUT) || parentName.startsWith(FD_RES_MENU)) {
+ VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(xml);
+ if (file != null) {
+ PsiManager psiManager = PsiManager.getInstance(myModule.getProject());
+ PsiFile psiFile = psiManager.findFile(file);
+ if (psiFile instanceof XmlFile) {
+ assert myLogger != null;
+ LayoutPsiPullParser parser = LayoutPsiPullParser.create((XmlFile)psiFile, myLogger);
+ if (parentName.startsWith(FD_RES_LAYOUT)) {
+ // For included layouts, don't see view cookies; we want the leaf to point back to the include tag
+ parser.setProvideViewCookies(false);
+ }
+ return parser;
+ }
}
}
}
diff --git a/android/src/com/android/tools/idea/sdk/CheckAndroidSdkUpdates.java b/android/src/com/android/tools/idea/sdk/CheckAndroidSdkUpdates.java
index 5a37988..bc4b8fb 100755
--- a/android/src/com/android/tools/idea/sdk/CheckAndroidSdkUpdates.java
+++ b/android/src/com/android/tools/idea/sdk/CheckAndroidSdkUpdates.java
@@ -58,12 +58,12 @@
private static final Logger LOG = Logger.getInstance("#com.android.tools.idea.sdk.CheckAndroidSdkUpdates");
private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.balloonGroup("Android SDK Notification Group");
- private static long mCheckTimestampMs;
- private static BackgroundableProcessIndicator sIndicator;
+ private static long ourCheckTimestampMs;
+ private static BackgroundableProcessIndicator ourIndicator;
public static void checkNow(@NotNull Project project) {
long now = System.currentTimeMillis();
- if (now - mCheckTimestampMs <= RemoteSdk.DEFAULT_EXPIRATION_PERIOD_MS) {
+ if (now - ourCheckTimestampMs <= RemoteSdk.DEFAULT_EXPIRATION_PERIOD_MS) {
LOG.info("Skip: too early");
return;
}
@@ -84,11 +84,11 @@
}
- if (sIndicator == null) {
- mCheckTimestampMs = now;
+ if (ourIndicator == null) {
+ ourCheckTimestampMs = now;
SdkUpdateCheckTask task = new SdkUpdateCheckTask(project, sdkData);
- sIndicator = new BackgroundableProcessIndicator(task);
- ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, sIndicator);
+ ourIndicator = new BackgroundableProcessIndicator(task);
+ ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, ourIndicator);
}
}
@@ -196,7 +196,7 @@
LOG.info("Android SDK: " + n + " updates found");
} finally {
- sIndicator = null;
+ ourIndicator = null;
}
}
diff --git a/android/src/com/android/tools/idea/stats/BuildRecord.java b/android/src/com/android/tools/idea/stats/BuildRecord.java
index 9791b05..00e1e8b 100755
--- a/android/src/com/android/tools/idea/stats/BuildRecord.java
+++ b/android/src/com/android/tools/idea/stats/BuildRecord.java
@@ -78,7 +78,7 @@
/**
* Returns the equivalent of {@link System#currentTimeMillis()} in the UTC timezone.
- * @return Now's timestamp, in millisconds since the epoch, in the UTC timezone.
+ * @return Now's timestamp, in milliseconds since the epoch, in the UTC timezone.
*/
public static long utcNow() {
Calendar c = Calendar.getInstance();
diff --git a/android/src/com/android/tools/idea/stats/StatsTimeCollector.java b/android/src/com/android/tools/idea/stats/StatsTimeCollector.java
index 4a021ef..485e7ca 100755
--- a/android/src/com/android/tools/idea/stats/StatsTimeCollector.java
+++ b/android/src/com/android/tools/idea/stats/StatsTimeCollector.java
@@ -33,7 +33,6 @@
* with the Usage Statistics setting panel. This is by design.</em>
*/
public class StatsTimeCollector {
-
private static final boolean isEnabled = StudioBuildStatsPersistenceComponent.getInstance() != null;
private static final TObjectLongHashMap<String> myTimestampMap = new TObjectLongHashMap<String>();
@@ -69,17 +68,19 @@
}
try {
long now = System.currentTimeMillis();
- long start = 0;
+ long start;
synchronized (myTimestampMap) {
start = myTimestampMap.remove(key);
}
if (start > 0 && start < now) {
- StudioBuildStatsPersistenceComponent i = StudioBuildStatsPersistenceComponent.getInstance();
- if (i != null) {
- i.addBuildRecord(new BuildRecord(key, Long.toString(now - start)));
+ StudioBuildStatsPersistenceComponent stats = StudioBuildStatsPersistenceComponent.getInstance();
+ if (stats != null) {
+ BuildRecord record = new BuildRecord(key, Long.toString(now - start));
+ stats.addBuildRecord(record);
}
}
- } catch (Throwable ignore) {}
+ } catch (Throwable ignore) {
+ }
}
}
diff --git a/android/src/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponent.java b/android/src/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponent.java
index ae7f94e..607b3cf 100755
--- a/android/src/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponent.java
+++ b/android/src/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponent.java
@@ -20,9 +20,11 @@
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
+import com.intellij.util.concurrency.SequentialTaskExecutor;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.ide.PooledThreadExecutor;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -63,6 +65,8 @@
private final LinkedList<BuildRecord> myRecords = new LinkedList<BuildRecord>();
+ private final SequentialTaskExecutor myTaskExecutor = new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE);
+
/**
* Retrieves an instance of the component or null if not available or configured.
*
@@ -82,7 +86,17 @@
*
* @param newRecord to be added to the internal queue.
*/
- public void addBuildRecord(@NotNull BuildRecord newRecord) {
+ public void addBuildRecord(@NotNull final BuildRecord newRecord) {
+ myTaskExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ addBuildRecordImmediately(newRecord);
+ }
+ });
+ }
+
+ @VisibleForTesting
+ void addBuildRecordImmediately(@NotNull BuildRecord newRecord) {
// Skip if there is no Application, allowing this to run using non-idea unit tests.
Application app = ApplicationManager.getApplication();
if (app != null && !app.isUnitTestMode()) {
diff --git a/android/src/org/jetbrains/android/inspections/ResourceTypeInspection.java b/android/src/org/jetbrains/android/inspections/ResourceTypeInspection.java
index e545d24..b05319e 100644
--- a/android/src/org/jetbrains/android/inspections/ResourceTypeInspection.java
+++ b/android/src/org/jetbrains/android/inspections/ResourceTypeInspection.java
@@ -362,8 +362,6 @@
String qualifiedName = annotation.getQualifiedName();
if (qualifiedName == null) {
continue;
- } else if (ApplicationManager.getApplication().isUnitTestMode() && qualifiedName.indexOf('.') == -1) {
- qualifiedName = SUPPORT_ANNOTATIONS_PREFIX + qualifiedName;
}
if (INT_DEF_ANNOTATION.equals(qualifiedName) || STRING_DEF_ANNOTATION.equals(qualifiedName)) {
diff --git a/android/testData/inspections/resourceType/flow/src/X.java b/android/testData/inspections/resourceType/flow/src/X.java
index 5901c13..62ec4cc 100644
--- a/android/testData/inspections/resourceType/flow/src/X.java
+++ b/android/testData/inspections/resourceType/flow/src/X.java
@@ -46,17 +46,17 @@
}
private static class MimeTypes {
- @StyleRes
- @DrawableRes
+ @android.support.annotation.StyleRes
+ @android.support.annotation.DrawableRes
public static int styleAndDrawable;
- @StyleRes
+ @android.support.annotation.StyleRes
public static int style;
- @DrawableRes
+ @android.support.annotation.DrawableRes
public static int drawable;
- @DrawableRes
+ @android.support.annotation.DrawableRes
public static int getIconForExt(String ext) {
return R.drawable.my_drawable;
}
@@ -71,12 +71,12 @@
return R.drawable.my_drawable;
}
- @StringRes
+ @android.support.annotation.StringRes
public static int getAnnotatedString() {
return R.string.my_string;
}
- @DrawableRes
+ @android.support.annotation.DrawableRes
public static int getAnnotatedDrawable() {
return R.drawable.my_drawable;
}
diff --git a/android/testData/inspections/resourceType/simple/src/X.java b/android/testData/inspections/resourceType/simple/src/X.java
index 2b899fb..7a1fadf 100644
--- a/android/testData/inspections/resourceType/simple/src/X.java
+++ b/android/testData/inspections/resourceType/simple/src/X.java
@@ -35,7 +35,7 @@
boolean error4 = resources.getBoolean(flow2);
}
- @DrawableRes
+ @android.support.annotation.DrawableRes
public int testResourceTypeReturnValues(Context context, boolean useString) {
if (useString) {
return R.string.app_name; // error
diff --git a/android/testSrc/com/android/tools/idea/gradle/variant/VariantSelectionVerifierTest.java b/android/testSrc/com/android/tools/idea/gradle/variant/ConflictResolutionTest.java
similarity index 83%
rename from android/testSrc/com/android/tools/idea/gradle/variant/VariantSelectionVerifierTest.java
rename to android/testSrc/com/android/tools/idea/gradle/variant/ConflictResolutionTest.java
index 75d43fb..2548fd3 100644
--- a/android/testSrc/com/android/tools/idea/gradle/variant/VariantSelectionVerifierTest.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,22 +34,18 @@
import java.util.List;
/**
- * Tests for {@link VariantSelectionVerifier}
+ * Tests for {@link ConflictResolution}
*/
-public class VariantSelectionVerifierTest extends IdeaTestCase {
+public class ConflictResolutionTest extends IdeaTestCase {
private Module myLibModule;
private IdeaAndroidProject myApp;
private IdeaAndroidProject myLib;
private String myLibGradlePath;
- private VariantSelectionVerifier myVerifier;
-
@Override
protected void setUp() throws Exception {
super.setUp();
- myVerifier = new VariantSelectionVerifier(myProject);
-
myLibModule = createModule("lib");
myLibGradlePath = ":lib";
@@ -137,37 +132,37 @@
}
}
- public void testFindFirstConflictWithoutConflict() {
+ public void testFindSelectionConflictsWithoutConflict() {
setUpDependencyOnLibrary("debug");
- ImmutableList<SelectionConflict> conflicts = myVerifier.findSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertTrue(conflicts.isEmpty());
}
- public void testFindFirstConflictWithoutEmptyVariantDependency() {
+ public void testFindSelectionConflictsWithoutEmptyVariantDependency() {
setUpDependencyOnLibrary("");
- ImmutableList<SelectionConflict> conflicts = myVerifier.findSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertTrue(conflicts.isEmpty());
}
- public void testFindFirstConflictWithoutNullVariantDependency() {
+ public void testFindSelectionConflictsWithoutNullVariantDependency() {
setUpDependencyOnLibrary(null);
- ImmutableList<SelectionConflict> conflicts = myVerifier.findSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertTrue(conflicts.isEmpty());
}
- public void testFindFirstConflictWithConflict() {
+ public void testFindSelectionConflictsWithConflict() {
setUpDependencyOnLibrary("release");
- ImmutableList<SelectionConflict> conflicts = myVerifier.findSelectionConflicts();
+ List<Conflict> conflicts = ConflictResolution.findConflicts(myProject).getSelectionConflicts();
assertEquals(1, conflicts.size());
- SelectionConflict conflict = conflicts.get(0);
+ Conflict conflict = conflicts.get(0);
assertSame(myLibModule, conflict.getSource());
assertSame("debug", conflict.getSelectedVariant());
- List<SelectionConflict.AffectedModule> affectedModules = conflict.getAffectedModules();
+ List<Conflict.AffectedModule> affectedModules = conflict.getAffectedModules();
assertEquals(1, affectedModules.size());
- SelectionConflict.AffectedModule affectedModule = affectedModules.get(0);
+ Conflict.AffectedModule affectedModule = affectedModules.get(0);
assertSame(myModule, affectedModule.getTarget());
assertSame("release", affectedModule.getExpectedVariant());
}
diff --git a/android/testSrc/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponentTest.java b/android/testSrc/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponentTest.java
index f8304b2..b167ffb 100755
--- a/android/testSrc/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponentTest.java
+++ b/android/testSrc/com/android/tools/idea/stats/StudioBuildStatsPersistenceComponentTest.java
@@ -74,7 +74,7 @@
new KeyString("key2", "value 2"),
});
- myComponent.addBuildRecord(b1);
+ myComponent.addBuildRecordImmediately(b1);
assertEquals(1, myComponent.getRecords().size());
assertEquals(b1, myComponent.getRecords().getFirst());
@@ -102,8 +102,8 @@
new KeyString("key2", "value 44"),
});
- myComponent.addBuildRecord(b1);
- myComponent.addBuildRecord(b2);
+ myComponent.addBuildRecordImmediately(b1);
+ myComponent.addBuildRecordImmediately(b2);
Element element2 = myComponent.getState();
assertNotNull(element2);