@iternio/react-native-auto-play 0.3.12 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +43 -1
  2. package/ReactNativeAutoPlay.podspec +0 -4
  3. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt +10 -1
  4. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/utils/SymbolFont.kt +29 -30
  5. package/ios/utils/SymbolFont.swift +44 -44
  6. package/ios/utils/VoiceInputManager.swift +1 -1
  7. package/lib/index.d.ts +1 -0
  8. package/lib/index.js +1 -0
  9. package/lib/types/Image.d.ts +33 -4
  10. package/lib/types/Maneuver.d.ts +2 -10
  11. package/lib/utils/NitroImage.d.ts +23 -2
  12. package/lib/utils/NitroImage.js +57 -3
  13. package/nitrogen/generated/android/c++/JGlyphImage.hpp +6 -1
  14. package/nitrogen/generated/android/c++/JNitroImage.hpp +1 -1
  15. package/nitrogen/generated/android/c++/JVariant_GlyphImage_AssetImage_RemoteImage.hpp +1 -1
  16. package/nitrogen/generated/android/c++/JVariant_PreferredImageLane_ImageLane.hpp +1 -1
  17. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/GlyphImage.kt +5 -2
  18. package/nitrogen/generated/ios/c++/HybridCarPlayDashboardSpecSwift.hpp +1 -1
  19. package/nitrogen/generated/ios/swift/GlyphImage.swift +7 -2
  20. package/nitrogen/generated/shared/c++/GlyphImage.hpp +6 -1
  21. package/package.json +2 -3
  22. package/src/index.ts +1 -0
  23. package/src/types/Image.ts +53 -18
  24. package/src/types/Maneuver.ts +3 -10
  25. package/src/utils/NitroImage.ts +66 -5
  26. package/android/src/main/res/font/materialsymbolsoutlined_regular.ttf +0 -0
  27. package/ios/Assets/MaterialSymbolsOutlined-Regular.ttf +0 -0
  28. package/lib/types/Glyphmap.d.ts +0 -4105
  29. package/lib/types/Glyphmap.js +0 -4105
  30. package/src/types/Glyphmap.ts +0 -4107
package/README.md CHANGED
@@ -304,7 +304,49 @@ When using build variants, Android Studio may not be aware of the selected varia
304
304
  To work around this and allow for debugging or enhancing the Android Automotive-specific implementation, you can temporarily set the automotive flags in your `gradle.properties` file or your default `.env` file before running a Gradle sync.
305
305
 
306
306
  ## Icons
307
- The library is using [Material Symbols](https://fonts.google.com/icons) for iconography. The font is bundled with the library, so no extra setup is required. You can use these icons on both Android Auto and CarPlay.
307
+ The library does **not** bundle any icon font the consuming app must provide one.
308
+
309
+ ### Setup
310
+
311
+ 1. Add a `.ttf` font file to your native projects:
312
+ - **iOS** — add `<name>.ttf` to your app bundle (no `UIAppFonts` entry needed — the library registers it via CoreText automatically).
313
+ - **Android** — place `<name>.ttf` in `res/font/`.
314
+
315
+ For cross-platform compatibility use **lowercase names with underscores only** (e.g. `material_symbols`).
316
+
317
+ 2. Register the font and an optional glyph map at startup:
318
+
319
+ ```ts
320
+ import { setIconFont } from '@iternio/react-native-auto-play';
321
+ import { glyphMap } from './assets/Glyphmap';
322
+
323
+ setIconFont('material_symbols', glyphMap);
324
+ ```
325
+
326
+ 3. Use glyph images by name or code point:
327
+
328
+ ```ts
329
+ { type: 'glyph', name: 'directions_car' }
330
+ { type: 'glyph', codepoint: 0xe531 }
331
+ ```
332
+
333
+ `setIconFont` must be called once before the first glyph is used (subsequent calls are ignored). If no font is registered, the library throws an error when a glyph image is rendered.
334
+
335
+ ### Type-safe glyph names
336
+
337
+ To get autocompletion and type checking for glyph names, create a declaration file in your app (e.g. `autoplay-glyphs.d.ts`):
338
+
339
+ ```ts
340
+ import type { GlyphName } from './assets/Glyphmap';
341
+
342
+ declare module '@iternio/react-native-auto-play' {
343
+ interface AutoPlayGlyphMap extends Record<GlyphName, number> {}
344
+ }
345
+ ```
346
+
347
+ Without this augmentation, `name` accepts any `string`. With it, only keys from your glyph map are allowed and you get full autocompletion.
348
+
349
+ The example app uses [Material Symbols](https://fonts.google.com/icons). See `apps/example/assets/symbolFont/` for the glyph map generation script.
308
350
 
309
351
  It is also possible to use custom bundled images (e.g. PNG, WEBP or Vector Drawables). Make sure to add them to your native projects.
310
352
  - iOS: Add to your `Images.xcassets`
@@ -25,10 +25,6 @@ Pod::Spec.new do |s|
25
25
  # react helpers like RCTConvert
26
26
  s.public_header_files = Array(s.attributes_hash['public_header_files']) + ["ios/ReactHelpers/*.h"]
27
27
 
28
- s.resource_bundles = {
29
- "ReactNativeAutoPlay" => ['ios/Assets/**/*.ttf']
30
- }
31
-
32
28
  s.pod_target_xcconfig = {
33
29
  # C++ compiler flags, mainly for folly.
34
30
  "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
@@ -598,11 +598,20 @@ object Parser {
598
598
  val image = result?.get()
599
599
  try {
600
600
  if (image is CloseableBitmap) {
601
- return image.underlyingBitmap.copy(Bitmap.Config.ARGB_8888, false)
601
+ // underlyingBitmap can be null when Fresco decodes to a CloseableBitmap
602
+ // whose backing bitmap has already been recycled or failed to allocate;
603
+ // copy() can also throw (e.g., OOM on very large remote images). Either
604
+ // way we return null so the caller falls back to a placeholder icon.
605
+ return image.underlyingBitmap?.copy(Bitmap.Config.ARGB_8888, false)
602
606
  } else if (image is CloseableXml) {
603
607
  val drawable = image.buildDrawable()
604
608
  return drawable?.toBitmap(width = image.width, height = image.height, Bitmap.Config.ARGB_8888)
605
609
  }
610
+ } catch (_: Exception) {
611
+ // Any decode/copy failure (OOM, recycled bitmap, invalid config, …) should
612
+ // not crash the car app — the image is optional decoration and the caller
613
+ // handles a null return with CarIcon.ALERT.
614
+ return null
606
615
  } finally {
607
616
  image?.close()
608
617
  result?.close()
@@ -10,39 +10,36 @@ import android.graphics.Typeface
10
10
  import androidx.car.app.CarContext
11
11
  import androidx.core.content.res.ResourcesCompat
12
12
  import androidx.core.graphics.createBitmap
13
- import androidx.core.graphics.drawable.IconCompat
14
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.GlyphImage
15
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroImage
16
13
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.BuildConfig
17
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.R
18
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.Parser
14
+ import com.margelo.nitro.swe.iternio.reactnativeautoplay.GlyphImage
19
15
 
20
16
  object SymbolFont {
21
- const val TAG = "SymbolFont"
22
-
23
- private var typeface: Typeface? = null
24
-
25
- private fun loadFont(context: Context) {
26
- if (typeface != null) {
27
- return
28
- }
17
+ private var cachedFontName: String? = null
18
+ private var cachedTypeface: Typeface? = null
29
19
 
30
- typeface = ResourcesCompat.getFont(context, R.font.materialsymbolsoutlined_regular)
20
+ private fun loadTypeface(context: Context, fontName: String): Typeface? {
21
+ if (fontName == cachedFontName) return cachedTypeface
22
+ val id = context.resources.getIdentifier(
23
+ fontName.lowercase(), "font", context.packageName
24
+ )
25
+ if (id == 0) return null
26
+ val tf = ResourcesCompat.getFont(context, id) ?: return null
27
+ cachedFontName = fontName
28
+ cachedTypeface = tf
29
+ return tf
31
30
  }
32
31
 
33
32
  private fun imageFromGlyph(
34
33
  context: Context,
35
- glyph: Double,
34
+ glyphImage: GlyphImage,
36
35
  color: Int,
37
36
  backgroundColor: Int,
38
37
  cornerRadius: Float = 8f, //TODO: make accessible and add it to GlyphImage.cacheKey
39
- fontScale: Float,
40
38
  ): Bitmap? {
41
- loadFont(context)
42
-
43
- val font = typeface ?: run {
44
- return null
45
- }
39
+ val font =
40
+ loadTypeface(context, glyphImage.fontName) ?: run {
41
+ return null
42
+ }
46
43
 
47
44
  val virtualScreenDensity = context.resources.displayMetrics.density
48
45
  val scale = BuildConfig.SCALE_FACTOR * virtualScreenDensity
@@ -59,6 +56,8 @@ object SymbolFont {
59
56
  }
60
57
  canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint)
61
58
 
59
+ val fontScale = (glyphImage.fontScale ?: 1.0).toFloat()
60
+
62
61
  // Setup text paint
63
62
  paint.reset()
64
63
  paint = Paint().apply {
@@ -70,7 +69,7 @@ object SymbolFont {
70
69
  }
71
70
 
72
71
  // Get the character from codepoint
73
- val codepoint = glyph.toInt()
72
+ val codepoint = glyphImage.glyph.toInt()
74
73
  val text = String(Character.toChars(codepoint))
75
74
 
76
75
  // Measure text
@@ -94,13 +93,13 @@ object SymbolFont {
94
93
  return bitmap
95
94
  }
96
95
 
97
- bitmap = imageFromGlyph(
98
- context = context,
99
- glyph = image.glyph,
100
- color = image.color.get(context),
101
- backgroundColor = image.backgroundColor.get(context),
102
- fontScale = (image.fontScale ?: 1.0).toFloat()
103
- )
96
+ bitmap =
97
+ imageFromGlyph(
98
+ context = context,
99
+ glyphImage = image,
100
+ color = image.color.get(context),
101
+ backgroundColor = image.backgroundColor.get(context),
102
+ )
104
103
 
105
104
  bitmap?.let {
106
105
  BitmapCache.put(context, image, it)
@@ -108,4 +107,4 @@ object SymbolFont {
108
107
 
109
108
  return bitmap
110
109
  }
111
- }
110
+ }
@@ -9,67 +9,65 @@ import CoreText
9
9
  import UIKit
10
10
 
11
11
  class SymbolFont {
12
- private static let defaultCanvasSize = 32
12
+ private static var cachedFontName: String?
13
+ private static var cachedPSName: String?
13
14
 
14
- private static var isRegistered = false
15
- private static var fontName: String?
16
-
17
- static func loadFont() {
18
- let podBundle = Bundle(for: SymbolFont.self)
15
+ private static func loadFont(named fontName: String) -> String? {
16
+ if fontName == cachedFontName {
17
+ return cachedPSName
18
+ }
19
19
 
20
- guard
21
- let bundleURL = podBundle.url(
22
- forResource: "ReactNativeAutoPlay",
23
- withExtension: "bundle"
24
- ),
25
- let resourceBundle = Bundle(url: bundleURL),
26
- let fontURL = resourceBundle.url(
27
- forResource: "MaterialSymbolsOutlined-Regular",
28
- withExtension: "ttf"
29
- )
30
- else {
31
- return
20
+ guard let url = Bundle.main.url(forResource: fontName, withExtension: "ttf") else {
21
+ print("[AutoPlay] \(fontName).ttf not found in the app bundle — glyph images will not render.")
22
+ return nil
32
23
  }
33
24
 
34
- guard let fontData = try? Data(contentsOf: fontURL) as CFData,
25
+ guard let fontData = try? Data(contentsOf: url) as CFData,
35
26
  let provider = CGDataProvider(data: fontData),
36
27
  let font = CGFont(provider)
37
28
  else {
38
- return
29
+ return nil
39
30
  }
40
31
 
41
32
  var error: Unmanaged<CFError>?
42
33
  CTFontManagerRegisterGraphicsFont(font, &error)
43
- if let error = error?.takeUnretainedValue() {
44
- print("Failed to register font: \(error)")
34
+ // Ignore already-registered errors (e.g. hot reload)
35
+
36
+ guard let psName = font.fullName as? String else {
37
+ return nil
45
38
  }
46
- else {
47
- print("Font \(font.fullName as String? ?? "unknown") registered")
39
+
40
+ cachedFontName = fontName
41
+ cachedPSName = psName
42
+ return psName
43
+ }
44
+
45
+ private static func uiFont(for glyphImage: GlyphImage, size: CGFloat, fontScale: CGFloat) -> UIFont? {
46
+ let pointSize = size * fontScale
47
+
48
+ guard let psName = loadFont(named: glyphImage.fontName) else {
49
+ return nil
48
50
  }
49
51
 
50
- SymbolFont.fontName = font.fullName as? String
51
- SymbolFont.isRegistered = true
52
+ return UIFont(name: psName, size: pointSize)
52
53
  }
53
54
 
54
55
  // creates a single color UIImage
55
56
  static func imageFromGlyph(
56
- glyph: Double,
57
+ glyphImage: GlyphImage,
57
58
  foregroundColor: UIColor,
58
59
  backgroundColor: UIColor,
59
60
  size: CGFloat,
60
61
  fontScale: CGFloat
61
62
  ) -> UIImage? {
62
- if !SymbolFont.isRegistered {
63
- SymbolFont.loadFont()
63
+ guard let font = uiFont(for: glyphImage, size: size, fontScale: fontScale) else {
64
+ return nil
64
65
  }
65
66
 
66
- guard let fontName = SymbolFont.fontName,
67
- let font = UIFont(name: fontName, size: size * fontScale)
68
- else {
67
+ guard let scalar = UnicodeScalar(UInt32(glyphImage.glyph)) else {
69
68
  return nil
70
69
  }
71
-
72
- let codepoint = String(UnicodeScalar(UInt32(glyph))!)
70
+ let codepoint = String(Character(scalar))
73
71
  let canvasSize = CGSize(width: size, height: size)
74
72
  let rect = CGRect(origin: .zero, size: canvasSize)
75
73
 
@@ -99,14 +97,14 @@ class SymbolFont {
99
97
  let y = (canvasSize.height - textSize.height) / 2
100
98
  attrString.draw(at: CGPoint(x: x, y: y))
101
99
 
102
- let image = UIGraphicsGetImageFromCurrentImageContext()
100
+ let uiImage = UIGraphicsGetImageFromCurrentImageContext()
103
101
  UIGraphicsEndImageContext()
104
102
 
105
- return image
103
+ return uiImage
106
104
  }
107
105
 
108
106
  static func imageFromGlyph(
109
- glyph: Double,
107
+ glyphImage: GlyphImage,
110
108
  size: CGFloat,
111
109
  foregroundColor: NitroColor,
112
110
  backgroundColor: NitroColor,
@@ -115,7 +113,7 @@ class SymbolFont {
115
113
  ) -> UIImage? {
116
114
  guard
117
115
  let lightImage = imageFromGlyph(
118
- glyph: glyph,
116
+ glyphImage: glyphImage,
119
117
  foregroundColor: Parser.doubleToColor(
120
118
  value: foregroundColor.lightColor
121
119
  ),
@@ -126,7 +124,7 @@ class SymbolFont {
126
124
  fontScale: fontScale
127
125
  ),
128
126
  let darkImage = imageFromGlyph(
129
- glyph: glyph,
127
+ glyphImage: glyphImage,
130
128
  foregroundColor: Parser.doubleToColor(
131
129
  value: foregroundColor.darkColor
132
130
  ),
@@ -167,6 +165,8 @@ class SymbolFont {
167
165
  ) -> UIImage? {
168
166
  guard let image else { return nil }
169
167
 
168
+ let fontScale = image.fontScale ?? 1.0
169
+
170
170
  if noImageAsset {
171
171
  let foregroundColor = Parser.doubleToColor(
172
172
  value: traitCollection.userInterfaceStyle == .light
@@ -180,21 +180,21 @@ class SymbolFont {
180
180
  )
181
181
 
182
182
  return SymbolFont.imageFromGlyph(
183
- glyph: image.glyph,
183
+ glyphImage: image,
184
184
  foregroundColor: foregroundColor,
185
185
  backgroundColor: backgroundColor,
186
186
  size: size,
187
- fontScale: image.fontScale ?? 1.0
187
+ fontScale: fontScale
188
188
  )
189
189
  }
190
190
 
191
191
  return SymbolFont.imageFromGlyph(
192
- glyph: image.glyph,
192
+ glyphImage: image,
193
193
  size: size,
194
194
  foregroundColor: image.color,
195
195
  backgroundColor: image.backgroundColor,
196
- fontScale: image.fontScale ?? 1.0,
196
+ fontScale: fontScale,
197
197
  traitCollection: traitCollection
198
- )!
198
+ )
199
199
  }
200
200
  }
@@ -90,7 +90,7 @@ class VoiceInputManager {
90
90
 
91
91
  // Activate the session first so inputNode reports the correct hardware format
92
92
  let session = AVAudioSession.sharedInstance()
93
- try session.setCategory(.playAndRecord, mode: .measurement, options: [.mixWithOthers])
93
+ try session.setCategory(.playAndRecord, mode: .measurement, options: [])
94
94
  try session.setActive(true)
95
95
 
96
96
  if let interfaceController {
package/lib/index.d.ts CHANGED
@@ -43,3 +43,4 @@ export * from './types/Trip';
43
43
  export type { AlertPriority, NavigationAlert as Alert, NavigationAlertAction as AlertAction, } from './utils/NitroAlert';
44
44
  export type { ThemedColor } from './utils/NitroColor';
45
45
  export type { GridButton } from './utils/NitroGrid';
46
+ export { setIconFont } from './utils/NitroImage';
package/lib/index.js CHANGED
@@ -45,3 +45,4 @@ export * from './types/SignInMethod';
45
45
  export * from './types/Telemetry';
46
46
  export * from './types/Text';
47
47
  export * from './types/Trip';
48
+ export { setIconFont } from './utils/NitroImage';
@@ -1,8 +1,22 @@
1
1
  import type { ImageSourcePropType } from 'react-native';
2
2
  import type { ThemedColor } from '../utils/NitroColor';
3
- import type { GlyphName } from './Glyphmap';
4
- export type AutoImage = {
5
- name: GlyphName;
3
+ /**
4
+ * Augment this interface in your app to get type-safe glyph name autocompletion.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * // autoplay-glyphs.d.ts
9
+ * import type { GlyphName } from './assets/symbolFont/Glyphmap';
10
+ * declare module '@iternio/react-native-auto-play' {
11
+ * interface AutoPlayGlyphMap extends Record<GlyphName, number> {}
12
+ * }
13
+ * ```
14
+ */
15
+ export interface AutoPlayGlyphMap {
16
+ }
17
+ /** Resolves to the augmented glyph name union, or falls back to `string` when not augmented. */
18
+ export type GlyphMapKey = keyof AutoPlayGlyphMap extends never ? string : Extract<keyof AutoPlayGlyphMap, string>;
19
+ type GlyphStyleFields = {
6
20
  /**
7
21
  * Sets the icon dark and light mode color or a single color for both.
8
22
  * Defaults to white for dark mode and black for light mode if not specified.
@@ -15,8 +29,22 @@ export type AutoImage = {
15
29
  */
16
30
  backgroundColor?: ThemedColor | string;
17
31
  fontScale?: number;
32
+ };
33
+ /** Glyph by name — looked up in the glyph map registered via {@link setIconFont}. */
34
+ export type AutoGlyphByName = GlyphStyleFields & {
18
35
  type: 'glyph';
19
- } | {
36
+ /** Key in the glyph map passed to `setIconFont`. */
37
+ name: GlyphMapKey;
38
+ /** Optional override — if set, used instead of the map lookup. */
39
+ codepoint?: number;
40
+ };
41
+ /** Glyph by raw Unicode code point. */
42
+ export type AutoGlyphByCodepoint = GlyphStyleFields & {
43
+ type: 'glyph';
44
+ codepoint: number;
45
+ };
46
+ export type AutoGlyph = AutoGlyphByName | AutoGlyphByCodepoint;
47
+ export type AutoImage = AutoGlyph | {
20
48
  image: ImageSourcePropType;
21
49
  /**
22
50
  * if specified the image gets tinted, if not it will just use the original image
@@ -38,3 +66,4 @@ export type AutoImage = {
38
66
  timeoutMs?: number;
39
67
  type: 'remote';
40
68
  };
69
+ export {};
@@ -1,6 +1,6 @@
1
1
  import type { ImageSourcePropType } from 'react-native';
2
2
  import type { ThemedColor } from '../utils/NitroColor';
3
- import type { GlyphName } from './Glyphmap';
3
+ import type { AutoGlyphByCodepoint, AutoGlyphByName } from './Image';
4
4
  import type { TravelEstimates } from './Trip';
5
5
  export declare enum ManeuverType {
6
6
  Depart = 0,
@@ -148,15 +148,7 @@ export interface PreferredLane extends Lane {
148
148
  highlightedAngle: number;
149
149
  isPreferred: boolean;
150
150
  }
151
- export type ManeuverImage = {
152
- name: GlyphName;
153
- /**
154
- * make sure to specify a color with a proper contrast ratio to cardBackgroundColor otherwise it might not get applied
155
- * defaults to white/black for dark/light mode
156
- */
157
- color?: ThemedColor | string;
158
- type: 'glyph';
159
- } | {
151
+ export type ManeuverImage = Pick<AutoGlyphByName, 'type' | 'name' | 'codepoint' | 'color'> | Pick<AutoGlyphByCodepoint, 'type' | 'codepoint' | 'color'> | {
160
152
  image: ImageSourcePropType;
161
153
  /**
162
154
  * if specified the image gets tinted, if not it will just use the original image
@@ -1,12 +1,34 @@
1
1
  import { type ImageResolvedAssetSource } from 'react-native';
2
2
  import type { AutoImage } from '../types/Image';
3
3
  import { type NitroColor } from './NitroColor';
4
+ /**
5
+ * Register the icon font and (optionally) a glyph map for name-based lookups.
6
+ * Must be called **once** before creating any templates. Subsequent calls are ignored.
7
+ *
8
+ * The font name maps directly to a native font asset:
9
+ * - **Android** — `res/font/<name>.ttf` (must be lowercase)
10
+ * - **iOS** — `<name>.ttf` in the app bundle (registered via CoreText automatically)
11
+ *
12
+ * For cross-platform compatibility use lowercase with underscores only.
13
+ *
14
+ * @param name Native font asset name (without extension).
15
+ * @param glyphMap Optional map of glyph names to Unicode code points.
16
+ * When provided, glyphs can use `{ type: 'glyph', name: 'icon_name' }`.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { glyphMap } from './assets/Glyphmap';
21
+ * setIconFont('material_symbols', glyphMap);
22
+ * ```
23
+ */
24
+ export declare function setIconFont(name: string, glyphMap?: Record<string, number>): void;
4
25
  interface AssetImage extends ImageResolvedAssetSource {
5
26
  color?: NitroColor;
6
27
  packager_asset: boolean;
7
28
  }
8
29
  interface GlyphImage {
9
30
  glyph: number;
31
+ fontName: string;
10
32
  color: NitroColor;
11
33
  backgroundColor: NitroColor;
12
34
  fontScale?: number;
@@ -17,8 +39,7 @@ interface RemoteImage {
17
39
  timeoutMs?: number;
18
40
  }
19
41
  /**
20
- * we need to map the ButtonImage.name from GlyphName to
21
- * the actual numeric value so we need a nitro specific type
42
+ * NitroModules-compatible image types passed to native.
22
43
  */
23
44
  export type NitroImage = GlyphImage | AssetImage | RemoteImage;
24
45
  declare function convert(image: AutoImage): NitroImage;
@@ -1,14 +1,68 @@
1
1
  import { Image } from 'react-native';
2
- import { glyphMap } from '../types/Glyphmap';
3
2
  import { NitroColorUtil } from './NitroColor';
3
+ let _iconFont;
4
+ let _glyphMap;
5
+ /**
6
+ * Register the icon font and (optionally) a glyph map for name-based lookups.
7
+ * Must be called **once** before creating any templates. Subsequent calls are ignored.
8
+ *
9
+ * The font name maps directly to a native font asset:
10
+ * - **Android** — `res/font/<name>.ttf` (must be lowercase)
11
+ * - **iOS** — `<name>.ttf` in the app bundle (registered via CoreText automatically)
12
+ *
13
+ * For cross-platform compatibility use lowercase with underscores only.
14
+ *
15
+ * @param name Native font asset name (without extension).
16
+ * @param glyphMap Optional map of glyph names to Unicode code points.
17
+ * When provided, glyphs can use `{ type: 'glyph', name: 'icon_name' }`.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { glyphMap } from './assets/Glyphmap';
22
+ * setIconFont('material_symbols', glyphMap);
23
+ * ```
24
+ */
25
+ export function setIconFont(name, glyphMap) {
26
+ if (_iconFont != null) {
27
+ return;
28
+ }
29
+ _iconFont = name;
30
+ _glyphMap = glyphMap;
31
+ }
32
+ function getIconFont() {
33
+ if (_iconFont == null) {
34
+ throw new Error('No icon font configured. Call setIconFont("your_font_name") before using glyph images.');
35
+ }
36
+ return _iconFont;
37
+ }
38
+ function resolveGlyph(image) {
39
+ if ('name' in image && image.name !== undefined) {
40
+ if (image.codepoint !== undefined) {
41
+ return image.codepoint;
42
+ }
43
+ if (_glyphMap == null) {
44
+ throw new Error(`Glyph name "${image.name}" used but no glyph map was provided to setIconFont().`);
45
+ }
46
+ const cp = _glyphMap[image.name];
47
+ if (cp === undefined) {
48
+ throw new Error(`Glyph name "${image.name}" not found in the glyph map.`);
49
+ }
50
+ return cp;
51
+ }
52
+ if (image.codepoint !== undefined) {
53
+ return image.codepoint;
54
+ }
55
+ throw new Error('Glyph image must provide either `name` or `codepoint`.');
56
+ }
4
57
  function convert(image) {
5
58
  if (image == null) {
6
59
  return undefined;
7
60
  }
8
61
  if (image.type === 'glyph') {
9
- const { color = { darkColor: 'white', lightColor: 'black' }, fontScale, name, backgroundColor = 'transparent', } = image;
62
+ const { color = { darkColor: 'white', lightColor: 'black' }, fontScale, backgroundColor = 'transparent', } = image;
10
63
  return {
11
- glyph: glyphMap[name],
64
+ glyph: resolveGlyph(image),
65
+ fontName: getIconFont(),
12
66
  color: NitroColorUtil.convert(color),
13
67
  backgroundColor: NitroColorUtil.convert(backgroundColor),
14
68
  fontScale,
@@ -13,6 +13,7 @@
13
13
  #include "JNitroColor.hpp"
14
14
  #include "NitroColor.hpp"
15
15
  #include <optional>
16
+ #include <string>
16
17
 
17
18
  namespace margelo::nitro::swe::iternio::reactnativeautoplay {
18
19
 
@@ -35,6 +36,8 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
35
36
  static const auto clazz = javaClassStatic();
36
37
  static const auto fieldGlyph = clazz->getField<double>("glyph");
37
38
  double glyph = this->getFieldValue(fieldGlyph);
39
+ static const auto fieldFontName = clazz->getField<jni::JString>("fontName");
40
+ jni::local_ref<jni::JString> fontName = this->getFieldValue(fieldFontName);
38
41
  static const auto fieldColor = clazz->getField<JNitroColor>("color");
39
42
  jni::local_ref<JNitroColor> color = this->getFieldValue(fieldColor);
40
43
  static const auto fieldBackgroundColor = clazz->getField<JNitroColor>("backgroundColor");
@@ -43,6 +46,7 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
43
46
  jni::local_ref<jni::JDouble> fontScale = this->getFieldValue(fieldFontScale);
44
47
  return GlyphImage(
45
48
  glyph,
49
+ fontName->toStdString(),
46
50
  color->toCpp(),
47
51
  backgroundColor->toCpp(),
48
52
  fontScale != nullptr ? std::make_optional(fontScale->value()) : std::nullopt
@@ -55,12 +59,13 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
55
59
  */
56
60
  [[maybe_unused]]
57
61
  static jni::local_ref<JGlyphImage::javaobject> fromCpp(const GlyphImage& value) {
58
- using JSignature = JGlyphImage(double, jni::alias_ref<JNitroColor>, jni::alias_ref<JNitroColor>, jni::alias_ref<jni::JDouble>);
62
+ using JSignature = JGlyphImage(double, jni::alias_ref<jni::JString>, jni::alias_ref<JNitroColor>, jni::alias_ref<JNitroColor>, jni::alias_ref<jni::JDouble>);
59
63
  static const auto clazz = javaClassStatic();
60
64
  static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
61
65
  return create(
62
66
  clazz,
63
67
  value.glyph,
68
+ jni::make_jstring(value.fontName),
64
69
  JNitroColor::fromCpp(value.color),
65
70
  JNitroColor::fromCpp(value.backgroundColor),
66
71
  value.fontScale.has_value() ? jni::JDouble::valueOf(value.fontScale.value()) : nullptr
@@ -15,11 +15,11 @@
15
15
  #include "RemoteImage.hpp"
16
16
  #include <variant>
17
17
  #include "JGlyphImage.hpp"
18
+ #include <string>
18
19
  #include "NitroColor.hpp"
19
20
  #include "JNitroColor.hpp"
20
21
  #include <optional>
21
22
  #include "JAssetImage.hpp"
22
- #include <string>
23
23
  #include "JRemoteImage.hpp"
24
24
 
25
25
  namespace margelo::nitro::swe::iternio::reactnativeautoplay {
@@ -15,11 +15,11 @@
15
15
  #include "RemoteImage.hpp"
16
16
  #include <variant>
17
17
  #include "JGlyphImage.hpp"
18
+ #include <string>
18
19
  #include "NitroColor.hpp"
19
20
  #include "JNitroColor.hpp"
20
21
  #include <optional>
21
22
  #include "JAssetImage.hpp"
22
- #include <string>
23
23
  #include "JRemoteImage.hpp"
24
24
 
25
25
  namespace margelo::nitro::swe::iternio::reactnativeautoplay {
@@ -19,11 +19,11 @@
19
19
  #include "RemoteImage.hpp"
20
20
  #include "JNitroImage.hpp"
21
21
  #include "JGlyphImage.hpp"
22
+ #include <string>
22
23
  #include "NitroColor.hpp"
23
24
  #include "JNitroColor.hpp"
24
25
  #include <optional>
25
26
  #include "JAssetImage.hpp"
26
- #include <string>
27
27
  #include "JRemoteImage.hpp"
28
28
  #include <vector>
29
29
  #include "JImageLane.hpp"