Add gutter icons for Android resource references

#KT-16843 Fixed
This commit is contained in:
Vyacheslav Gerasimov
2017-04-26 18:08:11 +03:00
parent 386ec53dd5
commit 49c8dfbb06
18 changed files with 650 additions and 44 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.AbstractDataFlowValueRenderingTest
import org.jetbrains.kotlin.addImport.AbstractAddImportTest
import org.jetbrains.kotlin.allopen.AbstractBytecodeListingTestForAllOpen
import org.jetbrains.kotlin.android.*
import org.jetbrains.kotlin.android.annotator.AbstractAndroidGutterIconTest
import org.jetbrains.kotlin.android.configure.AbstractConfigureProjectTest
import org.jetbrains.kotlin.android.folding.AbstractAndroidResourceFoldingTest
import org.jetbrains.kotlin.android.intention.AbstractAndroidIntentionTest
@@ -1260,6 +1261,10 @@ fun main(args: Array<String>) {
testClass<AbstractAndroidResourceFoldingTest> {
model("android/folding")
}
testClass<AbstractAndroidGutterIconTest> {
model("android/gutterIcon")
}
}
testGroup("plugins/plugins-tests/tests", "plugins/android-extensions/android-extensions-jps/testData") {
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.android
import com.android.SdkConstants
import com.android.resources.ResourceType.*
import com.android.tools.idea.AndroidPsiUtils.ResourceReferenceType.FRAMEWORK
import com.android.tools.idea.rendering.GutterIconRenderer
import com.android.tools.idea.res.ResourceHelper
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiElement
import org.jetbrains.android.AndroidColorAnnotator
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.android.ResourceReferenceAnnotatorUtil.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class AndroidResourceReferenceAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
val reference = element as? KtReferenceExpression ?: return
val androidFacet = AndroidFacet.getInstance(element) ?: return
val referenceTarget = reference.getResourceReferenceTargetDescriptor() ?: return
val resourceType = referenceTarget.getAndroidResourceType() ?: return
if (resourceType != COLOR && resourceType != DRAWABLE && resourceType != MIPMAP) {
return
}
val referenceType = referenceTarget.getResourceReferenceType()
val configuration = pickConfiguration(androidFacet, androidFacet.module, element.containingFile) ?: return
val resourceValue = findResourceValue(resourceType,
reference.text,
referenceType == FRAMEWORK,
androidFacet.module,
configuration) ?: return
val resourceResolver = configuration.resourceResolver ?: return
if (resourceType == COLOR) {
val color = ResourceHelper.resolveColor(resourceResolver, resourceValue, element.project)
if (color != null) {
val annotation = holder.createInfoAnnotation(element, null)
annotation.gutterIconRenderer = ColorRenderer(element, color)
}
}
else {
var file = ResourceHelper.resolveDrawable(resourceResolver, resourceValue, element.project)
if (file != null && file.path.endsWith(SdkConstants.DOT_XML)) {
file = pickBitmapFromXml(file, resourceResolver, element.project)
}
val iconFile = AndroidColorAnnotator.pickBestBitmap(file)
if (iconFile != null) {
val annotation = holder.createInfoAnnotation(element, null)
annotation.gutterIconRenderer = GutterIconRenderer(element, iconFile)
}
}
}
private fun KtReferenceExpression.getResourceReferenceTargetDescriptor(): JavaPropertyDescriptor? =
analyze(BodyResolveMode.PARTIAL)[BindingContext.REFERENCE_TARGET, this] as? JavaPropertyDescriptor
}
@@ -17,6 +17,11 @@
package org.jetbrains.kotlin.android
import com.android.SdkConstants
import com.android.SdkConstants.ANDROID_PKG
import com.android.SdkConstants.R_CLASS
import com.android.resources.ResourceType
import com.android.tools.idea.AndroidPsiUtils
import com.android.tools.idea.AndroidPsiUtils.ResourceReferenceType.*
import com.intellij.psi.PsiElement
import org.jetbrains.android.augment.AndroidPsiElementFinder
import org.jetbrains.android.facet.AndroidFacet
@@ -25,13 +30,17 @@ import org.jetbrains.android.util.AndroidResourceUtil.isManifestJavaFile
import org.jetbrains.android.util.AndroidResourceUtil.isRJavaFile
import org.jetbrains.android.util.AndroidUtils
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
@@ -40,49 +49,72 @@ internal fun PsiElement.getAndroidFacetForFile(): AndroidFacet? {
return AndroidFacet.getInstance(file)
}
internal object KotlinAndroidResourceUtil {
fun getReferredResourceOrManifestField(facet: AndroidFacet, expression: KtSimpleNameExpression, localOnly: Boolean)
= getReferredResourceOrManifestField(facet, expression, null, localOnly)
fun getReferredResourceOrManifestField(facet: AndroidFacet, expression: KtSimpleNameExpression,
className: String?, localOnly: Boolean): AndroidResourceUtil.MyReferredResourceFieldInfo? {
val resFieldName = expression.getReferencedName()
val resClassReference = expression.getPreviousInQualifiedChain() as? KtSimpleNameExpression ?: return null
val resClassName = resClassReference.getReferencedName()
if (resClassName.isEmpty() || className != null && className != resClassName) {
return null
}
val rClassReference = resClassReference.getPreviousInQualifiedChain() as? KtSimpleNameExpression ?: return null
val rClassDescriptor = rClassReference.analyze(BodyResolveMode.PARTIAL)
.get(BindingContext.REFERENCE_TARGET, rClassReference) as? ClassDescriptor ?: return null
val rClassShortName = rClassDescriptor.name.asString()
val fromManifest = AndroidUtils.MANIFEST_CLASS_NAME == rClassShortName
if (!fromManifest && AndroidUtils.R_CLASS_NAME != rClassShortName) {
return null
}
if (!localOnly) {
val qName = rClassDescriptor.fqNameSafe.asString()
if (SdkConstants.CLASS_R == qName || AndroidPsiElementFinder.INTERNAL_R_CLASS_QNAME == qName) {
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, true, false)
}
}
val containingFile = (rClassDescriptor.source.containingFile as? PsiSourceFile)?.psiFile ?: return null
if (if (fromManifest) !isManifestJavaFile(facet, containingFile) else !isRJavaFile(facet, containingFile)) {
return null
}
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, false, false)
internal fun JavaPropertyDescriptor.getAndroidResourceType(): ResourceType? {
if (getResourceReferenceType() == NONE) {
return null
}
private fun KtExpression.getPreviousInQualifiedChain(): KtExpression? {
val receiverExpression = getQualifiedExpressionForSelector()?.receiverExpression
return (receiverExpression as? KtQualifiedExpression)?.selectorExpression ?: receiverExpression
val containingClass = containingDeclaration as? JavaClassDescriptor ?: return null
return ResourceType.getEnum(containingClass.name.asString())
}
internal fun JavaPropertyDescriptor.getResourceReferenceType(): AndroidPsiUtils.ResourceReferenceType {
val containingClass = containingDeclaration as? JavaClassDescriptor ?: return NONE
val rClass = containingClass.containingDeclaration as? JavaClassDescriptor ?: return NONE
if (R_CLASS == rClass.name.asString()) {
if ((rClass.containingDeclaration as? PackageFragmentDescriptor)?.fqName?.asString() == ANDROID_PKG) {
return FRAMEWORK
}
else {
return APP
}
}
return NONE
}
internal fun getReferredResourceOrManifestField(facet: AndroidFacet, expression: KtSimpleNameExpression, localOnly: Boolean)
= getReferredResourceOrManifestField(facet, expression, null, localOnly)
internal fun getReferredResourceOrManifestField(facet: AndroidFacet, expression: KtSimpleNameExpression,
className: String?, localOnly: Boolean): AndroidResourceUtil.MyReferredResourceFieldInfo? {
val resFieldName = expression.getReferencedName()
val resClassReference = expression.getPreviousInQualifiedChain() as? KtSimpleNameExpression ?: return null
val resClassName = resClassReference.getReferencedName()
if (resClassName.isEmpty() || className != null && className != resClassName) {
return null
}
val rClassReference = resClassReference.getPreviousInQualifiedChain() as? KtSimpleNameExpression ?: return null
val rClassDescriptor = rClassReference.analyze(BodyResolveMode.PARTIAL)
.get(BindingContext.REFERENCE_TARGET, rClassReference) as? ClassDescriptor ?: return null
val rClassShortName = rClassDescriptor.name.asString()
val fromManifest = AndroidUtils.MANIFEST_CLASS_NAME == rClassShortName
if (!fromManifest && AndroidUtils.R_CLASS_NAME != rClassShortName) {
return null
}
if (!localOnly) {
val qName = rClassDescriptor.fqNameSafe.asString()
if (SdkConstants.CLASS_R == qName || AndroidPsiElementFinder.INTERNAL_R_CLASS_QNAME == qName) {
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, true, false)
}
}
val containingFile = (rClassDescriptor.source.containingFile as? PsiSourceFile)?.psiFile ?: return null
if (if (fromManifest) !isManifestJavaFile(facet, containingFile) else !isRJavaFile(facet, containingFile)) {
return null
}
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, false, false)
}
private fun KtExpression.getPreviousInQualifiedChain(): KtExpression? {
val receiverExpression = getQualifiedExpressionForSelector()?.receiverExpression
return (receiverExpression as? KtQualifiedExpression)?.selectorExpression ?: receiverExpression
}
@@ -0,0 +1,270 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.android;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.resources.ResourceItem;
import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.ResourceResolver;
import com.android.resources.ResourceType;
import com.android.tools.idea.configurations.Configuration;
import com.android.tools.idea.res.AppResourceRepository;
import com.android.tools.idea.res.LocalResourceRepository;
import com.android.tools.idea.res.ResourceHelper;
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
import com.android.utils.XmlUtils;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
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.psi.xml.XmlTag;
import com.intellij.util.ui.ColorIcon;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBUI;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import static com.android.SdkConstants.*;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_DRAWABLE;
import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE;
import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile;
/**
* Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator
*/
public class ResourceReferenceAnnotatorUtil {
@Nullable
public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) {
try {
String xml = Files.toString(file, Charsets.UTF_8);
Document document = XmlUtils.parseDocumentSilently(xml, true);
if (document != null && document.getDocumentElement() != null) {
Element root = document.getDocumentElement();
String tag = root.getTagName();
Element target = null;
String attribute = null;
if ("vector".equals(tag)) {
// Vectors are handled in the icon cache
return file;
}
else if ("bitmap".equals(tag) || "nine-patch".equals(tag)) {
target = root;
attribute = ATTR_SRC;
}
else if ("selector".equals(tag) ||
"level-list".equals(tag) ||
"layer-list".equals(tag) ||
"transition".equals(tag)) {
NodeList children = root.getChildNodes();
for (int i = children.getLength() - 1; i >= 0; i--) {
Node item = children.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE && TAG_ITEM.equals(item.getNodeName())) {
target = (Element)item;
if (target.hasAttributeNS(ANDROID_URI, ATTR_DRAWABLE)) {
attribute = ATTR_DRAWABLE;
break;
}
}
}
}
else if ("clip".equals(tag) || "inset".equals(tag) || "scale".equals(tag)) {
target = root;
attribute = ATTR_DRAWABLE;
} else {
// <shape> etc - no bitmap to be found
return null;
}
if (attribute != null && target.hasAttributeNS(ANDROID_URI, attribute)) {
String src = target.getAttributeNS(ANDROID_URI, attribute);
ResourceValue value = resourceResolver.findResValue(src, false);
if (value != null) {
return ResourceHelper.resolveDrawable(resourceResolver, value, project);
}
}
}
} catch (Throwable ignore) {
// Not logging for now; afraid to risk unexpected crashes in upcoming preview. TODO: Re-enable.
//Logger.getInstance(AndroidColorAnnotator.class).warn(String.format("Could not read/render icon image %1$s", file), e);
}
return null;
}
/** Looks up the resource item of the given type and name for the given configuration, if any */
@Nullable
public static ResourceValue findResourceValue(ResourceType type,
String name,
boolean isFramework,
Module module,
Configuration configuration) {
if (isFramework) {
ResourceRepository frameworkResources = configuration.getFrameworkResources();
if (frameworkResources == null) {
return null;
}
if (!frameworkResources.hasResourceItem(type, name)) {
return null;
}
ResourceItem item = frameworkResources.getResourceItem(type, name);
return item.getResourceValue(type, configuration.getFullConfig(), false);
} else {
LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true);
if (appResources == null) {
return null;
}
if (!appResources.hasResourceItem(type, name)) {
return null;
}
return appResources.getConfiguredValue(type, name, configuration.getFullConfig());
}
}
/** Picks a suitable configuration to use for resource resolution */
@Nullable
public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) {
return null;
}
VirtualFile parent = virtualFile.getParent();
if (parent == null) {
return null;
}
VirtualFile layout;
String parentName = parent.getName();
if (!parentName.startsWith(FD_RES_LAYOUT)) {
layout = pickLayoutFile(module, facet);
if (layout == null) {
return null;
}
} else {
layout = virtualFile;
}
return facet.getConfigurationManager().getConfiguration(layout);
}
public static class ColorRenderer extends GutterIconRenderer {
private final PsiElement myElement;
private final Color myColor;
ColorRenderer(@NotNull PsiElement element, @Nullable Color color) {
myElement = element;
myColor = color;
}
@NotNull
@Override
public Icon getIcon() {
Color color = getCurrentColor();
return JBUI.scale(color == null ? EmptyIcon.create(ICON_SIZE) : new ColorIcon(ICON_SIZE, color));
}
@Nullable
private Color getCurrentColor() {
if (myColor != null) {
return myColor;
} else if (myElement instanceof XmlTag) {
return ResourceHelper.parseColor(((XmlTag)myElement).getValue().getText());
} else if (myElement instanceof XmlAttributeValue) {
return ResourceHelper.parseColor(((XmlAttributeValue)myElement).getValue());
} else {
return null;
}
}
@Override
public AnAction getClickAction() {
if (myColor != null) { // Cannot set colors that were derived
return null;
}
return new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
if (editor != null) {
// Need ARGB support in platform color chooser; see
// https://youtrack.jetbrains.com/issue/IDEA-123498
//final Color color =
// ColorChooser.chooseColor(editor.getComponent(), AndroidBundle.message("android.choose.color"), getCurrentColor());
final Color color = ColorPicker.showDialog(editor.getComponent(), "Choose Color", getCurrentColor(), true, null, false);
if (color != null) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
if (myElement instanceof XmlTag) {
((XmlTag)myElement).getValue().setText(ResourceHelper.colorToString(color));
} else if (myElement instanceof XmlAttributeValue) {
XmlAttribute attribute = PsiTreeUtil.getParentOfType(myElement, XmlAttribute.class);
if (attribute != null) {
attribute.setValue(ResourceHelper.colorToString(color));
}
}
}
});
}
}
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ColorRenderer that = (ColorRenderer)o;
// TODO: Compare with modification count in app resources (if not framework)
if (myColor != null ? !myColor.equals(that.myColor) : that.myColor != null) return false;
if (!myElement.equals(that.myElement)) return false;
return true;
}
@Override
public int hashCode() {
int result = myElement.hashCode();
result = 31 * result + (myColor != null ? myColor.hashCode() : 0);
return result;
}
}
}
@@ -25,7 +25,7 @@ import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.android.inspections.CreateFileResourceQuickFix
import org.jetbrains.android.inspections.CreateValueResourceQuickFix
import org.jetbrains.android.util.AndroidResourceUtil
import org.jetbrains.kotlin.android.KotlinAndroidResourceUtil
import org.jetbrains.kotlin.android.getReferredResourceOrManifestField
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
@@ -39,7 +39,7 @@ class KotlinAndroidResourceQuickFixProvider : UnresolvedReferenceQuickFixProvide
manifest.`package`.value ?: return
val contextFile = expression.containingFile ?: return
val info = KotlinAndroidResourceUtil.getReferredResourceOrManifestField(facet, expression, true)
val info = getReferredResourceOrManifestField(facet, expression, true)
if (info == null || info.isFromManifest) {
return
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.android.annotator
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.PathUtil
import com.intellij.util.ui.ColorIcon
import junit.framework.TestCase
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.awt.Color
import java.io.File
import javax.swing.ImageIcon
abstract class AbstractAndroidGutterIconTest : KotlinAndroidTestCase() {
fun doTest(path: String) {
val testFile = File(path)
val testFileText = FileUtil.loadFile(testFile)
val withRuntime = InTextDirectivesUtils.isDirectiveDefined(testFileText, "// WITH_RUNTIME")
val drawable = InTextDirectivesUtils.isDirectiveDefined(testFileText, "// DRAWABLE")
val color = InTextDirectivesUtils.findListWithPrefixes(testFileText, "// COLOR: ").takeIf { it.isNotEmpty() }?.let {
val components = it.map { it.toInt(16) }
Color(components[0], components[1], components[2])
}
try {
if (withRuntime) {
ConfigLibraryUtil.configureKotlinRuntime(myFixture.module)
}
copyResourceDirectoryForTest(path)
myFixture.copyFileToProject(testFile.parent + "/R.java", "gen/${COM_MYAPP_PACKAGE_PATH}R.java")
val sourceFile = myFixture.copyFileToProject(path, "src/${PathUtil.getFileName(path)}")
myFixture.configureFromExistingVirtualFile(sourceFile)
val gutter = myFixture.findAllGutters().find {
when {
drawable -> it.icon is ImageIcon
color != null -> (it.icon as? ColorIcon)?.iconColor == color
else -> error("COLOR or DRAWABLE must be defined!")
}
}
TestCase.assertNotNull(gutter)
}
finally {
if (withRuntime) {
ConfigLibraryUtil.unConfigureKotlinRuntime(myFixture.module)
}
}
}
companion object {
private val COM_MYAPP_PACKAGE_PATH: String = "com/myapp/"
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.android.annotator;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/android/gutterIcon")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class AndroidGutterIconTestGenerated extends AbstractAndroidGutterIconTest {
public void testAllFilesPresentInGutterIcon() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/gutterIcon"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("color.kt")
public void testColor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/gutterIcon/color.kt");
doTest(fileName);
}
@TestMetadata("drawable.kt")
public void testDrawable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/gutterIcon/drawable.kt");
doTest(fileName);
}
@TestMetadata("mipmap.kt")
public void testMipmap() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/gutterIcon/mipmap.kt");
doTest(fileName);
}
@TestMetadata("systemColor.kt")
public void testSystemColor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/gutterIcon/systemColor.kt");
doTest(fileName);
}
@TestMetadata("systemDrawable.kt")
public void testSystemDrawable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/gutterIcon/systemDrawable.kt");
doTest(fileName);
}
@TestMetadata("idea/testData/android/gutterIcon/res")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Res extends AbstractAndroidGutterIconTest {
public void testAllFilesPresentInRes() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/gutterIcon/res"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
}
}
+2
View File
@@ -57,6 +57,8 @@
<lang.foldingBuilder language="kotlin" implementationClass="org.jetbrains.kotlin.android.folding.ResourceFoldingBuilder" />
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.android.AndroidResourceReferenceAnnotator" />
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+18
View File
@@ -0,0 +1,18 @@
package com.myapp;
public final class R {
public static final class drawable {
public static final int test_icon=0x7f02005d;
}
public static final class mipmap {
public static final int test_icon=0x7f04005d;
}
public static final class color {
public static final int colorAccent=0x7f0a0013;
public static final int colorPrimary=0x7f0a0014;
public static final int colorPrimaryDark=0x7f0a0015;
}
public static final class layout {
public static final int activity_kotlin=0x7f08005d;
}
}
+6
View File
@@ -0,0 +1,6 @@
// COLOR: FF,40,81
package com.myapp
import android.content.Context
<caret>fun Context.getPrimaryColor() = resources.getColor(R.color.colorAccent, null)
+6
View File
@@ -0,0 +1,6 @@
// DRAWABLE
package com.myapp
import android.content.Context
<caret>fun Context.getAddIcon() = resources.getDrawable(R.drawable.test_icon, null)
+6
View File
@@ -0,0 +1,6 @@
// DRAWABLE
package com.myapp
import android.content.Context
<caret>fun Context.getAddIcon() = resources.getDrawable(R.mipmap.test_icon, null)
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_kotlin"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin">
</RelativeLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
+4
View File
@@ -0,0 +1,4 @@
// COLOR: 0,0,0
import android.content.Context
<caret>fun getBlackColor() = android.R.color.black
+4
View File
@@ -0,0 +1,4 @@
// DRAWABLE
import android.content.Context
fun <caret>Context.getAddIcon() = resources.getDrawable(android.R.drawable.ic_menu_add, null)