Skip to content

Commit

Permalink
[resources] Update resource filtering and add density-based lookup
Browse files Browse the repository at this point in the history
The ResourceEnvironment class has been updated to include a filter by density. Now, resources are selected based on the screen size qualifiers, with the system defaulting to resources designed for a smaller screen if there are no better matches. Corresponding methods have also been enhanced and a test for image resource density has been added to verify the implemented changes.
  • Loading branch information
terrakok committed Jun 14, 2024
1 parent a1b88db commit bffc4b7
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ internal fun Resource.getResourceItemByEnvironment(environment: ResourceEnvironm
.also { if (it.size == 1) return it.first() }
.filterBy(environment.theme)
.also { if (it.size == 1) return it.first() }
.filterBy(environment.density)
.filterByDensity(environment.density)
.also { if (it.size == 1) return it.first() }
.let { items ->
if (items.isEmpty()) {
Expand Down Expand Up @@ -125,12 +125,40 @@ private fun List<ResourceItem>.filterBy(qualifier: Qualifier): List<ResourceItem
}
}

// https://developer.android.com/guide/topics/resources/providing-resources#BestMatch
// When selecting resources based on the screen size qualifiers,
// the system uses resources designed for a screen smaller than the current screen
// if there are no resources that better match.
// For example, a large-size screen uses normal-size screen resources if necessary.
private fun List<ResourceItem>.filterByDensity(density: DensityQualifier): List<ResourceItem> {
val items = this

val qualifiers = DensityQualifier.entries
.filter { it.dpi <= density.dpi }
.sortedByDescending { it.dpi }

var withQualifier = emptyList<ResourceItem>()
for (qualifier in qualifiers) {
withQualifier = items.filter { item -> item.qualifiers.any { it == qualifier } }
if (withQualifier.isNotEmpty()) break
}
if (withQualifier.isNotEmpty()) return withQualifier

//items with no DensityQualifier (default)
return items.filter { item ->
item.qualifiers.none { it is DensityQualifier }
}
}

// we need to filter by language and region together because there is slightly different logic:
// 1) if there is the exact match language+region then use it
// 2) if there is the language WITHOUT region match then use it
// 3) in other cases use items WITHOUT language and region qualifiers at all
// issue: https://github.com/JetBrains/compose-multiplatform/issues/4571
private fun List<ResourceItem>.filterByLocale(language: LanguageQualifier, region: RegionQualifier): List<ResourceItem> {
private fun List<ResourceItem>.filterByLocale(
language: LanguageQualifier,
region: RegionQualifier
): List<ResourceItem> {
val withLanguage = filter { item ->
item.qualifiers.any { it == language }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,44 @@ class ComposeResourceTest {
)
}

@Test
fun testImageResourceDensity() = runComposeUiTest {
val testResourceReader = TestResourceReader()
val imgRes = DrawableResource(
"test_id", setOf(
ResourceItem(setOf(DensityQualifier.MDPI), "2.png", -1, -1), //MDPI
ResourceItem(emptySet(), "1.png", -1, -1), //Default
)
)
val lowDpiEnvironment = object : ComposeEnvironment {
@Composable
override fun rememberEnvironment() = ResourceEnvironment(
language = LanguageQualifier("en"),
region = RegionQualifier("US"),
theme = ThemeQualifier.LIGHT,
density = DensityQualifier.LDPI
)
}

var environment by mutableStateOf(TestComposeEnvironment)
setContent {
CompositionLocalProvider(
LocalResourceReader provides testResourceReader,
LocalComposeEnvironment provides environment
) {
Image(painterResource(imgRes), null)
}
}
waitForIdle()
environment = lowDpiEnvironment
waitForIdle()

assertEquals(
expected = listOf("2.png", "1.png"), //MDPI - fist, Default - next
actual = testResourceReader.readPaths
)
}

@Test
fun testStringResourceCache() = runComposeUiTest {
val testResourceReader = TestResourceReader()
Expand Down

0 comments on commit bffc4b7

Please sign in to comment.