or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

font-resources.mdimage-drawable-resources.mdindex.mdplural-string-resources.mdresource-environment.mdstring-array-resources.mdstring-resources.md

resource-environment.mddocs/

0

# Resource Environment

1

2

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.

3

4

## Core Types

5

6

```kotlin { .api }

7

class ResourceEnvironment(

8

internal val language: LanguageQualifier,

9

internal val region: RegionQualifier,

10

internal val theme: ThemeQualifier,

11

internal val density: DensityQualifier

12

)

13

```

14

15

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.

16

17

## Environment Creation

18

19

### Composable Environment

20

21

```kotlin { .api }

22

@Composable

23

fun rememberResourceEnvironment(): ResourceEnvironment

24

```

25

26

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.

27

28

**Usage:**

29

```kotlin

30

@Composable

31

fun LocalizedContent() {

32

val environment = rememberResourceEnvironment()

33

34

// Environment automatically reflects:

35

// - Current system locale (language + region)

36

// - System theme (light/dark mode)

37

// - Screen density (MDPI, HDPI, XHDPI, etc.)

38

39

LaunchedEffect(environment) {

40

// React to environment changes

41

updateContentForEnvironment(environment)

42

}

43

}

44

```

45

46

### System Environment

47

48

```kotlin { .api }

49

fun getSystemResourceEnvironment(): ResourceEnvironment

50

```

51

52

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

53

54

**Usage:**

55

```kotlin

56

suspend fun loadSystemResources() {

57

val environment = getSystemResourceEnvironment()

58

val welcomeMessage = getString(environment, Res.string.welcome)

59

60

// Use environment for multiple resource loads

61

val appName = getString(environment, Res.string.app_name)

62

val version = getString(environment, Res.string.version_info)

63

}

64

65

class BackgroundService {

66

private var cachedEnvironment: ResourceEnvironment? = null

67

68

suspend fun processWithResources() {

69

val environment = cachedEnvironment ?: getSystemResourceEnvironment().also {

70

cachedEnvironment = it

71

}

72

73

val statusMessage = getString(environment, Res.string.processing_status)

74

updateNotification(statusMessage)

75

}

76

}

77

```

78

79

## Qualifiers

80

81

### Language Qualifier

82

83

```kotlin { .api }

84

class LanguageQualifier(val language: String) : Qualifier

85

```

86

87

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

88

89

**Common Language Codes:**

90

- `"en"` - English

91

- `"es"` - Spanish

92

- `"fr"` - French

93

- `"de"` - German

94

- `"ja"` - Japanese

95

- `"zh"` - Chinese

96

- `"ar"` - Arabic

97

98

### Region Qualifier

99

100

```kotlin { .api }

101

class RegionQualifier(val region: String) : Qualifier

102

```

103

104

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

105

106

**Common Region Codes:**

107

- `"US"` - United States

108

- `"GB"` - United Kingdom

109

- `"CA"` - Canada

110

- `"FR"` - France

111

- `"DE"` - Germany

112

- `"JP"` - Japan

113

- `"CN"` - China

114

115

### Theme Qualifier

116

117

```kotlin { .api }

118

enum class ThemeQualifier : Qualifier {

119

LIGHT,

120

DARK;

121

122

companion object {

123

fun selectByValue(isDark: Boolean): ThemeQualifier

124

}

125

}

126

```

127

128

Represents the system theme preference for light or dark mode.

129

130

**Usage:**

131

```kotlin

132

val currentTheme = ThemeQualifier.selectByValue(isSystemInDarkTheme())

133

134

// Manual theme selection

135

val lightTheme = ThemeQualifier.LIGHT

136

val darkTheme = ThemeQualifier.DARK

137

```

138

139

### Density Qualifier

140

141

```kotlin { .api }

142

enum class DensityQualifier(val dpi: Int) : Qualifier {

143

LDPI(120), // Low density

144

MDPI(160), // Medium density (baseline)

145

HDPI(240), // High density

146

XHDPI(320), // Extra high density

147

XXHDPI(480), // Extra extra high density

148

XXXHDPI(640); // Extra extra extra high density

149

150

companion object {

151

fun selectByValue(dpi: Int): DensityQualifier

152

fun selectByDensity(density: Float): DensityQualifier

153

}

154

}

155

```

156

157

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

158

159

**Density Selection:**

160

```kotlin

161

// From DPI value

162

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

163

164

// From density multiplier

165

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

166

```

167

168

## Resource Selection Algorithm

169

170

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

171

172

### Priority Order

173

1. **Locale matching** (language + region)

174

- Exact match (language + region)

175

- Language match (ignoring region)

176

- Default (no locale qualifiers)

177

178

2. **Theme matching**

179

- Exact theme match

180

- Default (no theme qualifier)

181

182

3. **Density matching**

183

- Exact or higher density match (preferred)

184

- Lower density with upscaling

185

- Default (MDPI assumed)

186

- LDPI as last resort

187

188

### Selection Examples

189

190

**Available Resources:**

191

```

192

drawable/icon.png # Default (MDPI)

193

drawable-hdpi/icon.png # High density

194

drawable-xhdpi/icon.png # Extra high density

195

drawable-night/icon.png # Dark theme default

196

drawable-night-hdpi/icon.png # Dark theme high density

197

```

198

199

**Selection for XHDPI + Dark Theme:**

200

1. `drawable-night-xhdpi/icon.png` (exact match) - **Not available**

201

2. `drawable-night-hdpi/icon.png` (theme + lower density) - **Selected**

202

203

### Custom Environment Creation

204

205

```kotlin

206

// Create specific environment for testing

207

val testEnvironment = ResourceEnvironment(

208

language = LanguageQualifier("es"),

209

region = RegionQualifier("MX"),

210

theme = ThemeQualifier.DARK,

211

density = DensityQualifier.XXHDPI

212

)

213

214

// Use for resource loading

215

suspend fun loadSpanishResources(): String {

216

return getString(testEnvironment, Res.string.welcome_message)

217

}

218

```

219

220

## Environment Monitoring

221

222

### Compose Integration

223

224

```kotlin

225

@Composable

226

fun EnvironmentAwareContent() {

227

val environment = rememberResourceEnvironment()

228

229

// React to specific environment changes

230

LaunchedEffect(environment.theme) {

231

// Theme changed

232

updateThemeSpecificContent(environment.theme)

233

}

234

235

LaunchedEffect(environment.language, environment.region) {

236

// Locale changed

237

updateLocalizedContent(environment.language, environment.region)

238

}

239

}

240

```

241

242

### Environment Comparison

243

244

```kotlin

245

fun environmentChanged(old: ResourceEnvironment, new: ResourceEnvironment): Boolean {

246

return old.language != new.language ||

247

old.region != new.region ||

248

old.theme != new.theme ||

249

old.density != new.density

250

}

251

252

// Track specific changes

253

fun localeChanged(old: ResourceEnvironment, new: ResourceEnvironment): Boolean {

254

return old.language != new.language || old.region != new.region

255

}

256

```

257

258

## Platform-Specific Behavior

259

260

### Android

261

- Automatic locale detection from system settings

262

- Theme detection from `Configuration.uiMode`

263

- Density detection from display metrics

264

- Supports all Android density buckets

265

266

### Desktop (JVM)

267

- Locale from `Locale.getDefault()`

268

- Theme detection from system preferences (when available)

269

- Density simulation for high-DPI displays

270

- Cross-platform consistency

271

272

### iOS

273

- Locale from `NSLocale.currentLocale`

274

- Theme detection from `UITraitCollection`

275

- Density mapping to Retina display scales

276

- Native iOS locale and theme integration

277

278

### Web (JS/Wasm)

279

- Browser locale detection from `navigator.language`

280

- Media query-based theme detection

281

- Device pixel ratio for density

282

- Progressive enhancement for older browsers

283

284

## Advanced Usage

285

286

### Environment Caching

287

288

```kotlin

289

class EnvironmentCache {

290

private var cachedEnvironment: ResourceEnvironment? = null

291

private var lastCheck: Long = 0

292

private val cacheTimeout = 5000L // 5 seconds

293

294

fun getCurrentEnvironment(): ResourceEnvironment {

295

val now = System.currentTimeMillis()

296

if (cachedEnvironment == null || now - lastCheck > cacheTimeout) {

297

cachedEnvironment = getSystemResourceEnvironment()

298

lastCheck = now

299

}

300

return cachedEnvironment!!

301

}

302

}

303

```

304

305

### Multi-Environment Resource Loading

306

307

```kotlin

308

suspend fun loadMultiLanguageContent(): Map<String, String> {

309

val languages = listOf("en", "es", "fr", "de")

310

val content = mutableMapOf<String, String>()

311

312

languages.forEach { lang ->

313

val environment = ResourceEnvironment(

314

LanguageQualifier(lang),

315

RegionQualifier(""),

316

ThemeQualifier.LIGHT,

317

DensityQualifier.MDPI

318

)

319

content[lang] = getString(environment, Res.string.app_description)

320

}

321

322

return content

323

}

324

```

325

326

### Environment-Specific Resource Preloading

327

328

```kotlin

329

class ResourcePreloader {

330

suspend fun preloadForEnvironment(environment: ResourceEnvironment) {

331

// Preload commonly used resources for this environment

332

val commonResources = listOf(

333

Res.string.app_name,

334

Res.string.loading,

335

Res.string.error_message,

336

Res.drawable.app_icon

337

)

338

339

commonResources.forEach { resource ->

340

when (resource) {

341

is StringResource -> getString(environment, resource)

342

is DrawableResource -> getDrawableResourceBytes(environment, resource)

343

}

344

}

345

}

346

}

347

```

348

349

## Testing and Development

350

351

### Environment Mocking

352

353

```kotlin

354

// Create test environments for different scenarios

355

val testEnvironments = mapOf(

356

"US English" to ResourceEnvironment(

357

LanguageQualifier("en"), RegionQualifier("US"),

358

ThemeQualifier.LIGHT, DensityQualifier.XHDPI

359

),

360

"Spanish Mexico" to ResourceEnvironment(

361

LanguageQualifier("es"), RegionQualifier("MX"),

362

ThemeQualifier.DARK, DensityQualifier.XXHDPI

363

),

364

"French Canada" to ResourceEnvironment(

365

LanguageQualifier("fr"), RegionQualifier("CA"),

366

ThemeQualifier.LIGHT, DensityQualifier.HDPI

367

)

368

)

369

370

// Test resource selection

371

testEnvironments.forEach { (name, environment) ->

372

val text = getString(environment, Res.string.welcome_message)

373

println("$name: $text")

374

}

375

```

376

377

### Environment Validation

378

379

```kotlin

380

fun validateResourceCoverage(

381

environments: List<ResourceEnvironment>,

382

resources: List<StringResource>

383

) {

384

environments.forEach { environment ->

385

resources.forEach { resource ->

386

try {

387

val text = getString(environment, resource)

388

println("✓ ${resource.key} available for $environment")

389

} catch (e: Exception) {

390

println("✗ ${resource.key} missing for $environment: ${e.message}")

391

}

392

}

393

}

394

}

395

```

396

397

## Best Practices

398

399

1. **Cache environment objects** when used frequently:

400

```kotlin

401

@Composable

402

fun OptimizedContent() {

403

val environment = rememberResourceEnvironment()

404

// Environment is automatically cached and updated by Compose

405

}

406

```

407

408

2. **Use appropriate environment access**:

409

```kotlin

410

// In Composables - use rememberResourceEnvironment()

411

@Composable

412

fun MyComposable() {

413

val env = rememberResourceEnvironment()

414

}

415

416

// Outside Composables - use getSystemResourceEnvironment() sparingly

417

suspend fun backgroundTask() {

418

val env = getSystemResourceEnvironment()

419

}

420

```

421

422

3. **Test with multiple environments**:

423

- Different locales (language + region combinations)

424

- Both light and dark themes

425

- Various screen densities

426

- Edge cases (missing resources, fallbacks)

427

428

4. **Handle environment changes gracefully**:

429

```kotlin

430

@Composable

431

fun AdaptiveContent() {

432

val environment = rememberResourceEnvironment()

433

434

// Content adapts automatically to environment changes

435

val title = stringResource(Res.string.title)

436

val icon = painterResource(Res.drawable.icon)

437

438

// Resources are automatically reloaded when environment changes

439

}

440

```

441

442

5. **Provide appropriate fallbacks**:

443

- Always include default resources (no qualifiers)

444

- Test resource selection with missing variants

445

- Handle graceful degradation for unsupported configurations