blob: eefcd1d9e8401305fb5374c44e3e1085edf01f91 [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;
import com.intellij.application.options.editor.WebEditorOptions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.keymap.impl.KeymapManagerImpl;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
/** Customize Android IDE specific experience. */
public class AndroidInitialConfigurator {
@NonNls
private static final ExtensionPointName<Runnable> EP_NAME =
ExtensionPointName.create("com.intellij.androidStudioInitializer");
@NonNls private static final String CONFIG_V1 = "AndroidInitConfigurator.V1";
@NonNls private static final String CONFIG_V2 = "AndroidInitConfigurator.V2";
@NonNls private static final String CONFIG_V3 = "AndroidInitConfigurator.V3";
@NonNls private static final String CONFIG_V4 = "AndroidInitConfigurator.V4";
@SuppressWarnings("SpellCheckingInspection")
@NonNls private static final String TODO_TOOLWINDOW_ACTION_ID = "ActivateTODOToolWindow";
@SuppressWarnings("SpellCheckingInspection")
@NonNls private static final String ANDROID_TOOLWINDOW_ACTION_ID = "ActivateAndroidToolWindow";
@SuppressWarnings("UnusedParameters")
public AndroidInitialConfigurator(MessageBus bus,
final PropertiesComponent propertiesComponent,
final FileTypeManager fileTypeManager) {
validateConfiguration();
setupSystemProperties();
customizeSettings(propertiesComponent);
// change default key maps to add a activate Android ToolWindow shortcut
setActivateAndroidToolWindowShortcut();
activateAndroidStudioInitializerExtensions();
}
/**
* Validates that the IDE configuration follows Android Studio requirements.
*/
private static void validateConfiguration() {
String[] versions = {
ApplicationInfo.getInstance().getMajorVersion(),
ApplicationInfo.getInstance().getMinorVersion(),
ApplicationInfo.getInstance().getMicroVersion(),
ApplicationInfo.getInstance().getPatchVersion(),
};
for (String version : versions) {
if (version == null || !version.matches("0|([1-9]+[0-9]*)")) {
throw new AssertionError("Invalid version component found: " + version);
}
}
}
/**
* Setup some Java system properties based on environment variables.
* This makes it easier to do local testing.
*/
private static void setupSystemProperties() {
// If defined, AS_UPDATE_URL should point to the *root* of the updates.xml file to use
// and patches are expected to be in the same folder.
String updateUrl = System.getenv("AS_UPDATE_URL");
if (updateUrl != null) {
if (!updateUrl.endsWith("/")) {
updateUrl += "/";
}
// Set the Java system properties expected by UpdateChecker.
System.setProperty("idea.updates.url", updateUrl + "updates.xml");
System.setProperty("idea.patches.url", updateUrl);
}
}
private static void customizeSettings(PropertiesComponent propertiesComponent) {
if (!propertiesComponent.getBoolean(CONFIG_V4, false)) {
propertiesComponent.setValue(CONFIG_V4, "true");
UISettings.getInstance().HIDE_TOOL_STRIPES = false;
if (!propertiesComponent.getBoolean(CONFIG_V3, false)) {
propertiesComponent.setValue(CONFIG_V3, "true");
// See https://youtrack.jetbrains.com/issue/IDEA-113332
// It's not really fixed but worked around by turning off the =-quoting
WebEditorOptions.getInstance().setInsertQuotesForAttributeValue(false);
}
if (!propertiesComponent.getBoolean(CONFIG_V1, false)) {
propertiesComponent.setValue(CONFIG_V1, "true");
CodeInsightSettings.getInstance().AUTO_POPUP_JAVADOC_INFO = true;
UISettings.getInstance().SCROLL_TAB_LAYOUT_IN_EDITOR = true;
EditorSettingsExternalizable.getInstance().setVirtualSpace(false);
// For Macs, use 10.5+ keymap as the default
if (SystemInfo.isMac) {
setDefaultMacKeymap();
}
}
if (!propertiesComponent.getBoolean(CONFIG_V2, false)) {
propertiesComponent.setValue(CONFIG_V2, "true");
UISettings.getInstance().SHOW_MAIN_TOOLBAR = true;
}
}
}
private static void setDefaultMacKeymap() {
KeymapManagerImpl instance = (KeymapManagerImpl)KeymapManager.getInstance();
Keymap mac105Keymap = getMac105Keymap();
if (mac105Keymap != null) {
instance.setActiveKeymap(mac105Keymap);
}
}
@Nullable
private static Keymap getMac105Keymap() {
for (Keymap keymap: KeymapManagerEx.getInstanceEx().getAllKeymaps()) {
if (keymap.getName().contains("10.5")) {
return keymap;
}
}
return null;
}
private static void setActivateAndroidToolWindowShortcut() {
// The IntelliJ keymap implementation behaves as follows:
// - getting a shortcut from a keymap gets the shortcut only from that keymap, and not from its parent, unless no shortcuts
// are defined in the keymap itself
// - however, adding a shortcut to a keymap explicitly copies all the shortcuts for that action from the parent keymap to this keymap
// The upshot of all this is that to add a shortcut, we should do so in all the child keymaps first, then the parent keymap.
// The following code does a simplified implementation of this behavior by changing the default keymap last after all the other
// keymaps have been changed.
Keymap defaultKeymap = null;
for (Keymap keymap: KeymapManagerEx.getInstanceEx().getAllKeymaps()) {
if (KeymapManager.DEFAULT_IDEA_KEYMAP.equals(keymap.getName())) {
defaultKeymap = keymap;
continue;
}
setActivateAndroidToolWindowShortcut(keymap);
}
if (defaultKeymap != null) {
setActivateAndroidToolWindowShortcut(defaultKeymap);
}
}
private static void setActivateAndroidToolWindowShortcut(Keymap keymap) {
KeyboardShortcut shortcut = removeFirstKeyboardShortcut(keymap, TODO_TOOLWINDOW_ACTION_ID);
if (shortcut != null) {
keymap.addShortcut(ANDROID_TOOLWINDOW_ACTION_ID, shortcut);
}
}
@Nullable
private static KeyboardShortcut removeFirstKeyboardShortcut(Keymap keymap, String actionId) {
Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut each : shortcuts) {
if (each instanceof KeyboardShortcut) {
keymap.removeShortcut(actionId, each);
return (KeyboardShortcut)each;
}
}
return null;
}
private static void activateAndroidStudioInitializerExtensions() {
Runnable[] extensions = EP_NAME.getExtensions();
for (Runnable r : extensions) {
r.run();
}
}
}