import 'dart:collection'; import 'dart:convert'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/evaluation.dart'; import 'package:aitrainer_app/model/exercise_plan.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/model/exercise_plan_template.dart'; import 'package:aitrainer_app/model/exercise_tree.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/model_change.dart'; import 'package:aitrainer_app/model/product.dart' as wt_product; import 'package:aitrainer_app/model/product.dart'; import 'package:aitrainer_app/model/product_test.dart'; import 'package:aitrainer_app/model/property.dart'; import 'package:aitrainer_app/model/purchase.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/service/firebase_api.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/service/package_service.dart'; import 'package:aitrainer_app/main.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/env.dart'; import 'package:aitrainer_app/util/track.dart'; import 'package:flurry/flurry.dart'; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; import 'package:package_info/package_info.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:intl/intl.dart'; import 'package:smartlook/smartlook.dart'; import 'customer_exercise_device.dart'; import 'exercise_device.dart'; enum SharePrefsChange { login, registration, logout, } /* Auth flow of the app 1. During the login screen the authentication will be executed - if not successful: message: Network error, try again later - if successful - get the stored shared preferences and customer id - if customer_id not present -> registration page - if present, check if the expiration_date > 10 days -> login page - else get the API customer by the stored customer_id - After registration / login store the preferences: - AuthToken - customer_id - last_store_date - is_registered - is_logged_in */ class Cache with Logging { static final Cache _singleton = Cache._internal(); // Keys to store and fetch data from SharedPreferences static final String authTokenKey = "auth_token"; static final String customerIdKey = "customer_id"; static final String firebaseUidKey = "firebase_uid"; static final String lastStoreDateKey = "last_date"; static final String isRegisteredKey = "is_registered"; static final String isLoggedInKey = "is_logged_in"; static final String langKey = "lang"; static final String serverKey = "live"; static final String hardwareKey = "hardware"; static final String loginTypeKey = "login_type"; static final String timerDisplayKey = "timer_display"; static final String activeExercisePlanKey = "active_exercise_plan"; static final String activeExercisePlanDateKey = "active_exercise_plan_date"; static final String activeExercisePlanDetailsKey = "active_exercise_details_plan"; static final String exerciseLogSeenKey = "exercise_log_seen"; static final String muscleDevelopmentSeenKey = "muscle_development_seen_key"; static String baseUrl = 'http://aitrainer.info:8888/api/'; static final String mediaUrl = 'https://aitrainer.info:4343/media/'; static final String username = 'bosi'; static final String password = 'andio2009'; String authToken = ""; AccessToken? accessTokenFacebook; Customer? userLoggedIn; String? firebaseUid; LoginType? loginType; PackageInfo? packageInfo; bool hasPurchased = false; bool firstLoad = true; List? _exerciseTypes; List? _exerciseTree; List? _evaluations; List? _exercises; ExercisePlan? _myExercisePlan; List? _properties; List? _products; List _purchases = []; List? _productTests; List _exercisePlanTemplates = []; ExercisePlan? activeExercisePlan; List? activeExercisePlanDetails; List? _devices; List? _customerDevices; LinkedHashMap _myExercisesPlanDetails = LinkedHashMap(); LinkedHashMap _tree = LinkedHashMap(); double _percentExercises = -1; Customer? _trainee; List? _exercisesTrainee; ExercisePlan? _traineeExercisePlan; LinkedHashMap _badges = LinkedHashMap(); List? deviceLanguages; String startPage = "home"; late String testEnvironment; bool liveServer = true; bool? hasHardware = false; bool? isExerciseLogSeen = false; bool? isMuscleDevelopmentSeen = false; factory Cache() { return _singleton; } Cache._internal() { String testEnv = EnvironmentConfig.test_env; this.testEnvironment = testEnv; if (testEnv == "1") { baseUrl = 'http://aitrainer.app:8899/api/'; liveServer = false; } } void setTestBaseUrl() { baseUrl = 'http://aitrainer.app:8899/api/'; } String getAuthToken() { return this.authToken; } Future saveActiveExercisePlan(ExercisePlan exercisePlan, List exercisePlanDetails) async { this.activeExercisePlan = exercisePlan; this.activeExercisePlanDetails = exercisePlanDetails; String exercisePlanJson = JsonEncoder().convert(exercisePlan.toJson()); String detailsJson = jsonEncode(exercisePlanDetails.map((i) => i.toJsonWithExerciseList()).toList()).toString(); Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences; sharedPreferences = await prefs; final DateTime now = DateTime.now(); sharedPreferences.setString(Cache.activeExercisePlanKey, exercisePlanJson); sharedPreferences.setString(Cache.activeExercisePlanDetailsKey, detailsJson); String savingDay = DateFormat("yyyy-MM-dd HH:mm:ss").format(now); sharedPreferences.setString(Cache.activeExercisePlanDateKey, savingDay); } Future deleteActiveExercisePlan() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences; sharedPreferences = await prefs; sharedPreferences.remove(Cache.activeExercisePlanDateKey); this.activeExercisePlan = null; this.activeExercisePlanDetails = null; } Future getActiveExercisePlan() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences; sharedPreferences = await prefs; final savedPlanDateString = sharedPreferences.getString(Cache.activeExercisePlanDateKey); if (savedPlanDateString == null) { return; } DateFormat format = DateFormat("yyyy-MM-dd HH:mm:ss"); DateTime savedPlanDate; savedPlanDate = format.parse(savedPlanDateString); print("Saved plan: $savedPlanDate"); final DateTime now = DateTime.now(); final DateTime added = savedPlanDate.add(Duration(days: 1)); if (added.isBefore(now)) { return; } String? exercisePlanJson = sharedPreferences.getString(Cache.activeExercisePlanKey); if (exercisePlanJson != null) { final Map map = JsonDecoder().convert(exercisePlanJson); this.activeExercisePlan = ExercisePlan.fromJson(map); } String? detailsJson = sharedPreferences.getString(Cache.activeExercisePlanDetailsKey); if (detailsJson != null) { Iterable json = jsonDecode(detailsJson); this.activeExercisePlanDetails = json.map((details) => ExercisePlanDetail.fromJsonWithExerciseList(details)).toList(); } } Future setServer(bool live) async { if (this.testEnvironment == "1") { liveServer = false; live = false; } Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences; sharedPreferences = await prefs; liveServer = live; sharedPreferences.setBool(Cache.serverKey, live); } void getHardware(SharedPreferences prefs) { final bool? hasHardware = prefs.getBool(Cache.hardwareKey); this.hasHardware = hasHardware; if (hasHardware == null) { this.hasHardware = false; } } Future selectedHardwareBefore() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; final bool? selectedHardware = sharedPreferences.getBool(Cache.hardwareKey); return selectedHardware == null; } Future setHardware(bool hasHardware) async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences; sharedPreferences = await prefs; sharedPreferences.setBool(Cache.hardwareKey, hasHardware); this.hasHardware = hasHardware; } void setServerAddress(SharedPreferences prefs) { if (this.testEnvironment == "1") { baseUrl = 'http://aitrainer.app:8899/api/'; print("TestEnv $baseUrl"); return; } final bool? live = prefs.getBool(Cache.serverKey); if (live == null) { baseUrl = 'http://aitrainer.app:8888/api/'; print("Live Env $baseUrl"); liveServer = true; return; } liveServer = live; if (live) { baseUrl = 'http://aitrainer.info:8888/api/'; } else { baseUrl = 'http://aitrainer.app:8899/api/'; } print("Env $baseUrl"); } Future setLoginTypeFromPrefs() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; final String? loginType = sharedPreferences.getString(Cache.loginTypeKey); LoginType type = LoginType.email; if (loginType == LoginType.apple.toString()) { type = LoginType.apple; } else if (loginType == LoginType.google.toString()) { type = LoginType.google; } else if (loginType == LoginType.fb.toString()) { type = LoginType.fb; } else if (loginType == LoginType.email.toString()) { type = LoginType.email; } //print("LoginType: " + loginType == null ? "NULL" : loginType); Cache().setLoginType(type); } static String? getToken(SharedPreferences prefs) { return prefs.getString(authTokenKey); } static String getBaseUrl() { return baseUrl; } static String getMediaUrl() { return mediaUrl; } afterRegistration(Customer customer) async { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; final String uid = Cache().firebaseUid!; SharedPreferences sharedPreferences = await prefs; sharedPreferences.setString(Cache.loginTypeKey, Cache().getLoginType().toString()); await setPreferences(prefs, SharePrefsChange.registration, customer.customerId!, uid); } afterLogin(Customer customer) async { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; SharedPreferences sharedPreferences = await prefs; sharedPreferences.setString(Cache.loginTypeKey, Cache().getLoginType().toString()); await setPreferences(prefs, SharePrefsChange.login, customer.customerId!, Cache().firebaseUid!); } afterFirebaseLogin() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; sharedPreferences.setString(Cache.loginTypeKey, Cache().getLoginType().toString()); await setPreferences(prefs, SharePrefsChange.login, userLoggedIn!.customerId!, Cache().firebaseUid!); } afterFacebookLogin() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; sharedPreferences.setString(Cache.loginTypeKey, Cache().getLoginType().toString()); await setPreferences(prefs, SharePrefsChange.login, userLoggedIn!.customerId!, Cache().firebaseUid!); } logout() async { if (this.accessTokenFacebook != null) { await FirebaseApi().logOutFacebook(); } userLoggedIn = null; firebaseUid = null; authToken = ""; _trainee = null; _percentExercises = -1; _exercisesTrainee = null; _traineeExercisePlan = null; _exercises = []; _myExercisesPlanDetails = LinkedHashMap(); log("Trainees is null? " + (_trainee == null).toString()); Future prefs = SharedPreferences.getInstance(); await setPreferences(prefs, SharePrefsChange.logout, 0, ""); } Future setPreferences(Future prefs, SharePrefsChange type, int customerId, String firebaseUid) async { SharedPreferences sharedPreferences; sharedPreferences = await prefs; DateTime now = DateTime.now(); sharedPreferences.setString(Cache.lastStoreDateKey, now.toString()); if (type == SharePrefsChange.registration) { sharedPreferences.setInt(Cache.customerIdKey, customerId); sharedPreferences.setBool(Cache.isRegisteredKey, true); sharedPreferences.setBool(Cache.isLoggedInKey, true); sharedPreferences.setString(Cache.firebaseUidKey, firebaseUid); await initCustomer(customerId); } else if (type == SharePrefsChange.login) { sharedPreferences.setInt(Cache.customerIdKey, customerId); sharedPreferences.setString(Cache.firebaseUidKey, firebaseUid); sharedPreferences.setBool(Cache.isLoggedInKey, true); await initCustomer(customerId); } else if (type == SharePrefsChange.logout) { sharedPreferences.setBool(Cache.isLoggedInKey, false); sharedPreferences.setInt(Cache.customerIdKey, 0); sharedPreferences.setString(Cache.firebaseUidKey, ""); sharedPreferences.setString(authTokenKey, ""); } initBadges(); } void setExerciseTypes(List exerciseTypes) { this._exerciseTypes = exerciseTypes; } void setExerciseTree(List exerciseTree) { this._exerciseTree = exerciseTree; } void setExercises(List exercises) => this._exercises = exercises; void setExercisesTrainee(List exercises) { this._exercisesTrainee = exercises; } void setWorkoutMenuTree(LinkedHashMap tree) { this._tree = tree; } List? getExerciseTypes() => this._exerciseTypes; ExerciseType? getExerciseTypeById(int exerciseTypeId) { ExerciseType? exerciseType; if (_exerciseTypes != null) { this._exerciseTypes!.forEach((element) { if (element.exerciseTypeId == exerciseTypeId) { exerciseType = element; } }); } return exerciseType; } List? getExerciseTree() => this._exerciseTree; List? getExercises() => this._exercises; List? getExercisesTrainee() => this._exercisesTrainee; LinkedHashMap getWorkoutMenuTree() => this._tree; void setPercentExercises(double percent) => this._percentExercises = percent; double getPercentExercises() => this._percentExercises; void addExercise(Exercise exercise) => _exercises!.add(exercise); void addExerciseTrainee(Exercise exercise) => _exercisesTrainee!.add(exercise); Customer? getTrainee() => this._trainee; void setTrainee(Customer trainee) => _trainee = trainee; void setTraineeExercisePlan(ExercisePlan exercisePlan) => this._traineeExercisePlan = exercisePlan; ExercisePlan? getTraineesExercisePlan() => this._traineeExercisePlan; void setMyExercisePlan(ExercisePlan exercisePlan) => _myExercisePlan = exercisePlan; ExercisePlan? getMyExercisePlan() => _myExercisePlan; void setMyExercisePlanDetails(LinkedHashMap listExercisePlanDetail) => _myExercisesPlanDetails = listExercisePlanDetail; void addToMyExercisePlanDetails(ExercisePlanDetail detail) => _myExercisesPlanDetails[detail.exerciseTypeId] = detail; LinkedHashMap getMyExercisePlanDetails() => _myExercisesPlanDetails; void resetMyExercisePlanDetails() => _myExercisesPlanDetails = LinkedHashMap(); void updateMyExercisePlanDetail(ExercisePlanDetail detail) { this.addToMyExercisePlanDetails(detail); } void deleteMyExercisePlanDetail(ExercisePlanDetail detail) => this.deleteMyExercisePlanDetailByExerciseTypeId(detail.exerciseTypeId); void deletedMyExercisePlanDetail(ExercisePlanDetail detail) => this._myExercisesPlanDetails[detail.exerciseTypeId]!.change = ModelChange.deleted; void deleteMyExercisePlanDetailByExerciseTypeId(int exerciseTypeId) { this._myExercisesPlanDetails[exerciseTypeId]!.change = ModelChange.delete; } void setProperties(List properties) => this._properties = properties; List? getProperties() => _properties; void setDevices(List devices) => this._devices = devices; List? getDevices() => this._devices; void setCustomerDevices(List devices) => this._customerDevices = devices; List? getCustomerDevices() => this._customerDevices; LinkedHashMap getBadges() => _badges; void setBadge(String key, bool inc) { if (inc) { if (_badges[key] != null) { _badges[key] = _badges[key]! + 1; } else { _badges[key] = 1; } } else { if (_badges[key] != null) { if (_badges[key] == 1) { _badges.remove(key); } else { _badges[key] = _badges[key]! - 1; } } } } void setBadgeNr(String key, int counter) { if (_badges[key] != null) { _badges[key] = _badges[key]! + counter; } else { _badges[key] = counter; } } void initBadges() { CustomerRepository customerRepository = CustomerRepository(); _badges = LinkedHashMap(); if (userLoggedIn == null) { return; } customerRepository.setCustomer(userLoggedIn!); int _ecto = customerRepository.getCustomerPropertyValue(PropertyEnum.Ectomorph.toStr()).toInt(); int _mezo = customerRepository.getCustomerPropertyValue(PropertyEnum.Mesomorph.toStr()).toInt(); int _endo = customerRepository.getCustomerPropertyValue(PropertyEnum.Endomorph.toStr()).toInt(); //print("endo " + _endo.toString() + " mezo " + _mezo.toString()); if (this.userLoggedIn != null) { if (this.userLoggedIn!.birthYear == null || this.userLoggedIn!.birthYear == 0) { setBadge("personalData", true); setBadge("account", true); } if (this._customerDevices == null || this._customerDevices!.isEmpty) { setBadge("customerDevice", true); setBadge("account", true); } if (userLoggedIn!.properties.isEmpty) { setBadge("personalData", true); setBadge("bodyType", true); setBadge("Sizes", true); setBadge("BMI", true); setBadge("BMR", true); setBadgeNr("My Body", 3); setBadgeNr("home", 3); } else if (customerRepository.getWeight() == 0) { setBadge("BMI", true); setBadge("BMR", true); setBadge("My Body", true); setBadgeNr("home", 1); } if (_ecto == 0 && _mezo == 0 && _endo == 0) { setBadge("account", true); setBadge("bodyType", true); } if (this._exercises == null || this._exercises!.isEmpty) { setBadge("home", true); setBadge("Muscle Build / Shape Toning", true); setBadge("Cardio", true); } if (customerRepository.getHeight() == 0) { setBadge("BMI", true); setBadge("BMR", true); setBadge("My Body", true); setBadgeNr("home", 1); } if (userLoggedIn!.goal == null) { setBadge("Goal", true); setBadge("account", true); } if (userLoggedIn!.fitnessLevel == null) { setBadge("FitnessLevel", true); setBadge("account", true); } if (this._exercises != null && this._exercises!.isNotEmpty) { if (!isExerciseLogSeen!) { setBadge("exerciseLog", true); setBadge("development", true); } if (!isMuscleDevelopmentSeen!) { setBadge("muscleDevelopment", true); setBadge("development", true); } } } log("Badges: " + _badges.toString()); } List? get products => _products; void setProducts(List value) => _products = value; List get purchases => _purchases; setPurchases(List value) => _purchases = value; // ignore: unnecessary_getters_setters List? get productTests => _productTests; // ignore: unnecessary_getters_setters set productTests(List? value) => _productTests = value; Future initCustomer(int customerId) async { log(" *** initCustomer"); await PackageApi().getCustomerPackage(customerId); if (!isInDebugMode) { Flurry.setUserId(customerId.toString()); Smartlook.setUserIdentifier(customerId.toString()); Track().track(TrackingEvent.enter); } await setLoginTypeFromPrefs(); await getActiveExercisePlan(); await isExerciseLogSeenPrefs(); await isMuscleDevelopmentSeenPrefs(); Cache().startPage = "home"; } AccessToken? get getAccessTokenFacebook => accessTokenFacebook; set setAccessTokenFacebook(AccessToken accessTokenFacebook) => this.accessTokenFacebook = accessTokenFacebook; LoginType? getLoginType() => loginType; void setLoginType(LoginType type) => this.loginType = type; List get exercisePlanTemplates => this._exercisePlanTemplates; setExercisePlanTemplates(value) => this._exercisePlanTemplates = value; setExerciseLogSeen() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; isExerciseLogSeen = true; sharedPreferences.setBool(Cache.exerciseLogSeenKey, true); } Future isExerciseLogSeenPrefs() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; isExerciseLogSeen = sharedPreferences.getBool(Cache.exerciseLogSeenKey); if (isExerciseLogSeen == null) { isExerciseLogSeen = false; } //print("ExerciseLogSeen $isExerciseLogSeen"); return isExerciseLogSeen!; } setMuscleDevelopmentSeen() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; isMuscleDevelopmentSeen = true; sharedPreferences.setBool(Cache.muscleDevelopmentSeenKey, true); } Future isMuscleDevelopmentSeenPrefs() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences = await prefs; isMuscleDevelopmentSeen = sharedPreferences.getBool(Cache.muscleDevelopmentSeenKey); if (isMuscleDevelopmentSeen == null) { isMuscleDevelopmentSeen = false; } return isMuscleDevelopmentSeen!; } List? get evaluations => this._evaluations; set evaluations(List? value) => this._evaluations = value; }