Merge "fix(non linear font scaling): fix crash with certain negative SP values"
This commit is contained in:
committed by
Android (Google) Code Review
commit
c9b04d73ae
@@ -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