Konstantin
11 months ago
committed by
GitHub
43 changed files with 784 additions and 251 deletions
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@ -1,14 +1,14 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.ui.platform.LocalContext |
||||
import androidx.compose.ui.text.font.Font |
||||
import androidx.compose.ui.text.font.FontStyle |
||||
import androidx.compose.ui.text.font.FontWeight |
||||
import androidx.compose.ui.text.font.* |
||||
|
||||
@ExperimentalResourceApi |
||||
@Composable |
||||
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font { |
||||
val path = resource.getPathByEnvironment() |
||||
val environment = rememberEnvironment() |
||||
val path = remember(environment) { resource.getPathByEnvironment(environment) } |
||||
return Font(path, LocalContext.current.assets, weight, style) |
||||
} |
@ -0,0 +1,18 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import android.content.res.Configuration |
||||
import android.content.res.Resources |
||||
import java.util.* |
||||
|
||||
internal actual fun getResourceEnvironment(): ResourceEnvironment { |
||||
val locale = Locale.getDefault() |
||||
val configuration = Resources.getSystem().configuration |
||||
val isDarkTheme = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES |
||||
val dpi = configuration.densityDpi |
||||
return ResourceEnvironment( |
||||
language = LanguageQualifier(locale.language), |
||||
region = RegionQualifier(locale.country), |
||||
theme = ThemeQualifier.selectByValue(isDarkTheme), |
||||
density = DensityQualifier.selectByValue(dpi) |
||||
) |
||||
} |
@ -1,14 +0,0 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.State |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import kotlinx.coroutines.runBlocking |
||||
|
||||
@Composable |
||||
internal actual fun <T> rememberState(key: Any, getDefault: () -> T, block: suspend () -> T): State<T> = remember(key) { |
||||
mutableStateOf( |
||||
runBlocking { block() } |
||||
) |
||||
} |
@ -0,0 +1,18 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import androidx.compose.runtime.* |
||||
import kotlinx.coroutines.runBlocking |
||||
|
||||
@Composable |
||||
internal actual fun <T> rememberResourceState( |
||||
key: Any, |
||||
getDefault: () -> T, |
||||
block: suspend (ResourceEnvironment) -> T |
||||
): State<T> { |
||||
val environment = rememberEnvironment() |
||||
return remember(key, environment) { |
||||
mutableStateOf( |
||||
runBlocking { block(environment) } |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
interface Qualifier |
||||
|
||||
data class LanguageQualifier( |
||||
val language: String |
||||
) : Qualifier |
||||
|
||||
data class RegionQualifier( |
||||
val region: String |
||||
) : Qualifier |
||||
|
||||
enum class ThemeQualifier : Qualifier { |
||||
LIGHT, |
||||
DARK; |
||||
|
||||
companion object { |
||||
fun selectByValue(isDark: Boolean) = |
||||
if (isDark) DARK else LIGHT |
||||
} |
||||
} |
||||
|
||||
//https://developer.android.com/guide/topics/resources/providing-resources |
||||
enum class DensityQualifier(val dpi: Int) : Qualifier { |
||||
LDPI(120), |
||||
MDPI(160), |
||||
HDPI(240), |
||||
XHDPI(320), |
||||
XXHDPI(480), |
||||
XXXHDPI(640); |
||||
|
||||
companion object { |
||||
fun selectByValue(dpi: Int) = when { |
||||
dpi <= LDPI.dpi -> LDPI |
||||
dpi <= MDPI.dpi -> MDPI |
||||
dpi <= HDPI.dpi -> HDPI |
||||
dpi <= XHDPI.dpi -> XHDPI |
||||
dpi <= XXHDPI.dpi -> XXHDPI |
||||
else -> XXXHDPI |
||||
} |
||||
fun selectByDensity(density: Float) = when { |
||||
density <= 0.75 -> LDPI |
||||
density <= 1.0 -> MDPI |
||||
density <= 1.33 -> HDPI |
||||
density <= 2.0 -> XHDPI |
||||
density <= 3.0 -> XXHDPI |
||||
else -> XXXHDPI |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,73 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme |
||||
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 |
||||
) |
||||
|
||||
@Composable |
||||
internal fun rememberEnvironment(): ResourceEnvironment { |
||||
val composeLocale = Locale.current |
||||
val composeTheme = isSystemInDarkTheme() |
||||
val composeDensity = LocalDensity.current |
||||
|
||||
//cache ResourceEnvironment unless compose environment is changed |
||||
return remember(composeLocale, composeTheme, composeDensity) { |
||||
ResourceEnvironment( |
||||
LanguageQualifier(composeLocale.language), |
||||
RegionQualifier(composeLocale.region), |
||||
ThemeQualifier.selectByValue(composeTheme), |
||||
DensityQualifier.selectByDensity(composeDensity.density) |
||||
) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Provides the resource environment for non-composable access to string resources. |
||||
* It is an expensive operation! Don't use it in composable functions with no cache! |
||||
*/ |
||||
internal expect fun getResourceEnvironment(): ResourceEnvironment |
||||
|
||||
internal fun Resource.getPathByEnvironment(environment: ResourceEnvironment): String { |
||||
//Priority of environments: https://developer.android.com/guide/topics/resources/providing-resources#table2 |
||||
items.toList() |
||||
.filterBy(environment.language) |
||||
.also { if (it.size == 1) return it.first().path } |
||||
.filterBy(environment.region) |
||||
.also { if (it.size == 1) return it.first().path } |
||||
.filterBy(environment.theme) |
||||
.also { if (it.size == 1) return it.first().path } |
||||
.filterBy(environment.density) |
||||
.also { if (it.size == 1) return it.first().path } |
||||
.let { items -> |
||||
if (items.isEmpty()) { |
||||
error("Resource with ID='$id' not found") |
||||
} else { |
||||
error("Resource with ID='$id' has more than one file: ${items.joinToString { it.path }}") |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun List<ResourceItem>.filterBy(qualifier: Qualifier): List<ResourceItem> { |
||||
//Android has a slightly different algorithm, |
||||
//but it provides the same result: https://developer.android.com/guide/topics/resources/providing-resources#BestMatch |
||||
|
||||
//filter items with the requested qualifier |
||||
val withQualifier = filter { item -> |
||||
item.qualifiers.any { it == qualifier } |
||||
} |
||||
|
||||
if (withQualifier.isNotEmpty()) return withQualifier |
||||
|
||||
//items with no requested qualifier type (default) |
||||
return filter { item -> |
||||
item.qualifiers.none { it::class == qualifier::class } |
||||
} |
||||
} |
@ -0,0 +1,19 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import org.jetbrains.skiko.SystemTheme |
||||
import org.jetbrains.skiko.currentSystemTheme |
||||
import java.awt.Toolkit |
||||
import java.util.* |
||||
|
||||
internal actual fun getResourceEnvironment(): ResourceEnvironment { |
||||
val locale = Locale.getDefault() |
||||
//FIXME: don't use skiko internals |
||||
val isDarkTheme = currentSystemTheme == SystemTheme.DARK |
||||
val dpi = Toolkit.getDefaultToolkit().screenResolution |
||||
return ResourceEnvironment( |
||||
language = LanguageQualifier(locale.language), |
||||
region = RegionQualifier(locale.country), |
||||
theme = ThemeQualifier.selectByValue(isDarkTheme), |
||||
density = DensityQualifier.selectByValue(dpi) |
||||
) |
||||
} |
@ -0,0 +1,21 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import platform.Foundation.* |
||||
import platform.UIKit.UIScreen |
||||
import platform.UIKit.UIUserInterfaceStyle |
||||
|
||||
internal actual fun getResourceEnvironment(): ResourceEnvironment { |
||||
val locale = NSLocale.currentLocale() |
||||
|
||||
val mainScreen = UIScreen.mainScreen |
||||
val isDarkTheme = mainScreen.traitCollection().userInterfaceStyle == UIUserInterfaceStyle.UIUserInterfaceStyleDark |
||||
|
||||
//there is no an API to get a physical screen size and calculate a real DPI |
||||
val density = mainScreen.scale.toFloat() |
||||
return ResourceEnvironment( |
||||
language = LanguageQualifier(locale.languageCode), |
||||
region = RegionQualifier(locale.regionCode.orEmpty()), |
||||
theme = ThemeQualifier.selectByValue(isDarkTheme), |
||||
density = DensityQualifier.selectByDensity(density) |
||||
) |
||||
} |
@ -0,0 +1,23 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import kotlinx.browser.window |
||||
|
||||
private external class Intl { |
||||
class Locale(locale: String) { |
||||
val language: String |
||||
val region: String |
||||
} |
||||
} |
||||
|
||||
internal actual fun getResourceEnvironment(): ResourceEnvironment { |
||||
val locale = Intl.Locale(window.navigator.language) |
||||
val isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)").matches |
||||
//96 - standard browser DPI https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio |
||||
val dpi: Int = (window.devicePixelRatio * 96).toInt() |
||||
return ResourceEnvironment( |
||||
language = LanguageQualifier(locale.language), |
||||
region = RegionQualifier(locale.region), |
||||
theme = ThemeQualifier.selectByValue(isDarkTheme), |
||||
density = DensityQualifier.selectByValue(dpi) |
||||
) |
||||
} |
@ -0,0 +1,32 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import kotlinx.cinterop.* |
||||
import platform.AppKit.NSScreen |
||||
import platform.CoreGraphics.CGDisplayPixelsWide |
||||
import platform.CoreGraphics.CGDisplayScreenSize |
||||
import platform.Foundation.* |
||||
|
||||
internal actual fun getResourceEnvironment(): ResourceEnvironment { |
||||
val locale = NSLocale.currentLocale() |
||||
val isDarkTheme = NSUserDefaults.standardUserDefaults.stringForKey("AppleInterfaceStyle") == "Dark" |
||||
|
||||
val dpi = NSScreen.mainScreen?.let { screen -> |
||||
val backingScaleFactor = screen.backingScaleFactor |
||||
val screenNumber = interpretObjCPointer<NSNumber>( |
||||
screen.deviceDescription["NSScreenNumber"].objcPtr() |
||||
).unsignedIntValue |
||||
|
||||
val displaySizePX = CGDisplayPixelsWide(screenNumber).toFloat() * backingScaleFactor |
||||
val displaySizeMM = CGDisplayScreenSize(screenNumber).useContents { width } |
||||
|
||||
//1 inch = 25.4 mm |
||||
((displaySizePX / displaySizeMM) * 25.4f).toInt() |
||||
} ?: 0 |
||||
|
||||
return ResourceEnvironment( |
||||
language = LanguageQualifier(locale.languageCode), |
||||
region = RegionQualifier(locale.regionCode.orEmpty()), |
||||
theme = ThemeQualifier.selectByValue(isDarkTheme), |
||||
density = DensityQualifier.selectByValue(dpi) |
||||
) |
||||
} |
@ -0,0 +1,23 @@
|
||||
package org.jetbrains.compose.resources |
||||
|
||||
import kotlinx.browser.window |
||||
|
||||
private external class Intl { |
||||
class Locale(locale: String) { |
||||
val language: String |
||||
val region: String |
||||
} |
||||
} |
||||
|
||||
internal actual fun getResourceEnvironment(): ResourceEnvironment { |
||||
val locale = Intl.Locale(window.navigator.language) |
||||
val isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)").matches |
||||
//96 - standard browser DPI https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio |
||||
val dpi: Int = (window.devicePixelRatio * 96).toInt() |
||||
return ResourceEnvironment( |
||||
language = LanguageQualifier(locale.language), |
||||
region = RegionQualifier(locale.region), |
||||
theme = ThemeQualifier.selectByValue(isDarkTheme), |
||||
density = DensityQualifier.selectByValue(dpi) |
||||
) |
||||
} |
@ -1,84 +1,123 @@
|
||||
package app.group.generated.resources |
||||
|
||||
import org.jetbrains.compose.resources.DrawableResource |
||||
import org.jetbrains.compose.resources.FontResource |
||||
import org.jetbrains.compose.resources.ImageResource |
||||
import org.jetbrains.compose.resources.LanguageQualifier |
||||
import org.jetbrains.compose.resources.RegionQualifier |
||||
import org.jetbrains.compose.resources.ResourceItem |
||||
import org.jetbrains.compose.resources.StringResource |
||||
import org.jetbrains.compose.resources.ThemeQualifier |
||||
|
||||
internal object Res { |
||||
public object fonts { |
||||
public val emptyfont: FontResource = FontResource( |
||||
"FONT:emptyfont", |
||||
public object drawable { |
||||
public val _3_strange_name: DrawableResource = DrawableResource( |
||||
"drawable:_3_strange_name", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/fonts/emptyFont.otf"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/drawable/3-strange-name.xml" |
||||
), |
||||
) |
||||
) |
||||
} |
||||
|
||||
public object images { |
||||
public val _3_strange_name: ImageResource = ImageResource( |
||||
"IMAGE:_3_strange_name", |
||||
public val vector: DrawableResource = DrawableResource( |
||||
"drawable:vector", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/images/3-strange-name.xml"), |
||||
ResourceItem( |
||||
setOf(LanguageQualifier("au"), RegionQualifier("US"), ), |
||||
"composeRes/drawable-au-rUS/vector.xml" |
||||
), |
||||
ResourceItem( |
||||
setOf(ThemeQualifier.DARK, LanguageQualifier("ge"), ), |
||||
"composeRes/drawable-dark-ge/vector.xml" |
||||
), |
||||
ResourceItem( |
||||
setOf(LanguageQualifier("en"), ), |
||||
"composeRes/drawable-en/vector.xml" |
||||
), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/drawable/vector.xml" |
||||
), |
||||
) |
||||
) |
||||
|
||||
public val vector: ImageResource = ImageResource( |
||||
"IMAGE:vector", |
||||
public val vector_2: DrawableResource = DrawableResource( |
||||
"drawable:vector_2", |
||||
setOf( |
||||
ResourceItem(setOf("q1", "q2"), "composeRes/images-q1-q2/vector.xml"), |
||||
ResourceItem(setOf("q1"), "composeRes/images-q1/vector.xml"), |
||||
ResourceItem(setOf("q2"), "composeRes/images-q2/vector.xml"), |
||||
ResourceItem(setOf(), "composeRes/images/vector.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/drawable/vector_2.xml" |
||||
), |
||||
) |
||||
) |
||||
} |
||||
|
||||
public val vector_2: ImageResource = ImageResource( |
||||
"IMAGE:vector_2", |
||||
public object font { |
||||
public val emptyfont: FontResource = FontResource( |
||||
"font:emptyfont", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/images/vector_2.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/font/emptyFont.otf" |
||||
), |
||||
) |
||||
) |
||||
} |
||||
|
||||
public object strings { |
||||
public object string { |
||||
public val app_name: StringResource = StringResource( |
||||
"STRING:app_name", |
||||
"string:app_name", |
||||
"app_name", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/values/strings.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/values/strings.xml" |
||||
), |
||||
) |
||||
) |
||||
|
||||
public val hello: StringResource = StringResource( |
||||
"STRING:hello", |
||||
"string:hello", |
||||
"hello", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/values/strings.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/values/strings.xml" |
||||
), |
||||
) |
||||
) |
||||
|
||||
public val multi_line: StringResource = StringResource( |
||||
"STRING:multi_line", |
||||
"string:multi_line", |
||||
"multi_line", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/values/strings.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/values/strings.xml" |
||||
), |
||||
) |
||||
) |
||||
|
||||
public val str_arr: StringResource = StringResource( |
||||
"STRING:str_arr", |
||||
"string:str_arr", |
||||
"str_arr", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/values/strings.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/values/strings.xml" |
||||
), |
||||
) |
||||
) |
||||
|
||||
public val str_template: StringResource = StringResource( |
||||
"STRING:str_template", |
||||
"string:str_template", |
||||
"str_template", |
||||
setOf( |
||||
ResourceItem(setOf(), "composeRes/values/strings.xml"), |
||||
ResourceItem( |
||||
setOf(), |
||||
"composeRes/values/strings.xml" |
||||
), |
||||
) |
||||
) |
||||
} |
||||
|
Loading…
Reference in new issue