diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java index 9feedd7..be3d4e8 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java @@ -14,11 +14,15 @@ import java.sql.SQLException; public class RecorderApp { public static void main(String[] args) { FlatLightLaf.setup(); + System.out.println("Setup FlatLAF"); try { DataSource dataSource = new DataSource("jdbc:sqlite:runs.db"); + System.out.println("Initialized SQLite3 datasource"); var window = new RecorderAppWindow(dataSource); + System.out.println("Initialized App Window"); window.addWindowListener(new WindowDataSourceCloser(dataSource)); window.setVisible(true); + System.out.println("Set App Window as visible"); } catch (SQLException e) { JOptionPane.showMessageDialog(null, "Failed to open database: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/Resources.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/Resources.java new file mode 100644 index 0000000..61f0498 --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/Resources.java @@ -0,0 +1,31 @@ +package com.github.andrewlalis.running_every_day; + +import java.awt.*; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public final class Resources { + private Resources() {} + + public static String readResourceAsString(String name) throws IOException { + try (var in = Resources.class.getClassLoader().getResourceAsStream(name)) { + if (in == null) { + throw new IOException("Missing resource: " + name); + } + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } + } + + public static Font readTTFFont(String name) throws IOException { + try (var in = Resources.class.getClassLoader().getResourceAsStream(name)) { + if (in == null) { + throw new IOException("Missing resource: " + name); + } + try { + return Font.createFont(Font.TRUETYPE_FONT, in); + } catch (FontFormatException e) { + throw new IOException(e); + } + } + } +} diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/db/DataSource.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/db/DataSource.java index 2307b37..1ebc1b4 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/db/DataSource.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/db/DataSource.java @@ -1,5 +1,6 @@ package com.github.andrewlalis.running_every_day.data.db; +import com.github.andrewlalis.running_every_day.Resources; import com.github.andrewlalis.running_every_day.data.RunRecordRepository; import java.io.IOException; @@ -53,19 +54,12 @@ public class DataSource { } if (shouldInitSchema) { try (var stmt = this.conn.createStatement()) { - stmt.execute(readResource("schema.sql")); + String query = Resources.readResourceAsString("schema.sql"); + stmt.execute(query); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); } } } - - public static String readResource(String name) { - try (var in = DataSource.class.getClassLoader().getResourceAsStream(name)) { - if (in == null) { - throw new RuntimeException("Missing resource: " + name); - } - return new String(in.readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } } diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/ChartsPanel.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/ChartsPanel.java index 243fe0d..1e83355 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/ChartsPanel.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/ChartsPanel.java @@ -3,6 +3,7 @@ package com.github.andrewlalis.running_every_day.view; import com.github.andrewlalis.running_every_day.data.db.DataSource; import com.github.andrewlalis.running_every_day.view.chart.ChartRenderingPanel; import com.github.andrewlalis.running_every_day.view.chart.WeightChartRenderer; +import com.github.andrewlalis.running_every_day.view.chart.WeightChartRenderer2; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.data.category.DefaultCategoryDataset; @@ -21,7 +22,7 @@ public class ChartsPanel extends JPanel { public ChartsPanel(DataSource dataSource) { super(new BorderLayout()); this.dataSource = dataSource; - var drawingPanel = new ChartRenderingPanel(new WeightChartRenderer(dataSource)); + var drawingPanel = new ChartRenderingPanel(new WeightChartRenderer2(dataSource)); this.add(drawingPanel, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/DateSeriesChartRenderer.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/DateSeriesChartRenderer.java new file mode 100644 index 0000000..42b9980 --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/DateSeriesChartRenderer.java @@ -0,0 +1,67 @@ +package com.github.andrewlalis.running_every_day.view.chart; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.time.Day; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; +import org.jfree.data.xy.XYDataset; + +import java.awt.*; +import java.text.DateFormat; +import java.time.LocalDate; + +public abstract class DateSeriesChartRenderer extends JFreeChartRenderer { + protected record Datapoint (double value, LocalDate date) {} + + private final String title; + private final Paint linePaint; + + protected DateSeriesChartRenderer(String title, Paint linePaint) { + this.title = title; + this.linePaint = linePaint; + } + + protected abstract Datapoint[] getData() throws Exception; + + @Override + protected JFreeChart getChart() throws Exception { + TimeSeries series = new TimeSeries("Series"); + double minValue = Double.MAX_VALUE; + double maxValue = Double.MIN_VALUE; + for (var d : getData()) { + minValue = Math.min(minValue, d.value()); + maxValue = Math.max(maxValue, d.value()); + series.add( + new Day( + d.date().getDayOfMonth(), + d.date().getMonthValue(), + d.date().getYear() + ), + d.value(), + false + ); + } + XYDataset dataset = new TimeSeriesCollection(series); + + DateAxis domainAxis = new DateAxis(); + domainAxis.setVerticalTickLabels(true); + domainAxis.setDateFormatOverride(DateFormat.getDateInstance()); + + NumberAxis rangeAxis = new NumberAxis(); + rangeAxis.setRangeWithMargins(minValue, maxValue); + + XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, new XYLineAndShapeRenderer()); + var chart = new JFreeChart(title, plot); + chart.removeLegend(); + return chart; + } + + @Override + protected void applyCustomStyles(JFreeChart chart) { + applyStandardXYLineColor(chart, linePaint); + } +} diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java index cd97c52..3f4c20c 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java @@ -1,24 +1,75 @@ package com.github.andrewlalis.running_every_day.view.chart; +import com.github.andrewlalis.running_every_day.Resources; +import org.jfree.chart.ChartTheme; import org.jfree.chart.JFreeChart; +import org.jfree.chart.StandardChartTheme; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import java.awt.*; import java.awt.geom.Rectangle2D; +import java.io.IOException; public abstract class JFreeChartRenderer implements ChartRenderer { + private JFreeChart lastChart = null; + public static final ChartTheme standardChartTheme = getChartTheme(); + protected abstract JFreeChart getChart() throws Exception; + protected void applyCustomStyles(JFreeChart chart) {} + + public void refresh() { + try { + lastChart = getChart(); + standardChartTheme.apply(lastChart); + applyCustomStyles(lastChart); + } catch (Exception e) { + e.printStackTrace(); + } + } @Override public void render(Graphics2D graphics, Rectangle2D area) { try { - var chart = getChart(); - // Apply theme to the chart. - chart.draw(graphics, area); + if (lastChart == null) { + refresh(); + } + lastChart.draw(graphics, area); } catch (Exception e) { graphics.setColor(Color.BLACK); graphics.setBackground(Color.WHITE); graphics.fill(area); graphics.drawString("Error: " + e.getMessage(), 20, 40); + e.printStackTrace(); } } + + private static ChartTheme getChartTheme() { + StandardChartTheme theme = new StandardChartTheme("Standard Theme"); + Font baseFont = Font.getFont("sans-serif"); + Font lightFont = Font.getFont("sans-serif"); + try { + baseFont = Resources.readTTFFont("font/Roboto-Regular.ttf"); + lightFont = Resources.readTTFFont("font/Roboto-Light.ttf"); + } catch (IOException e) { + e.printStackTrace(); + } + theme.setSmallFont(baseFont.deriveFont(10f)); + theme.setRegularFont(baseFont.deriveFont(12f)); + theme.setLargeFont(lightFont.deriveFont(18f)); + theme.setExtraLargeFont(lightFont.deriveFont(30f)); + + theme.setChartBackgroundPaint(Color.WHITE); + theme.setPlotBackgroundPaint(Color.WHITE); + theme.setRangeGridlinePaint(Color.GRAY); + theme.setDomainGridlinePaint(Color.GRAY); + + return theme; + } + + protected static void applyStandardXYLineColor(JFreeChart chart, Paint paint) { + XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer(); + renderer.setSeriesPaint(0, paint); + renderer.setSeriesShapesVisible(0, false); + renderer.setSeriesStroke(0, new BasicStroke(3)); + } } diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer.java index 0fdb6df..cdf18f5 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer.java @@ -6,18 +6,18 @@ import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.time.Day; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; +import java.awt.*; import java.text.DateFormat; import java.time.LocalDate; import java.util.List; public class WeightChartRenderer extends JFreeChartRenderer { - private record WeightDatapoint(int weightGrams, LocalDate date){} + private record WeightDatapoint(double weightKg, LocalDate date){} private final DataSource dataSource; @@ -30,26 +30,36 @@ public class WeightChartRenderer extends JFreeChartRenderer { TimeSeries series = new TimeSeries("Weight", "Date", "Weight (Kg)"); List datapoints = Queries.findAll( dataSource.conn(), - "SELECT date, weight FROM run ORDER BY date ASC", + "SELECT date, weight FROM run ORDER BY date ASC LIMIT 50", rs -> new WeightDatapoint( - rs.getInt(2), + rs.getInt(2) / 1000.0, LocalDate.parse(rs.getString(1)) ) ); + double minWeight = Double.MAX_VALUE; + double maxWeight = Double.MIN_VALUE; for (var dp : datapoints) { - series.add(new Day(dp.date.getDayOfMonth(), dp.date.getMonthValue(), dp.date.getYear()), dp.weightGrams); + minWeight = Math.min(minWeight, dp.weightKg); + maxWeight = Math.max(maxWeight, dp.weightKg); + series.add(new Day(dp.date.getDayOfMonth(), dp.date.getMonthValue(), dp.date.getYear()), dp.weightKg); } TimeSeriesCollection dataset = new TimeSeriesCollection(series); - DateAxis domainAxis = new DateAxis("Date"); + DateAxis domainAxis = new DateAxis(); domainAxis.setVerticalTickLabels(true); domainAxis.setDateFormatOverride(DateFormat.getDateInstance()); + NumberAxis rangeAxis = new NumberAxis("Weight (Kg)"); - rangeAxis.setAutoRange(true); - XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false); + rangeAxis.setRangeWithMargins(minWeight, maxWeight); - XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, renderer); + XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, new XYLineAndShapeRenderer()); + var chart = new JFreeChart("Weight", plot); + chart.removeLegend(); + return chart; + } - return new JFreeChart(plot); + @Override + protected void applyCustomStyles(JFreeChart chart) { + applyStandardXYLineColor(chart, Color.BLUE); } } diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer2.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer2.java new file mode 100644 index 0000000..54c96fd --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/WeightChartRenderer2.java @@ -0,0 +1,30 @@ +package com.github.andrewlalis.running_every_day.view.chart; + +import com.github.andrewlalis.running_every_day.data.db.DataSource; +import com.github.andrewlalis.running_every_day.data.db.Queries; + +import java.awt.*; +import java.time.LocalDate; +import java.util.List; + +public class WeightChartRenderer2 extends DateSeriesChartRenderer { + private final DataSource dataSource; + + public WeightChartRenderer2(DataSource dataSource) { + super("Weight (Kg)", Color.BLUE); + this.dataSource = dataSource; + } + + @Override + protected Datapoint[] getData() throws Exception { + List datapoints = Queries.findAll( + dataSource.conn(), + "SELECT weight, date FROM run ORDER BY date ASC LIMIT 50", + rs -> new Datapoint( + rs.getInt(1) / 1000.0, + LocalDate.parse(rs.getString(2)) + ) + ); + return datapoints.toArray(new Datapoint[0]); + } +} diff --git a/recorder/src/main/resources/font/LICENSE.txt b/recorder/src/main/resources/font/LICENSE.txt new file mode 100644 index 0000000..75b5248 --- /dev/null +++ b/recorder/src/main/resources/font/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/recorder/src/main/resources/font/Roboto-Black.ttf b/recorder/src/main/resources/font/Roboto-Black.ttf new file mode 100644 index 0000000..0112e7d Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Black.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-BlackItalic.ttf b/recorder/src/main/resources/font/Roboto-BlackItalic.ttf new file mode 100644 index 0000000..b2c6aca Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-BlackItalic.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-Bold.ttf b/recorder/src/main/resources/font/Roboto-Bold.ttf new file mode 100644 index 0000000..43da14d Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Bold.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-BoldItalic.ttf b/recorder/src/main/resources/font/Roboto-BoldItalic.ttf new file mode 100644 index 0000000..bcfdab4 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-BoldItalic.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-Italic.ttf b/recorder/src/main/resources/font/Roboto-Italic.ttf new file mode 100644 index 0000000..1b5eaa3 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Italic.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-Light.ttf b/recorder/src/main/resources/font/Roboto-Light.ttf new file mode 100644 index 0000000..e7307e7 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Light.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-LightItalic.ttf b/recorder/src/main/resources/font/Roboto-LightItalic.ttf new file mode 100644 index 0000000..2d277af Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-LightItalic.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-Medium.ttf b/recorder/src/main/resources/font/Roboto-Medium.ttf new file mode 100644 index 0000000..ac0f908 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Medium.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-MediumItalic.ttf b/recorder/src/main/resources/font/Roboto-MediumItalic.ttf new file mode 100644 index 0000000..fc36a47 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-MediumItalic.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-Regular.ttf b/recorder/src/main/resources/font/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Regular.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-Thin.ttf b/recorder/src/main/resources/font/Roboto-Thin.ttf new file mode 100644 index 0000000..2e0dee6 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-Thin.ttf differ diff --git a/recorder/src/main/resources/font/Roboto-ThinItalic.ttf b/recorder/src/main/resources/font/Roboto-ThinItalic.ttf new file mode 100644 index 0000000..084f9c0 Binary files /dev/null and b/recorder/src/main/resources/font/Roboto-ThinItalic.ttf differ