1 /* <lambda>null2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.compose.ui.text.font 18 19 import android.content.Context 20 import android.content.res.AssetManager 21 import android.graphics.Typeface 22 import android.graphics.fonts.FontVariationAxis 23 import android.os.Build 24 import android.os.ParcelFileDescriptor 25 import androidx.annotation.RequiresApi 26 import androidx.compose.ui.text.ExperimentalTextApi 27 import androidx.compose.ui.unit.Density 28 import androidx.compose.ui.util.fastMap 29 import java.io.File 30 31 internal sealed class AndroidPreloadedFont 32 constructor( 33 final override val weight: FontWeight, 34 final override val style: FontStyle, 35 variationSettings: FontVariation.Settings 36 ) : 37 AndroidFont( 38 FontLoadingStrategy.Blocking, 39 AndroidPreloadedFontTypefaceLoader, 40 variationSettings 41 ) { 42 abstract val cacheKey: String? 43 44 internal abstract fun doLoad(context: Context?): Typeface? 45 46 private var didInitWithContext: Boolean = false 47 // subclasses MUST initialize this by calling doLoad(null) - after overriding doLoad as final 48 internal var typeface: Typeface? = null 49 50 internal fun loadCached(context: Context): Typeface? { 51 if (!didInitWithContext && typeface == null) { 52 typeface = doLoad(context) 53 } 54 didInitWithContext = true 55 return typeface 56 } 57 } 58 59 private object AndroidPreloadedFontTypefaceLoader : AndroidFont.TypefaceLoader { loadBlockingnull60 override fun loadBlocking(context: Context, font: AndroidFont): Typeface? = 61 (font as? AndroidPreloadedFont)?.loadCached(context) 62 63 override suspend fun awaitLoad(context: Context, font: AndroidFont): Nothing { 64 throw UnsupportedOperationException("All preloaded fonts are blocking.") 65 } 66 } 67 68 @OptIn(ExperimentalTextApi::class) /* FontVariation.Settings */ 69 internal class AndroidAssetFont 70 constructor( 71 val assetManager: AssetManager, 72 val path: String, 73 weight: FontWeight = FontWeight.Normal, 74 style: FontStyle = FontStyle.Normal, 75 variationSettings: FontVariation.Settings 76 ) : AndroidPreloadedFont(weight, style, variationSettings) { 77 doLoadnull78 override fun doLoad(context: Context?): Typeface? { 79 return if (Build.VERSION.SDK_INT >= 26) { 80 TypefaceBuilderCompat.createFromAssets(assetManager, path, context, variationSettings) 81 } else { 82 Typeface.createFromAsset(assetManager, path) 83 } 84 } 85 86 init { 87 typeface = doLoad(null) 88 } 89 90 override val cacheKey: String = "asset:$path" 91 toStringnull92 override fun toString(): String { 93 return "Font(assetManager, path=$path, weight=$weight, style=$style)" 94 } 95 equalsnull96 override fun equals(other: Any?): Boolean { 97 if (this === other) return true 98 if (other !is AndroidAssetFont) return false 99 100 if (path != other.path) return false 101 if (variationSettings != other.variationSettings) return false 102 103 return true 104 } 105 hashCodenull106 override fun hashCode(): Int { 107 var result = path.hashCode() 108 result = 31 * result + variationSettings.hashCode() 109 return result 110 } 111 } 112 113 @OptIn(ExperimentalTextApi::class) 114 internal class AndroidFileFont 115 constructor( 116 val file: File, 117 weight: FontWeight = FontWeight.Normal, 118 style: FontStyle = FontStyle.Normal, 119 variationSettings: FontVariation.Settings 120 ) : AndroidPreloadedFont(weight, style, variationSettings) { 121 doLoadnull122 override fun doLoad(context: Context?): Typeface? { 123 return if (Build.VERSION.SDK_INT >= 26) { 124 TypefaceBuilderCompat.createFromFile(file, context, variationSettings) 125 } else { 126 Typeface.createFromFile(file) 127 } 128 } 129 130 init { 131 typeface = doLoad(null) 132 } 133 134 override val cacheKey: String? = null 135 toStringnull136 override fun toString(): String { 137 return "Font(file=$file, weight=$weight, style=$style)" 138 } 139 } 140 141 @RequiresApi(26) 142 @OptIn(ExperimentalTextApi::class) 143 internal class AndroidFileDescriptorFont 144 constructor( 145 val fileDescriptor: ParcelFileDescriptor, 146 weight: FontWeight = FontWeight.Normal, 147 style: FontStyle = FontStyle.Normal, 148 variationSettings: FontVariation.Settings 149 ) : AndroidPreloadedFont(weight, style, variationSettings) { 150 doLoadnull151 override fun doLoad(context: Context?): Typeface? { 152 return if (Build.VERSION.SDK_INT >= 26) { 153 TypefaceBuilderCompat.createFromFileDescriptor( 154 fileDescriptor, 155 context, 156 variationSettings 157 ) 158 } else { 159 throw IllegalArgumentException("Cannot create font from file descriptor for SDK < 26") 160 } 161 } 162 163 init { 164 typeface = doLoad(null) 165 } 166 167 override val cacheKey: String? = null 168 toStringnull169 override fun toString(): String { 170 return "Font(fileDescriptor=$fileDescriptor, weight=$weight, style=$style)" 171 } 172 } 173 174 @RequiresApi(api = 26) 175 private object TypefaceBuilderCompat { 176 @ExperimentalTextApi createFromAssetsnull177 fun createFromAssets( 178 assetManager: AssetManager, 179 path: String, 180 context: Context?, 181 variationSettings: FontVariation.Settings 182 ): Typeface? { 183 if (context == null) { 184 return null 185 } 186 return Typeface.Builder(assetManager, path) 187 .setFontVariationSettings(variationSettings.toVariationSettings(context)) 188 .build() 189 } 190 191 @ExperimentalTextApi createFromFilenull192 fun createFromFile( 193 file: File, 194 context: Context?, 195 variationSettings: FontVariation.Settings 196 ): Typeface? { 197 if (context == null) { 198 return null 199 } 200 return Typeface.Builder(file) 201 .setFontVariationSettings(variationSettings.toVariationSettings(context)) 202 .build() 203 } 204 205 @ExperimentalTextApi createFromFileDescriptornull206 fun createFromFileDescriptor( 207 fileDescriptor: ParcelFileDescriptor, 208 context: Context?, 209 variationSettings: FontVariation.Settings, 210 ): Typeface? { 211 if (context == null) { 212 return null 213 } 214 return Typeface.Builder(fileDescriptor.fileDescriptor) 215 .setFontVariationSettings(variationSettings.toVariationSettings(context)) 216 .build() 217 } 218 219 @RequiresApi(Build.VERSION_CODES.O) 220 @ExperimentalTextApi FontVariationnull221 private fun FontVariation.Settings.toVariationSettings( 222 context: Context? 223 ): Array<FontVariationAxis> { 224 val density = 225 if (context != null) { 226 Density(context) 227 } else if (!needsDensity) { 228 // we don't need density, so make a fake one and be on with it 229 Density(1f, 1f) 230 } else { 231 // cannot reach 232 throw IllegalStateException("Required density, but not provided") 233 } 234 return settings 235 .fastMap { setting -> 236 FontVariationAxis(setting.axisName, setting.toVariationValue(density)) 237 } 238 .toTypedArray() 239 } 240 } 241