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

[resources] Update resource density-based lookup to be equal with the android logic #4969

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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,59 @@ private fun List<ResourceItem>.filterBy(qualifier: Qualifier): List<ResourceItem
}
}

// https://developer.android.com/guide/topics/resources/providing-resources#BestMatch
// In general, Android prefers scaling down a larger original image to scaling up a smaller original image.
private fun List<ResourceItem>.filterByDensity(density: DensityQualifier): List<ResourceItem> {
val items = this
var withQualifier = emptyList<ResourceItem>()

// filter with the same or better density
val exactAndHigherQualifiers = DensityQualifier.entries
.filter { it.dpi >= density.dpi }
.sortedBy { it.dpi }

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

// filter with low density
val lowQualifiers = DensityQualifier.entries
.minus(DensityQualifier.LDPI)
.filter { it.dpi < density.dpi }
.sortedByDescending { it.dpi }
for (qualifier in lowQualifiers) {
withQualifier = items.filter { item -> item.qualifiers.any { it == qualifier } }
if (withQualifier.isNotEmpty()) break
}
if (withQualifier.isNotEmpty()) return withQualifier

//items with no DensityQualifier (default)
// The system assumes that default resources (those from a directory without configuration qualifiers)
// are designed for the baseline pixel density (mdpi) and resizes those bitmaps
// to the appropriate size for the current pixel density.
// https://developer.android.com/training/multiscreen/screendensities#DensityConsiderations
val withNoDensity = items.filter { item ->
item.qualifiers.none { it is DensityQualifier }
}
if (withNoDensity.isNotEmpty()) return withNoDensity

//items with LDPI density
return items.filter { item ->
item.qualifiers.any { it == DensityQualifier.LDPI }
}
}

// 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.XXXHDPI), "2.png", -1, -1),
ResourceItem(setOf(DensityQualifier.MDPI), "1.png", -1, -1),
)
)
val mdpiEnvironment = object : ComposeEnvironment {
@Composable
override fun rememberEnvironment() = ResourceEnvironment(
language = LanguageQualifier("en"),
region = RegionQualifier("US"),
theme = ThemeQualifier.LIGHT,
density = DensityQualifier.MDPI
)
}

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

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

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