Merge remote-tracking branch 'origin/master'

This commit is contained in:
Anna Kozlova
2012-01-26 16:40:49 +04:00
89 changed files with 2541 additions and 232 deletions
@@ -1,6 +1,6 @@
command.0.execution.failed.with.exit.code.1=Command \"{0}\" execution failed with exit code {1}
aidl.filetype.description=Android IDL files
add.string.resource.intention.text=Add a string resource
add.string.resource.intention.text=Extract string resource
resource.name=Resource name
intention.family=Android
package.not.found.error=Package is not specified in the manifest file
@@ -8,8 +8,8 @@ cannot.resolve.flag.error=Cannot resolve flag
cannot.resolve.format.error=Cannot resolve format
create.resource.quickfix.name=Create resource {0} in {1}
quick.fixes.family=Android Quick Fixes
not.resource.file.error="File {0} is not resource file"
check.resource.dir.error=Check if resource directory exists
not.resource.file.error=File {0} is not resource file
check.resource.dir.error=Cannot find resource directory for module {0}
choose.device.dialog.title=Choose Device
replace.avd.question=AVD {0} already exists. Replace?
create.avd.dialog.title=Create Android Virtual Device
@@ -147,6 +147,7 @@ create.run.configuration.title=Create Android Run Configuration
file.must.be.under.module.error=Cannot compute relative path for {0}
android.extract.package.choose.dest.apk=Choose destination APK file
android.cannot.create.file.error=Cannot create file {0}
android.cannot.create.dir.error=Cannot create directory {0}
android.manifest.not.found.error=AndroidManifest.xml doesn't exist or has incorrect root tag
android.apt.settings.title=AAPT Compiler
android.aidl.settings.title=AIDL Compiler
@@ -305,4 +306,20 @@ android.facet.settings.custom.debug.keystore.label=C&ustom debug keystore\:
android.export.package.run.proguard.label=Run &ProGuard
android.export.package.proguad.config.label=C&onfig file path\:
android.extract.package.specify.proguard.cfg.path.error=Please specify ProGuard config file path
android.inspections.lint.name=Android Lint Validation
android.inspections.lint.name=Android Lint Validation
android.lint.quickfixes.family=Android Lint Quick Fixes
android.lint.inspections.add.content.description=Add 'contentDescription' attribute
android.lint.inspections.scroll.view.children=ScrollView element can have only one child
android.lint.inspections.deprecated=Deprecated XML elements
android.lint.inspections.missing.prefix=Missing android XML namespace
android.lint.inspections.adapter.view.children=AdapterView element cannot have children
android.lint.inspections.add.android.prefix=Add android prefix
android.lint.inspections.replace.with.zero.dp=Replace size attribute with 0dp
android.lint.inspections.set.baseline.attribute=Set 'baselineAligned' attribute
android.lint.inspections.remove.attribute=Remove attribute
android.lint.inspections.convert.to.dp=Convert to \\"dp\\"...
android.lint.inspections.set.to.wrap.content=Replace size attribute with 'wrap_content'
android.lint.inspections.add.permission.attribute=Add 'permission' attribute
android.lint.inspections.add.input.type.attribute=Add 'inputType' attribute
android.lint.inspections.remove.unnecessary.view=Remove unnecessary view
android.lint.inspections.replace.with.suggested.characters=Replace with suggested characters
@@ -3,7 +3,7 @@
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="407" height="170"/>
<xy x="20" y="20" width="417" height="188"/>
</constraints>
<properties/>
<border type="none"/>
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.actions.CreateXmlResourceDialog">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="5" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="82472" binding="myDeviceConfigurationWrapper" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<component id="43fab" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="83b48"/>
<text value="&amp;Name:"/>
</properties>
</component>
<component id="83b48" class="javax.swing.JTextField" binding="myNameField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="c0cba" class="javax.swing.JComboBox" binding="myModuleCombo">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="61abf" class="com.intellij.ui.components.JBLabel" binding="myModuleLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="c0cba"/>
<text value="&amp;Module:"/>
</properties>
</component>
<component id="d6c07" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="832da"/>
<text value="&amp;Directory name:"/>
</properties>
</component>
<component id="832da" class="javax.swing.JTextField" binding="myDirectoryNameField">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="da401" class="com.intellij.ui.components.JBLabel" binding="myErrorLabel">
<constraints>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="21872" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="185f7"/>
<text value="&amp;File name:"/>
</properties>
</component>
<component id="185f7" class="javax.swing.JTextField" binding="myFileNameField">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
</children>
</grid>
</form>
@@ -0,0 +1,264 @@
package org.jetbrains.android.actions;
import com.android.AndroidConstants;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.intellij.CommonBundle;
import com.intellij.ide.ui.ListCellRendererWrapper;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.containers.HashSet;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.Resources;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.uipreview.DeviceConfiguratorPanel;
import org.jetbrains.android.uipreview.InvalidOptionValueException;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
public class CreateXmlResourceDialog extends DialogWrapper {
private JPanel myPanel;
private JPanel myDeviceConfigurationWrapper;
private JTextField myNameField;
private JComboBox myModuleCombo;
private JBLabel myModuleLabel;
private JTextField myDirectoryNameField;
private JBLabel myErrorLabel;
private JTextField myFileNameField;
private final DeviceConfiguratorPanel myDeviceConfiguratorPanel;
private final Module myModule;
private final ResourceType myResourceType;
public CreateXmlResourceDialog(@NotNull Module module, @NotNull ResourceType resourceType) {
super(module.getProject());
myResourceType = resourceType;
final Set<Module> modulesSet = new HashSet<Module>();
modulesSet.add(module);
for (AndroidFacet depFacet : AndroidUtils.getAllAndroidDependencies(module, true)) {
modulesSet.add(depFacet.getModule());
}
assert modulesSet.size() > 0;
if (modulesSet.size() == 1) {
myModule = module;
myModuleLabel.setVisible(false);
myModuleCombo.setVisible(false);
}
else {
myModule = null;
final Module[] modules = modulesSet.toArray(new Module[modulesSet.size()]);
Arrays.sort(modules, new Comparator<Module>() {
@Override
public int compare(Module m1, Module m2) {
return m1.getName().compareTo(m2.getName());
}
});
myModuleCombo.setModel(new DefaultComboBoxModel(modules));
myModuleCombo.setSelectedItem(module);
myModuleCombo.setRenderer(new ListCellRendererWrapper<Module>(myModuleCombo.getRenderer()) {
@Override
public void customize(JList list, Module module, int index, boolean selected, boolean hasFocus) {
setText(module.getName());
setIcon(ModuleType.get(module).getNodeIcon(false));
}
});
}
myDeviceConfiguratorPanel = new DeviceConfiguratorPanel(null) {
@Override
public void applyEditors() {
try {
doApplyEditors();
final FolderConfiguration config = myDeviceConfiguratorPanel.getConfiguration();
myErrorLabel.setText("");
myDirectoryNameField.setText(config.getFolderName(ResourceFolderType.VALUES));
}
catch (InvalidOptionValueException e) {
myErrorLabel.setText("<html><body><font color=\"red\">" + e.getMessage() + "</font></body></html>");
myDirectoryNameField.setText(AndroidConstants.FD_RES_VALUES);
}
}
};
myDeviceConfigurationWrapper.add(myDeviceConfiguratorPanel, BorderLayout.CENTER);
final String defaultResFileName = ResourceManager.getDefaultResourceFileName(resourceType.getName());
if (defaultResFileName != null) {
myFileNameField.setText(defaultResFileName);
}
myDirectoryNameField.setText(AndroidConstants.FD_RES_VALUES);
myDeviceConfiguratorPanel.updateAll();
init();
}
@Override
protected ValidationInfo doValidate() {
final String resourceName = getResourceName();
final Module selectedModule = getModule();
final String directoryName = getDirectoryName();
final String fileName = getFileName();
if (resourceName.length() == 0) {
return new ValidationInfo("specify resource name", myNameField);
}
else if (!AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) {
return new ValidationInfo(resourceName + " is not correct resource name", myNameField);
}
else if (fileName.length() == 0) {
return new ValidationInfo("specify file name", myFileNameField);
}
else if (selectedModule == null) {
return new ValidationInfo("specify module", myModuleCombo);
}
else if (!ResourceFolderType.VALUES.getName().equals(
ResourceManager.getResourceTypeByDirName(directoryName))) {
return new ValidationInfo("directory name is not appropriate for value resources");
}
final ValidationInfo info = checkIfResourceAlreadyExists(selectedModule, resourceName, myResourceType, directoryName, fileName);
if (info != null) {
return info;
}
try {
myDeviceConfiguratorPanel.doApplyEditors();
}
catch (InvalidOptionValueException e) {
return new ValidationInfo("fix errors in configuration editor");
}
return null;
}
@Nullable
private static ValidationInfo checkIfResourceAlreadyExists(@NotNull Module selectedModule,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull String directoryName,
@NotNull String fileName) {
if (resourceName.length() == 0 ||
directoryName.length() == 0 ||
fileName.length() == 0) {
return null;
}
final VirtualFile resourceDir = AndroidRootUtil.getResourceDir(selectedModule);
if (resourceDir == null) {
return null;
}
final VirtualFile resourceSubdir = resourceDir.findChild(directoryName);
if (resourceSubdir == null) {
return null;
}
final VirtualFile resFile = resourceSubdir.findChild(fileName);
if (resFile == null) {
return null;
}
if (resFile.getFileType() != StdFileTypes.XML) {
return new ValidationInfo("File " + FileUtil.toSystemDependentName(resFile.getPath()) + " is not XML file");
}
final Resources resources = AndroidUtils.loadDomElement(selectedModule, resFile, Resources.class);
if (resources == null) {
return new ValidationInfo(AndroidBundle.message("not.resource.file.error", FileUtil.toSystemDependentName(resFile.getPath())));
}
for (ResourceElement element : ResourceManager.getValueResources(resourceType.getName(), resources)) {
if (resourceName.equals(element.getName().getValue())) {
return new ValidationInfo("resource '" + resourceName + "' already exists in " + FileUtil.toSystemDependentName(
resFile.getPath()));
}
}
return null;
}
@Override
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
@Override
protected void doOKAction() {
final String resourceName = getResourceName();
final String fileName = getFileName();
final String dirName = getDirectoryName();
final Module module = getModule();
if (resourceName.length() == 0) {
Messages.showErrorDialog(myPanel, "Resource name is not specified", CommonBundle.getErrorTitle());
}
else if (!AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) {
Messages.showErrorDialog(myPanel, resourceName + " is not correct resource name", CommonBundle.getErrorTitle());
}
else if (fileName.length() == 0) {
Messages.showErrorDialog(myPanel, "File name is not specified", CommonBundle.getErrorTitle());
}
else if (dirName.length() == 0) {
Messages.showErrorDialog(myPanel, "Directory name is not specified", CommonBundle.getErrorTitle());
}
else if (module == null) {
Messages.showErrorDialog(myPanel, "Module is not specified", CommonBundle.getErrorTitle());
}
else {
super.doOKAction();
}
}
@NotNull
public String getResourceName() {
return myNameField.getText().trim();
}
@NotNull
public String getDirectoryName() {
return myDirectoryNameField.getText().trim();
}
@NotNull
public String getFileName() {
return myFileNameField.getText().trim();
}
@Nullable
public Module getModule() {
return myModule != null ? myModule : (Module)myModuleCombo.getSelectedItem();
}
@Override
protected JComponent createCenterPanel() {
return myPanel;
}
}
@@ -165,7 +165,12 @@ public class ResourceReferenceConverter extends ResolvingConverter<ResourceValue
}
private Set<String> getResourceTypes(ConvertContext context) {
ResourceType resourceType = context.getInvocationElement().getAnnotation(ResourceType.class);
return getResourceTypes(context.getInvocationElement());
}
@NotNull
public Set<String> getResourceTypes(@NotNull DomElement element) {
ResourceType resourceType = element.getAnnotation(ResourceType.class);
Set<String> types = new HashSet<String>(myResourceTypes);
if (resourceType != null) {
String s = resourceType.value();
@@ -0,0 +1,72 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.xml.XmlExtension;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
/**
* @author Eugene.Kudelevsky
*/
class AddMissingPrefixQuickFix implements AndroidLintQuickFix {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AddMissingPrefixQuickFix");
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class, false);
if (attribute == null) {
return;
}
final XmlTag tag = attribute.getParent();
if (tag == null) {
LOG.debug("tag is null");
return;
}
String androidNsPrefix = tag.getPrefixByNamespace(SdkConstants.NS_RESOURCES);
if (androidNsPrefix == null) {
final PsiFile file = tag.getContainingFile();
final XmlExtension extension = XmlExtension.getExtension(file);
if (extension == null) {
LOG.debug("Cannot get XmlExtension for file + " + file);
return;
}
if (!(file instanceof XmlFile)) {
LOG.debug(file + " is not XmlFile");
return;
}
final XmlFile xmlFile = (XmlFile)file;
final String defaultPrefix = "android";
extension.insertNamespaceDeclaration(xmlFile, null, Collections.singleton(SdkConstants.NS_RESOURCES), defaultPrefix, null);
androidNsPrefix = defaultPrefix;
}
attribute.setName(androidNsPrefix + ':' + attribute.getLocalName());
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class, false) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.add.android.prefix");
}
}
@@ -0,0 +1,47 @@
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.intentions.AndroidAddStringResourceAction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidAddStringResourceQuickFix extends AndroidAddStringResourceAction {
private final PsiElement myStartElement;
public AndroidAddStringResourceQuickFix(@NotNull PsiElement startElement) {
myStartElement = startElement;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return getAttributeValue(myStartElement) != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
invokeIntention(project, editor, file, null);
}
public void invokeIntention(Project project, Editor editor, PsiFile file, String resName) {
final XmlAttributeValue attributeValue = getAttributeValue(myStartElement);
if (attributeValue != null) {
doInvoke(project, editor, file, resName, attributeValue);
}
}
@Nullable
private static XmlAttributeValue getAttributeValue(@NotNull PsiElement element) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class);
return attribute != null ? attribute.getValueElement() : null;
}
}
@@ -7,11 +7,11 @@ import com.android.tools.lint.client.api.Lint;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ex.CustomEditInspectionToolsSettingsAction;
import com.intellij.codeInspection.ex.DisableInspectionToolAction;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ex.EditInspectionToolsSettingsAction;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
@@ -22,7 +22,10 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
@@ -30,6 +33,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -62,7 +66,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
}
final FileType fileType = file.getFileType();
if (fileType == StdFileTypes.XML) {
if (facet.getLocalResourceManager().getFileResourceType(file) == null &&
AndroidRootUtil.getManifestFile(module) != vFile) {
@@ -95,7 +99,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
return state.getIssues();
}
}, client);
lint.analyze(Collections.singletonList(new File(state.getMainFile().getPath())), null);
}
finally {
@@ -126,10 +130,10 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
if (!enabled) {
continue;
}
result.add(issue);
result.add(issue);
}
return result;
}
}
@Override
public void apply(@NotNull PsiFile file, State state, @NotNull AnnotationHolder holder) {
@@ -141,31 +145,41 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
final Issue issue = problemData.getIssue();
final String message = problemData.getMessage();
final TextRange range = problemData.getTextRange();
if (range.getStartOffset() == range.getEndOffset()) {
continue;
}
final Pair<AndroidLintInspectionBase, HighlightDisplayLevel> pair = getHighlighLevelAndInspection(issue, file);
final Pair<AndroidLintInspectionBase, HighlightDisplayLevel> pair = AndroidLintUtil.getHighlighLevelAndInspection(issue, file);
if (pair == null) {
continue;
}
final AndroidLintInspectionBase inspection = pair.getFirst();
final HighlightDisplayLevel displayLevel = pair.getSecond();
final Annotation annotation = createAnnotation(holder, message, range, displayLevel);
if (inspection != null) {
final HighlightDisplayKey key = HighlightDisplayKey.find(inspection.getShortName());
if (key != null) {
annotation.registerFix(new MyDisableInspectionFix(key));
annotation.registerFix(new CustomEditInspectionToolsSettingsAction(key, new Computable<String>() {
@Override
public String compute() {
return "Edit '" + inspection.getDisplayName() + "' inspection settings";
final PsiElement startElement = file.findElementAt(range.getStartOffset());
final PsiElement endElement = file.findElementAt(range.getEndOffset() - 1);
if (startElement != null && endElement != null) {
for (AndroidLintQuickFix fix : inspection.getQuickFixes(message)) {
if (fix.isApplicable(startElement, endElement, false)) {
annotation.registerFix(new MyFixingIntention(fix, startElement, endElement));
}
}
}));
for (IntentionAction intention : inspection.getIntentions(startElement, endElement)) {
annotation.registerFix(intention);
}
}
annotation.registerFix(new MyDisableInspectionFix(key));
annotation.registerFix(new MyEditInspectionToolsSettingsAction(key, inspection));
}
}
}
@@ -189,36 +203,6 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
}
}
@Nullable
private static Pair<AndroidLintInspectionBase, HighlightDisplayLevel> getHighlighLevelAndInspection(@NotNull Issue issue,
@NotNull PsiElement context) {
final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(issue);
if (inspectionShortName == null) {
return null;
}
final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName);
if (key == null) {
return null;
}
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile();
if (!profile.isToolEnabled(key, context)) {
return null;
}
final InspectionToolWrapper toolWrapper =
(InspectionToolWrapper)profile.getInspectionTool(inspectionShortName, context);
if (toolWrapper == null) {
return null;
}
final AndroidLintInspectionBase inspection = (AndroidLintInspectionBase)toolWrapper.getTool();
final HighlightDisplayLevel errorLevel = profile.getErrorLevel(key, context);
return new Pair<AndroidLintInspectionBase, HighlightDisplayLevel>(inspection,
errorLevel != null ? errorLevel : HighlightDisplayLevel.WARNING);
}
private static class MyDisableInspectionFix implements IntentionAction, Iconable {
private final DisableInspectionToolAction myDisableInspectionToolAction;
@@ -258,4 +242,90 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
return myDisableInspectionToolAction.getIcon(flags);
}
}
public static class MyFixingIntention implements IntentionAction, HighPriorityAction {
private final AndroidLintQuickFix myQuickFix;
private final PsiElement myStartElement;
private final PsiElement myEndElement;
public MyFixingIntention(@NotNull AndroidLintQuickFix quickFix, @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
myQuickFix = quickFix;
myStartElement = startElement;
myEndElement = endElement;
}
@NotNull
@Override
public String getText() {
return myQuickFix.getName();
}
@NotNull
@Override
public String getFamilyName() {
return AndroidBundle.message("android.lint.quickfixes.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
myQuickFix.apply(myStartElement, myEndElement, editor);
}
@Override
public boolean startInWriteAction() {
return true;
}
@NotNull
public AndroidLintQuickFix getQuickFix() {
return myQuickFix;
}
}
private static class MyEditInspectionToolsSettingsAction implements IntentionAction, Iconable {
private final EditInspectionToolsSettingsAction myEditInspectionToolsSettingsAction;
private final AndroidLintInspectionBase myInspection;
private MyEditInspectionToolsSettingsAction(@NotNull HighlightDisplayKey key, @NotNull final AndroidLintInspectionBase inspection) {
myEditInspectionToolsSettingsAction = new EditInspectionToolsSettingsAction(key);
myInspection = inspection;
}
@NotNull
@Override
public String getText() {
return "Edit '" + myInspection.getDisplayName() + "' inspection settings";
}
@NotNull
@Override
public String getFamilyName() {
return myEditInspectionToolsSettingsAction.getFamilyName();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
myEditInspectionToolsSettingsAction.invoke(project, editor, file);
}
@Override
public boolean startInWriteAction() {
return myEditInspectionToolsSettingsAction.startInWriteAction();
}
@Override
public Icon getIcon(@IconFlags int flags) {
return myEditInspectionToolsSettingsAction.getIcon(flags);
}
}
}
@@ -1,10 +1,7 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.PositionXmlParser;
import com.android.tools.lint.client.api.IDomParser;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.client.api.Lint;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.*;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
@@ -83,7 +80,7 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten
final File[] ioContentRoots = toIoFiles(allContentRoots);
final AnalysisScope scope = context.getRefManager().getScope();
final LintClient client = new MyLintClient(project, problemMap, scope);
final LintClient client = new MyLintClient(project, problemMap, scope, issues);
final Lint lint = new Lint(new IssueRegistry() {
@Override
public List<Issue> getIssues() {
@@ -128,13 +125,21 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten
private final Project myProject;
private final Map<Issue, Map<File, List<ProblemData>>> myProblemMap;
private final AnalysisScope myScope;
private final Collection<Issue> myIssues;
private MyLintClient(@NotNull Project project,
@NotNull Map<Issue, Map<File, List<ProblemData>>> problemMap,
@NotNull AnalysisScope scope) {
@NotNull AnalysisScope scope,
@NotNull Collection<Issue> issues) {
myProject = project;
myProblemMap = problemMap;
myScope = scope;
myIssues = issues;
}
@Override
public Configuration getConfiguration(com.android.tools.lint.detector.api.Project project) {
return new IntellijLintConfiguration(myIssues);
}
@Override
@@ -248,6 +253,9 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten
@Override
public List<File> getJavaSourceFolders(com.android.tools.lint.detector.api.Project project) {
final Module module = findModuleForLintProject(myProject, project);
if (module == null) {
return Collections.emptyList();
}
final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(false);
final List<File> result = new ArrayList<File>(sourceRoots.length);
@@ -5,11 +5,13 @@ import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -34,12 +36,12 @@ import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
public abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AndroidLintInspectionBase");
private static final Map<Issue, String> ourIssue2InspectionShortName = new HashMap<Issue, String>();
private final Issue myIssue;
protected final Issue myIssue;
private final String[] myGroupPath;
private final String myDisplayName;
@@ -58,6 +60,29 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
addIssue(issue, getShortName());
}
@NotNull
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return AndroidLintQuickFix.EMPTY_ARRAY;
}
@NotNull
protected IntentionAction[] getIntentions(@NotNull PsiElement startElement, @NotNull PsiElement endElement) {
return IntentionAction.EMPTY_ARRAY;
}
@NotNull
private LocalQuickFix[] getLocalQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) {
final AndroidLintQuickFix[] fixes = getQuickFixes(message);
final LocalQuickFix[] result = new LocalQuickFix[fixes.length];
for (int i = 0; i < fixes.length; i++) {
if (fixes[i].isApplicable(startElement, endElement, true)) {
result[i] = new MyLocalQuickFix(fixes[i]);
}
}
return result;
}
@Override
public void runInspection(AnalysisScope scope,
final InspectionManager manager,
@@ -81,7 +106,7 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
for (final Map.Entry<File, List<ProblemData>> entry : file2ProblemList.entrySet()) {
final File file = entry.getKey();
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
if (vFile == null) {
continue;
}
@@ -104,9 +129,9 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
}
@NotNull
private static ProblemDescriptor[] computeProblemDescriptors(@NotNull PsiFile psiFile,
@NotNull InspectionManager manager,
@NotNull List<ProblemData> problems) {
private ProblemDescriptor[] computeProblemDescriptors(@NotNull PsiFile psiFile,
@NotNull InspectionManager manager,
@NotNull List<ProblemData> problems) {
final List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
for (ProblemData problemData : problems) {
@@ -128,9 +153,9 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
}
}
}
if (f != null) {
result.add(manager.createProblemDescriptor(f, message, false, LocalQuickFix.EMPTY_ARRAY,
result.add(manager.createProblemDescriptor(f, message, false, getLocalQuickFixes(f, f, message),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
}
}
@@ -140,7 +165,8 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
if (startElement != null && endElement != null) {
result.add(manager.createProblemDescriptor(startElement, endElement, message + "#loc",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false));
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false,
getLocalQuickFixes(startElement, endElement, message)));
}
}
}
@@ -218,7 +244,7 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
@Override
public boolean isEnabledByDefault() {
return true;
return myIssue.isEnabledByDefault();
}
@NotNull
@@ -254,4 +280,29 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
return null;
}
}
static class MyLocalQuickFix implements LocalQuickFix {
private final AndroidLintQuickFix myLintQuickFix;
MyLocalQuickFix(@NotNull AndroidLintQuickFix lintQuickFix) {
myLintQuickFix = lintQuickFix;
}
@NotNull
@Override
public String getName() {
return myLintQuickFix.getName();
}
@NotNull
@Override
public String getFamilyName() {
return AndroidBundle.message("android.lint.quickfixes.family");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
myLintQuickFix.apply(descriptor.getStartElement(), descriptor.getEndElement(), null);
}
}
}
@@ -1,7 +1,13 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.checks.*;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintConstants;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionToolProvider;
import com.intellij.psi.PsiElement;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
@@ -9,11 +15,11 @@ import com.intellij.codeInspection.InspectionToolProvider;
public class AndroidLintInspectionToolProvider implements InspectionToolProvider {
@Override
public Class[] getInspectionClasses() {
return new Class[] {
return new Class[]{
AndroidLintContentDescriptionInspection.class,
AndroidLintAdapterViewChildrenInspection.class,
AndroidLintScrollViewCountInspection.class,
AndroidLintDeprecatedInspection.class,
//AndroidLintDeprecatedInspection.class,
AndroidLintMissingPrefixInspection.class,
AndroidLintDuplicateIdsInspection.class,
AndroidLintGridLayoutInspection.class,
@@ -169,29 +175,44 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintContentDescriptionInspection() {
super("Missing content description", AccessibilityDetector.ISSUE);
}
@NotNull
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.content.description"),
LintConstants.ATTR_CONTENT_DESCRIPTION, null)
};
}
}
public static class AndroidLintAdapterViewChildrenInspection extends AndroidLintInspectionBase {
public AndroidLintAdapterViewChildrenInspection() {
super("AdapterView element cannot have children", ChildCountDetector.ADAPTERVIEW_ISSUE);
super(AndroidBundle.message("android.lint.inspections.adapter.view.children"), ChildCountDetector.ADAPTERVIEW_ISSUE);
}
}
public static class AndroidLintScrollViewCountInspection extends AndroidLintInspectionBase {
public AndroidLintScrollViewCountInspection() {
super("ScrollView element can have only one child", ChildCountDetector.SCROLLVIEW_ISSUE);
super(AndroidBundle.message("android.lint.inspections.scroll.view.children"), ChildCountDetector.SCROLLVIEW_ISSUE);
}
}
public static class AndroidLintDeprecatedInspection extends AndroidLintInspectionBase {
// it seems we don't need it because we have our own 'deprecated api' inspection
/*public static class AndroidLintDeprecatedInspection extends AndroidLintInspectionBase {
public AndroidLintDeprecatedInspection() {
super("Deprecated XML elements", DeprecationDetector.ISSUE);
super(AndroidBundle.message("android.lint.inspections.deprecated"), DeprecationDetector.ISSUE);
}
}
}*/
public static class AndroidLintMissingPrefixInspection extends AndroidLintInspectionBase {
public AndroidLintMissingPrefixInspection() {
super("Missing android XML namespace", DetectMissingPrefix.MISSING_NAMESPACE);
super(AndroidBundle.message("android.lint.inspections.missing.prefix"), DetectMissingPrefix.MISSING_NAMESPACE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new AddMissingPrefixQuickFix()};
}
}
@@ -211,12 +232,26 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintHardcodedTextInspection() {
super("Hardcoded text", HardcodedValuesDetector.ISSUE);
}
@NotNull
@Override
protected IntentionAction[] getIntentions(@NotNull final PsiElement startElement, @NotNull PsiElement endElement) {
return new IntentionAction[]{new AndroidAddStringResourceQuickFix(startElement)};
}
}
public static class AndroidLintInefficientWeightInspection extends AndroidLintInspectionBase {
public AndroidLintInefficientWeightInspection() {
super("Inefficient layout weight", InefficientWeightDetector.INEFFICIENT_WEIGHT);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new InefficientWeightQuickFix()
};
}
}
public static class AndroidLintNestedWeightsInspection extends AndroidLintInspectionBase {
@@ -229,6 +264,15 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintDisableBaselineAlignmentInspection() {
super("Missing baselineAligned attribute", InefficientWeightDetector.BASELINE_WEIGHTS);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.set.baseline.attribute"),
LintConstants.ATTR_BASELINE_ALIGNED, "false")
};
}
}
public static class AndroidLintManifestOrderInspection extends AndroidLintInspectionBase {
@@ -253,6 +297,12 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintObsoleteLayoutParamInspection() {
super("Obsolete layout params", ObsoleteLayoutParamsDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new RemoveAttributeQuickFix()};
}
}
public static class AndroidLintProguardInspection extends AndroidLintInspectionBase {
@@ -265,18 +315,39 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintPxUsageInspection() {
super("Using 'px' dimension", PxUsageDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new ConvertToDpQuickFix()};
}
}
public static class AndroidLintScrollViewSizeInspection extends AndroidLintInspectionBase {
public AndroidLintScrollViewSizeInspection() {
super("ScrollView size validation", ScrollViewChildDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new SetScrollViewSizeQuickFix()};
}
}
public static class AndroidLintExportedServiceInspection extends AndroidLintInspectionBase {
public AndroidLintExportedServiceInspection() {
super("Exported service does not require permission", SecurityDetector.EXPORTED_SERVICE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.permission.attribute"),
LintConstants.ATTR_PERMISSION, null)
};
}
}
public static class AndroidLintGrantAllUrisInspection extends AndroidLintInspectionBase {
@@ -295,6 +366,15 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintTextFieldsInspection() {
super("Text field missing inputType or hint settings", TextFieldDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.input.type.attribute"),
LintConstants.ATTR_INPUT_TYPE, null)
};
}
}
public static class AndroidLintTooManyViewsInspection extends AndroidLintInspectionBase {
@@ -309,31 +389,31 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
}
}
public static class AndroidLintTypographyDashesInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyDashesInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyDashesInspection() {
super("Hyphen can be replaced by dash", TypographyDetector.DASHES);
}
}
public static class AndroidLintTypographyQuotesInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyQuotesInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyQuotesInspection() {
super("Straight quotes can be replaced by curvy quotes", TypographyDetector.QUOTES);
}
}
public static class AndroidLintTypographyFractionsInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyFractionsInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyFractionsInspection() {
super("Fraction string can be replaced with fraction character", TypographyDetector.FRACTIONS);
}
}
public static class AndroidLintTypographyEllipsisInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyEllipsisInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyEllipsisInspection() {
super("Ellipsis string can be replaced with ellipsis character", TypographyDetector.ELLIPSIS);
}
}
public static class AndroidLintTypographyOtherInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyOtherInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyOtherInspection() {
super("Other typographical problems", TypographyDetector.OTHER);
}
@@ -355,5 +435,23 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintUselessLeafInspection() {
super("Useless leaf layout", UselessViewDetector.USELESS_LEAF);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new RemoveUselessViewQuickFix(myIssue)};
}
}
private static class AndroidLintTypographyInspectionBase extends AndroidLintInspectionBase {
public AndroidLintTypographyInspectionBase(String displayName, Issue issue) {
super(displayName, issue);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)};
}
}
}
@@ -0,0 +1,20 @@
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
interface AndroidLintQuickFix {
AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0];
void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor);
boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode);
@NotNull
String getName();
}
@@ -0,0 +1,57 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.openapi.util.Pair;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class AndroidLintUtil {
@NonNls static final String ATTR_VALUE_VERTICAL = "vertical";
@NonNls static final String ATTR_VALUE_WRAP_CONTENT = "wrap_content";
@NonNls static final String ATTR_LAYOUT_HEIGHT = "layout_height";
@NonNls static final String ATTR_LAYOUT_WIDTH = "layout_width";
@NonNls static final String ATTR_ORIENTATION = "orientation";
private AndroidLintUtil() {
}
@Nullable
static Pair<AndroidLintInspectionBase, HighlightDisplayLevel> getHighlighLevelAndInspection(@NotNull Issue issue,
@NotNull PsiElement context) {
final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(issue);
if (inspectionShortName == null) {
return null;
}
final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName);
if (key == null) {
return null;
}
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile();
if (!profile.isToolEnabled(key, context)) {
return null;
}
final InspectionToolWrapper toolWrapper =
(InspectionToolWrapper)profile.getInspectionTool(inspectionShortName, context);
if (toolWrapper == null) {
return null;
}
final AndroidLintInspectionBase inspection = (AndroidLintInspectionBase)toolWrapper.getTool();
final HighlightDisplayLevel errorLevel = profile.getErrorLevel(key, context);
return new Pair<AndroidLintInspectionBase, HighlightDisplayLevel>(inspection,
errorLevel != null ? errorLevel : HighlightDisplayLevel.WARNING);
}
}
@@ -0,0 +1,127 @@
package org.jetbrains.android.inspections.lint;
import com.android.resources.Density;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Eugene.Kudelevsky
*/
class ConvertToDpQuickFix implements AndroidLintQuickFix {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.ConvertToDpQuickFix");
private static final Pattern PX_ATTR_VALUE_PATTERN = Pattern.compile("(\\d+)px");
private static int ourPrevDpi = Density.DEFAULT_DENSITY;
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
if (editor == null) {
return;
}
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
final List<Density> densities = new ArrayList<Density>();
for (Density density : Density.values()) {
if (density.getDpiValue() > 0) {
densities.add(density);
}
}
final String[] densityPresentableNames = new String[densities.size()];
String defaultValue = null;
String initialValue = null;
for (int i = 0; i < densities.size(); i++) {
final Density density = densities.get(i);
densityPresentableNames[i] = getLabelForDensity(density);
final int dpi = density.getDpiValue();
if (dpi == 0) {
continue;
}
if (dpi == ourPrevDpi) {
initialValue = densityPresentableNames[i];
}
else if (dpi == Density.DEFAULT_DENSITY) {
defaultValue = densityPresentableNames[i];
}
}
if (initialValue == null) {
initialValue = defaultValue;
}
if (initialValue == null) {
return;
}
final int dpi;
if (ApplicationManager.getApplication().isUnitTestMode()) {
dpi = Density.DEFAULT_DENSITY;
}
else {
final int selectedIndex = Messages
.showChooseDialog("What is the screen density the current px value works with?", "Choose density", densityPresentableNames,
initialValue, null);
if (selectedIndex < 0) {
return;
}
dpi = densities.get(selectedIndex).getDpiValue();
}
//noinspection AssignmentToStaticFieldFromInstanceMethod
ourPrevDpi = dpi;
for (XmlAttribute attribute : tag.getAttributes()) {
final String value = attribute.getValue();
if (value.endsWith("px")) {
final Matcher matcher = PX_ATTR_VALUE_PATTERN.matcher(value);
if (matcher.matches()) {
final String numberString = matcher.group(1);
try {
final int px = Integer.parseInt(numberString);
final int dp = px * 160 / dpi;
attribute.setValue(Integer.toString(dp) + "dp");
}
catch (NumberFormatException nufe) {
LOG.error(nufe);
}
}
}
}
}
@NotNull
private static String getLabelForDensity(@NotNull Density density) {
return String.format("%1$s (%2$d)", density.getShortDisplayValue(), density.getDpiValue());
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return !inBatchMode && PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.convert.to.dp");
}
}
@@ -0,0 +1,200 @@
package org.jetbrains.android.inspections.lint;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.*;
/**
* @author Eugene.Kudelevsky
*/
class FakeNode implements Node {
private final String myText;
FakeNode(@NotNull String text) {
myText = text;
}
@Override
public String getNodeName() {
throw new UnsupportedOperationException();
}
@Override
public String getNodeValue() throws DOMException {
return myText;
}
@Override
public void setNodeValue(String nodeValue) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public short getNodeType() {
throw new UnsupportedOperationException();
}
@Override
public Node getParentNode() {
throw new UnsupportedOperationException();
}
@Override
public NodeList getChildNodes() {
throw new UnsupportedOperationException();
}
@Override
public Node getFirstChild() {
throw new UnsupportedOperationException();
}
@Override
public Node getLastChild() {
throw new UnsupportedOperationException();
}
@Override
public Node getPreviousSibling() {
throw new UnsupportedOperationException();
}
@Override
public Node getNextSibling() {
throw new UnsupportedOperationException();
}
@Override
public NamedNodeMap getAttributes() {
throw new UnsupportedOperationException();
}
@Override
public Document getOwnerDocument() {
throw new UnsupportedOperationException();
}
@Override
public Node insertBefore(Node newChild, Node refChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public Node removeChild(Node oldChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public Node appendChild(Node newChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public boolean hasChildNodes() {
throw new UnsupportedOperationException();
}
@Override
public Node cloneNode(boolean deep) {
throw new UnsupportedOperationException();
}
@Override
public void normalize() {
throw new UnsupportedOperationException();
}
@Override
public boolean isSupported(String feature, String version) {
throw new UnsupportedOperationException();
}
@Override
public String getNamespaceURI() {
throw new UnsupportedOperationException();
}
@Override
public String getPrefix() {
throw new UnsupportedOperationException();
}
@Override
public void setPrefix(String prefix) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public String getLocalName() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasAttributes() {
throw new UnsupportedOperationException();
}
@Override
public String getBaseURI() {
throw new UnsupportedOperationException();
}
@Override
public short compareDocumentPosition(Node other) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public String getTextContent() throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public void setTextContent(String textContent) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public boolean isSameNode(Node other) {
throw new UnsupportedOperationException();
}
@Override
public String lookupPrefix(String namespaceURI) {
throw new UnsupportedOperationException();
}
@Override
public boolean isDefaultNamespace(String namespaceURI) {
throw new UnsupportedOperationException();
}
@Override
public String lookupNamespaceURI(String prefix) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEqualNode(Node arg) {
throw new UnsupportedOperationException();
}
@Override
public Object getFeature(String feature, String version) {
throw new UnsupportedOperationException();
}
@Override
public Object setUserData(String key, Object data, UserDataHandler handler) {
throw new UnsupportedOperationException();
}
@Override
public Object getUserData(String key) {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,54 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class InefficientWeightQuickFix implements AndroidLintQuickFix {
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
final XmlTag parentTag = tag.getParentTag();
if (parentTag == null) {
return;
}
String attrName;
if (AndroidLintUtil.ATTR_VALUE_VERTICAL
.equals(parentTag.getAttributeValue(AndroidLintUtil.ATTR_ORIENTATION, SdkConstants.NS_RESOURCES))) {
attrName = AndroidLintUtil.ATTR_LAYOUT_HEIGHT;
}
else {
attrName = AndroidLintUtil.ATTR_LAYOUT_WIDTH;
}
tag.setAttribute(attrName, SdkConstants.NS_RESOURCES, "0dp");
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return false;
}
return tag.getParentTag() != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.replace.with.zero.dp");
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.PositionXmlParser;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.IDomParser;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Context;
@@ -43,6 +44,11 @@ class IntellijLintClient extends LintClient implements Disposable {
myState = state;
}
@Override
public Configuration getConfiguration(com.android.tools.lint.detector.api.Project project) {
return new IntellijLintConfiguration(myState.getIssues());
}
@Override
public void report(Context context, Issue issue, Location location, String message, Object data) {
if (location != null) {
@@ -0,0 +1,43 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
class IntellijLintConfiguration extends Configuration {
private final Set<Issue> myIssueSet;
IntellijLintConfiguration(@NotNull Collection<Issue> issueSet) {
myIssueSet = new HashSet<Issue>(issueSet);
}
@Override
public void ignore(Context context, Issue issue, Location location, String message, Object data) {
throw new UnsupportedOperationException();
}
@Override
public void setSeverity(Issue issue, Severity severity) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEnabled(Issue issue) {
return myIssueSet.contains(issue);
}
@Override
public Severity getSeverity(Issue issue) {
return Severity.WARNING;
}
}
@@ -0,0 +1,33 @@
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class RemoveAttributeQuickFix implements AndroidLintQuickFix {
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class);
if (attribute != null) {
attribute.getParent().setAttribute(attribute.getName(), null);
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.remove.attribute");
}
}
@@ -0,0 +1,58 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.checks.UselessViewDetector;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class RemoveUselessViewQuickFix implements AndroidLintQuickFix {
private final Issue myIssue;
public RemoveUselessViewQuickFix(@NotNull Issue issue) {
myIssue = issue;
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
final XmlTag parentTag = tag.getParentTag();
if (parentTag == null) {
return;
}
if (myIssue.getId().equals(UselessViewDetector.USELESS_LEAF.getId())) {
tag.delete();
}
else {
assert false;
// todo: implement
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return false;
}
return tag.getParentTag() != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.remove.unnecessary.view");
}
}
@@ -0,0 +1,74 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class SetAttributeQuickFix implements AndroidLintQuickFix {
private final String myName;
private final String myAttributeName;
private final String myValue;
SetAttributeQuickFix(@NotNull String name, @NotNull String attributeName, @Nullable String value) {
super();
myName = name;
myAttributeName = attributeName;
myValue = value;
}
@NotNull
@Override
public String getName() {
return myName;
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false);
if (tag == null) {
return;
}
final XmlAttribute attribute = tag.setAttribute(myAttributeName, SdkConstants.NS_RESOURCES, "");
if (attribute != null) {
if (myValue != null) {
attribute.setValue(myValue);
}
if (editor != null) {
final XmlAttributeValue valueElement = attribute.getValueElement();
final TextRange valueTextRange = attribute.getValueTextRange();
if (valueElement != null && valueTextRange != null) {
final int valueElementStart = valueElement.getTextRange().getStartOffset();
editor.getCaretModel().moveToOffset(valueElementStart + valueTextRange.getStartOffset());
if (valueTextRange.getStartOffset() < valueTextRange.getEndOffset()) {
editor.getSelectionModel().setSelection(valueElementStart + valueTextRange.getStartOffset(),
valueElementStart + valueTextRange.getEndOffset());
}
}
}
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false);
if (tag == null) {
return false;
}
return tag.getAttribute(myAttributeName, SdkConstants.NS_RESOURCES) == null;
}
}
@@ -0,0 +1,50 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.android.tools.lint.detector.api.LintConstants;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class SetScrollViewSizeQuickFix implements AndroidLintQuickFix {
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
final XmlTag parentTag = tag.getParentTag();
if (parentTag == null) {
return;
}
final boolean isHorizontal = LintConstants.HORIZONTAL_SCROLL_VIEW.equals(parentTag.getName());
final String attributeName = isHorizontal
? AndroidLintUtil.ATTR_LAYOUT_WIDTH
: AndroidLintUtil.ATTR_LAYOUT_HEIGHT;
tag.setAttribute(attributeName, SdkConstants.NS_RESOURCES, AndroidLintUtil.ATTR_VALUE_WRAP_CONTENT);
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return false;
}
return tag.getParentTag() != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.set.to.wrap.content");
}
}
@@ -0,0 +1,67 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.checks.TypographyDetector;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlText;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
class TypographyQuickFix implements AndroidLintQuickFix {
private final Issue myIssue;
private final String myMessage;
public TypographyQuickFix(@NotNull Issue issue, @NotNull String message) {
myIssue = issue;
myMessage = message;
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
for (PsiElement child : tag.getChildren()) {
if (child instanceof XmlText) {
final XmlText xmlText = (XmlText)child;
final String value = xmlText.getValue();
if (value != null) {
final List<TypographyDetector.ReplaceEdit> edits = TypographyDetector.getEdits(myIssue.getId(), myMessage, new FakeNode(value));
final StringBuilder builder = new StringBuilder(value);
for (TypographyDetector.ReplaceEdit edit : edits) {
builder.replace(edit.offset, edit.offset + edit.length, edit.replaceWith);
}
final String newValue = builder.toString();
if (!newValue.equals(value)) {
xmlText.setValue(newValue);
}
}
}
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters");
}
}
@@ -16,42 +16,57 @@
package org.jetbrains.android.intentions;
import com.android.resources.ResourceType;
import com.intellij.CommonBundle;
import com.intellij.codeInsight.intention.AbstractIntentionAction;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.template.*;
import com.intellij.codeInsight.template.impl.*;
import com.intellij.codeInsight.template.macro.VariableOfTypeMacro;
import com.intellij.ide.actions.CreateElementActionBase;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidatorEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.xml.Converter;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.GenericAttributeValue;
import org.jetbrains.android.AndroidFileTemplateProvider;
import org.jetbrains.android.actions.CreateXmlResourceDialog;
import org.jetbrains.android.dom.converters.ResourceReferenceConverter;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.dom.resources.Resources;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.LinkedHashSet;
import java.util.Set;
import static com.intellij.openapi.ui.Messages.showInputDialog;
import static org.jetbrains.android.util.AndroidUtils.VIEW_CLASS_NAME;
/**
@@ -61,7 +76,9 @@ import static org.jetbrains.android.util.AndroidUtils.VIEW_CLASS_NAME;
* Time: 5:02:31 PM
* To change this template use File | Settings | File Templates.
*/
public class AndroidAddStringResourceAction extends AbstractIntentionAction {
public class AndroidAddStringResourceAction extends AbstractIntentionAction implements HighPriorityAction {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.intentions.AndroidAddStringResourceAction");
private static final String CONTEXT = AndroidUtils.ANDROID_PACKAGE + ".content.Context";
private static final String RESOURCES = AndroidUtils.ANDROID_PACKAGE + ".content.res.Resources";
@@ -94,6 +111,31 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
return (String)value;
}
}
else if (file instanceof XmlFile && element instanceof XmlAttributeValue) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class);
if (attribute != null) {
final GenericAttributeValue domAttribute = DomManager.getDomManager(element.getProject()).getDomElement(attribute);
if (domAttribute != null) {
final Converter converter = domAttribute.getConverter();
if (converter instanceof ResourceReferenceConverter) {
final ResourceValue value = (ResourceValue)domAttribute.getValue();
if (value != null && !value.isReference()) {
final Set<String> types = ((ResourceReferenceConverter)converter).getResourceTypes(domAttribute);
for (String type : types) {
if (ResourceType.STRING.getName().equals(type)) {
return ((XmlAttributeValue)element).getValue();
}
}
}
}
}
}
}
return null;
}
@@ -124,50 +166,70 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
}
static void doInvoke(Project project, Editor editor, PsiFile file, @Nullable String resName) {
AndroidFacet facet = AndroidFacet.getInstance(file);
assert facet != null;
PsiElement element = getPsiElement(file, editor);
final PsiElement element = getPsiElement(file, editor);
assert element != null;
doInvoke(project, editor, file, resName, element);
}
protected static void doInvoke(Project project, Editor editor, PsiFile file, @Nullable String resName, PsiElement element) {
String value = getStringLiteralValue(element, file);
assert value != null;
final AndroidFacet facet = AndroidFacet.getInstance(file);
assert facet != null;
value = value.replace("'", "\\'").replace("\"", "\\\"");
String aPackage = getPackage(facet);
final String aPackage = getPackage(facet);
if (aPackage == null) {
Messages.showErrorDialog(project, AndroidBundle.message("package.not.found.error"), CommonBundle.getErrorTitle());
return;
}
if (resName == null) {
resName =
showInputDialog(project, AndroidBundle.message("resource.name"), AndroidBundle.message("add.string.resource.intention.text"),
Messages.getQuestionIcon(), "", new InputValidatorEx() {
public String getErrorText(String inputString) {
if (inputString == null || inputString.length() == 0) return null;
String[] ids = inputString.split(".");
for (String id : ids) {
if (!StringUtil.isJavaIdentifier(id)) {
return AndroidBundle.message("android.identifier.expected", id);
}
}
return null;
}
final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING);
dialog.setTitle("Extract String Resource");
dialog.show();
public boolean checkInput(String inputString) {
return inputString != null && AndroidResourceUtil.isCorrectAndroidResourceName(inputString);
}
if (!dialog.isOK()) {
return;
}
public boolean canClose(String inputString) {
return checkInput(inputString);
}
});
final Module module = dialog.getModule();
if (module == null) {
return;
}
final ResourceElement createdElement =
doCreate(module, dialog.getResourceName(), ResourceType.STRING, dialog.getFileName(), dialog.getDirectoryName(), value);
if (createdElement != null) {
resName = createdElement.getName().getValue();
}
}
if (resName == null) return;
LocalResourceManager manager = facet.getLocalResourceManager();
String resType = "string";
ResourceElement resElement = manager.addValueResource(resType, resName, value);
assert resElement != null;
createJavaResourceReference(project, editor, file, element, aPackage, resName, resType);
else {
assert ApplicationManager.getApplication().isUnitTestMode();
doCreate(facet.getModule(), resName, ResourceType.STRING, "strings.xml", "values", value);
}
if (resName == null) {
return;
}
if (file instanceof PsiJavaFile) {
createJavaResourceReference(project, editor, file, element, aPackage, resName, ResourceType.STRING.getName());
}
else {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class);
if (attribute != null) {
attribute.setValue(ResourceValue.referenceTo('@', null, ResourceType.STRING.getName(), resName).toString());
}
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
UndoUtil.markPsiFileForUndo(file);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ApplicationManager.getApplication().saveAll();
@@ -188,14 +250,25 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
assert methodName != null;
final TemplateImpl template;
final boolean inStaticContext = RefactoringUtil.isInStaticContext(element, null);
if (extendsContext && !inStaticContext) {
template = new TemplateImpl("", "$resources$." + methodName + "(" + field + ")", "");
MacroCallNode node = new MacroCallNode(new MyVarOfTypeExpression("getResources()"));
node.addParameter(new ConstantNode(RESOURCES));
template.addVariable("resources", node, new ConstantNode(""), true);
if (ResourceType.STRING.getName().equals(resType)) {
template = new TemplateImpl("", methodName + '(' + field + ')', "");
}
else {
template = new TemplateImpl("", "$resources$." + methodName + "(" + field + ")", "");
MacroCallNode node = new MacroCallNode(new MyVarOfTypeExpression("getResources()"));
node.addParameter(new ConstantNode(RESOURCES));
template.addVariable("resources", node, new ConstantNode(""), true);
}
}
else {
template = new TemplateImpl("", "$context$.getResources()." + methodName + "(" + field + ")", "");
if (ResourceType.STRING.getName().equals(resType)) {
template = new TemplateImpl("", "$context$." + methodName + "(" + field + ")", "");
}
else {
template = new TemplateImpl("", "$context$.getResources()." + methodName + "(" + field + ")", "");
}
final boolean extendsView = getContainingInheritorOf(element, VIEW_CLASS_NAME) != null;
MacroCallNode node =
new MacroCallNode(extendsView && !inStaticContext ? new MyVarOfTypeExpression("getContext()") : new VariableOfTypeMacro());
@@ -242,6 +315,104 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
return true;
}
@Nullable
private static ResourceElement doCreate(@NotNull Module module,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull String fileName,
@NotNull String dirName,
@NotNull String value) {
final Project project = module.getProject();
final AndroidFacet facet = AndroidFacet.getInstance(module);
assert facet != null;
try {
return addValueResource(facet, resourceName, resourceType, fileName, dirName, value);
}
catch (Exception e) {
final String message = CreateElementActionBase.filterMessage(e.getMessage());
if (message == null || message.length() == 0) {
LOG.error(e);
}
else {
LOG.info(e);
reportError(project, message);
}
return null;
}
}
@Nullable
public static ResourceElement addValueResource(@NotNull AndroidFacet facet,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull String fileName,
@NotNull String dirName,
@NotNull String value) throws Exception {
final VirtualFile resFile = findOrCreateResourceFile(facet, fileName, dirName);
if (resFile == null) {
return null;
}
final Resources resources = AndroidUtils.loadDomElement(facet.getModule(), resFile, Resources.class);
if (resources == null) {
reportError(facet.getModule().getProject(), AndroidBundle.message("not.resource.file.error", fileName));
return null;
}
final ResourceElement element = AndroidResourceUtil.addValueResource(resourceType.getName(), resources);
element.getName().setValue(resourceName);
if (value.length() > 0) {
element.setStringValue(value);
}
return element;
}
@Nullable
private static VirtualFile findOrCreateResourceFile(@NotNull AndroidFacet facet,
@NotNull final String fileName,
@NotNull String dirName) throws Exception {
final Module module = facet.getModule();
final Project project = module.getProject();
final VirtualFile resDir = facet.getLocalResourceManager().getResourceDir();
if (resDir == null) {
reportError(project, AndroidBundle.message("check.resource.dir.error", module.getName()));
return null;
}
final VirtualFile dir = AndroidUtils.createChildDirectoryIfNotExist(project, resDir, dirName);
final String dirPath = FileUtil.toSystemDependentName(resDir.getPath() + '/' + dirName);
if (dir == null) {
reportError(project, AndroidBundle.message("android.cannot.create.dir.error", dirPath));
return null;
}
final VirtualFile file = dir.findChild(fileName);
if (file != null) {
return file;
}
AndroidFileTemplateProvider
.createFromTemplate(project, dir, AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE, fileName);
final VirtualFile result = dir.findChild(fileName);
if (result == null) {
reportError(project, AndroidBundle.message("android.cannot.create.file.error", dirPath + File.separatorChar + fileName));
}
return result;
}
private static void reportError(@NotNull Project project, @NotNull String message) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new IncorrectOperationException(message);
}
else {
Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle());
}
}
private static class MyVarOfTypeExpression extends VariableOfTypeMacro {
private final String myDefaultValue;
@@ -208,7 +208,8 @@ public class LocalResourceManager extends ResourceManager {
private VirtualFile findOrCreateResourceFile(@NotNull final String fileName) {
VirtualFile dir = getResourceDir();
if (dir == null) {
Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("check.resource.dir.error"), CommonBundle.getErrorTitle());
Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("check.resource.dir.error", myModule.getName()),
CommonBundle.getErrorTitle());
return null;
}
final VirtualFile valuesDir = findOrCreateChildDir(dir, AndroidConstants.FD_RES_VALUES);
@@ -240,7 +241,8 @@ public class LocalResourceManager extends ResourceManager {
VirtualFile resDir = getResourceDir();
Project project = myModule.getProject();
if (resDir == null) {
Messages.showErrorDialog(project, AndroidBundle.message("check.resource.dir.error"), CommonBundle.getErrorTitle());
Messages
.showErrorDialog(project, AndroidBundle.message("check.resource.dir.error", myModule.getName()), CommonBundle.getErrorTitle());
return null;
}
PsiElement[] createdElements = CreateResourceFileAction.createResourceFile(project, resDir, resType, fileOrResourceName);
@@ -269,7 +271,7 @@ public class LocalResourceManager extends ResourceManager {
CommonBundle.getErrorTitle());
return null;
}
ResourceElement element = addValueResource(type, resources);
ResourceElement element = AndroidResourceUtil.addValueResource(type, resources);
element.getName().setValue(name);
if (value != null) {
element.setStringValue(value);
@@ -277,41 +279,6 @@ public class LocalResourceManager extends ResourceManager {
return element;
}
@NotNull
private static ResourceElement addValueResource(@NotNull final String type, @NotNull final Resources resources) {
if (type.equals("string")) {
return resources.addString();
}
else if (type.equals("dimen")) {
return resources.addDimen();
}
else if (type.equals("color")) {
return resources.addColor();
}
else if (type.equals("drawable")) {
return resources.addDrawable();
}
else if (type.equals("style")) {
return resources.addStyle();
}
else if (type.equals("array")) {
// todo: choose among string-array, integer-array and array
return resources.addStringArray();
}
else if (type.equals("integer")) {
return resources.addInteger();
}
else if (type.equals("bool")) {
return resources.addBool();
}
else if (type.equals("id")) {
Item item = resources.addItem();
item.getType().setValue("id");
return item;
}
throw new IllegalArgumentException("Incorrect resource type");
}
@Nullable
private VirtualFile findOrCreateChildDir(@NotNull final VirtualFile dir, @NotNull final String name) {
try {
@@ -252,7 +252,7 @@ public abstract class DeviceConfiguratorPanel extends JPanel {
applyEditors();
}
protected void doApplyEditors() throws InvalidOptionValueException {
public void doApplyEditors() throws InvalidOptionValueException {
try {
final FolderConfiguration newConfig = new FolderConfiguration();
@@ -35,9 +35,7 @@ import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.android.AndroidIdIndex;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.dom.resources.Attr;
import org.jetbrains.android.dom.resources.DeclareStyleable;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.*;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.resourceManagers.ResourceManager;
@@ -402,4 +400,39 @@ public class AndroidResourceUtil {
List<PsiElement> targets = findResourcesByField(manager, resField);
return PsiUtilBase.toPsiElementArray(targets);
}
@NotNull
public static ResourceElement addValueResource(@NotNull final String type, @NotNull final Resources resources) {
if (type.equals("string")) {
return resources.addString();
}
else if (type.equals("dimen")) {
return resources.addDimen();
}
else if (type.equals("color")) {
return resources.addColor();
}
else if (type.equals("drawable")) {
return resources.addDrawable();
}
else if (type.equals("style")) {
return resources.addStyle();
}
else if (type.equals("array")) {
// todo: choose among string-array, integer-array and array
return resources.addStringArray();
}
else if (type.equals("integer")) {
return resources.addInteger();
}
else if (type.equals("bool")) {
return resources.addBool();
}
else if (type.equals("id")) {
Item item = resources.addItem();
item.getType().setValue("id");
return item;
}
throw new IllegalArgumentException("Incorrect resource type");
}
}
@@ -6,6 +6,6 @@ import p1.p2.R;
public class Class extends View {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -6,6 +6,6 @@ import p1.p2.R;
public class Class extends View {
public void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -6,6 +6,6 @@ import p1.p2.R;
public class Class extends View {
public void f() {
String s = getContext().getResources().getString(R.string.hello);
String s = getContext().getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.Context;
import android.content.res.Resources;
public class Class extends Context {
public void f(Resources resources) {
public void f(Context resources) {
String s = "h<caret>ello";
}
}
@@ -5,7 +5,7 @@ import android.content.res.Resources;
import p1.p2.R;
public class Class extends Context {
public void f(Resources resources) {
String s = resources.getString(R.string.hello);
public void f(Context resources) {
String s = getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.Context;
import android.content.res.Resources;
public class Class extends Context {
public void f(Resources resourc<caret>es) {
public void f(Context resourc<caret>es) {
String s = "hello";
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class {
public void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import p1.p2.R;
public class Class {
public void f() {
String s = .getResources().getString(R.string.hello);
String s = .getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import android.content.Context;
public class Class {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class extends Context {
public void f() {
String s = getResources().getString(R.string.hello);
String s = getString(R.string.hello);
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class extends Context {
public void f(Context context) {
String s = r.getString(R.string.hello);
String s = getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import android.content.Context;
public class Class extends Context {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class extends Context {
public static void f() {
String s = c.getResources().getString(R.string.hello);
String s = c.getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public static void f(Resources resources) {
public static void f(Context resources) {
String s = "h<caret>ello";
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public static void f(Resources resources) {
String s = .getResources().getString(R.string.hello);
public static void f(Context resources) {
String s = resources.getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public void f(Resources resources) {
public void f(Context context) {
String s = "h<caret>ello";
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public void f(Resources resources) {
String s = getResources().getString(R.string.hello);
public void f(Context context) {
String s = getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import android.content.Context;
public class ClassEscape {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="he<caret>llo"
/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wra<caret>p_content"
android:text="hello"
/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/a1<caret>0" >
</application>
</manifest>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="hell<caret>o" >
</application>
</manifest>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/hello" >
</application>
</manifest>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application>
<activity android:name="MyAc<caret>tivity">
</activity>
</application>
</manifest>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<warning><ListView android:layout_width="wrap_content" android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</ListView></warning>
</LinearLayout>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<warning><LinearLayout<caret> xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<AbsoluteLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="10"/>
</LinearLayout></warning>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:baselineAligned="<selection>false</selection>">
<AbsoluteLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="10"/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<warning><Ima<caret>geView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/></warning>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
<warning>android:con<caret>tentDescription=""</warning>/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:contentDescription="<caret>"/>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="fill_parent" <warning>android:width="100<caret>px"</warning>/>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:width="100dp"/>
</LinearLayout>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/id123"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
<warning>android:id="@+id/id123"</warning>
/>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<warning><EditText android:layout_width="wrap_content" android:l<caret>ayout_height="wrap_content"/></warning>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="<caret>"/>
</LinearLayout>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="Hello, world!" >
<warning><service android:name=".MyExportedService" <caret>android:exported="true"/></warning>
</application>
</manifest>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="Hello, world!" >
<service android:name=".MyExportedService" android:exported="true" android:permission="<caret>"/>
</application>
</manifest>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
<warning>android:t<caret>ext="Hello World, MyActivity"</warning>
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
<warning>android:text="He<caret>llo World, MyActivity"</warning>
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
<warning>android:layout_wid<caret>th="fill_parent"</warning>
android:layout_height="wrap_content"
android:layout_weight="10"
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="10"
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
<warning>id="@+id/id1<caret>23"</warning>
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:an="http://schemas.android.com/apk/res/android"
an:layout_width="fill_parent"
an:layout_height="fill_parent"
>
<TextView
an:layout_width="fill_parent"
an:layout_height="wrap_content"
<warning>id="@+id/id1<caret>23"</warning>
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:an="http://schemas.android.com/apk/res/android"
an:layout_width="fill_parent"
an:layout_height="fill_parent"
>
<TextView
an:layout_width="fill_parent"
an:layout_height="wrap_content"
an:id="@+id/id123"
/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/id123"
/>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="fill_parent" <warning>android:l<caret>ayout_row="10"</warning>/>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="fill_parent"/>
</LinearLayout>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<warning><ScrollView android:layout_height="wrap_content" android:layout_width="fill_parent">
<TextView android:layout_height="fill_parent" android:layout_width="wrap_content"/>
<TextView android:layout_height="fill_parent" android:layout_width="wrap_content"/>
</ScrollView></warning>
</LinearLayout>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" <warning>android:layout_height="f<caret>ill_parent"</warning>/>
</ScrollView>
</LinearLayout>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content"/>
</ScrollView>
</LinearLayout>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<warning><string name="s">10-2<caret>0</string></warning>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="s">10–20</string>
</resources>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">untitled303</string>
<warning><string name="app_name1">aba 'a<caret>ba'</string></warning>
</resources>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">untitled303</string>
<string name="app_name1">aba ‘aba’</string>
</resources>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<warning><ImageSwitcher android:layout_widt<caret>h="wrap_content" android:layout_height="wrap_content"/></warning>
</LinearLayout>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</LinearLayout>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<warning><ImageSwitche<caret>r android:layout_width="wrap_content" android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</ImageSwitcher></warning>
</LinearLayout>
@@ -0,0 +1,217 @@
package org.jetbrains.android;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.android.inspections.lint.*;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidLintTest extends AndroidTestCase {
@NonNls private static final String BASE_PATH = "/lint/";
public AndroidLintTest() {
super(false);
}
public void testHardcodedQuickfix() throws Exception {
doTestHardcodedQuickfix();
}
public void testHardcodedQuickfix1() throws Exception {
doTestHardcodedQuickfix();
}
private void doTestHardcodedQuickfix() throws IOException {
final IntentionAction action = doTestHighlightingAndGetQuickfix(
new AndroidLintInspectionToolProvider.AndroidLintHardcodedTextInspection(),
AndroidBundle.message("add.string.resource.intention.text"), false ? "AndroidManifest.xml" : "/res/layout/layout.xml", "xml");
assertNotNull(action);
assertTrue(action.isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile()));
new WriteCommandAction(myFixture.getProject(), "") {
@Override
protected void run(Result result) throws Throwable {
((AndroidAddStringResourceQuickFix)action)
.invokeIntention(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "hello");
}
}.execute();
myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml");
}
public void testContentDescription() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintContentDescriptionInspection(),
AndroidBundle.message("android.lint.inspections.add.content.description"),
"/res/layout/layout.xml", "xml");
}
public void testContentDescription1() throws Exception {
doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintContentDescriptionInspection(),
"/res/layout/layout.xml", "xml");
}
public void testAdapterViewChildren() throws Exception {
doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintAdapterViewChildrenInspection(),
"/res/layout/layout.xml", "xml");
}
public void testScrollViewChildren() throws Exception {
doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintScrollViewCountInspection(),
"/res/layout/layout.xml", "xml");
}
public void testMissingPrefix() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintMissingPrefixInspection(),
AndroidBundle.message("android.lint.inspections.add.android.prefix"),
"/res/layout/layout.xml", "xml");
}
public void testMissingPrefix1() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintMissingPrefixInspection(),
AndroidBundle.message("android.lint.inspections.add.android.prefix"),
"/res/layout/layout.xml", "xml");
}
public void testDuplicatedIds() throws Exception {
doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintDuplicateIdsInspection(),
"/res/layout/layout.xml", "xml");
}
public void testInefficientWeight() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintInefficientWeightInspection(),
AndroidBundle.message("android.lint.inspections.replace.with.zero.dp"),
"/res/layout/layout.xml", "xml");
}
public void testBaselineWeights() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintDisableBaselineAlignmentInspection(),
AndroidBundle.message("android.lint.inspections.set.baseline.attribute"),
"/res/layout/layout.xml", "xml");
}
public void testObsoleteLayoutParams() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintObsoleteLayoutParamInspection(),
AndroidBundle.message("android.lint.inspections.remove.attribute"),
"/res/layout/layout.xml", "xml");
}
public void testConvertToDp() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintPxUsageInspection(),
AndroidBundle.message("android.lint.inspections.convert.to.dp"),
"/res/layout/layout.xml", "xml");
}
public void testScrollViewSize() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintScrollViewSizeInspection(),
AndroidBundle.message("android.lint.inspections.set.to.wrap.content"),
"/res/layout/layout.xml", "xml");
}
public void testExportedService() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintExportedServiceInspection(),
AndroidBundle.message("android.lint.inspections.add.permission.attribute"),
"AndroidManifest.xml", "xml");
}
public void testEditText() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintTextFieldsInspection(),
AndroidBundle.message("android.lint.inspections.add.input.type.attribute"),
"/res/layout/layout.xml", "xml");
}
public void testUselessLeaf() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintUselessLeafInspection(),
AndroidBundle.message("android.lint.inspections.remove.unnecessary.view"),
"/res/layout/layout.xml", "xml");
}
public void testUselessParent() throws Exception {
doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintUselessParentInspection(),
"/res/layout/layout.xml", "xml");
}
public void testTypographyDashes() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintTypographyDashesInspection(),
AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters"),
"/res/values/typography.xml", "xml");
}
public void testTypographyQuotes() throws Exception {
doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintTypographyQuotesInspection(),
AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters"),
"/res/values/typography.xml", "xml");
}
private void doTestNoFix(@NotNull AndroidLintInspectionBase inspection, @NotNull String copyTo, @NotNull String extension)
throws IOException {
doTestHighlighting(inspection, copyTo, extension);
IntentionAction action = null;
for (IntentionAction a : myFixture.getAvailableIntentions()) {
if (a instanceof AndroidLintExternalAnnotator.MyFixingIntention) {
action = a;
}
}
assertNull(action);
}
private void doTestWithFix(@NotNull AndroidLintInspectionBase inspection,
@NotNull String message,
@NotNull String copyTo,
@NotNull String extension)
throws IOException {
final IntentionAction action = doTestHighlightingAndGetQuickfix(inspection, message, copyTo, extension);
assertNotNull(action);
assertTrue(action.isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile()));
new WriteCommandAction(myFixture.getProject(), "") {
@Override
protected void run(Result result) throws Throwable {
action.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile());
}
}.execute();
myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after." + extension);
}
@Nullable
private IntentionAction doTestHighlightingAndGetQuickfix(@NotNull AndroidLintInspectionBase inspection,
@NotNull String message,
@NotNull String copyTo,
@NotNull String extension) throws IOException {
doTestHighlighting(inspection, copyTo, extension);
IntentionAction action = null;
for (IntentionAction a : myFixture.getAvailableIntentions()) {
if (message.equals(a.getText())) {
action = a;
}
}
return action;
}
private void doTestHighlighting(@NotNull AndroidLintInspectionBase inspection, @NotNull String copyTo, @NotNull String extension)
throws IOException {
if (!"AndroidManifest.xml".equals(copyTo)) {
createManifest();
}
myFixture.enableInspections(new GlobalInspectionToolWrapper(inspection));
final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + "." + extension, copyTo);
myFixture.configureFromExistingVirtualFile(file);
myFixture.doHighlighting();
myFixture.checkHighlighting(true, false, false);
}
}
@@ -11,12 +11,18 @@ import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.AndroidTestCase;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidAddStringResourceActionTest extends AndroidTestCase {
private static final String BASE_PATH = "addStringRes/";
public AndroidAddStringResourceActionTest() {
super(false);
}
@Override
public void setUp() throws Exception {
super.setUp();
@@ -29,37 +35,31 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase {
super.tearDown();
}
public void test1() {
public void test1() throws IOException {
doTest();
}
public void test2() {
public void test2() throws IOException {
doTest();
}
public void test3() {
public void test3() throws IOException {
doTest();
}
public void test4() {
public void test4() throws IOException {
doTest();
}
public void test5() {
doTest(new Runnable() {
@Override
public void run() {
myFixture.type('r');
TemplateManagerImpl.getTemplateState(myFixture.getEditor()).nextTab();
}
});
}
public void test6() {
public void test5() throws IOException {
doTest();
}
public void test7() {
public void test6() throws IOException {
doTest();
}
public void test7() throws IOException {
doTest(new Runnable() {
@Override
public void run() {
@@ -69,24 +69,19 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase {
});
}
public void test8() {
doTest(new Runnable() {
@Override
public void run() {
myFixture.performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM);
}
});
}
public void test9() {
public void test8() throws IOException {
doTest();
}
public void test10() {
public void test9() throws IOException {
doTest();
}
public void test11() {
public void test10() throws IOException {
doTest();
}
public void test11() throws IOException {
doTest(new Runnable() {
@Override
public void run() {
@@ -96,18 +91,12 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase {
});
}
public void test12() {
public void test12() throws IOException {
doTest();
}
public void test13() {
doTest(new Runnable() {
@Override
public void run() {
myFixture.type("r");
myFixture.performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM);
}
});
public void test13() throws IOException {
doTest();
}
public void test14() {
@@ -117,36 +106,68 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase {
assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), javaPsiFile));
}
public void testEscape() {
public void testEscape() throws IOException {
doTest(getTestName(false), "strings.xml", null, true, "strings_escape_after.xml");
}
public void testNewFile() {
public void testNewFile() throws IOException {
doTest("1", null, null, true);
}
public void testInvalidStringsXml() {
public void testInvalidStringsXml() throws IOException {
try {
doTest("1", "strings_invalid.xml", null, true);
fail();
}
catch (IncorrectOperationException e) {
// in normal mode error dialog will be shown
assertEquals("invalid strings.xml", e.getMessage());
assertEquals("File strings.xml is not resource file", e.getMessage());
}
myFixture.checkResultByFile(BASE_PATH + "Class1.java");
myFixture.checkResultByFile("res/values/strings.xml", BASE_PATH + "strings_invalid.xml", false);
}
private void doTest() {
public void testFromLayout() throws IOException {
createManifest();
final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "res/layout/layout.xml");
doExtractAndCheckStringsXml("strings.xml", null, true, "strings_after.xml", file);
myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml");
}
public void testFromLayout1() throws IOException {
createManifest();
final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "res/layout/layout.xml");
myFixture.configureFromExistingVirtualFile(file);
assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile()));
}
public void testFromManifest() {
final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "AndroidManifest.xml");
myFixture.configureFromExistingVirtualFile(file);
assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile()));
}
public void testFromManifest1() {
final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "AndroidManifest.xml");
doExtractAndCheckStringsXml("strings.xml", null, true, "strings_after.xml", file);
myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml");
}
public void testFromManifest2() {
final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "AndroidManifest.xml");
myFixture.configureFromExistingVirtualFile(file);
assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile()));
}
private void doTest() throws IOException {
doTest(getTestName(false), "strings.xml", null, true);
}
private void doTest(Runnable invokeAfterTemplate) {
private void doTest(Runnable invokeAfterTemplate) throws IOException {
doTest(getTestName(false), "strings.xml", invokeAfterTemplate, false);
}
private void doTest(String testName, String stringsXml, final Runnable invokeAfterTemplate, final boolean closePopup) {
private void doTest(String testName, String stringsXml, final Runnable invokeAfterTemplate, final boolean closePopup) throws IOException {
doTest(testName, stringsXml, invokeAfterTemplate, closePopup, "strings_after.xml");
}
@@ -154,12 +175,22 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase {
String stringsXml,
@Nullable final Runnable invokeAfterTemplate,
final boolean closePopup,
String stringsAfter) {
String stringsAfter) throws IOException {
createManifest();
VirtualFile javaFile = myFixture.copyFileToProject(BASE_PATH + "Class" + testName + ".java", "src/p1/p2/Class.java");
doExtractAndCheckStringsXml(stringsXml, invokeAfterTemplate, closePopup, stringsAfter, javaFile);
myFixture.checkResultByFile(BASE_PATH + "Class" + testName + "_after.java");
}
private void doExtractAndCheckStringsXml(String stringsXml,
final Runnable invokeAfterTemplate,
final boolean closePopup,
String stringsAfter,
VirtualFile javaFile) {
if (stringsXml != null) {
myFixture.copyFileToProject(BASE_PATH + stringsXml, "res/values/strings.xml");
}
myFixture.copyFileToProject(BASE_PATH + "R.java", "src/p1/p2/R.java");
VirtualFile javaFile = myFixture.copyFileToProject(BASE_PATH + "Class" + testName + ".java", "src/p1/p2/Class.java");
myFixture.configureFromExistingVirtualFile(javaFile);
final PsiFile javaPsiFile = myFixture.getFile();
assertTrue(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), javaPsiFile));
@@ -180,7 +211,6 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase {
}
}
}, "", "");
myFixture.checkResultByFile(BASE_PATH + "Class" + testName + "_after.java");
myFixture.checkResultByFile("res/values/strings.xml", BASE_PATH + stringsAfter, false);
}
}