CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-compose-components--components-resources

Resource management library for Compose Multiplatform applications providing type-safe access to images, strings, fonts, and drawable assets across all platforms.

Pending
Overview
Eval results
Files

resource-environment.mddocs/

Resource Environment

The Resource Environment system manages the context for resource selection, including locale (language and region), theme (light/dark), and screen density. This enables automatic selection of the most appropriate resource variant for the current device and user preferences.

Core Types

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

The ResourceEnvironment encapsulates all the factors that influence resource selection. It provides a complete context for determining which specific resource variant to load from the available options.

Environment Creation

Composable Environment

@Composable
fun rememberResourceEnvironment(): ResourceEnvironment

Creates and remembers a ResourceEnvironment based on the current Compose context. This automatically tracks changes in system locale, theme, and density, updating resources as needed.

Usage:

@Composable
fun LocalizedContent() {
    val environment = rememberResourceEnvironment()
    
    // Environment automatically reflects:
    // - Current system locale (language + region)
    // - System theme (light/dark mode)
    // - Screen density (MDPI, HDPI, XHDPI, etc.)
    
    LaunchedEffect(environment) {
        // React to environment changes
        updateContentForEnvironment(environment)
    }
}

System Environment

fun getSystemResourceEnvironment(): ResourceEnvironment

Provides the resource environment for non-Composable contexts. This is an expensive operation and should be used sparingly, preferably with caching.

Usage:

suspend fun loadSystemResources() {
    val environment = getSystemResourceEnvironment()
    val welcomeMessage = getString(environment, Res.string.welcome)
    
    // Use environment for multiple resource loads
    val appName = getString(environment, Res.string.app_name)
    val version = getString(environment, Res.string.version_info)
}

class BackgroundService {
    private var cachedEnvironment: ResourceEnvironment? = null
    
    suspend fun processWithResources() {
        val environment = cachedEnvironment ?: getSystemResourceEnvironment().also {
            cachedEnvironment = it
        }
        
        val statusMessage = getString(environment, Res.string.processing_status)
        updateNotification(statusMessage)
    }
}

Qualifiers

Language Qualifier

class LanguageQualifier(val language: String) : Qualifier

Represents the language component of the locale (ISO 639-1 language codes).

Common Language Codes:

  • "en" - English
  • "es" - Spanish
  • "fr" - French
  • "de" - German
  • "ja" - Japanese
  • "zh" - Chinese
  • "ar" - Arabic

Region Qualifier

class RegionQualifier(val region: String) : Qualifier

Represents the region/country component of the locale (ISO 3166-1 alpha-2 country codes).

Common Region Codes:

  • "US" - United States
  • "GB" - United Kingdom
  • "CA" - Canada
  • "FR" - France
  • "DE" - Germany
  • "JP" - Japan
  • "CN" - China

Theme Qualifier

enum class ThemeQualifier : Qualifier {
    LIGHT,
    DARK;
    
    companion object {
        fun selectByValue(isDark: Boolean): ThemeQualifier
    }
}

Represents the system theme preference for light or dark mode.

Usage:

val currentTheme = ThemeQualifier.selectByValue(isSystemInDarkTheme())

// Manual theme selection
val lightTheme = ThemeQualifier.LIGHT
val darkTheme = ThemeQualifier.DARK

Density Qualifier

enum class DensityQualifier(val dpi: Int) : Qualifier {
    LDPI(120),    // Low density
    MDPI(160),    // Medium density (baseline)
    HDPI(240),    // High density
    XHDPI(320),   // Extra high density
    XXHDPI(480),  // Extra extra high density
    XXXHDPI(640); // Extra extra extra high density
    
    companion object {
        fun selectByValue(dpi: Int): DensityQualifier
        fun selectByDensity(density: Float): DensityQualifier
    }
}

Represents the screen density for selecting appropriate image and layout resources.

Density Selection:

// From DPI value
val density1 = DensityQualifier.selectByValue(320) // Returns XHDPI

// From density multiplier
val density2 = DensityQualifier.selectByDensity(2.0f) // Returns XHDPI

Resource Selection Algorithm

The library uses a sophisticated algorithm to select the best matching resource:

Priority Order

  1. Locale matching (language + region)

    • Exact match (language + region)
    • Language match (ignoring region)
    • Default (no locale qualifiers)
  2. Theme matching

    • Exact theme match
    • Default (no theme qualifier)
  3. Density matching

    • Exact or higher density match (preferred)
    • Lower density with upscaling
    • Default (MDPI assumed)
    • LDPI as last resort

Selection Examples

Available Resources:

drawable/icon.png              # Default (MDPI)
drawable-hdpi/icon.png         # High density
drawable-xhdpi/icon.png        # Extra high density
drawable-night/icon.png        # Dark theme default
drawable-night-hdpi/icon.png   # Dark theme high density

Selection for XHDPI + Dark Theme:

  1. drawable-night-xhdpi/icon.png (exact match) - Not available
  2. drawable-night-hdpi/icon.png (theme + lower density) - Selected

Custom Environment Creation

// Create specific environment for testing
val testEnvironment = ResourceEnvironment(
    language = LanguageQualifier("es"),
    region = RegionQualifier("MX"),
    theme = ThemeQualifier.DARK,
    density = DensityQualifier.XXHDPI
)

// Use for resource loading
suspend fun loadSpanishResources(): String {
    return getString(testEnvironment, Res.string.welcome_message)
}

Environment Monitoring

Compose Integration

@Composable
fun EnvironmentAwareContent() {
    val environment = rememberResourceEnvironment()
    
    // React to specific environment changes
    LaunchedEffect(environment.theme) {
        // Theme changed
        updateThemeSpecificContent(environment.theme)
    }
    
    LaunchedEffect(environment.language, environment.region) {
        // Locale changed
        updateLocalizedContent(environment.language, environment.region)
    }
}

Environment Comparison

fun environmentChanged(old: ResourceEnvironment, new: ResourceEnvironment): Boolean {
    return old.language != new.language ||
           old.region != new.region ||
           old.theme != new.theme ||
           old.density != new.density
}

// Track specific changes
fun localeChanged(old: ResourceEnvironment, new: ResourceEnvironment): Boolean {
    return old.language != new.language || old.region != new.region
}

Platform-Specific Behavior

Android

  • Automatic locale detection from system settings
  • Theme detection from Configuration.uiMode
  • Density detection from display metrics
  • Supports all Android density buckets

Desktop (JVM)

  • Locale from Locale.getDefault()
  • Theme detection from system preferences (when available)
  • Density simulation for high-DPI displays
  • Cross-platform consistency

iOS

  • Locale from NSLocale.currentLocale
  • Theme detection from UITraitCollection
  • Density mapping to Retina display scales
  • Native iOS locale and theme integration

Web (JS/Wasm)

  • Browser locale detection from navigator.language
  • Media query-based theme detection
  • Device pixel ratio for density
  • Progressive enhancement for older browsers

Advanced Usage

Environment Caching

class EnvironmentCache {
    private var cachedEnvironment: ResourceEnvironment? = null
    private var lastCheck: Long = 0
    private val cacheTimeout = 5000L // 5 seconds
    
    fun getCurrentEnvironment(): ResourceEnvironment {
        val now = System.currentTimeMillis()
        if (cachedEnvironment == null || now - lastCheck > cacheTimeout) {
            cachedEnvironment = getSystemResourceEnvironment()
            lastCheck = now
        }
        return cachedEnvironment!!
    }
}

Multi-Environment Resource Loading

suspend fun loadMultiLanguageContent(): Map<String, String> {
    val languages = listOf("en", "es", "fr", "de")
    val content = mutableMapOf<String, String>()
    
    languages.forEach { lang ->
        val environment = ResourceEnvironment(
            LanguageQualifier(lang),
            RegionQualifier(""),
            ThemeQualifier.LIGHT,
            DensityQualifier.MDPI
        )
        content[lang] = getString(environment, Res.string.app_description)
    }
    
    return content
}

Environment-Specific Resource Preloading

class ResourcePreloader {
    suspend fun preloadForEnvironment(environment: ResourceEnvironment) {
        // Preload commonly used resources for this environment
        val commonResources = listOf(
            Res.string.app_name,
            Res.string.loading,
            Res.string.error_message,
            Res.drawable.app_icon
        )
        
        commonResources.forEach { resource ->
            when (resource) {
                is StringResource -> getString(environment, resource)
                is DrawableResource -> getDrawableResourceBytes(environment, resource)
            }
        }
    }
}

Testing and Development

Environment Mocking

// Create test environments for different scenarios
val testEnvironments = mapOf(
    "US English" to ResourceEnvironment(
        LanguageQualifier("en"), RegionQualifier("US"),
        ThemeQualifier.LIGHT, DensityQualifier.XHDPI
    ),
    "Spanish Mexico" to ResourceEnvironment(
        LanguageQualifier("es"), RegionQualifier("MX"),
        ThemeQualifier.DARK, DensityQualifier.XXHDPI
    ),
    "French Canada" to ResourceEnvironment(
        LanguageQualifier("fr"), RegionQualifier("CA"),
        ThemeQualifier.LIGHT, DensityQualifier.HDPI
    )
)

// Test resource selection
testEnvironments.forEach { (name, environment) ->
    val text = getString(environment, Res.string.welcome_message)
    println("$name: $text")
}

Environment Validation

fun validateResourceCoverage(
    environments: List<ResourceEnvironment>,
    resources: List<StringResource>
) {
    environments.forEach { environment ->
        resources.forEach { resource ->
            try {
                val text = getString(environment, resource)
                println("✓ ${resource.key} available for $environment")
            } catch (e: Exception) {
                println("✗ ${resource.key} missing for $environment: ${e.message}")
            }
        }
    }
}

Best Practices

  1. Cache environment objects when used frequently:

    @Composable
    fun OptimizedContent() {
        val environment = rememberResourceEnvironment()
        // Environment is automatically cached and updated by Compose
    }
  2. Use appropriate environment access:

    // In Composables - use rememberResourceEnvironment()
    @Composable
    fun MyComposable() {
        val env = rememberResourceEnvironment()
    }
    
    // Outside Composables - use getSystemResourceEnvironment() sparingly
    suspend fun backgroundTask() {
        val env = getSystemResourceEnvironment()
    }
  3. Test with multiple environments:

    • Different locales (language + region combinations)
    • Both light and dark themes
    • Various screen densities
    • Edge cases (missing resources, fallbacks)
  4. Handle environment changes gracefully:

    @Composable
    fun AdaptiveContent() {
        val environment = rememberResourceEnvironment()
        
        // Content adapts automatically to environment changes
        val title = stringResource(Res.string.title)
        val icon = painterResource(Res.drawable.icon)
        
        // Resources are automatically reloaded when environment changes
    }
  5. Provide appropriate fallbacks:

    • Always include default resources (no qualifiers)
    • Test resource selection with missing variants
    • Handle graceful degradation for unsupported configurations

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-compose-components--components-resources

docs

font-resources.md

image-drawable-resources.md

index.md

plural-string-resources.md

resource-environment.md

string-array-resources.md

string-resources.md

tile.json