blob: 45e41a155fcb0a2b45059874bcb5621fd34332fb [file] [log] [blame]
/*
* 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.startup;
import com.android.SdkConstants;
import com.android.sdklib.IAndroidTarget;
import com.android.tools.idea.actions.*;
import com.android.tools.idea.gradle.actions.EditBuildTypesAction;
import com.android.tools.idea.gradle.actions.EditFlavorsAction;
import com.android.tools.idea.gradle.actions.EditLibraryAndDependenciesAction;
import com.android.tools.idea.gradle.actions.SelectBuildVariantAction;
import com.android.tools.idea.sdk.IdeSdks;
import com.android.tools.idea.welcome.config.FirstRunWizardMode;
import com.android.tools.idea.welcome.wizard.AndroidStudioWelcomeScreenProvider;
import com.android.utils.Pair;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl;
import com.intellij.ide.AppLifecycleListener;
import com.intellij.ide.actions.TemplateProjectSettingsGroup;
import com.intellij.ide.projectView.actions.MarkRootGroup;
import com.intellij.ide.projectView.impl.MoveModuleToGroupTopLevel;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.HighlighterColors;
import com.intellij.openapi.editor.XmlHighlighterColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurableEP;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.CodeStyleScheme;
import com.intellij.psi.codeStyle.CodeStyleSchemes;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.util.PlatformUtils;
import com.intellij.util.SystemProperties;
import com.intellij.util.TimeoutUtil;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.android.AndroidPlugin;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
import org.jetbrains.android.sdk.AndroidSdkType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static com.android.SdkConstants.EXT_JAR;
import static com.android.tools.idea.gradle.util.GradleUtil.cleanUpPreferences;
import static com.android.tools.idea.gradle.util.GradleUtil.stopAllGradleDaemons;
import static com.android.tools.idea.gradle.util.PropertiesUtil.getProperties;
import static com.android.tools.idea.sdk.VersionCheck.isCompatibleVersion;
import static com.intellij.openapi.options.Configurable.APPLICATION_CONFIGURABLE;
import static com.intellij.openapi.util.io.FileUtil.*;
import static com.intellij.openapi.util.io.FileUtilRt.getExtension;
import static com.intellij.openapi.util.text.StringUtil.isEmpty;
import static org.jetbrains.android.sdk.AndroidSdkUtils.*;
/** Initialization performed only in the context of the Android IDE. */
public class AndroidStudioSpecificInitializer implements Runnable {
/**
* We set the timeout for Gradle daemons to -1, this way IDEA will not set it to 1 minute and it will use the default instead (3 hours.)
* We need to keep Gradle daemons around as much as possible because creating new daemons is resource-consuming and slows down the IDE.
*/
public static final int GRADLE_DAEMON_TIMEOUT_MS = -1;
static {
System.setProperty("external.system.remote.process.idle.ttl.ms", String.valueOf(GRADLE_DAEMON_TIMEOUT_MS));
}
private static final Logger LOG = Logger.getInstance("#com.android.tools.idea.startup.AndroidStudioSpecificInitializer");
private static final List<String> IDE_SETTINGS_TO_REMOVE = Lists.newArrayList(
"org.jetbrains.plugins.javaFX.JavaFxSettingsConfigurable", "org.intellij.plugins.xpathView.XPathConfigurable",
"org.intellij.lang.xpath.xslt.impl.XsltConfigImpl$UIImpl"
);
@NonNls private static final String USE_IDEA_NEW_PROJECT_WIZARDS = "use.idea.newProjectWizard";
@NonNls private static final String USE_JPS_MAKE_ACTIONS = "use.idea.jpsMakeActions";
@NonNls private static final String USE_IDEA_NEW_FILE_POPUPS = "use.idea.newFilePopupActions";
@NonNls private static final String USE_IDEA_PROJECT_STRUCTURE = "use.idea.projectStructure";
@NonNls public static final String ENABLE_EXPERIMENTAL_ACTIONS = "enable.experimental.actions";
@NonNls private static final String ANDROID_SDK_FOLDER_NAME = "sdk";
/** Paths relative to the IDE installation folder where the Android SDK maybe present. */
private static final String[] ANDROID_SDK_RELATIVE_PATHS =
{ ANDROID_SDK_FOLDER_NAME, File.separator + ".." + File.separator + ANDROID_SDK_FOLDER_NAME,};
public static boolean isAndroidStudio() {
return "AndroidStudio".equals(PlatformUtils.getPlatformPrefix());
}
private static void checkInstallation() {
String studioHome = PathManager.getHomePath();
if (isEmpty(studioHome)) {
LOG.info("Unable to find Studio home directory");
return;
}
File studioHomePath = new File(toSystemDependentName(studioHome));
if (!studioHomePath.isDirectory()) {
LOG.info(String.format("The path '%1$s' does not belong to an existing directory", studioHomePath.getPath()));
return;
}
File androidPluginLibFolderPath = new File(studioHomePath, join("plugins", "android", "lib"));
if (!androidPluginLibFolderPath.isDirectory()) {
LOG.info(String.format("The path '%1$s' does not belong to an existing directory", androidPluginLibFolderPath.getPath()));
return;
}
// Look for signs that the installation is corrupt due to improper updates (typically unzipping on top of previous install)
// which doesn't delete files that have been removed or renamed
String cause = null;
File[] children = notNullize(androidPluginLibFolderPath.listFiles());
if (hasMoreThanOneBuilderModelFile(children)) {
cause = "(Found multiple versions of builder-model-*.jar in plugins/android/lib.)";
} else if (new File(studioHomePath, join("plugins", "android-designer")).exists()) {
cause = "(Found plugins/android-designer which should not be present.)";
}
if (cause != null) {
String msg = "Your Android Studio installation is corrupt and will not work properly.\n" +
cause + "\n" +
"This usually happens if Android Studio is extracted into an existing older version.\n\n" +
"Please reinstall (and make sure the new installation directory is empty first.)";
String title = "Corrupt Installation";
int option = Messages.showDialog(msg, title, new String[]{"Quit", "Proceed Anyway"}, 0, Messages.getErrorIcon());
if (option == 0) {
ApplicationManagerEx.getApplicationEx().exit();
}
}
}
@VisibleForTesting
static boolean hasMoreThanOneBuilderModelFile(@NotNull File[] libraryFiles) {
int builderModelFileCount = 0;
for (File file : libraryFiles) {
String fileName = file.getName();
if (fileName.startsWith("builder-model-") && EXT_JAR.equals(getExtension(fileName))) {
if (++builderModelFileCount > 1) {
return true;
}
}
}
return false;
}
private static void cleanUpIdePreferences() {
try {
ExtensionPoint<ConfigurableEP<Configurable>> ideConfigurable = Extensions.getRootArea().getExtensionPoint(APPLICATION_CONFIGURABLE);
cleanUpPreferences(ideConfigurable, IDE_SETTINGS_TO_REMOVE);
}
catch (Throwable e) {
LOG.info("Failed to clean up IDE preferences", e);
}
}
private static void replaceIdeaNewProjectActions() {
// Unregister IntelliJ's version of the project actions and manually register our own.
replaceAction("OpenFile", new AndroidOpenFileAction());
replaceAction("NewProject", new AndroidNewProjectAction());
replaceAction("NewModule", new AndroidNewModuleAction());
replaceAction("NewModuleInGroup", new AndroidNewModuleInGroupAction());
replaceAction("ImportProject", new AndroidImportProjectAction());
replaceAction("CreateLibraryFromFile", new CreateLibraryFromFilesAction());
replaceAction("ImportModule", new AndroidImportModuleAction());
hideAction(IdeActions.ACTION_GENERATE_ANT_BUILD, "Generate Ant Build...");
hideAction("AddFrameworkSupport", "Add Framework Support...");
hideAction("BuildArtifact", "Build Artifacts...");
hideAction("RunTargetAction", "Run Ant Target");
replaceProjectPopupActions();
replaceIdeaWelcomeScreenActions();
}
private static void replaceIdeaWelcomeScreenActions() {
// Update the Welcome Screen actions
ActionManager am = ActionManager.getInstance();
AndroidOpenFileAction openFileAction = new AndroidOpenFileAction();
openFileAction.getTemplatePresentation().setText("Open an existing Android Studio project");
replaceAction("WelcomeScreen.OpenProject", openFileAction);
AndroidNewProjectAction newProjectAction = new AndroidNewProjectAction();
newProjectAction.getTemplatePresentation().setText("Start a new Android Studio project");
replaceAction("WelcomeScreen.CreateNewProject", newProjectAction);
AndroidImportProjectAction importProjectAction = new AndroidImportProjectAction();
importProjectAction.getTemplatePresentation().setText("Import project (Eclipse ADT, Gradle, etc.)");
replaceAction("WelcomeScreen.ImportProject", importProjectAction);
moveAction("WelcomeScreen.ImportProject", "WelcomeScreen.QuickStart.IDEA", "WelcomeScreen.QuickStart",
new Constraints(Anchor.AFTER, "WelcomeScreen.GetFromVcs"));
am.getAction("WelcomeScreen.GetFromVcs").getTemplatePresentation().setText("Check out project from Version Control");
}
private static void replaceProjectStructureActions() {
AndroidTemplateProjectStructureAction showDefaultProjectStructureAction = new AndroidTemplateProjectStructureAction();
showDefaultProjectStructureAction.getTemplatePresentation().setText("Default Project Structure...");
replaceAction("TemplateProjectStructure", showDefaultProjectStructureAction);
ActionManager am = ActionManager.getInstance();
AnAction action = am.getAction("WelcomeScreen.Configure.IDEA");
if (action instanceof DefaultActionGroup) {
DefaultActionGroup projectSettingsGroup = (DefaultActionGroup)action;
AnAction[] children = projectSettingsGroup.getChildren(null);
if (children.length == 1 && children[0] instanceof TemplateProjectSettingsGroup) {
projectSettingsGroup.replaceAction(children[0], new AndroidTemplateProjectSettingsGroup());
}
}
}
private static void replaceIdeaMakeActions() {
// 'Build' > 'Make Project' action
replaceAction("CompileDirty", new AndroidMakeProjectAction());
// 'Build' > 'Make Modules' action
replaceAction(IdeActions.ACTION_MAKE_MODULE, new AndroidMakeModuleAction());
// 'Build' > 'Rebuild' action
replaceAction(IdeActions.ACTION_COMPILE_PROJECT, new AndroidRebuildProjectAction());
// 'Build' > 'Compile Modules' action
hideAction(IdeActions.ACTION_COMPILE, "Compile Module(s)");
}
private static void replaceAction(String actionId, AnAction newAction) {
ActionManager am = ActionManager.getInstance();
AnAction oldAction = am.getAction(actionId);
if (oldAction != null) {
newAction.getTemplatePresentation().setIcon(oldAction.getTemplatePresentation().getIcon());
am.unregisterAction(actionId);
}
am.registerAction(actionId, newAction);
}
private static void moveAction(@NotNull String actionId, @NotNull String oldGroupId, @NotNull String groupId, @NotNull Constraints constraints) {
ActionManager am = ActionManager.getInstance();
AnAction action = am.getAction(actionId);
AnAction group = am.getAction(groupId);
AnAction oldGroup = am.getAction(oldGroupId);
if (action != null && oldGroup != null && group != null && oldGroup instanceof DefaultActionGroup && group instanceof DefaultActionGroup) {
((DefaultActionGroup)oldGroup).getChildren(null); //call get children to resolve stubs
((DefaultActionGroup)oldGroup).remove(action);
((DefaultActionGroup)group).add(action, constraints);
}
}
private static void hideAction(@NotNull String actionId, @NotNull String backupText) {
AnAction oldAction = ActionManager.getInstance().getAction(actionId);
if (oldAction != null) {
AnAction newAction = new AndroidActionRemover(oldAction, backupText);
replaceAction(actionId, newAction);
}
}
private static void replaceProjectPopupActions() {
Deque<Pair<DefaultActionGroup, AnAction>> stack = new ArrayDeque<Pair<DefaultActionGroup, AnAction>>();
stack.add(Pair.of((DefaultActionGroup)null, ActionManager.getInstance().getAction("ProjectViewPopupMenu")));
while (!stack.isEmpty()) {
Pair<DefaultActionGroup, AnAction> entry = stack.pop();
DefaultActionGroup parent = entry.getFirst();
AnAction action = entry.getSecond();
if (action instanceof DefaultActionGroup) {
DefaultActionGroup actionGroup = (DefaultActionGroup)action;
for (AnAction child : actionGroup.getChildActionsOrStubs()) {
stack.push(Pair.of(actionGroup, child));
}
}
if (action instanceof MoveModuleToGroupTopLevel) {
parent.remove(action);
parent.add(new AndroidActionGroupRemover((ActionGroup)action, "Move Module to Group"),
new Constraints(Anchor.AFTER, "OpenModuleSettings"));
} else if (action instanceof MarkRootGroup) {
parent.remove(action);
parent.add(new AndroidActionGroupRemover((ActionGroup)action, "Mark Directory As"),
new Constraints(Anchor.AFTER, "OpenModuleSettings"));
}
}
}
private static void setupSdks() {
File androidHome = IdeSdks.getAndroidSdkPath();
if (androidHome != null) {
// Do not prompt user to select SDK path (we have one already.) Instead, check SDK compatibility when a project is opened.
return;
}
// If running in a GUI test we don't want the "Select SDK" dialog to show up when running GUI tests.
if (AndroidPlugin.isGuiTestingMode()) {
// This is good enough. Later on in the GUI test we'll validate the given SDK path.
return;
}
final Sdk sdk = findFirstCompatibleAndroidSdk();
if (sdk != null) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
String androidHome = sdk.getHomePath();
assert androidHome != null;
IdeSdks.createAndroidSdkPerAndroidTarget(new File(toSystemDependentName(androidHome)));
}
});
return;
}
// Called in a 'invokeLater' block, otherwise file chooser will hang forever.
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
File androidSdkPath = getAndroidSdkPath();
if (androidSdkPath == null) {
return;
}
FirstRunWizardMode wizardMode = AndroidStudioWelcomeScreenProvider.getWizardMode();
// Only show "Select SDK" dialog if the "First Run" wizard is not displayed.
boolean promptSdkSelection = wizardMode == null;
Sdk sdk = createNewAndroidPlatform(androidSdkPath.getPath(), promptSdkSelection);
if (sdk != null) {
// Rename the SDK to fit our default naming convention.
if (sdk.getName().startsWith(SDK_NAME_PREFIX)) {
SdkModificator sdkModificator = sdk.getSdkModificator();
sdkModificator.setName(SDK_NAME_PREFIX + sdk.getName().substring(SDK_NAME_PREFIX.length()));
sdkModificator.commitChanges();
// Rename the JDK that goes along with this SDK.
AndroidSdkAdditionalData additionalData = getAndroidSdkAdditionalData(sdk);
if (additionalData != null) {
Sdk jdk = additionalData.getJavaSdk();
if (jdk != null) {
sdkModificator = jdk.getSdkModificator();
sdkModificator.setName(DEFAULT_JDK_NAME);
sdkModificator.commitChanges();
}
}
// Fill out any missing build APIs for this new SDK.
IdeSdks.createAndroidSdkPerAndroidTarget(androidSdkPath);
}
}
}
});
}
@Nullable
private static Sdk findFirstCompatibleAndroidSdk() {
List<Sdk> sdks = getAllAndroidSdks();
for (Sdk sdk : sdks) {
String sdkPath = sdk.getHomePath();
if (isCompatibleVersion(sdkPath)) {
return sdk;
}
}
if (!sdks.isEmpty()) {
return sdks.get(0);
}
return null;
}
@Nullable
private static File getAndroidSdkPath() {
String studioHome = PathManager.getHomePath();
if (isEmpty(studioHome)) {
LOG.info("Unable to find Studio home directory");
}
else {
LOG.info(String.format("Found Studio home directory at: '%1$s'", studioHome));
for (String path : ANDROID_SDK_RELATIVE_PATHS) {
File dir = new File(studioHome, path);
String absolutePath = toCanonicalPath(dir.getAbsolutePath());
LOG.info(String.format("Looking for Android SDK at '%1$s'", absolutePath));
if (AndroidSdkType.getInstance().isValidSdkHome(absolutePath)) {
LOG.info(String.format("Found Android SDK at '%1$s'", absolutePath));
return new File(absolutePath);
}
}
}
LOG.info("Unable to locate SDK within the Android studio installation.");
String androidHomeValue = System.getenv(SdkConstants.ANDROID_HOME_ENV);
String msg = String.format("Checking if ANDROID_HOME is set: '%1$s' is '%2$s'", SdkConstants.ANDROID_HOME_ENV, androidHomeValue);
LOG.info(msg);
if (!isEmpty(androidHomeValue) && AndroidSdkType.getInstance().isValidSdkHome(androidHomeValue)) {
LOG.info("Using Android SDK specified by the environment variable.");
return new File(toSystemDependentName(androidHomeValue));
}
String sdkPath = getLastSdkPathUsedByAndroidTools();
if (!isEmpty(sdkPath) && AndroidSdkType.getInstance().isValidSdkHome(androidHomeValue)) {
msg = String.format("Last SDK used by Android tools: '%1$s'", sdkPath);
} else {
msg = "Unable to locate last SDK used by Android tools";
}
LOG.info(msg);
return sdkPath == null ? null : new File(toSystemDependentName(sdkPath));
}
/**
* Returns the value for property 'lastSdkPath' as stored in the properties file at $HOME/.android/ddms.cfg, or {@code null} if the file
* or property doesn't exist.
*
* This is only useful in a scenario where existing users of ADT/Eclipse get Studio, but without the bundle. This method duplicates some
* functionality of {@link com.android.prefs.AndroidLocation} since we don't want any file system writes to happen during this process.
*/
@Nullable
private static String getLastSdkPathUsedByAndroidTools() {
String userHome = SystemProperties.getUserHome();
if (userHome == null) {
return null;
}
File file = new File(new File(userHome, ".android"), "ddms.cfg");
if (!file.exists()) {
return null;
}
try {
Properties properties = getProperties(file);
return properties.getProperty("lastSdkPath");
} catch (IOException e) {
return null;
}
}
/**
* Remove popup actions that we don't use
*/
private static void hideIdeaNewFilePopupActions() {
hideAction("NewHtmlFile", "HTML File");
hideAction("NewPackageInfo", "package-info.java");
// Hide designer actions
hideAction("NewForm", "GUI Form");
hideAction("NewDialog", "Dialog");
hideAction("NewFormSnapshot", "Form Snapshot");
// Hide individual actions that aren't part of a group
replaceAction("Groovy.NewClass", new EmptyAction());
replaceAction("Groovy.NewScript", new EmptyAction());
}
/**
* Registers an callback that gets notified when the IDE is closing.
*/
private static void registerAppClosing() {
Application app = ApplicationManager.getApplication();
MessageBusConnection connection = app.getMessageBus().connect(app);
connection.subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
@Override
public void appClosing() {
try {
stopAllGradleDaemons(false);
}
catch (IOException e) {
LOG.info("Failed to stop Gradle daemons", e);
}
}
});
}
private static void checkAndSetAndroidSdkSources() {
for (Sdk sdk : getAllAndroidSdks()) {
checkAndSetSources(sdk);
}
}
private static void checkAndSetSources(@NotNull Sdk sdk) {
VirtualFile[] storedSources = sdk.getRootProvider().getFiles(OrderRootType.SOURCES);
if (storedSources.length > 0) {
return;
}
AndroidPlatform platform = AndroidPlatform.getInstance(sdk);
if (platform != null) {
SdkModificator sdkModificator = sdk.getSdkModificator();
IAndroidTarget target = platform.getTarget();
findAndSetPlatformSources(target, sdkModificator);
sdkModificator.commitChanges();
}
}
@Override
public void run() {
checkInstallation();
cleanUpIdePreferences();
if (!Boolean.getBoolean(USE_IDEA_NEW_PROJECT_WIZARDS)) {
replaceIdeaNewProjectActions();
}
if (!Boolean.getBoolean(USE_IDEA_PROJECT_STRUCTURE)) {
replaceProjectStructureActions();
}
if (!Boolean.getBoolean(USE_JPS_MAKE_ACTIONS)) {
replaceIdeaMakeActions();
}
if (!Boolean.getBoolean(USE_IDEA_NEW_FILE_POPUPS)) {
hideIdeaNewFilePopupActions();
}
try {
// Setup JDK and Android SDK if necessary
setupSdks();
} catch (Exception e) {
LOG.error("Unexpected error while setting up SDKs: ", e);
}
addExtraBuildActions();
hideMiscActions();
hideUnwantedIntentions();
registerAppClosing();
// Always reset the Default scheme to match Android standards
// User modifications won't be lost since they are made in a separate scheme (copied off of this default scheme)
CodeStyleScheme scheme = CodeStyleSchemes.getInstance().getDefaultScheme();
if (scheme != null) {
CodeStyleSettings settings = scheme.getCodeStyleSettings();
if (settings != null) {
AndroidCodeStyleSettingsModifier.modify(settings);
}
}
// Modify built-in "Default" color scheme to remove background from XML tags.
// "Darcula" and user schemes will not be touched.
EditorColorsScheme colorsScheme = EditorColorsManager.getInstance().getScheme(EditorColorsScheme.DEFAULT_SCHEME_NAME);
TextAttributes textAttributes = colorsScheme.getAttributes(HighlighterColors.TEXT);
TextAttributes xmlTagAttributes = colorsScheme.getAttributes(XmlHighlighterColors.XML_TAG);
xmlTagAttributes.setBackgroundColor(textAttributes.getBackgroundColor());
checkAndSetAndroidSdkSources();
}
private static void hideMiscActions() {
ActionManager am = ActionManager.getInstance();
// "Configure Plugins..." Not sure why it's called StartupWizard.
AnAction pluginAction = am.getAction("StartupWizard");
// Never applicable in the context of android studio, so just set to invisible.
pluginAction.getTemplatePresentation().setVisible(false);
}
private static void addExtraBuildActions() {
ActionManager actionManager = ActionManager.getInstance();
AnAction buildMenu = actionManager.getAction("BuildMenu");
if (buildMenu instanceof DefaultActionGroup) {
DefaultActionGroup buildMenuGroup = (DefaultActionGroup)buildMenu;
buildMenuGroup.addSeparator();
buildMenuGroup.add(new EditBuildTypesAction());
buildMenuGroup.add(new EditFlavorsAction());
buildMenuGroup.add(new EditLibraryAndDependenciesAction());
buildMenuGroup.add(new SelectBuildVariantAction());
}
}
private static Set<String> unwantedIntetionFamilyNames = Sets.newHashSet("Add testng.jar to classpath", /* TestNGOrderEntryFix */
"Add jcip-annotations.jar to classpath" /* JCiPOrderEntryFix */);
/**
* Disable the intentions that we don't want in android studio.
*/
private void hideUnwantedIntentions() {
IntentionManager intentionManager = IntentionManager.getInstance();
if (!(intentionManager instanceof IntentionManagerImpl)) {
return;
}
// IntentionManagerImpl.hasActiveRequests equals true when there is unprocessed register extension request, theoretically, it is
// possible that two requests have a relative big time gap, so that hasActiveRequests could become true after first request has been
// processed but the second one haven't been sent, thus cause us skipping the intentions we care about.
// In reality this isn't problem so far because all the extension register requests are sent through a loop.
// TODO Ideally, we want make IntentionManagerImpl.registerIntentionFromBean as protected method so we could override it and ignore the
// unwanted intentions in the first place.
while (((IntentionManagerImpl)intentionManager).hasActiveRequests()) {
TimeoutUtil.sleep(100);
}
for (IntentionAction intentionAction : intentionManager.getIntentionActions()) {
if (unwantedIntetionFamilyNames.contains(intentionAction.getFamilyName())) {
intentionManager.unregisterIntention(intentionAction);
}
}
}
}