Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions apps/example/assets/symbolFont/generateGlyphmap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// generateGlyphmap.js — generates a typed glyph map from the Material Symbols codepoints file.
const fs = require('fs');

const FontName = 'MaterialSymbolsOutlined-Regular';
const input = fs.readFileSync(`${__dirname}/${FontName}.codepoints`, 'utf8');
const lines = input.split(/\r?\n/);

const entries = [];
for (const line of lines) {
if (!line.trim()) continue;
const [name, hex] = line.split(/\s+/);
const codepoint = Number.parseInt(hex, 16);
entries.push([name, codepoint]);
}

const linesOut = [
`// Auto-generated from ${FontName}.codepoints — do not edit!`,
'export const glyphMap = {',
...entries.map(([name, cp]) => ` "${name}": 0x${cp.toString(16)},`),
'} as const;',
'',
'export type GlyphName = keyof typeof glyphMap;',
'',
];

fs.writeFileSync(`${__dirname}/Glyphmap.ts`, linesOut.join('\n'));
console.log('Glyphmap.ts written!');
5 changes: 5 additions & 0 deletions apps/example/autoplay-glyphs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { GlyphName } from './assets/symbolFont/Glyphmap';

declare module '@iternio/react-native-auto-play' {
interface AutoPlayGlyphMap extends Record<GlyphName, number> {}
}
12 changes: 4 additions & 8 deletions apps/example/ios/example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
1A2B3C4D5E6F70890A1B2C3D /* material_symbols.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F70890A1B2C3E /* material_symbols.ttf */; };
3585ECDB0EF282D3532C23DC /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5FA2767A6B04BFB3C75275D /* libPods-example.a */; };
734D4B1D2E89AFC80035C035 /* Entitlements.plist in Resources */ = {isa = PBXBuildFile; fileRef = 734D4B1C2E89AFC80035C035 /* Entitlements.plist */; };
73D4B86D2F92354D00628A0C /* PCMPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 73D4B86C2F92354D00628A0C /* PCMPlayer.m */; };
Expand All @@ -21,6 +22,7 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
1A2B3C4D5E6F70890A1B2C3E /* material_symbols.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = material_symbols.ttf; path = Resources/material_symbols.ttf; sourceTree = "<group>"; };
734D4B1C2E89AFC80035C035 /* Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Entitlements.plist; path = example/Entitlements.plist; sourceTree = "<group>"; };
73D4B86C2F92354D00628A0C /* PCMPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = PCMPlayer.m; path = example/PCMPlayer.m; sourceTree = "<group>"; };
73D4B86E2F92355500628A0C /* example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "example-Bridging-Header.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -79,6 +81,7 @@
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* example */,
1A2B3C4D5E6F70890A1B2C3E /* material_symbols.ttf */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
Expand Down Expand Up @@ -170,6 +173,7 @@
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
ED703283CBD2F685D063C276 /* PrivacyInfo.xcprivacy in Resources */,
1A2B3C4D5E6F70890A1B2C3D /* material_symbols.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -222,14 +226,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
Expand All @@ -243,14 +243,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
Expand Down
4 changes: 4 additions & 0 deletions apps/example/src/AutoPlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
MessageTemplate,
type RootComponentInitialProps,
SafeAreaView,
setIconFont,
type TextButton,
useMapTemplate,
} from '@iternio/react-native-auto-play';
import { glyphMap } from '../assets/symbolFont/Glyphmap';
import type { UnsubscribeListener } from '@reduxjs/toolkit';
import { useEffect, useState } from 'react';
import { Platform, Text, View } from 'react-native';
Expand Down Expand Up @@ -229,6 +231,8 @@ const AutoPlayRoot = (props: RootComponentInitialProps) => {
};

const registerRunnable = () => {
setIconFont('material_symbols', glyphMap);

const onConnect = () => {
const rootTemplate = new MapTemplate({
component: AutoPlayRoot,
Expand Down
44 changes: 43 additions & 1 deletion packages/react-native-autoplay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,49 @@ When using build variants, Android Studio may not be aware of the selected varia
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.

## Icons
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.
The library does **not** bundle any icon font — the consuming app must provide one.

### Setup

1. Add a `.ttf` font file to your native projects:
- **iOS** — add `<name>.ttf` to your app bundle (no `UIAppFonts` entry needed — the library registers it via CoreText automatically).
- **Android** — place `<name>.ttf` in `res/font/`.

For cross-platform compatibility use **lowercase names with underscores only** (e.g. `material_symbols`).

2. Register the font and an optional glyph map at startup:

```ts
import { setIconFont } from '@iternio/react-native-auto-play';
import { glyphMap } from './assets/Glyphmap';

setIconFont('material_symbols', glyphMap);
```

3. Use glyph images by name or code point:

```ts
{ type: 'glyph', name: 'directions_car' }
{ type: 'glyph', codepoint: 0xe531 }
```

`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.

### Type-safe glyph names

To get autocompletion and type checking for glyph names, create a declaration file in your app (e.g. `autoplay-glyphs.d.ts`):

```ts
import type { GlyphName } from './assets/Glyphmap';

declare module '@iternio/react-native-auto-play' {
interface AutoPlayGlyphMap extends Record<GlyphName, number> {}
}
```

Without this augmentation, `name` accepts any `string`. With it, only keys from your glyph map are allowed and you get full autocompletion.

The example app uses [Material Symbols](https://fonts.google.com/icons). See `apps/example/assets/symbolFont/` for the glyph map generation script.

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.
- iOS: Add to your `Images.xcassets`
Expand Down
4 changes: 0 additions & 4 deletions packages/react-native-autoplay/ReactNativeAutoPlay.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ Pod::Spec.new do |s|
# react helpers like RCTConvert
s.public_header_files = Array(s.attributes_hash['public_header_files']) + ["ios/ReactHelpers/*.h"]

s.resource_bundles = {
"ReactNativeAutoPlay" => ['ios/Assets/**/*.ttf']
}

s.pod_target_xcconfig = {
# C++ compiler flags, mainly for folly.
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,36 @@ import android.graphics.Typeface
import androidx.car.app.CarContext
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.IconCompat
import com.margelo.nitro.swe.iternio.reactnativeautoplay.GlyphImage
import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroImage
import com.margelo.nitro.swe.iternio.reactnativeautoplay.BuildConfig
import com.margelo.nitro.swe.iternio.reactnativeautoplay.R
import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.Parser
import com.margelo.nitro.swe.iternio.reactnativeautoplay.GlyphImage

object SymbolFont {
const val TAG = "SymbolFont"

private var typeface: Typeface? = null

private fun loadFont(context: Context) {
if (typeface != null) {
return
}
private var cachedFontName: String? = null
private var cachedTypeface: Typeface? = null

typeface = ResourcesCompat.getFont(context, R.font.materialsymbolsoutlined_regular)
private fun loadTypeface(context: Context, fontName: String): Typeface? {
if (fontName == cachedFontName) return cachedTypeface
val id = context.resources.getIdentifier(
fontName.lowercase(), "font", context.packageName
)
if (id == 0) return null
val tf = ResourcesCompat.getFont(context, id) ?: return null
cachedFontName = fontName
cachedTypeface = tf
return tf
}

private fun imageFromGlyph(
context: Context,
glyph: Double,
glyphImage: GlyphImage,
color: Int,
backgroundColor: Int,
cornerRadius: Float = 8f, //TODO: make accessible and add it to GlyphImage.cacheKey
fontScale: Float,
): Bitmap? {
loadFont(context)

val font = typeface ?: run {
return null
}
val font =
loadTypeface(context, glyphImage.fontName) ?: run {
return null
}

val virtualScreenDensity = context.resources.displayMetrics.density
val scale = BuildConfig.SCALE_FACTOR * virtualScreenDensity
Expand All @@ -59,6 +56,8 @@ object SymbolFont {
}
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint)

val fontScale = (glyphImage.fontScale ?: 1.0).toFloat()

// Setup text paint
paint.reset()
paint = Paint().apply {
Expand All @@ -70,7 +69,7 @@ object SymbolFont {
}

// Get the character from codepoint
val codepoint = glyph.toInt()
val codepoint = glyphImage.glyph.toInt()
val text = String(Character.toChars(codepoint))

// Measure text
Expand All @@ -94,18 +93,18 @@ object SymbolFont {
return bitmap
}

bitmap = imageFromGlyph(
context = context,
glyph = image.glyph,
color = image.color.get(context),
backgroundColor = image.backgroundColor.get(context),
fontScale = (image.fontScale ?: 1.0).toFloat()
)
bitmap =
imageFromGlyph(
context = context,
glyphImage = image,
color = image.color.get(context),
backgroundColor = image.backgroundColor.get(context),
)

bitmap?.let {
BitmapCache.put(context, image, it)
}

return bitmap
}
}
}
48 changes: 0 additions & 48 deletions packages/react-native-autoplay/assets/symbolFont/bundleAsset.js

This file was deleted.

Loading
Loading