chromium/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/TextScalePreference.java

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.accessibility.settings;

import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.SeekBar;
import android.widget.TextView;
import org.chromium.chrome.R;

import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import java.text.NumberFormat;

/**
 * Preference that allows the user to change the scaling factor that's applied to web page text.
 * This also shows a preview of how large a typical web page's text will appear.
 */
public class TextScalePreference extends Preference implements SeekBar.OnSeekBarChangeListener {
    private static final float MIN = 0.5f;
    private static final float MAX = 2.0f;
    private static final float STEP = 0.05f;

    /**
     * Online body text tends to be around 13-16px. We ask the user to adjust the text scale until
     * 12px text is legible, that way all body text will be legible (and since font boosting
     * approximately preserves relative font size differences, other text will be bigger/smaller as
     * appropriate).
     */
    private static final float SMALLEST_STANDARD_FONT_SIZE_PX = 12.0f;

    private float mUserFontScaleFactor = MIN;
    private float mFontScaleFactor;

    private TextView mAmount;
    private TextView mPreview;
    private SeekBar mSeekBar;

    private NumberFormat mFormat = NumberFormat.getPercentInstance();

    /** Constructor for inflating from XML. */
    public TextScalePreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        setLayoutResource(R.layout.custom_preference);
        setWidgetLayoutResource(R.layout.preference_text_scale);
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);

        mSeekBar = (SeekBar) holder.findViewById(R.id.seekbar);
        mSeekBar.setOnSeekBarChangeListener(this);
        mSeekBar.setMax(userFontScaleFactorToProgress(MAX));
        mSeekBar.setProgress(userFontScaleFactorToProgress(mUserFontScaleFactor));

        mAmount = (TextView) holder.findViewById(R.id.seekbar_amount);
        mPreview = (TextView) holder.findViewById(R.id.preview);

        updateViews();
    }

    void updateFontScaleFactors(
            float fontScaleFactor, float userFontScaleFactor, boolean updateViews) {
        mFontScaleFactor = fontScaleFactor;
        mUserFontScaleFactor = userFontScaleFactor;

        if (updateViews) updateViews();
    }

    private void updateViews() {
        mAmount.setText(mFormat.format(mUserFontScaleFactor));

        // On Android R+, use stateDescription so the only percentage announced to the user is
        // the scaling percent. For previous versions the SeekBar percentage is always announced.
        String userFriendlyFontDescription =
                getContext()
                        .getResources()
                        .getString(
                                R.string.font_size_accessibility_label,
                                mFormat.format(mUserFontScaleFactor));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            mSeekBar.setStateDescription(userFriendlyFontDescription);
        } else {
            mSeekBar.setContentDescription(userFriendlyFontDescription);
        }

        mPreview.setTextSize(
                TypedValue.COMPLEX_UNIT_DIP, SMALLEST_STANDARD_FONT_SIZE_PX * mFontScaleFactor);
    }

    /**
     * {@link SeekBar} only supports integer steps, and starts from 0, so the following methods are
     * necessary to scale floating point pref values to/from integer progress amounts.
     *
     * @param progress {@link SeekBar} progress amount.
     * @return User font scale factor preference value.
     */
    private float progressToUserFontScaleFactor(int progress) {
        return MIN + progress * STEP;
    }

    private int userFontScaleFactorToProgress(float userFontScaleFactor) {
        return Math.round((userFontScaleFactor - MIN) / STEP);
    }

    /** Notifies {@link Preference.OnPreferenceChangeListener}s of updated value. */
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (!fromUser) return;

        float userFontScaleFactor = progressToUserFontScaleFactor(progress);
        if (userFontScaleFactor == mUserFontScaleFactor) return;

        callChangeListener(userFontScaleFactor);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {}

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {}

    public CharSequence getAmountForTesting() {
        return mAmount.getText();
    }
}