Support android.support.v4.Fragment classes
This commit is contained in:
@@ -3,5 +3,6 @@ ERROR: Required plugin option not present: org.jetbrains.kotlin.android:androidR
|
||||
Plugin "org.jetbrains.kotlin.android" usage:
|
||||
androidRes <path> Android resources path (required)
|
||||
androidManifest <path> Android manifest file (required)
|
||||
supportV4 <path> Support android-v4 library
|
||||
|
||||
COMPILATION_ERROR
|
||||
+6
-1
@@ -36,11 +36,16 @@ public class AndroidSubplugin : KotlinGradleSubplugin {
|
||||
val resourceDir = mainSourceSet.getRes().getSrcDirs().firstOrNull()
|
||||
val manifestFile = mainSourceSet.getManifest().getSrcFile()
|
||||
|
||||
val compileDependencies = project.getConfigurations().getByName("compile").getFiles()
|
||||
val supportV4 = compileDependencies?.filter { it.name.startsWith("support-v4-") }?.isNotEmpty() ?: false
|
||||
val supportV4Property = if (supportV4) "true" else "false"
|
||||
|
||||
if (resourceDir != null) {
|
||||
resourceDir.listFiles { it.isDirectory() && it.name.startsWith("layout") }?.forEach { task.source(it) }
|
||||
return listOf(
|
||||
SubpluginOption("androidRes", resourceDir.getAbsolutePath()),
|
||||
SubpluginOption("androidManifest", manifestFile.getAbsolutePath())
|
||||
SubpluginOption("androidManifest", manifestFile.getAbsolutePath()),
|
||||
SubpluginOption("supportV4", supportV4Property)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ public object AndroidConfigurationKeys {
|
||||
public val ANDROID_RES_PATH: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("android resources search path")
|
||||
|
||||
public val ANDROID_MANIFEST: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("android manifest file")
|
||||
|
||||
public val SUPPORT_V4: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("'true' if compiled with support-v4 library")
|
||||
}
|
||||
|
||||
public class AndroidCommandLineProcessor : CommandLineProcessor {
|
||||
@@ -46,16 +48,18 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
|
||||
|
||||
public val RESOURCE_PATH_OPTION: CliOption = CliOption("androidRes", "<path>", "Android resources path")
|
||||
public val MANIFEST_FILE_OPTION: CliOption = CliOption("androidManifest", "<path>", "Android manifest file")
|
||||
public val SUPPORT_V4_OPTION: CliOption = CliOption("supportV4", "<path>", "Support android-v4 library", required = false)
|
||||
}
|
||||
|
||||
override val pluginId: String = ANDROID_COMPILER_PLUGIN_ID
|
||||
|
||||
override val pluginOptions: Collection<CliOption> = listOf(RESOURCE_PATH_OPTION, MANIFEST_FILE_OPTION)
|
||||
override val pluginOptions: Collection<CliOption> = listOf(RESOURCE_PATH_OPTION, MANIFEST_FILE_OPTION, SUPPORT_V4_OPTION)
|
||||
|
||||
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
|
||||
when (option) {
|
||||
RESOURCE_PATH_OPTION -> configuration.put(AndroidConfigurationKeys.ANDROID_RES_PATH, value)
|
||||
MANIFEST_FILE_OPTION -> configuration.put(AndroidConfigurationKeys.ANDROID_MANIFEST, value)
|
||||
SUPPORT_V4_OPTION -> configuration.put(AndroidConfigurationKeys.SUPPORT_V4, value)
|
||||
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
|
||||
}
|
||||
}
|
||||
@@ -73,9 +77,13 @@ public class AndroidComponentRegistrar : ComponentRegistrar {
|
||||
public override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val androidResPath = configuration.get(AndroidConfigurationKeys.ANDROID_RES_PATH)
|
||||
val androidManifest = configuration.get(AndroidConfigurationKeys.ANDROID_MANIFEST)
|
||||
val supportV4 = configuration.get(AndroidConfigurationKeys.SUPPORT_V4) ?: "false"
|
||||
|
||||
if (androidResPath != null && androidManifest != null) {
|
||||
project.registerService(javaClass<AndroidUIXmlProcessor>(), CliAndroidUIXmlProcessor(project, androidManifest, androidResPath))
|
||||
val xmlProcessor = CliAndroidUIXmlProcessor(project, androidManifest, androidResPath)
|
||||
if (supportV4 == "true") xmlProcessor.supportV4 = true
|
||||
|
||||
project.registerService(javaClass<AndroidUIXmlProcessor>(), xmlProcessor)
|
||||
project.registerService(javaClass<AndroidResourceManager>(), CliAndroidResourceManager(project, androidManifest, androidResPath))
|
||||
|
||||
ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project))
|
||||
|
||||
@@ -44,12 +44,16 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
private enum class AndroidClassType(val internalClassName: String, val supportsCache: Boolean = false) {
|
||||
ACTIVITY : AndroidClassType("android/app/Activity", true)
|
||||
FRAGMENT : AndroidClassType("android/app/Fragment", true)
|
||||
VIEW : AndroidClassType("android/view/View")
|
||||
ACTIVITY : AndroidClassType(AndroidConst.ACTIVITY_FQNAME.innerName, true)
|
||||
FRAGMENT : AndroidClassType(AndroidConst.FRAGMENT_FQNAME.innerName, true)
|
||||
SUPPORT_FRAGMENT : AndroidClassType(AndroidConst.SUPPORT_FRAGMENT_FQNAME.innerName, true)
|
||||
VIEW : AndroidClassType(AndroidConst.VIEW_FQNAME.innerName)
|
||||
UNKNOWN : AndroidClassType("")
|
||||
}
|
||||
|
||||
private val String.innerName: String
|
||||
get() = replace('.', '/')
|
||||
|
||||
public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
companion object {
|
||||
private val PROPERTY_NAME = "_\$_findViewCache"
|
||||
@@ -145,7 +149,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
||||
v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
AndroidClassType.FRAGMENT -> {
|
||||
AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> {
|
||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
|
||||
v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
|
||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
||||
@@ -169,9 +173,10 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
|
||||
private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
|
||||
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
|
||||
"android.app.Activity" -> AndroidClassType.ACTIVITY
|
||||
"android.app.Fragment" -> AndroidClassType.FRAGMENT
|
||||
"android.view.View" -> AndroidClassType.VIEW
|
||||
AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY
|
||||
AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT
|
||||
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT
|
||||
AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -291,7 +296,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
loadId()
|
||||
iv.invokevirtual(className, "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
AndroidClassType.FRAGMENT -> {
|
||||
AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> {
|
||||
iv.invokevirtual(className, "getView", "()Landroid/view/View;", false)
|
||||
loadId()
|
||||
iv.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
|
||||
|
||||
+6
@@ -41,6 +41,12 @@ public object AndroidConst {
|
||||
|
||||
val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache"
|
||||
|
||||
val VIEW_FQNAME = "android.view.View"
|
||||
val ACTIVITY_FQNAME = "android.app.Activity"
|
||||
val FRAGMENT_FQNAME = "android.app.Fragment"
|
||||
val SUPPORT_V4_PACKAGE = "android.support.v4"
|
||||
val SUPPORT_FRAGMENT_FQNAME = "$SUPPORT_V4_PACKAGE.app.Fragment"
|
||||
|
||||
val IGNORED_XML_WIDGET_TYPES = setOf("requestFocus", "merge", "tag", "check", "fragment")
|
||||
|
||||
val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes())
|
||||
|
||||
+9
-3
@@ -66,6 +66,8 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
||||
|
||||
protected abstract val cachedSources: CachedValue<List<AndroidSyntheticFile>>
|
||||
|
||||
var supportV4 = false
|
||||
|
||||
private val cachedJetFiles: CachedValue<List<JetFile>> by Delegates.lazy {
|
||||
cachedValue {
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
@@ -90,8 +92,9 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
||||
val clearCacheFile = renderSyntheticFile("clearCache") {
|
||||
writePackage(AndroidConst.SYNTHETIC_PACKAGE)
|
||||
writeAndroidImports()
|
||||
writeClearCacheFunction("android.app.Activity")
|
||||
writeClearCacheFunction("android.app.Fragment")
|
||||
writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME)
|
||||
writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME)
|
||||
if (supportV4) writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
|
||||
}
|
||||
|
||||
listOf(clearCacheFile,
|
||||
@@ -109,6 +112,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
||||
|
||||
val mainLayoutFile = renderMainLayoutFile(layoutName, resources)
|
||||
val viewLayoutFile = renderViewLayoutFile(layoutName, resources)
|
||||
|
||||
listOf(mainLayoutFile, viewLayoutFile)
|
||||
}.filterNotNull() + commonFiles
|
||||
}
|
||||
@@ -138,7 +142,9 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
||||
|
||||
for (res in resources) {
|
||||
properties(res).forEach {
|
||||
writeSyntheticProperty(it.first, res, it.second)
|
||||
if (supportV4 || !it.first.startsWith(AndroidConst.SUPPORT_V4_PACKAGE)) {
|
||||
writeSyntheticProperty(it.first, res, it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -31,7 +31,8 @@ public abstract class AndroidResource(val id: String) {
|
||||
public class AndroidWidget(id: String, override val className: String) : AndroidResource(id) {
|
||||
override val mainProperties = listOf(
|
||||
"android.app.Activity" to "findViewById(0)",
|
||||
"android.app.Fragment" to "getView().findViewById(0)")
|
||||
"android.app.Fragment" to "getView().findViewById(0)",
|
||||
"android.support.v4.app.Fragment" to "getView().findViewById(0)")
|
||||
|
||||
override val viewProperties = listOf("android.view.View" to "findViewById(0)")
|
||||
|
||||
|
||||
+7
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.plugin.android
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import org.jetbrains.kotlin.plugin.android.IDEAndroidResourceManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.plugin.android.AndroidXmlVisitor
|
||||
@@ -29,6 +30,12 @@ import com.intellij.psi.util.CachedValueProvider.Result
|
||||
|
||||
class IDEAndroidUIXmlProcessor(val module: Module) : AndroidUIXmlProcessor(module.getProject()) {
|
||||
|
||||
init {
|
||||
val scope = module.getModuleWithDependenciesAndLibrariesScope(false)
|
||||
supportV4 = JavaPsiFacade.getInstance(module.getProject())
|
||||
.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, scope).isNotEmpty()
|
||||
}
|
||||
|
||||
override val resourceManager: IDEAndroidResourceManager = IDEAndroidResourceManager(module)
|
||||
|
||||
private val psiTreeChangePreprocessor by Delegates.lazy {
|
||||
|
||||
@@ -23,6 +23,9 @@ import org.jetbrains.jps.model.module.JpsModule
|
||||
import java.io.File
|
||||
import org.jetbrains.jps.android.AndroidJpsUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType
|
||||
import org.jetbrains.jps.model.module.JpsLibraryDependency
|
||||
import org.jetbrains.jps.model.module.JpsModuleDependency
|
||||
|
||||
public class KotlinAndroidJpsPlugin : KotlinJpsCompilerArgumentsProvider {
|
||||
override fun getExtraArguments(moduleBuildTarget: ModuleBuildTarget, context: CompileContext): List<String> {
|
||||
@@ -30,12 +33,27 @@ public class KotlinAndroidJpsPlugin : KotlinJpsCompilerArgumentsProvider {
|
||||
val pluginId = ANDROID_COMPILER_PLUGIN_ID
|
||||
val resPath = getAndroidResPath(module)
|
||||
val manifestFile = getAndroidManifest(module)
|
||||
val supportV4 = if (isSupportV4LibraryAttached(module)) "true" else "false"
|
||||
|
||||
return if (resPath != null && manifestFile != null) listOf(
|
||||
getPluginOptionString(pluginId, RESOURCE_PATH_OPTION_NAME, resPath),
|
||||
getPluginOptionString(pluginId, MANIFEST_FILE_OPTION_NAME, manifestFile))
|
||||
getPluginOptionString(pluginId, MANIFEST_FILE_OPTION_NAME, manifestFile),
|
||||
getPluginOptionString(pluginId, SUPPORT_V4_OPTION_NAME, supportV4))
|
||||
else listOf()
|
||||
}
|
||||
|
||||
private fun isSupportV4LibraryAttached(module: JpsModule): Boolean {
|
||||
return module.getDependenciesList().getDependencies().any { dep ->
|
||||
when (dep) {
|
||||
is JpsLibraryDependency ->
|
||||
dep.getLibrary()?.getFiles(JpsOrderRootType.COMPILED)?.any {
|
||||
it.name.startsWith("support-v4") && it.extension.toUpperCase() == "JAR"
|
||||
} ?: false
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getClasspath(moduleBuildTarget: ModuleBuildTarget, context: CompileContext): List<String> {
|
||||
val inJar = File(PathUtil.getJarPathForClass(javaClass)).isFile()
|
||||
val manifestFile = getAndroidManifest(moduleBuildTarget.getModule())
|
||||
@@ -72,6 +90,7 @@ public class KotlinAndroidJpsPlugin : KotlinJpsCompilerArgumentsProvider {
|
||||
|
||||
private val RESOURCE_PATH_OPTION_NAME = "androidRes"
|
||||
private val MANIFEST_FILE_OPTION_NAME = "androidManifest"
|
||||
private val SUPPORT_V4_OPTION_NAME = "supportV4"
|
||||
|
||||
private fun getPluginOptionString(pluginId: String, key: String, value: String): String {
|
||||
return "plugin:$pluginId:$key=$value"
|
||||
|
||||
Reference in New Issue
Block a user