Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[resources] Add functions to retrieve bytes from drawable or font resources. #4651

Merged
merged 10 commits into from
Apr 23, 2024
Merged
4 changes: 0 additions & 4 deletions components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ in Android Studio or in AppCode with [installed CocoaPods](https://kotlinlang.or
### Run JS in browser with WebAssembly Skia via Gradle:
`./gradlew :resources:demo:shared:jsBrowserDevelopmentRun`

### Run MacOS via Gradle:
- on Intel CPU: `./gradlew :resources:demo:shared:runDebugExecutableMacosX64`
- on Apple Silicon: `./gradlew :resources:demo:shared:runDebugExecutableMacosArm64`

# Tests
Run script:
```bash
Expand Down
2 changes: 1 addition & 1 deletion components/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ android.useAndroidX=true

#Versions
kotlin.version=1.9.23
compose.version=1.6.10-beta01
compose.version=1.6.10-dev1596
agp.version=8.2.2

#Compose
Expand Down
16 changes: 0 additions & 16 deletions components/resources/demo/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,8 @@ kotlin {
binaries.executable()
}

listOf(
macosX64(),
macosArm64()
).forEach { macosTarget ->
macosTarget.binaries {
executable {
entryPoint = "main"
}
}
}

applyDefaultHierarchyTemplate()
sourceSets {
all {
languageSettings {
optIn("org.jetbrains.compose.resources.ExperimentalResourceApi")
}
}
val desktopMain by getting
val wasmJsMain by getting

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import components.resources.demo.shared.generated.resources.Res
import components.resources.demo.shared.generated.resources.droid_icon
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.getDrawableResourceBytes
import org.jetbrains.compose.resources.rememberResourceEnvironment

@OptIn(ExperimentalResourceApi::class)
@Composable
fun FileRes(paddingValues: PaddingValues) {
Column(
modifier = Modifier.padding(paddingValues)
modifier = Modifier.padding(paddingValues).verticalScroll(rememberScrollState())
) {
Text(
modifier = Modifier.padding(16.dp),
Expand Down Expand Up @@ -48,6 +53,34 @@ fun FileRes(paddingValues: PaddingValues) {
Text(bytes.decodeToString())
""".trimIndent()
)
HorizontalDivider(modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp))
OutlinedCard(
modifier = Modifier.padding(horizontal = 16.dp),
shape = RoundedCornerShape(4.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
) {
val composeEnv = rememberResourceEnvironment()
var bytes by remember { mutableStateOf(ByteArray(0)) }
LaunchedEffect(Unit) {
bytes = getDrawableResourceBytes(composeEnv, Res.drawable.droid_icon)
}
Text(
modifier = Modifier.padding(8.dp),
text = "droid_icon byte size = " + bytes.size,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Text(
modifier = Modifier.padding(16.dp),
text = """
val composeEnv = rememberResourceEnvironment()
var bytes by remember { mutableStateOf(ByteArray(0)) }
LaunchedEffect(Unit) {
bytes = getDrawableResourceBytes(composeEnv, Res.drawable.droid_icon)
}
Text("droid_icon byte size = " + bytes.size)
""".trimIndent()
)
Text(
modifier = Modifier.padding(16.dp),
text = "File: 'files/platform-text.txt'",
Expand Down Expand Up @@ -80,5 +113,23 @@ fun FileRes(paddingValues: PaddingValues) {
Text(bytes.decodeToString())
""".trimIndent()
)
HorizontalDivider(modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp))
OutlinedCard(
modifier = Modifier.padding(horizontal = 16.dp),
shape = RoundedCornerShape(4.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
) {
Text(
modifier = Modifier.padding(8.dp),
text = "File URI: " + Res.getUri("files/platform-text.txt"),
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Text(
modifier = Modifier.padding(16.dp),
text = """
Text("File URI: " + Res.getUri("files/platform-text.txt"))
""".trimIndent()
)
}
}

This file was deleted.

This file was deleted.

1 change: 1 addition & 0 deletions components/resources/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ kotlin {
optIn("kotlinx.cinterop.ExperimentalForeignApi")
optIn("kotlin.experimental.ExperimentalNativeApi")
optIn("org.jetbrains.compose.resources.InternalResourceApi")
optIn("org.jetbrains.compose.resources.ExperimentalResourceApi")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.jetbrains.compose.resources

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.ui.text.font.*

/**
Expand Down Expand Up @@ -32,4 +33,20 @@ expect fun Font(
resource: FontResource,
igordmn marked this conversation as resolved.
Show resolved Hide resolved
weight: FontWeight = FontWeight.Normal,
style: FontStyle = FontStyle.Normal
): Font
): Font

/**
* Retrieves the byte array of the font resource.
*
* @param environment The optional resource environment.
* @param resource The font resource.
* @return The byte array representing the font resource.
*/
@ExperimentalResourceApi
suspend fun getFontResourceBytes(
environment: ResourceEnvironment = getSystemResourceEnvironment(),
resource: FontResource
): ByteArray {
val resourceItem = resource.getResourceItemByEnvironment(environment)
return DefaultResourceReader.read(resourceItem.path)
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ internal expect fun SvgElement.toSvgPainter(density: Density): Painter

private val emptySvgPainter: Painter by lazy { BitmapPainter(emptyImageBitmap) }

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun svgPainter(resource: DrawableResource): Painter {
val resourceReader = LocalResourceReader.current
Expand All @@ -110,6 +109,22 @@ private fun svgPainter(resource: DrawableResource): Painter {
return svgPainter
}

/**
* Retrieves the byte array of the drawable resource.
*
* @param environment The optional resource environment.
* @param resource The drawable resource.
* @return The byte array representing the drawable resource.
*/
@ExperimentalResourceApi
suspend fun getDrawableResourceBytes(
environment: ResourceEnvironment = getSystemResourceEnvironment(),
resource: DrawableResource
): ByteArray {
val resourceItem = resource.getResourceItemByEnvironment(environment)
return DefaultResourceReader.read(resourceItem.path)
}

internal expect fun ByteArray.toImageBitmap(): ImageBitmap
internal expect fun ByteArray.toXmlElement(): Element
internal expect fun ByteArray.toSvgElement(): SvgElement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,23 @@ fun pluralStringResource(resource: PluralStringResource, quantity: Int): String
* @throws IllegalArgumentException If the provided ID or the pluralization is not found in the resource file.
*/
suspend fun getPluralString(resource: PluralStringResource, quantity: Int): String =
loadPluralString(resource, quantity, DefaultResourceReader, getResourceEnvironment())
loadPluralString(resource, quantity, DefaultResourceReader, getSystemResourceEnvironment())

/**
* Loads a string using the specified string resource.
*
* @param environment The resource environment.
* @param resource The string resource to be used.
* @param quantity The quantity of the pluralization to use.
* @return The loaded string resource.
*
* @throws IllegalArgumentException If the provided ID or the pluralization is not found in the resource file.
*/
suspend fun getPluralString(
igordmn marked this conversation as resolved.
Show resolved Hide resolved
environment: ResourceEnvironment,
resource: PluralStringResource,
quantity: Int
): String = loadPluralString(resource, quantity, DefaultResourceReader, environment)

private suspend fun loadPluralString(
resource: PluralStringResource,
Expand Down Expand Up @@ -99,9 +115,32 @@ suspend fun getPluralString(resource: PluralStringResource, quantity: Int, varar
resource, quantity,
formatArgs.map { it.toString() },
DefaultResourceReader,
getResourceEnvironment(),
getSystemResourceEnvironment(),
)

/**
* Loads a string using the specified string resource.
*
* @param environment The resource environment.
* @param resource The string resource to be used.
* @param quantity The quantity of the pluralization to use.
* @param formatArgs The arguments to be inserted into the formatted string.
* @return The loaded string resource.
*
* @throws IllegalArgumentException If the provided ID or the pluralization is not found in the resource file.
*/
suspend fun getPluralString(
igordmn marked this conversation as resolved.
Show resolved Hide resolved
environment: ResourceEnvironment,
resource: PluralStringResource,
quantity: Int,
vararg formatArgs: Any
): String = loadPluralString(
resource, quantity,
formatArgs.map { it.toString() },
DefaultResourceReader,
environment
)

private suspend fun loadPluralString(
resource: PluralStringResource,
quantity: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.intl.Locale

internal data class ResourceEnvironment(
val language: LanguageQualifier,
val region: RegionQualifier,
val theme: ThemeQualifier,
val density: DensityQualifier
@ExperimentalResourceApi
data class ResourceEnvironment internal constructor(
igordmn marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal val language: LanguageQualifier,
internal val region: RegionQualifier,
internal val theme: ThemeQualifier,
internal val density: DensityQualifier
)

internal interface ComposeEnvironment {
Expand Down Expand Up @@ -39,14 +40,32 @@ internal val DefaultComposeEnvironment = object : ComposeEnvironment {
//ComposeEnvironment provider will be overridden for tests
internal val LocalComposeEnvironment = staticCompositionLocalOf { DefaultComposeEnvironment }

/**
* Returns an instance of [ResourceEnvironment].
*
* The [ResourceEnvironment] class represents the environment for resources.
*
* @return An instance of [ResourceEnvironment] representing the current environment.
*/
@ExperimentalResourceApi
@Composable
fun rememberResourceEnvironment(): ResourceEnvironment {
val composeEnvironment = LocalComposeEnvironment.current
return composeEnvironment.rememberEnvironment()
}

internal expect fun getSystemEnvironment(): ResourceEnvironment

//the function reference will be overridden for tests
//@TestOnly
internal var getResourceEnvironment = ::getSystemEnvironment

/**
* Provides the resource environment for non-composable access to string resources.
* Provides the resource environment for non-composable access to resources.
* It is an expensive operation! Don't use it in composable functions with no cache!
*/
internal var getResourceEnvironment = ::getSystemEnvironment
@ExperimentalResourceApi
fun getSystemResourceEnvironment(): ResourceEnvironment = getResourceEnvironment()

@OptIn(InternalResourceApi::class)
internal fun Resource.getResourceItemByEnvironment(environment: ResourceEnvironment): ResourceItem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,21 @@ fun stringArrayResource(resource: StringArrayResource): List<String> {
* @throws IllegalStateException if the string array with the given ID is not found.
*/
suspend fun getStringArray(resource: StringArrayResource): List<String> =
loadStringArray(resource, DefaultResourceReader, getResourceEnvironment())
loadStringArray(resource, DefaultResourceReader, getSystemResourceEnvironment())

/**
* Loads a list of strings using the specified string array resource.
*
* @param environment The resource environment.
* @param resource The string array resource to be used.
* @return A list of strings representing the items in the string array.
*
* @throws IllegalStateException if the string array with the given ID is not found.
*/
suspend fun getStringArray(
igordmn marked this conversation as resolved.
Show resolved Hide resolved
environment: ResourceEnvironment,
resource: StringArrayResource
): List<String> = loadStringArray(resource, DefaultResourceReader, environment)

private suspend fun loadStringArray(
resource: StringArrayResource,
Expand Down
Loading