Generating preview on packing/unpacking watchface

fonts_experiment 1.0.1.0
Valeriy Mironov 2017-11-30 03:46:17 +02:00
parent fa2cbebad2
commit 027b9c266d
76 changed files with 1977 additions and 12 deletions

View File

@ -3,6 +3,8 @@
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/Environment/Hierarchy/PsiConfigurationSettingsKey/CustomLocation/@EntryValue">C:\Users\valeronm\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_048a238b\SolutionCaches</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.3")]
[assembly: AssemblyFileVersion("1.0.0.3")]
[assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")]

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Drawing;
using WatchFace.Parser.Models;
namespace WatchFace.Parser.Helpers
{
public class DrawerHelper
{
public static Size CalculateBounds(IEnumerable<Bitmap> images, long spacing)
{
long width = 0;
var height = 0;
foreach (var image in images)
{
width += image.Width + spacing;
if (image.Height > height) height = image.Height;
}
width -= spacing;
return new Size((int) width, height);
}
public static void DrawImages(Graphics drawer, IEnumerable<Bitmap> images, long spacing,
TextAlignment alignment, Rectangle box)
{
var bitmap = CalculateBounds(images, spacing);
int x, y;
if (alignment.HasFlag(TextAlignment.Left))
x = box.X;
else if (alignment.HasFlag(TextAlignment.Right))
x = box.Right - bitmap.Width + 1;
else
x = (box.Left + box.Right - bitmap.Width) >> 1;
if (alignment.HasFlag(TextAlignment.Top))
y = box.Top;
else if (alignment.HasFlag(TextAlignment.Bottom))
y = box.Bottom - bitmap.Height + 1;
else
y = (box.Top + box.Bottom - bitmap.Height) >> 1;
foreach (var image in images)
{
drawer.DrawImage(image, x, y);
x += image.Width + (int) spacing;
}
}
}
}

View File

@ -0,0 +1,10 @@
using System.Drawing;
using WatchFace.Parser.Models;
namespace WatchFace.Parser.Interfaces
{
public interface IDrawable
{
void Draw(Graphics drawer, Bitmap[] resources, WatchState state);
}
}

View File

@ -0,0 +1,13 @@
namespace WatchFace.Parser.Models
{
public enum AirCondition
{
Excellent,
Good,
Light,
Moderate,
Heavy,
Critical,
Unknown = 0xff
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class CaloriesElement : NumberElement, IDrawable
{
public CaloriesElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Calories);
}
}
}

View File

@ -0,0 +1,48 @@
using System.Drawing;
using WatchFace.Parser.Helpers;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class DistanceElement : CompositeElement, IDrawable
{
public DistanceElement(Parameter parameter, Element parent, string name= null) :
base(parameter, parent, name) { }
public NumberElement Number { get; set; }
public long KilometerImageIndex { get; set; }
public long DecimalPointImageIndex { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var kilometers = state.Distance / 1000;
var decimals = state.Distance % 1000 / 10;
var images = Number.GetImagesForNumber(resources, kilometers);
images.Add(resources[DecimalPointImageIndex]);
images.AddRange(Number.GetImagesForNumber(resources, decimals));
images.Add(resources[KilometerImageIndex]);
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, Number.GetBox());
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Number = new NumberElement(parameter, this, nameof(Number));
return Number;
case 2:
KilometerImageIndex = parameter.Value;
return new ValueElement(parameter, this, nameof(KilometerImageIndex));
case 3:
DecimalPointImageIndex = parameter.Value;
return new ValueElement(parameter, this, nameof(DecimalPointImageIndex));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,17 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class PulseElement : NumberElement, IDrawable
{
public PulseElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.Pulse != null)
Draw(drawer, resources, state.Pulse.Value);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class StepsElement : NumberElement, IDrawable
{
public StepsElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Steps);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class StepsGoalElement : NumberElement, IDrawable
{
public StepsGoalElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Goal);
}
}
}

View File

@ -0,0 +1,38 @@
namespace WatchFace.Parser.Models.Elements
{
public class ActivityElement : ContainerElement
{
public ActivityElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public StepsElement Steps { get; set; }
public StepsGoalElement StepsGoal { get; set; }
public CaloriesElement Calories { get; set; }
public PulseElement Pulse { get; set; }
public DistanceElement Distance { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Steps = new StepsElement(parameter, this);
return Steps;
case 2:
StepsGoal = new StepsGoalElement(parameter, this);
return StepsGoal;
case 3:
Calories = new CaloriesElement(parameter, this);
return Calories;
case 4:
Pulse = new PulseElement(parameter, this);
return Pulse;
case 5:
Distance = new DistanceElement(parameter, this);
return Distance;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Models.Elements.Common;
namespace WatchFace.Parser.Models.Elements.AnalogDial
{
public class HoursClockHandElement : ClockHandElement
{
public HoursClockHandElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Hour % 12 + (double) state.Time.Minute / 60, 12);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Models.Elements.Common;
namespace WatchFace.Parser.Models.Elements.AnalogDial
{
public class MinutesClockHandElement : ClockHandElement
{
public MinutesClockHandElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Minute, 60);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Models.Elements.Common;
namespace WatchFace.Parser.Models.Elements.AnalogDial
{
public class SecondsClockHandElement : ClockHandElement
{
public SecondsClockHandElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Second, 60);
}
}
}

View File

@ -0,0 +1,32 @@
using WatchFace.Parser.Models.Elements.AnalogDial;
namespace WatchFace.Parser.Models.Elements
{
public class AnalogDialElement : ContainerElement
{
public AnalogDialElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public HoursClockHandElement Hours { get; set; }
public MinutesClockHandElement Minutes { get; set; }
public SecondsClockHandElement Seconds { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Hours = new HoursClockHandElement(parameter, this, nameof(Hours));
return Hours;
case 2:
Minutes = new MinutesClockHandElement(parameter, this, nameof(Minutes));
return Minutes;
case 3:
Seconds = new SecondsClockHandElement(parameter, this, nameof(Seconds));
return Seconds;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,22 @@
namespace WatchFace.Parser.Models.Elements
{
public class BackgroundElement : ContainerElement
{
public BackgroundElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public ImageElement Image { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Image = new ImageElement(parameter, this, nameof(Image));
return Image;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Collections.Generic;
namespace WatchFace.Parser.Models.Elements
{
public class CompositeElement : Element
{
public CompositeElement() { }
public CompositeElement(IEnumerable<Parameter> parameters)
{
foreach (var parameterChild in parameters)
Children.Add(CreateChildForParameter(parameterChild));
}
public CompositeElement(Parameter parameter, Element parent, string name = null) : base(parameter, parent, name)
{
foreach (var parameterChild in parameter.Children)
Children.Add(CreateChildForParameter(parameterChild));
}
public List<Element> Children { get; } = new List<Element>();
public void CreateChilds(List<Parameter> parameter)
{
foreach (var parameterChild in parameter)
Children.Add(CreateChildForParameter(parameterChild));
}
protected virtual Element CreateChildForParameter(Parameter parameter)
{
if (parameter.HasChildren)
return new ContainerElement(parameter, this);
return new ValueElement(parameter, this);
}
}
}

View File

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class ContainerElement : CompositeElement, IDrawable
{
public ContainerElement() { }
public ContainerElement(IEnumerable<Parameter> parameters) : base(parameters) { }
public ContainerElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public virtual void Draw(Graphics drawer, Bitmap[] images, WatchState state)
{
foreach (var element in Children.OfType<IDrawable>())
element.Draw(drawer, images, state);
}
}
}

View File

@ -0,0 +1,24 @@
namespace WatchFace.Parser.Models.Elements
{
public class Element
{
protected readonly string _name;
protected readonly Element _parent;
protected Element() { }
protected Element(Parameter parameter, Element parent, string name = null)
{
Id = parameter.Id;
_name = name;
_parent = parent;
}
protected bool HasParent => _parent != null;
protected string StringId => Id.ToString();
public byte? Id { get; }
public string Name => _name ?? $"Unknown{Id}";
public string Path => HasParent ? $"{_parent.Path}.{StringId}" : StringId;
}
}

View File

@ -0,0 +1,12 @@
namespace WatchFace.Parser.Models.Elements
{
public class ValueElement : Element
{
public ValueElement(Parameter parameter, Element parent, string name = null) : base(parameter, parent, name)
{
Value = parameter.Value;
}
public long Value { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements.Battery
{
public class BatteryImageSetElement : ImageSetElement
{
public BatteryImageSetElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var imageIndex = state.BatteryLevel * (int) ImagesCount / 100;
Draw(drawer, resources, imageIndex);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Models.Elements.GoalProgress;
namespace WatchFace.Parser.Models.Elements.Battery
{
public class BatteryLenearProgressElement : LinearProgressElement
{
public BatteryLenearProgressElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.BatteryLevel, 100);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements.Battery
{
public class BatteryNumberElement : NumberElement, IDrawable
{
public BatteryNumberElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.BatteryLevel);
}
}
}

View File

@ -0,0 +1,32 @@
using WatchFace.Parser.Models.Elements.Battery;
namespace WatchFace.Parser.Models.Elements
{
public class BatteryElement : ContainerElement
{
public BatteryElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public BatteryNumberElement Text { get; set; }
public BatteryImageSetElement Icon { get; set; }
public BatteryLenearProgressElement Scale { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Text = new BatteryNumberElement(parameter, this);
return Text;
case 2:
Icon = new BatteryImageSetElement(parameter, this);
return Icon;
case 3:
Scale = new BatteryLenearProgressElement(parameter, this);
return Scale;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,56 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements.GoalProgress
{
public abstract class CircularProgressElement : CoordinatesElement, IDrawable
{
protected CircularProgressElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public long RadiusX { get; set; }
public long RadiusY { get; set; }
public long StartAngle { get; set; }
public long EndAngle { get; set; }
public long Width { get; set; }
public Color Color { get; set; }
public abstract void Draw(Graphics drawer, Bitmap[] resources, WatchState state);
public void Draw(Graphics drawer, Bitmap[] resources, int value, int total)
{
var sectorAngle = (EndAngle - StartAngle) * value / total;
var pen = new Pen(Color, Width);
var rect = new Rectangle((int) (X - RadiusX + Width / 2), (int) (Y - RadiusY + Width / 2),
(int) (RadiusX * 2 - Width / 2 - 1), (int) (RadiusY * 2 - Width / 2 - 1));
drawer.DrawArc(pen, rect, StartAngle - 90, sectorAngle);
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 3:
RadiusX = parameter.Value;
return new ValueElement(parameter, this);
case 4:
RadiusY = parameter.Value;
return new ValueElement(parameter, this);
case 5:
StartAngle = parameter.Value;
return new ValueElement(parameter, this);
case 6:
EndAngle = parameter.Value;
return new ValueElement(parameter, this);
case 7:
Width = parameter.Value;
return new ValueElement(parameter, this);
case 8:
Color = Color.FromArgb(0xff, Color.FromArgb((int) parameter.Value));
return new ValueElement(parameter, this);
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements.Common
{
public abstract class ClockHandElement : CompositeElement, IDrawable
{
protected ClockHandElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public bool OnlyBorder { get; set; }
public Color Color { get; set; }
public CoordinatesElement Center { get; set; }
public List<CoordinatesElement> Shape { get; set; } = new List<CoordinatesElement>();
public ImageElement CenterImage { get; set; }
public abstract void Draw(Graphics drawer, Bitmap[] resources, WatchState state);
public void Draw(Graphics drawer, Bitmap[] resources, double value, double total)
{
var angle = value * 360 / total - 90;
var points = Shape.Select(point => RotatePoint(point, angle)).ToArray();
if (OnlyBorder)
{
drawer.DrawPolygon(new Pen(Color), points);
}
else
{
drawer.FillPolygon(new SolidBrush(Color), points);
drawer.DrawPolygon(new Pen(Color, 1), points);
}
CenterImage?.Draw(drawer, resources);
}
private Point RotatePoint(CoordinatesElement element, double degrees)
{
var radians = degrees / 180 * Math.PI;
var x = element.X * Math.Cos(radians) + element.Y * Math.Sin(radians);
var y = element.X * Math.Sin(radians) + element.Y * Math.Cos(radians);
return new Point((int) Math.Round(x + Center.X), (int) Math.Round(y + Center.Y));
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
OnlyBorder = parameter.Value > 0;
return new ValueElement(parameter, this);
case 2:
Color = Color.FromArgb(0xff, Color.FromArgb((int) parameter.Value));
return new ValueElement(parameter, this);
case 3:
Center = new CoordinatesElement(parameter, this);
return Center;
case 4:
var point = new CoordinatesElement(parameter, this);
Shape.Add(point);
return point;
case 5:
CenterImage = new ImageElement(parameter, this);
return CenterImage;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,26 @@
namespace WatchFace.Parser.Models.Elements
{
public class CoordinatesElement : CompositeElement
{
public CoordinatesElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public long X { get; set; }
public long Y { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
X = parameter.Value;
return new ValueElement(parameter, this, nameof(X));
case 2:
Y = parameter.Value;
return new ValueElement(parameter, this, nameof(Y));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,34 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements
{
public class DigitElement : CoordinatesElement
{
public DigitElement(Parameter parameter, Element parent, string name) : base(parameter, parent, name) { }
public long ImageIndex { get; set; }
public long ImagesCount { get; set; }
public void Draw(Graphics drawer, Bitmap[] images, int digit)
{
if (digit >= ImagesCount) return;
var imageIndex = ImageIndex + digit;
drawer.DrawImage(images[imageIndex], new Point((int) X, (int) Y));
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 3:
ImageIndex = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndex));
case 4:
ImagesCount = parameter.Value;
return new ValueElement(parameter, this, nameof(ImagesCount));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,34 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class ImageElement : CoordinatesElement, IDrawable
{
public ImageElement(Parameter parameter, Element parent, string name = null) : base(parameter, parent, name) { }
public long ImageIndex { get; set; }
public virtual void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources);
}
public void Draw(Graphics drawer, Bitmap[] images)
{
drawer.DrawImage(images[ImageIndex], new Point((int) X, (int) Y));
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 3:
ImageIndex = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndex));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,31 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements
{
public class ImageSetElement : ImageElement
{
public ImageSetElement(Parameter parameter, Element parent, string name = null) : base(parameter, parent, name) { }
public long ImagesCount { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, int index)
{
if (index >= ImagesCount) return;
var imageIndex = ImageIndex + index;
drawer.DrawImage(resources[imageIndex], new Point((int) X, (int) Y));
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 4:
ImagesCount = parameter.Value;
return new ValueElement(parameter, this);
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements.GoalProgress
{
public abstract class LinearProgressElement : CompositeElement, IDrawable
{
protected LinearProgressElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public long ImageIndex { get; set; }
public List<CoordinatesElement> Sectors { get; set; } = new List<CoordinatesElement>();
public abstract void Draw(Graphics drawer, Bitmap[] resources, WatchState state);
public void Draw(Graphics drawer, Bitmap[] resources, int value, int total)
{
var showSectors = Sectors.Count * value / total;
for (var i = 0; i < showSectors && i < Sectors.Count; i++)
drawer.DrawImage(resources[ImageIndex + i], Sectors[i].X, Sectors[i].Y);
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
ImageIndex = parameter.Value;
return new ValueElement(parameter, this);
case 2:
var sector = new CoordinatesElement(parameter, this);
Sectors.Add(sector);
return sector;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,68 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using WatchFace.Parser.Helpers;
namespace WatchFace.Parser.Models.Elements
{
public class NumberElement : CoordinatesElement
{
public NumberElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public long BottomRightX { get; set; }
public long BottomRightY { get; set; }
public TextAlignment Alignment { get; set; }
public long Spacing { get; set; }
public long ImageIndex { get; set; }
public long ImagesCount { get; set; }
public Rectangle GetBox()
{
return new Rectangle((int) X, (int) Y, (int) (BottomRightX - X), (int) (BottomRightY - Y));
}
public void Draw(Graphics drawer, Bitmap[] images, int number, int minimumDigits = 1)
{
DrawerHelper.DrawImages(drawer, GetImagesForNumber(images, number, minimumDigits), (int) Spacing, Alignment, GetBox());
}
public List<Bitmap> GetImagesForNumber(Bitmap[] images, int number, int minimumDigits = 1)
{
var stringNumber = number.ToString().PadLeft(minimumDigits, '0');
return (from digitChar in stringNumber
select int.Parse(digitChar.ToString())
into digit
where digit < ImagesCount
select images[ImageIndex + digit]).ToList();
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 3:
BottomRightX = parameter.Value;
return new ValueElement(parameter, this, nameof(BottomRightX));
case 4:
BottomRightY = parameter.Value;
return new ValueElement(parameter, this, nameof(BottomRightY));
case 5:
Alignment = (TextAlignment) parameter.Value;
return new ValueElement(parameter, this, nameof(Alignment));
case 6:
Spacing = parameter.Value;
return new ValueElement(parameter, this, nameof(Spacing));
case 7:
ImageIndex = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndex));
case 8:
ImagesCount = parameter.Value;
return new ValueElement(parameter, this, nameof(ImagesCount));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,41 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public abstract class SwitchElement : CompositeElement, IDrawable
{
protected SwitchElement(Parameter parameter, Element parent, string name) : base(parameter, parent, name) { }
public CoordinatesElement Coordinates { get; set; }
public long? ImageIndexOn { get; set; }
public long? ImageIndexOff { get; set; }
public void Draw(Graphics drawer, Bitmap[] images, WatchState state)
{
var imageIndex = SwitchState(state) ? ImageIndexOn : ImageIndexOff;
if (imageIndex == null) return;
drawer.DrawImage(images[imageIndex.Value], new Point((int) Coordinates.X, (int) Coordinates.Y));
}
public abstract bool SwitchState(WatchState state);
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Coordinates = new CoordinatesElement(parameter, this, nameof(Coordinates));
return Coordinates;
case 2:
ImageIndexOn = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndexOn));
case 3:
ImageIndexOff = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndexOff));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,34 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements
{
public class TwoDigitsElement : CompositeElement
{
public TwoDigitsElement(Parameter parameter, Element parent, string name) : base(parameter, parent, name) { }
public DigitElement Tens { get; set; }
public DigitElement Ones { get; set; }
public void Draw(Graphics drawer, Bitmap[] images, int number)
{
if (number > 99) number = number % 100;
Tens.Draw(drawer, images, number / 10);
Ones.Draw(drawer, images, number % 10);
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Tens = new DigitElement(parameter, this, nameof(Tens));
return Tens;
case 2:
Ones = new DigitElement(parameter, this, nameof(Ones));
return Ones;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,41 @@
using System.Drawing;
using WatchFace.Parser.Helpers;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class OneLineMonthAndDayElement : CompositeElement, IDrawable
{
public OneLineMonthAndDayElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public NumberElement Number { get; set; }
public long DelimiterImageIndex { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var monthAndDay = (MonthAndDayElement) _parent;
var images = Number.GetImagesForNumber(resources, state.Time.Month, monthAndDay.TwoDigitsMonth ? 2 : 1);
images.Add(resources[DelimiterImageIndex]);
images.AddRange(Number.GetImagesForNumber(resources, state.Time.Day, monthAndDay.TwoDigitsDay ? 2 : 1));
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, Number.GetBox());
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Number = new NumberElement(parameter, this);
return Number;
case 2:
DelimiterImageIndex = parameter.Value;
return new ValueElement(parameter, this);
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class SeparateMonthAndDayElement : CompositeElement, IDrawable
{
public SeparateMonthAndDayElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public NumberElement Month { get; set; }
public NumberElement Day { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var monthAndDay = (MonthAndDayElement) _parent;
Month?.Draw(drawer, resources, state.Time.Month, monthAndDay.TwoDigitsMonth ? 2 : 1);
Day?.Draw(drawer, resources, state.Time.Day, monthAndDay.TwoDigitsDay ? 2 : 1);
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Month = new NumberElement(parameter, this);
return Month;
case 3:
Day = new NumberElement(parameter, this);
return Day;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,34 @@
namespace WatchFace.Parser.Models.Elements
{
public class MonthAndDayElement : ContainerElement
{
public MonthAndDayElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public OneLineMonthAndDayElement OneLine { get; set; }
public SeparateMonthAndDayElement Separate { get; set; }
public bool TwoDigitsMonth { get; set; }
public bool TwoDigitsDay { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Separate = new SeparateMonthAndDayElement(parameter, this);
return Separate;
case 2:
OneLine = new OneLineMonthAndDayElement(parameter, this);
return OneLine;
case 3:
TwoDigitsMonth = parameter.Value == 1;
return new ValueElement(parameter, this);
case 4:
TwoDigitsDay = parameter.Value == 1;
return new ValueElement(parameter, this);
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class WeekDayElement : ImageSetElement
{
private static readonly Dictionary<DayOfWeek, int> DaysOfWeek = new Dictionary<DayOfWeek, int>
{
{DayOfWeek.Monday, 0},
{DayOfWeek.Tuesday, 1},
{DayOfWeek.Wednesday, 2},
{DayOfWeek.Thursday, 3},
{DayOfWeek.Friday, 4},
{DayOfWeek.Saturday, 5},
{DayOfWeek.Sunday, 6}
};
public WeekDayElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, DaysOfWeek[state.Time.DayOfWeek]);
}
}
}

View File

@ -0,0 +1,26 @@
namespace WatchFace.Parser.Models.Elements
{
public class DateElement : ContainerElement
{
public DateElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public MonthAndDayElement MonthAndDay { get; set; }
public WeekDayElement WeekDay { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
MonthAndDay = new MonthAndDayElement(parameter, this);
return MonthAndDay;
case 2:
WeekDay = new WeekDayElement(parameter, this);
return WeekDay;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,15 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements.GoalProgress
{
public class CircularGoalProgressElement : CircularProgressElement
{
public CircularGoalProgressElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Steps, state.Goal);
}
}
}

View File

@ -0,0 +1,15 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements.GoalProgress
{
public class GoalReachedElement : ImageElement
{
public GoalReachedElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] images, WatchState state)
{
if (state.Steps >= state.Goal) Draw(drawer, images);
}
}
}

View File

@ -0,0 +1,15 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements.GoalProgress
{
public class LinearGoalProgressElement : LinearProgressElement
{
public LinearGoalProgressElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Steps, state.Goal);
}
}
}

View File

@ -0,0 +1,32 @@
using WatchFace.Parser.Models.Elements.GoalProgress;
namespace WatchFace.Parser.Models.Elements
{
public class GoalProgressElement : ContainerElement
{
public GoalProgressElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public GoalReachedElement GoalReached { get; set; }
public LinearGoalProgressElement Linear { get; set; }
public CircularGoalProgressElement Circular { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
GoalReached = new GoalReachedElement(parameter, this);
return GoalReached;
case 2:
Linear = new LinearGoalProgressElement(parameter, this);
return Linear;
case 3:
Circular = new CircularGoalProgressElement(parameter, this);
return Circular;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,13 @@
namespace WatchFace.Parser.Models.Elements
{
public class AlarmElement : SwitchElement
{
public AlarmElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override bool SwitchState(WatchState state)
{
return state.Alarm;
}
}
}

View File

@ -0,0 +1,13 @@
namespace WatchFace.Parser.Models.Elements
{
public class BluetoothElement : SwitchElement
{
public BluetoothElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override bool SwitchState(WatchState state)
{
return state.Bluetooth;
}
}
}

View File

@ -0,0 +1,13 @@
namespace WatchFace.Parser.Models.Elements
{
public class DoNotDisturbElement : SwitchElement
{
public DoNotDisturbElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override bool SwitchState(WatchState state)
{
return state.DoNotDisturb;
}
}
}

View File

@ -0,0 +1,13 @@
namespace WatchFace.Parser.Models.Elements
{
public class UnlockedElement : SwitchElement
{
public UnlockedElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public override bool SwitchState(WatchState state)
{
return state.Unlocked;
}
}
}

View File

@ -0,0 +1,34 @@
namespace WatchFace.Parser.Models.Elements
{
public class StatusElement : ContainerElement
{
public StatusElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public BluetoothElement Bluetooth { get; set; }
public AlarmElement Alarm { get; set; }
public UnlockedElement Unlocked { get; set; }
public DoNotDisturbElement DoNotDisturb { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Bluetooth = new BluetoothElement(parameter, this);
return Bluetooth;
case 2:
Alarm = new AlarmElement(parameter, this);
return Alarm;
case 3:
Unlocked = new UnlockedElement(parameter, this);
return Unlocked;
case 4:
DoNotDisturb = new DoNotDisturbElement(parameter, this);
return DoNotDisturb;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,33 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class AmPmElement : CoordinatesElement, IDrawable
{
public AmPmElement(Parameter parameter, Element parent, string name) : base(parameter, parent, name) { }
public long ImageIndexAm { get; set; }
public long ImageIndexPm { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var imageIndex = state.Time.Hour < 12 ? ImageIndexAm : ImageIndexPm;
drawer.DrawImage(resources[imageIndex], new Point((int) X, (int) Y));
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 3:
ImageIndexAm = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndexAm));
case 4:
ImageIndexPm = parameter.Value;
return new ValueElement(parameter, this, nameof(ImageIndexPm));
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,18 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class HoursElement : TwoDigitsElement, IDrawable
{
public HoursElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var timeElement = (TimeElement) _parent;
var hours = timeElement.AmPm == null ? state.Time.Hour : state.Time.Hour % 12;
Draw(drawer, resources, hours);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class MinutesElement : TwoDigitsElement, IDrawable
{
public MinutesElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Minute);
}
}
}

View File

@ -0,0 +1,16 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class SecondsElement : TwoDigitsElement, IDrawable
{
public SecondsElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Second);
}
}
}

View File

@ -0,0 +1,34 @@
namespace WatchFace.Parser.Models.Elements
{
public class TimeElement : ContainerElement
{
public TimeElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public HoursElement Hours { get; set; }
public MinutesElement Minutes { get; set; }
public SecondsElement Seconds { get; set; }
public AmPmElement AmPm { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Hours = new HoursElement(parameter, this, nameof(Hours));
return Hours;
case 2:
Minutes = new MinutesElement(parameter, this, nameof(Minutes));
return Minutes;
case 3:
Seconds = new SecondsElement(parameter, this, nameof(Seconds));
return Seconds;
case 4:
AmPm = new AmPmElement(parameter, this, nameof(AmPm));
return AmPm;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,55 @@
using System.Collections.Generic;
namespace WatchFace.Parser.Models.Elements
{
public class WatchFace : ContainerElement
{
public WatchFace(IEnumerable<Parameter> parameters) : base(parameters) { }
public BackgroundElement Background { get; set; }
public TimeElement Time { get; set; }
public ActivityElement Activity { get; set; }
public DateElement Date { get; set; }
public WeatherElement Weather { get; set; }
public GoalProgressElement GoalProgress { get; set; }
public StatusElement Status { get; set; }
public BatteryElement Battery { get; set; }
public AnalogDialElement AnalogDial { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 2:
Background = new BackgroundElement(parameter);
return Background;
case 3:
Time = new TimeElement(parameter);
return Time;
case 4:
Activity = new ActivityElement(parameter);
return Activity;
case 5:
Date = new DateElement(parameter);
return Date;
case 6:
Weather = new WeatherElement(parameter);
return Weather;
case 7:
GoalProgress = new GoalProgressElement(parameter);
return GoalProgress;
case 8:
Status = new StatusElement(parameter);
return Status;
case 9:
Battery = new BatteryElement(parameter);
return Battery;
case 10:
AnalogDial = new AnalogDialElement(parameter);
return AnalogDial;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,18 @@
using System.Drawing;
namespace WatchFace.Parser.Models.Elements
{
public class AirPollutionImageElement : ImageSetElement
{
public AirPollutionImageElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.Air == AirCondition.Unknown) return;
var imageIndex = (int) state.Air;
Draw(drawer, resources, imageIndex);
}
}
}

View File

@ -0,0 +1,22 @@
namespace WatchFace.Parser.Models.Elements
{
public class AirPollutionElement : ContainerElement
{
public AirPollutionElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public AirPollutionImageElement Current { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 2:
Current = new AirPollutionImageElement(parameter, this);
return Current;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,17 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class CurrentTemperatureElement : TemperatureNumberElement, IDrawable
{
public CurrentTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.CurrentTemperature != null)
Draw(drawer, resources, state.CurrentTemperature.Value);
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using WatchFace.Parser.Helpers;
namespace WatchFace.Parser.Models.Elements
{
public class TemperatureNumberElement : CompositeElement
{
public TemperatureNumberElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public NumberElement Number { get; set; }
public long MinusImageIndex { get; set; }
public long? DegreesImageIndex { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, int temperature)
{
var images = GetImagesForTemperature(resources, temperature);
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, Number.GetBox());
}
public List<Bitmap> GetImagesForTemperature(Bitmap[] resources, int temperature)
{
var images = new List<Bitmap>();
if (temperature < 0)
images.Add(resources[MinusImageIndex]);
images.AddRange(Number.GetImagesForNumber(resources, Math.Abs(temperature)));
if (DegreesImageIndex != null)
images.Add(resources[DegreesImageIndex.Value]);
return images;
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Number = new NumberElement(parameter, this);
return Number;
case 2:
MinusImageIndex = parameter.Value;
return new ValueElement(parameter, this);
case 3:
DegreesImageIndex = parameter.Value;
return new ValueElement(parameter, this);
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using WatchFace.Parser.Helpers;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class OnelineTemperatureElement : CompositeElement, IDrawable
{
public OnelineTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public NumberElement Number { get; set; }
public long MinusImageIndex { get; set; }
public long DelimiterImageIndex { get; set; }
public bool AppendDegreesToBoth { get; set; }
public long? DegreesImageIndex { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.DayTemperature == null || state.NightTemperature == null) return;
var images = new List<Bitmap>();
if (state.DayTemperature < 0)
images.Add(resources[MinusImageIndex]);
images.AddRange(Number.GetImagesForNumber(resources, Math.Abs(state.DayTemperature.Value)));
if (AppendDegreesToBoth && DegreesImageIndex != null)
images.Add(resources[DegreesImageIndex.Value]);
images.Add(resources[DelimiterImageIndex]);
if (state.NightTemperature < 0)
images.Add(resources[MinusImageIndex]);
images.AddRange(Number.GetImagesForNumber(resources, Math.Abs(state.NightTemperature.Value)));
if (DegreesImageIndex != null)
images.Add(resources[DegreesImageIndex.Value]);
DrawerHelper.DrawImages(drawer, images, Number.Spacing, Number.Alignment, Number.GetBox());
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Number = new NumberElement(parameter, this);
return Number;
case 2:
MinusImageIndex = parameter.Value;
return new ValueElement(parameter, this);
case 3:
DelimiterImageIndex = parameter.Value;
return new ValueElement(parameter, this);
case 4:
AppendDegreesToBoth = parameter.Value > 0;
return new ValueElement(parameter, this);
case 5:
DegreesImageIndex = parameter.Value;
return new ValueElement(parameter, this);
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,34 @@
namespace WatchFace.Parser.Models.Elements
{
public class SeparateTemperatureElement : ContainerElement
{
public SeparateTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public TodayDayTemperatureElement Day { get; set; }
public TodayNightTemperatureElement Night { get; set; }
public CoordinatesElement Unknown3 { get; set; }
public CoordinatesElement Unknown4 { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Day = new TodayDayTemperatureElement(parameter, this);
return Day;
case 2:
Night = new TodayNightTemperatureElement(parameter, this);
return Night;
case 3:
Unknown3 = new CoordinatesElement(parameter, this);
return Unknown3;
case 4:
Unknown4 = new CoordinatesElement(parameter, this);
return Unknown4;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,17 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class TodayDayTemperatureElement : TemperatureNumberElement, IDrawable
{
public TodayDayTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.DayTemperature != null)
Draw(drawer, resources, state.DayTemperature.Value);
}
}
}

View File

@ -0,0 +1,17 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class TodayNightTemperatureElement : TemperatureNumberElement, IDrawable
{
public TodayNightTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.NightTemperature != null)
Draw(drawer, resources, state.NightTemperature.Value);
}
}
}

View File

@ -0,0 +1,26 @@
namespace WatchFace.Parser.Models.Elements
{
public class TodayTemperatureElement : ContainerElement
{
public TodayTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public SeparateTemperatureElement Separate { get; set; }
public OnelineTemperatureElement Oneline { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Separate = new SeparateTemperatureElement(parameter, this);
return Separate;
case 2:
Oneline = new OnelineTemperatureElement(parameter, this);
return Oneline;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,26 @@
namespace WatchFace.Parser.Models.Elements
{
public class TemperatureElement : ContainerElement
{
public TemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public CurrentTemperatureElement Current { get; set; }
public TodayTemperatureElement Today { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Current = new CurrentTemperatureElement(parameter, this);
return Current;
case 2:
Today = new TodayTemperatureElement(parameter, this);
return Today;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,50 @@
using System.Drawing;
using System.Reflection;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class WeatherIconsElement : CompositeElement, IDrawable
{
public WeatherIconsElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public CoordinatesElement Current { get; set; }
public CoordinatesElement Today { get; set; }
public CoordinatesElement Tomorrow { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.CurrentWeather != WeatherCondition.Unknown)
drawer.DrawImage(LoadWeatherImage(state.CurrentWeather), Current.X, Current.Y);
else if (state.TodayWeather != WeatherCondition.Unknown)
drawer.DrawImage(LoadWeatherImage(state.TodayWeather), Today.X, Today.Y);
else if (state.TomorrowWeather != WeatherCondition.Unknown)
drawer.DrawImage(LoadWeatherImage(state.TomorrowWeather), Tomorrow.X, Tomorrow.Y);
}
private static Bitmap LoadWeatherImage(WeatherCondition weather)
{
var appLocation = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
return (Bitmap) Image.FromFile(System.IO.Path.Combine(appLocation, "WeatherIcons", $"{(int) weather}.png"));
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Current = new CoordinatesElement(parameter, this);
return Current;
case 3:
Today = new CoordinatesElement(parameter, this);
return Today;
case 4:
Tomorrow = new CoordinatesElement(parameter, this);
return Tomorrow;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,30 @@
namespace WatchFace.Parser.Models.Elements
{
public class WeatherElement : ContainerElement
{
public WeatherElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public WeatherIconsElement WeatherIcons { get; set; }
public TemperatureElement Temperature { get; set; }
public AirPollutionElement AirPollution { get; set; }
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
WeatherIcons = new WeatherIconsElement(parameter, this);
return WeatherIcons;
case 2:
Temperature = new TemperatureElement(parameter, this);
return Temperature;
case 3:
AirPollution = new AirPollutionElement(parameter, this);
return AirPollution;
default:
return base.CreateChildForParameter(parameter);
}
}
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace WatchFace.Parser.Models
{
[Flags]
public enum TextAlignment
{
Left = 2,
Right = 4,
Top = 16,
Bottom = 32
}
}

View File

@ -0,0 +1,31 @@
using System;
namespace WatchFace.Parser.Models
{
public class WatchState
{
public DateTime Time { get; set; } = DateTime.Now;
public int Steps { get; set; } = 14876;
public int Goal { get; set; } = 8000;
public int Calories { get; set; } = 764;
public int Distance { get; set; } = 2367;
public int? Pulse { get; set; } = 62;
public int? CurrentTemperature { get; set; } = -10;
public int? DayTemperature { get; set; } = -15;
public int? NightTemperature { get; set; } = -24;
public int? TomorrowDayTemperature { get; set; }
public int? TomorrowNightTemperature { get; set; }
public WeatherCondition CurrentWeather { get; set; } = WeatherCondition.Cloudy;
public WeatherCondition TodayWeather { get; set; } = WeatherCondition.Unknown;
public WeatherCondition TomorrowWeather { get; set; } = WeatherCondition.Unknown;
public AirCondition Air { get; set; } = AirCondition.Excellent;
public int BatteryLevel { get; set; } = 67;
public bool Bluetooth { get; set; } = true;
public bool Unlocked { get; set; } = true;
public bool DoNotDisturb { get; set; }
public bool Alarm { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace WatchFace.Parser.Models
{
public enum WeatherCondition
{
Unknown = 279,
Cloudy = 247
}
}

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.3")]
[assembly: AssemblyFileVersion("1.0.0.3")]
[assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")]

View File

@ -40,6 +40,7 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
@ -76,10 +77,77 @@
<Compile Include="Elements\WeatherElements\TemperatureNumber.cs" />
<Compile Include="Elements\WeatherElements\TodayTemperature.cs" />
<Compile Include="Elements\WeatherElements\WeatherIcon.cs" />
<Compile Include="Helpers\DrawerHelper.cs" />
<Compile Include="Interfaces\IDrawable.cs" />
<Compile Include="JsonConverters\HexStringJsonConverter.cs" />
<Compile Include="Models\Elements\Activity\CaloriesElement.cs" />
<Compile Include="Models\Elements\Activity\StepsElement.cs" />
<Compile Include="Models\Elements\Activity\StepsGoalElement.cs" />
<Compile Include="Models\Elements\Activity\PulseElement.cs" />
<Compile Include="Models\Elements\AnalogDial\SecondsClockHandElement.cs" />
<Compile Include="Models\Elements\AnalogDial\MinutesClockHandElement.cs" />
<Compile Include="Models\Elements\AnalogDial\HoursClockHandElement.cs" />
<Compile Include="Models\Elements\Basic\ContainerElement.cs" />
<Compile Include="Models\Elements\Battery\BatteryLenearProgressElement.cs" />
<Compile Include="Models\Elements\Battery\BatteryNumberElement.cs" />
<Compile Include="Models\Elements\Common\ImageSetElement.cs" />
<Compile Include="Models\Elements\Common\CircularProgressElement.cs" />
<Compile Include="Models\Elements\Common\ClockHandElement.cs" />
<Compile Include="Models\Elements\GoalProgressElement.cs" />
<Compile Include="Models\Elements\GoalProgress\GoalReachedElement.cs" />
<Compile Include="Models\Elements\Common\LinearProgressElement.cs" />
<Compile Include="Models\Elements\GoalProgress\CircularGoalProgressElement.cs" />
<Compile Include="Models\Elements\GoalProgress\LinearGoalProgressElement.cs" />
<Compile Include="Models\Elements\Battery\BatteryImageSetElement.cs" />
<Compile Include="Models\Elements\BatteryElement.cs" />
<Compile Include="Models\Elements\StatusElement.cs" />
<Compile Include="Models\Elements\AnalogDialElement.cs" />
<Compile Include="Models\Elements\WeatherElement.cs" />
<Compile Include="Models\Elements\DateElement.cs" />
<Compile Include="Models\Elements\Activity\DistanceElement.cs" />
<Compile Include="Models\Elements\BackgroundElement.cs" />
<Compile Include="Models\Elements\ActivityElement.cs" />
<Compile Include="Models\Elements\Date\MonthAndDay\SeparateMonthAndDayElement.cs" />
<Compile Include="Models\Elements\Date\MonthAndDay\OneLineMonthAndDayElement.cs" />
<Compile Include="Models\Elements\Date\MonthAndDayElement.cs" />
<Compile Include="Models\Elements\Date\WeekDayElement.cs" />
<Compile Include="Models\Elements\TimeElement.cs" />
<Compile Include="Models\Elements\Time\SecondsElement.cs" />
<Compile Include="Models\Elements\Time\MinutesElement.cs" />
<Compile Include="Models\Elements\Time\HoursElement.cs" />
<Compile Include="Models\Elements\Common\TwoDigitsElement.cs" />
<Compile Include="Models\Elements\WatchFace.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\OnelineTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\TodayNightTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\TodayDayTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\CurrentTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\AirPollution\AirPollutionImageElement.cs" />
<Compile Include="Models\Elements\Weather\AirPollutionElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\SeparateTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\TodayTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\TemperatureNumberElement.cs" />
<Compile Include="Models\Elements\Weather\TemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\WeatherIconsElement.cs" />
<Compile Include="Models\TextAlignment.cs" />
<Compile Include="Models\Elements\Common\DigitElement.cs" />
<Compile Include="Models\Elements\Basic\ValueElement.cs" />
<Compile Include="Models\Elements\Status\UnlockedElement.cs" />
<Compile Include="Models\Elements\Status\DoNotDisturbElement.cs" />
<Compile Include="Models\Elements\Status\AlarmElement.cs" />
<Compile Include="Models\Elements\Status\BluetoothElement.cs" />
<Compile Include="Models\Elements\Common\SwitchElement.cs" />
<Compile Include="Models\Elements\Basic\CompositeElement.cs" />
<Compile Include="Models\Elements\Basic\Element.cs" />
<Compile Include="Models\Elements\Time\AmPmElement.cs" />
<Compile Include="Models\Elements\Common\ImageElement.cs" />
<Compile Include="Models\Elements\Common\CoordinatesElement.cs" />
<Compile Include="Models\Elements\Common\NumberElement.cs" />
<Compile Include="Models\Header.cs" />
<Compile Include="Models\Parameter.cs" />
<Compile Include="Models\ParameterFlags.cs" />
<Compile Include="Models\WatchState.cs" />
<Compile Include="Models\AirCondition.cs" />
<Compile Include="Models\WeatherCondition.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reader.cs" />
<Compile Include="Attributes\ParameterImagesCountAttribute.cs" />
@ -100,5 +168,14 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="WeatherIcons\247.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="WeatherIcons\279.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

View File

@ -20,11 +20,8 @@ namespace WatchFace.Parser
_images = images;
}
public void Write(WatchFace watchFace)
public void Write(IList<Parameter> descriptor)
{
Logger.Trace("Building parameters for watch face...");
var descriptor = ParametersConverter.Build(watchFace);
Logger.Trace("Encoding parameters...");
var encodedParameters = new Dictionary<byte, MemoryStream>(descriptor.Count);
foreach (var parameter in descriptor)

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Newtonsoft.Json;
using NLog;
@ -8,6 +9,7 @@ using NLog.Config;
using NLog.Targets;
using Resources;
using Resources.Models;
using WatchFace.Parser.Models;
using WatchFace.Parser.Utils;
using Reader = WatchFace.Parser.Reader;
using Writer = WatchFace.Parser.Writer;
@ -29,7 +31,8 @@ namespace WatchFace
Console.WriteLine();
Console.WriteLine("Usage examples:");
Console.WriteLine(" {0}.exe watchface.bin - unpacks watchface images and config", AppName);
Console.WriteLine(" {0}.exe watchface.json - packs config and referenced images to bin file", AppName);
Console.WriteLine(" {0}.exe watchface.json - packs config and referenced images to bin file",
AppName);
Console.WriteLine(" {0}.exe mili_chaohu.res - unpacks resource file images", AppName);
Console.WriteLine(" {0}.exe mili_chaohu - packs folder content to res file", AppName);
return;
@ -123,6 +126,14 @@ namespace WatchFace
var watchFace = ParseResources(reader);
if (watchFace == null) return;
Logger.Debug("Generating preview...");
var previewWatchFace = new Parser.Models.Elements.WatchFace(reader.Parameters);
var preview = new Bitmap(176, 176);
var graphics = Graphics.FromImage(preview);
previewWatchFace.Draw(graphics, reader.Images.ToArray(), new WatchState());
preview.Save(Path.Combine(outputDirectory, $"{baseName}.png"), ImageFormat.Png);
Logger.Debug("Exporting resources to '{0}'", outputDirectory);
var reDescriptor = new FileDescriptor {Images = reader.Images};
new Extractor(reDescriptor).Extract(outputDirectory);
@ -185,11 +196,22 @@ namespace WatchFace
var imagesReader = new ImagesLoader(imagesDirectory);
imagesReader.Process(watchFace);
Logger.Trace("Building parameters for watch face...");
var descriptor = ParametersConverter.Build(watchFace);
Logger.Debug("Generating preview...");
var previewWatchFace = new Parser.Models.Elements.WatchFace(descriptor);
var preview = new Bitmap(176, 176);
var graphics = Graphics.FromImage(preview);
previewWatchFace.Draw(graphics, imagesReader.Images.ToArray(), new WatchState());
preview.Save(Path.ChangeExtension(outputFileName, ".png"));
Logger.Debug("Writing watch face to '{0}'", outputFileName);
using (var fileStream = File.OpenWrite(outputFileName))
{
var writer = new Writer(fileStream, imagesReader.Images);
writer.Write(watchFace);
writer.Write(descriptor);
fileStream.Flush();
}
}

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.3")]
[assembly: AssemblyFileVersion("1.0.0.3")]
[assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")]