diff options
7 files changed, 165 insertions, 4 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index cf0e90fb43ce..c3a49305af87 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8950,6 +8950,18 @@ public final class Settings { "high_text_contrast_enabled"; /** + * Setting that specifies the status of the High Contrast Text + * rectangle refresh's one-time prompt. + * 0 = UNKNOWN + * 1 = PROMPT_SHOWN + * 2 = PROMPT_UNNECESSARY + * + * @hide + */ + public static final String ACCESSIBILITY_HCT_RECT_PROMPT_STATUS = + "accessibility_hct_rect_prompt_status"; + + /** * The color contrast, float in [-1, 1], 1 being the highest contrast. * * @hide diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 2e0fe9eb13d9..7e9d62315ef1 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -105,6 +105,7 @@ message SecureSettingsProto { optional SettingProto accessibility_gesture_targets = 57 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto display_daltonizer_saturation_level = 58 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_key_gesture_targets = 59 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hct_rect_prompt_status = 60 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 731cb7269037..18bebd40b03a 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -52,6 +52,7 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, + Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, Settings.Secure.CONTRAST_LEVEL, Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 039832cee6f2..1d7608d7d4d0 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -88,6 +88,9 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put( + Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, + new DiscreteValueValidator(new String[] {"0", "1", "2"})); VALIDATORS.put(Secure.CONTRAST_LEVEL, new InclusiveFloatRangeValidator(-1f, 1f)); VALIDATORS.put( Secure.ACCESSIBILITY_CAPTIONING_PRESET, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 6f40e2760386..9bbb509f323a 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -25,6 +25,8 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.hardware.display.ColorDisplayManager; import android.icu.util.ULocale; @@ -32,6 +34,7 @@ import android.media.AudioManager; import android.media.RingtoneManager; import android.media.Utils; import android.net.Uri; +import android.os.Build; import android.os.LocaleList; import android.os.RemoteException; import android.os.ServiceManager; @@ -50,6 +53,7 @@ import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManage import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Set; @@ -69,6 +73,9 @@ public class SettingsHelper { private static final int LONG_PRESS_POWER_FOR_ASSISTANT = 5; /** See frameworks/base/core/res/res/values/config.xml#config_keyChordPowerVolumeUp **/ private static final int KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS = 2; + @VisibleForTesting + static final String HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION = + "com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED"; // Error messages for logging metrics. private static final String ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA = @@ -252,6 +259,23 @@ public class SettingsHelper { // Don't write it to setting. Let the broadcast receiver in // AccessibilityManagerService handle restore/merging logic. return; + } else if (com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect() + && Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED.equals(name)) { + final boolean currentlyEnabled = Settings.Secure.getInt( + context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1; + final boolean enabledInRestore = value != null && Integer.parseInt(value) == 1; + + // If restoring from Android 15 or earlier and the user didn't already enable HCT + // on this new device, then don't restore and trigger custom migration logic. + final boolean needsCustomMigration = !currentlyEnabled + && restoredFromSdkInt < Build.VERSION_CODES.BAKLAVA + && enabledInRestore; + if (needsCustomMigration) { + migrateHighContrastText(context); + return; + } + // fall through to the ordinary write to settings } // Default case: write the restored value to settings @@ -529,6 +553,30 @@ public class SettingsHelper { } } + private static void migrateHighContrastText(Context context) { + final Intent intent = new Intent(HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION) + .setPackage(getSettingsAppPackage(context)); + context.sendBroadcastAsUser(intent, context.getUser(), null); + } + + /** + * Returns the System Settings application's package name + */ + private static String getSettingsAppPackage(Context context) { + String settingsAppPackage = null; + PackageManager packageManager = context.getPackageManager(); + if (packageManager != null) { + List<ResolveInfo> results = packageManager.queryIntentActivities( + new Intent(Settings.ACTION_SETTINGS), + PackageManager.MATCH_SYSTEM_ONLY); + if (!results.isEmpty()) { + settingsAppPackage = results.getFirst().activityInfo.applicationInfo.packageName; + } + } + + return !TextUtils.isEmpty(settingsAppPackage) ? settingsAppPackage : "com.android.settings"; + } + /* package */ byte[] getLocaleData() { Configuration conf = mContext.getResources().getConfiguration(); return conf.getLocales().toLanguageTags().getBytes(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 37eda3ebf9a8..f9c64422b0db 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1778,6 +1778,9 @@ class SettingsProtoDumpUtil { Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, SecureSettingsProto.Accessibility.HIGH_TEXT_CONTRAST_ENABLED); dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, + SecureSettingsProto.Accessibility.HCT_RECT_PROMPT_STATUS); + dumpSetting(s, p, Settings.Secure.CONTRAST_LEVEL, SecureSettingsProto.Accessibility.CONTRAST_LEVEL); dumpSetting(s, p, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java index 048d93b09967..8bffba3cb75f 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java @@ -26,15 +26,19 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.SettingsStringUtil; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.util.test.BroadcastInterceptingContext; +import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; @@ -48,7 +52,8 @@ import java.util.concurrent.ExecutionException; */ @RunWith(AndroidJUnit4.class) public class SettingsHelperRestoreTest { - + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final float FLOAT_TOLERANCE = 0.01f; private Context mContext; @@ -57,11 +62,99 @@ public class SettingsHelperRestoreTest { @Before public void setUp() { - mContext = InstrumentationRegistry.getContext(); + mContext = InstrumentationRegistry.getInstrumentation().getContext(); mContentResolver = mContext.getContentResolver(); mSettingsHelper = new SettingsHelper(mContext); } + @After + public void cleanUp() { + setDefaultAccessibilityDisplayMagnificationScale(); + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0); + Settings.Secure.putString(mContentResolver, Settings.Secure.ACCESSIBILITY_QS_TARGETS, null); + Settings.Secure.putString(mContentResolver, + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null); + } + + @Test + public void restoreHighTextContrastEnabled_currentlyEnabled_enableInRestoredFromVanilla_dontSendNotification_hctKeepsEnabled() + throws ExecutionException, InterruptedException { + BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext( + mContext); + BroadcastInterceptingContext.FutureIntent futureIntent = + interceptingContext.nextBroadcastIntent( + SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION); + mContentResolver = interceptingContext.getContentResolver(); + String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED; + Settings.Secure.putInt(mContentResolver, settingName, 1); + + mSettingsHelper.restoreValue( + interceptingContext, + mContentResolver, + new ContentValues(2), + Settings.Secure.getUriFor(settingName), + settingName, + String.valueOf(1), + Build.VERSION_CODES.VANILLA_ICE_CREAM); + + futureIntent.assertNotReceived(); + assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(1); + } + + @EnableFlags(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) + @Test + public void restoreHighTextContrastEnabled_currentlyDisabled_enableInRestoredFromVanilla_sendNotification_hctKeepsDisabled() + throws ExecutionException, InterruptedException { + BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext( + mContext); + BroadcastInterceptingContext.FutureIntent futureIntent = + interceptingContext.nextBroadcastIntent( + SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION); + mContentResolver = interceptingContext.getContentResolver(); + String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED; + Settings.Secure.putInt(mContentResolver, settingName, 0); + + mSettingsHelper.restoreValue( + interceptingContext, + mContentResolver, + new ContentValues(2), + Settings.Secure.getUriFor(settingName), + settingName, + String.valueOf(1), + Build.VERSION_CODES.VANILLA_ICE_CREAM); + + Intent intentReceived = futureIntent.get(); + assertThat(intentReceived).isNotNull(); + assertThat(intentReceived.getPackage()).isNotNull(); + assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(0); + } + + @Test + public void restoreHighTextContrastEnabled_currentlyDisabled_enableInRestoredFromAfterVanilla_dontSendNotification_hctShouldEnabled() + throws ExecutionException, InterruptedException { + BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext( + mContext); + BroadcastInterceptingContext.FutureIntent futureIntent = + interceptingContext.nextBroadcastIntent( + SettingsHelper.HIGH_CONTRAST_TEXT_RESTORED_BROADCAST_ACTION); + mContentResolver = interceptingContext.getContentResolver(); + String settingName = Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED; + Settings.Secure.putInt(mContentResolver, settingName, 0); + + mSettingsHelper.restoreValue( + interceptingContext, + mContentResolver, + new ContentValues(2), + Settings.Secure.getUriFor(settingName), + settingName, + String.valueOf(1), + Build.VERSION_CODES.BAKLAVA); + + futureIntent.assertNotReceived(); + assertThat(Settings.Secure.getInt(mContentResolver, settingName, 0)).isEqualTo(1); + } + /** Tests for {@link Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. */ @Test public void |