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.