DelegatingFontLoaderForDeprecatedUsage.android.kt

/*
 * 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.compose.ui.text.font

import android.content.Context
import androidx.compose.ui.text.ExperimentalTextApi

/**
 * Bridge between subclasses of Font.ResourceLoader and the new FontFamily.Resolver API.
 *
 * To use add as a CompositionLocal replacing the default FontFamily.Resolver:
 *
 * ```
 * LocalFontFamilyResolver provides createFontFamilyResolver(myFontResourceLoader, context)
 * ```
 *
 * This FontFamily.Resolver is capable of loading all fonts that the default FontFamily.Resolver is.
 * In addition, it will delegate all resource fonts to the provided Font.ResourceLoader, preserving
 * the behavior of Compose 1.0.
 *
 * This method will be removed by Compose 2.0, and callers should migrate to using [AndroidFont] to
 * implement the same behavior using font fallback chains.
 *
 * A FontFamily.Resolver created this way will not share caches with other FontFamily.Resolvers.
 */
@Suppress("DEPRECATION")
@OptIn(ExperimentalTextApi::class)
@Deprecated("This exists to bridge existing Font.ResourceLoader subclasses to be used as a" +
    "FontFamily.ResourceLoader during upgrade.",
    replaceWith = ReplaceWith("createFontFamilyResolver()"),
)
fun createFontFamilyResolver(
    fontResourceLoader: Font.ResourceLoader,
    context: Context
): FontFamily.Resolver {
    return FontFamilyResolverImpl(
        DelegatingFontLoaderForBridgeUsage(fontResourceLoader, context.applicationContext)
    )
}

@Suppress("DEPRECATION")
@OptIn(ExperimentalTextApi::class)
@Deprecated("This exists to bridge existing Font.ResourceLoader APIs, and should be " +
    "removed with them",
    replaceWith = ReplaceWith("createFontFamilyResolver()"),
)
internal actual fun createFontFamilyResolver(
    fontResourceLoader: Font.ResourceLoader
): FontFamily.Resolver {
    return FontFamilyResolverImpl(DelegatingFontLoaderForDeprecatedUsage(fontResourceLoader))
}

/**
 * Allow converting between a Font.ResourceLoader and a FontLoader for deprecated APIs.
 *
 * This class is not able to load all fonts and exists for API interop.
 *
 * If you are experiencing a crash in custom text code, replace [Font.ResourceLoader] with a
 * [FontFamily.Resolver] using [createFontFamilyResolver] (Font.ResourceLoader, Context).
 */
@Suppress("DEPRECATION")
internal class DelegatingFontLoaderForDeprecatedUsage(
    internal val loader: Font.ResourceLoader
) : PlatformFontLoader {

    // never consider these reusable for caching
    override val cacheKey: Any = Any()

    override fun loadBlocking(font: Font): Any = loader.load(font)

    override suspend fun awaitLoad(font: Font): Any = loader.load(font)
}

/**
 * Allow converting between a Font.ResourceLoader and a FontLoader for real usage by apps that
 * subclassed Font.ResourceLoader in Compose 1.0.
 *
 * This loader is capable of performing all font loads as an upgrade bridge.
 */
@Suppress("DEPRECATION")
internal class DelegatingFontLoaderForBridgeUsage(
    internal val loader: Font.ResourceLoader,
    private val context: Context
) : PlatformFontLoader {
    // never consider these reusable for caching
    override val cacheKey: Any = Any()

    override fun loadBlocking(font: Font): Any? {
        return when (font) {
            is AndroidFont -> font.typefaceLoader.loadBlocking(context, font)
            else -> loader.load(font)
        }
    }

    override suspend fun awaitLoad(font: Font): Any? {
        return when (font) {
            is AndroidFont -> font.typefaceLoader.awaitLoad(context, font)
            else -> loader.load(font)
        }
    }
}