v1.0.0 outsourced from aitrainer_app

This commit is contained in:
Tibor Bossanyi (Freelancer)
2023-01-28 12:53:16 +01:00
commit ca96abf8c0
87 changed files with 7189 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
import 'dart:io';
import 'package:workouttest_util/util/logging.dart';
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AppLanguage with Logging {
static final AppLanguage _singleton = AppLanguage._internal();
Locale? _appLocale = Locale('en');
factory AppLanguage() {
return _singleton;
}
AppLanguage._internal();
static Future<AppLanguage> getInstance() async {
return _singleton;
}
Locale get appLocal => _appLocale ?? Locale("en");
Future<void> fetchLocale() async {
var prefs = await SharedPreferences.getInstance();
String? langCode = prefs.getString('language_code');
log(" ---- lang code $langCode");
if (langCode == null) {
_appLocale = Locale('en');
} else {
_appLocale = Locale(langCode);
}
log(" ---- Fetched lang: " + _appLocale.toString());
}
getLocale(SharedPreferences prefs) {
String? langCode = prefs.getString('language_code');
if (langCode == null) {
final String localName = Platform.localeName;
if (localName.endsWith("HU")) {
_appLocale = Locale('hu');
langCode = "hu";
} else {
_appLocale = Locale('en');
langCode = "en";
}
}
_appLocale = Locale(langCode);
log(" ---- Get lang: " + _appLocale.toString() + " lang code $langCode");
}
void changeLanguage(Locale type) async {
var prefs = await SharedPreferences.getInstance();
if (_appLocale == type) {
return;
}
if (type == Locale("hu")) {
_appLocale = Locale("hu");
await prefs.setString('language_code', 'hu');
await prefs.setString('countryCode', 'HU');
} else {
_appLocale = Locale("en");
await prefs.setString('language_code', 'en');
await prefs.setString('countryCode', 'US');
}
log(" ---- Stored lang: " + _appLocale.toString());
}
}
+78
View File
@@ -0,0 +1,78 @@
import 'dart:convert';
import 'package:workouttest_util/util/logging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
class AppLocalizations with Logging {
late Locale locale;
bool isTest;
late Map<String, String> _localizedStrings;
AppLocalizations(this.locale, {this.isTest = false});
// Helper method to keep the code in the widgets concise
// Localizations are accessed using an InheritedWidget "of" syntax
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
// Static member to have a simple access to the delegate from the MaterialApp
static const LocalizationsDelegate<AppLocalizations> delegate = AppLocalizationsDelegate();
setLocale(Locale locale) {
this.locale = locale;
}
Future<bool> load() async {
// Load the language JSON file from the "lang" folder
log(" -- load language pieces " + locale.languageCode);
String jsonString = await rootBundle.loadString('i18n/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
Future<AppLocalizations> loadTest(Locale locale) async {
return AppLocalizations(locale);
}
// This method will be called from every widget which needs a localized text
String translate(String key) {
if (isTest) return key;
String? translated = _localizedStrings[key];
return translated != null ? translated : key;
}
}
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
final bool isTest;
// This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor.
const AppLocalizationsDelegate({this.isTest = false});
@override
bool isSupported(Locale locale) {
// Include all of your supported language codes here
return ['en', 'hu'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) async {
// AppLocalizations class is where the JSON loading actually runs
AppLocalizations localizations = new AppLocalizations(locale, isTest: this.isTest);
if (isTest) {
await localizations.loadTest(locale);
} else {
await localizations.load();
}
return localizations;
}
@override
bool shouldReload(AppLocalizationsDelegate old) => false;
}
+176
View File
@@ -0,0 +1,176 @@
import 'dart:convert';
import 'package:workouttest_util/util/app_language.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class DateRate {
static String daily = "daily";
static String weekly = "weekly";
static String monthly = "monthly";
static String yearly = "yearly";
}
mixin Common {
final emailError = "Please type a right email address here.";
final passwordError = "The password must have at least 8 characters.";
String toJson(Map<String, String> map) {
String rc = "{";
map.forEach((key, value) {
rc += "'$key':'$value'";
});
rc += "}";
return rc;
}
String getDateLocale(DateTime datetime, bool timeDisplay) {
var date = datetime;
String dateName = DateFormat(DateFormat.YEAR_NUM_MONTH_DAY, AppLanguage().appLocal.toString()).format(date.toUtc());
if (timeDisplay) {
dateName += " " + DateFormat(DateFormat.HOUR_MINUTE, AppLanguage().appLocal.toString()).format(date.toUtc());
}
return dateName;
}
String utf8convert(String text) {
List<int> bytes = text.toString().codeUnits;
return utf8.decode(bytes);
}
double mediaSizeWidth(BuildContext context) {
return MediaQuery.of(context).size.width;
}
/// Calculates week number from a date as per https://en.wikipedia.org/wiki/ISO_week_date#Calculation
int weekNumber(DateTime date) {
int dayOfYear = int.parse(DateFormat("D").format(date));
return ((dayOfYear - date.weekday + 10) / 7).floor();
}
static String? emailValidation(String? email) {
final String error = "Please type an email address";
if (email == null) {
return error;
}
bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(email);
return emailValid ? null : error;
}
static String? passwordValidation(String? value) {
final String error = "Password too short";
if (value == null || value.length == 0) {
return error;
}
bool valid = 8 < value.length;
return valid ? null : error;
}
static normalizeDecimal(String value) {
if (value.isEmpty) {
return 0;
}
value = value.replaceFirst(",", ".");
value = value.replaceAll(RegExp(r'[^0-9.]'), "");
return value;
}
double calculate1RM(double weight, double repeat) {
if (weight == 0 || repeat == 0) {
return 0;
}
double rmWendler = weight * repeat * 0.0333 + weight;
double rmOconner = weight * (1 + repeat / 40);
//print("Weight: $weight repeat: $repeat, $rmWendler, Oconner: $rmOconner");
double average = (rmWendler + rmOconner) / 2;
return average;
}
double getRepeatByOneRepMax(double oneRepMax, double weight) {
double repeats = ( 80 * oneRepMax - 80 * weight ) / ( weight * ( 40 * 0.0333 + 1) );
//print("getRepeatsByOneRepMax: $repeats");
return repeats;
}
int calculateRepeatBy1RMPercent(double oneRepMax, double weight, double percent) {
return getRepeatByOneRepMax(oneRepMax , weight * percent).round();
}
double calculate1RMPercentByRepeat(double oneRepMax, double weight, double repeat) {
double percent = 80 * oneRepMax / (weight * ( repeat * ( 40 * 0.0333 + 1 ) + 80));
return percent;
}
static double get1RMPercent(int repeats) {
double percent = 1;
if (repeats >= 35) {
percent = 0.50;
} else if (repeats > 12) {
percent = (100 - 2 * repeats) / 100;
} else {
percent = (100.0 - 2 * repeats) / 100;
}
//print("1RM Percent: $percent repeats: $repeats");
return percent;
}
static double roundWeight(double weight) {
double rounded = weight.round().toDouble();
if (weight > 35) {
final double remainder = weight % 5;
if (remainder < 1) {
rounded = ((weight / 5).floor() * 5).toDouble();
} else if (remainder > 1 && remainder <= 2.5) {
rounded = (weight / 5).floor() * 5 + 2.5;
} else if (remainder > 2.5 && remainder < 3.25) {
rounded = (weight / 5).floor() * 5 + 2.5;
} else {
rounded = (((weight / 5).ceil() * 5)).toDouble();
}
}
return rounded;
}
static int calculateQuantityByChangedWeight(double initialRM, double weight, double repeat) {
final double repeatWendler = (initialRM - weight) / 0.0333 / weight;
final double repeatOconner = (initialRM / weight - 1) * 40;
final newRepeat = ((repeatOconner + repeatWendler) / 2).ceil();
print("Initial 1RM: $initialRM Weight: $weight repeatWendler: $repeatWendler repeat Oconner: $repeatOconner. NEW REPEAT: $newRepeat");
return newRepeat;
}
static int reCalculateRepeatsByChangedWeight(double weight, double repeat, double changedWeight) {
final double rmWendler = weight * repeat * 0.0333 + weight;
final double rmOconner = weight * (1 + repeat / 40);
final double repeatWendler = (rmWendler - changedWeight) / 0.0333 / changedWeight;
final double repeatOconner = (rmOconner / changedWeight - 1) * 40;
final newRepeat = ((repeatOconner + repeatWendler) / 2).ceil();
print("Weight: $weight changedWeight: $changedWeight repeatWendler: $repeatWendler repeat Oconner: $repeatOconner. NEW REPEAT: $newRepeat");
return newRepeat;
}
static double calculateWeigthByChangedQuantity(double weight, double repeat, double changedRepeats) {
final double rmWendler = weight * repeat * 0.0333 + weight;
final double rmOconner = weight * (1 + repeat / 40);
final double initialRM = (rmWendler + rmOconner) / 2;
final double weightWendler = rmWendler / (changedRepeats * 0.0333 + 1);
final double weightOconner = rmOconner / (1 + changedRepeats / 40);
final double newWeight = ((weightWendler + weightOconner) / 2);
print(
"Initial 1RM: $initialRM repeat: $repeat changedRepeat: $changedRepeats Weight: $weight weightWendler: $weightWendler weight Oconner: $weightOconner. NEW WEIGHT: $newWeight");
return newWeight;
}
}
class CommonHoldingClass with Common {}
+129
View File
@@ -0,0 +1,129 @@
import 'package:workouttest_util/util/string_extension.dart';
enum LoginType { email, fb, google, apple }
extension LoginTypeExt on LoginType {
bool equalsTo(LoginType type) => this.toString() == type.toString();
bool equalsStringTo(String type) => this.toString() == type;
}
enum TrackingEvent {
enter,
login,
logout,
registration,
login_skip,
home,
sizes,
sizes_save,
my_development,
my_exerciseplan,
account,
settings,
sales_page,
purchase_request,
purchase_successful,
exercise_new,
exercise_new_no_registration,
exercise_new_paralell,
result,
exercise_log,
exercise_log_open,
exercise_log_delete,
exercise_log_result,
my_body_development,
my_muscle_development,
my_size_development,
my_custom_exercise_plan,
my_custom_exercise_plan_save,
my_exercise_plan_execute_open,
my_exercise_plan_execute_save,
my_special_plan,
my_suggested_plan,
prediction,
search,
exercise_device,
customer_change,
settings_lang,
settings_server,
test_set_edit,
test_set_new,
tutorial_step,
tutorial_finished,
tutorial_activate,
terms_of_use,
data_privacy,
delete_account,
faq,
training_plan_open,
training_plan_start,
training_plan_execute,
training_plan_finished,
training_plan_custom,
trial,
feedback_email,
}
T enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhere((type) => type.toString().split(".").last == value);
}
extension TrackingEventExt on TrackingEvent {
String enumToString() => this.toString().split(".").last;
bool equalsTo(TrackingEvent event) => this.toString() == event.toString();
bool equalsStringTo(String event) => this.toString() == event;
}
enum PropertyEnum { Ectomorph, Mesomorph, Endomorph }
extension PropertyExt on PropertyEnum {
String toStr() => this.toString().split(".").last;
bool equalsTo(PropertyEnum event) => this.toString() == event.toString();
bool equalsStringTo(String event) => this.toString() == event;
}
enum SizesEnum { Weight, Height, Shoulder, Neck, Biceps, Chest, Belly, Hip, ThighTop, ThighMiddle, Knee, Calf, Ankle, Underarm, Lowerarm }
extension SizesExt on SizesEnum {
String toStr() => this.toString().split(".").last;
bool equalsTo(SizesEnum event) => this.toString() == event.toString();
bool equalsStringTo(String event) => this.toString() == event;
}
enum EvaluationText { very_poor, poor, fair, below_average, average, above_average, good, excellent, elite }
extension EvaluationTextExt on EvaluationText {
String toStr() => this.toString().split(".").last;
bool equalsTo(EvaluationText eval) => this.toString() == eval.toString();
bool equalsStringTo(String eval) => this.toStr() == eval;
}
enum ExerciseTypeTrainingPlanState { none, added, executed }
extension ExerciseTypeTrainingPlanStateExt on ExerciseTypeTrainingPlanState {
String toStr() => this.toString().split(".").last;
bool equalsTo(ExerciseTypeTrainingPlanState state) => this.toString() == state.toString();
bool equalsStringTo(String state) => this.toStr() == state;
}
enum ExerciseSaveType { test, training, test_set }
extension ExerciseSaveTypeExt on ExerciseSaveType {
String toStr() => this.toString().split(".").last;
bool equalsTo(ExerciseSaveType type) => this.toString() == type.toString();
bool equalsStringTo(String type) => this.toStr() == type;
}
enum MuscleGroup { chest, biceps, triceps, back, shoulders, core, thigh, calf }
extension MuscleGroupExt on MuscleGroup {
String toStr() => this.toString().split(".").last;
String toText() => this.toString().split(".").last.capitalize();
bool equalsTo(MuscleGroup type) => this.toString() == type.toString();
bool equalsStringTo(String type) => this.toStr() == type;
String getStrByIndex(int index) => MuscleGroup.values.elementAt(index).toStr();
MuscleGroup getByIndex(int index) => MuscleGroup.values.elementAt(index);
String getTextByIndex(int index) => MuscleGroup.values.elementAt(index).toText();
}
+3
View File
@@ -0,0 +1,3 @@
class EnvironmentConfig {
static const test_env = String.fromEnvironment('test_env');
}
+16
View File
@@ -0,0 +1,16 @@
import 'package:workouttest_util/util/env.dart';
import 'package:intl/intl.dart';
mixin Logging {
void log(String message) {
DateTime time = DateTime.now();
print(DateFormat('yyyy-MM-dd HH:mm:ss ').format(time) + message);
}
void trace(String message) {
String testEnv = EnvironmentConfig.test_env;
if (testEnv == "1") {
log(message);
}
}
}
+13
View File
@@ -0,0 +1,13 @@
class NotFoundException implements Exception {
final String message;
const NotFoundException({required this.message});
}
class WorkoutTestException implements Exception {
static const String CUSTOMER_EXISTS = "customer-exists";
static const String LOGIN_CANCELLED = "login-cancelled";
final String message;
final String code;
const WorkoutTestException({required this.message, required this.code});
}
+5
View File
@@ -0,0 +1,5 @@
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${this.substring(1).toLowerCase()}";
}
}
+53
View File
@@ -0,0 +1,53 @@
import 'package:workouttest_util/model/cache.dart';
import 'package:workouttest_util/util/logging.dart';
import 'package:workouttest_util/service/tracking_service.dart';
import 'package:workouttest_util/util/enums.dart';
import 'package:workouttest_util/model/tracking.dart' as model;
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:posthog_session/posthog_session.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:flutter/foundation.dart';
class Track with Logging {
static final Track _singleton = Track._internal();
static FirebaseAnalytics analytics = FirebaseAnalytics.instance;
factory Track() {
return _singleton;
}
Track._internal();
void track(TrackingEvent event, {String eventValue = ""}) {
model.Tracking tracking = model.Tracking();
tracking.customerId = Cache().userLoggedIn == null ? 0 : Cache().userLoggedIn!.customerId!;
if (kReleaseMode) {
tracking.event = event.enumToString();
if (eventValue.isNotEmpty) {
tracking.eventValue = eventValue;
}
tracking.dateAdd = DateTime.now();
TrackingApi().saveTracking(tracking);
FirebaseMessaging.instance.subscribeToTopic(event.enumToString());
analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue});
if (eventValue.isNotEmpty) {
MatomoTracker.instance.trackEvent(eventCategory: "wt", action: event.enumToString(), eventName: eventValue);
} else {
MatomoTracker.instance.trackEvent(eventCategory: "wt", action: event.enumToString());
}
Posthog().capture(
eventName: event.enumToString(),
properties: {
'eventValue': eventValue,
'customer': tracking.customerId,
},
);
}
}
}