diff --git a/fonts/Kickstart-Light.ttf b/fonts/Kickstart-Light.ttf
new file mode 100644
index 00000000..80534113
Binary files /dev/null and b/fonts/Kickstart-Light.ttf differ
diff --git a/fonts/Kickstart.ttf b/fonts/Kickstart.ttf
new file mode 100644
index 00000000..94caa3b2
Binary files /dev/null and b/fonts/Kickstart.ttf differ
diff --git a/fonts/Leco-Bold.ttf b/fonts/Leco-Bold.ttf
new file mode 100644
index 00000000..aab7c13d
Binary files /dev/null and b/fonts/Leco-Bold.ttf differ
diff --git a/images/kickstart/big.svg b/images/kickstart/big.svg
new file mode 100644
index 00000000..2e2168e6
--- /dev/null
+++ b/images/kickstart/big.svg
@@ -0,0 +1,12 @@
+
diff --git a/images/kickstart/round.svg b/images/kickstart/round.svg
new file mode 100644
index 00000000..0ea16ff0
--- /dev/null
+++ b/images/kickstart/round.svg
@@ -0,0 +1,18 @@
+
diff --git a/images/kickstart/small_bw.svg b/images/kickstart/small_bw.svg
new file mode 100644
index 00000000..548e5ca9
--- /dev/null
+++ b/images/kickstart/small_bw.svg
@@ -0,0 +1,13 @@
+
diff --git a/images/kickstart/small_bw_hr.svg b/images/kickstart/small_bw_hr.svg
new file mode 100644
index 00000000..1b61f75c
--- /dev/null
+++ b/images/kickstart/small_bw_hr.svg
@@ -0,0 +1,14 @@
+
diff --git a/images/kickstart/small_color.svg b/images/kickstart/small_color.svg
new file mode 100644
index 00000000..e59530c9
--- /dev/null
+++ b/images/kickstart/small_color.svg
@@ -0,0 +1,10 @@
+
diff --git a/images/tictoc/classic/background.svg b/images/tictoc/classic/background.svg
new file mode 100644
index 00000000..44b79fac
--- /dev/null
+++ b/images/tictoc/classic/background.svg
@@ -0,0 +1,6 @@
+
diff --git a/images/tictoc/time/background.svg b/images/tictoc/time/background.svg
new file mode 100644
index 00000000..61108c19
--- /dev/null
+++ b/images/tictoc/time/background.svg
@@ -0,0 +1,4 @@
+
diff --git a/images/tictoc/time/hour.svg b/images/tictoc/time/hour.svg
new file mode 100644
index 00000000..f8c1d7c2
--- /dev/null
+++ b/images/tictoc/time/hour.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time/minute.svg b/images/tictoc/time/minute.svg
new file mode 100644
index 00000000..3bf9223e
--- /dev/null
+++ b/images/tictoc/time/minute.svg
@@ -0,0 +1,6 @@
+
diff --git a/images/tictoc/time_round/black_14/background.svg b/images/tictoc/time_round/black_14/background.svg
new file mode 100644
index 00000000..9f2de5cd
--- /dev/null
+++ b/images/tictoc/time_round/black_14/background.svg
@@ -0,0 +1,7 @@
+
diff --git a/images/tictoc/time_round/black_14/hour.svg b/images/tictoc/time_round/black_14/hour.svg
new file mode 100644
index 00000000..94d5ff08
--- /dev/null
+++ b/images/tictoc/time_round/black_14/hour.svg
@@ -0,0 +1,6 @@
+
diff --git a/images/tictoc/time_round/black_14/minute.svg b/images/tictoc/time_round/black_14/minute.svg
new file mode 100644
index 00000000..89273a1f
--- /dev/null
+++ b/images/tictoc/time_round/black_14/minute.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/black_14/overlay.svg b/images/tictoc/time_round/black_14/overlay.svg
new file mode 100644
index 00000000..6382dec9
--- /dev/null
+++ b/images/tictoc/time_round/black_14/overlay.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/black_20/background.svg b/images/tictoc/time_round/black_20/background.svg
new file mode 100644
index 00000000..8f2b7f7b
--- /dev/null
+++ b/images/tictoc/time_round/black_20/background.svg
@@ -0,0 +1,7 @@
+
diff --git a/images/tictoc/time_round/black_20/hour.svg b/images/tictoc/time_round/black_20/hour.svg
new file mode 100644
index 00000000..9a5580a8
--- /dev/null
+++ b/images/tictoc/time_round/black_20/hour.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/black_20/minute.svg b/images/tictoc/time_round/black_20/minute.svg
new file mode 100644
index 00000000..c8392555
--- /dev/null
+++ b/images/tictoc/time_round/black_20/minute.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/black_20/overlay.svg b/images/tictoc/time_round/black_20/overlay.svg
new file mode 100644
index 00000000..4df2b161
--- /dev/null
+++ b/images/tictoc/time_round/black_20/overlay.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/black_20/second.svg b/images/tictoc/time_round/black_20/second.svg
new file mode 100644
index 00000000..276cefb1
--- /dev/null
+++ b/images/tictoc/time_round/black_20/second.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/gold_14/background.svg b/images/tictoc/time_round/gold_14/background.svg
new file mode 100644
index 00000000..ee372d2f
--- /dev/null
+++ b/images/tictoc/time_round/gold_14/background.svg
@@ -0,0 +1,21 @@
+
diff --git a/images/tictoc/time_round/gold_14/hour.svg b/images/tictoc/time_round/gold_14/hour.svg
new file mode 100644
index 00000000..b7dc99ef
--- /dev/null
+++ b/images/tictoc/time_round/gold_14/hour.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/gold_14/minute.svg b/images/tictoc/time_round/gold_14/minute.svg
new file mode 100644
index 00000000..5f943b97
--- /dev/null
+++ b/images/tictoc/time_round/gold_14/minute.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/gold_14/overlay.svg b/images/tictoc/time_round/gold_14/overlay.svg
new file mode 100644
index 00000000..7876ca46
--- /dev/null
+++ b/images/tictoc/time_round/gold_14/overlay.svg
@@ -0,0 +1,6 @@
+
diff --git a/images/tictoc/time_round/silver_14/background.svg b/images/tictoc/time_round/silver_14/background.svg
new file mode 100644
index 00000000..e89a2a75
--- /dev/null
+++ b/images/tictoc/time_round/silver_14/background.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_14/hour.svg b/images/tictoc/time_round/silver_14/hour.svg
new file mode 100644
index 00000000..55d203ae
--- /dev/null
+++ b/images/tictoc/time_round/silver_14/hour.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_14/minute.svg b/images/tictoc/time_round/silver_14/minute.svg
new file mode 100644
index 00000000..7988a26a
--- /dev/null
+++ b/images/tictoc/time_round/silver_14/minute.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_14/overlay.svg b/images/tictoc/time_round/silver_14/overlay.svg
new file mode 100644
index 00000000..c9c34eb1
--- /dev/null
+++ b/images/tictoc/time_round/silver_14/overlay.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_14/second.svg b/images/tictoc/time_round/silver_14/second.svg
new file mode 100644
index 00000000..9ad0fa08
--- /dev/null
+++ b/images/tictoc/time_round/silver_14/second.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_20/background.svg b/images/tictoc/time_round/silver_20/background.svg
new file mode 100644
index 00000000..c0c306eb
--- /dev/null
+++ b/images/tictoc/time_round/silver_20/background.svg
@@ -0,0 +1,25 @@
+
diff --git a/images/tictoc/time_round/silver_20/hour.svg b/images/tictoc/time_round/silver_20/hour.svg
new file mode 100644
index 00000000..7adddcc0
--- /dev/null
+++ b/images/tictoc/time_round/silver_20/hour.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_20/minute.svg b/images/tictoc/time_round/silver_20/minute.svg
new file mode 100644
index 00000000..b8151fd3
--- /dev/null
+++ b/images/tictoc/time_round/silver_20/minute.svg
@@ -0,0 +1,3 @@
+
diff --git a/images/tictoc/time_round/silver_20/overlay.svg b/images/tictoc/time_round/silver_20/overlay.svg
new file mode 100644
index 00000000..a5a285a3
--- /dev/null
+++ b/images/tictoc/time_round/silver_20/overlay.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/ui/common/faces/kickstart_faces.dart b/lib/ui/common/faces/kickstart_faces.dart
new file mode 100644
index 00000000..021520ed
--- /dev/null
+++ b/lib/ui/common/faces/kickstart_faces.dart
@@ -0,0 +1,143 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'dart:ui';
+import 'dart:math';
+
+class KickstartActivityPainter extends CustomPainter {
+ final double stepPercentage;
+ final double monthPercentage;
+ final KickstartType type;
+
+ KickstartActivityPainter(
+ this.stepPercentage, this.monthPercentage, this.type);
+
+ Path createTypePath(Size size, KickstartType type) {
+ double stroke = size.width * 0.07;
+ if (type == KickstartType.round) {
+ stroke = size.width * 0.08;
+ Rect screen = const Offset(0, 0) & Size.square(size.width);
+ return Path()..arcTo(screen, -0.5 * pi, 1.9 * pi, false);
+ }
+ return Path()
+ ..moveTo(size.width * 0.5, 0)
+ ..lineTo(size.width - stroke, 0)
+ ..arcToPoint(Offset(size.width, stroke),
+ radius: Radius.circular(stroke), rotation: pi / 2)
+ ..lineTo(size.width, size.height - stroke)
+ ..arcToPoint(Offset(size.width - stroke, size.height),
+ radius: Radius.circular(stroke), rotation: pi / 2)
+ ..lineTo(stroke, size.height)
+ ..arcToPoint(Offset(0, size.height - stroke),
+ radius: Radius.circular(stroke), rotation: pi / 2)
+ ..lineTo(0, stroke)
+ ..arcToPoint(Offset(stroke, 0),
+ radius: Radius.circular(stroke), rotation: pi / 2)
+ ..lineTo(size.width * 0.3, 0);
+ }
+
+ Tangent getMonthPosition(
+ Path originalPath, double monthPercentage, Size size) {
+ final totalLength = originalPath
+ .computeMetrics()
+ .fold(0.0, (double prev, PathMetric metric) => prev + metric.length);
+
+ final length = totalLength * monthPercentage;
+
+ var currentLength = 0.0;
+
+ var metricsIterator = originalPath.computeMetrics().iterator;
+
+ Tangent? position;
+
+ while (metricsIterator.moveNext()) {
+ var metric = metricsIterator.current;
+
+ var nextLength = currentLength + metric.length;
+
+ final isLastSegment = nextLength > length;
+ if (isLastSegment) {
+ final remainingLength = length - currentLength;
+ position = metric.getTangentForOffset(remainingLength);
+ break;
+ }
+
+ currentLength = nextLength;
+ }
+
+ return position ?? Tangent.fromAngle(Offset(size.width / 2, 0), -0.5 * pi);
+ }
+
+ Path createPathSegment(Path originalPath, double stepPercentage) {
+ final totalLength = originalPath
+ .computeMetrics()
+ .fold(0.0, (double prev, PathMetric metric) => prev + metric.length);
+
+ final length = totalLength * stepPercentage;
+
+ var currentLength = 0.0;
+
+ final path = new Path();
+
+ var metricsIterator = originalPath.computeMetrics().iterator;
+
+ while (metricsIterator.moveNext()) {
+ var metric = metricsIterator.current;
+
+ var nextLength = currentLength + metric.length;
+
+ final isLastSegment = nextLength > length;
+ if (isLastSegment) {
+ final remainingLength = length - currentLength;
+ final pathSegment = metric.extractPath(0.0, remainingLength);
+
+ path.addPath(pathSegment, Offset.zero);
+ break;
+ } else {
+ final pathSegment = metric.extractPath(0.0, metric.length);
+ path.addPath(pathSegment, Offset.zero);
+ }
+
+ currentLength = nextLength;
+ }
+
+ return path;
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final fullPath = createTypePath(size, type);
+
+ final path = createPathSegment(fullPath, stepPercentage);
+
+ final Paint paint = Paint();
+ paint.color = Color(0xFF00AA53);
+ if (type == KickstartType.small_bw) paint.color = Color(0xFF808080);
+ paint.style = PaintingStyle.stroke;
+ paint.strokeCap = StrokeCap.butt;
+ paint.strokeWidth = size.width * 0.14;
+ if (type == KickstartType.round) paint.strokeWidth = size.width * 0.16;
+
+ canvas.drawPath(path, paint);
+
+ // Draw the yellow month average indicator
+ paint.color = Color(0xFFFFFF02);
+ if (type == KickstartType.small_bw) paint.color = Color(0xFFFFFFFF);
+ paint.strokeCap = StrokeCap.round;
+ paint.strokeWidth = size.width * 0.03;
+
+ final position = getMonthPosition(fullPath, monthPercentage, size);
+ final angle = position.angle;
+ final offset = size.width * 0.09;
+ final dx = offset * sin(angle);
+ final dy = offset * cos(angle);
+ final indicator = Path()
+ ..moveTo(position.position.dx + dx, position.position.dy + dy)
+ ..lineTo(position.position.dx - dx, position.position.dy - dy);
+ canvas.drawPath(indicator, paint);
+ }
+
+ @override
+ bool shouldRepaint(CustomPainter oldDelegate) => true;
+}
+
+enum KickstartType { small_bw, small_color, round, big }
diff --git a/lib/ui/common/faces/pebble_faces.dart b/lib/ui/common/faces/pebble_faces.dart
new file mode 100644
index 00000000..baf6b55f
--- /dev/null
+++ b/lib/ui/common/faces/pebble_faces.dart
@@ -0,0 +1,424 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:cobble/domain/entities/hardware_platform.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_svg_provider/flutter_svg_provider.dart';
+import 'package:cobble/ui/common/icons/watch_icon.dart';
+import 'package:cobble/ui/common/faces/kickstart_faces.dart';
+import 'dart:math';
+import 'package:intl/intl.dart';
+
+class _FaceLayer {
+ _FaceLayer({this.svg, this.angle, this.alignment, this.child});
+ final String? svg;
+ final double? angle;
+ final Alignment? alignment;
+ final Widget? child;
+}
+
+class _WatchFace extends StatelessWidget {
+ _WatchFace(this.layers, {this.size = const Size(92, 92), this.prefix = ''});
+ final List<_FaceLayer> layers;
+ final Size size;
+ final String prefix;
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: size.width,
+ height: size.height,
+ child: Stack(
+ children: layers.map((e) {
+ if (e.svg != null)
+ return Transform.rotate(
+ child: Image(image: Svg(prefix + e.svg!, size: size)),
+ angle: e.angle ?? 0,
+ );
+ else
+ return Container(child: e.child, alignment: e.alignment);
+ }).toList(),
+ ),
+ );
+ }
+}
+
+class PebbleWatchFace extends HookWidget {
+ PebbleWatchFace(this.watchface, this.model, {this.width = 92.0});
+
+ final DefaultWatchface watchface;
+ final PebbleWatchModel model;
+ final double width;
+
+ @override
+ Widget build(BuildContext context) {
+ double height = width * (7 / 6);
+ DateTime now = DateTime.now();
+
+ String leadingZero(int value) {
+ return (value < 10 ? "0" : "") + value.toString();
+ }
+
+ Widget dateIndicator(Color color, EdgeInsets margin) {
+ return Container(
+ margin: margin,
+ child: Text(
+ "${DateFormat.E().format(now).toUpperCase()} ${leadingZero(now.day)}",
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ color: color,
+ fontSize: width * 0.08,
+ ),
+ ),
+ );
+ }
+
+ Widget textContainer(String text,
+ {double fontSize = 0.1,
+ Color color = const Color(0xFFFFFFFF),
+ String? fontFamily,
+ FontWeight? fontWeight,
+ double top = 0,
+ double bottom = 0,
+ double left = 0,
+ double right = 0}) {
+ return Container(
+ margin: EdgeInsets.fromLTRB(
+ width * left, width * top, width * right, width * bottom),
+ child: Text(
+ text,
+ style: TextStyle(
+ fontWeight: fontWeight,
+ fontFamily: fontFamily,
+ color: color,
+ fontSize: width * fontSize,
+ ),
+ ),
+ );
+ }
+
+ // TODO: make those actually update in real time
+ DateTime midnight = DateTime(now.year, now.month, now.day);
+ int secondsSinceMidnight = now.difference(midnight).inSeconds;
+ double hoursAngle = ((pi / 6) * (secondsSinceMidnight / 3600)) % (2 * pi);
+ double minutesAngle = ((pi / 30) * (secondsSinceMidnight / 60)) % (2 * pi);
+ double secondsAngle = ((pi / 30) * secondsSinceMidnight) % (2 * pi);
+ PebbleWatchLine line = watchLineFromNumber(model.index);
+
+ Widget? roundTicToc(model) {
+ switch (model) {
+ case PebbleWatchModel.time_round_silver_14:
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(
+ child: dateIndicator(
+ Color(0xFF545454), EdgeInsets.only(bottom: width * 0.14)),
+ alignment: Alignment.bottomCenter,
+ ),
+ _FaceLayer(svg: 'hour.svg', angle: hoursAngle),
+ _FaceLayer(svg: 'minute.svg', angle: minutesAngle),
+ // _FaceLayer(svg: 'second.svg', angle: secondsAngle), // This was apparently removed from firmware
+ _FaceLayer(svg: 'overlay.svg'),
+ ],
+ size: Size(width, width),
+ prefix: 'images/tictoc/time_round/silver_14/',
+ );
+ case PebbleWatchModel.time_round_silver_20:
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(svg: 'hour.svg', angle: hoursAngle),
+ _FaceLayer(svg: 'minute.svg', angle: minutesAngle),
+ _FaceLayer(svg: 'overlay.svg'),
+ ],
+ size: Size(width, width),
+ prefix: 'images/tictoc/time_round/silver_20/',
+ );
+ case PebbleWatchModel.time_round_rose_gold_14:
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(svg: 'hour.svg', angle: hoursAngle),
+ _FaceLayer(svg: 'minute.svg', angle: minutesAngle),
+ _FaceLayer(svg: 'overlay.svg'),
+ ],
+ size: Size(width, width),
+ prefix: 'images/tictoc/time_round/gold_14/',
+ );
+ case PebbleWatchModel.time_round_black_14:
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(
+ child: dateIndicator(
+ Color(0xFFFFFFFF), EdgeInsets.only(bottom: width * 0.14)),
+ alignment: Alignment.bottomCenter,
+ ),
+ _FaceLayer(svg: 'hour.svg', angle: hoursAngle),
+ _FaceLayer(svg: 'minute.svg', angle: minutesAngle),
+ _FaceLayer(svg: 'overlay.svg'),
+ ],
+ size: Size(width, width),
+ prefix: 'images/tictoc/time_round/black_14/',
+ );
+ case PebbleWatchModel.time_round_black_20:
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(
+ child: dateIndicator(
+ Color(0xFFFFFFFF), EdgeInsets.only(left: width * 0.14)),
+ alignment: Alignment.centerLeft,
+ ),
+ _FaceLayer(svg: 'hour.svg', angle: hoursAngle),
+ _FaceLayer(svg: 'minute.svg', angle: minutesAngle),
+ _FaceLayer(svg: 'second.svg', angle: secondsAngle),
+ _FaceLayer(svg: 'overlay.svg'),
+ ],
+ size: Size(width, width),
+ prefix: 'images/tictoc/time_round/black_20/',
+ );
+ }
+ }
+
+ switch (watchface) {
+ case DefaultWatchface.tictoc:
+ if (line == PebbleWatchLine.time ||
+ line == PebbleWatchLine.time_steel) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(svg: 'hour.svg', angle: hoursAngle),
+ _FaceLayer(svg: 'minute.svg', angle: minutesAngle),
+ ],
+ size: Size(width, height),
+ prefix: 'images/tictoc/time/',
+ );
+ } else if (line == PebbleWatchLine.classic ||
+ line == PebbleWatchLine.steel ||
+ line == PebbleWatchLine.pebble_2) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'background.svg'),
+ _FaceLayer(
+ child: textContainer(DateFormat.MMMMd().format(now),
+ fontSize: 0.14, bottom: 0.5, left: 0.05),
+ alignment: Alignment.bottomLeft,
+ ),
+ _FaceLayer(
+ child: textContainer(DateFormat.Hm().format(now),
+ fontWeight: FontWeight.bold,
+ fontSize: 0.32,
+ bottom: 0.12,
+ left: 0.05),
+ alignment: Alignment.bottomLeft,
+ ),
+ ],
+ size: Size(width, height),
+ prefix: 'images/tictoc/classic/',
+ );
+ } else if (line == PebbleWatchLine.time_round) {
+ final result = roundTicToc(model);
+ if (result != null) return result;
+ }
+ break;
+ case DefaultWatchface.kickstart:
+ // TODO: implement actual values from pebble health into KickstartActivityPainter
+ // and handle whether or not the person has health enabled
+ if (line == PebbleWatchLine.time ||
+ line == PebbleWatchLine.time_steel) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'images/kickstart/small_color.svg'),
+ _FaceLayer(
+ child: SizedBox(
+ height: height,
+ width: width,
+ child: ClipRect(
+ child: new CustomPaint(
+ painter: new KickstartActivityPainter(
+ 0.4, 0.2, KickstartType.small_color),
+ ),
+ ),
+ ),
+ ),
+ _FaceLayer(
+ child: textContainer(DateFormat.Hm().format(now),
+ fontFamily: 'Kickstart', fontSize: 0.48, top: 0.28),
+ alignment: Alignment.topCenter,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "10,323", // TODO: replace with the actual step count
+ color: Color(0xFF00AA52),
+ fontFamily: 'Kickstart',
+ fontSize: 0.26,
+ bottom: 0.14),
+ alignment: Alignment.bottomCenter,
+ ),
+ ],
+ size: Size(width, height),
+ );
+ } else if (line == PebbleWatchLine.time_round) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'images/kickstart/round.svg'),
+ _FaceLayer(
+ child: SizedBox(
+ height: width,
+ width: width,
+ child: ClipRect(
+ child: new CustomPaint(
+ painter: new KickstartActivityPainter(
+ 0.4, 0.2, KickstartType.round),
+ ),
+ ),
+ ),
+ ),
+ _FaceLayer(
+ child: textContainer(DateFormat.Hm().format(now),
+ fontFamily: 'Kickstart', fontSize: 0.38, top: 0.24),
+ alignment: Alignment.topCenter,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "10,323", // TODO: replace with the actual step count
+ fontFamily: 'Kickstart',
+ color: Color(0xFF00AA52),
+ fontSize: 0.22,
+ bottom: 0.14),
+ alignment: Alignment.bottomCenter,
+ ),
+ ],
+ size: Size(width, width),
+ );
+ } else if (line == PebbleWatchLine.classic ||
+ line == PebbleWatchLine.steel) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'images/kickstart/small_bw.svg'),
+ _FaceLayer(
+ child: SizedBox(
+ height: height,
+ width: width,
+ child: ClipRect(
+ child: new CustomPaint(
+ painter: new KickstartActivityPainter(
+ 0.4, 0.2, KickstartType.small_bw),
+ ),
+ ),
+ ),
+ ),
+ _FaceLayer(
+ child: textContainer(DateFormat.Hm().format(now),
+ fontFamily: 'LecoBold', fontSize: 0.22, top: 0.30),
+ alignment: Alignment.topCenter,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "10,323", // TODO: replace with the actual step count
+ fontWeight: FontWeight.bold,
+ fontSize: 0.12,
+ bottom: 0.31,
+ right: 0.14),
+ alignment: Alignment.bottomRight,
+ ),
+ ],
+ size: Size(width, height),
+ );
+ } else if (line == PebbleWatchLine.pebble_2) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'images/kickstart/small_bw_hr.svg'),
+ _FaceLayer(
+ child: SizedBox(
+ height: height,
+ width: width,
+ child: ClipRect(
+ child: new CustomPaint(
+ painter: new KickstartActivityPainter(
+ 0.4, 0.2, KickstartType.small_bw),
+ ),
+ ),
+ ),
+ ),
+ _FaceLayer(
+ child: textContainer(DateFormat.Hm().format(now),
+ fontFamily: 'LecoBold', fontSize: 0.22, top: 0.20),
+ alignment: Alignment.topCenter,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "120 BPM", // TODO: replace with the actual heartrate
+ fontWeight: FontWeight.bold,
+ fontSize: 0.12,
+ bottom: 0.39,
+ right: 0.14),
+ alignment: Alignment.bottomRight,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "10,323", // TODO: replace with the actual step count
+ fontWeight: FontWeight.bold,
+ fontSize: 0.12,
+ bottom: 0.19,
+ right: 0.14),
+ alignment: Alignment.bottomRight,
+ ),
+ ],
+ size: Size(width, height),
+ );
+ } else if (line == PebbleWatchLine.time_2) {
+ return _WatchFace(
+ [
+ _FaceLayer(svg: 'images/kickstart/big.svg'),
+ _FaceLayer(
+ child: SizedBox(
+ height: width * (57 / 50),
+ width: width,
+ child: ClipRect(
+ child: new CustomPaint(
+ painter: new KickstartActivityPainter(
+ 0.4, 0.2, KickstartType.big),
+ ),
+ ),
+ ),
+ ),
+ _FaceLayer(
+ child: textContainer(DateFormat.Hm().format(now),
+ fontFamily: 'Kickstart', fontSize: 0.52),
+ alignment: Alignment.topCenter,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "120 BPM", // TODO: replace with the actual heartrate
+ color: Color(0xFFFF0000),
+ fontFamily: 'Kickstart',
+ fontSize: 0.26,
+ bottom: 0.29,
+ right: 0.12),
+ alignment: Alignment.bottomRight,
+ ),
+ _FaceLayer(
+ child: textContainer(
+ "10,323", // TODO: replace with the actual step count
+ color: Color(0xFF00AA52),
+ fontFamily: 'Kickstart',
+ fontSize: 0.26,
+ bottom: 0.07,
+ right: 0.12),
+ alignment: Alignment.bottomRight,
+ ),
+ ],
+ size: Size(width, width * (57 / 50)),
+ );
+ }
+ break;
+ }
+ return Container();
+ }
+}
+
+enum DefaultWatchface {
+ tictoc,
+ kickstart,
+}
diff --git a/lib/ui/home/tabs/test_tab.dart b/lib/ui/home/tabs/test_tab.dart
index d445c240..b4ff122c 100644
--- a/lib/ui/home/tabs/test_tab.dart
+++ b/lib/ui/home/tabs/test_tab.dart
@@ -8,6 +8,7 @@ import 'package:cobble/infrastructure/datasources/preferences.dart';
import 'package:cobble/infrastructure/datasources/workarounds.dart';
import 'package:cobble/infrastructure/pigeons/pigeons.g.dart';
import 'package:cobble/ui/common/components/cobble_button.dart';
+import 'package:cobble/ui/common/faces/pebble_faces.dart';
import 'package:cobble/ui/common/icons/watch_icon.dart';
import 'package:cobble/ui/devoptions/dev_options_page.dart';
import 'package:cobble/ui/router/cobble_navigator.dart';
@@ -66,6 +67,7 @@ class TestTab extends HookWidget implements CobbleScreen {
}, ["one-time"]);
String statusText;
+ Widget defaultWatchfaces = Container();
if (connectionState.isConnecting == true) {
statusText = "Connecting to ${connectionState.currentWatchAddress}";
} else if (connectionState.isConnected == true) {
@@ -77,6 +79,12 @@ class TestTab extends HookWidget implements CobbleScreen {
fwVersion =
connectionState.currentConnectedWatch!.runningFirmware.version;
}
+ defaultWatchfaces = Row(
+ children: [
+ PebbleWatchFace(DefaultWatchface.tictoc, model),
+ PebbleWatchFace(DefaultWatchface.kickstart, model),
+ ]
+ );
statusText = "Connected to ${connectionState.currentWatchAddress}" +
" ($model, firmware $fwVersion)";
@@ -91,6 +99,8 @@ class TestTab extends HookWidget implements CobbleScreen {
child: SingleChildScrollView(
child: Column(
children: [
+ defaultWatchfaces,
+
RaisedButton(
onPressed: () {
notifications.sendTestNotification();
diff --git a/pubspec.yaml b/pubspec.yaml
index e32afbde..e8622fda 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -98,6 +98,14 @@ flutter:
assets:
- lang/
- images/
+ - images/kickstart/
+ - images/tictoc/classic/
+ - images/tictoc/time/
+ - images/tictoc/time_round/silver_14/
+ - images/tictoc/time_round/silver_20/
+ - images/tictoc/time_round/black_14/
+ - images/tictoc/time_round/black_20/
+ - images/tictoc/time_round/gold_14/
fonts:
- family: RebbleIcons
@@ -106,6 +114,15 @@ flutter:
- family: PebbleWatchIcons
fonts:
- asset: fonts/PebbleWatchIcons.ttf
+ - family: Kickstart
+ fonts:
+ - asset: fonts/Kickstart.ttf
+ - family: KickstartLight
+ fonts:
+ - asset: fonts/Kickstart-Light.ttf
+ - family: LecoBold
+ fonts:
+ - asset: fonts/Leco-Bold.ttf
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.