Browse Source
Compose resources can be located in different KMP source sets in the `composeResources` directory. For each resource an accessor will be generated in the suitable kotlin source set.support/1.6.10 v1.6.10-dev1575
Konstantin
4 weeks ago
committed by
GitHub
39 changed files with 714 additions and 365 deletions
@ -0,0 +1,133 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import org.gradle.api.Project |
||||
import org.gradle.api.provider.Provider |
||||
import org.jetbrains.compose.ComposePlugin |
||||
import org.jetbrains.compose.internal.utils.uppercaseFirstChar |
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension |
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet |
||||
import java.io.File |
||||
|
||||
internal fun Project.configureComposeResourcesGeneration( |
||||
kotlinExtension: KotlinProjectExtension, |
||||
resClassSourceSetName: String, |
||||
config: Provider<ResourcesExtension>, |
||||
generateModulePath: Boolean |
||||
) { |
||||
logger.info("Configure compose resources generation") |
||||
|
||||
//lazy check a dependency on the Resources library |
||||
val shouldGenerateCode = config.map { |
||||
when (it.generateResClass) { |
||||
ResourcesExtension.ResourceClassGeneration.Auto -> { |
||||
configurations.run { |
||||
val commonSourceSet = kotlinExtension.sourceSets.getByName(resClassSourceSetName) |
||||
//because the implementation configuration doesn't extend the api in the KGP ¯\_(ツ)_/¯ |
||||
getByName(commonSourceSet.implementationConfigurationName).allDependencies + |
||||
getByName(commonSourceSet.apiConfigurationName).allDependencies |
||||
}.any { dep -> |
||||
val depStringNotation = dep.let { "${it.group}:${it.name}:${it.version}" } |
||||
depStringNotation == ComposePlugin.CommonComponentsDependencies.resources |
||||
} |
||||
} |
||||
|
||||
ResourcesExtension.ResourceClassGeneration.Always -> true |
||||
ResourcesExtension.ResourceClassGeneration.Never -> false |
||||
} |
||||
} |
||||
val packageName = config.getResourcePackage(project) |
||||
val makeAccessorsPublic = config.map { it.publicResClass } |
||||
val packagingDir = config.getModuleResourcesDir(project) |
||||
|
||||
kotlinExtension.sourceSets.all { sourceSet -> |
||||
if (sourceSet.name == resClassSourceSetName) { |
||||
configureResClassGeneration( |
||||
sourceSet, |
||||
shouldGenerateCode, |
||||
packageName, |
||||
makeAccessorsPublic, |
||||
packagingDir, |
||||
generateModulePath |
||||
) |
||||
} |
||||
|
||||
//common resources must be converted (XML -> CVR) |
||||
val preparedResourcesTask = registerPrepareComposeResourcesTask(sourceSet) |
||||
val preparedResources = preparedResourcesTask.flatMap { it.outputDir.asFile } |
||||
configureResourceAccessorsGeneration( |
||||
sourceSet, |
||||
preparedResources, |
||||
shouldGenerateCode, |
||||
packageName, |
||||
makeAccessorsPublic, |
||||
packagingDir, |
||||
generateModulePath |
||||
) |
||||
} |
||||
|
||||
//setup task execution during IDE import |
||||
tasks.configureEach { importTask -> |
||||
if (importTask.name == "prepareKotlinIdeaImport") { |
||||
importTask.dependsOn(tasks.withType(CodeGenerationTask::class.java)) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun Project.configureResClassGeneration( |
||||
resClassSourceSet: KotlinSourceSet, |
||||
shouldGenerateCode: Provider<Boolean>, |
||||
packageName: Provider<String>, |
||||
makeAccessorsPublic: Provider<Boolean>, |
||||
packagingDir: Provider<File>, |
||||
generateModulePath: Boolean |
||||
) { |
||||
logger.info("Configure Res class generation for ${resClassSourceSet.name}") |
||||
|
||||
val genTask = tasks.register( |
||||
"generateComposeResClass", |
||||
GenerateResClassTask::class.java |
||||
) { task -> |
||||
task.packageName.set(packageName) |
||||
task.shouldGenerateCode.set(shouldGenerateCode) |
||||
task.makeAccessorsPublic.set(makeAccessorsPublic) |
||||
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/commonResClass")) |
||||
|
||||
if (generateModulePath) { |
||||
task.packagingDir.set(packagingDir) |
||||
} |
||||
} |
||||
|
||||
//register generated source set |
||||
resClassSourceSet.kotlin.srcDir(genTask.map { it.codeDir }) |
||||
} |
||||
|
||||
private fun Project.configureResourceAccessorsGeneration( |
||||
sourceSet: KotlinSourceSet, |
||||
resourcesDir: Provider<File>, |
||||
shouldGenerateCode: Provider<Boolean>, |
||||
packageName: Provider<String>, |
||||
makeAccessorsPublic: Provider<Boolean>, |
||||
packagingDir: Provider<File>, |
||||
generateModulePath: Boolean |
||||
) { |
||||
logger.info("Configure resource accessors generation for ${sourceSet.name}") |
||||
|
||||
val genTask = tasks.register( |
||||
"generateResourceAccessorsFor${sourceSet.name.uppercaseFirstChar()}", |
||||
GenerateResourceAccessorsTask::class.java |
||||
) { task -> |
||||
task.packageName.set(packageName) |
||||
task.sourceSetName.set(sourceSet.name) |
||||
task.shouldGenerateCode.set(shouldGenerateCode) |
||||
task.makeAccessorsPublic.set(makeAccessorsPublic) |
||||
task.resDir.set(resourcesDir) |
||||
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/${sourceSet.name}ResourceAccessors")) |
||||
|
||||
if (generateModulePath) { |
||||
task.packagingDir.set(packagingDir) |
||||
} |
||||
} |
||||
|
||||
//register generated source set |
||||
sourceSet.kotlin.srcDir(genTask.map { it.codeDir }) |
||||
} |
@ -1,147 +1,59 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import org.gradle.api.DefaultTask |
||||
import org.gradle.api.file.DirectoryProperty |
||||
import org.gradle.api.file.RegularFile |
||||
import org.gradle.api.provider.Property |
||||
import org.gradle.api.provider.Provider |
||||
import org.gradle.api.tasks.* |
||||
import java.io.File |
||||
import java.io.RandomAccessFile |
||||
import java.nio.file.Path |
||||
import kotlin.io.path.relativeTo |
||||
|
||||
/** |
||||
* This task should be FAST and SAFE! Because it is being run during IDE import. |
||||
*/ |
||||
internal abstract class GenerateResClassTask : DefaultTask() { |
||||
internal abstract class CodeGenerationTask : DefaultTask() |
||||
|
||||
internal abstract class GenerateResClassTask : CodeGenerationTask() { |
||||
companion object { |
||||
private const val RES_FILE_NAME = "Res" |
||||
} |
||||
|
||||
@get:Input |
||||
abstract val packageName: Property<String> |
||||
|
||||
@get:Input |
||||
@get:Optional |
||||
abstract val moduleDir: Property<File> |
||||
abstract val packagingDir: Property<File> |
||||
|
||||
@get:Input |
||||
abstract val shouldGenerateResClass: Property<Boolean> |
||||
abstract val shouldGenerateCode: Property<Boolean> |
||||
|
||||
@get:Input |
||||
abstract val makeResClassPublic: Property<Boolean> |
||||
|
||||
@get:InputFiles |
||||
@get:PathSensitive(PathSensitivity.RELATIVE) |
||||
abstract val resDir: Property<File> |
||||
abstract val makeAccessorsPublic: Property<Boolean> |
||||
|
||||
@get:OutputDirectory |
||||
abstract val codeDir: Property<File> |
||||
abstract val codeDir: DirectoryProperty |
||||
|
||||
@TaskAction |
||||
fun generate() { |
||||
try { |
||||
val kotlinDir = codeDir.get() |
||||
logger.info("Clean directory $kotlinDir") |
||||
kotlinDir.deleteRecursively() |
||||
kotlinDir.mkdirs() |
||||
val dir = codeDir.get().asFile |
||||
dir.deleteRecursively() |
||||
dir.mkdirs() |
||||
|
||||
if (shouldGenerateResClass.get()) { |
||||
val rootResDir = resDir.get() |
||||
logger.info("Generate resources for $rootResDir") |
||||
if (shouldGenerateCode.get()) { |
||||
logger.info("Generate $RES_FILE_NAME.kt") |
||||
|
||||
//get first level dirs |
||||
val dirs = rootResDir.listNotHiddenFiles() |
||||
|
||||
dirs.forEach { f -> |
||||
if (!f.isDirectory) { |
||||
error("${f.name} is not directory! Raw files should be placed in '${rootResDir.name}/files' directory.") |
||||
} |
||||
} |
||||
|
||||
//type -> id -> resource item |
||||
val resources: Map<ResourceType, Map<String, List<ResourceItem>>> = dirs |
||||
.flatMap { dir -> |
||||
dir.listNotHiddenFiles() |
||||
.mapNotNull { it.fileToResourceItems(rootResDir.toPath()) } |
||||
.flatten() |
||||
} |
||||
.groupBy { it.type } |
||||
.mapValues { (_, items) -> items.groupBy { it.name } } |
||||
getResFileSpecs( |
||||
resources, |
||||
packageName.get(), |
||||
moduleDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "", |
||||
makeResClassPublic.get() |
||||
).forEach { it.writeTo(kotlinDir) } |
||||
val pkgName = packageName.get() |
||||
val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "" |
||||
val isPublic = makeAccessorsPublic.get() |
||||
getResFileSpec(pkgName, RES_FILE_NAME, moduleDirectory, isPublic).writeTo(dir) |
||||
} else { |
||||
logger.info("Generation Res class is disabled") |
||||
} |
||||
} catch (e: Exception) { |
||||
//message must contain two ':' symbols to be parsed by IDE UI! |
||||
logger.error("e: GenerateResClassTask was failed:", e) |
||||
logger.error("e: $name task was failed:", e) |
||||
} |
||||
} |
||||
|
||||
private fun File.fileToResourceItems( |
||||
relativeTo: Path |
||||
): List<ResourceItem>? { |
||||
val file = this |
||||
val dirName = file.parentFile.name ?: return null |
||||
val typeAndQualifiers = dirName.split("-") |
||||
if (typeAndQualifiers.isEmpty()) return null |
||||
|
||||
val typeString = typeAndQualifiers.first().lowercase() |
||||
val qualifiers = typeAndQualifiers.takeLast(typeAndQualifiers.size - 1) |
||||
val path = file.toPath().relativeTo(relativeTo) |
||||
|
||||
|
||||
if (typeString == "string") { |
||||
error("Forbidden directory name '$dirName'! String resources should be declared in 'values/strings.xml'.") |
||||
} |
||||
|
||||
if (typeString == "files") { |
||||
if (qualifiers.isNotEmpty()) error("The 'files' directory doesn't support qualifiers: '$dirName'.") |
||||
return null |
||||
} |
||||
|
||||
if (typeString == "values" && file.extension.equals(XmlValuesConverterTask.CONVERTED_RESOURCE_EXT, true)) { |
||||
return getValueResourceItems(file, qualifiers, path) |
||||
} |
||||
|
||||
val type = ResourceType.fromString(typeString) ?: error("Unknown resource type: '$typeString'.") |
||||
return listOf(ResourceItem(type, qualifiers, file.nameWithoutExtension.asUnderscoredIdentifier(), path)) |
||||
} |
||||
|
||||
private fun getValueResourceItems(dataFile: File, qualifiers: List<String>, path: Path) : List<ResourceItem> { |
||||
val result = mutableListOf<ResourceItem>() |
||||
dataFile.bufferedReader().use { f -> |
||||
var offset = 0L |
||||
var line: String? = f.readLine() |
||||
while (line != null) { |
||||
val size = line.encodeToByteArray().size |
||||
|
||||
//first line is meta info |
||||
if (offset > 0) { |
||||
result.add(getValueResourceItem(line, offset, size.toLong(), qualifiers, path)) |
||||
} |
||||
|
||||
offset += size + 1 // "+1" for newline character |
||||
line = f.readLine() |
||||
} |
||||
} |
||||
return result |
||||
} |
||||
|
||||
private fun getValueResourceItem( |
||||
recordString: String, |
||||
offset: Long, |
||||
size: Long, |
||||
qualifiers: List<String>, |
||||
path: Path |
||||
) : ResourceItem { |
||||
val record = ValueResourceRecord.createFromString(recordString) |
||||
return ResourceItem(record.type, qualifiers, record.key.asUnderscoredIdentifier(), path, offset, size) |
||||
} |
||||
} |
||||
|
||||
internal fun File.listNotHiddenFiles(): List<File> = |
||||
listFiles()?.filter { !it.isHidden }.orEmpty() |
||||
|
||||
internal fun String.asUnderscoredIdentifier(): String = |
||||
replace('-', '_') |
||||
.let { if (it.isNotEmpty() && it.first().isDigit()) "_$it" else it } |
@ -0,0 +1,157 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import org.gradle.api.file.DirectoryProperty |
||||
import org.gradle.api.provider.Property |
||||
import org.gradle.api.tasks.Input |
||||
import org.gradle.api.tasks.InputFiles |
||||
import org.gradle.api.tasks.Optional |
||||
import org.gradle.api.tasks.OutputDirectory |
||||
import org.gradle.api.tasks.PathSensitive |
||||
import org.gradle.api.tasks.PathSensitivity |
||||
import org.gradle.api.tasks.SkipWhenEmpty |
||||
import org.gradle.api.tasks.TaskAction |
||||
import java.io.File |
||||
import java.nio.file.Path |
||||
import kotlin.io.path.relativeTo |
||||
|
||||
internal abstract class GenerateResourceAccessorsTask : CodeGenerationTask() { |
||||
@get:Input |
||||
abstract val packageName: Property<String> |
||||
|
||||
@get:Input |
||||
abstract val sourceSetName: Property<String> |
||||
|
||||
@get:Input |
||||
@get:Optional |
||||
abstract val packagingDir: Property<File> |
||||
|
||||
@get:Input |
||||
abstract val shouldGenerateCode: Property<Boolean> |
||||
|
||||
@get:Input |
||||
abstract val makeAccessorsPublic: Property<Boolean> |
||||
|
||||
@get:InputFiles |
||||
@get:SkipWhenEmpty |
||||
@get:PathSensitive(PathSensitivity.RELATIVE) |
||||
abstract val resDir: Property<File> |
||||
|
||||
@get:OutputDirectory |
||||
abstract val codeDir: DirectoryProperty |
||||
|
||||
@TaskAction |
||||
fun generate() { |
||||
try { |
||||
val kotlinDir = codeDir.get().asFile |
||||
val rootResDir = resDir.get() |
||||
val sourceSet = sourceSetName.get() |
||||
|
||||
logger.info("Clean directory $kotlinDir") |
||||
kotlinDir.deleteRecursively() |
||||
kotlinDir.mkdirs() |
||||
|
||||
if (shouldGenerateCode.get()) { |
||||
logger.info("Generate accessors for $rootResDir") |
||||
|
||||
//get first level dirs |
||||
val dirs = rootResDir.listNotHiddenFiles() |
||||
|
||||
dirs.forEach { f -> |
||||
if (!f.isDirectory) { |
||||
error("${f.name} is not directory! Raw files should be placed in '${rootResDir.name}/files' directory.") |
||||
} |
||||
} |
||||
|
||||
//type -> id -> resource item |
||||
val resources: Map<ResourceType, Map<String, List<ResourceItem>>> = dirs |
||||
.flatMap { dir -> |
||||
dir.listNotHiddenFiles() |
||||
.mapNotNull { it.fileToResourceItems(rootResDir.toPath()) } |
||||
.flatten() |
||||
} |
||||
.groupBy { it.type } |
||||
.mapValues { (_, items) -> items.groupBy { it.name } } |
||||
|
||||
val pkgName = packageName.get() |
||||
val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "" |
||||
val isPublic = makeAccessorsPublic.get() |
||||
getAccessorsSpecs( |
||||
resources, pkgName, sourceSet, moduleDirectory, isPublic |
||||
).forEach { it.writeTo(kotlinDir) } |
||||
} else { |
||||
logger.info("Generation accessors for $rootResDir is disabled") |
||||
} |
||||
} catch (e: Exception) { |
||||
//message must contain two ':' symbols to be parsed by IDE UI! |
||||
logger.error("e: $name task was failed:", e) |
||||
} |
||||
} |
||||
|
||||
private fun File.fileToResourceItems( |
||||
relativeTo: Path |
||||
): List<ResourceItem>? { |
||||
val file = this |
||||
val dirName = file.parentFile.name ?: return null |
||||
val typeAndQualifiers = dirName.split("-") |
||||
if (typeAndQualifiers.isEmpty()) return null |
||||
|
||||
val typeString = typeAndQualifiers.first().lowercase() |
||||
val qualifiers = typeAndQualifiers.takeLast(typeAndQualifiers.size - 1) |
||||
val path = file.toPath().relativeTo(relativeTo) |
||||
|
||||
|
||||
if (typeString == "string") { |
||||
error("Forbidden directory name '$dirName'! String resources should be declared in 'values/strings.xml'.") |
||||
} |
||||
|
||||
if (typeString == "files") { |
||||
if (qualifiers.isNotEmpty()) error("The 'files' directory doesn't support qualifiers: '$dirName'.") |
||||
return null |
||||
} |
||||
|
||||
if (typeString == "values" && file.extension.equals(XmlValuesConverterTask.CONVERTED_RESOURCE_EXT, true)) { |
||||
return getValueResourceItems(file, qualifiers, path) |
||||
} |
||||
|
||||
val type = ResourceType.fromString(typeString) ?: error("Unknown resource type: '$typeString'.") |
||||
return listOf(ResourceItem(type, qualifiers, file.nameWithoutExtension.asUnderscoredIdentifier(), path)) |
||||
} |
||||
|
||||
private fun getValueResourceItems(dataFile: File, qualifiers: List<String>, path: Path): List<ResourceItem> { |
||||
val result = mutableListOf<ResourceItem>() |
||||
dataFile.bufferedReader().use { f -> |
||||
var offset = 0L |
||||
var line: String? = f.readLine() |
||||
while (line != null) { |
||||
val size = line.encodeToByteArray().size |
||||
|
||||
//first line is meta info |
||||
if (offset > 0) { |
||||
result.add(getValueResourceItem(line, offset, size.toLong(), qualifiers, path)) |
||||
} |
||||
|
||||
offset += size + 1 // "+1" for newline character |
||||
line = f.readLine() |
||||
} |
||||
} |
||||
return result |
||||
} |
||||
|
||||
private fun getValueResourceItem( |
||||
recordString: String, |
||||
offset: Long, |
||||
size: Long, |
||||
qualifiers: List<String>, |
||||
path: Path |
||||
): ResourceItem { |
||||
val record = ValueResourceRecord.createFromString(recordString) |
||||
return ResourceItem(record.type, qualifiers, record.key.asUnderscoredIdentifier(), path, offset, size) |
||||
} |
||||
} |
||||
|
||||
internal fun File.listNotHiddenFiles(): List<File> = |
||||
listFiles()?.filter { !it.isHidden }.orEmpty() |
||||
|
||||
internal fun String.asUnderscoredIdentifier(): String = |
||||
replace('-', '_') |
||||
.let { if (it.isNotEmpty() && it.first().isDigit()) "_$it" else it } |
@ -1,65 +0,0 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import org.gradle.api.Project |
||||
import org.gradle.api.provider.Provider |
||||
import org.jetbrains.compose.ComposePlugin |
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet |
||||
import java.io.File |
||||
|
||||
internal fun Project.configureGenerationComposeResClass( |
||||
commonComposeResourcesDir: Provider<File>, |
||||
commonSourceSet: KotlinSourceSet, |
||||
config: Provider<ResourcesExtension>, |
||||
generateModulePath: Boolean |
||||
) { |
||||
logger.info("Configure accessors for '${commonSourceSet.name}'") |
||||
|
||||
//lazy check a dependency on the Resources library |
||||
val shouldGenerateResClass = config.map { |
||||
when (it.generateResClass) { |
||||
ResourcesExtension.ResourceClassGeneration.Auto -> { |
||||
configurations.run { |
||||
//because the implementation configuration doesn't extend the api in the KGP ¯\_(ツ)_/¯ |
||||
getByName(commonSourceSet.implementationConfigurationName).allDependencies + |
||||
getByName(commonSourceSet.apiConfigurationName).allDependencies |
||||
}.any { dep -> |
||||
val depStringNotation = dep.let { "${it.group}:${it.name}:${it.version}" } |
||||
depStringNotation == ComposePlugin.CommonComponentsDependencies.resources |
||||
} |
||||
} |
||||
|
||||
ResourcesExtension.ResourceClassGeneration.Always -> { |
||||
true |
||||
} |
||||
|
||||
ResourcesExtension.ResourceClassGeneration.Never -> { |
||||
false |
||||
} |
||||
} |
||||
} |
||||
|
||||
val genTask = tasks.register( |
||||
"generateComposeResClass", |
||||
GenerateResClassTask::class.java |
||||
) { task -> |
||||
task.packageName.set(config.getResourcePackage(project)) |
||||
task.shouldGenerateResClass.set(shouldGenerateResClass) |
||||
task.makeResClassPublic.set(config.map { it.publicResClass }) |
||||
task.resDir.set(commonComposeResourcesDir) |
||||
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin").map { it.asFile }) |
||||
|
||||
if (generateModulePath) { |
||||
task.moduleDir.set(config.getModuleResourcesDir(project)) |
||||
} |
||||
} |
||||
|
||||
//register generated source set |
||||
commonSourceSet.kotlin.srcDir(genTask.map { it.codeDir }) |
||||
|
||||
//setup task execution during IDE import |
||||
tasks.configureEach { |
||||
if (it.name == "prepareKotlinIdeaImport") { |
||||
it.dependsOn(genTask) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,26 @@
|
||||
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||
|
||||
package my.lib.res |
||||
|
||||
import kotlin.OptIn |
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||
import org.jetbrains.compose.resources.StringResource |
||||
|
||||
@ExperimentalResourceApi |
||||
private object AndroidMainString0 { |
||||
public val android_str: StringResource by |
||||
lazy { init_android_str() } |
||||
} |
||||
|
||||
@ExperimentalResourceApi |
||||
public val Res.string.android_str: StringResource |
||||
get() = AndroidMainString0.android_str |
||||
|
||||
@ExperimentalResourceApi |
||||
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||
"string:android_str", "android_str", |
||||
setOf( |
||||
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.androidMain.cvr", 10, |
||||
39), |
||||
) |
||||
) |
@ -0,0 +1,26 @@
|
||||
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||
|
||||
package my.lib.res |
||||
|
||||
import kotlin.OptIn |
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||
import org.jetbrains.compose.resources.StringResource |
||||
|
||||
@ExperimentalResourceApi |
||||
private object DesktopMainString0 { |
||||
public val desktop_str: StringResource by |
||||
lazy { init_desktop_str() } |
||||
} |
||||
|
||||
@ExperimentalResourceApi |
||||
public val Res.string.desktop_str: StringResource |
||||
get() = DesktopMainString0.desktop_str |
||||
|
||||
@ExperimentalResourceApi |
||||
private fun init_desktop_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||
"string:desktop_str", "desktop_str", |
||||
setOf( |
||||
org.jetbrains.compose.resources.ResourceItem(setOf(), |
||||
"values/desktop_strings.desktopMain.cvr", 10, 39), |
||||
) |
||||
) |
@ -0,0 +1,26 @@
|
||||
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||
|
||||
package app.group.resources_test.generated.resources |
||||
|
||||
import kotlin.OptIn |
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||
import org.jetbrains.compose.resources.StringResource |
||||
|
||||
@ExperimentalResourceApi |
||||
private object AndroidMainString0 { |
||||
public val android_str: StringResource by |
||||
lazy { init_android_str() } |
||||
} |
||||
|
||||
@ExperimentalResourceApi |
||||
internal val Res.string.android_str: StringResource |
||||
get() = AndroidMainString0.android_str |
||||
|
||||
@ExperimentalResourceApi |
||||
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||
"string:android_str", "android_str", |
||||
setOf( |
||||
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.androidMain.cvr", 10, |
||||
39), |
||||
) |
||||
) |
@ -0,0 +1,26 @@
|
||||
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||
|
||||
package app.group.resources_test.generated.resources |
||||
|
||||
import kotlin.OptIn |
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||
import org.jetbrains.compose.resources.StringResource |
||||
|
||||
@ExperimentalResourceApi |
||||
private object DesktopMainString0 { |
||||
public val desktop_str: StringResource by |
||||
lazy { init_desktop_str() } |
||||
} |
||||
|
||||
@ExperimentalResourceApi |
||||
internal val Res.string.desktop_str: StringResource |
||||
get() = DesktopMainString0.desktop_str |
||||
|
||||
@ExperimentalResourceApi |
||||
private fun init_desktop_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||
"string:desktop_str", "desktop_str", |
||||
setOf( |
||||
org.jetbrains.compose.resources.ResourceItem(setOf(), |
||||
"values/desktop_strings.desktopMain.cvr", 10, 39), |
||||
) |
||||
) |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="android_str">Android string</string> |
||||
</resources> |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="desktop_str">Desktop string</string> |
||||
</resources> |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="android_str">Android string</string> |
||||
</resources> |
@ -0,0 +1,10 @@
|
||||
package me.sample.app |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import kmpresourcepublication.appmodule.generated.resources.Res |
||||
import kmpresourcepublication.appmodule.generated.resources.android_str |
||||
import org.jetbrains.compose.resources.stringResource |
||||
|
||||
@Composable |
||||
actual fun getPlatformSpecificString(): String = |
||||
stringResource(Res.string.android_str) |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="iOS_str">Ios string</string> |
||||
</resources> |
@ -0,0 +1,10 @@
|
||||
package me.sample.app |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import kmpresourcepublication.appmodule.generated.resources.Res |
||||
import kmpresourcepublication.appmodule.generated.resources.iOS_str |
||||
import org.jetbrains.compose.resources.stringResource |
||||
|
||||
@Composable |
||||
actual fun getPlatformSpecificString(): String = |
||||
stringResource(Res.string.iOS_str) |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="js_str">JS string</string> |
||||
</resources> |
@ -0,0 +1,10 @@
|
||||
package me.sample.app |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import kmpresourcepublication.appmodule.generated.resources.Res |
||||
import kmpresourcepublication.appmodule.generated.resources.js_str |
||||
import org.jetbrains.compose.resources.stringResource |
||||
|
||||
@Composable |
||||
actual fun getPlatformSpecificString(): String = |
||||
stringResource(Res.string.js_str) |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="desktop_str">Desktop string</string> |
||||
</resources> |
@ -0,0 +1,10 @@
|
||||
package me.sample.app |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import kmpresourcepublication.appmodule.generated.resources.Res |
||||
import kmpresourcepublication.appmodule.generated.resources.desktop_str |
||||
import org.jetbrains.compose.resources.stringResource |
||||
|
||||
@Composable |
||||
actual fun getPlatformSpecificString(): String = |
||||
stringResource(Res.string.desktop_str) |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="wasm_str">Wasm string</string> |
||||
</resources> |
@ -0,0 +1,10 @@
|
||||
package me.sample.app |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import kmpresourcepublication.appmodule.generated.resources.Res |
||||
import kmpresourcepublication.appmodule.generated.resources.wasm_str |
||||
import org.jetbrains.compose.resources.stringResource |
||||
|
||||
@Composable |
||||
actual fun getPlatformSpecificString(): String = |
||||
stringResource(Res.string.wasm_str) |
Loading…
Reference in new issue