Merge "Handle different font scale values during B&R" into 24D1-dev
diff --git a/packages/SettingsProvider/res/values/arrays.xml b/packages/SettingsProvider/res/values/arrays.xml
new file mode 100644
index 0000000..e56d0f2
--- /dev/null
+++ b/packages/SettingsProvider/res/values/arrays.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2024 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- NOTE: if you change this, you must also add the corresponding scale key and lookup table to
+     frameworks/base/core/java/android/content/res/FontScaleConverterFactory.java
+     TODO(b/341235102): Remove font_scale array duplication
+     -->
+    <string-array name="entryvalues_font_size" translatable="false">
+        <item>0.85</item>
+        <item>1.0</item>
+        <item>1.15</item>
+        <item>1.30</item>
+        <item>1.50</item>
+        <item>1.80</item>
+        <item>2.0</item>
+    </string-array>
+
+</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 4c255a5..11fa8f4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -48,7 +48,6 @@
                 Settings.System.WIFI_STATIC_DNS2,
                 Settings.System.BLUETOOTH_DISCOVERABILITY,
                 Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
-                Settings.System.DEFAULT_DEVICE_FONT_SCALE,
                 Settings.System.FONT_SCALE,
                 Settings.System.DIM_SCREEN,
                 Settings.System.SCREEN_OFF_TIMEOUT,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 7b49608..77c6528 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,6 +16,8 @@
 
 package com.android.providers.settings;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.backup.BackupAgentHelper;
 import android.app.backup.BackupDataInput;
@@ -57,6 +59,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settingslib.display.DisplayDensityConfiguration;
+import com.android.window.flags.Flags;
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -88,6 +91,7 @@
 
     private static final byte[] NULL_VALUE = new byte[0];
     private static final int NULL_SIZE = -1;
+    private static final float FONT_SCALE_DEF_VALUE = 1.0f;
 
     private static final String KEY_SYSTEM = "system";
     private static final String KEY_SECURE = "secure";
@@ -111,7 +115,6 @@
     // Versioning of the Network Policies backup payload.
     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
 
-
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
     private static final int STATE_SYSTEM                = 0;
@@ -208,10 +211,19 @@
     // Populated in onRestore().
     private int mRestoredFromSdkInt;
 
+    // The available font scale for the current device
+    @Nullable
+    private String[] mAvailableFontScales;
+
+    // The font_scale default value for this device.
+    private float mDefaultFontScale;
+
     @Override
     public void onCreate() {
         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
-
+        mDefaultFontScale = getBaseContext().getResources().getFloat(R.dimen.def_device_font_scale);
+        mAvailableFontScales = getBaseContext().getResources()
+                .getStringArray(R.array.entryvalues_font_size);
         mSettingsHelper = new SettingsHelper(this);
         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
         super.onCreate();
@@ -917,6 +929,23 @@
                     continue;
                 }
             }
+
+            if (Settings.System.FONT_SCALE.equals(key)) {
+                // If the current value is different from the default it means that it's been
+                // already changed for a11y reason. In that case we don't need to restore
+                // the new value.
+                final float currentValue = Settings.System.getFloat(cr, Settings.System.FONT_SCALE,
+                        mDefaultFontScale);
+                if (currentValue != mDefaultFontScale) {
+                    Log.d(TAG, "Font scale not restored because changed for a11y reason.");
+                    continue;
+                }
+                final String toRestore = value;
+                value = findClosestAllowedFontScale(value, mAvailableFontScales);
+                Log.d(TAG, "Restored font scale from: " + toRestore + " to " + value);
+            }
+
+
             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
                     mRestoredFromSdkInt);
 
@@ -924,6 +953,32 @@
         }
     }
 
+
+    @VisibleForTesting
+    static String findClosestAllowedFontScale(@NonNull String requestedFontScale,
+            @NonNull String[] availableFontScales) {
+        if (Flags.configurableFontScaleDefault()) {
+            final float requestedValue = Float.parseFloat(requestedFontScale);
+            // Whatever is the requested value, we search the closest allowed value which is
+            // equals or larger. Note that if the requested value is the previous default,
+            // and this is still available, the value will be preserved.
+            float candidate = 0.0f;
+            boolean fontScaleFound = false;
+            for (int i = 0; !fontScaleFound && i < availableFontScales.length; i++) {
+                final float fontScale = Float.parseFloat(availableFontScales[i]);
+                if (fontScale >= requestedValue) {
+                    candidate = fontScale;
+                    fontScaleFound = true;
+                }
+            }
+            // If the current value is greater than all the allowed ones, we return the
+            // largest possible.
+            return fontScaleFound ? String.valueOf(candidate) : String.valueOf(
+                    availableFontScales[availableFontScales.length - 1]);
+        }
+        return requestedFontScale;
+    }
+
     @VisibleForTesting
     SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
         // Figure out the white list and redirects to the global table.  We restore anything
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 3e0d05c..140c566 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -386,6 +386,7 @@
         // it means that the user has performed a global gesture to enable accessibility or set
         // these settings in the Accessibility portion of the Setup Wizard, and definitely needs
         // these features working after the restore.
+        // Note: Settings.Secure.FONT_SCALE is already handled in the caller class.
         switch (name) {
             case Settings.Secure.ACCESSIBILITY_ENABLED:
             case Settings.Secure.TOUCH_EXPLORATION_ENABLED:
@@ -405,8 +406,6 @@
                 float currentScale = Settings.Secure.getFloat(
                         mContext.getContentResolver(), name, defaultScale);
                 return Math.abs(currentScale - defaultScale) >= FLOAT_TOLERANCE;
-            case Settings.System.FONT_SCALE:
-                return Settings.System.getFloat(mContext.getContentResolver(), name, 1.0f) != 1.0f;
             default:
                 return false;
         }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4f9e11a..ecc22ef 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -907,6 +907,7 @@
                         Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
                         Settings.System.EGG_MODE, // I am the lolrus
                         Settings.System.END_BUTTON_BEHAVIOR, // bug?
+                        Settings.System.DEFAULT_DEVICE_FONT_SCALE, // Non configurable
                         Settings.System
                                 .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
                         // candidate for backup?
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 433aac7..d4ca4a3 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -31,6 +31,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.provider.settings.validators.SettingsValidators;
 import android.provider.settings.validators.Validator;
@@ -39,6 +40,8 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.window.flags.Flags;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -54,8 +57,12 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 
-/** Tests for the SettingsHelperTest */
+/**
+ * Tests for the SettingsHelperTest
+ * Usage: atest SettingsProviderTest:SettingsBackupAgentTest
+ */
 @RunWith(AndroidJUnit4.class)
 public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
     private static final Uri TEST_URI = Uri.EMPTY;
@@ -213,6 +220,32 @@
         assertFalse(settingsHelper.mWrittenValues.containsKey(PRESERVED_TEST_SETTING));
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_CONFIGURABLE_FONT_SCALE_DEFAULT)
+    public void testFindClosestAllowedFontScale() {
+        final String[] availableFontScales = new String[]{"0.5", "0.9", "1.0", "1.1", "1.5"};
+        final Function<String, String> testedMethod =
+                (value) -> SettingsBackupAgent.findClosestAllowedFontScale(value,
+                        availableFontScales);
+
+        // Any allowed value needs to be preserved.
+        assertEquals("0.5", testedMethod.apply("0.5"));
+        assertEquals("0.9", testedMethod.apply("0.9"));
+        assertEquals("1.0", testedMethod.apply("1.0"));
+        assertEquals("1.1", testedMethod.apply("1.1"));
+        assertEquals("1.5", testedMethod.apply("1.5"));
+
+        // When the current value is not one of the available, the first larger is returned
+        assertEquals("0.5", testedMethod.apply("0.3"));
+        assertEquals("0.9", testedMethod.apply("0.8"));
+        assertEquals("1.1", testedMethod.apply("1.05"));
+        assertEquals("1.5", testedMethod.apply("1.2"));
+
+        // When the current value is larger than the only one available, the largest allowed
+        // is returned.
+        assertEquals("1.5", testedMethod.apply("1.8"));
+    }
+
     private byte[] generateBackupData(Map<String, String> keyValueData) {
         int totalBytes = 0;
         for (String key : keyValueData.keySet()) {