diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index f6216fa659152..a73ebc2630b9b 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1685,12 +1685,16 @@ - + + + + + + + + + + + + + + diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java new file mode 100644 index 0000000000000..55ef854081328 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Activity; +import android.compat.testing.PlatformCompatChangeRule; +import android.os.Bundle; +import android.platform.test.annotations.Presubmit; +import android.provider.Settings; +import android.view.View; +import android.widget.TextView; + +import androidx.test.core.app.ActivityScenario; +import androidx.test.espresso.matcher.BoundedMatcher; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.ShellIdentityUtils; +import com.android.frameworks.coretests.R; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Test for verifying non-linear font scaling behavior. + * Build/Install/Run: + * atest FrameworksCoreTests:android.content.res.FontScaleConverterActivityTest + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +@Presubmit +public class FontScaleConverterActivityTest { + @Rule + public ActivityScenarioRule rule = new ActivityScenarioRule<>(TestActivity.class); + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + private float mOriginalFontScale = Float.MIN_VALUE; + + @Before + public void setup() { + mOriginalFontScale = Settings.System.getFloat( + InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(), + Settings.System.FONT_SCALE, + Float.MIN_VALUE + ); + } + + @After + public void teardown() { + if (mOriginalFontScale != Float.MIN_VALUE) { + setSystemFontScale(mOriginalFontScale); + } + } + + @Test + public void testFontsScaleNonLinearly() { + final ActivityScenario scenario = rule.getScenario(); + + setSystemFontScale(2f); + + var densityRef = new AtomicReference(); + scenario.onActivity(activity -> { + densityRef.compareAndSet(null, activity.getResources().getDisplayMetrics().density); + }); + var density = densityRef.get(); + assertThat(density).isNotNull(); + + onView(withId(R.id.text8sp)).check(matches(withTextSizeInRange( + 15f * density, + 16f * density + ))); + onView(withId(R.id.text18sp)).check(matches(withTextSizeInRange( + 20f * density, + 36f * density + ))); + onView(withId(R.id.text35sp)).check(matches(withTextSizeInRange( + 35 * density, + 60 * density + ))); + } + + @Test + public void testOnConfigurationChanged_doesNotCrash() { + final ActivityScenario scenario = rule.getScenario(); + + scenario.onActivity(activity -> { + var config = new Configuration(activity.getResources().getConfiguration()); + config.fontScale = 1.2f; + + activity.onConfigurationChanged(config); + activity.finish(); + }); + } + + @Test + public void testUpdateConfiguration_doesNotCrash() { + final ActivityScenario scenario = rule.getScenario(); + + scenario.onActivity(activity -> { + activity.updateConfiguration(); + activity.finish(); + }); + } + + private static void setSystemFontScale(float fontScale) { + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.System.putFloat( + InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(), + Settings.System.FONT_SCALE, + fontScale + ); + }); + } + + private Matcher withTextSizeInRange(float sizeStartPx, float sizeEndPx) { + return new BoundedMatcher<>(TextView.class) { + private static final float TOLERANCE = 0.05f; + + @Override + public void describeMismatch(Object item, Description description) { + super.describeMismatch(item, description); + description.appendText("was ").appendValue(((TextView) item).getTextSize()); + } + + @Override + public void describeTo(Description description) { + description.appendText("withTextSize between " + sizeStartPx + " and " + sizeEndPx); + } + + @Override + protected boolean matchesSafely(TextView textView) { + var textSize = textView.getTextSize(); + return sizeStartPx - TOLERANCE < textSize && textSize < sizeEndPx + TOLERANCE; + } + }; + } + + /** Test Activity */ + public static class TestActivity extends Activity { + final Configuration mConfig = new Configuration(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().getDecorView().setKeepScreenOn(true); + setShowWhenLocked(true); + setTurnScreenOn(true); + + setContentView(R.layout.font_scale_converter_activity); + } + + public Configuration updateConfiguration() { + Settings.System.getConfiguration(getContentResolver(), mConfig); + return mConfig; + } + } +}