feat(map): use Font Awesome link icons for the point events

Swaps the emoji for Font Awesome Free 6.5.2 "link" and "link-slash"
(solid), inlined as SVG paths instead of loaded from a CDN. Pulling
the icon font would be a ~75KB network download for two glyphs, and
the popups need to render whether or not that request succeeds.

Inline SVG also takes a fill colour, so connected draws green and
dropped draws red, and it renders identically regardless of the
device's emoji font - 💔 was a stand-in because the literal broken
chain (U+26D3 U+FE0F U+200D U+1F4A5) is a 2023 addition that shows
as a blank box on older Android.

Both paths verified byte-for-byte against upstream.

Icons: CC BY 4.0, Copyright 2024 Fonticons, Inc.
https://fontawesome.com/license/free

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
n0tst3v3
2026-08-19 10:48:48 -06:00
co-authored by Claude Opus 5
parent f16efce66e
commit 6808e88599
@@ -34,6 +34,43 @@ public final class MapHtmlBuilder {
public int aggressiveness = HistoryNormalizer.DEFAULT_AGGRESSIVENESS;
}
/*
* Font Awesome Free 6.5.2 "link" and "link-slash" (solid), inlined as
* SVG paths rather than pulled from a CDN - the whole icon font would be
* a ~75KB download over the network for two glyphs, and the popups have
* to render when the map is loaded from cache.
*
* Icons: CC BY 4.0 (https://fontawesome.com/license/free),
* Copyright 2024 Fonticons, Inc.
*/
private static final String ICON_LINK_PATH =
"M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4"
+ "l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1"
+ "c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8"
+ "c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6"
+ "c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6"
+ "C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7z"
+ "M60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4"
+ "l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1"
+ "c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2"
+ "c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6"
+ "c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6"
+ "C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z";
private static final String ICON_LINK_SLASH_PATH =
"M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464"
+ "c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L489.3 358.2l90.5-90.5"
+ "c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1"
+ "c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1"
+ "c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114l-96 96-31.9-25"
+ "C430.9 239.6 420.1 175.1 377 132c-52.2-52.3-134.5-56.2-191.3-11.7"
+ "L38.8 5.1zM239 162c30.1-14.9 67.7-9.9 92.8 15.3c20 20 27.5 48.3 21.7 74.5"
+ "L239 162zM116.6 187.9L60.2 244.3c-56.5 56.5-56.5 148 0 204.5"
+ "c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6"
+ "s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6"
+ "C74 372 74 321 105.5 289.5l61.8-61.8-50.6-39.9zM220.9 270"
+ "c-2.1 39.8 12.2 80.1 42.2 110c38.9 38.9 94.4 51 143.6 36.3L220.9 270z";
/** Diameter of a device's own marker; history points draw at half. */
private static final int DEVICE_MARKER_SIZE = 22;
@@ -43,6 +80,15 @@ public final class MapHtmlBuilder {
private MapHtmlBuilder() {
}
/** One of the inlined Font Awesome glyphs, tinted and sized for a popup line. */
private static String icon(String path, String color) {
return "<svg viewBox='0 0 640 512' width='15' height='12' "
+ "fill='" + color + "' "
+ "style='vertical-align:-1px;margin-right:5px'>"
+ "<path d='" + path + "'/></svg>";
}
/** The round white-bordered marker, shared by devices and history points. */
private static String divIcon(
String color,
@@ -522,11 +568,15 @@ public final class MapHtmlBuilder {
*/
if (LocationPoint.EVENT_CONNECTED.equals(point.event)) {
html.append("<br>🔗 Connected to the phone here");
html.append("<br>")
.append(icon(ICON_LINK_PATH, "#00A86B"))
.append("Connected to the phone here");
} else if (LocationPoint.EVENT_DISCONNECTED.equals(point.event)) {
html.append("<br>💔 Link to the phone dropped here");
html.append("<br>")
.append(icon(ICON_LINK_SLASH_PATH, "#E53935"))
.append("Link to the phone dropped here");
}
long time = HistoryNormalizer.pointTime(point);