diff --git a/asset/image/WT_reg_light_background.png b/asset/image/WT_reg_light_background.png new file mode 100644 index 0000000..922c546 Binary files /dev/null and b/asset/image/WT_reg_light_background.png differ diff --git a/asset/image/ankle_weight.png b/asset/image/ankle_weight.png new file mode 100644 index 0000000..215861f Binary files /dev/null and b/asset/image/ankle_weight.png differ diff --git a/asset/image/button_apple.png b/asset/image/button_apple.png new file mode 100644 index 0000000..78a03a6 Binary files /dev/null and b/asset/image/button_apple.png differ diff --git a/asset/image/button_fb.png b/asset/image/button_fb.png new file mode 100644 index 0000000..4c26d3d Binary files /dev/null and b/asset/image/button_fb.png differ diff --git a/asset/image/button_google.png b/asset/image/button_google.png new file mode 100644 index 0000000..e913986 Binary files /dev/null and b/asset/image/button_google.png differ diff --git a/asset/image/vest_weight.png b/asset/image/vest_weight.png new file mode 100644 index 0000000..3811b6d Binary files /dev/null and b/asset/image/vest_weight.png differ diff --git a/i18n/en.json b/i18n/en.json index c324114..8a8815c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -7,13 +7,16 @@ "TRAINING!": "TRAINING!", "Login": "Login", "Logout": "Logout", + "SignUp with Email": "SignUp with Email", "Tests": "Tests", "Change Language": "Change Language", "Password too short": "Password too short (at least 9 characters)", "Please type an email address": "Please type an email address", "Exception: Please accept our data policy":"Please accept our data policy", - "Please accept our data protection policy. For more information please click on 'Privacy'":"Please accept our data protection policy. For more information please click on 'Privacy'", - "SignUp": "SignUp", + "Please accept our data protection policy.":"Please accept our data protection policy.", + "For more information please click on 'Privacy'":"For more information please click on 'Privacy'", + "SignUp": "SignUp with Email", + "SignUpLink": "SignUp", "Privacy": "Privacy", "Change App Language": "Change App Language", "English": "English", @@ -26,9 +29,17 @@ "Please log in": "Please log in", "Exception: Customer does not exist or the password is wrong": "The email does not exist or the password is wrong", + "Exception: Facebook signup was not successful. Please try another method": "Facebook signup was not successful. Please try another method", "Exception: Customer exists": "The email address has been registered already", + "Exception: The email address has been registered already":"The email address has been registered already", "Exception: Please type an email address": "Please type an email address", "Exception: Password too short": "Password too short (at least 9 characters)", + "Exception: Google Sign In failed":"Google Sign In failed", + "Exception: Apple Sign-In failed":"Apple Sign-In failed", + "Exception: Apple Sign-In cancelled":"Apple Sign-In cancelled", + "Exception: Apple Sign In failure: email address is necessary": "Apple Sign In failure: email address is necessary", + + "There is an error: during registration:": "There is an error: during registration:", "Please select an exercise": "Please select an exercise", "Cardio": "Cardio", @@ -199,7 +210,7 @@ "Persistence!": "Persistence!", "Greetings!": "Greetings!", - "The purpose is to measure you physical condition": "The purpose is to measure your physical condition.", + "The purpose is to measure you physical condition": "The purpose is to measure your physical condition. Your first goal is to test all your muscle regions with a 'base' exercise.", "The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.": "The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.", "Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.": "Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.", "Please continue your tests with a": "Please continue your tests with a", @@ -286,6 +297,7 @@ "Unleash your potential with WorkoutTest Premium!":"Unleash your potential with WorkoutTest Premium!", "feature is reachable after you finished":"feature is reachable after you finished", "100% test circles":"100% test circles", + "Keep testing":"Keep Testing", "Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.":"Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.", diff --git a/i18n/hu.json b/i18n/hu.json index 8c82843..7ed0c37 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -12,8 +12,11 @@ "Password too short": "A jelszó min. 9 karakterből álljon", "Please type an email address": "Kérlek írj be egy email címet", "SignUp": "Regisztráció", + "SignUpLink": "Regisztráció", + "SignUp with Email": "Email Regisztráció", "Exception: Please accept our data policy":"Kérlek fogadd el az adatvédelmi szabályzatunkat", - "Please accept our data protection policy. For more information please click on 'Privacy'":"Kérlek fogadd el az adatvédelmi szabályzatunkat. További információkért kattints az 'Adatkezelés' linkre.", + "Please accept our data protection policy.":"Kérlek fogadd el az adatvédelmi szabályzatunkat.", + "For more information please click on 'Privacy'":"További információkért kattints az 'Adatkezelés' linkre.", "Privacy": "Adatkezelés", "Change App Language": "Nyelvválasztás", "English": "Angol", @@ -29,11 +32,17 @@ "Customer does not exist or the password is wrong": "A felhasználó nem létezik vagy a jelszó rossz.", "The email does not exist or the password is wrong": "A felhasználó nem létezik vagy a jelszó rossz.", "Exception: Facebook login was not successful": "Facebook bejelentkezés sikertelen", + "Exception: Facebook signup was not successful. Please try another method": "Facebook regisztráció sikertelen. Kérlek próbálj mással regisztrálni", "Exception: Please type an email address": "Kérlek írj be egy email címet", "Exception: Password too short": "A jelszó min. 9 karakterből álljon", "Exception: You have a previous Facebook login operation in progress":"Az előző bejelentkezés még folyamatban van.", "Exception: Facebook login cancelled":"Facebook bejelentkezés megszakítva ", "Exception: Facebook login failed":"Facebook bejelentkezés sikertelen", + "Exception: The email address has been registered already":"Ezzel email címmel már regisztráltak", + "Exception: Google Sign In failed":"Google bejelentkezés sikertelen", + "Exception: Apple Sign-In failed":"Apple bejelentkezés sikertelen", + "Exception: Apple Sign-In cancelled":"Apple bejelentkezés megszakítva", + "Exception: Apple Sign In failure: email address is necessary": "Apple bejelentkezés sikertelen: email cím szükséges.", "Please select an exercise": "Válassz ki egy gyakorlatot", "There is an error: during registration:": "Hiba lépett fel a regisztráció során:", @@ -73,7 +82,7 @@ "Endurance_desc":"

Erőállóképességi teszt lényege, ahogy az 1RM tesztnél is, hogy a Neked megfelelő SÚLY és ISMÉTLÉS számot tudjuk javasolni.

Nagyon fontos, hogy szabályosan és a kért ismétléssel dolgozz!

Ha a célod a hosszabb távú erő fenntartása, netán sportoló vagy, akkor feltétlen teszteld az erőállóképességi modulunkat is.


Miért az erőállóképesség?

Javítja az izmok oxigén és tápanyagellátottságát és ezáltal képes leszel egyre nagyobb súlyok egyre hosszabbtávon való megmozgatására. Például egyre több fekvőtámaszra és húzódzkodásra.

Kevésbé tömegnövelő hatású, ámbár atlétikus és nagyon erős testalkatot kölcsönöz, ha hosszútávon gyakorlod.

", "OneRepMax_desc":"

Az egy ismétléses maximum, vagy más néven 1RM ismerete számodra fontos lehet a Neked megfelelő SÚLY és ISMÉTLÉS kiszámításában.

Végezd el szabályosan(!) a tesztet, hogy a Neked legmegfelelőbb súlyokat és ismétléseket tudjuk javasolni a későbbiekben.

Ha a célod az izom, vagy az erőnövelés, akkor feltétlen csináld meg az 1RM teszteket!


Mi az 1RM?

Az a súly, amit egyetlen egyszer lennél képes szabályosan megmozgatni. Az egyszer szabályosan végrehajtott maximális súlyú gyakorlatból származtatjuk a céloknak megfelelő súly és ismétlésszámokat.

", - "Name": "Név", + "Name": "Vezetkéknév", "Exercise": "Gyakorlat", "Quantity": "Mennyiség", "Unit": "Egység", @@ -197,7 +206,7 @@ "Persistence!": "Kitartás!", "Greetings!": "Üdvözöllek!", - "The purpose is to measure you physical condition": "A cél a jelenlegi fizikai kondíciód felmérése.", + "The purpose is to measure you physical condition": "A cél a jelenlegi fizikai kondíciód felmérése. Az első célod az összes izomcsoport tesztelése egy 'base' gyakorlattal.", "The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.": "A gyakorlatok javasolt végrehajtási sorrendje: mell - bicepsz - tricepsz - hát - váll - has - comb - vádli.", "Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.": "Menj a menüben az Erő - Max Erő - Mell menüpontba, és válaszd ki a kedvenc gyakorlatod.", "Please continue your tests with a": "Kérlek folytasd a gyakorlatokat egy", @@ -284,6 +293,7 @@ "Unleash your potential with WorkoutTest Premium!":"Bontakoztasd ki az erősségeidet WorkoutTest Prémiummal!", "feature is reachable after you finished":"funkció elérhető számodra, miután teljesítetted", "100% test circles":"100%-os teszt-köröd", + "Keep testing":"Folytasd a tesztelést", "Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.":"Élvezd ezt a prémium funkciót is, amely megjeleníti a korábbi gyakorlatok teljes kiértékelését", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b3106f5..3b8901b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,11 @@ PODS: + - AppAuth (1.4.0): + - AppAuth/Core (= 1.4.0) + - AppAuth/ExternalUserAgent (= 1.4.0) + - AppAuth/Core (1.4.0) + - AppAuth/ExternalUserAgent (1.4.0) + - apple_sign_in (0.0.1): + - Flutter - devicelocale (0.0.1): - Flutter - FBSDKCoreKit (8.2.0): @@ -11,14 +18,24 @@ PODS: - FBSDKLoginKit/Login (= 8.2.0) - FBSDKLoginKit/Login (8.2.0): - FBSDKCoreKit (~> 8.2.0) + - Firebase/Analytics (6.33.0): + - Firebase/Core - Firebase/Auth (6.33.0): - Firebase/CoreOnly - FirebaseAuth (~> 6.9.2) + - Firebase/Core (6.33.0): + - Firebase/CoreOnly + - FirebaseAnalytics (= 6.8.3) - Firebase/CoreOnly (6.33.0): - FirebaseCore (= 6.10.3) - Firebase/Messaging (6.33.0): - Firebase/CoreOnly - FirebaseMessaging (~> 4.7.0) + - firebase_analytics (6.3.0): + - Firebase/Analytics (~> 6.33.0) + - Firebase/CoreOnly (~> 6.33.0) + - firebase_core + - Flutter - firebase_auth (0.18.4-1): - Firebase/Auth (~> 6.33.0) - Firebase/CoreOnly (~> 6.33.0) @@ -32,6 +49,15 @@ PODS: - Firebase/Messaging (~> 6.33.0) - firebase_core - Flutter + - FirebaseAnalytics (6.8.3): + - FirebaseCore (~> 6.10) + - FirebaseInstallations (~> 1.6) + - GoogleAppMeasurement (= 6.8.3) + - GoogleUtilities/AppDelegateSwizzler (~> 6.7) + - GoogleUtilities/MethodSwizzler (~> 6.7) + - GoogleUtilities/Network (~> 6.7) + - "GoogleUtilities/NSData+zlib (~> 6.7)" + - nanopb (~> 1.30906.0) - FirebaseAuth (6.9.2): - FirebaseCore (~> 6.10) - GoogleUtilities/AppDelegateSwizzler (~> 6.7) @@ -73,15 +99,26 @@ PODS: - FBSDKCoreKit (~> 8.2.0) - FBSDKLoginKit (~> 8.2.0) - Flutter - - flutter_inapp_purchase (0.0.1): - - Flutter - flutter_keyboard_visibility (0.0.1): - Flutter - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - google_sign_in (0.0.1): + - Flutter + - GoogleSignIn (~> 5.0) + - GoogleAppMeasurement (6.8.3): + - GoogleUtilities/AppDelegateSwizzler (~> 6.7) + - GoogleUtilities/MethodSwizzler (~> 6.7) + - GoogleUtilities/Network (~> 6.7) + - "GoogleUtilities/NSData+zlib (~> 6.7)" + - nanopb (~> 1.30906.0) - GoogleDataTransport (7.5.1): - nanopb (~> 1.30906.0) + - GoogleSignIn (5.0.2): + - AppAuth (~> 1.2) + - GTMAppAuth (~> 1.0) + - GTMSessionFetcher/Core (~> 1.1) - GoogleUtilities/AppDelegateSwizzler (6.7.2): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -90,6 +127,8 @@ PODS: - PromisesObjC (~> 1.2) - GoogleUtilities/Logger (6.7.2): - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (6.7.2): + - GoogleUtilities/Logger - GoogleUtilities/Network (6.7.2): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" @@ -99,7 +138,14 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (6.7.2): - GoogleUtilities/Logger + - GTMAppAuth (1.1.0): + - AppAuth/Core (~> 1.4) + - GTMSessionFetcher (~> 1.4) + - GTMSessionFetcher (1.5.0): + - GTMSessionFetcher/Full (= 1.5.0) - GTMSessionFetcher/Core (1.5.0) + - GTMSessionFetcher/Full (1.5.0): + - GTMSessionFetcher/Core (= 1.5.0) - nanopb (1.30906.0): - nanopb/decode (= 1.30906.0) - nanopb/encode (= 1.30906.0) @@ -122,15 +168,17 @@ PODS: - Flutter DEPENDENCIES: + - apple_sign_in (from `.symlinks/plugins/apple_sign_in/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - flurry (from `.symlinks/plugins/flurry/ios`) - Flutter (from `Flutter`) - flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`) - - flutter_inapp_purchase (from `.symlinks/plugins/flutter_inapp_purchase/ios`) - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) + - google_sign_in (from `.symlinks/plugins/google_sign_in/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -140,9 +188,11 @@ DEPENDENCIES: SPEC REPOS: trunk: + - AppAuth - FBSDKCoreKit - FBSDKLoginKit - Firebase + - FirebaseAnalytics - FirebaseAuth - FirebaseCore - FirebaseCoreDiagnostics @@ -151,16 +201,23 @@ SPEC REPOS: - FirebaseMessaging - Flurry-iOS-SDK - FMDB + - GoogleAppMeasurement - GoogleDataTransport + - GoogleSignIn - GoogleUtilities + - GTMAppAuth - GTMSessionFetcher - nanopb - PromisesObjC - Protobuf EXTERNAL SOURCES: + apple_sign_in: + :path: ".symlinks/plugins/apple_sign_in/ios" devicelocale: :path: ".symlinks/plugins/devicelocale/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" firebase_auth: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: @@ -173,10 +230,10 @@ EXTERNAL SOURCES: :path: Flutter flutter_facebook_auth: :path: ".symlinks/plugins/flutter_facebook_auth/ios" - flutter_inapp_purchase: - :path: ".symlinks/plugins/flutter_inapp_purchase/ios" flutter_keyboard_visibility: :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" + google_sign_in: + :path: ".symlinks/plugins/google_sign_in/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" shared_preferences: @@ -191,13 +248,17 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter/ios" SPEC CHECKSUMS: + AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7 + apple_sign_in: 7716c7ddfa195aeab7dec0dc374ef4ff45d1adb4 devicelocale: feebbe5e7a30adb8c4f83185de1b50ff19b44f00 FBSDKCoreKit: 4afd6ff53d8133a433dbcda44451c9498f8c6ce4 FBSDKLoginKit: 7181765f2524d7ebf82d9629066c8e6caafc99d0 Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 + firebase_analytics: 36a619088c46224900829f14f4daa71585693a6f firebase_auth: d5159db3873478d1ac839af7b10d2f831516136a firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75 + FirebaseAnalytics: 5dd088bd2e67bb9d13dbf792d1164ceaf3052193 FirebaseAuth: c92d49ada7948d1a23466e3db17bc4c2039dddc3 FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 @@ -208,11 +269,14 @@ SPEC CHECKSUMS: Flurry-iOS-SDK: 8f3f7fce27177002f15f145eede88dc1b9ac0cd0 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec flutter_facebook_auth: bad08a3d465e7b7ba9d8468a9dc7df3f69c136b8 - flutter_inapp_purchase: 5c6a1ac3f11b11d0c8c0321c0c41c1f05805e4c8 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + google_sign_in: 6bd214b9c154f881422f5fe27b66aaa7bbd580cc + GoogleAppMeasurement: 966e88df9d19c15715137bb2ddaf52373f111436 GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 + GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213 GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + GTMAppAuth: 197a8dabfea5d665224aa00d17f164fc2248dab9 GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b27c0d8..32adb50 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -156,6 +156,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, EB0A62903CC30EC853FC7908 /* [CP] Embed Pods Frameworks */, + C1BA2CB9D651F269CCBE06B3 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -265,6 +266,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + C1BA2CB9D651F269CCBE06B3 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; EB0A62903CC30EC853FC7908 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -366,7 +384,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -509,7 +527,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -544,7 +562,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 80c67d1..d6084d5 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -24,6 +24,7 @@ CFBundleURLSchemes fb584181112271127 + com.googleusercontent.apps.926782702216-2nsi7d9at3pc5ts8gkobt5697v590kb9 diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index f166400..98bdd20 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -4,6 +4,10 @@ aps-environment development + com.apple.developer.applesignin + + Default + com.apple.developer.authentication-services.autofill-credential-provider diff --git a/lib/bloc/login/login_bloc.dart b/lib/bloc/login/login_bloc.dart index be8f5b4..5849559 100644 --- a/lib/bloc/login/login_bloc.dart +++ b/lib/bloc/login/login_bloc.dart @@ -21,6 +21,7 @@ class LoginBloc extends Bloc with Trans { final BuildContext context; final bool isRegistration; bool dataPolicyAllowed = false; + bool obscure = true; LoginBloc({this.accountBloc, this.userRepository, this.context, this.isRegistration}) : super(LoginInitial()); @override @@ -51,6 +52,20 @@ class LoginBloc extends Bloc with Trans { Flurry.logEvent("Login"); Flurry.logEvent("LoginFB"); yield LoginSuccess(); + } else if (event is LoginGoogle) { + yield LoginLoading(); + await userRepository.getUserByGoogle(); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn)); + Flurry.logEvent("Login"); + Flurry.logEvent("LoginGoogle"); + yield LoginSuccess(); + } else if (event is LoginApple) { + yield LoginLoading(); + await userRepository.getUserByApple(); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn)); + Flurry.logEvent("Login"); + Flurry.logEvent("LoginApple"); + yield LoginSuccess(); } else if (event is RegistrationSubmit) { yield LoginLoading(); if (!this.dataPolicyAllowed) { @@ -75,9 +90,36 @@ class LoginBloc extends Bloc with Trans { Flurry.logEvent("RegistrationFB"); Flurry.logEvent("Registration"); yield LoginSuccess(); + } else if (event is RegistrationGoogle) { + yield LoginLoading(); + if (!this.dataPolicyAllowed) { + yield LoginError(); + throw Exception("Please accept our data policy"); + } + await userRepository.addUserGoogle(); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn)); + await saveCustomer(); + Flurry.logEvent("RegistrationGoogle"); + Flurry.logEvent("Registration"); + yield LoginSuccess(); + } else if (event is RegistrationApple) { + yield LoginLoading(); + if (!this.dataPolicyAllowed) { + yield LoginError(); + throw Exception("Please accept our data policy"); + } + await userRepository.addUserApple(); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn)); + await saveCustomer(); + Flurry.logEvent("RegistrationApple"); + Flurry.logEvent("Registration"); + yield LoginSuccess(); } else if (event is DataProtectionClicked) { - this.dataPolicyAllowed = event.marked; + yield LoginLoading(); + this.dataPolicyAllowed = !dataPolicyAllowed; yield LoginReady(); + } else if (event is LoginPasswordChangeObscure) { + this.obscure = !this.obscure; } } on Exception catch (e) { yield LoginError(message: e.toString()); diff --git a/lib/bloc/login/login_event.dart b/lib/bloc/login/login_event.dart index c51f179..908df30 100644 --- a/lib/bloc/login/login_event.dart +++ b/lib/bloc/login/login_event.dart @@ -35,6 +35,14 @@ class LoginFB extends LoginEvent { const LoginFB(); } +class LoginGoogle extends LoginEvent { + const LoginGoogle(); +} + +class LoginApple extends LoginEvent { + const LoginApple(); +} + class DataProtectionClicked extends LoginEvent { final bool marked; const DataProtectionClicked({this.marked}); @@ -47,3 +55,11 @@ class RegistrationSubmit extends LoginEvent { class RegistrationFB extends LoginEvent { const RegistrationFB(); } + +class RegistrationGoogle extends LoginEvent { + const RegistrationGoogle(); +} + +class RegistrationApple extends LoginEvent { + const RegistrationApple(); +} diff --git a/lib/bloc/sales/sales_bloc.dart b/lib/bloc/sales/sales_bloc.dart index b37c91a..df069d8 100644 --- a/lib/bloc/sales/sales_bloc.dart +++ b/lib/bloc/sales/sales_bloc.dart @@ -29,14 +29,14 @@ class SalesBloc extends Bloc with Logging { if (event is SalesLoad) { yield SalesLoading(); //Flurry.logEvent("SalesPageOpen"); - await PlatformPurchaseApi().initPurchasePlatformState(); - this.getProductSet(); + // await PlatformPurchaseApi().initPurchasePlatformState(); + // this.getProductSet(); yield SalesReady(); } else if (event is SalesPurchase) { final int productId = event.productId; trace("Requesting purchase for" + productId.toString()); //Flurry.logEvent("PurchaseRequest"); - PlatformPurchaseApi().purchase(getSelectedProduct(productId)); + // PlatformPurchaseApi().purchase(getSelectedProduct(productId)); } } on Exception catch (ex) { yield SalesError(message: ex.toString()); @@ -53,7 +53,7 @@ class SalesBloc extends Bloc with Logging { return prod; } - String getLocalizedPrice(String productId) { + /* String getLocalizedPrice(String productId) { String price = ""; for (var product in PlatformPurchaseApi().getIAPItems()) { if (Platform.isAndroid) { @@ -109,5 +109,5 @@ class SalesBloc extends Bloc with Logging { productTest.dateView = DateTime.now(); //ProductTestApi().saveProductTest(productTest); //Cache().productTests.add(productTest); - } + } */ } diff --git a/lib/bloc/session/session_bloc.dart b/lib/bloc/session/session_bloc.dart index 5f866bf..57bac26 100644 --- a/lib/bloc/session/session_bloc.dart +++ b/lib/bloc/session/session_bloc.dart @@ -42,7 +42,7 @@ class SessionBloc extends Bloc with Logging { @override Future close() async { await this.close(); - PlatformPurchaseApi().close(); + //PlatformPurchaseApi().close(); super.close(); } } diff --git a/lib/main.dart b/lib/main.dart index e57b2dd..43a2c73 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -32,6 +32,8 @@ import 'package:aitrainer_app/view/reset_password.dart'; import 'package:aitrainer_app/view/sales_page.dart'; import 'package:aitrainer_app/view/settings.dart'; import 'package:aitrainer_app/widgets/home.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_analytics/observer.dart'; import 'package:flurry/flurry.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -163,7 +165,7 @@ class WorkoutTestApp extends StatelessWidget { @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - //final FirebaseAnalytics analytics = FirebaseAnalytics(); + final FirebaseAnalytics analytics = FirebaseAnalytics(); initFlurry(); PushNotificationsManager().init(); return MaterialApp( @@ -233,7 +235,7 @@ class WorkoutTestApp extends StatelessWidget { bodyText1: GoogleFonts.openSans(textStyle: TextStyle(fontSize: 14.0)), )), navigatorObservers: [ - //FirebaseAnalyticsObserver(analytics: analytics), + FirebaseAnalyticsObserver(analytics: analytics), ], home: AitrainerHome(), ); diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 7f68fac..6987ce3 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -73,7 +73,6 @@ class Cache with Logging { AccessToken accessTokenFacebook; Customer userLoggedIn; String firebaseUid; - String facebookUid; bool hasPurchased = false; @@ -201,7 +200,7 @@ class Cache with Logging { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; - final String uid = Cache().firebaseUid == null ? Cache().facebookUid : Cache().firebaseUid; + final String uid = Cache().firebaseUid; await setPreferences(prefs, SharePrefsChange.registration, customer.customerId, uid); } @@ -219,7 +218,7 @@ class Cache with Logging { afterFacebookLogin() async { Future prefs = SharedPreferences.getInstance(); - await setPreferences(prefs, SharePrefsChange.login, userLoggedIn.customerId, Cache().facebookUid); + await setPreferences(prefs, SharePrefsChange.login, userLoggedIn.customerId, Cache().firebaseUid); } logout() async { @@ -457,7 +456,4 @@ class Cache with Logging { AccessToken get getAccessTokenFacebook => accessTokenFacebook; set setAccessTokenFacebook(AccessToken accessTokenFacebook) => this.accessTokenFacebook = accessTokenFacebook; - - String get getFacebookUid => facebookUid; - set setFacebookUid(String facebookUid) => this.facebookUid = facebookUid; } diff --git a/lib/repository/user_repository.dart b/lib/repository/user_repository.dart index 3985b32..c54381a 100644 --- a/lib/repository/user_repository.dart +++ b/lib/repository/user_repository.dart @@ -2,8 +2,12 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/user.dart'; import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/service/firebase_api.dart'; +import 'package:aitrainer_app/service/logging.dart'; +import 'package:aitrainer_app/util/not_found_exception.dart'; +import 'package:firebase_auth/firebase_auth.dart' as auth; +import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; -class UserRepository { +class UserRepository with Logging { User user; UserRepository() { @@ -31,29 +35,166 @@ class UserRepository { await CustomerApi().addUser(modelUser); } } catch (e) { - print(e.toString()); + log(e.toString()); throw new Exception(e); } } Future addUserFB() async { final User modelUser = this.user; + try { + Map userData = await FirebaseApi().registerWithFacebook(); + if (userData != null) { + modelUser.email = userData['email']; + if (modelUser.email == null) { + throw new Exception("Facebook signup was not successful. Please try another method"); + } + modelUser.password = Cache().firebaseUid; + modelUser.firebaseUid = Cache().firebaseUid; + await CustomerApi().addUser(modelUser); + } else { + throw new Exception("Facebook signup was not successful. Please try another method"); + } + } on auth.FirebaseAuthException catch (e) { + if (e.code == 'email-already-in-use') { + log('The account already exists for that email.'); + throw Exception("The email address has been registered already"); + } + } on WorkoutTestException catch (ex) { + if (ex.code == WorkoutTestException.CUSTOMER_EXISTS) { + log('The account already exists for that email.'); + throw Exception("The email address has been registered already"); + } + } on Exception catch (ex) { + log("Google exception: " + ex.toString()); + throw Exception("Google Sign In failed"); + } + } - Map userData = await FirebaseApi().signInWithFacebook(); - if (userData != null) { - modelUser.email = userData['email']; - modelUser.password = "1234567"; - modelUser.firebaseUid = Cache().facebookUid; - await CustomerApi().addUser(modelUser); + Future addUserGoogle() async { + final User modelUser = this.user; + try { + Map userData = await FirebaseApi().registerWithGoogle(); + if (userData != null) { + modelUser.email = userData['email']; + if (modelUser.email == null) { + throw new Exception("Google signup was not successful. Please try another method"); + } + modelUser.password = Cache().firebaseUid; + modelUser.firebaseUid = Cache().firebaseUid; + await CustomerApi().addUser(modelUser); + } else { + throw new Exception("Google signup was not successful. Please try another method"); + } + } on auth.FirebaseAuthException catch (e) { + if (e.code == 'email-already-in-use') { + log('The account already exists for that email.'); + throw Exception("The email address has been registered already"); + } + } on WorkoutTestException catch (ex) { + if (ex.code == WorkoutTestException.CUSTOMER_EXISTS) { + log('The account already exists for that email.'); + throw Exception("The email address has been registered already"); + } + } on Exception catch (ex) { + log("Google exception: " + ex.toString()); + throw Exception("Google Sign In failed"); + } + } + + Future addUserApple() async { + final User modelUser = this.user; + try { + Map userData = await FirebaseApi().registerWithApple(); + if (userData != null) { + modelUser.email = userData['email']; + if (modelUser.email == null) { + throw new Exception("Apple signup was not successful. Please try another method"); + } + modelUser.password = Cache().firebaseUid; + modelUser.firebaseUid = Cache().firebaseUid; + await CustomerApi().addUser(modelUser); + } else { + throw new Exception("Apple signup was not successful. Please try another method"); + } + } on auth.FirebaseAuthException catch (e) { + if (e.code == 'email-already-in-use') { + log('The account already exists for that email.'); + throw Exception("The email address has been registered already"); + } + } on WorkoutTestException catch (ex) { + if (ex.code == WorkoutTestException.CUSTOMER_EXISTS) { + log('The account already exists for that email.'); + throw Exception("The email address has been registered already"); + } + } on Exception catch (ex) { + log("Apple exception: " + ex.toString()); + throw Exception(ex); } } Future getUserByFB() async { final User modelUser = this.user; - Map userData = await FirebaseApi().signInWithFacebook(); + try { + Map userData = await FirebaseApi().signInWithFacebook(); + if (userData == null) { + throw new Exception("Facebook login was not successful"); + } + modelUser.email = userData['email']; + + await CustomerApi().getUserByEmail(modelUser.email); + await Cache().afterFirebaseLogin(); + } on FacebookAuthException catch (e) { + switch (e.errorCode) { + case FacebookAuthErrorCode.OPERATION_IN_PROGRESS: + throw Exception("You have a previous Facebook login operation in progress"); + break; + case FacebookAuthErrorCode.CANCELLED: + throw Exception("Facebook login cancelled"); + break; + case FacebookAuthErrorCode.FAILED: + throw Exception("Facebook login failed"); + break; + } + } on NotFoundException catch (ex) { + log("FB exception: " + ex.toString()); + throw Exception("Customer does not exist or the password is wrong"); + } on Exception catch (e) { + log(e.toString()); + throw new Exception(e); + } + } + + Future getUserByGoogle() async { + final User modelUser = this.user; + Map userData = await FirebaseApi().signInWithGoogle(); + if (userData == null || userData['email'] == null) { + throw new Exception("Google login was not successful"); + } modelUser.email = userData['email']; - await CustomerApi().getUserByEmail(modelUser.email); - await Cache().afterFacebookLogin(); + try { + await CustomerApi().getUserByEmail(modelUser.email); + await Cache().afterFirebaseLogin(); + } on Exception catch (ex) { + log("Google exception: " + ex.toString()); + throw Exception("Customer does not exist or the password is wrong"); + } + } + + Future getUserByApple() async { + final User modelUser = this.user; + Map userData = await FirebaseApi().signInWithApple(); + if (userData == null || userData['email'] == null) { + throw new Exception("Apple login was not successful"); + } + modelUser.email = userData['email']; + try { + await CustomerApi().getUserByEmail(modelUser.email); + await Cache().afterFirebaseLogin(); + } on Exception catch (ex) { + log("Apple exception: " + ex.toString()); + throw Exception("Customer does not exist or the password is wrong"); + } } Future getUser() async { @@ -64,7 +205,7 @@ class UserRepository { await CustomerApi().getUserByEmail(modelUser.email); await Cache().afterFirebaseLogin(); } else { - print("Exception: user not found or password is wrong"); + log("Exception: user not found or password is wrong"); throw Exception("Customer does not exist or the password is wrong"); } } diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index e5cdb2f..81763e8 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -7,6 +7,7 @@ import 'package:aitrainer_app/model/user.dart'; import 'package:aitrainer_app/service/api.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/service/logging.dart'; +import 'package:aitrainer_app/util/not_found_exception.dart'; class CustomerApi with Logging { final APIClient _client = new APIClient(); @@ -44,12 +45,16 @@ class CustomerApi with Logging { try { int status = jsonDecode(responseBody)['status']; if (status != null) { - throw new Exception(jsonDecode(responseBody)['error']); + String error = jsonDecode(responseBody)['error']; + throw new Exception(error); } else { customer = Customer.fromJson(jsonDecode(responseBody)); await Cache().afterRegistration(customer); } } on FormatException { + if (responseBody == "Customer exists") { + throw WorkoutTestException(code: WorkoutTestException.CUSTOMER_EXISTS, message: responseBody); + } throw new Exception(responseBody); } } diff --git a/lib/service/firebase_api.dart b/lib/service/firebase_api.dart index a4233dd..42f7466 100644 --- a/lib/service/firebase_api.dart +++ b/lib/service/firebase_api.dart @@ -1,10 +1,13 @@ import 'package:aitrainer_app/model/cache.dart'; -import 'package:aitrainer_app/service/logging.dart'; +import 'package:aitrainer_app/service/logging.dart' as logging; +import 'package:apple_sign_in/apple_sign_in.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; +import 'package:google_sign_in/google_sign_in.dart'; -class FirebaseApi with Logging { +class FirebaseApi with logging.Logging { + bool appleSignInAvailable = false; static FirebaseApi _instance; static final FirebaseAuth auth = FirebaseAuth.instance; @@ -28,6 +31,7 @@ class FirebaseApi with Logging { try { // Wait for Firebase to initialize and set `_initialized` state to true await Firebase.initializeApp(); + this.appleSignInAvailable = await AppleSignIn.isAvailable(); } catch (e) { // Set `_error` state to true if Firebase initialization fails log("Error initializing Firebase"); @@ -81,34 +85,200 @@ class FirebaseApi with Logging { return rc; } + Future> signInWithApple() async { + Map userData = Map(); + + final AuthorizationResult result = await AppleSignIn.performRequests([ + AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName]) + ]); + switch (result.status) { + case AuthorizationStatus.authorized: + print('User authorized'); + break; + case AuthorizationStatus.error: + print('User error'); + throw Exception("Apple Sign-In failed"); + break; + case AuthorizationStatus.cancelled: + print('User cancelled'); + throw Exception("Apple Sign-In cancelled"); + break; + } + + // Create an `OAuthCredential` from the credential returned by Apple. + final oauthCredential = OAuthProvider("apple.com").credential( + idToken: String.fromCharCodes(result.credential.identityToken), + accessToken: String.fromCharCodes(result.credential.authorizationCode)); + + // Sign in the user with Firebase. If the nonce we generated earlier does + // not match the nonce in `appleCredential.identityToken`, sign in will fail. + UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential); + + log("userCredential: " + userCredential.toString()); + + log("Apple Credentials: " + + result.credential.user.toString() + + " state " + + result.credential.state.toString() + + " email " + + userCredential.user.email); + userData['email'] = userCredential.user.email; + + return userData; + } + + Future> registerWithApple() async { + Map userData = Map(); + final AuthorizationResult result = await AppleSignIn.performRequests([ + AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName]) + ]); + switch (result.status) { + case AuthorizationStatus.authorized: + print('Apple User authorized'); + break; + case AuthorizationStatus.error: + print('Apple User error'); + throw Exception("Apple Sign-In failed"); + break; + case AuthorizationStatus.cancelled: + print('User cancelled'); + throw Exception("Apple Sign-In cancelled"); + break; + } + + // Create an `OAuthCredential` from the credential returned by Apple. + final oauthCredential = OAuthProvider("apple.com").credential( + idToken: String.fromCharCodes(result.credential.identityToken), + accessToken: String.fromCharCodes(result.credential.authorizationCode)); + + // Sign in the user with Firebase. If the nonce we generated earlier does + // not match the nonce in `appleCredential.identityToken`, sign in will fail. + UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential); + + Cache().firebaseUid = userCredential.user.uid; + + userData['email'] = userCredential.user.email; + + return userData; + } + + Future> signInWithGoogle() async { + Map userData = Map(); + + try { + // Trigger the authentication flow + GoogleSignIn _googleSignIn = GoogleSignIn( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + ); + final GoogleSignInAccount googleUser = await _googleSignIn.signIn(); + + if (googleUser == null) { + throw Exception("Google Sign In failed"); + } + + // Obtain the auth details from the request + final GoogleSignInAuthentication googleAuth = await googleUser.authentication; + + // Create a new credential + final GoogleAuthCredential credential = GoogleAuthProvider.credential( + accessToken: googleAuth.accessToken, + idToken: googleAuth.idToken, + ); + + await FirebaseAuth.instance.signInWithCredential(credential); + + if (googleUser != null) { + log("GoogleUser: " + googleUser.toString()); + userData['email'] = googleUser.email; + userData['id'] = googleUser.id; + userData['name'] = googleUser.displayName; + } + } on Exception catch (ex) { + log("Google exception: " + ex.toString()); + throw Exception("Google Sign In failed"); + } + return userData; + } + + Future> registerWithGoogle() async { + Map userData = Map(); + + // Trigger the authentication flow + GoogleSignIn _googleSignIn = GoogleSignIn( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + ); + final GoogleSignInAccount googleUser = await _googleSignIn.signIn(); + + if (googleUser == null) { + throw Exception("Google Sign In failed"); + } + + // Obtain the auth details from the request + final GoogleSignInAuthentication googleAuth = await googleUser.authentication; + + // Create a new credential + final GoogleAuthCredential credential = GoogleAuthProvider.credential( + accessToken: googleAuth.accessToken, + idToken: googleAuth.idToken, + ); + + final userCredential = await FirebaseAuth.instance.signInWithCredential(credential); + + log("Google credentials: " + credential.toString() + " GoogleUser: " + googleUser.toString()); + Cache().firebaseUid = userCredential.user.uid; + + userData['email'] = googleUser.email; + + return userData; + } + Future> signInWithFacebook() async { Map userData; - try { - // by default the login method has the next permissions ['email','public_profile'] - AccessToken accessToken = await FacebookAuth.instance.login(); - if (accessToken != null) { - log(accessToken.toJson().toString()); - Cache().accessTokenFacebook = accessToken; - // get the user data - userData = await FacebookAuth.instance.getUserData(); - Cache().facebookUid = userData['id']; - log(userData.toString()); - } else { - throw Exception("Facebook login was not successful"); - } - } on FacebookAuthException catch (e) { - switch (e.errorCode) { - case FacebookAuthErrorCode.OPERATION_IN_PROGRESS: - throw Exception("You have a previous Facebook login operation in progress"); - break; - case FacebookAuthErrorCode.CANCELLED: - throw Exception("Facebook login cancelled"); - break; - case FacebookAuthErrorCode.FAILED: - throw Exception("Facebook login failed"); - break; - } + + // by default the login method has the next permissions ['email','public_profile'] + AccessToken accessToken = await FacebookAuth.instance.login(); + if (accessToken != null) { + log(accessToken.toJson().toString()); + Cache().accessTokenFacebook = accessToken; + // get the user data + userData = await FacebookAuth.instance.getUserData(); + Cache().firebaseUid = userData['id']; + log(userData.toString()); + } else { + throw Exception("Facebook login was not successful"); } + + return userData; + } + + Future> registerWithFacebook() async { + Map userData; + + // by default the login method has the next permissions ['email','public_profile'] + AccessToken accessToken = await FacebookAuth.instance.login(); + if (accessToken != null) { + Cache().accessTokenFacebook = accessToken; + // get the user data + userData = await FacebookAuth.instance.getUserData(); + + // Create a credential from the access token + final FacebookAuthCredential facebookAuthCredential = FacebookAuthProvider.credential(accessToken.token); + + // Once signed in, return the UserCredential + final userCredential = await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential); + + Cache().firebaseUid = userCredential.user.uid; + log(userData.toString()); + } else { + throw Exception("Facebook login was not successful"); + } + return userData; } diff --git a/lib/util/not_found_exception.dart b/lib/util/not_found_exception.dart index ab56186..6cd97d8 100644 --- a/lib/util/not_found_exception.dart +++ b/lib/util/not_found_exception.dart @@ -1,4 +1,13 @@ class NotFoundException implements Exception { final String message; const NotFoundException({this.message}); -} \ No newline at end of file +} + +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({this.message, this.code}); +} diff --git a/lib/util/platform_purchase.dart b/lib/util/platform_purchase.dart index 57d8246..a0f7df8 100644 --- a/lib/util/platform_purchase.dart +++ b/lib/util/platform_purchase.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:io'; - +/* import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/product.dart'; import 'package:aitrainer_app/service/logging.dart'; @@ -21,15 +21,7 @@ class PlatformPurchaseApi with Logging { List getProductList() => _productList; - /* Platform.isAndroid - ? [ - 'android.test.purchased', - 'point_1000', - '5000_point', - 'android.test.canceled', - ] - : ['com.cooni.point1000', 'com.cooni.point5000']; - */ + factory PlatformPurchaseApi() { return _singleton; @@ -167,3 +159,4 @@ class PlatformPurchaseApi with Logging { this._purchases = items; } } + */ diff --git a/lib/view/customer_exercise_device.dart b/lib/view/customer_exercise_device.dart index 357a0ed..a149d7e 100644 --- a/lib/view/customer_exercise_device.dart +++ b/lib/view/customer_exercise_device.dart @@ -10,6 +10,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:modal_progress_hud/modal_progress_hud.dart'; // ignore: must_be_immutable class CustomerExerciseDevicePage extends StatelessWidget with Trans { @@ -37,19 +38,20 @@ class CustomerExerciseDevicePage extends StatelessWidget with Trans { ..add(CustomerExerciseDeviceLoad()), child: BlocConsumer( listener: (context, state) { - if (state is CustomerExerciseDeviceLoading) { - Scaffold.of(context).showSnackBar(SnackBar( - duration: Duration(milliseconds: 100), - backgroundColor: Colors.transparent, - content: Container(child: Center(child: CircularProgressIndicator())))); - } else if (state is CustomerExerciseDeviceError) { + if (state is CustomerExerciseDeviceError) { Scaffold.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } }, builder: (context, state) { final bloc = BlocProvider.of(context); - return getPage(bloc, cWidth, cHeight); + return ModalProgressHUD( + child: getPage(bloc, cWidth, cHeight), + inAsyncCall: state is CustomerExerciseDeviceLoading, + opacity: 0.5, + color: Colors.black54, + progressIndicator: CircularProgressIndicator(), + ); }, )))); } diff --git a/lib/view/login.dart b/lib/view/login.dart index 2084b3e..453b528 100644 --- a/lib/view/login.dart +++ b/lib/view/login.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/bloc/login/login_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; @@ -68,29 +70,40 @@ class LoginPage extends StatelessWidget with Trans { return Form( key: _scaffoldKey, child: Container( - padding: const EdgeInsets.only(left: 15, right: 50), - child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 100.0), children: [ - FlatButton( - child: new Image.asset( - 'asset/image/login_fb.png', - width: MediaQuery.of(context).size.width * .85, - ), - onPressed: () => {loginBloc.add(LoginFB())}, - ), - Text(AppLocalizations.of(context).translate("OR")), - Divider(), + padding: const EdgeInsets.only(left: 20, right: 20), + child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 150.0), children: [ + ListTile(title: Text(t("Login"), style: GoogleFonts.inter())), Row( - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ - new InkWell( - child: new Text(AppLocalizations.of(context).translate('Login'), - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), + FlatButton( + child: Image.asset( + 'asset/image/button_fb.png', + width: 60, + ), + onPressed: () => {loginBloc.add(LoginFB())}, ), + FlatButton( + child: Image.asset( + 'asset/image/button_google.png', + width: 60, + ), + onPressed: () => {loginBloc.add(LoginGoogle())}, + ), + Platform.isIOS + ? FlatButton( + child: Image.asset( + 'asset/image/button_apple.png', + width: 60, + ), + onPressed: () => {loginBloc.add(LoginApple())}, + ) + : Offstage(), ], ), - Divider( - color: Colors.transparent, - ), + Divider(), + ListTile(title: Text(t("OR"), style: GoogleFonts.inter())), + Divider(), TextFormField( key: LibraryKeys.loginEmailField, decoration: InputDecoration( @@ -118,7 +131,7 @@ class LoginPage extends StatelessWidget with Trans { ), TextFormField( key: LibraryKeys.loginPasswordField, - obscureText: true, + obscureText: loginBloc.obscure, decoration: InputDecoration( labelStyle: TextStyle(fontSize: 14), contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15), @@ -168,7 +181,7 @@ class LoginPage extends StatelessWidget with Trans { ), Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ InkWell( - child: Text(AppLocalizations.of(context).translate('SignUp')), + child: Text(AppLocalizations.of(context).translate('SignUpLink')), onTap: () => Navigator.of(context).pushNamed('registration'), ), Spacer(flex: 2), diff --git a/lib/view/registration.dart b/lib/view/registration.dart index b66d3c8..d0a715d 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/bloc/login/login_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; @@ -11,7 +13,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:modal_progress_hud/modal_progress_hud.dart'; -import 'package:toggle_switch/toggle_switch.dart'; import '../library_keys.dart'; @@ -74,28 +75,48 @@ class RegistrationPage extends StatelessWidget with Trans { return Form( key: _scaffoldKey, child: Container( - padding: const EdgeInsets.only(left: 15, right: 50), + padding: const EdgeInsets.only(left: 20, right: 20), child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 150.0), children: [ - FlatButton( - child: Image.asset( - 'asset/image/fb_registration.png', - width: MediaQuery.of(context).size.width * .85, - ), - onPressed: () => {loginBloc.add(RegistrationFB())}, - ), - ListTile(title: Text(AppLocalizations.of(context).translate("OR"))), + ListTile(title: Text(t("SignUp"), style: GoogleFonts.inter())), Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FlatButton( + child: Image.asset( + 'asset/image/button_fb.png', + width: 60, + ), + onPressed: () => {loginBloc.add(RegistrationFB())}, + ), + FlatButton( + child: Image.asset( + 'asset/image/button_google.png', + width: 60, + ), + onPressed: () => {loginBloc.add(RegistrationGoogle())}, + ), + Platform.isIOS + ? FlatButton( + child: Image.asset( + 'asset/image/button_apple.png', + width: 60, + ), + onPressed: () => {loginBloc.add(RegistrationApple())}, + ) + : Offstage(), + ], + ), + ListTile(title: Text(t("OR"), style: GoogleFonts.inter())), + /* Row( mainAxisAlignment: MainAxisAlignment.start, children: [ InkWell( - child: - Text(AppLocalizations.of(context).translate('SignUp'), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), + child: Text(AppLocalizations.of(context).translate('SignUp with Email'), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), ), ], - ), - Divider( - color: Colors.transparent, - ), + ), */ + TextFormField( key: LibraryKeys.loginEmailField, decoration: InputDecoration( @@ -123,7 +144,7 @@ class RegistrationPage extends StatelessWidget with Trans { ), TextFormField( key: LibraryKeys.loginPasswordField, - obscureText: true, + obscureText: loginBloc.obscure, decoration: InputDecoration( labelStyle: TextStyle(fontSize: 14), contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15), @@ -153,6 +174,9 @@ class RegistrationPage extends StatelessWidget with Trans { color: Colors.transparent, ), getDataProtection(loginBloc), + Divider( + color: Colors.transparent, + ), Row(mainAxisAlignment: MainAxisAlignment.start, children: [ FlatButton( key: LibraryKeys.loginOKButton, @@ -196,22 +220,16 @@ class RegistrationPage extends StatelessWidget with Trans { } Widget getDataProtection(LoginBloc loginBloc) { - return ListTile( - subtitle: Text(t("Please accept our data protection policy. For more information please click on 'Privacy'")), - title: ToggleSwitch( - minWidth: 100.0, - minHeight: 30.0, - fontSize: 14.0, - initialLabelIndex: loginBloc.dataPolicyAllowed ? 0 : 1, - activeBgColor: Colors.indigo, - activeFgColor: Colors.white, - inactiveBgColor: Colors.white30, - inactiveFgColor: Colors.black, - labels: [t('Yes'), t('No')], - onToggle: (index) { - loginBloc.add(DataProtectionClicked(marked: index == 0)); - }, - ), + return CheckboxListTile( + title: Text(t("Please accept our data protection policy.")), + subtitle: Text(t("For more information please click on 'Privacy'")), + dense: true, + value: loginBloc.dataPolicyAllowed, + activeColor: Colors.indigo, + onChanged: (value) { + loginBloc.add(DataProtectionClicked(marked: value)); + }, + controlAffinity: ListTileControlAffinity.leading, // <-- leading Checkbox ); } } diff --git a/lib/widgets/dialog_premium.dart b/lib/widgets/dialog_premium.dart index 7c0eddc..1fefcc0 100644 --- a/lib/widgets/dialog_premium.dart +++ b/lib/widgets/dialog_premium.dart @@ -12,7 +12,7 @@ class DialogPremium extends StatefulWidget { String description, function, unlockedText; final int unlockRound; - final bool unlocked; + bool unlocked; DialogPremium( {Key key, @@ -27,6 +27,8 @@ class DialogPremium extends StatefulWidget { description = description ?? ""; function = function ?? ""; unlockedText = unlockedText ?? ""; + + unlocked = true; } @override @@ -88,9 +90,9 @@ class _DialogPremiumState extends State with Trans { alignment: AlignmentDirectional.topEnd, children: [ Text( - t("Go Premium") + " ", + widget.unlocked ? t("Keep testing") : t("Go Premium") + " ", style: GoogleFonts.archivoBlack( - fontSize: 24, + fontSize: widget.unlocked ? 18 : 24, color: Colors.yellow[400], shadows: [ Shadow( @@ -106,32 +108,34 @@ class _DialogPremiumState extends State with Trans { ], ), ), - Positioned( - right: 3, - top: 0, - child: AnimatedSwitcher( - duration: Duration(milliseconds: 900), - //reverseDuration: Duration(milliseconds: 200), - transitionBuilder: (Widget child, Animation animation) { - return FadeTransition(child: child, opacity: animation); - }, - child: isStart - ? Icon( - CustomIcon.star_2, - color: Colors.yellow[300], - ) - : Offstage() /* Icon( + !widget.unlocked + ? Positioned( + right: 3, + top: 0, + child: AnimatedSwitcher( + duration: Duration(milliseconds: 900), + //reverseDuration: Duration(milliseconds: 200), + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition(child: child, opacity: animation); + }, + child: isStart + ? Icon( + CustomIcon.star_2, + color: Colors.yellow[300], + ) + : Offstage() /* Icon( CustomIcon.exclamation_circle, color: Colors.yellow[300], ) */ - )), + )) + : Offstage(), ], ), SizedBox( height: 35, ), Text( - t("Unleash your potential with WorkoutTest Premium!"), + widget.unlocked ? "" : t("Unleash your potential with WorkoutTest Premium!"), style: GoogleFonts.inter( fontSize: 14, color: Colors.white, @@ -160,7 +164,7 @@ class _DialogPremiumState extends State with Trans { Align( alignment: Alignment.center, child: GestureDetector( - onTap: () => Navigator.of(context).pushNamed("salesPage"), + onTap: () => widget.unlocked ? Navigator.of(context).pop() : Navigator.of(context).pushNamed("salesPage"), child: Stack( alignment: Alignment.center, children: [ @@ -222,61 +226,60 @@ class _DialogPremiumState extends State with Trans { List getDescriptionText() { List list = List(); - if (widget.unlocked) { + /* if (widget.unlocked) { list.add(TextSpan(text: widget.unlockedText)); - } else { - list.add(TextSpan(text: t("The"))); - list.add(TextSpan(text: t(" "))); - list.add( - TextSpan( - text: t(widget.function), - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.yellow[300], - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - ), + } */ + list.add(TextSpan(text: t("The"))); + list.add(TextSpan(text: t(" "))); + list.add( + TextSpan( + text: t(widget.function), + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.yellow[300], + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], ), - ); - list.add(TextSpan(text: t(" "))); - list.add(TextSpan(text: t("feature is reachable after you finished"))); - list.add(TextSpan(text: t(" "))); - list.add( - TextSpan( - text: widget.unlockRound == 1 ? t("the first") : t("the second"), - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.yellow[300], - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - ), + ), + ); + list.add(TextSpan(text: t(" "))); + list.add(TextSpan(text: t("feature is reachable after you finished"))); + list.add(TextSpan(text: t(" "))); + list.add( + TextSpan( + text: widget.unlockRound == 1 ? t("the first") : t("the second"), + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.yellow[300], + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], ), - ); - list.add(TextSpan(text: t(" "))); - list.add(TextSpan(text: t("100% test circles"))); - } + ), + ); + list.add(TextSpan(text: t(" "))); + list.add(TextSpan(text: t("100% test circles"))); return list; } diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 4826c1c..94f2423 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -91,6 +91,6 @@ class _HomePageState extends State with Logging { @override void dispose() async { super.dispose(); - await PlatformPurchaseApi().close(); + //await PlatformPurchaseApi().close(); } } diff --git a/lib/widgets/image_button.dart b/lib/widgets/image_button.dart index 0ea40a7..8da9af7 100644 --- a/lib/widgets/image_button.dart +++ b/lib/widgets/image_button.dart @@ -54,7 +54,7 @@ class ImageButton extends StatelessWidget { //print("Top: " + top.toStringAsFixed(0) + " length: " + ((style.fontSize - 5) * text.length).toString()); } final double width = MediaQuery.of(context).size.width; - print("Mediawidth: " + width.toStringAsFixed(0)); + //print("Mediawidth: " + width.toStringAsFixed(0)); return Stack(alignment: AlignmentDirectional.bottomStart, children: [ FlatButton( child: image == null @@ -152,11 +152,11 @@ class ImageButton extends StatelessWidget { imageName, fit: BoxFit.fitWidth, alignment: Alignment.center, - errorBuilder: (context, error, stackTrace) { - String url = Cache.mediaUrl + '/' + imageName; //.substring(11); + /* errorBuilder: (context, error, stackTrace) { + String url = Cache.mediaUrl + imageName; //.substring(11); Widget image = FadeInImage.assetNetwork(placeholder: 'asset/image/dots.gif', image: url, height: this.height); return image; - }, + }, */ ); } on Exception catch (_) { String url = Cache.mediaUrl + '/images/' + imageName; diff --git a/pubspec.lock b/pubspec.lock index 25edc20..1ace789 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.6" + apple_sign_in: + dependency: "direct main" + description: + name: apple_sign_in + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" archive: dependency: transitive description: @@ -205,7 +212,7 @@ packages: source: hosted version: "0.14.2" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto url: "https://pub.dartlang.org" @@ -281,6 +288,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.2.1" + firebase: + dependency: transitive + description: + name: firebase + url: "https://pub.dartlang.org" + source: hosted + version: "7.3.3" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + url: "https://pub.dartlang.org" + source: hosted + version: "6.3.0" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" firebase_auth: dependency: "direct main" description: @@ -398,13 +433,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" - flutter_inapp_purchase: - dependency: "direct main" - description: - name: flutter_inapp_purchase - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" flutter_keyboard_visibility: dependency: transitive description: @@ -448,20 +476,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.19.1" - freezed: - dependency: "direct main" - description: - name: freezed - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.6" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.0" glob: dependency: transitive description: @@ -476,6 +490,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" + google_sign_in: + dependency: "direct main" + description: + name: google_sign_in + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.9" + google_sign_in_platform_interface: + dependency: transitive + description: + name: google_sign_in_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + google_sign_in_web: + dependency: transitive + description: + name: google_sign_in_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.2" gradient_bottom_navigation_bar: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 961a9e3..9c4cb66 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.4+45 +version: 1.1.4+46 environment: sdk: ">=2.7.0 <3.0.0" @@ -30,7 +30,7 @@ dependencies: sentry: ^3.0.1 flutter_bloc: ^6.1.1 equatable: ^1.2.5 - freezed: ^0.12.2 + #freezed: ^0.12.2 flutter_form_bloc: ^0.19.0 spider_chart: ^0.1.5 rainbow_color: ^0.1.1 @@ -45,17 +45,21 @@ dependencies: #health: ^3.0.0 stop_watch_timer: ^0.6.0+1 #geolocator: ^6.1.13 - flutter_inapp_purchase: ^3.0.1 + #flutter_inapp_purchase: ^3.0.1 modal_progress_hud: ^0.1.3 flutter_html: ^1.1.1 wakelock: ^0.2.1+1 - firebase_core: ^0.5.2 - #firebase_analytics: ^6.2.0 - firebase_messaging: ^7.0.3 + firebase_core: ^0.5.0 + firebase_analytics: ^6.2.0 + firebase_messaging: ^7.0.0 firebase_auth: ^0.18.3 flutter_facebook_auth: ^2.0.0+1 + google_sign_in: ^4.5.9 + apple_sign_in: ^0.1.0 + crypto: ^2.1.5 + flurry: ^0.0.7 animated_widgets: ^1.0.6 @@ -121,6 +125,7 @@ flutter: - asset/image/WT_black_background.png - asset/image/WT_black_G_background.png - asset/image/WT_plainblack_background.png + - asset/image/WT_reg_light_background.png - asset/image/WT_menu.png - asset/image/WT_login.png - asset/image/WT_OK.png @@ -136,8 +141,9 @@ flutter: - asset/image/WT_Results_for_runners.png - asset/image/WT_Results_for_female.png - asset/image/WT_Results_for_men.png - - asset/image/login_fb.png - - asset/image/fb_registration.png + - asset/image/button_fb.png + - asset/image/button_apple.png + - asset/image/button_google.png - asset/image/lock.png - asset/image/Congrats_N1.jpg - asset/image/testemfejl400x400.jpg @@ -178,6 +184,8 @@ flutter: - asset/image/equipment_home_place.jpg - asset/image/equipment_gym_place.jpg - asset/image/equipment_street_place.jpg + - asset/image/ankle_weight.png + - asset/image/vest_weight.png - asset/image/haken.png - asset/image/pict_calorie.png - asset/image/pict_development_by_bodypart_percent.png