Loading core/java/android/content/res/FontScaleConverter.java +4 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt +67 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } } Loading
core/java/android/content/res/FontScaleConverter.java +4 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading
core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt +67 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } }