diff --git a/.gitignore b/.gitignore index 0fa6b67..c40f453 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,8 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# Ignore imprint.md +assets/imprint.md + +upload-keystore.jks diff --git a/LocalizationTable.csv b/LocalizationTable.csv deleted file mode 100644 index 8e42ce7..0000000 --- a/LocalizationTable.csv +++ /dev/null @@ -1,37 +0,0 @@ -key,en,de -appTitle,MultiplyMe Alpha, -helloWorld,Hello World!,Hallo Welt -creditsMessage,Developed by Janneck Franke,Entwickelt von Janneck Franke -singleTableTitle,Single Multiplication Table,Einzelne Einmaleins Reihe -singleTableDescription,Practise a single multiplication table,Eine einzelne Einmaleins Reihe üben. -multipleTableTitle,Multiple Multiplication Tables,Mehrere Einmaleins Reihen -multipleTableDescription,Practise multiple multiplication tables,Mehrere Einmaleins Reihen üben. -shortcutTenTitle,Tables 1 to 10,Zahlenreihen 1 bis 10 -shortcutTenDescription,Practise all tables from 1 to 10.,Die Zahlenreihen 1 bis 10 jeweils von 1 bis 10 üben. -practiseTitle,Practise,Üben -statisticsTitle,Statistics,Statistiken -settingsTitle,Settings,Einstellungen -statisticsErrorMessage,Statistics will be a feature in a future version.,Statistiken sind für eine spätere Version geplant. -multiplicationTableTable,Table,Zahlenreihe -multiplicationTableRangeA,Start,Start -multiplicationTableRangeB,End,Ende -multiplicationTableRandomize,Randomize Order,Zufällige Reihenfolge -multiplicationTableErrorHeadline,Hold on…,Moment mal… -multiplicationTableErrorFillInAll,You have to fill in all 3 fields!,Du musst alle 3 Felder ausfüllen! -inProgressProgressDisplay,Task {current} / {total},Aufgabe {current} / {total} -resultsHeadline,Results,Ergebnisse -resultsTimeDisplay,Time: {duration},Zeit: {duration} -multiplicationTableAddTable,Add table,Zahlenreihe hinzufügen -multiplicationTableErrorNotEnoughInformation,You did not give all required information!,Du hast nicht alle nötigen Informationen eingegeben! -multiplicationTableHeadline,Multiplication Tables,Einmaleins Reihen -multiplicationTableErrorDidNotInputNumber,You did not input a number!,Du hast keine Zahl angegeben! -multiplicationTableErrorAlreadyAddedTable,You already added that table!,Diese Reihe hast du schon hinzugefügt! -inProgressErrorDidNotAnswer,You did not give an answer!,Du hast keine Antwort gegeben! -inProgressConfirmQuitMessage,Do you really want to quit?,Möchtest du wirklich abbrechen? -resultsHeaderMessage,Well done! See your results below.,Gut gemacht! Die Ergebnisse siehst du unten. -resultsTotalCorrectMessage,Correct answers: {totalCorrect} of {total} ({percentage}%},Korrekte Antworten: {totalCorrect} von {total} ({percentage}%) -resultsTotalWrongMessage,Wrong answers: {totalWrong} of {total} ({percentage}%},Falsche Antworten: {totalWrong} von {total} ({percentage}%) -dialogYes,Yes,Ja -dialogNo,No,Nein -settingsDeleteStatisticsDataTitle,Delete statistics data,Statistiken löschen -settingsDeleteStatisticsDataConfirmationMessage,Do you really want to delete all your statistics data? This cannot be undone.,Möchtest du wirklich deine Statistiken löschen? Dieser Schritt kann nicht rückgängig gemacht werden. diff --git a/LocalizationTable.xlsx b/LocalizationTable.xlsx deleted file mode 100644 index c9804ad..0000000 Binary files a/LocalizationTable.xlsx and /dev/null differ diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..149fa8b --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,15 @@ +# Privacy Policy + +By using this app (MultiplyMe), you agree with the privacy policy as described below in this document. + +## Collected data +I do not collect, store or share any data through this app, be it personal data or not. Any and all data generated by the app stays on your phone and is not collected on any server, nor is it shared with third parties. The app also does not use any third party packages or extensions that collect any data, nor does it contain any ads. + +## E-Mail +If you contact me via E-Mail, your name and E-Mail address is only used to reply to your specific inquiry. They will not be used for any marketing purposes. + +## Contact +If you have any questions, suggestions or other remarks about this privacy policy or the app in general, you can contact me via the E-Mail address janneckf03@gmail.com. + +--- +##### Last updated: 10.03.2023 \ No newline at end of file diff --git a/README.md b/README.md index 4ae3083..2762593 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # MultiplyMe -A math practise app, originally developed to practise specifically multiplication tables. \ No newline at end of file +A math practise app mainly developed for practising multiplication tables. This was a private project of mine from december 2021/january 2022 that I turned into a public repo and released on google play. It is also one of my first flutter projects and has (as of march 2023) not the most optimized codebase. Feel free to create a fork and expand the project to your own liking. + +## Imprint +Since it is required by german law to create an imprint for any website or app, this app contains an entry for that in the settings. To avoid checking in personal data in a public repository, the app loads a markdown file from `assets/imprint.md` that is excluded through the `.gitignore`. You have to add an `imprint.md` file with at least one character, otherwise the app will not compile. \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index ad1e5f1..15a4f54 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,6 +24,12 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + android { compileSdkVersion flutter.compileSdkVersion @@ -34,18 +40,31 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.AnnoyingStudio.multiply_me" - minSdkVersion flutter.minSdkVersion + applicationId "com.JanneckFranke.multiply_me" + minSdkVersion 26 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig signingConfigs.release } } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1dc5fd2..637cc9a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + Imprint in the app. The file is not included in the git repository, to avoid checking in personal data. The `assets/imprint.md` file **has to** be here though, otherwise the app won't build! \ No newline at end of file diff --git a/lib/classes/analytics_math_session.dart b/lib/classes/analytics_math_session.dart index 784d868..687b47f 100644 --- a/lib/classes/analytics_math_session.dart +++ b/lib/classes/analytics_math_session.dart @@ -1,7 +1,4 @@ -import 'dart:developer'; - import 'package:multiply_me/classes/analytics_math_task.dart'; -import 'package:multiply_me/classes/math_task.dart'; class MathSession { late DateTime sessionDate; diff --git a/lib/classes/analytics_math_task.dart b/lib/classes/analytics_math_task.dart index 65a1a4a..cc04d57 100644 --- a/lib/classes/analytics_math_task.dart +++ b/lib/classes/analytics_math_task.dart @@ -1,6 +1,4 @@ -import 'package:flutter/services.dart'; import '../helpers/duration_converter.dart'; -import "package:duration/duration.dart"; class AnalyticsMathTask { late double firstFigure; diff --git a/lib/components/detail_analytics_screen.dart b/lib/components/detail_analytics_screen.dart index 4fc9ade..dbe0a69 100644 --- a/lib/components/detail_analytics_screen.dart +++ b/lib/components/detail_analytics_screen.dart @@ -14,7 +14,7 @@ class DetailAnalyticsScreen extends StatefulWidget { class _DetailAnalyticsScreenState extends State { List analyticsList = []; - Duration totalDuration = Duration(); + Duration totalDuration = const Duration(); void finishSession() { Navigator.of(context).pop(); diff --git a/lib/components/finished_screen.dart b/lib/components/finished_screen.dart index 8a3d65e..115b09c 100644 --- a/lib/components/finished_screen.dart +++ b/lib/components/finished_screen.dart @@ -1,17 +1,11 @@ -import 'dart:developer'; -import "dart:convert"; -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:multiply_me/classes/analytics_data.dart'; import 'package:multiply_me/classes/analytics_math_session.dart'; import 'package:multiply_me/classes/analytics_math_task.dart'; -import "../helpers/json_utils.dart"; import 'package:multiply_me/classes/math_result.dart'; import 'package:multiply_me/helpers/save_file_loader.dart'; import 'package:multiply_me/main.dart'; import "package:flutter_gen/gen_l10n/app_localizations.dart"; -import 'package:path_provider/path_provider.dart'; class FinishedScreen extends StatefulWidget { const FinishedScreen({Key? key, required this.resultList}) : super(key: key); diff --git a/lib/components/practiseScreens/multiple_multiplication_tables.dart b/lib/components/practiseScreens/multiple_multiplication_tables.dart index 18c7b44..ddd2213 100644 --- a/lib/components/practiseScreens/multiple_multiplication_tables.dart +++ b/lib/components/practiseScreens/multiple_multiplication_tables.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:multiply_me/classes/math_task.dart'; @@ -5,6 +7,8 @@ import 'package:multiply_me/components/in_progress_screen.dart'; import "package:flutter_gen/gen_l10n/app_localizations.dart"; +import '../../helpers/dialog_helper.dart'; + class MultipleMultiplicationTables extends StatefulWidget { const MultipleMultiplicationTables({Key? key}) : super(key: key); @@ -35,52 +39,52 @@ class _MultipleMultiplicationTablesState void startPractise(BuildContext context) { var localization = Localizations.of(context, AppLocalizations); - if (rangeANumberController.text != "" && - rangeBNumberController.text != "" && - tables.isNotEmpty) { - double rangeA = double.parse(rangeANumberController.text); - double rangeB = double.parse(rangeBNumberController.text); - List taskItems = []; + double rangeA = -1; + double rangeB = -1; - for (double table in tables) { - for (double i = rangeA; i <= rangeB; i++) { - taskItems.add(MathTask(i, table, "×")); - } - } + if (rangeANumberController.text == "" || + rangeBNumberController.text == "" || + tables.isEmpty) { + DialogHelper.showInfoDialog( + context, + localization!.multiplicationTableErrorHeadline, + localization.multiplicationTableErrorFillInAll, + ); + return; + } - if (randomizeValue) { - taskItems.shuffle(); - } + rangeA = double.parse(rangeANumberController.text); + rangeB = double.parse(rangeBNumberController.text); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => InProgressScreen( - taskList: taskItems, - ), - ), + if (rangeA > rangeB) { + DialogHelper.showInfoDialog( + context, + localization!.multiplicationTableErrorHeadline, + localization.multiplicationTableErrorRangeAGreater, ); - } else { - showAlertBox(localization!.multiplicationTableErrorHeadline, - localization.multiplicationTableErrorNotEnoughInformation); + return; } - } - /// Shows a simple alert with an "okay" button (no yes/no options, just for warnings!) - void showAlertBox(String title, String content) { - showDialog( - context: context, - builder: (BuildContext ctx) { - return AlertDialog( - title: Text(title), - content: Text(content), - actions: [ - TextButton( - onPressed: () => {Navigator.pop(context)}, - child: const Text("Okay")) - ], - ); - }); + List taskItems = []; + + for (double table in tables) { + for (double i = rangeA; i <= rangeB; i++) { + taskItems.add(MathTask(i, table, "×")); + } + } + + if (randomizeValue) { + taskItems.shuffle(); + } + + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => InProgressScreen( + taskList: taskItems, + ), + ), + ); } /// Returns a list of cards for each element in the input @@ -161,13 +165,15 @@ class _MultipleMultiplicationTablesState color: Colors.white, onPressed: () { if (tableInputController.text == "") { - showAlertBox( + DialogHelper.showInfoDialog( + context, localization.multiplicationTableErrorHeadline, localization .multiplicationTableErrorDidNotInputNumber); } else if (tables.contains( double.parse(tableInputController.text))) { - showAlertBox( + DialogHelper.showInfoDialog( + context, localization.multiplicationTableErrorHeadline, localization .multiplicationTableErrorAlreadyAddedTable); diff --git a/lib/components/practiseScreens/multiplication_tables.dart b/lib/components/practiseScreens/multiplication_tables.dart index 02b691e..2d4d16a 100644 --- a/lib/components/practiseScreens/multiplication_tables.dart +++ b/lib/components/practiseScreens/multiplication_tables.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:multiply_me/classes/math_task.dart'; import 'package:multiply_me/components/in_progress_screen.dart'; import "package:flutter_gen/gen_l10n/app_localizations.dart"; +import 'package:multiply_me/helpers/dialog_helper.dart'; class MultiplicationTablesScreen extends StatefulWidget { const MultiplicationTablesScreen({Key? key}) : super(key: key); @@ -22,48 +23,56 @@ class _MultiplicationTablesScreenState /// Start a practise round void startPractise() { + var localization = + Localizations.of(context, AppLocalizations); + // Parse Numbers - if (baseNumberController.text != "" && - rangeANumberController.text != "" && - rangeBNumberController.text != "") { - double base = double.parse(baseNumberController.text); - double rangeA = double.parse(rangeANumberController.text); - double rangeB = double.parse(rangeBNumberController.text); - List taskItems = []; + double base = -1; + double rangeA = -1; + double rangeB = -1; - // Generate Tasks - for (double i = rangeA; i <= rangeB; i++) { - taskItems.add(MathTask(i, base, "×")); - } + if (baseNumberController.text == "" || + rangeANumberController.text == "" || + rangeBNumberController.text == "") { + DialogHelper.showInfoDialog( + context, + localization!.multiplicationTableErrorHeadline, + localization.multiplicationTableErrorFillInAll, + ); + return; + } - if (randomizeValue) { - taskItems.shuffle(); - } + base = double.parse(baseNumberController.text); + rangeA = double.parse(rangeANumberController.text); + rangeB = double.parse(rangeBNumberController.text); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => InProgressScreen( - taskList: taskItems, - ), - ), + if (rangeA > rangeB) { + DialogHelper.showInfoDialog( + context, + localization!.multiplicationTableErrorHeadline, + localization.multiplicationTableErrorRangeAGreater, ); - } else { - showDialog( - context: context, - builder: (BuildContext ctx) { - var localization = - Localizations.of(context, AppLocalizations); - return AlertDialog( - title: Text(localization!.multiplicationTableErrorHeadline), - content: Text(localization.multiplicationTableErrorFillInAll), - actions: [ - TextButton( - onPressed: () => {Navigator.pop(context)}, - child: const Text("Okay")) - ], - ); - }); + return; + } + + List taskItems = []; + + // Generate Tasks + for (double i = rangeA; i <= rangeB; i++) { + taskItems.add(MathTask(i, base, "×")); + } + + if (randomizeValue) { + taskItems.shuffle(); } + + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => InProgressScreen( + taskList: taskItems, + ), + ), + ); } @override diff --git a/lib/components/settings_screen.dart b/lib/components/settings_screen.dart index c412392..981ce7f 100644 --- a/lib/components/settings_screen.dart +++ b/lib/components/settings_screen.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import "package:flutter_gen/gen_l10n/app_localizations.dart"; +import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "../helpers/json_utils.dart"; +import '../helpers/url_helper.dart'; +import "../helpers/imprint_dialog.dart"; class SettingsScreen extends StatefulWidget { const SettingsScreen({Key? key}) : super(key: key); @@ -12,7 +15,6 @@ class SettingsScreen extends StatefulWidget { class _SettingsScreenState extends State { void showConfirmationDialog( String title, String content, String answerYes, String answerNo) { - int count = 0; showDialog( context: context, builder: (BuildContext ctx) { @@ -36,6 +38,22 @@ class _SettingsScreenState extends State { }); } + void showAlertBox(String title, String content) { + showDialog( + context: context, + builder: (BuildContext ctx) { + return AlertDialog( + title: Text(title), + content: Text(content), + actions: [ + TextButton( + onPressed: () => {Navigator.pop(context)}, + child: const Text("Okay")) + ], + ); + }); + } + @override Widget build(BuildContext context) { var localization = @@ -57,24 +75,79 @@ class _SettingsScreenState extends State { localization.dialogNo) }, ), + const Divider(), + ListTile( + leading: const Icon(Icons.description_outlined), + title: Text(localization.settingsViewImprintTitle), + onTap: () => ImprintDialog.buildImprint(context), + ), ListTile( leading: const Icon(Icons.info_outline), - title: const Text("About"), + title: Text(localization.settingsAboutViewTitle), onTap: () => { showAboutDialog( context: context, applicationName: "MultiplyMe Beta", - applicationLegalese: - "© 2022-2023 Annoying Studio\nDeveloped by Janneck", - applicationVersion: "1.0.0") + applicationLegalese: localization.settingsAboutViewLegalese, + applicationVersion: "1.0.0", + children: [ + const SizedBox( + height: 15, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.code_sharp), + title: Text(localization.settingsAboutViewSourceCode), + onTap: () async { + String url = + "https://github.com/ntdoJanneck/multiply_me/"; + bool openResult = + await UrlHelper.loadUrl(Uri.parse(url)); + if (!openResult) { + showAlertBox( + localization.settingsAboutViewErrorUriHeadline, + localization + .settingsAboutViewErrorUriContent(url)); + } + }, + ), + ListTile( + leading: const Icon(Icons.shield_outlined), + title: + Text(localization.settingsAboutViewPrivacyPolicy), + onTap: () async { + String url = + "https://github.com/ntdoJanneck/multiply_me/blob/main/PRIVACY.md"; + bool openResult = + await UrlHelper.loadUrl(Uri.parse(url)); + if (!openResult) { + showAlertBox( + localization.settingsAboutViewErrorUriHeadline, + localization + .settingsAboutViewErrorUriContent(url)); + } + }, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.font_download_outlined), + title: Text(localization.settingsAboutViewFontLicense), + onTap: () async { + String url = + "https://github.com/googlefonts/roboto/blob/main/LICENSE"; + bool openResult = + await UrlHelper.loadUrl(Uri.parse(url)); + if (!openResult) { + showAlertBox( + localization.settingsAboutViewErrorUriHeadline, + localization + .settingsAboutViewErrorUriContent(url)); + } + }, + ) + ]) }, ), - const Divider( - height: 10, - ), - const SizedBox( - height: 20, - ), ], ), ), diff --git a/lib/components/statistics_screen.dart b/lib/components/statistics_screen.dart index 5d0472e..232fcb0 100644 --- a/lib/components/statistics_screen.dart +++ b/lib/components/statistics_screen.dart @@ -1,12 +1,8 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; -import "package:flutter_gen/gen_l10n/app_localizations.dart"; import 'package:intl/intl.dart'; import 'package:multiply_me/classes/analytics_data.dart'; import 'package:multiply_me/classes/analytics_math_session.dart'; import 'package:multiply_me/components/detail_analytics_screen.dart'; -import 'package:multiply_me/components/finished_screen.dart'; import 'package:multiply_me/helpers/save_file_loader.dart'; class StatisticsScreen extends StatefulWidget { @@ -69,9 +65,6 @@ class _StatisticsScreenState extends State { @override Widget build(BuildContext context) { - var localization = - Localizations.of(context, AppLocalizations); - return Scaffold( body: Form( child: SingleChildScrollView( diff --git a/lib/helpers/dialog_helper.dart b/lib/helpers/dialog_helper.dart new file mode 100644 index 0000000..18ada05 --- /dev/null +++ b/lib/helpers/dialog_helper.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class DialogHelper { + /// Shows a simple alert with an "okay" button (no yes/no options, just for info!) + static void showInfoDialog(BuildContext context, String title, String content, + {String confirmText = "Okay"}) { + showDialog( + context: context, + builder: (BuildContext ctx) { + return AlertDialog( + title: Text(title), + content: Text(content), + actions: [ + TextButton( + onPressed: () => {Navigator.pop(context)}, + child: Text(confirmText)) + ], + ); + }, + ); + } +} diff --git a/lib/helpers/duration_converter.dart b/lib/helpers/duration_converter.dart index eca3412..3d6d665 100644 --- a/lib/helpers/duration_converter.dart +++ b/lib/helpers/duration_converter.dart @@ -1,7 +1,5 @@ library duration_converter; -import 'dart:developer'; - class DurationConverter { /// Converts a timestamp with the format HH:MM:ss.mmmmmm to a duration. static Duration toDuration(String isoString) { diff --git a/lib/helpers/imprint_dialog.dart b/lib/helpers/imprint_dialog.dart new file mode 100644 index 0000000..8acac8c --- /dev/null +++ b/lib/helpers/imprint_dialog.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; +import "package:flutter/services.dart"; +import "package:flutter_markdown/flutter_markdown.dart"; + +class ImprintDialog { + static Future buildImprint(BuildContext context) async { + var localization = + Localizations.of(context, AppLocalizations); + String imprintText = await ImprintDialog.getImprintText(); + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(localization!.settingsViewImprintTitle), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + MarkdownBody( + data: imprintText, + shrinkWrap: true, + ) + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(localization.settingsImprintClose)) + ], + ); + }, + ); + } + + static Future getImprintText() async { + try { + return await rootBundle.loadString("assets/imprint.md"); + } catch (e) { + return "No imprint!"; + } + } +} diff --git a/lib/helpers/url_helper.dart b/lib/helpers/url_helper.dart new file mode 100644 index 0000000..856e336 --- /dev/null +++ b/lib/helpers/url_helper.dart @@ -0,0 +1,12 @@ +import 'package:url_launcher/url_launcher.dart'; + +class UrlHelper { + static Future loadUrl(Uri url) async { + try { + await launchUrl(url); + return true; + } catch (e) { + return false; + } + } +} diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index dbabab5..ad6ebb6 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -23,6 +23,7 @@ "resultsTimeDisplay":"Zeit: {duration}", "multiplicationTableAddTable":"Zahlenreihe hinzufügen", "multiplicationTableErrorNotEnoughInformation":"Du hast nicht alle nötigen Informationen eingegeben!", + "multiplicationTableErrorRangeAGreater": "Die Start-Zahl muss kleiner sein als die End-Zahl!", "multiplicationTableHeadline":"Einmaleins Reihen", "multiplicationTableErrorDidNotInputNumber": "Du hast keine Zahl angegeben!", "multiplicationTableErrorAlreadyAddedTable":"Diese Reihe hast du schon hinzugefügt!", @@ -34,5 +35,22 @@ "dialogYes":"Ja", "dialogNo":"Nein", "settingsDeleteStatisticsDataTitle": "Statistiken löschen", - "settingsDeleteStatisticsDataConfirmationMessage": "Möchtest du wirklich deine Statistiken löschen? Dieser Schritt kann nicht rückgängig gemacht werden." + "settingsDeleteStatisticsDataConfirmationMessage": "Möchtest du wirklich deine Statistiken löschen? Dieser Schritt kann nicht rückgängig gemacht werden.", + "settingsAboutViewSourceCode": "Quellcode auf GitHub ansehen", + "settingsAboutViewPrivacyPolicy": "Datenschutzerklärung ansehen", + "settingsAboutViewLegalese": "© 2023 Janneck Franke, lizensiert unter der MIT-Lizenz.", + "settingsAboutViewErrorUriContent": "Der URL ({url}) konnte nicht geöffnet werden.", + "@settingsAboutViewErrorUriContent": { + "placeholders": { + "url": { + "type": "String", + "example": "unbekannt" + } + } + }, + "settingsAboutViewErrorUriHeadline": "Fehler beim öffnen", + "settingsAboutViewFontLicense": "Schriftart (Roboto) Lizenz", + "settingsViewImprintTitle": "Impressum", + "settingsAboutViewTitle": "Über diese App", + "settingsImprintClose": "SCHLIEßEN" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9c838d1..fbc42f1 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,5 +1,5 @@ { - "appTitle": "QuickMaths Beta", + "appTitle": "MultiplyMe", "helloWorld": "Hello World!", "creditsMessage": "Developed by Janneck", "singleTableTitle": "Single Multiplication Table", @@ -13,11 +13,12 @@ "settingsTitle": "Settings", "statisticsErrorMessage": "Statistics will be a feature in a future version.", "multiplicationTableTable": "Table", - "multiplicationTableRangeA": "Range A", - "multiplicationTableRangeB": "Range B", + "multiplicationTableRangeA": "Range start", + "multiplicationTableRangeB": "Range end", "multiplicationTableRandomize": "Randomize Order", "multiplicationTableErrorHeadline": "Hold on…", "multiplicationTableErrorFillInAll": "You have to fill in all 3 fields!", + "multiplicationTableErrorRangeAGreater": "The range start has to be smaller than the range end!", "inProgressProgressDisplay": "Task {current} of {total}", "@inProgressProgressDisplay": { "placeholders": { @@ -59,5 +60,22 @@ "dialogYes": "Yes", "dialogNo": "No", "settingsDeleteStatisticsDataTitle": "Delete statistics data", - "settingsDeleteStatisticsDataConfirmationMessage": "Do you really want to delete all your statistics data? This cannot be undone." + "settingsDeleteStatisticsDataConfirmationMessage": "Do you really want to delete all your statistics data? This cannot be undone.", + "settingsAboutViewSourceCode": "View source code on GitHub", + "settingsAboutViewPrivacyPolicy": "View privacy policy", + "settingsAboutViewLegalese": "© 2023 Janneck Franke, licensed under the MIT-license.", + "settingsAboutViewErrorUriContent": "Couldn't open the URL ({url}).", + "@settingsAboutViewErrorUriContent": { + "placeholders": { + "url": { + "type": "String", + "example": "unknown" + } + } + }, + "settingsAboutViewErrorUriHeadline": "Opening URL failed", + "settingsAboutViewFontLicense": "Font (Roboto) license", + "settingsViewImprintTitle": "Imprint", + "settingsAboutViewTitle": "About", + "settingsImprintClose": "CLOSE" } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 56cd2a8..921edda 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" async: dependency: transitive description: @@ -107,11 +115,32 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_markdown: + dependency: "direct main" + description: + name: flutter_markdown + sha256: "7b25c10de1fea883f3c4f9b8389506b54053cd00807beab69fd65c8653a2711f" + url: "https://pub.dev" + source: hosted + version: "0.6.14" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "959ef4add147753f990b4a7c6cccb746d5792dbdc81b1cde99e62e7edb31b206" + url: "https://pub.dev" + source: hosted + version: "10.4.0" google_fonts: dependency: "direct main" description: @@ -160,6 +189,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + markdown: + dependency: transitive + description: + name: markdown + sha256: b3c60dee8c2af50ad0e6e90cceba98e47718a6ee0a7a6772c77846a0cc21f78b + url: "https://pub.dev" + source: hosted + version: "7.0.1" matcher: dependency: transitive description: @@ -325,6 +362,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" + url: "https://pub.dev" + source: hosted + version: "6.1.10" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "1f4d9ebe86f333c15d318f81dcdc08b01d45da44af74552608455ebdc08d9732" + url: "https://pub.dev" + source: hosted + version: "6.0.24" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: c9cd648d2f7ab56968e049d4e9116f96a85517f1dd806b96a86ea1018a3a82e5 + url: "https://pub.dev" + source: hosted + version: "6.1.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: e29039160ab3730e42f3d811dc2a6d5f2864b90a70fb765ea60144b03307f682 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "2dddb3291a57b074dade66b5e07e64401dd2487caefd4e9e2f467138d8c7eb06" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "574cfbe2390666003c3a1d129bdc4574aaa6728f0c00a4829a81c316de69dd9b" + url: "https://pub.dev" + source: hosted + version: "2.0.15" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "97c9067950a0d09cbd93e2e3f0383d1403989362b97102fbf446473a48079a4b" + url: "https://pub.dev" + source: hosted + version: "3.0.4" vector_math: dependency: transitive description: @@ -351,4 +452,4 @@ packages: version: "1.0.0" sdks: dart: ">=2.19.0-345.0.dev <4.0.0" - flutter: ">=3.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index e385d02..c975be0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Test publish_to: 'none' -version: 0.1.0+1 +version: 1.0.0+3 environment: sdk: ">=2.16.0-21.0.dev <3.0.0" @@ -18,6 +18,9 @@ dependencies: path_provider: ^2.0.8 duration: ^3.0.8 google_fonts: ^4.0.3 + font_awesome_flutter: ^10.4.0 + url_launcher: ^6.1.10 + flutter_markdown: ^0.6.14 dev_dependencies: @@ -28,6 +31,8 @@ dev_dependencies: flutter: generate: true uses-material-design: true + assets: + - assets/imprint.md fonts: - family: Roboto fonts: diff --git a/web/manifest.json b/web/manifest.json index 5ee60df..03d6236 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -1,6 +1,6 @@ { - "name": "QuickMaths", - "short_name": "QuickMaths", + "name": "MultiplyMe", + "short_name": "MultiplyMe", "start_url": ".", "display": "standalone", "background_color": "#0175C2", diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..4f78848 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..88b22e5 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 8625868..aa92e12 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"multiply_me", origin, size)) { + if (!window.CreateAndShow(L"MultiplyMe", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20c..33f9dc1 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ