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

Pack all resources to assets on the android target. #4965

Merged
merged 13 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

terrakok marked this conversation as resolved.
Show resolved Hide resolved
subprojects {
version = findProperty("deploy.version") ?: property("compose.version")!!
version = findProperty("deploy.version")!!

plugins.withId("java") {
configureIfExists<JavaPluginExtension> {
Expand Down
3 changes: 1 addition & 2 deletions components/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ android.useAndroidX=true

#Versions
kotlin.version=1.9.23
compose.version=1.6.10-beta02
agp.version=8.2.2
deploy.version=0.1.0-SNAPSHOT

#Compose
org.jetbrains.compose.experimental.jscanvas.enabled=true
org.jetbrains.compose.experimental.wasm.enabled=true
org.jetbrains.compose.experimental.macos.enabled=true
compose.desktop.verbose=true
compose.useMavenLocal=false
Expand Down
4 changes: 3 additions & 1 deletion components/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "androidx-compose" }
androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-compose" }
androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-compose" }
androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-compose" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidx-compose" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidx-compose" }
10 changes: 10 additions & 0 deletions components/resources/demo/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ kotlin {
desktopMain.dependencies {
implementation(compose.desktop.common)
}
androidMain.dependencies {
implementation(libs.androidx.ui.tooling)
implementation(libs.androidx.ui.tooling.preview)
}

val nonAndroidMain by creating {
dependsOn(commonMain.get())
Expand All @@ -73,6 +77,12 @@ android {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.11"
}
}

compose.experimental {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,27 @@

package org.jetbrains.compose.resources.demo.shared

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.PreviewContextConfigurationEffect

@Composable
fun MainView() {
UseResources()
}

@Preview(showBackground = true)
@Composable
fun ImagesResPreview() {
ImagesRes(PaddingValues())
}

@OptIn(ExperimentalResourceApi::class)
@Preview(showBackground = true)
@Composable
fun FileResPreview() {
PreviewContextConfigurationEffect()
FileRes(PaddingValues())
}
8 changes: 1 addition & 7 deletions components/resources/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ plugins {
id("org.jetbrains.kotlinx.binary-compatibility-validator")
}

val composeVersion = extra["compose.version"] as String

kotlin {
jvm("desktop")
androidTarget {
Expand Down Expand Up @@ -187,6 +185,7 @@ android {
assets.srcDir("src/androidInstrumentedTest/assets")
}
named("test") { resources.srcDir(commonTestResources) }
named("main") { manifest.srcFile("src/androidMain/AndroidManifest.xml") }
}
}

Expand All @@ -202,11 +201,6 @@ apiValidation {
nonPublicMarkers.add("org.jetbrains.compose.resources.InternalResourceApi")
}

// adding it here to make sure skiko is unpacked and available in web tests
compose.experimental {
web.application {}
}

//utility task to generate CLDRPluralRuleLists.kt file by 'CLDRPluralRules/plurals.xml'
tasks.register<GeneratePluralRuleListsTask>("generatePluralRuleLists") {
val projectDir = project.layout.projectDirectory
Expand Down
13 changes: 13 additions & 0 deletions components/resources/library/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<provider
android:authorities="${applicationId}.resources.AndroidContextProvider"
android:name="org.jetbrains.compose.resources.AndroidContextProvider"
android:exported="false"
android:enabled="true">
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.jetbrains.compose.resources

import android.annotation.SuppressLint
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode

internal val androidContext get() = AndroidContextProvider.ANDROID_CONTEXT

/**
* The function configures the android context
* to be used for non-composable resource read functions
*
* e.g. `Res.readBytes(...)`
*
* Example usage:
* ```
* @Preview
* @Composable
* fun MyPreviewComponent() {
* PreviewContextConfigurationEffect()
* //...
* }
* ```
*/
@ExperimentalResourceApi
@Composable
fun PreviewContextConfigurationEffect() {
if (LocalInspectionMode.current) {
AndroidContextProvider.ANDROID_CONTEXT = LocalContext.current
}
}

//https://andretietz.com/2017/09/06/autoinitialise-android-library/
internal class AndroidContextProvider : ContentProvider() {
terrakok marked this conversation as resolved.
Show resolved Hide resolved
companion object {
@SuppressLint("StaticFieldLeak")
var ANDROID_CONTEXT: Context? = null
}

override fun onCreate(): Boolean {
ANDROID_CONTEXT = context
return true
}

override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? = null
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int = 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import androidx.compose.ui.text.font.*
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val path = remember(environment, resource) { resource.getResourceItemByEnvironment(environment).path }
return Font(path, LocalContext.current.assets, weight, style)
val assets = LocalContext.current.assets
return Font(path, assets, weight, style)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package org.jetbrains.compose.resources

import java.io.File
import android.content.res.AssetManager
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import java.io.FileNotFoundException
import java.io.InputStream

internal actual fun getPlatformResourceReader(): ResourceReader = object : ResourceReader {
private val assets: AssetManager by lazy {
val context = androidContext ?: error(
"Android context is not initialized. " +
"If it happens in the Preview mode then call PreviewContextConfigurationEffect() function."
)
context.assets
}

override suspend fun read(path: String): ByteArray {
val resource = getResourceAsStream(path)
return resource.readBytes()
Expand Down Expand Up @@ -33,39 +45,52 @@ internal actual fun getPlatformResourceReader(): ResourceReader = object : Resou
private fun InputStream.readBytes(byteArray: ByteArray, offset: Int, size: Int) {
var readBytes = 0
while (readBytes < size) {
val count = read(byteArray, offset + readBytes, size - readBytes)
val count = read(byteArray, offset + readBytes, size - readBytes)
if (count <= 0) break
readBytes += count
}
}

override fun getUri(path: String): String {
val classLoader = getClassLoader()
val resource = classLoader.getResource(path) ?: run {
//try to find a font in the android assets
if (File(path).isFontResource()) {
classLoader.getResource("assets/$path")
} else null
} ?: throw MissingResourceException(path)
return resource.toURI().toString()
val uri = if (assets.hasFile(path)) {
Uri.parse("file:///android_asset/$path")
} else {
val classLoader = getClassLoader()
val resource = classLoader.getResource(path) ?: throw MissingResourceException(path)
resource.toURI()
}
return uri.toString()
}

private fun getResourceAsStream(path: String): InputStream {
val classLoader = getClassLoader()
val resource = classLoader.getResourceAsStream(path) ?: run {
//try to find a font in the android assets
if (File(path).isFontResource()) {
classLoader.getResourceAsStream("assets/$path")
} else null
} ?: throw MissingResourceException(path)
return resource
}

private fun File.isFontResource(): Boolean {
return this.parentFile?.name.orEmpty().startsWith("font")
return try {
assets.open(path)
} catch (e: FileNotFoundException) {
val classLoader = getClassLoader()
classLoader.getResourceAsStream(path) ?: throw MissingResourceException(path)
}
}

private fun getClassLoader(): ClassLoader {
return this.javaClass.classLoader ?: error("Cannot find class loader")
}
}

private fun AssetManager.hasFile(path: String): Boolean {
var inputStream: InputStream? = null
val result = try {
inputStream = open(path)
true
} catch (e: FileNotFoundException) {
false
} finally {
inputStream?.close()
}
return result
}
}

internal actual val ProvidableCompositionLocal<ResourceReader>.currentOrPreview: ResourceReader
@Composable get() {
PreviewContextConfigurationEffect()
return current
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private val emptyImageBitmap: ImageBitmap by lazy { ImageBitmap(1, 1) }
*/
@Composable
fun imageResource(resource: DrawableResource): ImageBitmap {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val imageBitmap by rememberResourceState(resource, resourceReader, { emptyImageBitmap }) { env ->
val path = resource.getResourceItemByEnvironment(env).path
val cached = loadImage(path, resourceReader) {
Expand All @@ -78,7 +78,7 @@ private val emptyImageVector: ImageVector by lazy {
*/
@Composable
fun vectorResource(resource: DrawableResource): ImageVector {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val density = LocalDensity.current
val imageVector by rememberResourceState(resource, resourceReader, density, { emptyImageVector }) { env ->
val path = resource.getResourceItemByEnvironment(env).path
Expand All @@ -98,7 +98,7 @@ private val emptySvgPainter: Painter by lazy { BitmapPainter(emptyImageBitmap) }

@Composable
private fun svgPainter(resource: DrawableResource): Painter {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val density = LocalDensity.current
val svgPainter by rememberResourceState(resource, resourceReader, density, { emptySvgPainter }) { env ->
val path = resource.getResourceItemByEnvironment(env).path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class PluralStringResource
*/
@Composable
fun pluralStringResource(resource: PluralStringResource, quantity: Int): String {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val pluralStr by rememberResourceState(resource, quantity, { "" }) { env ->
loadPluralString(resource, quantity, resourceReader, env)
}
Expand Down Expand Up @@ -93,7 +93,7 @@ private suspend fun loadPluralString(
*/
@Composable
fun pluralStringResource(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val args = formatArgs.map { it.toString() }
val pluralStr by rememberResourceState(resource, quantity, args, { "" }) { env ->
loadPluralString(resource, quantity, args, resourceReader, env)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.jetbrains.compose.resources

import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf

class MissingResourceException(path: String) : Exception("Missing resource with path: $path")
Expand Down Expand Up @@ -34,3 +36,7 @@ internal val DefaultResourceReader = getPlatformResourceReader()

//ResourceReader provider will be overridden for tests
internal val LocalResourceReader = staticCompositionLocalOf { DefaultResourceReader }

//For an android preview we need to initialize the resource reader with the local context
internal expect val ProvidableCompositionLocal<ResourceReader>.currentOrPreview: ResourceReader
@Composable get
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class StringArrayResource
*/
@Composable
fun stringArrayResource(resource: StringArrayResource): List<String> {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val array by rememberResourceState(resource, { emptyList() }) { env ->
loadStringArray(resource, resourceReader, env)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class StringResource
*/
@Composable
fun stringResource(resource: StringResource): String {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val str by rememberResourceState(resource, { "" }) { env ->
loadString(resource, resourceReader, env)
}
Expand Down Expand Up @@ -75,7 +75,7 @@ private suspend fun loadString(
*/
@Composable
fun stringResource(resource: StringResource, vararg formatArgs: Any): String {
val resourceReader = LocalResourceReader.current
val resourceReader = LocalResourceReader.currentOrPreview
val args = formatArgs.map { it.toString() }
val str by rememberResourceState(resource, args, { "" }) { env ->
loadString(resource, args, resourceReader, env)
Expand Down
Loading
Loading