Typography.java

/*
 * Copyright 2022 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 androidx.wear.tiles.material;

import static androidx.annotation.Dimension.DP;
import static androidx.annotation.Dimension.SP;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.DisplayMetrics;

import androidx.annotation.Dimension;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;

/**
 * Typography styles, currently set up to match Wear's styling.
 *
 * @deprecated Use the new class {@link androidx.wear.protolayout.material.Typography} which
 *     provides the same API and functionality.
 */
@Deprecated
@SuppressWarnings("deprecation")
public class Typography {
    /** Typography for large display text. */
    public static final int TYPOGRAPHY_DISPLAY1 = 1;

    /** Typography for medium display text. */
    public static final int TYPOGRAPHY_DISPLAY2 = 2;

    /** Typography for small display text. */
    public static final int TYPOGRAPHY_DISPLAY3 = 3;

    /** Typography for large title text. */
    public static final int TYPOGRAPHY_TITLE1 = 4;

    /** Typography for medium title text. */
    public static final int TYPOGRAPHY_TITLE2 = 5;

    /** Typography for small title text. */
    public static final int TYPOGRAPHY_TITLE3 = 6;

    /** Typography for large body text. */
    public static final int TYPOGRAPHY_BODY1 = 7;

    /** Typography for medium body text. */
    public static final int TYPOGRAPHY_BODY2 = 8;

    /** Typography for bold button text. */
    public static final int TYPOGRAPHY_BUTTON = 9;

    /** Typography for large caption text. */
    public static final int TYPOGRAPHY_CAPTION1 = 10;

    /** Typography for medium caption text. */
    public static final int TYPOGRAPHY_CAPTION2 = 11;

    /** Typography for small caption text. */
    public static final int TYPOGRAPHY_CAPTION3 = 12;

    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
        TYPOGRAPHY_DISPLAY1,
        TYPOGRAPHY_DISPLAY2,
        TYPOGRAPHY_DISPLAY3,
        TYPOGRAPHY_TITLE1,
        TYPOGRAPHY_TITLE2,
        TYPOGRAPHY_TITLE3,
        TYPOGRAPHY_BODY1,
        TYPOGRAPHY_BODY2,
        TYPOGRAPHY_BUTTON,
        TYPOGRAPHY_CAPTION1,
        TYPOGRAPHY_CAPTION2,
        TYPOGRAPHY_CAPTION3
    })
    @interface TypographyName {}

    /** Mapping for line height for different typography. */
    @NonNull
    private static final Map<Integer, Float> TYPOGRAPHY_TO_LINE_HEIGHT_SP = new HashMap<>();

    static {
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY1, 46f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY2, 40f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY3, 36f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE1, 28f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE2, 24f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE3, 20f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BODY1, 20f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BODY2, 18f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BUTTON, 19f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION1, 18f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION2, 16f);
        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION3, 14f);
    }

    /**
     * Returns the {@link androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder} for the given
     * androidx.wear.tiles.LayoutElementBuilders.FontStyle code with the recommended size, weight
     * and letter spacing. Font will be scalable.
     */
    @NonNull
    static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder getFontStyleBuilder(
            @TypographyName int fontStyleCode, @NonNull Context context) {
        return getFontStyleBuilder(fontStyleCode, context, true);
    }

    private Typography() {}

    /**
     * Returns the {@link androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder} for the given
     * Typography code with the recommended size, weight and letter spacing, with the option to make
     * this font not scalable.
     */
    @NonNull
    static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder getFontStyleBuilder(
            @TypographyName int typographyCode, @NonNull Context context, boolean isScalable) {
        switch (typographyCode) {
            case TYPOGRAPHY_BODY1:
                return body1(isScalable, context);
            case TYPOGRAPHY_BODY2:
                return body2(isScalable, context);
            case TYPOGRAPHY_BUTTON:
                return button(isScalable, context);
            case TYPOGRAPHY_CAPTION1:
                return caption1(isScalable, context);
            case TYPOGRAPHY_CAPTION2:
                return caption2(isScalable, context);
            case TYPOGRAPHY_CAPTION3:
                return caption3(isScalable, context);
            case TYPOGRAPHY_DISPLAY1:
                return display1(isScalable, context);
            case TYPOGRAPHY_DISPLAY2:
                return display2(isScalable, context);
            case TYPOGRAPHY_DISPLAY3:
                return display3(isScalable, context);
            case TYPOGRAPHY_TITLE1:
                return title1(isScalable, context);
            case TYPOGRAPHY_TITLE2:
                return title2(isScalable, context);
            case TYPOGRAPHY_TITLE3:
                return title3(isScalable, context);
            default:
                // Shouldn't happen.
                throw new IllegalArgumentException(
                        "Typography " + typographyCode + " doesn't exist.");
        }
    }

    /**
     * Returns the recommended line height for the given Typography to be added to the Text
     * component.
     */
    @NonNull
    static androidx.wear.tiles.DimensionBuilders.SpProp getLineHeightForTypography(
            @TypographyName int typography) {
        if (!TYPOGRAPHY_TO_LINE_HEIGHT_SP.containsKey(typography)) {
            throw new IllegalArgumentException("Typography " + typography + " doesn't exist.");
        }
        return androidx.wear.tiles.DimensionBuilders.sp(
                androidx.wear.tiles.material.Helper.checkNotNull(
                                TYPOGRAPHY_TO_LINE_HEIGHT_SP.get(typography))
                        .intValue());
    }

    @NonNull
    @SuppressLint("ResourceType")
    // This is a helper function to make the font not scalable. It should interpret in value as DP
    // and convert it to SP which is needed to be passed in as a font size. However, we will pass an
    // SP object to it, because the default style is defined in it, but for the case when the font
    // size on device in 1, so the DP is equal to SP.
    // TODO(b/267744228): Remove the warning suppression.
    @SuppressWarnings("deprecation")
    private static androidx.wear.tiles.DimensionBuilders.SpProp dpToSp(
            @NonNull Context context, @Dimension(unit = DP) float valueDp) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        float scaledSp = (valueDp / metrics.scaledDensity) * metrics.density;
        return androidx.wear.tiles.DimensionBuilders.sp(scaledSp);
    }

    // The @Dimension(unit = SP) on androidx.wear.tiles.DimensionBuilders.sp() is seemingly being
    // ignored, so lint complains that we're passing SP to something expecting PX. Just suppress the
    // warning for now.
    @SuppressLint("ResourceType")
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder
            createFontStyleBuilder(
                    @Dimension(unit = SP) int size,
                    @androidx.wear.tiles.LayoutElementBuilders.FontWeight int weight,
                    @androidx.wear.tiles.LayoutElementBuilders.FontVariant int variant,
                    float letterSpacing,
                    boolean isScalable,
                    @NonNull Context context) {
        return new androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder()
                .setSize(
                        isScalable
                                ? androidx.wear.tiles.DimensionBuilders.sp(size)
                                : dpToSp(context, size))
                .setLetterSpacing(androidx.wear.tiles.DimensionBuilders.em(letterSpacing))
                .setVariant(variant)
                .setWeight(weight);
    }

    /** Font style for large display text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                40,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_TITLE,
                0.01f,
                isScalable,
                context);
    }

    /** Font style for medium display text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                34,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_TITLE,
                0.03f,
                isScalable,
                context);
    }

    /** Font style for small display text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                30,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_TITLE,
                0.03f,
                isScalable,
                context);
    }

    /** Font style for large title text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                24,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_TITLE,
                0.008f,
                isScalable,
                context);
    }

    /** Font style for medium title text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                20,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_TITLE,
                0.01f,
                isScalable,
                context);
    }

    /** Font style for small title text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                16,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_TITLE,
                0.01f,
                isScalable,
                context);
    }

    /** Font style for normal body text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                16,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_NORMAL,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_BODY,
                0.01f,
                isScalable,
                context);
    }

    /** Font style for small body text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                14,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_NORMAL,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_BODY,
                0.014f,
                isScalable,
                context);
    }

    /** Font style for bold button text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                15,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_BOLD,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_BODY,
                0.03f,
                isScalable,
                context);
    }

    /** Font style for large caption text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                14,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_BODY,
                0.01f,
                isScalable,
                context);
    }

    /** Font style for medium caption text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                12,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_BODY,
                0.01f,
                isScalable,
                context);
    }

    /** Font style for small caption text. */
    @NonNull
    private static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption3(
            boolean isScalable, @NonNull Context context) {
        return createFontStyleBuilder(
                10,
                androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM,
                androidx.wear.tiles.LayoutElementBuilders.FONT_VARIANT_BODY,
                0.01f,
                isScalable,
                context);
    }
}