fix(non linear font scaling): fix crash with certain negative SP values
The crash was due to some old logic that was looking for near matches. Now we only return exact matches, and let the interpolation deal with near matches. Add test to make sure it doesn't crash at any value passed in. Test: atest FrameworksCoreTests:android.content.res.FontScaleConverterTest Bug: b/260984829 Change-Id: Ic90d1c5b48862e4f78b90be5926c9ce6d468acf9
This commit is contained in:
@@ -36,11 +36,6 @@ import java.util.Arrays;
|
||||
* @hide
|
||||
*/
|
||||
public class FontScaleConverter {
|
||||
/**
|
||||
* How close the given SP should be to a canonical SP in the array before they are considered
|
||||
* the same for lookup purposes.
|
||||
*/
|
||||
private static final float THRESHOLD_FOR_MATCHING_SP = 0.02f;
|
||||
|
||||
@VisibleForTesting
|
||||
final float[] mFromSpValues;
|
||||
@@ -78,10 +73,11 @@ public class FontScaleConverter {
|
||||
public float convertSpToDp(float sp) {
|
||||
final float spPositive = Math.abs(sp);
|
||||
// TODO(b/247861374): find a match at a higher index?
|
||||
final int spRounded = Math.round(spPositive);
|
||||
final float sign = Math.signum(sp);
|
||||
final int index = Arrays.binarySearch(mFromSpValues, spRounded);
|
||||
if (index >= 0 && Math.abs(spRounded - spPositive) < THRESHOLD_FOR_MATCHING_SP) {
|
||||
// We search for exact matches only, even if it's just a little off. The interpolation will
|
||||
// handle any non-exact matches.
|
||||
final int index = Arrays.binarySearch(mFromSpValues, spPositive);
|
||||
if (index >= 0) {
|
||||
// exact match, return the matching dp
|
||||
return sign * mToDpValues[index];
|
||||
} else {
|
||||
|
||||
@@ -18,8 +18,12 @@ package android.content.res
|
||||
|
||||
import androidx.core.util.forEach
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.google.common.truth.Truth.assertWithMessage
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@@ -72,7 +76,70 @@ class FontScaleConverterFactoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
fun allFeasibleScalesAndConversionsDoNotCrash() {
|
||||
generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
|
||||
.mapNotNull{ FontScaleConverterFactory.forScale(it) }
|
||||
.flatMap{ table ->
|
||||
generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
|
||||
.map{ Pair(table, it) }
|
||||
}
|
||||
.forEach { (table, sp) ->
|
||||
try {
|
||||
assertWithMessage(
|
||||
"convertSpToDp(%s) on table: %s",
|
||||
sp.toString(),
|
||||
table.toString()
|
||||
)
|
||||
.that(table.convertSpToDp(sp))
|
||||
.isFinite()
|
||||
} catch (e: Exception) {
|
||||
throw AssertionError("Exception during convertSpToDp($sp) on table: $table", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateSequenceOfFractions() {
|
||||
val fractions = generateSequenceOfFractions(-1000f..1000f, step = 0.1f)
|
||||
.toList()
|
||||
fractions.forEach {
|
||||
assertThat(it).isAtLeast(-1000f)
|
||||
assertThat(it).isAtMost(1000f)
|
||||
}
|
||||
|
||||
assertThat(fractions).isInStrictOrder()
|
||||
assertThat(fractions).hasSize(1000 * 2 * 10 + 1) // Don't forget the 0 in the middle!
|
||||
|
||||
assertThat(fractions).contains(100f)
|
||||
assertThat(fractions).contains(500.1f)
|
||||
assertThat(fractions).contains(500.2f)
|
||||
assertThat(fractions).contains(0.2f)
|
||||
assertThat(fractions).contains(0f)
|
||||
assertThat(fractions).contains(-10f)
|
||||
assertThat(fractions).contains(-10f)
|
||||
assertThat(fractions).contains(-10.3f)
|
||||
|
||||
assertThat(fractions).doesNotContain(-10.31f)
|
||||
assertThat(fractions).doesNotContain(0.35f)
|
||||
assertThat(fractions).doesNotContain(0.31f)
|
||||
assertThat(fractions).doesNotContain(-.35f)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CONVERSION_TOLERANCE = 0.05f
|
||||
}
|
||||
}
|
||||
|
||||
fun generateSequenceOfFractions(
|
||||
range: ClosedFloatingPointRange<Float>,
|
||||
step: Float
|
||||
): Sequence<Float> {
|
||||
val multiplier = 1f / step
|
||||
val start = floor(range.start * multiplier).toInt()
|
||||
val endInclusive = ceil(range.endInclusive * multiplier).toInt()
|
||||
return generateSequence(start) { it + 1 }
|
||||
.takeWhile { it <= endInclusive }
|
||||
.map{ it.toFloat() / multiplier }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user