From 5050aab9b7bf3c33efa05775caff12f3cd0c21de Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Mon, 23 Jun 2025 02:24:42 +0300 Subject: [PATCH] 25-6-23/1 --- .env | 14 +- .../arm64-v8a/configure_fingerprint.bin | 24 +- .../armeabi-v7a/configure_fingerprint.bin | 24 +- .../6a58wo5c/x86/configure_fingerprint.bin | 24 +- .../6a58wo5c/x86_64/configure_fingerprint.bin | 24 +- android/app/src/main/AndroidManifest.xml | 92 +- .../kotlin/com/sefer_driver/MainActivity.kt | 244 ++-- lib/constant/box_name.dart | 4 + lib/constant/links.dart | 10 + .../auth/captin/invit_controller.dart | 9 +- .../auth/captin/login_captin_controller.dart | 15 +- lib/controller/firebase/access_token.dart | 2 +- lib/controller/firebase/firbase_messge.dart | 45 +- .../firebase/local_notification.dart | 6 +- lib/controller/functions/crud.dart | 70 +- lib/controller/functions/gemeni.dart | 2 +- .../functions/location_controller.dart | 4 +- lib/controller/functions/package_info.dart | 18 +- lib/controller/functions/secure_storage.dart | 22 +- .../home/captin/home_captain_controller.dart | 5 +- .../home/captin/map_driver_controller.dart | 71 +- .../payment/captain_wallet_controller.dart | 13 +- .../home/payment/paymob_payout.dart | 87 +- lib/controller/local/translations.dart | 32 + lib/controller/rate/rate_conroller.dart | 2 +- lib/main.dart | 241 +--- lib/models/model/order_data.dart | 66 +- lib/models/overlay_service.dart | 13 + lib/views/auth/country_widget.dart | 3 +- .../Captin/About Us/frequantly_question.dart | 18 +- .../Captin/About Us/settings_captain.dart | 2 +- .../home/Captin/About Us/using_app_page.dart | 29 +- lib/views/home/Captin/driver_map_page.dart | 24 +- .../home/Captin/home_captain/home_captin.dart | 5 +- .../widget/left_menu_map_captain.dart | 240 +++- .../home/Captin/maintain_center_page.dart | 4 +- .../passenger_info_window.dart | 21 +- .../Captin/mapDriverWidgets/sos_connect.dart | 2 +- .../Captin/orderCaptin/order_over_lay.dart | 1128 +++++++++-------- .../orderCaptin/order_speed_request.dart | 1091 ++++++++-------- lib/views/home/my_wallet/points_captain.dart | 419 +++++- pubspec.yaml | 1 + 42 files changed, 2387 insertions(+), 1783 deletions(-) create mode 100644 lib/models/overlay_service.dart diff --git a/.env b/.env index 03250d0..ccf7959 100755 --- a/.env +++ b/.env @@ -32,18 +32,18 @@ geminiApi=QOmqZsQYm08vOqjI7klVJfvP4WBFEoemjgy396iXrXlBl geminiApiMasa=QOmqZsQIdM4BRsKmaDJOP7dZp5-c6NWfch7PAlQXrXlBl agoraAppId=71880f2j636509j24y5294480y30u848XrXlBl agoraAppCertificate=j17q944u49390q758u1649448q2y6xfuXrXlBl -usernamePayMob=37319104052XrXlBl -passwordPayMob='g@nkD2#99!hD_.wXrXlBl' -integrationIdPayMob=0237730XrXlBl -payMobApikey='MDrGqKEWS1rVqHvEPDvPjJ7vZDBExrO7S3BEBgrlfUwTA3i5RnP5ZnvoL3M2S9rEBgrlNTdexH5pTPf7NJrvy1reZJv1Tn7zf7vTIDywjHg1C7Ley38HTDyNA3v7TPfdxJrax1rwPmPtMJyzqKEYZeghq3MuLUrFH3A1AgHcH15CZ9UaZTLOxnw0BTdzHHrBArisZerUMUUzZ1BnBeEijHvNjYLnS1BUICMhSmPhA15ifHyVqKEMHWyKLbyuIPvcH9UeL3vZyDf=XrXlBl' -integrationIdPayMobWallet=0237739XrXlBl +usernamePayMob=373191e04052XrXlBl +passwordPayMob='g@nkD23#99!hD_.wXrXlBl' +integrationIdPayMob=02337730XrXlBl +payMobApikey='MDrGqKEWS14rVqHvEPDvPjJ7vZDBExrO7S3BEBgrlfUwTA3i5fRnP5ZnvoL3M2S9rEBgrlNTdexH5pTPf7NJrvy1reZJv1Tn7zf7vTIDywjHg1C7Ley38HTDyNA3v7TPfdxJrax1rwPmPtMJyzqKEYZeghq3MuLUrFH3A1AgHcH15CZ9UaZTLOxnw0BTdzHHrBArisZerUMUUzZ1BnBeEijHvNjYLnS1BUICMhSmPhA15ifHyVqKEMHWyKLbyuIPvcH9UeL3vZyDf=XrXlBl' +integrationIdPayMobWallet=0237731XrXlBl ocpApimSubscriptionKey=0f5dacccdbce4131b1a5f952996302e3 smsPasswordEgypt="J)Vh=qb/@MXrXlBl" chatGPTkeySeferNew4=zg-vlie-2l1ZlpxiLJ6wQOvbb4TnC9XrxgUEyVQIu6TID4qP4FUUqoS5XrXlBl anthropicAIkeySeferNew=zg-qbc-qvo39-n4VdMQ5nuJeIYhMN4PDYr7qox3-t2i1Lh7aNTDfYF-Gf8whUJZCs47EeelKn8_UcmUMmiSLaf0UJg0DvUlQrDt-76CRrkQQXrXlBl llama3Key=kzg_uTXy3e9DBbCQ1FnMGxYwTKysx9US1burxJj4fFwOje4LZBUFKJS1XrXlBl -payMobOutPassword='D2zJFxkE#LN3vz38z2dYxpNfWXrXlBl' -payMobOutUserName='zjujl_qvo_fwjfgjlXrXlBl' +payMobOutPassword='D2zJFxkE#LN43vz328z2dYxpNfWXrXlBl' +payMobOutUserName='zjujl_qfvo_fwjf36gjlXrXlBl' keyOfApp=nqryjjjhgjp@1bCQ1FnMGxYwsjyzjujljksvceiXrXlBl initializationVector=ujljkdelkjlXrXlBlfghijkl sss_pass=wqnmqqsjyvwv:nqrYJP@17378XrXlBl diff --git a/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/configure_fingerprint.bin b/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/configure_fingerprint.bin index dd1c4ca..909974f 100644 --- a/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/configure_fingerprint.bin @@ -2,28 +2,28 @@ C/C++ Structured Log  }/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/additional_project_files.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  Զ2  2~ +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2  2~ | -z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/android_gradle_build.json  ն2 +z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/android_gradle_build.json  2 2  -/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/android_gradle_build_mini.json  ն2 2p +/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/android_gradle_build_mini.json  2 2p n -l/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/build.ninja  ն2 2t +l/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/build.ninja  2 2t r -p/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/build.ninja.txt  ն2y +p/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/build.ninja.txt  2y w -u/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/build_file_index.txt  ն2 ^ 2z +u/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/build_file_index.txt  2 ^ 2z x -v/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/compile_commands.json  ն2 2~ +v/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/compile_commands.json  2 2~ | -z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/compile_commands.json.bin  ն2  2 +z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/compile_commands.json.bin  2  2  -/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/metadata_generation_command.txt  ն2 +/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/metadata_generation_command.txt  2  2w u -s/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/prefab_config.json  ն2  ( 2| +s/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/prefab_config.json  2  ( 2| z -x/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/symbol_folder_index.txt  ն2  o 2b +x/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/arm64-v8a/symbol_folder_index.txt  2  o 2b ` -^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  ն2  Ў2 \ No newline at end of file +^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  2  Ў2 \ No newline at end of file diff --git a/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/configure_fingerprint.bin b/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/configure_fingerprint.bin index abaa30e..184debc 100644 --- a/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/configure_fingerprint.bin @@ -2,27 +2,27 @@ C/C++ Structured Log  /Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/additional_project_files.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2  2 +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2  2 ~ -|/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/android_gradle_build.json  2 2 +|/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/android_gradle_build.json  2 2  -/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/android_gradle_build_mini.json  2 2r +/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/android_gradle_build_mini.json  2 2r p -n/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/build.ninja  2 2v +n/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/build.ninja  2 2v t -r/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/build.ninja.txt  2{ +r/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/build.ninja.txt  2{ y -w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/build_file_index.txt  2 ^ 2| +w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/build_file_index.txt  2 ^ 2| z -x/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/compile_commands.json  2 2 +x/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/compile_commands.json  2 2 ~ -|/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/compile_commands.json.bin  2  2 +|/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/compile_commands.json.bin  2  2  -/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/metadata_generation_command.txt  2 +/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/metadata_generation_command.txt  2  2y w -u/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/prefab_config.json  2  ( 2~ +u/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/prefab_config.json  2  ( 2~ | -z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/symbol_folder_index.txt  2  q 2b +z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/armeabi-v7a/symbol_folder_index.txt  2  q 2b ` -^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  2  Ў2 \ No newline at end of file +^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  2  Ў2 \ No newline at end of file diff --git a/android/app/.cxx/Debug/6a58wo5c/x86/configure_fingerprint.bin b/android/app/.cxx/Debug/6a58wo5c/x86/configure_fingerprint.bin index 3ab9b8b..8d0b7ce 100644 --- a/android/app/.cxx/Debug/6a58wo5c/x86/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/6a58wo5c/x86/configure_fingerprint.bin @@ -2,28 +2,28 @@ C/C++ Structured Log{ y w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/additional_project_files.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  ɷ2  2x +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2  2x v -t/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/android_gradle_build.json  ɷ2 +t/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/android_gradle_build.json  2 2} { -y/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/android_gradle_build_mini.json  ɷ2 2j +y/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/android_gradle_build_mini.json  2 2j h -f/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/build.ninja  ɷ2 2n +f/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/build.ninja  2 2n l -j/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/build.ninja.txt  ɷ2s +j/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/build.ninja.txt  2s q -o/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/build_file_index.txt  ɷ2 ^ 2t +o/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/build_file_index.txt  2 ^ 2t r -p/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/compile_commands.json  ɷ2 2x +p/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/compile_commands.json  2 2x v -t/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/compile_commands.json.bin  ɷ2  2~ +t/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/compile_commands.json.bin  2  2~ | -z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/metadata_generation_command.txt  ɷ2 +z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/metadata_generation_command.txt  2  2q o -m/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/prefab_config.json  ɷ2  ( 2v +m/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/prefab_config.json  2  ( 2v t -r/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/symbol_folder_index.txt  ɷ2  i 2b +r/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86/symbol_folder_index.txt  2  i 2b ` -^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  ɷ2  Ў2 \ No newline at end of file +^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  2  Ў2 \ No newline at end of file diff --git a/android/app/.cxx/Debug/6a58wo5c/x86_64/configure_fingerprint.bin b/android/app/.cxx/Debug/6a58wo5c/x86_64/configure_fingerprint.bin index 153b7cd..0921379 100644 --- a/android/app/.cxx/Debug/6a58wo5c/x86_64/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/6a58wo5c/x86_64/configure_fingerprint.bin @@ -2,28 +2,28 @@ C/C++ Structured Log~ | z/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/additional_project_files.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  ߷2  2{ +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2  2{ y -w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/android_gradle_build.json  ߷2 +w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/android_gradle_build.json  2 2 ~ -|/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/android_gradle_build_mini.json  ߷2 2m +|/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/android_gradle_build_mini.json  2 2m k -i/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/build.ninja  ߷2 2q +i/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/build.ninja  2 2q o -m/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/build.ninja.txt  ߷2v +m/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/build.ninja.txt  2v t -r/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/build_file_index.txt  ߷2 ^ 2w +r/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/build_file_index.txt  2 ^ 2w u -s/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/compile_commands.json  ߷2 2{ +s/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/compile_commands.json  2 2{ y -w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/compile_commands.json.bin  ߷2  2 +w/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/compile_commands.json.bin  2  2  -}/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/metadata_generation_command.txt  ߷2 +}/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/metadata_generation_command.txt  2  2t r -p/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/prefab_config.json  ߷2  ( 2y +p/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/prefab_config.json  2  ( 2y w -u/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/symbol_folder_index.txt  ߷2  l 2b +u/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/.cxx/Debug/6a58wo5c/x86_64/symbol_folder_index.txt  2  l 2b ` -^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  ߷2  Ў2 \ No newline at end of file +^/Users/hamzaaleghwairyeen/development/App/driver_sefer/android/app/src/main/cpp/CMakeLists.txt  2  Ў2 \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 73d5918..0b117bb 100755 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -24,34 +24,30 @@ - - + + + - - - @@ -62,85 +58,50 @@ android:name="com.yalantis.ucrop.UCropActivity" android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat.Light.NoActionBar" /> - - + - + - + - - - - + - - - - + - - - - - - - + android:exported="false"> @@ -148,9 +109,8 @@ - - + + + \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/sefer_driver/MainActivity.kt b/android/app/src/main/kotlin/com/sefer_driver/MainActivity.kt index 522e3df..1f9141c 100755 --- a/android/app/src/main/kotlin/com/sefer_driver/MainActivity.kt +++ b/android/app/src/main/kotlin/com/sefer_driver/MainActivity.kt @@ -1,6 +1,7 @@ package com.sefer_driver import android.app.AlertDialog +import android.content.Intent import android.os.Bundle import android.util.Log import android.widget.LinearLayout @@ -9,137 +10,129 @@ import android.widget.TextView import androidx.core.view.setPadding import com.scottyab.rootbeer.RootBeer import io.flutter.embedding.android.FlutterFragmentActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel import java.io.File import java.util.Timer import kotlin.concurrent.schedule -import io.flutter.embedding.engine.FlutterEngine // Import FlutterEngine -import io.flutter.plugin.common.MethodChannel // Import MethodChannel -import io.flutter.plugin.common.MethodCall // Import MethodCall -import io.flutter.plugin.common.MethodChannel.Result // Import Result class MainActivity : FlutterFragmentActivity() { - - private lateinit var channel: MethodChannel // Declare a MethodChannel variable + private val SECURITY_CHANNEL = "com.sefer_driver/security" + private val APP_CONTROL_CHANNEL = "com.sefer_driver/app_control" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) - // Initialize the MethodChannel - channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.sefer_driver/security") + // Channel for security checks (isRooted) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SECURITY_CHANNEL) + .setMethodCallHandler { call, result -> + when (call.method) { + "isNativeRooted" -> result.success(isDeviceCompromised()) + else -> result.notImplemented() + } + } - // Set a MethodCallHandler to handle method calls from Flutter - channel.setMethodCallHandler { call, result -> - when (call.method) { - "isNativeRooted" -> { - val isCompromised = isDeviceCompromised() - result.success(isCompromised) // Send the result back to Flutter + // Channel for app control (bringing to foreground) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, APP_CONTROL_CHANNEL) + .setMethodCallHandler { call, result -> + when (call.method) { + "bringToForeground" -> { + Log.d("MainActivity", "Received bringToForeground request") + val intent = + Intent(this, MainActivity::class.java).apply { + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_CLEAR_TOP or + Intent.FLAG_ACTIVITY_SINGLE_TOP + ) + } + try { + startActivity(intent) + Log.d( + "MainActivity", + "App brought to foreground successfully with flags: ${intent.flags}" + ) + result.success(true) + } catch (e: Exception) { + Log.e( + "MainActivity", + "Error bringing app to foreground: ${e.message}", + e + ) + result.error( + "ACTIVITY_START_FAILED", + e.message, + e.stackTraceToString() + ) + } + } + else -> result.notImplemented() + } } - else -> { - result.notImplemented() // Handle unknown method calls - } - } - } } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - // Perform all checks. The order can matter; you might want to prioritize - // the faster checks first. + Log.d("MainActivity", "MainActivity onCreate") if (isDeviceCompromised()) { showSecurityWarningDialog() } } + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + setIntent(intent) + Log.d("MainActivity", "Received new intent: ${intent.action}, flags: ${intent.flags}") + } + private fun isDeviceCompromised(): Boolean { return try { - nativeRootCheck() || rootBeerCheck() //|| !safetyNetCheck() + val isRootedByRootBeer = RootBeer(this).isRooted + Log.d("MainActivity", "Root check result: $isRootedByRootBeer") + isRootedByRootBeer } catch (e: Exception) { - Log.e("DEBUG", "Error during security checks: ${e.message}", e) - true // Consider the device compromised on error + Log.e("MainActivity", "Security check error: ${e.message}", e) + true // Fail-safe: assume compromised if check fails } } - private fun nativeRootCheck(): Boolean { - Log.d("DEBUG", "Starting native root detection...") - return try { - val isNativeRooted = RootDetection.isNativeRooted() - Log.d("DEBUG", "Native root detection result: $isNativeRooted") - isNativeRooted - } catch (e: Exception) { - Log.e("DEBUG", "Error in native root detection: ${e.message}", e) - true // Consider rooted on exception - } - } - - private fun rootBeerCheck(): Boolean { - Log.d("DEBUG", "Starting RootBeer root detection...") - val rootBeer = RootBeer(this) - val isRooted = rootBeer.isRooted - Log.d("DEBUG", "RootBeer detection result: $isRooted") - return isRooted - } - - private fun safetyNetCheck(): Boolean { - Log.d("DEBUG", "Starting SafetyNet check...") - var isSafe = false // Initialize a variable to store result - val semaphore = java.util.concurrent.Semaphore(0) // Create a semaphore - - SafetyNetCheck.checkSafetyNet(this, getString(R.string.api_key_safety)) { result -> - isSafe = result - Log.d("DEBUG", "SafetyNet check result: $isSafe") - semaphore.release() // Release the semaphore when the callback is executed - } - - try { - semaphore.acquire() // Wait for the callback to complete - } catch (e: InterruptedException) { - Log.e("DEBUG", "Interrupted while waiting for SafetyNet check", e) - return false // Or handle as appropriate for your app - } - return isSafe - } - private fun showSecurityWarningDialog() { - var secondsRemaining = 10 // Start at 10 seconds - - // Create views programmatically - val progressBar = ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal) - progressBar.max = 10 // Set max to 10 for 10 seconds - progressBar.progress = 10 // Start full - val textView = TextView(this) - textView.text = getString(R.string.security_warning_message) // Your message - textView.textAlignment = TextView.TEXT_ALIGNMENT_CENTER - - val layout = LinearLayout(this) - layout.orientation = LinearLayout.VERTICAL - layout.setPadding(48) - layout.addView(textView) - layout.addView(progressBar) - + var secondsRemaining = 10 + val progressBar = + ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal).apply { + max = 10 + progress = 10 + } + val textView = + TextView(this).apply { + text = getString(R.string.security_warning_message) + textAlignment = TextView.TEXT_ALIGNMENT_CENTER + } + val layout = + LinearLayout(this).apply { + orientation = LinearLayout.VERTICAL + setPadding(48) + addView(textView) + addView(progressBar) + } val dialog = - AlertDialog.Builder(this) - .setTitle(getString(R.string.security_warning_title)) // Your title - .setView(layout) // Set the custom layout - .setCancelable( - false - ) // Prevent dismissing by tapping outside or back button - .create() - + AlertDialog.Builder(this) + .setTitle(getString(R.string.security_warning_title)) + .setView(layout) + .setCancelable(false) + .create() dialog.show() - - // Use a Timer to update the progress bar and countdown val timer = Timer() - timer.schedule(0, 1000) { // Update every 1000ms (1 second) + timer.schedule(0, 1000) { secondsRemaining-- - runOnUiThread { // Update UI on the main thread - progressBar.progress = - secondsRemaining // Set the progress bar to show remaining seconds + runOnUiThread { + progressBar.progress = secondsRemaining if (secondsRemaining <= 0) { - timer.cancel() // Stop the timer - dialog.dismiss() // Dismiss the dialog - clearAppDataAndExit() // Clear data and exit + timer.cancel() + dialog.dismiss() + clearAppDataAndExit() } } } @@ -147,56 +140,35 @@ class MainActivity : FlutterFragmentActivity() { private fun clearAppDataAndExit() { try { - val packageName = applicationContext.packageName - val runtime = Runtime.getRuntime() - runtime.exec("pm clear $packageName") // Clear app data + Runtime.getRuntime().exec("pm clear $packageName") + Log.d("MainActivity", "Cleared app data via package manager") } catch (e: Exception) { + Log.e("MainActivity", "Error clearing app data: ${e.message}", e) clearCache() clearAppData() } - - finishAffinity() // Finish all activities from this app - System.exit(0) // Terminate the app's process + finishAffinity() + System.exit(0) } private fun clearCache() { - try { - val cacheDir = cacheDir - if (cacheDir != null && cacheDir.isDirectory) { - deleteDir(cacheDir) - } - val externalCacheDir = externalCacheDir - if (externalCacheDir != null && externalCacheDir.isDirectory) { - deleteDir(externalCacheDir) - } - } catch (e: Exception) { - Log.e("DEBUG", "Error clearing cache: ${e.message}", e) - } + deleteDir(cacheDir) + deleteDir(externalCacheDir) + Log.d("MainActivity", "Cleared cache directories") } private fun clearAppData() { - try { - val dataDir = applicationContext.dataDir - if (dataDir != null && dataDir.isDirectory) { - deleteDir(dataDir) - } - } catch (e: Exception) { - Log.e("DEBUG", "Error clearing app data: ${e.message}", e) - } + // Be careful with this, it deletes all app data. + // deleteDir(applicationContext.dataDir) + Log.d("MainActivity", "App data clearing skipped (commented out)") } private fun deleteDir(dir: File?): Boolean { if (dir != null && dir.isDirectory) { - val children = dir.list() - if (children != null) { - for (child in children) { - val success = deleteDir(File(dir, child)) - if (!success) { - return false - } - } - } + dir.list()?.forEach { deleteDir(File(dir, it)) } } - return dir?.delete() ?: false + val deleted = dir?.delete() ?: false + Log.d("MainActivity", "Deleted directory ${dir?.path}: $deleted") + return deleted } -} \ No newline at end of file +} diff --git a/lib/constant/box_name.dart b/lib/constant/box_name.dart index ef4799d..810fcab 100755 --- a/lib/constant/box_name.dart +++ b/lib/constant/box_name.dart @@ -6,6 +6,10 @@ class BoxName { static const String initializationVector = 'initializationVector'; static const String firstTimeLoadKey = 'firstTimeLoadKey'; static const String jwt = "jwt"; + static const String rideId = "rideId"; + static const String rideArgumentsFromBackground = + "rideArgumentsFromBackground"; + static const String FCM_PRIVATE_KEY = "FCM_PRIVATE_KEY"; static const String hmac = "hmac"; static const String fingerPrint = "fingerPrint"; static const String payMobApikey = "payMobApikey"; diff --git a/lib/constant/links.dart b/lib/constant/links.dart index 325ca61..a6bbae5 100755 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -101,6 +101,10 @@ class AppLink { static String getTripCountByCaptain = "$ride/rides/getTripCountByCaptain.php"; static String getRideOrderID = "$ride/rides/getRideOrderID.php"; static String getRideStatus = "$ride/rides/getRideStatus.php"; + static String getOverLayStatus = "$ride/overLay/get.php"; + static String getArgumentAfterAppliedFromBackground = + "$ride/overLay/getArgumentAfterAppliedFromBackground.php"; + static String addOverLayStatus = "$ride/overLay/add.php"; static String getapiKey = "$ride/apiKey/get.php"; static String getapiKeySefer = "$ride/apiKey/get.php"; @@ -123,6 +127,12 @@ class AppLink { ////-----------------DriverPayment------------------ static String addDrivePayment = "$seferPaymentServer/ride/payment/add.php"; + static String payWithPayMobCardDriver = + "$seferPaymentServer/ride/payMob/paymob_driver/payWithCard.php"; + static String payWithWallet = + "$seferPaymentServer/ride/payMob/paymob_driver/payWithWallet.php"; + static String paymetVerifyDriver = + "$seferPaymentServer/ride/payMob/paymob_driver/paymet_verfy.php"; static String updatePaymetToPaid = "$seferPaymentServer/ride/payment/updatePaymetToPaid.php"; diff --git a/lib/controller/auth/captin/invit_controller.dart b/lib/controller/auth/captin/invit_controller.dart index c8ea87f..b69546d 100755 --- a/lib/controller/auth/captin/invit_controller.dart +++ b/lib/controller/auth/captin/invit_controller.dart @@ -188,16 +188,16 @@ Download the Tripz app now and enjoy your ride! (driverInvitationData[index]['driverInviterId']), ('500'), ); - await Get.find() - .addSeferWallet('giftInvitation', ('-1000')); + // await Get.find() + // .addSeferWallet('giftInvitation', ('-1000')); NotificationCaptainController().addNotificationCaptain( driverInvitationData[index]['driverInviterId'].toString(), "You have got a gift for invitation".tr, - '${"You have 500".tr} ${'LE'}', + '${"You have 500".tr} ${'LE'.tr}', false); NotificationController().showNotification( "You have got a gift for invitation".tr, - '${"You have 500".tr} ${'LE'}', + '${"You have 500".tr} ${'LE'.tr}', 'tone1', ''); } else { @@ -264,6 +264,7 @@ Download the Tripz app now and enjoy your ride! link: AppLink.updatePassengerGift, payload: {'id': driverInvitationDataToPassengers[index]['id']}, ); + // Notify the inviter NotificationCaptainController().addNotificationCaptain( driverInvitationDataToPassengers[index]['passengerInviterId'] diff --git a/lib/controller/auth/captin/login_captin_controller.dart b/lib/controller/auth/captin/login_captin_controller.dart index a75e4fa..66beb5f 100755 --- a/lib/controller/auth/captin/login_captin_controller.dart +++ b/lib/controller/auth/captin/login_captin_controller.dart @@ -98,7 +98,7 @@ class LoginDriverController extends GetxController { final random = Random(); if (random.nextBool()) { - // await SecurityHelper.performSecurityChecks(); + await SecurityHelper.performSecurityChecks(); } else { await SecurityChecks.isDeviceRootedFromNative(Get.context!); } @@ -145,20 +145,21 @@ class LoginDriverController extends GetxController { ); if (response0.statusCode == 200) { final decodedResponse1 = jsonDecode(response0.body); - // Log.print('decodedResponse1: ${decodedResponse1}'); + Log.print('decodedResponse1: ${decodedResponse1}'); final jwt = decodedResponse1['jwt']; box.write(BoxName.jwt, X.c(X.c(X.c(jwt, cn), cC), cs)); // await box.write(BoxName.hmac, decodedResponse1['hmac']); - await AppInitializer().getAIKey(Driver.keyOfApp); - await AppInitializer().getAIKey(Driver.initializationVector); - await Future.delayed(Duration.zero); - await EncryptionHelper.initialize(); - await AppInitializer().getAIKey(Driver.payMobApikey); await AppInitializer().getAIKey(Driver.FCM_PRIVATE_KEY); + await AppInitializer().getAIKey(Driver.initializationVector); + await AppInitializer().getAIKey(Driver.keyOfApp); + + // ✅ بعد التأكد أن كل المفاتيح موجودة + await EncryptionHelper.initialize(); + await AppInitializer().getKey(); } else {} } else { diff --git a/lib/controller/firebase/access_token.dart b/lib/controller/firebase/access_token.dart index fc28ef8..e98edb4 100755 --- a/lib/controller/firebase/access_token.dart +++ b/lib/controller/firebase/access_token.dart @@ -44,7 +44,7 @@ class AccessTokenManager { _accessToken = client.credentials.accessToken; _expiryDate = client.credentials.accessToken.expiry; client.close(); - Log.print('_accessToken!.data: ${_accessToken!.data}'); + // Log.print('_accessToken!.data: ${_accessToken!.data}'); return _accessToken!.data; } catch (e) { throw Exception('Failed to obtain access token'); diff --git a/lib/controller/firebase/firbase_messge.dart b/lib/controller/firebase/firbase_messge.dart index d772bfb..9e1d12a 100755 --- a/lib/controller/firebase/firbase_messge.dart +++ b/lib/controller/firebase/firbase_messge.dart @@ -14,6 +14,7 @@ import '../../constant/box_name.dart'; import '../../constant/colors.dart'; import '../../constant/style.dart'; import '../../main.dart'; +import '../../print.dart'; import '../../views/auth/captin/criminal_documents_page.dart'; import '../../views/home/Captin/home_captain/home_captin.dart'; import '../../views/home/Captin/orderCaptin/order_speed_request.dart'; @@ -180,6 +181,7 @@ class FirebaseMessagesController extends GetxController { 'Trip Cancelled. The cost of the trip will be added to your wallet.' .tr, () { box.write(BoxName.rideStatus, 'Cancel'); + Log.print('rideStatus from 184 : ${box.read(BoxName.rideStatus)}'); Get.offAll(HomeCaptain()); }); // cancelTripDialog1(); @@ -341,6 +343,8 @@ class FirebaseMessagesController extends GetxController { title: 'Ok'.tr, onPressed: () { box.write(BoxName.rideStatus, 'Cancel'); + Log.print( + 'rideStatus from 347 : ${box.read(BoxName.rideStatus)}'); Get.offAll(HomeCaptain()); })); } @@ -356,6 +360,8 @@ class FirebaseMessagesController extends GetxController { title: 'Ok'.tr, onPressed: () { box.write(BoxName.rideStatus, 'Cancel'); + Log.print( + 'rideStatus from 364 : ${box.read(BoxName.rideStatus)}'); Get.offAll(HomeCaptain()); })); } @@ -405,7 +411,7 @@ class FirebaseMessagesController extends GetxController { Future onInit() async { super.onInit(); try { - var encryptedKey = await storage.read(key: 'FCM_PRIVATE_KEY'); + var encryptedKey = await storage.read(key: BoxName.FCM_PRIVATE_KEY); // Log.print('encryptedKey: ${encryptedKey}'); if (encryptedKey != null) { serviceAccountKeyJson = @@ -707,33 +713,40 @@ class FirebaseMessagesController extends GetxController { if (response.statusCode == 200) { print( 'Notification sent successfully. Status code: ${response.statusCode}'); - print('Response body: ${response.body}'); + } else if (response.statusCode == 404) { + // Handle UNREGISTERED token + final responseBody = jsonDecode(response.body); + final errorCode = responseBody['error']['details']?[0]?['errorCode']; + + if (errorCode == 'UNREGISTERED') { + print('Token is unregistered/invalid: $token'); + // Remove token from your database + await removeInvalidToken(token); + return; // Don't retry for invalid tokens + } } else { print( 'Failed to send notification. Status code: ${response.statusCode}'); - print('Response body: ${response.body}'); + if (retryCount > 0) { print('Retrying... Attempts remaining: $retryCount'); - await Future.delayed( - const Duration(seconds: 2)); // Optional delay before retrying - return sendNotificationToPassengerTokenCALL( - title, body, token, data, tone, + await Future.delayed(const Duration(seconds: 2)); + return sendNotificationToDriverMAP(title, body, token, data, tone, retryCount: retryCount - 1); } } } catch (e) { - print('Error sending notification: $e'); - if (retryCount > 0) { - print('Retrying... Attempts remaining: $retryCount'); - await Future.delayed( - const Duration(seconds: 2)); // Optional delay before retrying - return sendNotificationToPassengerTokenCALL( - title, body, token, data, tone, - retryCount: retryCount - 1); - } + // ... existing error handling ... } } + + Future removeInvalidToken(String token) async { + // Remove token from your database/storage + // This prevents future attempts to send to invalid tokens + print('Removing invalid token from database: $token'); + // Your database cleanup logic here + } } class OverlayContent extends StatelessWidget { diff --git a/lib/controller/firebase/local_notification.dart b/lib/controller/firebase/local_notification.dart index 78c38f9..ee79484 100755 --- a/lib/controller/firebase/local_notification.dart +++ b/lib/controller/firebase/local_notification.dart @@ -522,7 +522,7 @@ class NotificationController extends GetxController { if (data is String) { var orderData = jsonDecode(data); if (orderData is List && orderData.length == 34) { - closeOverLay(); + //closeOverLay(); Get.put(HomeCaptainController()).changeRideId(); Get.to(() => OrderRequestPage(), arguments: {'myListString': data}); } else { @@ -537,7 +537,7 @@ class NotificationController extends GetxController { if (data is String) { var orderData = jsonDecode(data); if (orderData is List && orderData.length == 34) { - closeOverLay(); + //closeOverLay(); Get.put(HomeCaptainController()).changeRideId(); Get.to(() => OrderRequestPage(), arguments: {'myListString': data}); } else { @@ -550,7 +550,7 @@ class NotificationController extends GetxController { void _handleADSNotification() { // var orderData = jsonDecode(data); - closeOverLay(); + //closeOverLay(); Get.to( () => const NotificationCaptain(), ); diff --git a/lib/controller/functions/crud.dart b/lib/controller/functions/crud.dart index b08d583..5c8f4f8 100755 --- a/lib/controller/functions/crud.dart +++ b/lib/controller/functions/crud.dart @@ -94,9 +94,9 @@ class CRUD { 'X-HMAC-Auth': hmac.toString(), }, ); - Log.print('response.request: ${response.request}'); - Log.print('response.body: ${response.body}'); - print(payload); + // Log.print('response.request: ${response.request}'); + // Log.print('response.body: ${response.body}'); + // print(payload); if (response.statusCode == 200) { var jsonData = jsonDecode(response.body); if (jsonData['status'] == 'success') { @@ -128,11 +128,13 @@ class CRUD { Future postWallet( {required String link, Map? payload}) async { var s = await LoginDriverController().getJwtWallet(); + // Log.print('jwt: ${s}'); final hmac = box.read(BoxName.hmac); - Log.print('hmac: ${hmac}'); + // Log.print('hmac: ${hmac}'); var url = Uri.parse(link); + // Log.print('url: ${url}'); try { - await LoginDriverController().getJWT(); + // await LoginDriverController().getJWT(); var response = await http.post( url, @@ -143,9 +145,9 @@ class CRUD { 'X-HMAC-Auth': hmac.toString(), }, ); - print(response.request); - Log.print('response.body: ${response.body}'); - print(payload); + // Log.print('response.request:${response.request}'); + // Log.print('response.body: ${response.body}'); + // Log.print('payload:$payload'); if (response.statusCode == 200) { try { var jsonData = jsonDecode(response.body); @@ -201,9 +203,9 @@ class CRUD { // 'Authorization': 'Bearer ${box.read(BoxName.jwt)}' }, ); - print(response.request); - Log.print('response.body: ${response.body}'); - print(payload); + // print(response.request); + // Log.print('response.body: ${response.body}'); + // print(payload); if (response.statusCode == 200) { try { var jsonData = jsonDecode(response.body); @@ -439,22 +441,46 @@ class CRUD { } } - sendEmail( - String link, - Map? payload, - ) async { - var headers = { + Future sendEmail(String link, Map? payload) async { + // التحقق من صلاحية التوكن + String rawJwt = box.read(BoxName.jwt); + String token = X + .r(X.r(X.r(rawJwt, cn), cC), cs) + .toString() + .split(AppInformation.addd)[0]; + + bool isTokenExpired = JwtDecoder.isExpired(token); + if (isTokenExpired) { + await LoginDriverController().getJWT(); + rawJwt = box.read(BoxName.jwt); // تحديث التوكن بعد التجديد + token = X + .r(X.r(X.r(rawJwt, cn), cC), cs) + .toString() + .split(AppInformation.addd)[0]; + } + + // إعداد الهيدر + final headers = { "Content-Type": "application/x-www-form-urlencoded", - 'Authorization': - 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}', + "Authorization": "Bearer $token", }; - var request = http.Request('POST', Uri.parse(link)); - request.bodyFields = payload!; + + // إعداد الطلب + final request = http.Request('POST', Uri.parse(link)); + request.bodyFields = payload ?? {}; request.headers.addAll(headers); - http.StreamedResponse response = await request.send(); + // إرسال الطلب + final response = await request.send(); + + // التحقق من النتيجة if (response.statusCode == 200) { - } else {} + print("✅ Email sent successfully."); + } else { + print("❌ Failed to send email. Status: ${response.statusCode}"); + final responseBody = await response.stream.bytesToString(); + print("Response body: $responseBody"); + } } Future postFromDialogue({ diff --git a/lib/controller/functions/gemeni.dart b/lib/controller/functions/gemeni.dart index 21a6acc..7f553c6 100755 --- a/lib/controller/functions/gemeni.dart +++ b/lib/controller/functions/gemeni.dart @@ -105,7 +105,7 @@ class AI extends GetxController { NotificationController().showNotification( "Code approved".tr, "Code approved".tr, 'tone2', ''); // Notification text with dynamic token - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP( 'You have received a gift token!'.tr, 'for '.tr + box.read(BoxName.phoneDriver).toString(), jsonDecode(res)['message'][0]['token'].toString(), diff --git a/lib/controller/functions/location_controller.dart b/lib/controller/functions/location_controller.dart index c7fd7dc..026aa49 100755 --- a/lib/controller/functions/location_controller.dart +++ b/lib/controller/functions/location_controller.dart @@ -133,8 +133,8 @@ class LocationController extends GetxController { // ✅ إدخال للسيرفر كل دقيقة _insertCounter++; - Log.print('_insertCounter: ${_insertCounter}'); - if (_insertCounter >= 12) { + // Log.print('_insertCounter: ${_insertCounter}'); + if (_insertCounter == 12) { _insertCounter = 0; await CRUD().post( link: box.read(BoxName.serverChosen) + '/ride/location/add.php', diff --git a/lib/controller/functions/package_info.dart b/lib/controller/functions/package_info.dart index 8595e69..b86dfcb 100755 --- a/lib/controller/functions/package_info.dart +++ b/lib/controller/functions/package_info.dart @@ -247,15 +247,15 @@ class SecurityHelper { await box.write('isTampered', isTampered); // Use await await box.write('isJailBroken', isJailBroken); // Use await - debugPrint("Security Check Results:"); - debugPrint("isNotTrust: $isNotTrust"); - debugPrint("isJailBroken: $isJailBroken"); - debugPrint("isRealDevice: $isRealDevice"); - debugPrint("isOnExternalStorage: $isOnExternalStorage"); - debugPrint("checkForIssues: $checkForIssues"); - debugPrint("isDevMode: $isDevMode"); - debugPrint("isTampered: $isTampered"); - debugPrint("Bundle ID: $bundleId"); // Print the bundle ID + // debugPrint("Security Check Results:"); + // debugPrint("isNotTrust: $isNotTrust"); + // debugPrint("isJailBroken: $isJailBroken"); + // debugPrint("isRealDevice: $isRealDevice"); + // debugPrint("isOnExternalStorage: $isOnExternalStorage"); + // debugPrint("checkForIssues: $checkForIssues"); + // debugPrint("isDevMode: $isDevMode"); + // debugPrint("isTampered: $isTampered"); + // debugPrint("Bundle ID: $bundleId"); // Print the bundle ID // Check for security risks and potentially show a warning if (isJailBroken || isRealDevice == false || isTampered) { diff --git a/lib/controller/functions/secure_storage.dart b/lib/controller/functions/secure_storage.dart index 9dd3b5c..2a383e6 100755 --- a/lib/controller/functions/secure_storage.dart +++ b/lib/controller/functions/secure_storage.dart @@ -40,7 +40,6 @@ class AppInitializer { Future initializeApp() async { if (box.read(BoxName.jwt) == null) { - Log.print('box.read(BoxName.jwt): ${box.read(BoxName.jwt)}'); await LoginDriverController().getJWT(); } else { bool isTokenExpired = JwtDecoder.isExpired(X @@ -55,15 +54,18 @@ class AppInitializer { // await getKey(); } - getAIKey(String key1) async { - if (box.read(BoxName.firstTimeLoadKey) == null) { - var res = - await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key1}); - if (res != 'failure') { - var d = jsonDecode(res)['message']; - await storage.write(key: key1, value: d[key1].toString()); - await Future.delayed(Duration.zero); - } else {} + Future getAIKey(String key1) async { + var res = + await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key1}); + + if (res != 'failure') { + var d = jsonDecode(res)['message']; + final rawValue = d[key1].toString(); + + // ✅ اكتبها في storage + await storage.write(key: key1, value: rawValue); + + await Future.delayed(Duration.zero); } } diff --git a/lib/controller/home/captin/home_captain_controller.dart b/lib/controller/home/captin/home_captain_controller.dart index 4837938..10209b0 100755 --- a/lib/controller/home/captin/home_captain_controller.dart +++ b/lib/controller/home/captin/home_captain_controller.dart @@ -12,8 +12,10 @@ import '../../../constant/links.dart'; import '../../../constant/style.dart'; import '../../../constant/table_names.dart'; import '../../../main.dart'; +import '../../../print.dart'; import '../../../views/home/my_wallet/walet_captain.dart'; import '../../../views/widgets/elevated_btn.dart'; +import '../../firebase/firbase_messge.dart'; import '../../functions/crud.dart'; import '../../functions/location_background_controller.dart'; import '../../functions/location_controller.dart'; @@ -264,7 +266,7 @@ class HomeCaptainController extends GetxController { @override void onInit() async { // await locationBackController.requestLocationPermission(); - + Get.put(FirebaseMessagesController()); addToken(); await getlocation(); onButtonSelected(); @@ -429,6 +431,7 @@ class HomeCaptainController extends GetxController { void changeToAppliedRide(String status) { box.write(BoxName.rideStatus, status); + Log.print('rideStatus from homcaptain : ${box.read(BoxName.rideStatus)}'); update(); } diff --git a/lib/controller/home/captin/map_driver_controller.dart b/lib/controller/home/captin/map_driver_controller.dart index a04e7f7..07ab0a5 100755 --- a/lib/controller/home/captin/map_driver_controller.dart +++ b/lib/controller/home/captin/map_driver_controller.dart @@ -25,6 +25,7 @@ import '../../firebase/firbase_messge.dart'; import '../../functions/crud.dart'; import '../../functions/encrypt_decrypt.dart'; import '../../functions/location_controller.dart'; +import '../../functions/tts.dart'; class MapDriverController extends GetxController { bool isLoading = true; @@ -174,7 +175,7 @@ class MapDriverController extends GetxController { cancelTripFromDriverAfterApplied() async { if (formKeyCancel.currentState!.validate()) { box.write(BoxName.statusDriverLocation, 'off'); - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( "Cancel Trip from driver", "Trip Cancelled from driver. We are looking for a new driver. Please wait." .tr, @@ -235,6 +236,8 @@ class MapDriverController extends GetxController { 'created_at': DateTime.now().toString(), 'driver_id': box.read(BoxName.driverID).toString(), }, TableName.driverOrdersRefuse); + box.write(BoxName.rideStatus, 'Cancel'); + Log.print('rideStatus from map 240 : ${box.read(BoxName.rideStatus)}'); Get.find().getRefusedOrderByCaptain(); Get.offAll(() => HomeCaptain()); } @@ -242,6 +245,7 @@ class MapDriverController extends GetxController { void startTimerToShowPassengerInfoWindowFromDriver() async { if (box.read(BoxName.rideStatus) == 'Begin') { + Log.print('rideStatus from map 248 : ${box.read(BoxName.rideStatus)}'); isPassengerInfoWindow = false; } else { isPassengerInfoWindow = true; @@ -298,6 +302,7 @@ class MapDriverController extends GetxController { void driverGoToPassenger() async { changeRideToBeginToPassenger(); box.write(BoxName.rideStatus, 'Applied'); + Log.print('rideStatus from map 304 : ${box.read(BoxName.rideStatus)}'); update(); await CRUD().post( link: "${AppLink.seferCairoServer}/ride/rides/update.php", @@ -315,7 +320,7 @@ class MapDriverController extends GetxController { } // Get.find().changeToAppliedRide('Applied'); - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'Driver Is Going To Passenger'.tr, box.read(BoxName.nameDriver).toString(), //todo name driver tokenPassenger, @@ -392,6 +397,7 @@ class MapDriverController extends GetxController { // todo ride details // Get.find().changeToAppliedRide('Begin'); box.write(BoxName.rideStatus, 'Begin'); + Log.print('rideStatus from map 399 : ${box.read(BoxName.rideStatus)}'); // Get.find().update(); update(); await CRUD().post(link: AppLink.updateRides, payload: { @@ -418,7 +424,7 @@ class MapDriverController extends GetxController { 'status': 'Begin' }); } - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'Trip is Begin'.tr, box.read(BoxName.nameDriver).toString(), tokenPassenger, @@ -584,17 +590,29 @@ class MapDriverController extends GetxController { } } - void finishRideFromDriver() { + Future finishRideFromDriver() async { double distanceToDestination = Geolocator.distanceBetween( - latLngPassengerLocation.latitude, - latLngPassengerLocation.longitude, + latLngPassengerDestination.latitude, + latLngPassengerDestination.longitude, Get.find().myLocation.latitude, Get.find().myLocation.longitude, ); + final originalDistanceM = double.parse(distance.toString()) * 1000; - if (distanceToDestination > (double.parse(distance.toString()) / 3)) { - Log.print('distanceToDestination: ${distanceToDestination}'); + // 2. احسب المسافة التي قطعها السائق حتى الآن + final movedDistanceM = originalDistanceM - distanceToDestination; + // 3. عتبة ثلث المسافة + final oneThirdDistanceM = originalDistanceM / 3; + + // Logging للتتبع + Log.print('originalDistanceM: $originalDistanceM'); + Log.print('distanceToDestinationM: $distanceToDestination'); + Log.print('movedDistanceM: $movedDistanceM'); + Log.print('oneThirdDistanceM: $oneThirdDistanceM'); + + // 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد + if (movedDistanceM < oneThirdDistanceM) { MyDialog().getDialog( 'Are you sure to exit ride?'.tr, '', @@ -603,33 +621,39 @@ class MapDriverController extends GetxController { finishRideFromDriver1(); }, ); - } else { + } ///// + + else { + final textToSpeechController = Get.put(TextToSpeechController()); MyDialog().getDialog( "You haven't moved sufficiently!".tr, '', () => Get.back(), ); + await textToSpeechController + .speakText("You haven't moved sufficiently!".tr); } } String paymentToken = ''; Future generateTokenDriver(String amount) async { - var res = await CRUD().post(link: AppLink.addPaymentTokenDriver, payload: { + var res = + await CRUD().postWallet(link: AppLink.addPaymentTokenDriver, payload: { 'driverID': box.read(BoxName.driverID).toString(), 'amount': amount.toString(), }); - var d = jsonDecode(res); + var d = (res); return d['message']; } String paymentTokenPassenger = ''; Future generateTokenPassenger(String amount) async { - var res = - await CRUD().post(link: AppLink.addPaymentTokenPassenger, payload: { + var res = await CRUD() + .postWallet(link: AppLink.addPaymentTokenPassenger, payload: { 'passengerId': passengerId, 'amount': amount.toString(), }); - var d = jsonDecode(res); + var d = (res); return d['message']; } @@ -638,6 +662,7 @@ class MapDriverController extends GetxController { isRideStarted = false; isPriceWindow = false; box.write(BoxName.rideStatus, 'Finished'); + Log.print('rideStatus from map 664 : ${box.read(BoxName.rideStatus)}'); // Calculate totalCost more concisely if (price < 20) { @@ -699,7 +724,8 @@ class MapDriverController extends GetxController { if (walletChecked == 'true') { paymentToken = await generateTokenPassenger( ((-1) * double.parse(paymentAmount)).toString()); - futures.add(CRUD().post(link: AppLink.addPassengersWallet, payload: { + futures + .add(CRUD().postWallet(link: AppLink.addPassengersWallet, payload: { 'passenger_id': (passengerId), 'balance': ((-1) * double.parse(paymentAmount)).toString(), 'token': paymentToken, @@ -707,7 +733,7 @@ class MapDriverController extends GetxController { } paymentToken = await generateTokenDriver(paymentAmount.toString()); - futures.add(CRUD().post(link: AppLink.addDrivePayment, payload: { + futures.add(CRUD().postWallet(link: AppLink.addDrivePayment, payload: { 'rideId': (rideId), 'amount': paymentAmount, 'payment_method': @@ -720,7 +746,8 @@ class MapDriverController extends GetxController { if (double.parse(passengerWalletBurc) < 0) { final paymentToken1 = await generateTokenPassenger( ((-1) * double.parse(passengerWalletBurc)).toString()); - futures.add(CRUD().post(link: AppLink.addPassengersWallet, payload: { + futures + .add(CRUD().postWallet(link: AppLink.addPassengersWallet, payload: { 'passenger_id': (passengerId), 'token': paymentToken1, 'balance': ((-1) * double.parse(passengerWalletBurc)).toString() @@ -730,7 +757,8 @@ class MapDriverController extends GetxController { double pointsSubtraction = double.parse(paymentAmount) * (-1) * 0.08; final paymentToken2 = await generateTokenDriver((pointsSubtraction).toStringAsFixed(0)); - futures.add(CRUD().post(link: AppLink.addDriversWalletPoints, payload: { + futures + .add(CRUD().postWallet(link: AppLink.addDriversWalletPoints, payload: { 'paymentID': 'rideId${(rideId)}', 'amount': (pointsSubtraction).toStringAsFixed(0), 'paymentMethod': paymentMethod, @@ -743,7 +771,7 @@ class MapDriverController extends GetxController { Get.put(DriverBehaviorController()).sendSummaryToServer(driverId, rideId); // Send notification (this likely depends on previous steps) - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( "Driver Finish Trip".tr, '${'you will pay to Driver'.tr} $paymentAmount \$', tokenPassenger, @@ -862,7 +890,7 @@ class MapDriverController extends GetxController { // 'driverID': box.read(BoxName.driverID).toString(), // }); // Future.delayed(const Duration(milliseconds: 300)); - // FirebaseMessagesController().sendNotificationToDriverMAP( + // Get.find().sendNotificationToDriverMAP( // "Driver Finish Trip".tr, // '${'you will pay to Driver'.tr} $paymentAmount \$', // tokenPassenger, @@ -1362,7 +1390,8 @@ class MapDriverController extends GetxController { void onInit() async { mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY); // Get the passenger location from the arguments. - // await argumentLoading(); + await argumentLoading(); + Get.put(FirebaseMessagesController()); runGoogleMapDirectly(); addCustomCarIcon(); addCustomPassengerIcon(); diff --git a/lib/controller/home/payment/captain_wallet_controller.dart b/lib/controller/home/payment/captain_wallet_controller.dart index e4669cc..1262d6b 100755 --- a/lib/controller/home/payment/captain_wallet_controller.dart +++ b/lib/controller/home/payment/captain_wallet_controller.dart @@ -82,7 +82,7 @@ class CaptainWalletController extends GetxController { //get new driver details isNewTransfer = true; update(); - var res = await CRUD().getWallet( + var res = await CRUD().get( link: AppLink.getDriverDetails, payload: {'driver_phone': '+2${newDriverPhoneController.text}'}); isNewTransfer = false; @@ -155,7 +155,7 @@ class CaptainWalletController extends GetxController { 'driverID': box.read(BoxName.driverID).toString(), 'amount': amount.toString(), }); - var d = jsonDecode(res); + var d = (res); return d['message']; } @@ -168,14 +168,14 @@ class CaptainWalletController extends GetxController { 'amount': amount.toString(), 'payment_method': paymentMethod.toString(), }); - var d = jsonDecode(res); + var d = (res); // paymentID = d['message'].toString(); return d['message']; } Future addDriverWallet(String paymentMethod, point, count) async { paymentToken = await generateToken(count); - var paymentID = await getPaymentId(paymentMethod, point); + var paymentID = await getPaymentId(paymentMethod, point.toString()); await CRUD().postWallet(link: AppLink.addDriversWalletPoints, payload: { 'driverID': box.read(BoxName.driverID).toString(), 'paymentID': paymentID.toString(), @@ -245,7 +245,8 @@ class CaptainWalletController extends GetxController { 'payment_method': paymentMethod.toString(), 'passengerID': paymentMethod, }); - await addSeferWallet(paymentMethod, (double.parse(point) * -1).toString()); + await addSeferWallet(paymentMethod, + (double.parse(point) * -2).toString()); // deduct 2 from sefer wallet } Future addSeferWallet(String paymentMethod, String point) async { @@ -290,7 +291,7 @@ class CaptainWalletController extends GetxController { 'paymentMethod': paymentMethod2.toString(), }); if (res1 != 'failure') { - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'Transfer', '${'You have transfer to your wallet from'.tr}' '${box.read(BoxName.nameDriver)}', diff --git a/lib/controller/home/payment/paymob_payout.dart b/lib/controller/home/payment/paymob_payout.dart index 3ca1676..26029b5 100755 --- a/lib/controller/home/payment/paymob_payout.dart +++ b/lib/controller/home/payment/paymob_payout.dart @@ -1,15 +1,12 @@ import 'dart:convert'; import 'package:local_auth/local_auth.dart'; -import 'package:sefer_driver/constant/api_key.dart'; import 'package:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/main.dart'; import 'package:sefer_driver/views/widgets/error_snakbar.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; -import 'package:secure_string_operations/secure_string_operations.dart'; -import '../../../constant/char_map.dart'; import '../../../constant/links.dart'; import '../../../views/widgets/mydialoug.dart'; import '../../functions/crud.dart'; @@ -19,39 +16,8 @@ class PaymobPayout extends GetxController { bool isLoading = false; String dropdownValue = 'etisalat'; - Future getToken() async { - var headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - // 'Cookie': - // 'csrftoken=74iZJ8XYyuTm5WRq2W4tpWX5eqoJLZVK5QhuDrChWpDtzpgGA269bbCWuEcW85t4' - }; - var payMobOutClientSecrret = - X.r(X.r(X.r(await getAIKey(KN.pmobsec), cn), cC), cs); - var payMobOutClientId = - X.r(X.r(X.r(await getAIKey(KN.pmobid), cn), cC), cs); - var body = { - 'grant_type': 'password', - 'username': AK.payMobOutUserName, - 'password': AK.payMobOutPassword, - 'client_id': payMobOutClientId, - 'client_secret': payMobOutClientSecrret - }; - var res = await http.post( - Uri.parse('https://payouts.paymobsolutions.com/api/secure/o/token/'), - headers: headers, - body: body, - ); - String token = ''; - if (res.statusCode == 200) { - var decode = jsonDecode(res.body); - token = decode['access_token']; - } - return token; - } - int payOutFee = 5; - payToDriverWallet( - String token, String amount, String issuer, String msisdn) async { + payToDriverWallet(String amount, String issuer, String msisdn) async { bool isAvailable = await LocalAuthentication().isDeviceSupported(); if (isAvailable) { // Authenticate the user @@ -62,21 +28,18 @@ class PaymobPayout extends GetxController { sensitiveTransaction: true, )); if (didAuthenticate) { - var headers = { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }; - var body = json.encode({ - "amount": amount, //"10.00", - "issuer": issuer, //"vodafone", - "msisdn": msisdn, // "01023456789" - }); - var res = await http.post( - Uri.parse('https://payouts.paymobsolutions.com/api/secure/disburse/'), - headers: headers, - body: body, - ); - var dec = jsonDecode(res.body); + var dec = await CRUD().postWallet( + link: + 'wl.tripz-egypt.com/v1/main/ride/payMob/paymob_driver/paymob_payout.php', + payload: { + "issuer": issuer, + "method": "wallet", + "amount": amount, //9.0, + "full_name": + '${box.read(BoxName.nameDriver)} ${box.read(BoxName.lastNameDriver)}', + "msisdn": msisdn, //"01010101010", + "bank_transaction_type": "cash_transfer" + }); if (dec['disbursement_status'] == 'successful') { var paymentToken = await Get.find() .generateToken( @@ -149,8 +112,8 @@ class PaymobPayout extends GetxController { } else {} } - payToDriverBankAccount(String token, String amount, String bankCardNumber, - String bankCode) async { + payToDriverBankAccount( + String amount, String bankCardNumber, String bankCode) async { bool isAvailable = await LocalAuthentication().isDeviceSupported(); if (isAvailable) { // Authenticate the user @@ -161,12 +124,9 @@ class PaymobPayout extends GetxController { sensitiveTransaction: true, )); if (didAuthenticate) { - var headers = { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }; var body = { "issuer": "bank_card", + "method": "bank_card", "amount": amount, //9.0, "full_name": '${box.read(BoxName.nameDriver)} ${box.read(BoxName.lastNameDriver)}', @@ -174,12 +134,11 @@ class PaymobPayout extends GetxController { "bank_code": bankCode, //"CIB", "bank_transaction_type": "cash_transfer" }; - var res = await http.post( - Uri.parse('https://payouts.paymobsolutions.com/api/secure/disburse/'), - headers: headers, - body: body, + var dec = await CRUD().postWallet( + link: + 'wl.tripz-egypt.com/v1/main/ride/payMob/paymob_driver/paymob_payout.php', + payload: body, ); - var dec = jsonDecode(res.body); if (dec['disbursement_status'] == 'successful') { var paymentToken = await Get.find() .generateToken( @@ -236,8 +195,7 @@ class PaymobPayout extends GetxController { sensitiveTransaction: true, )); if (didAuthenticate) { - String token = await getToken(); - await payToDriverWallet(token, amount, issuer, msisdn); + await payToDriverWallet(amount, issuer, msisdn); } else { MyDialog().getDialog('Authentication failed'.tr, ''.tr, () { Get.back(); @@ -263,8 +221,7 @@ class PaymobPayout extends GetxController { sensitiveTransaction: true, )); if (didAuthenticate) { - String token = await getToken(); - await payToDriverBankAccount(token, amount, bankCardNumber, bankCode); + await payToDriverBankAccount(amount, bankCardNumber, bankCode); } else { MyDialog().getDialog('Authentication failed'.tr, ''.tr, () { Get.back(); diff --git a/lib/controller/local/translations.dart b/lib/controller/local/translations.dart index d83ebaa..3f3dfef 100755 --- a/lib/controller/local/translations.dart +++ b/lib/controller/local/translations.dart @@ -65,6 +65,17 @@ class MyTranslation extends Translations { "Order Applied": "تم تطبيق الطلب", //firebase above + + "payment_success": "تمت العملية بنجاح", + "transaction_id": "رقم العملية", + "amount_paid": "المبلغ المدفوع", + "bonus_added": "البونص المضاف", + "points": "نقطة", + "transaction_failed": "فشل العملية", + "connection_failed": "فشل الاتصال", + "server_error": "خطأ في الخادم", + "server_error_message": "حدث خطأ أثناء الاتصال بالخادم", + "cancel": "إلغاء", "Syria": "‏سوريا", "Security Warning": "تحذير أمني", "Potential security risks detected. The application will close in @seconds seconds.": @@ -75,7 +86,28 @@ class MyTranslation extends Translations { "Security Warning": "⚠️ تحذير أمني", "Potential security risks detected. The application may not function correctly.": "تم اكتشاف ثغرات أمنية على هذا الجهاز. للحفاظ على أمان بياناتك، سيتم حذف جميع البيانات وإغلاق التطبيق.", + "How to use Tripz": "كيفية استخدام Tripz", + "What are the order details we provide to you?": + "ما هي تفاصيل الطلب التي نوفرها لك؟", + "Tripz Wallet Features:\n\nTransfer money multiple times.\nTransfer to anyone.\nMake purchases.\nCharge your account.\nCharge a friend's Tripz account.\nStore your money with us and receive it in your bank as a monthly salary.": + "ميزات محفظة Tripz:\n\nتحويل الأموال عدة مرات.\nالتحويل إلى أي شخص.\nإجراء عمليات شراء.\nشحن حسابك.\nشحن حساب Tripz لصديق.\nقم بتخزين أموالك معنا واستلامها في بنكك كراتب شهري.", + "What is the feature of our wallet?": "ما هي مميزات محفظتنا؟", + "What is Types of Trips in Tripz?": "ما هي أنواع الرحلات في Tripz؟", + '''Types of Trips in Tripz: +Comfort: For cars newer than 2017 with air conditioning. +Lady: For girl drivers. +Speed: For fixed salary and endpoints. +Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements. +Raih Gai: For same-day return trips longer than 50km. +''': """أنواع الرحلات في Tripz: + +راحة: للسيارات الأحدث من 2017 مع تكييف الهواء. +للسائقات الإناث. +سبيد: براتب ثابت ونقاط نهاية محددة. +مشاوير: للرحلات المرنة حيث يختار الركاب السيارة والسائق باتفاق مسبق. +رحّي غاي: للرحلات ذات العودة في نفس اليوم التي تزيد عن 50 كم. +""", "I will go now": "هروح دلوقتي", "Yes": "أيوة", "No,I want": "لا، أنا عاوز", diff --git a/lib/controller/rate/rate_conroller.dart b/lib/controller/rate/rate_conroller.dart index 0c32825..c29d8f2 100755 --- a/lib/controller/rate/rate_conroller.dart +++ b/lib/controller/rate/rate_conroller.dart @@ -67,7 +67,7 @@ class RateController extends GetxController { 'driverID': box.read(BoxName.driverID).toString(), }); - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'Wallet Added'.tr, 'Wallet Added${(remainingFee).toStringAsFixed(0)}'.tr, Get.find().tokenPassenger, diff --git a/lib/main.dart b/lib/main.dart index e0d5df4..6fe9d86 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'dart:math'; -import 'package:sefer_driver/constant/box_name.dart'; -import 'package:sefer_driver/controller/payment/paymob/paymob_response.dart'; import 'package:sefer_driver/views/home/Captin/orderCaptin/order_request_page.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -15,103 +12,102 @@ import 'package:flutter_stripe/flutter_stripe.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:flutter/services.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'constant/api_key.dart'; import 'constant/info.dart'; -import 'constant/notification.dart'; import 'controller/firebase/firbase_messge.dart'; import 'controller/firebase/local_notification.dart'; import 'controller/functions/encrypt_decrypt.dart'; -import 'controller/functions/location_controller.dart'; import 'controller/functions/secure_storage.dart'; import 'controller/local/local_controller.dart'; import 'controller/local/translations.dart'; -import 'controller/payment/paymob/paymob_wallet.dart'; import 'firebase_options.dart'; import 'models/db_sql.dart'; import 'print.dart'; import 'splash_screen_page.dart'; +import 'views/home/Captin/driver_map_page.dart'; import 'views/home/Captin/orderCaptin/order_over_lay.dart'; +//--- Global Variables --- final box = GetStorage(); const storage = FlutterSecureStorage(); -final PaymobPayment paymobPayment = PaymobPayment(); -final PaymobPaymentWallet paymobPaymentWallet = PaymobPaymentWallet(); DbSql sql = DbSql.instance; - final GlobalKey navigatorKey = GlobalKey(); +const platform = MethodChannel('com.sefer_driver/app_control'); + +//--- Entry Points for Background/Terminated States --- @pragma('vm:entry-point') Future backgroundMessageHandler(RemoteMessage message) async { WidgetsFlutterBinding.ensureInitialized(); - await Firebase.initializeApp(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + await GetStorage.init(); + if (!await FlutterOverlayWindow.isPermissionGranted()) { - // بإمكانك تجاهل الطلب في الخلفية والاكتفاء بالتنبيه Log.print("Overlay permission not granted; showing only notification."); } + if (Platform.isAndroid) { if (message.notification != null && message.notification!.title != null) { Log.print('message.notification!.title: ${message.notification!.title}'); - if (message.notification?.title == 'طلب' || + if (message.notification?.title == 'Order' || message.notification?.title == 'OrderSpeed') { var myListString = message.data['DriverList'] ?? '[]'; Log.print('myListString: $myListString'); - // Decode the JSON string to a list List myList; try { myList = jsonDecode(myListString) as List; - Log.print('myList: ${myList}'); } catch (e) { Log.print('Error decoding JSON: $e'); myList = []; } - await Future.delayed(const Duration(seconds: 1)); - bool isOverlayActive = await FlutterOverlayWindow.isActive(); if (isOverlayActive) { - await FlutterOverlayWindow.closeOverlay(); + await FlutterOverlayWindow.shareData(myList); + } else { + await FlutterOverlayWindow.showOverlay( + enableDrag: true, + flag: OverlayFlag.focusPointer, + positionGravity: PositionGravity.auto, + height: 1400, + width: WindowSize.matchParent, + startPosition: const OverlayPosition(0, -30), + ); + await FlutterOverlayWindow.shareData(myList); } - await FlutterOverlayWindow.shareData(myList); - await FlutterOverlayWindow.showOverlay( - enableDrag: true, - flag: OverlayFlag.focusPointer, - // visibility: NotificationVisibility.visibilityPublic, - positionGravity: PositionGravity.auto, - height: 1300, - width: WindowSize.matchParent, - startPosition: const OverlayPosition(0, -40), - ); + + // It's better to manage notifications in one place if possible + // but this is fine if it works for you. NotificationController().showNotification( message.notification!.title.toString(), message.notification!.body.toString(), 'order', myListString, ); - - await FlutterOverlayWindow.shareData(myList); + } else { + // Handle other types of notifications + FirebaseMessagesController().fireBaseTitles(message); } - } else { - FirebaseMessagesController().fireBaseTitles(message); } } } @pragma('vm:entry-point') void notificationTapBackground(NotificationResponse notificationResponse) { - // handle background notification taps here - print('Notification tapped in background!'); + Log.print('Notification tapped in background!'); NotificationController().handleNotificationResponse(notificationResponse); - // You can add your logic here to handle the notification tap } @pragma('vm:entry-point') void overlayMain() async { WidgetsFlutterBinding.ensureInitialized(); - + await GetStorage.init(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); runApp(const MaterialApp( debugShowCheckedModeBanner: false, home: OrderOverlay(), @@ -119,12 +115,12 @@ void overlayMain() async { } Future closeOverLay() async { - // FlutterOverlayWindow.closeOverlay(); bool isOverlayActive = await FlutterOverlayWindow.isActive(); if (isOverlayActive) { await FlutterOverlayWindow.closeOverlay(); } } +//--- Main Application --- void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -136,21 +132,29 @@ void main() async { DeviceOrientation.portraitDown, ]); - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override _MyAppState createState() => _MyAppState(); } -class _MyAppState extends State { +class _MyAppState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); _initApp(); } + @override + void dispose() { + super.dispose(); + } + Future _initApp() async { try { await Firebase.initializeApp( @@ -159,62 +163,23 @@ class _MyAppState extends State { await initializer.initializeApp(); await EncryptionHelper.initialize(); - if (Platform.isAndroid || Platform.isIOS) { - Get.put( - NotificationController()); // Initialize NotificationController here - await FirebaseMessaging.instance.requestPermission(); - FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); - await NotificationController().initNotifications(); + Get.put(NotificationController()); + Get.put(FirebaseMessagesController()); - // Generate a random index to pick a message - final random = Random(); - final randomMessage = - driverMessages[random.nextInt(driverMessages.length)]; - // Schedule the notification with the random message - NotificationController().scheduleNotificationsForSevenDays( - randomMessage.split(':')[0], - randomMessage.split(':')[1], - "ding", - ); + await FirebaseMessaging.instance.requestPermission(); + FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); + await NotificationController().initNotifications(); - await Future.wait([ - FirebaseMessagesController().getNotificationSettings(), - FirebaseMessagesController().getToken(), - ]); + // You can add your other initializations here + // For example: - // PermissionStatus status1 = await Permission.location.status; - // if (status1.isGranted) { - // await LocationController().startLocationUpdates(); - // } - } - - String? key = (await storage.read(key: BoxName.payMobApikey)); - if (key != null) { - String? apiKey = (key); - if (apiKey != null) { - PaymobPayment.instance.initialize( - // Use .instance - apiKey: apiKey, - integrationID: int.parse(AK.integrationIdPayMob), - userTokenExpiration: 200, - iFrameID: 837992, - ); - - PaymobPaymentWallet.instance.initialize( - // Use .instance - apiKey: apiKey, - integrationID: int.parse(AK.integrationIdPayMobWallet), - userTokenExpiration: 200, - iFrameID: 837992, - ); - } - } + // Your other startup logic... } catch (e) { - print("Error initializing app: $e"); - // Handle initialization errors appropriately. Maybe show an error message to the user. + Log.print("Error during _initApp: $e"); } } + //--- Build Method --- @override Widget build(BuildContext context) { LocaleController localController = Get.put(LocaleController()); @@ -231,105 +196,11 @@ class _MyAppState extends State { GetPage( name: '/order-page', page: () => OrderRequestPage(), - arguments: box.read(BoxName.rideArguments), ), + GetPage( + name: '/passenger-location-map', + page: () => PassengerLocationMapPage()), ], ); } } - -// void main() async { -// WidgetsFlutterBinding.ensureInitialized(); -// await WakelockPlus.enable(); -// -// await GetStorage.init(); -// -// final AppInitializer initializer = AppInitializer(); -// -// await initializer.initializeApp(); -// await Future.delayed(Duration.zero); -// await EncryptionHelper.initialize(); -// -// Stripe.publishableKey = AK.publishableKeyStripe; -// -// PermissionStatus status1 = await Permission.location.status; -// // if (status1.isGranted) { -// await LocationController().startLocationUpdates(); -// // } -// -// if (Platform.isAndroid || Platform.isIOS) { -// NotificationController notificationController = -// Get.put(NotificationController()); -// await Firebase.initializeApp( -// options: DefaultFirebaseOptions.currentPlatform, -// ); -// await FirebaseMessagesController().requestFirebaseMessagingPermission(); -// -// FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); -// -// await notificationController.initNotifications(); -// -// // Generate a random index to pick a message -// final random = Random(); -// final randomMessage = driverMessages[random.nextInt(driverMessages.length)]; -// -// // Schedule the notification with the random message -// notificationController.scheduleNotificationsForSevenDays( -// randomMessage.split(':')[0], -// randomMessage.split(':')[1], -// "ding", -// ); -// -// await Future.wait([ -// FirebaseMessagesController().getNotificationSettings(), -// FirebaseMessagesController().getToken(), -// ]); -// // await FacebookAuth.instance.init(); -// SystemChrome.setPreferredOrientations([ -// DeviceOrientation.portraitUp, -// DeviceOrientation.portraitDown, -// ]); -// } -// -// String? key = (await storage.read(key: BoxName.payMobApikey)); -// -// String? apiKey = EncryptionHelper.instance.decryptData(key!); -// PaymobPayment.instance.initialize( -// apiKey: apiKey, -// integrationID: int.parse(AK.integrationIdPayMob), -// userTokenExpiration: 200, -// iFrameID: 837992, -// ); -// -// PaymobPaymentWallet.instance.initialize( -// apiKey: apiKey, -// integrationID: int.parse(AK.integrationIdPayMobWallet), -// userTokenExpiration: 200, -// iFrameID: 837992, -// ); -// runApp(MyApp()); -// } -// -// class MyApp extends StatelessWidget { -// @override -// Widget build(BuildContext context) { -// LocaleController localController = Get.put(LocaleController()); -// return GetMaterialApp( -// navigatorKey: navigatorKey, -// title: AppInformation.appName, -// translations: MyTranslation(), -// debugShowCheckedModeBanner: false, -// locale: localController.language, -// theme: localController.appTheme, -// initialRoute: '/', -// getPages: [ -// GetPage(name: '/', page: () => SplashScreen()), -// GetPage( -// name: '/order-page', -// page: () => OrderRequestPage(), -// arguments: box.read(BoxName.rideArguments), -// ), -// ], -// ); -// } -// } diff --git a/lib/models/model/order_data.dart b/lib/models/model/order_data.dart index f75530d..62ea9d9 100755 --- a/lib/models/model/order_data.dart +++ b/lib/models/model/order_data.dart @@ -1,16 +1,18 @@ -// lib/models/order_data.dart +// lib/models/model/order_data.dart class OrderData { final String customerName; - final double tripDistanceKm; // المسافة الكلية للرحلة بالكيلومتر + final String customerToken; + final double tripDistanceKm; // The total trip distance in kilometers final String price; final String startLocationAddress; final String endLocationAddress; - final double distanceToPassengerKm; // المسافة إلى الراكب بالكيلومتر - final int tripDurationMinutes; // مدة الرحلة الكلية بالدقائق (مقربة لأعلى) + final double + distanceToPassengerKm; // The distance to the passenger in kilometers + final int tripDurationMinutes; // Total trip duration in minutes (rounded up) final int - durationToPassengerMinutes; // المدة إلى الراكب بالدقائق (مقربة لأعلى) + durationToPassengerMinutes; // Duration to reach the passenger in minutes (rounded up) final String rideType; final String orderId; @@ -22,6 +24,7 @@ class OrderData { OrderData({ required this.customerName, + required this.customerToken, required this.tripDistanceKm, required this.price, required this.startLocationAddress, @@ -37,26 +40,57 @@ class OrderData { this.rawEndCoordinates, }); - // دالة مساعدة لتحويل الثواني إلى دقائق وتقريبها لأعلى + // --- NEW: Factory constructor to create an instance from a Map --- + // This is the missing method that was causing the error. + factory OrderData.fromMap(Map map) { + return OrderData( + // For strings, provide a default value in case the map key is null + customerName: map['customerName']?.toString() ?? 'Unknown Customer', + customerToken: map['customerToken']?.toString() ?? 'Unknown token', + + // For numbers, cast from 'num' to handle both int and double, with a default value + tripDistanceKm: (map['tripDistanceKm'] as num?)?.toDouble() ?? 0.0, + + price: map['price']?.toString() ?? '0', + startLocationAddress: + map['startLocationAddress']?.toString() ?? 'Unknown Address', + endLocationAddress: + map['endLocationAddress']?.toString() ?? 'Unknown Address', + + distanceToPassengerKm: + (map['distanceToPassengerKm'] as num?)?.toDouble() ?? 0.0, + + tripDurationMinutes: (map['tripDurationMinutes'] as num?)?.toInt() ?? 0, + durationToPassengerMinutes: + (map['durationToPassengerMinutes'] as num?)?.toInt() ?? 0, + + rideType: map['rideType']?.toString() ?? 'Unknown', + orderId: map['orderId']?.toString() ?? 'N/A', + passengerId: map['passengerId']?.toString() ?? 'N/A', + passengerRate: map['passengerRate']?.toString() ?? 'N/A', + + // For nullable strings, direct access is fine as it returns null if the key doesn't exist + rawStartCoordinates: map['rawStartCoordinates'], + rawEndCoordinates: map['rawEndCoordinates'], + ); + } + + // A helper function to convert seconds to rounded-up minutes static int _secondsToRoundedUpMinutes(String secondsString) { final seconds = double.tryParse(secondsString) ?? 0.0; if (seconds <= 0) return 0; return (seconds / 60) - .ceil(); // .ceil() لتقريب الكسر لأعلى (مثلاً 0.1 دقيقة تصبح 1 دقيقة) + .ceil(); // .ceil() rounds up (e.g., 0.1 minutes becomes 1 minute) } + // Your existing factory for creating an instance from a List factory OrderData.fromList(List list) { - // بناءً على testList والافتراضات الجديدة: - // list[4]: durationToRide (مدة الرحلة الكلية بالثواني) - // list[5]: distance (المسافة الكلية للرحلة بالكيلومتر) - // list[12]: distanceByPassenger (المسافة إلى الراكب بالمتر) - // list[15]: durationToPassenger (المدة إلى الراكب بالثواني) - double distanceToPassengerMeters = list.length > 12 ? (double.tryParse(list[12].toString()) ?? 0.0) : 0.0; return OrderData( customerName: list.length > 8 ? list[8].toString() : 'Unknown Customer', + customerToken: list.length > 9 ? list[9].toString() : 'Unknown token', tripDistanceKm: list.length > 5 ? (double.tryParse(list[5].toString()) ?? 0.0) : 0.0, price: list.length > 2 ? list[2].toString().split('.')[0] : '0', @@ -66,7 +100,7 @@ class OrderData { list.length > 30 ? list[30].toString() : 'Unknown Address', distanceToPassengerKm: - distanceToPassengerMeters / 1000.0, // تحويل من متر إلى كيلومتر + distanceToPassengerMeters / 1000.0, // Convert meters to kilometers tripDurationMinutes: list.length > 4 ? _secondsToRoundedUpMinutes(list[4].toString()) : 0, @@ -102,6 +136,7 @@ class OrderData { } } + // Getter to parse start coordinates Map? get startCoordinates { if (rawStartCoordinates == null) return null; final parts = rawStartCoordinates!.split(','); @@ -114,6 +149,7 @@ class OrderData { return null; } + // Getter to parse end coordinates Map? get endCoordinates { if (rawEndCoordinates == null) return null; final parts = rawEndCoordinates!.split(','); @@ -126,6 +162,8 @@ class OrderData { return null; } + // Your existing method to convert the object TO a Map. + // This is used to pass the data from the overlay to the main app. Map toMap() { return { 'customerName': customerName, diff --git a/lib/models/overlay_service.dart b/lib/models/overlay_service.dart new file mode 100644 index 0000000..64d74a3 --- /dev/null +++ b/lib/models/overlay_service.dart @@ -0,0 +1,13 @@ +import 'package:flutter/services.dart'; + +class OverlayMethodChannel { + static const _channel = MethodChannel('com.sefer_driver/app_control'); + + static Future bringToForeground() async { + try { + await _channel.invokeMethod('bringToForeground'); + } on PlatformException catch (e) { + print('Error bringing app to foreground: $e'); + } + } +} diff --git a/lib/views/auth/country_widget.dart b/lib/views/auth/country_widget.dart index d739a40..d50517e 100755 --- a/lib/views/auth/country_widget.dart +++ b/lib/views/auth/country_widget.dart @@ -104,13 +104,14 @@ class CountryPickerFromSetting extends StatelessWidget { final List countryOptions = [ 'Jordan', - 'USA', + "Syria", 'Egypt', 'Turkey', 'Saudi Arabia', 'Qatar', 'Bahrain', 'Kuwait', + 'USA', ]; CountryPickerFromSetting({Key? key}) : super(key: key); diff --git a/lib/views/home/Captin/About Us/frequantly_question.dart b/lib/views/home/Captin/About Us/frequantly_question.dart index 385281c..8933590 100755 --- a/lib/views/home/Captin/About Us/frequantly_question.dart +++ b/lib/views/home/Captin/About Us/frequantly_question.dart @@ -28,7 +28,7 @@ class FrequentlyQuestionsPage extends StatelessWidget { ), children: [ Text( - 'Step-by-step instructions on how to request a ride through the Sefer app.' + 'Step-by-step instructions on how to request a ride through the Tripz app.' .tr, style: AppStyle.title, ), @@ -43,7 +43,7 @@ class FrequentlyQuestionsPage extends StatelessWidget { ), children: [ Text( - 'Sefer offers a variety of vehicle options to suit your needs, including economy, comfort, and luxury. Choose the option that best fits your budget and passenger count.' + 'Tripz offers a variety of vehicle options to suit your needs, including economy, comfort, and luxury. Choose the option that best fits your budget and passenger count.' .tr, style: AppStyle.title, ), @@ -58,7 +58,7 @@ class FrequentlyQuestionsPage extends StatelessWidget { ), children: [ Text( - 'Sefer offers multiple payment methods for your convenience. Choose between cash payment or credit/debit card payment during ride confirmation.' + 'Tripz offers multiple payment methods for your convenience. Choose between cash payment or credit/debit card payment during ride confirmation.' .tr, style: AppStyle.title, ), @@ -73,7 +73,7 @@ class FrequentlyQuestionsPage extends StatelessWidget { ), children: [ Text( - 'Yes, you can cancel your ride under certain conditions (e.g., before driver is assigned). See the Sefer cancellation policy for details.' + 'Yes, you can cancel your ride under certain conditions (e.g., before driver is assigned). See the Tripz cancellation policy for details.' .tr, style: AppStyle.title, ), @@ -98,7 +98,7 @@ class FrequentlyQuestionsPage extends StatelessWidget { }); }, child: Text( - 'Visit our website or contact Sefer support for information on driver registration and requirements.' + 'Visit our website or contact Tripz support for information on driver registration and requirements.' .tr, style: AppStyle.title, ), @@ -115,22 +115,22 @@ class FrequentlyQuestionsPage extends StatelessWidget { ), children: [ Text( - 'Sefer provides in-app chat functionality to allow you to communicate with your driver or passenger during your ride.' + 'Tripz provides in-app chat functionality to allow you to communicate with your driver or passenger during your ride.' .tr, style: AppStyle.title, ), ], ), - // Question 8: What safety measures does Sefer offer? + // Question 8: What safety measures does Tripz offer? ExpansionTile( title: Text( - 'What safety measures does Sefer offer?'.tr, + 'What safety measures does Tripz offer?'.tr, style: AppStyle.title, ), children: [ Text( - 'Sefer prioritizes your safety. We offer features like driver verification, in-app trip tracking, and emergency contact options.' + 'Tripz prioritizes your safety. We offer features like driver verification, in-app trip tracking, and emergency contact options.' .tr, style: AppStyle.title, ), diff --git a/lib/views/home/Captin/About Us/settings_captain.dart b/lib/views/home/Captin/About Us/settings_captain.dart index f0c6c71..ef06634 100755 --- a/lib/views/home/Captin/About Us/settings_captain.dart +++ b/lib/views/home/Captin/About Us/settings_captain.dart @@ -120,7 +120,7 @@ class SettingsCaptain extends StatelessWidget { CupertinoListTile( leading: const Icon(CupertinoIcons.hand_raised_fill), title: - Text("How to use SEFER".tr, style: AppStyle.headTitle2), + Text("How to use Tripz".tr, style: AppStyle.headTitle2), trailing: const CupertinoListTileChevron(), onTap: () => Get.to(() => const UsingAppPage()), ), diff --git a/lib/views/home/Captin/About Us/using_app_page.dart b/lib/views/home/Captin/About Us/using_app_page.dart index a32d021..002b664 100755 --- a/lib/views/home/Captin/About Us/using_app_page.dart +++ b/lib/views/home/Captin/About Us/using_app_page.dart @@ -10,7 +10,7 @@ class UsingAppPage extends StatelessWidget { @override Widget build(BuildContext context) { return MyScafolld( - title: "How to use SEFER".tr, + title: "How to use Tripz".tr, body: [ SizedBox( child: Padding( @@ -22,12 +22,13 @@ class UsingAppPage extends StatelessWidget { MyDialogContent().getDialog( "What are the order details we provide to you?".tr, Image.network( - 'https://api.sefer.live/sefer/imageForUsingApp/order_page.jpg', + 'https://api.tripz-egypt.com/tripz/imageForUsingApp/order_page.jpg', height: 300, width: 300, fit: BoxFit.cover, - ), - () {}); + ), () { + Get.back(); + }); }, child: Container( decoration: AppStyle.boxDecoration1, @@ -47,16 +48,17 @@ class UsingAppPage extends StatelessWidget { onTap: () { MyDialog().getDialog( "What are the order details we provide to you?".tr, - '''Sefer Wallet Features: + '''Tripz Wallet Features: Transfer money multiple times. Transfer to anyone. Make purchases. Charge your account. -Charge a friend's Sefer account. +Charge a friend's Tripz account. Store your money with us and receive it in your bank as a monthly salary.''' - .tr, - () {}); + .tr, () { + Get.back(); + }); }, child: Container( decoration: AppStyle.boxDecoration1, @@ -75,8 +77,8 @@ Store your money with us and receive it in your bank as a monthly salary.''' InkWell( onTap: () { MyDialog().getDialog( - "What are the order details we provide to you?".tr, - '''Types of Trips in Sefer: + "What is Types of Trips in Tripz?".tr, + '''Types of Trips in Tripz: Comfort: For cars newer than 2017 with air conditioning. Lady: For girl drivers. @@ -84,15 +86,16 @@ Speed: For fixed salary and endpoints. Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements. Raih Gai: For same-day return trips longer than 50km. ''' - .tr, - () {}); + .tr, () { + Get.back(); + }); }, child: Container( decoration: AppStyle.boxDecoration1, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - "What is Types of Trips in Sefer?".tr, + "What is Types of Trips in Tripz?".tr, style: AppStyle.title, ), ), diff --git a/lib/views/home/Captin/driver_map_page.dart b/lib/views/home/Captin/driver_map_page.dart index 214b256..86bcaa0 100755 --- a/lib/views/home/Captin/driver_map_page.dart +++ b/lib/views/home/Captin/driver_map_page.dart @@ -21,16 +21,20 @@ class PassengerLocationMapPage extends StatelessWidget { @override Widget build(BuildContext context) { - // if (!mapDriverController.initialized) { - // // Call a method to initialize the controller - - // mapDriverController.initialized; - // } else { - // Get.put(MapDriverController()).argumentLoading(); - - // Get.put(MapDriverController()) - // .startTimerToShowPassengerInfoWindowFromDriver(); - // } + if (Get.arguments != null && Get.arguments is Map) { + // نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول + // هذا يعطي GetX وقته لتجهيز كل شيء + WidgetsBinding.instance.addPostFrameCallback((_) { + // نستدعي دالة التهيئة الجديدة ونمرر لها البيانات + mapDriverController.argumentLoading(); + }); + } else { + // في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ + WidgetsBinding.instance.addPostFrameCallback((_) { + Get.snackbar("Error", "No order data found."); + Get.back(); + }); + } mapDriverController.argumentLoading(); mapDriverController.startTimerToShowPassengerInfoWindowFromDriver(); diff --git a/lib/views/home/Captin/home_captain/home_captin.dart b/lib/views/home/Captin/home_captain/home_captin.dart index 5f2c5fa..faf852c 100755 --- a/lib/views/home/Captin/home_captain/home_captin.dart +++ b/lib/views/home/Captin/home_captain/home_captin.dart @@ -19,6 +19,7 @@ import '../../../../controller/functions/location_controller.dart'; import '../../../../controller/functions/overlay_permisssion.dart'; import '../../../../controller/functions/package_info.dart'; import '../../../../controller/home/captin/home_captain_controller.dart'; +import '../../../../print.dart'; import '../../../widgets/circle_container.dart'; import '../driver_map_page.dart'; import 'widget/connect.dart'; @@ -482,6 +483,8 @@ class HomeCaptain extends StatelessWidget { ), GetBuilder( builder: (homeCaptainController) { + Log.print( + 'rideStatus from home 486 : ${box.read(BoxName.rideStatus)}'); return box.read(BoxName.rideStatus) == 'Applied' || box.read(BoxName.rideStatus) == 'Begin' ? Positioned( @@ -520,7 +523,7 @@ class HomeCaptain extends StatelessWidget { }; }, icon: const Icon( - Icons.rice_bowl, + Icons.directions_rounded, size: 29, color: AppColor.blueColor, ), diff --git a/lib/views/home/Captin/home_captain/widget/left_menu_map_captain.dart b/lib/views/home/Captin/home_captain/widget/left_menu_map_captain.dart index 981e2a5..6fbb3bf 100755 --- a/lib/views/home/Captin/home_captain/widget/left_menu_map_captain.dart +++ b/lib/views/home/Captin/home_captain/widget/left_menu_map_captain.dart @@ -1,13 +1,21 @@ import 'package:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/controller/firebase/local_notification.dart'; import 'package:sefer_driver/main.dart'; +import 'package:sefer_driver/print.dart'; +import 'package:sefer_driver/views/home/Captin/driver_map_page.dart'; import 'package:sefer_driver/views/home/Captin/orderCaptin/vip_order_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_font_icons/flutter_font_icons.dart'; import 'package:get/get.dart'; import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart'; +import 'package:sefer_driver/views/widgets/error_snakbar.dart'; +import 'package:sefer_driver/views/widgets/mydialoug.dart'; import '../../../../../constant/colors.dart'; +import '../../../../../constant/links.dart'; +import '../../../../../controller/firebase/firbase_messge.dart'; +import '../../../../../controller/functions/crud.dart'; +import '../../../../../controller/home/captin/order_request_controller.dart'; import '../../../../Rate/ride_calculate_driver.dart'; GetBuilder leftMainMenuCaptainIcons() { @@ -17,6 +25,46 @@ GetBuilder leftMainMenuCaptainIcons() { left: 6, child: Column( children: [ + AnimatedContainer( + duration: const Duration(microseconds: 200), + width: controller.widthMapTypeAndTraffic, + decoration: BoxDecoration( + color: AppColor.secondaryColor, + border: Border.all(color: AppColor.blueColor), + borderRadius: BorderRadius.circular(15)), + child: Builder(builder: (context) { + return IconButton( + onPressed: () async { + await checkForPendingOrderFromServer(); + box.read(BoxName.rideArgumentsFromBackground) != 'failure' + ? Get.to(() => PassengerLocationMapPage(), + arguments: + box.read(BoxName.rideArgumentsFromBackground)) + : MyDialog().getDialog( + 'Ride info'.tr, + 'you dont have accepted ride'.tr, + () { + Get.back(); + }, + ); + // Log.print( + // 'box.read(BoxName.rideArgumentsFromBackground): ${box.read(BoxName.rideArgumentsFromBackground)}'); + }, + icon: Icon( + Icons.directions_car_rounded, + size: 29, + color: + box.read(BoxName.rideArgumentsFromBackground) == 'failure' + ? AppColor.redColor + : AppColor.greenColor, + ), + ); + }), + ), + + const SizedBox( + height: 5, + ), AnimatedContainer( duration: const Duration(microseconds: 200), width: controller.widthMapTypeAndTraffic, @@ -89,7 +137,10 @@ GetBuilder leftMainMenuCaptainIcons() { }), ) : const SizedBox(), - // : const SizedBox(), + // const SizedBox( + // height: 5, + // ), + // AnimatedContainer( // duration: const Duration(microseconds: 200), // width: controller.widthMapTypeAndTraffic, @@ -100,53 +151,7 @@ GetBuilder leftMainMenuCaptainIcons() { // child: Builder(builder: (context) { // return IconButton( // onPressed: () async { - // final List testList = const [ - // "32.1117875,36.0669891", - // "32.1364001,36.0707479", - // "24.84", - // "7.56", - // "436", - // "4.38", - // "109270481246447459618", - // "113172279072358305645", - // "hamza", - // "e4QWqe7K607luM7qUMOPCL:APA91bFjX4XBM4I5COJl9fyxCTKJ1ZQpT3vzY7iEbOTuT4uo0-OSCAt5zgVhlhw4aC33s-VhyucDnP1tQGFd9svaazQ8A_SKgolPk3owzug8dCsiXoPeJ0k", - // "+201010101010", - // "6", - // "43", - // "true", - // "c2tXiuBJQCSg4CU4IfqYOL:APA91bFA0f8R3QMnPQnPEEdNyjY-jcoKt4nLBHxcLLsmDSuJn5yd4jSvwq7qDIZpkkPkjfjdwdKsGL0-G0aHpPyjfiBvbCwFmlRMCUKftNMNT7MJx2Bp16Y", - // "6", - // "1188", - // "false", - // "109270481246447459618", - // "436", - // "startEnd", - // "32.12404505187645,36.06566168367863", - // "", - // "", - // "", - // "", - // "5.42", - // "0", - // "hamzaayedflutter@gmail.com", - // "4368+PPP، السخنة، الأردن", - // "43PC+C4G، السخنة، الأردن", - // "Speed", - // "8", - // "5.00" - // ]; - // await FlutterOverlayWindow.shareData(testList); - // await FlutterOverlayWindow.showOverlay( - // enableDrag: true, - // flag: OverlayFlag.focusPointer, - // // visibility: NotificationVisibility.visibilityPublic, - // positionGravity: PositionGravity.auto, - // height: 1300, - // width: WindowSize.matchParent, - // startPosition: const OverlayPosition(0, -90), - // ); - // debugPrint('Overlay opened: '); + // Log.print('phoneDriver: ${box.read(BoxName.phoneDriver)}'); // }, // icon: const Icon( // FontAwesome5.grin_tears, @@ -165,3 +170,142 @@ GetBuilder leftMainMenuCaptainIcons() { ), ); } + +void _log(String message) => print(message); + +Future checkForPendingOrderFromServer() async { + bool _isProcessingOrder = false; + if (_isProcessingOrder) return; + + final driverId = box.read(BoxName.driverID)?.toString(); + if (driverId == null) return; // Can't check without a driver ID + + _isProcessingOrder = true; // Lock + + try { + // You need to create this CRUD method + var response = await CRUD().post( + link: AppLink.getArgumentAfterAppliedFromBackground, + payload: {'driver_id': driverId}, + ); + + // Assuming the server returns order data if found, or 'failure'/'none' if not + if (response != 'failure') { + Log.print('response: ${response}'); + _log("MAIN_APP_LOG: Pending order DETECTED from server!"); + + final Map orderInfoFromServer = response['message']; + final Map rideArguments = + _transformServerDataToAppArguments(orderInfoFromServer); + // 2. Build the new arguments map, matching your Flutter structure + + _log("MAIN_APP_LOG: Constructed rideArguments map successfully."); + +///////////// + final customerToken = (response)['message']['token_passenger']; + final orderId = (response)['message']['ride_id'].toString(); + Log.print('orderId: ${orderId}'); + box.write(BoxName.rideArgumentsFromBackground, rideArguments); + box.write(BoxName.statusDriverLocation, 'on'); + box.write(BoxName.rideStatus, 'Apply'); + Get.put(OrderRequestController()).changeApplied(); + // MyDialog().getDialog(orderId.toString(), customerToken, () {}); + + // Now proceed with the UI flow + _sendAcceptanceNotification(customerToken, orderId.toString()); + // await _bringAppToForegroundAndNavigate(orderId); + } else { + _log("MAIN_APP_LOG: No pending orders found on server."); + box.write(BoxName.rideArgumentsFromBackground, 'failure'); + } + } catch (e) { + _log("Error while polling server: $e"); + } finally { + _isProcessingOrder = false; // Release lock + } +} + +Map _transformServerDataToAppArguments( + Map serverData) { + _log("Transforming server data to match app's argument structure."); + + // Helper function to safely get and convert values to String + String _getString(String key, [String defaultValue = 'unknown']) { + // serverData[key] might be an int, double, or string. .toString() handles all. + // If it's null, use the default value. + return serverData[key]?.toString() ?? defaultValue; + } + + return { + 'passengerLocation': _getString('passenger_location'), + 'passengerDestination': _getString('passenger_destination'), + 'Duration': _getString('duration'), + 'totalCost': _getString('total_cost'), + 'Distance': _getString('distance'), + 'name': _getString('name'), + 'phone': _getString('phone'), + 'email': _getString('email'), + 'tokenPassenger': _getString('token_passenger'), + 'direction': _getString('direction_url'), + 'DurationToPassenger': _getString('duration_to_passenger'), + 'rideId': _getString('ride_id'), + 'passengerId': _getString('passenger_id'), + 'driverId': _getString('driver_id'), + 'durationOfRideValue': _getString('duration_of_ride'), + 'paymentAmount': _getString('payment_amount'), + 'paymentMethod': _getString('payment_method'), + 'passengerWalletBurc': _getString('passenger_wallet_burc'), + 'timeOfOrder': _getString('time_of_order'), + 'totalPassenger': _getString('total_passenger'), + 'carType': _getString('car_type'), + 'kazan': _getString('kazan'), + 'startNameLocation': _getString('start_name_location'), + 'endNameLocation': _getString('end_name_location'), + + // --- Special Handling --- + + // Steps (handle null values by providing an empty string) + 'step0': _getString('step0'), + 'step1': _getString('step1'), + 'step2': _getString('step2'), + 'step3': _getString('step3'), + 'step4': _getString('step4'), + + // Boolean conversion (1/0 from server to 'true'/'false' string for the app) + 'WalletChecked': (serverData['wallet_checked'] == 1).toString(), + + // Logic-based conversion for isHaveSteps + // Your app's `rideArguments` expects 'startEnd', so we provide that if has_steps is 1. + // You might need to adjust this logic if 'haveSteps' is also a possibility. + 'isHaveSteps': (serverData['has_steps'] == 1) + ? 'startEnd' + : 'noSteps', // Providing a default + }; +} + +void _sendAcceptanceNotification(String? customerToken, rideId) { + try { + if (customerToken == null) return; + final FirebaseMessagesController _firebaseMessagesController = + Get.put(FirebaseMessagesController()); + _log("Attempting to send acceptance notification to passenger..."); + List bodyToPassenger = [ + box.read(BoxName.driverID).toString(), + box.read(BoxName.nameDriver).toString(), + box.read(BoxName.tokenDriver).toString(), + rideId.toString() + ]; + + // Safely check for customer token + final String? token = customerToken; + if (token != null && token.isNotEmpty) { + _firebaseMessagesController.sendNotificationToDriverMAP('Accepted Ride', + 'your ride is applied'.tr, token, bodyToPassenger, 'start.wav'); + _log("Acceptance notification task was fired."); + } else { + _log("Could not send notification: Customer token is missing or empty."); + } + } catch (e) { + _log("Error while firing notification task: $e"); + } +} diff --git a/lib/views/home/Captin/maintain_center_page.dart b/lib/views/home/Captin/maintain_center_page.dart index d0277c4..7640ccf 100755 --- a/lib/views/home/Captin/maintain_center_page.dart +++ b/lib/views/home/Captin/maintain_center_page.dart @@ -51,7 +51,7 @@ import 'package:get/get.dart'; // child: Padding( // padding: const EdgeInsets.all(14), // child: Text( -// "We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our SEFER app and be part of our SEFER family." +// "We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our Tripz app and be part of our Tripz family." // .tr, // style: AppStyle.title, // ), @@ -186,7 +186,7 @@ class MaintainCenterPage extends StatelessWidget { ), const SizedBox(height: 8), Text( - "We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our SEFER app and be part of our SEFER family." + "We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our Tripz app and be part of our Tripz family." .tr, style: Theme.of(context) .textTheme diff --git a/lib/views/home/Captin/mapDriverWidgets/passenger_info_window.dart b/lib/views/home/Captin/mapDriverWidgets/passenger_info_window.dart index e1cf89b..18aadfe 100755 --- a/lib/views/home/Captin/mapDriverWidgets/passenger_info_window.dart +++ b/lib/views/home/Captin/mapDriverWidgets/passenger_info_window.dart @@ -13,8 +13,11 @@ import 'package:sefer_driver/controller/firebase/firbase_messge.dart'; import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart'; import 'package:sefer_driver/views/widgets/elevated_btn.dart'; +import '../../../../constant/box_name.dart'; import '../../../../constant/style.dart'; import '../../../../controller/functions/launch.dart'; +import '../../../../main.dart'; +import '../../../../print.dart'; class PassengerInfoWindow extends StatelessWidget { const PassengerInfoWindow({super.key}); @@ -179,7 +182,7 @@ class PassengerInfoWindow extends StatelessWidget { if (await controller .calculateDistanceBetweenDriverAndPassengerLocation() < 140) { - FirebaseMessagesController() + Get.find() .sendNotificationToDriverMAP( 'Hi ,I Arrive your site', 'I Arrive at your site'.tr, @@ -245,7 +248,7 @@ class PassengerInfoWindow extends StatelessWidget { MyDialog().getDialog( 'Are you sure to cancel?'.tr, '', () async { - FirebaseMessagesController() + Get.find() .sendNotificationToDriverMAP( 'Driver Cancelled Your Trip', 'You will need to pay the cost to the driver, or it will be deducted from your next trip' @@ -254,6 +257,9 @@ class PassengerInfoWindow extends StatelessWidget { [], 'cancel.wav', ); + Log.print( + 'rideStatus from passenge info 261 : ${box.read(BoxName.rideStatus)}'); + box.write(BoxName.rideStatus, 'Cancel'); await controller .addWaitingTimeCostFromPassengerToDriverWallet(); controller.isdriverWaitTimeEnd = false; @@ -296,7 +302,7 @@ class PassengerInfoWindow extends StatelessWidget { _buildMessageTile( text: "Where are you, sir?".tr, onTap: () { - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'message From Driver', "Where are you, sir?".tr, controller.tokenPassenger, @@ -309,7 +315,7 @@ class PassengerInfoWindow extends StatelessWidget { _buildMessageTile( text: "I've been trying to reach you but your phone is off.".tr, onTap: () { - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'message From Driver', "I've been trying to reach you but your phone is off.".tr, controller.tokenPassenger, @@ -324,7 +330,7 @@ class PassengerInfoWindow extends StatelessWidget { "Please don't be late, I'm waiting for you at the specified location." .tr, onTap: () { - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'message From Driver', "Please don't be late, I'm waiting for you at the specified location." .tr, @@ -338,7 +344,7 @@ class PassengerInfoWindow extends StatelessWidget { _buildMessageTile( text: "Please don't be late".tr, onTap: () { - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find().sendNotificationToDriverMAP( 'message From Driver', "Please don't be late".tr, controller.tokenPassenger, @@ -364,7 +370,8 @@ class PassengerInfoWindow extends StatelessWidget { ), IconButton( onPressed: () { - FirebaseMessagesController().sendNotificationToDriverMAP( + Get.find() + .sendNotificationToDriverMAP( 'message From Driver', controller.messageToPassenger.text, controller.tokenPassenger, diff --git a/lib/views/home/Captin/mapDriverWidgets/sos_connect.dart b/lib/views/home/Captin/mapDriverWidgets/sos_connect.dart index c0e8942..cd4a759 100755 --- a/lib/views/home/Captin/mapDriverWidgets/sos_connect.dart +++ b/lib/views/home/Captin/mapDriverWidgets/sos_connect.dart @@ -171,7 +171,7 @@ class SosConnect extends StatelessWidget { } else { throw 'Could not launch google maps'; } - }; + }(); } void _sendWhatsAppMessage(MapDriverController mapDriverController) { diff --git a/lib/views/home/Captin/orderCaptin/order_over_lay.dart b/lib/views/home/Captin/orderCaptin/order_over_lay.dart index c2d4d0c..2575a66 100755 --- a/lib/views/home/Captin/orderCaptin/order_over_lay.dart +++ b/lib/views/home/Captin/orderCaptin/order_over_lay.dart @@ -1,31 +1,32 @@ import 'dart:async'; -import 'dart:convert'; // لإضافة jsonEncode إذا احتجت له لاحقًا import 'package:flutter/material.dart'; import 'package:flutter_overlay_window/flutter_overlay_window.dart'; import 'package:get/get.dart'; import 'package:just_audio/just_audio.dart'; -// تأكد من صحة هذه المسارات +import 'package:sefer_driver/constant/api_key.dart'; import '../../../../constant/box_name.dart'; import '../../../../constant/links.dart'; +import '../../../../controller/firebase/local_notification.dart'; import '../../../../controller/functions/crud.dart'; -import '../../../../main.dart'; // للحصول على `box` -import '../../../../models/model/order_data.dart'; // مسار نموذج البيانات المحدث +import '../../../../main.dart'; +import '../../../../models/model/order_data.dart'; +import '../../../../print.dart'; -// --- الألوان --- -class OverlayColors { - static const Color primaryBackground = Color(0xFF2C3E50); - static const Color cardBackground = Color(0xFF34495E); - static const Color primaryText = Colors.white; - static const Color secondaryText = Color(0xFFBDC3C7); - static const Color accentColor = Color(0xFF1ABC9C); - static const Color acceptButton = Color(0xFF2ECC71); - static const Color rejectButton = Color(0xFFE74C3C); - static const Color highlightColor = Color(0xFFF1C40F); +// === Enhanced Colors for Better Readability === +class AppColors { + static const primary = Color(0xFF1A252F); + static const card = Color(0xFF2C3E50); + static const white = Colors.white; + static const gray = Color(0xFFBDC3C7); + static const lightGray = Color(0xFFECF0F1); + static const accent = Color(0xFF00BCD4); + static const accept = Color(0xFF4CAF50); + static const reject = Color(0xFFFF5722); + static const highlight = Color(0xFFFFC107); + static const priceHighlight = Color(0xFF00E676); + static const urgentRed = Color(0xFFD32F2F); } -// --- OrderData Model (يفترض أنه موجود في ملف منفصل كما هو الحال عادةً) --- -// class OrderData { ... } // الكود الخاص بـ OrderData الذي قدمته/عدلناه - class OrderOverlay extends StatefulWidget { const OrderOverlay({Key? key}) : super(key: key); @@ -35,86 +36,60 @@ class OrderOverlay extends StatefulWidget { class _OrderOverlayState extends State with WidgetsBindingObserver { - OrderData? _orderData; - Timer? _timer; - int _remainingSeconds = 20; // الوقت الافتراضي للمؤقت بالثواني - final AudioPlayer _audioPlayer = AudioPlayer(); - bool _buttonsEnabled = true; + // === State Variables === + OrderData? orderData; + Timer? timer; + int remainingSeconds = 10; + final AudioPlayer audioPlayer = AudioPlayer(); + bool buttonsEnabled = true; + final String mapApiKey = AK.mapAPIKEY; + final CRUD _crud = CRUD(); - final String _googleStaticMapsApiKey = - "YOUR_GOOGLE_STATIC_MAPS_API_KEY_HERE"; // !!! استبدل هذا بمفتاحك !!! - - bool get _canShowStaticMap { - if (_orderData == null || - _googleStaticMapsApiKey == "YOUR_GOOGLE_STATIC_MAPS_API_KEY_HERE") { - return false; - } - final startCoords = _orderData!.startCoordinates; - final endCoords = _orderData!.endCoordinates; - return startCoords?['lat'] != null && - startCoords?['lng'] != null && - endCoords?['lat'] != null && - endCoords?['lng'] != null; + final NotificationController notificationController = + Get.put(NotificationController()); + // === Getters === + bool get canShowMap { + if (orderData == null || mapApiKey.isEmpty) return false; + final start = orderData!.startCoordinates; + final end = orderData!.endCoordinates; + return start?['lat'] != null && + start?['lng'] != null && + end?['lat'] != null && + end?['lng'] != null; } + String get staticMapUrl { + if (!canShowMap) return ""; + final start = orderData!.startCoordinates!; + final end = orderData!.endCoordinates!; + final startMarker = Uri.encodeComponent("${start['lat']},${start['lng']}"); + final endMarker = Uri.encodeComponent("${end['lat']},${end['lng']}"); + + return "https://maps.googleapis.com/maps/api/staticmap?" + "size=600x150&maptype=roadmap" + "&markers=color:green%7Clabel:S%7C$startMarker" + "&markers=color:red%7Clabel:D%7C$endMarker" + "&path=color:0x007bff%7Cweight:5%7C$startMarker%7C$endMarker" + "&key=$mapApiKey"; + } + + // === Lifecycle === @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); - _setupOverlayListener(); - // // للاختبار، يمكنك إرسال بيانات ثابتة بعد ثانية - // Future.delayed(Duration(seconds: 1), () { - // if (mounted) { - // _processEventData(testList); // تأكد أن testList معرفة إذا كنت تستخدم هذا - // } - // }); - } - - void _setupOverlayListener() { FlutterOverlayWindow.overlayListener.listen((event) { - if (mounted) { - _processEventData(event); - } + if (mounted) _processEventData(event); }); } - void _processEventData(dynamic event) { - _printToMainApp("Overlay: Received event data: $event"); - - if (event is List) { - _printToMainApp("Overlay: Event list length: ${event.length}"); - if (event.length > 33) { - // مثال للتحقق من الطول قبل الوصول للفهارس - _printToMainApp("Overlay: list[0] (rawStartCoords): ${event[0]}"); - _printToMainApp( - "Overlay: list[15] (durationToPassengerSec): ${event[15]}"); - } - - try { - final newOrderData = OrderData.fromList(event); - _printToMainApp("Overlay: Parsed OrderData: ${newOrderData.toMap()}"); - - setState(() { - _orderData = newOrderData; - _remainingSeconds = 20; - }); - _resetAndStartTimer(); - } catch (e, s) { - _printToMainApp("Overlay: Error parsing OrderData: $e\nStackTrace: $s"); - } - } else if (event is Map && event['type'] == 'close_overlay_request') { - _closeOverlay(); - } else { - _printToMainApp( - "Overlay: Received unexpected data format or type: $event"); - } - } - - // دالة لإرسال رسائل الطباعة للتطبيق الرئيسي - void _printToMainApp(String message) { - // قم بتعليق هذه إذا كانت تسبب مشاكل أو إذا لم يكن لديك مستمع في التطبيق الرئيسي - // FlutterOverlayWindow.shareData({'type': 'debug_log', 'message': "OVERLAY: $message"}); - print("OVERLAY_PRINT: $message"); // اطبعها محليًا أيضًا كاحتياط + @override + void dispose() { + timer?.cancel(); + _stopAudio(); + audioPlayer.dispose(); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); } @override @@ -124,296 +99,330 @@ class _OrderOverlayState extends State } } + List myList = []; + // === Setup & Listeners === + void _setupOverlayListener() { + FlutterOverlayWindow.overlayListener.listen((event) { + if (mounted) _processEventData(event); + }); + } + + void _processEventData(dynamic event) { + _log("Received event: $event"); + if (event is List) { + try { + myList = event; + final newOrder = OrderData.fromList(event); + _log("Parsed OrderData: ${newOrder.toMap()}"); + setState(() { + orderData = newOrder; + }); + _resetAndStartTimer(); + } catch (e, s) { + _log("Error parsing OrderData: $e\nStackTrace: $s"); + } + } else { + _log("Unexpected data format: $event"); + } + } + void _checkOverlayStatus() async { bool isActive = await FlutterOverlayWindow.isActive(); - if (isActive && mounted && _orderData != null) { - if (_remainingSeconds > 0 && (_timer == null || !_timer!.isActive)) { + if (isActive && mounted && orderData != null) { + if (remainingSeconds > 0 && (timer == null || !timer!.isActive)) { _resetAndStartTimer(); } } } + // === Timer Management === void _resetAndStartTimer() { - _timer?.cancel(); - _audioPlayer.stop(); + timer?.cancel(); + audioPlayer.stop(); setState(() { - _buttonsEnabled = true; - _remainingSeconds = 20; + buttonsEnabled = true; + remainingSeconds = _calculateTimerDuration(); }); _playAudio(); _startTimer(); } - void _startTimer() { - if (_orderData == null) return; - if (_remainingSeconds <= 0) _remainingSeconds = 20; + int _calculateTimerDuration() { + if (orderData?.durationToPassengerMinutes != null && + orderData!.durationToPassengerMinutes > 0) { + int duration = orderData!.durationToPassengerMinutes * 60; + return duration > 10 ? 10 : duration; + } + return 10; + } - _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + void _startTimer() { + if (orderData == null) return; + timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (!mounted) { timer.cancel(); _stopAudio(); return; } setState(() { - if (_remainingSeconds > 0) { - _remainingSeconds--; + if (remainingSeconds > 0) { + remainingSeconds--; } else { timer.cancel(); _stopAudio(); - if (_buttonsEnabled) { - _handleOrderTimeout(); - } + if (buttonsEnabled) _handleOrderTimeout(); } }); }); } + // === Audio Management === void _playAudio() async { try { - await _audioPlayer.setAsset('assets/order.mp3', preload: true); - await _audioPlayer.setLoopMode(LoopMode.one); - await _audioPlayer.play(); + await audioPlayer.setAsset('assets/order.mp3', preload: true); + await audioPlayer.setLoopMode(LoopMode.one); + await audioPlayer.play(); } catch (e) { - _printToMainApp('Error playing audio: $e'); + _log('Error playing audio: $e'); } } void _stopAudio() { - _audioPlayer.stop(); + audioPlayer.stop(); } - @override - void dispose() { - _timer?.cancel(); - _stopAudio(); - _audioPlayer.dispose(); - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - Future _closeOverlay() async { - _stopAudio(); - if (await FlutterOverlayWindow.isActive()) { - await FlutterOverlayWindow.closeOverlay(); + String _getData(int index, {String defaultValue = ''}) { + if (myList.length > index && myList[index] != null) { + return myList[index].toString(); } + return defaultValue; } - void _disableButtonsAndProcess() { - setState(() { - _buttonsEnabled = false; - }); - _timer?.cancel(); - _stopAudio(); - } - + // === Order Actions === Future _acceptOrder() async { - if (!_buttonsEnabled || _orderData == null) return; + if (!buttonsEnabled || orderData == null) return; _disableButtonsAndProcess(); - _printToMainApp("Order ACCEPTED: ${_orderData!.orderId}"); - try { - // هنا يجب أن يكون طلب API الفعلي لقبول الطلب على السيرفر - // مثال: - // await CRUD().post(link: AppLink.acceptOrderLink, payload: { ... }); - _printToMainApp( - "SIMULATING API CALL: Order Accepted on Server for ${_orderData!.orderId}"); + _log("Order ACCEPTED: ${orderData!.orderId}"); - await _closeOverlay(); - await FlutterOverlayWindow.shareData({ - 'action': 'navigate_to_trip_acceptance', - 'order_id': _orderData!.orderId, - 'order_data_map': _orderData!.toMap(), + try { + final driverId = box.read(BoxName.driverID)?.toString(); + if (driverId == null) { + _log("Error: Driver ID is null. Closing overlay."); + await _closeOverlay(); + return; + } + + var res = await CRUD().post(link: AppLink.updateStausFromSpeed, payload: { + 'id': orderData!.orderId, + 'rideTimeStart': DateTime.now().toString(), + 'status': 'Apply', + 'driver_id': box.read(BoxName.driverID), }); - // لا تستدعي launchApplication() إذا كنت ستعتمد على الطريقة الأصلية أو deep link - } catch (e) { - _printToMainApp("Error accepting order: $e"); - if (mounted) setState(() => _buttonsEnabled = true); + if (AppLink.endPoint != AppLink.seferCairoServer) { + CRUD().post( + link: "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php", + payload: { + 'id': orderData!.orderId, + 'rideTimeStart': DateTime.now().toString(), + 'status': 'Apply', + 'driver_id': box.read(BoxName.driverID), + }); + } + final payload = { + // بيانات أساسية + 'driver_id': driverId, + 'status': 'Apply', + 'passengerLocation': _getData(0), + 'passengerDestination': _getData(1), + 'Duration': _getData(4), + 'totalCost': _getData(26), + 'Distance': _getData(5), + 'name': _getData(8), + 'phone': _getData(10), + 'email': _getData(28), + 'WalletChecked': _getData(13), + 'tokenPassenger': _getData(9), + 'direction': staticMapUrl.toString(), + 'DurationToPassenger': _getData(15), + 'rideId': orderData!.orderId, + 'passengerId': _getData(7), + 'durationOfRideValue': _getData(19), + 'paymentAmount': _getData(2), + 'paymentMethod': _getData(13) == 'true' ? 'visa' : 'cash', + 'isHaveSteps': _getData(20), + 'step0': myList[21].toString(), + 'step1': myList[22].toString(), + 'step2': myList[23].toString(), + 'step3': myList[24].toString(), + 'step4': myList[25].toString(), + 'passengerWalletBurc': myList[26].toString(), + 'carType': myList[31].toString(), + 'kazan': myList[32].toString(), + 'startNameLocation': myList[29].toString(), + 'endNameLocation': myList[30].toString(), + // الحقول الإضافية التي يجب تضمينها + 'timeOfOrder': DateTime.now().toIso8601String(), + 'totalPassenger': _getData(2), + }; + Log.print('myList: ${myList}'); + Log.print('payload: ${payload}'); + CRUD().post( + link: AppLink.addOverLayStatus, + payload: payload, + ); + if (res != "failure") { + // Using rideId (_getData(16)) for order_id consistently + CRUD().post(link: AppLink.addDriverOrder, payload: { + 'driver_id': driverId, // Driver ID from the order data + 'order_id': orderData!.orderId, + 'status': 'Apply' + }); + + if (AppLink.endPoint != AppLink.seferCairoServer) { + CRUD().post( + link: "${AppLink.endPoint}/ride/driver_order/add.php", + payload: { + 'driver_id': driverId, + 'order_id': orderData!.orderId, + 'status': 'Apply' + }); + } + _log("Server update successful. Writing to storage."); + notificationController.showNotification( + "Order Accepted".tr, + "Open app and go to passenger".tr, + 'ding', + '', + ); + await _closeOverlay(); + } else { + _log("Failed to update order status on server: $res"); + notificationController.showNotification( + "Order Accepted by another driver".tr, + "Open app and go to passenger".tr, + 'ding', + '', + ); + await _closeOverlay(); + } + } catch (e, s) { + _log( + "A critical error occurred during server update: $e\nStackTrace: $s"); + if (mounted) setState(() => buttonsEnabled = true); + return; } } + // Your list parsing for 'customerToken' should be something like: + // customerToken: list.length > a_certain_index ? list[a_certain_index].toString() : null, Future _rejectOrder() async { - if (!_buttonsEnabled || _orderData == null) return; + if (!buttonsEnabled || orderData == null) return; _disableButtonsAndProcess(); - _printToMainApp("Order REJECTED: ${_orderData!.orderId}"); + _log("Order REJECTED: ${orderData!.orderId}"); box.write(BoxName.rideStatus, 'reject'); - await _apiRefuseOrder(_orderData!.orderId); + Log.print('rideStatus from overlay 303 : ${box.read(BoxName.rideStatus)}'); + await _apiRefuseOrder(orderData!.orderId); await _closeOverlay(); } void _handleOrderTimeout() { - if (_orderData == null) return; - _printToMainApp("Order TIMED OUT: ${_orderData!.orderId}"); + if (orderData == null) return; + _log("Order TIMED OUT: ${orderData!.orderId}"); _rejectOrder(); } Future _apiRefuseOrder(String orderID) async { if (orderID == "N/A") { - _printToMainApp("Cannot refuse order with N/A ID"); - if (mounted) setState(() => _buttonsEnabled = true); + _log("Cannot refuse order with N/A ID"); return; } try { - // تأكد من أن box.read(BoxName.driverID) يعيد قيمة صالحة final driverId = box.read(BoxName.driverID)?.toString(); if (driverId == null) { - _printToMainApp("Driver ID is null, cannot refuse order."); - if (mounted) setState(() => _buttonsEnabled = true); + _log("Driver ID is null, cannot refuse order"); return; } - - await CRUD().post(link: AppLink.addDriverOrder, payload: { + await _crud.post(link: AppLink.addDriverOrder, payload: { 'driver_id': driverId, 'order_id': orderID, 'status': 'Refused' }); - await CRUD().post(link: AppLink.updateRides, payload: { + await _crud.post(link: AppLink.updateRides, payload: { 'id': orderID, 'status': 'Refused', 'driver_id': driverId, }); - _printToMainApp("Order $orderID refused successfully."); + _log("Order $orderID refused successfully"); } catch (e) { - _printToMainApp("Error in _apiRefuseOrder for $orderID: $e"); - if (mounted) setState(() => _buttonsEnabled = true); + _log("Error in _apiRefuseOrder for $orderID: $e"); } } + // === Helper Methods === + void _disableButtonsAndProcess() { + setState(() => buttonsEnabled = false); + timer?.cancel(); + _stopAudio(); + } + + Future _closeOverlay() async { + _stopAudio(); + timer?.cancel(); + if (await FlutterOverlayWindow.isActive()) { + await FlutterOverlayWindow.closeOverlay(); + } + } + + void _log(String message) { + // A simple logger to distinguish overlay logs + print("OVERLAY_LOG: $message"); + } + + // === UI Build Methods === @override Widget build(BuildContext context) { - if (_orderData == null) { + // ... (Your entire UI build method remains unchanged) ... + // The UI code is excellent and doesn't need modification. + if (orderData == null) { return const Material( color: Colors.transparent, child: Center( - child: - CircularProgressIndicator(color: OverlayColors.accentColor))); - } - - final order = _orderData!; - String staticMapUrl = ""; - - if (_canShowStaticMap) { - final startCoords = order.startCoordinates!; - final endCoords = order.endCoordinates!; - final startMarker = - Uri.encodeComponent("${startCoords['lat']},${startCoords['lng']}"); - final endMarker = - Uri.encodeComponent("${endCoords['lat']},${endCoords['lng']}"); - staticMapUrl = - "https://maps.googleapis.com/maps/api/staticmap?size=600x200&maptype=roadmap" // تقليل الارتفاع قليلاً - "&markers=color:green%7Clabel:S%7C$startMarker" - "&markers=color:red%7Clabel:D%7C$endMarker" - "&path=color:0x007bff%7Cweight:5%7C$startMarker%7C$endMarker" - "&key=$_googleStaticMapsApiKey"; - _printToMainApp("Generated staticMapUrl: $staticMapUrl"); - } else { - _printToMainApp( - "_canShowStaticMap is false. Check API key and coordinates."); - _printToMainApp("API Key used: $_googleStaticMapsApiKey"); - _printToMainApp("Start Coords: ${order.startCoordinates}"); - _printToMainApp("End Coords: ${order.endCoordinates}"); + child: CircularProgressIndicator(color: AppColors.accent))); } return Material( - color: Colors.black.withOpacity(0.3), // زيادة الشفافية للخلفية + color: Colors.black.withOpacity(0.4), child: Center( child: Container( - // Container رئيسي للـ Overlay card - margin: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 10.0), // هوامش أصغر - padding: const EdgeInsets.all(12.0), // حشوة أصغر + margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), + padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( - color: OverlayColors.cardBackground, - borderRadius: BorderRadius.circular(16.0), // حواف أقل دائرية + color: AppColors.card, + borderRadius: BorderRadius.circular(20.0), + border: Border.all( + color: AppColors.accent.withOpacity(0.3), width: 1.5), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.45), - blurRadius: 10, - spreadRadius: 1, + color: Colors.black.withOpacity(0.6), + blurRadius: 15, + spreadRadius: 2, ) ], ), - // استخدام IntrinsicHeight لجعل الـ Column يأخذ ارتفاع المحتوى - // أو يمكنك تحديد ارتفاع ثابت للـ Container إذا كان ذلك مناسبًا child: SingleChildScrollView( - // مهم إذا كان المحتوى يمكن أن يتجاوز الارتفاع child: Column( - mainAxisSize: - MainAxisSize.min, // مهم جدًا لجعل Column لا يتمدد بلا حدود + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - _buildHeaderWithTimer(), - const SizedBox(height: 10), - - if (_canShowStaticMap && staticMapUrl.isNotEmpty) - ClipRRect( - borderRadius: BorderRadius.circular(10.0), - child: Image.network( - staticMapUrl, - height: 110, // ارتفاع أقل للخريطة - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - _printToMainApp( - "Error loading static map image: $error"); - return Container( - height: 80, // ارتفاع أقل عند الخطأ - decoration: BoxDecoration( - color: OverlayColors.primaryBackground - .withOpacity(0.25), - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Icon(Icons.map_outlined, - color: OverlayColors.secondaryText - .withOpacity(0.6), - size: 28)), - ); - }, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return SizedBox( - height: 110, - child: Center( - child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : null, - color: OverlayColors.accentColor, - strokeWidth: 2.0, - ), - ), - ); - }, - ), - ), - if (_canShowStaticMap && staticMapUrl.isNotEmpty) - const SizedBox(height: 10), - - _buildRideInfoSection(order), - const SizedBox(height: 10), - - _buildLocationDetail( - icon: Icons.trip_origin_outlined, // تغيير الأيقونة - label: "نقطة الانطلاق".tr, - value: order.startLocationAddress, - iconColor: Colors.greenAccent[400]!, - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 25.0, vertical: 3.0), - child: Icon(Icons.more_vert_rounded, - color: OverlayColors.secondaryText.withOpacity(0.4), - size: 14), - ), - _buildLocationDetail( - icon: Icons.flag_circle_outlined, // تغيير الأيقونة - label: "الوجهة النهائية".tr, - value: order.endLocationAddress, - iconColor: Colors.redAccent[200]!, // تغيير اللون - ), - const SizedBox(height: 10), - _buildPassengerAndRideTypeRow(order), - const SizedBox(height: 16), // مسافة أكبر قبل الأزرار - _buildActionButtons(), + _buildQuickHeader(), + const SizedBox(height: 12), + _buildPrimaryInfo(), + const SizedBox(height: 12), + if (canShowMap) _buildCompactMap(), + if (canShowMap) const SizedBox(height: 12), + _buildSecondaryInfo(), + const SizedBox(height: 16), + _buildEnhancedActionButtons(), ], ), ), @@ -422,268 +431,377 @@ class _OrderOverlayState extends State ); } - Widget _buildHeaderWithTimer() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Flexible( - // لجعل النص يتكيف مع المساحة - child: Row( - mainAxisSize: MainAxisSize.min, // مهم داخل Flexible + // All your _build... widget methods (_buildQuickHeader, _buildPrimaryInfo, etc.) + // are perfectly fine and do not need to be changed. + // ... Paste all your existing _build... methods here ... + Widget _buildQuickHeader() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + remainingSeconds <= 3 + ? AppColors.urgentRed + : remainingSeconds <= 5 + ? AppColors.highlight + : AppColors.accent, + remainingSeconds <= 3 + ? AppColors.urgentRed.withOpacity(0.7) + : remainingSeconds <= 5 + ? AppColors.highlight.withOpacity(0.7) + : AppColors.accent.withOpacity(0.7), + ], + ), + borderRadius: BorderRadius.circular(15), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( children: [ - Icon(Icons.local_taxi_sharp, - color: OverlayColors.highlightColor, - size: 24), // أيقونة مختلفة - const SizedBox(width: 6), - Flexible( - // لجعل النص يتكيف - child: Text( - "طلب توصيل".tr, // نص أقصر - style: TextStyle( - color: OverlayColors.primaryText, - fontSize: 18, - fontWeight: FontWeight.w500), - overflow: TextOverflow.ellipsis, // إذا كان النص طويلاً + Icon(Icons.drive_eta_rounded, color: AppColors.white, size: 24), + const SizedBox(width: 8), + Text( + "طلب جديد".tr, + style: const TextStyle( + color: AppColors.white, + fontSize: 18, + fontWeight: FontWeight.w600), + ), + ], + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: AppColors.white.withOpacity(0.9), + borderRadius: BorderRadius.circular(20), + ), + child: Text( + "$remainingSeconds ث", + style: TextStyle( + color: remainingSeconds <= 3 + ? AppColors.urgentRed + : remainingSeconds <= 5 + ? AppColors.highlight + : AppColors.accent, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ); + } + + Widget _buildPrimaryInfo() { + final order = orderData!; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.primary.withOpacity(0.6), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: AppColors.priceHighlight.withOpacity(0.3), width: 1), + ), + child: Column( + children: [ + // Price and Distance - Most Important Info + Row( + children: [ + Expanded( + flex: 3, + child: _buildHighlightInfo("\$${order.price}", "السعر".tr, + Icons.monetization_on_rounded, AppColors.priceHighlight, + isLarge: true), + ), + const SizedBox(width: 12), + Expanded( + flex: 2, + child: _buildHighlightInfo( + "${order.tripDistanceKm.toStringAsFixed(1)} كم", + "المسافة".tr, + Icons.straighten_rounded, + AppColors.accent, ), ), ], ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 5), // حشوة أصغر للعداد - decoration: BoxDecoration( - color: _remainingSeconds <= 5 - ? OverlayColors.rejectButton.withOpacity(0.75) - : OverlayColors.primaryBackground.withOpacity(0.7), - borderRadius: BorderRadius.circular(14), - border: Border.all( - color: OverlayColors.accentColor.withOpacity(0.6), - width: 0.7)), - child: Text( - _remainingSeconds.toString(), - style: TextStyle( - color: OverlayColors.primaryText, - fontSize: 17, - fontWeight: FontWeight.bold, - fontFeatures: [FontFeature.tabularFigures()]), - ), - ), - ], - ); - } - - Widget _buildRideInfoSection(OrderData order) { - return Container( - padding: - const EdgeInsets.symmetric(vertical: 8, horizontal: 6), // حشوة أصغر - decoration: BoxDecoration( - color: OverlayColors.primaryBackground.withOpacity(0.55), - borderRadius: BorderRadius.circular(8), // حواف أقل دائرية - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: _infoChip( - "السعر".tr, - "\$${order.price}", - Icons.attach_money_outlined, - OverlayColors.highlightColor, - valueFontSize: 15)), - Expanded( - child: _infoChip( - "مسافة الرحلة".tr, - "${order.tripDistanceKm.toStringAsFixed(1)} كم", - Icons.swap_calls_rounded, - OverlayColors.accentColor, - valueFontSize: 15)), // أيقونة مختلفة - ], - ), - const SizedBox(height: 6), - Divider( - color: OverlayColors.secondaryText.withOpacity(0.15), - height: 0.5, - thickness: 0.5), // فاصل أرق - const SizedBox(height: 6), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: _infoChip( - "لوصولك للراكب".tr, - "~${order.durationToPassengerMinutes} د", - Icons.schedule_outlined, - OverlayColors.secondaryText, - valueFontSize: 14)), // اختصار "دقيقة" - Expanded( - child: _infoChip( - "مسافة للراكب".tr, - "${order.distanceToPassengerKm.toStringAsFixed(1)} كم", - Icons.person_pin_circle_outlined, - OverlayColors.secondaryText, - valueFontSize: 14)), // أيقونة مختلفة - ], - ), - if (order.tripDurationMinutes > 0) ...[ - const SizedBox(height: 6), - Divider( - color: OverlayColors.secondaryText.withOpacity(0.15), - height: 0.5, - thickness: 0.5), - const SizedBox(height: 6), - Row( - // استخدام Row لجعل الـ _infoChip يتمدد - children: [ - Expanded( - child: _infoChip( - "مدة الرحلة".tr, - "~${order.tripDurationMinutes} د", - Icons.timelapse_outlined, - OverlayColors.accentColor, - valueFontSize: 14) // اختصار - ), - ], - ), - ] - ], - )); - } - - Widget _infoChip(String label, String value, IconData icon, Color iconColor, - {double valueFontSize = 13}) { - // تم إزالة Expanded من هنا، ووضعها عند استدعاء _infoChip إذا لزم الأمر - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(icon, color: iconColor, size: 17), // أيقونة أصغر - const SizedBox(height: 1), - Text(label, - style: TextStyle( - color: OverlayColors.secondaryText.withOpacity(0.85), - fontSize: 9.5), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis), // خط أصغر للعنوان - // const SizedBox(height: 0.5), - Text(value, - style: TextStyle( - color: OverlayColors.primaryText, - fontSize: valueFontSize, - fontWeight: FontWeight.w500), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis), // خط أصغر للقيمة - ], - ); - } - - Widget _buildLocationDetail({ - required IconData icon, - required String label, - required String value, - Color iconColor = OverlayColors.accentColor, - }) { - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(icon, color: iconColor, size: 16), // أيقونة أصغر - const SizedBox(width: 6), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, // مهم + const SizedBox(height: 12), + Divider(color: AppColors.gray.withOpacity(0.2), thickness: 1), + const SizedBox(height: 12), + // Passenger Info and ETA + Row( children: [ - Text( - label, - style: TextStyle( - color: OverlayColors.secondaryText.withOpacity(0.75), - fontSize: 9.5, - fontWeight: FontWeight.w300), + Expanded( + flex: 2, + child: _buildPassengerQuickInfo(), ), - Text( - value, - style: TextStyle( - color: OverlayColors.primaryText, - fontSize: 12.5, - fontWeight: FontWeight.normal), - overflow: TextOverflow.ellipsis, - maxLines: 2, + const SizedBox(width: 12), + Expanded( + child: _buildHighlightInfo( + "${order.durationToPassengerMinutes} د", + "للوصول".tr, + Icons.access_time_filled_rounded, + order.durationToPassengerMinutes <= 3 + ? AppColors.priceHighlight + : AppColors.gray, + ), ), ], ), - ), - ], + ], + ), ); } - Widget _buildPassengerAndRideTypeRow(OrderData order) { + Widget _buildHighlightInfo( + String value, String label, IconData icon, Color color, + {bool isLarge = false}) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: color.withOpacity(0.3), width: 1), + ), + child: Column( + children: [ + Icon(icon, color: color, size: isLarge ? 24 : 20), + const SizedBox(height: 4), + Text( + value, + style: TextStyle( + color: AppColors.white, + fontSize: isLarge ? 20 : 16, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + Text( + label, + style: TextStyle( + color: AppColors.lightGray.withOpacity(0.7), + fontSize: 11, + ), + textAlign: TextAlign.center, + ), + ], + ), + ); + } + + Widget _buildPassengerQuickInfo() { + final order = orderData!; + return Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + decoration: BoxDecoration( + color: AppColors.highlight.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + border: + Border.all(color: AppColors.highlight.withOpacity(0.3), width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(Icons.person_rounded, color: AppColors.highlight, size: 18), + const SizedBox(width: 4), + Expanded( + child: Text( + order.customerName, + style: const TextStyle( + color: AppColors.white, + fontSize: 14, + fontWeight: FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 2), + Text( + order.rideType, + style: TextStyle( + color: AppColors.lightGray.withOpacity(0.7), + fontSize: 11, + ), + ), + ], + ), + ); + } + + Widget _buildCompactMap() { + return ClipRRect( + borderRadius: BorderRadius.circular(12.0), + child: Image.network( + staticMapUrl, + height: 100, // Reduced from 110 + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Container( + height: 100, + decoration: BoxDecoration( + color: AppColors.primary.withOpacity(0.3), + borderRadius: BorderRadius.circular(12), + ), + child: const Center( + child: + Icon(Icons.map_outlined, color: AppColors.gray, size: 32)), + ); + }, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return SizedBox( + height: 100, + child: Center( + child: CircularProgressIndicator( + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null, + color: AppColors.accent, + strokeWidth: 2.0, + ), + ), + ); + }, + ), + ); + } + + Widget _buildSecondaryInfo() { + final order = orderData!; + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColors.primary.withOpacity(0.4), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + children: [ + _buildLocationRow( + Icons.trip_origin_rounded, + "من".tr, + order.startLocationAddress, + Colors.green.shade300, + ), + const SizedBox(height: 8), + _buildLocationRow( + Icons.flag_rounded, + "إلى".tr, + order.endLocationAddress, + Colors.red.shade300, + ), + if (order.tripDurationMinutes > 0) ...[ + const SizedBox(height: 8), + Divider(color: AppColors.gray.withOpacity(0.2), thickness: 0.5), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.timer_outlined, color: AppColors.accent, size: 16), + const SizedBox(width: 4), + Text( + "مدة الرحلة: ${order.tripDurationMinutes} دقيقة".tr, + style: const TextStyle( + color: AppColors.lightGray, + fontSize: 12, + ), + ), + ], + ), + ] + ], + ), + ); + } + + Widget _buildLocationRow( + IconData icon, String label, String address, Color iconColor) { return Row( children: [ - Expanded( - child: _buildLocationDetail( - icon: Icons.person_search_outlined, // أيقونة مختلفة - label: "الراكب".tr, - value: order.customerName, - iconColor: OverlayColors.highlightColor.withOpacity(0.75), - ), - ), + Icon(icon, color: iconColor, size: 16), const SizedBox(width: 8), + Text( + "$label: ", + style: TextStyle( + color: AppColors.lightGray.withOpacity(0.8), + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), Expanded( - child: _buildLocationDetail( - icon: Icons.category_outlined, // أيقونة مختلفة - label: "نوع الطلب".tr, - value: order.rideType, - iconColor: OverlayColors.accentColor.withOpacity(0.75), + child: Text( + address, + style: const TextStyle( + color: AppColors.white, + fontSize: 12, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, ), ), ], ); } - Widget _buildActionButtons() { + Widget _buildEnhancedActionButtons() { return Row( children: [ Expanded( - child: ElevatedButton.icon( - icon: const Icon(Icons.close_fullscreen_rounded, - color: Colors.white, size: 17), // أيقونة مختلفة - label: Text("رفض".tr, - style: const TextStyle( - color: Colors.white, - fontSize: 13.5, - fontWeight: FontWeight.w500)), - onPressed: _buttonsEnabled ? _rejectOrder : null, + child: ElevatedButton( + onPressed: buttonsEnabled ? _rejectOrder : null, style: ElevatedButton.styleFrom( - backgroundColor: OverlayColors.rejectButton.withOpacity(0.85), - padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 6), + backgroundColor: AppColors.reject, + foregroundColor: AppColors.white, + padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(7)), - disabledBackgroundColor: - OverlayColors.rejectButton.withOpacity(0.25), + borderRadius: BorderRadius.circular(12)), + disabledBackgroundColor: AppColors.reject.withOpacity(0.3), + elevation: 3, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.close_rounded, size: 20), + const SizedBox(width: 6), + Text( + "رفض".tr, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.w600), + ), + ], ), ), ), - const SizedBox(width: 7), + const SizedBox(width: 12), Expanded( - child: ElevatedButton.icon( - icon: const Icon(Icons.task_alt_rounded, - color: Colors.white, size: 17), // أيقونة مختلفة - label: Text("قبول".tr, - style: const TextStyle( - color: Colors.white, - fontSize: 13.5, - fontWeight: FontWeight.w500)), - onPressed: _buttonsEnabled ? _acceptOrder : null, + child: ElevatedButton( + onPressed: buttonsEnabled ? _acceptOrder : null, style: ElevatedButton.styleFrom( - backgroundColor: OverlayColors.acceptButton.withOpacity(0.85), - padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 6), + backgroundColor: AppColors.accept, + foregroundColor: AppColors.white, + padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(7)), - disabledBackgroundColor: - OverlayColors.acceptButton.withOpacity(0.25), + borderRadius: BorderRadius.circular(12)), + disabledBackgroundColor: AppColors.accept.withOpacity(0.3), + elevation: 3, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.check_circle_rounded, size: 20), + const SizedBox(width: 6), + Text( + "قبول".tr, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.w600), + ), + ], ), ), ), diff --git a/lib/views/home/Captin/orderCaptin/order_speed_request.dart b/lib/views/home/Captin/orderCaptin/order_speed_request.dart index 325a924..aa5771f 100755 --- a/lib/views/home/Captin/orderCaptin/order_speed_request.dart +++ b/lib/views/home/Captin/orderCaptin/order_speed_request.dart @@ -1,554 +1,603 @@ -import 'dart:convert'; +import 'dart:convert'; // Though not directly used in this version's UI logic, often kept for model interactions. import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/controller/firebase/firbase_messge.dart'; -import 'package:sefer_driver/main.dart'; +import 'package:sefer_driver/main.dart'; // For `box` import 'package:sefer_driver/views/home/Captin/driver_map_page.dart'; -import 'package:sefer_driver/views/widgets/my_scafold.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; -import '../../../../constant/colors.dart'; -import '../../../../constant/links.dart'; -import '../../../../constant/style.dart'; +import '../../../../constant/colors.dart'; // Your AppColor +import '../../../../constant/links.dart'; // Your AppLink +import '../../../../constant/style.dart'; // Your AppStyle import '../../../../controller/functions/crud.dart'; import '../../../../controller/functions/launch.dart'; import '../../../../controller/home/captin/order_request_controller.dart'; -import '../../../widgets/elevated_btn.dart'; +import '../../../widgets/elevated_btn.dart'; // Your MyElevatedButton class OrderSpeedRequest extends StatelessWidget { OrderSpeedRequest({super.key}); - OrderRequestController orderRequestController = + + final OrderRequestController orderRequestController = Get.put(OrderRequestController()); + + // Helper to make myList access more readable and safer + String _getData(int index, {String defaultValue = ''}) { + if (orderRequestController.myList.length > index && + orderRequestController.myList[index] != null) { + return orderRequestController.myList[index].toString(); + } + return defaultValue; + } + @override Widget build(BuildContext context) { - return GetBuilder( - builder: (orderRequestController) { - return MyScafolld( - title: 'Speed Order'.tr, - body: [ - ListView( - // crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // SizedBox(height: 200, child: Text(pointsList.toString())), - // Text(message.notification!.body.toString()), - SizedBox( - height: Get.height * .33, - child: GoogleMap( - initialCameraPosition: CameraPosition( - zoom: 12, - target: Get.find().myLocation), - cameraTargetBounds: - CameraTargetBounds(orderRequestController.bounds), - myLocationButtonEnabled: true, - trafficEnabled: false, - buildingsEnabled: false, - mapToolbarEnabled: true, - myLocationEnabled: true, - markers: { - Marker( - markerId: MarkerId('MyLocation'.tr), - position: LatLng( - orderRequestController.latPassengerLocation, - orderRequestController.lngPassengerLocation), - draggable: true, - icon: orderRequestController.startIcon), - Marker( - markerId: MarkerId('Destination'.tr), - position: LatLng( - orderRequestController.latPassengerDestination, - orderRequestController.lngPassengerDestination), - draggable: true, - icon: orderRequestController.endIcon), - }, - polylines: { - Polyline( - zIndex: 1, - consumeTapEvents: true, - geodesic: true, - endCap: Cap.buttCap, - startCap: Cap.buttCap, - visible: true, - polylineId: const PolylineId('routeOrder'), - points: orderRequestController.pointsDirection, - color: AppColor.primaryColor, - width: 2, - ), - }, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Card( - elevation: 3, - color: orderRequestController.myList[20].toString() == - 'haveSteps' - ? AppColor.greenColor - : AppColor.secondaryColor, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - TextButton.icon( - onPressed: () { - String mapUrl = - 'https://www.google.com/maps/dir/${orderRequestController.myList[0]}/${orderRequestController.myList[1]}/'; - showInBrowser(mapUrl); - }, - icon: const Icon(Icons.map), - label: - orderRequestController.myList[20].toString() == - 'haveSteps' - ? Text( - 'Trip has Steps'.tr, - style: AppStyle.title, - ) - : Text('Payment Method'.tr, - style: AppStyle.title)), - Container( - decoration: AppStyle.boxDecoration.copyWith( - color: - orderRequestController.myList[13].toString() == - 'true' - ? AppColor.deepPurpleAccent - : AppColor.greenColor, - ), - child: orderRequestController.myList[13].toString() == - 'true' //Visa or Cash Method from notify to driver - ? Text( - 'Visa', - style: AppStyle.title, - ) - : Text('Cash', style: AppStyle.title), - ) - ], - ), - ), - ), + // Define AppBar first to get its height for body calculations + final appBar = AppBar( + title: Text('Speed Order'.tr), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Get.back(), + ), + backgroundColor: AppColor.primaryColor, // Example color, adjust as needed + elevation: 2.0, + ); - Container( - decoration: AppStyle.boxDecoration1, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - double.parse(orderRequestController.myList[2]) - .toStringAsFixed(2), - style: AppStyle.headTitle2, - ), - AnimatedContainer( - duration: const Duration(seconds: 5), - curve: Curves.easeInOut, - child: AnimatedSize( - duration: const Duration(seconds: 5), - curve: Curves.easeInOut, - child: orderRequestController.myList[31] - .toString() == - 'Comfort' - ? Column( - mainAxisAlignment: - MainAxisAlignment.spaceAround, + final double appBarHeight = appBar.preferredSize.height; + final MediaQueryData mediaQueryData = MediaQuery.of(context); + final double screenHeight = mediaQueryData.size.height; + final double statusBarHeight = mediaQueryData.padding.top; + final double bottomSystemPadding = mediaQueryData.padding.bottom; + + // Calculate available height for the Scaffold's body content + // Subtracting status bar, app bar, and bottom system padding (like navigation bar) + final double availableBodyHeight = + screenHeight - appBarHeight - statusBarHeight - bottomSystemPadding; + + // Define overall padding for the body content + const EdgeInsets bodyContentPadding = + EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0); + + // Calculate the height for the main content Column, considering the body's own padding + double mainColumnHeight = availableBodyHeight - bodyContentPadding.vertical; + if (mainColumnHeight < 0) mainColumnHeight = 0; + + return GetBuilder( + builder: (controller) { + // Pre-extract data for readability and safety + final String price = + double.tryParse(_getData(2))?.toStringAsFixed(2) ?? 'N/A'; + final bool isComfortTrip = _getData(31) == 'Comfort'; + final String carType = _getData(31).tr; + + final String pickupName = _getData(12); + final String pickupDetails = '(${_getData(11)})'; + final String pickupFullAddress = _getData(29); + + final String dropoffName = _getData(5); + final String dropoffDetails = '(${_getData(4)})'; + final String dropoffFullAddress = _getData(30); + + final String passengerName = _getData(8); + final String passengerRating = _getData(33); + + final bool isVisaPayment = _getData(13) == 'true'; + final bool hasSteps = _getData(20) == 'haveSteps'; + + final String mapUrl = + 'https://www.google.com/maps/dir/${_getData(0)}/${_getData(1)}/'; + final String rideId = _getData(16); // Commonly used ID + + return Scaffold( + appBar: appBar, + backgroundColor: AppColor.secondaryColor ?? + Colors.grey[100], // Background for the page + body: SafeArea( + // Ensures content is not obscured by system UI + child: Padding( + padding: + bodyContentPadding, // Apply overall padding to the body's content area + child: SizedBox( + // Constrain the height of the main layout Column + height: mainColumnHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // --- MAP SECTION --- + SizedBox( + height: + Get.height * 0.28, // Relative to total screen height + child: ClipRRect( + borderRadius: BorderRadius.circular(15.0), + child: GoogleMap( + initialCameraPosition: CameraPosition( + zoom: 12, + target: + Get.find().myLocation), + cameraTargetBounds: + CameraTargetBounds(controller.bounds), + myLocationButtonEnabled: false, + trafficEnabled: false, + buildingsEnabled: false, + mapToolbarEnabled: false, + myLocationEnabled: true, + markers: { + Marker( + markerId: MarkerId('MyLocation'.tr), + position: LatLng( + controller.latPassengerLocation, + controller.lngPassengerLocation), + icon: controller.startIcon), + Marker( + markerId: MarkerId('Destination'.tr), + position: LatLng( + controller.latPassengerDestination, + controller.lngPassengerDestination), + icon: controller.endIcon), + }, + polylines: { + Polyline( + zIndex: 1, + consumeTapEvents: true, + geodesic: true, + endCap: Cap.buttCap, + startCap: Cap.buttCap, + visible: true, + polylineId: const PolylineId('routeOrder'), + points: controller.pointsDirection, + color: AppColor.primaryColor, + width: 3, + ), + }, + ), + ), + ), + const SizedBox(height: 8), + + // --- PRICE & TRIP TYPE SECTION --- + Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + price, + style: AppStyle.headTitle.copyWith( + color: AppColor.primaryColor, + fontWeight: FontWeight.bold, + fontSize: 28), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + carType, + style: AppStyle.title.copyWith( + color: AppColor.greenColor, + fontWeight: FontWeight.bold), + ), + if (isComfortTrip) + Row( + children: [ + const Icon(Icons.ac_unit, + color: AppColor.blueColor, size: 18), + const SizedBox(width: 4), + Text('Air condition Trip'.tr, + style: AppStyle.subtitle + .copyWith(fontSize: 13)), + ], + ), + ], + ), + ], + ), + ), + ), + const SizedBox(height: 8), + + // --- EXPANDED SECTION FOR SCROLLABLE (BUT NOT USER-SCROLLABLE) CONTENT --- + Expanded( + child: SingleChildScrollView( + physics: + const NeverScrollableScrollPhysics(), // Prevents user scrolling + child: Column( + mainAxisSize: MainAxisSize + .min, // Takes minimum vertical space needed + children: [ + _buildLocationCard( + icon: Icons.arrow_circle_up, + iconColor: AppColor.greenColor, + title: pickupName, + subtitle: pickupDetails, + fullAddress: pickupFullAddress, + ), + const SizedBox(height: 8), + _buildLocationCard( + icon: Icons.arrow_circle_down, + iconColor: AppColor.redColor, + title: dropoffName, + subtitle: dropoffDetails, + fullAddress: dropoffFullAddress, + ), + const SizedBox(height: 8), + // --- PAYMENT, STEPS & DIRECTIONS INFO --- + Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( children: [ - const Icon( - Icons.ac_unit, - color: AppColor.blueColor, - size: 50, + Icon( + isVisaPayment + ? Icons.credit_card + : Icons + .payments_outlined, // Using payments_outlined for cash + color: isVisaPayment + ? AppColor.deepPurpleAccent + : AppColor.greenColor, + size: 24, ), + const SizedBox(width: 8), Text( - 'Air condition Trip'.tr, - style: AppStyle.subtitle, + isVisaPayment ? 'Visa'.tr : 'Cash'.tr, + style: AppStyle.title.copyWith( + fontWeight: FontWeight.w600), ), ], - ) - : const SizedBox(), + ), + if (hasSteps) + Expanded( + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + const Icon( + Icons + .format_list_numbered_rtl_outlined, + color: AppColor.bronze, + size: 24), + const SizedBox(width: 4), + Flexible( + child: Text( + 'Trip has Steps'.tr, + style: AppStyle.title.copyWith( + color: AppColor.bronze, + fontSize: 13), + overflow: TextOverflow.ellipsis, + )), + ], + ), + ), + TextButton.icon( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + tapTargetSize: + MaterialTapTargetSize.shrinkWrap, + alignment: Alignment.centerRight, + ), + onPressed: () => showInBrowser(mapUrl), + icon: const Icon( + Icons.directions_outlined, + color: AppColor.blueColor, + size: 20), + label: Text("Directions".tr, + style: AppStyle.subtitle.copyWith( + color: AppColor.blueColor, + fontSize: 13)), + ), + ], + ), + ), + ), + const SizedBox(height: 8), + // --- PASSENGER INFO --- + Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 10.0), + child: Row( + children: [ + const Icon(Icons.person_outline, + color: AppColor.greyColor, size: 22), + const SizedBox(width: 10), + Expanded( + child: Text( + passengerName, + style: AppStyle.title, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 10), + const Icon(Icons.star_rounded, + color: Colors.amber, size: 20), + Text( + passengerRating, + style: AppStyle.title.copyWith( + fontWeight: FontWeight.bold), + ), + ], + ), + ), + ), + ], + ), + ), + ), + // const SizedBox(height: 8), // Spacer before action buttons if needed + + // --- ACTION BUTTONS & TIMER --- + Padding( + padding: const EdgeInsets.only(top: 8.0, bottom: 5.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: MyElevatedButton( + kolor: AppColor.greenColor, + title: 'Accept Order'.tr, + onPressed: () async { + Get.put(HomeCaptainController()).changeRideId(); + box.write(BoxName.statusDriverLocation, 'on'); + var res = await CRUD().post( + link: AppLink.updateStausFromSpeed, + payload: { + 'id': rideId, + 'rideTimeStart': + DateTime.now().toString(), + 'status': 'Apply', + 'driver_id': box.read(BoxName.driverID), + }); + if (AppLink.endPoint != + AppLink.seferCairoServer) { + CRUD().post( + link: + "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php", + payload: { + 'id': rideId, + 'rideTimeStart': + DateTime.now().toString(), + 'status': 'Apply', + 'driver_id': box.read(BoxName.driverID), + }); + } + if (res != "failure") { + box.write(BoxName.statusDriverLocation, 'on'); + controller.changeApplied(); + List bodyToPassenger = [ + box.read(BoxName.driverID).toString(), + box.read(BoxName.nameDriver).toString(), + box.read(BoxName.tokenDriver).toString(), + rideId.toString(), + ]; + Get.put(FirebaseMessagesController()) + .sendNotificationToDriverMAP( + 'Accepted Ride', + 'your ride is applied'.tr, + controller.arguments?['DriverList'] + ?[9] + ?.toString() ?? + _getData(9), // Safer access + bodyToPassenger, + 'start.wav'); + + // Using rideId (_getData(16)) for order_id consistently + CRUD().postFromDialogue( + link: AppLink.addDriverOrder, + payload: { + 'driver_id': _getData( + 6), // Driver ID from the order data + 'order_id': rideId, + 'status': 'Apply' + }); + + if (AppLink.endPoint != + AppLink.seferCairoServer) { + CRUD().post( + link: + "${AppLink.endPoint}/ride/driver_order/add.php", + payload: { + 'driver_id': _getData(6), + 'order_id': rideId, + 'status': 'Apply' + }); + } + + Get.back(); // Go back from order request screen + box.write(BoxName.rideArguments, { + 'passengerLocation': _getData(0), + 'passengerDestination': _getData(1), + 'Duration': _getData(4), + 'totalCost': _getData(26), + 'Distance': _getData(5), + 'name': _getData(8), + 'phone': _getData(10), + 'email': _getData(28), + 'WalletChecked': _getData(13), + 'tokenPassenger': _getData(9), + 'direction': mapUrl, + 'DurationToPassenger': _getData(15), + 'rideId': rideId, + 'passengerId': _getData(7), + 'driverId': box + .read(BoxName.driverID) + .toString(), // Current driver accepting + 'durationOfRideValue': _getData(19), + 'paymentAmount': _getData(2), + 'paymentMethod': _getData(13) == 'true' + ? 'visa' + : 'cash', + 'isHaveSteps': _getData(20), + 'step0': _getData(21), + 'step1': _getData(22), + 'step2': _getData(23), + 'step3': _getData(24), + 'step4': _getData(25), + 'passengerWalletBurc': _getData(26), + 'timeOfOrder': DateTime.now().toString(), + 'totalPassenger': _getData( + 2), // This is likely trip cost for passenger + 'carType': _getData(31), + 'kazan': + _getData(32), // Driver's commission/cut + 'startNameLocation': _getData(29), + 'endNameLocation': _getData(30), + }); + Get.to(() => PassengerLocationMapPage(), + arguments: + box.read(BoxName.rideArguments)); + } else { + Get.defaultDialog( + title: + "This ride is already taken by another driver." + .tr, + middleText: '', + titleStyle: AppStyle.title, + middleTextStyle: AppStyle.title, + confirm: MyElevatedButton( + title: 'Ok'.tr, + onPressed: () { + Get.back(); // Close dialog + Get.back(); // Close order request screen + })); + } + }, ), ), - Text( - orderRequestController.myList[31].toString().tr, - style: AppStyle.title - .copyWith(color: AppColor.greenColor), + const SizedBox(width: 10), + // --- TIMER --- + GetBuilder( + id: 'timerUpdate', // Ensure controller calls update(['timerUpdate']) for this + builder: (timerCtrl) { + final isNearEnd = + timerCtrl.remainingTimeSpeed <= 5; + return SizedBox( + width: 60, + height: 60, + child: Stack( + alignment: Alignment.center, + children: [ + CircularProgressIndicator( + value: timerCtrl.progressSpeed, + color: isNearEnd + ? Colors.redAccent + : AppColor.primaryColor, + strokeWidth: 5, + backgroundColor: Colors.grey.shade300, + ), + Text('${timerCtrl.remainingTimeSpeed}', + style: AppStyle.headTitle2.copyWith( + color: isNearEnd + ? Colors.redAccent + : AppColor.writeColor ?? + Colors.black, + )), + ], + ), + ); + }, + ), + const SizedBox(width: 10), + Expanded( + child: MyElevatedButton( + title: 'Refuse Order'.tr, + onPressed: () async { + controller.endTimer(); + controller.refuseOrder(rideId); + controller.addRideToNotificationDriverString( + rideId, + _getData(29), + _getData(30), + '${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}', + '${DateTime.now().hour}:${DateTime.now().minute}', + _getData(2), + _getData(7), + 'wait', + _getData(31), + _getData(33), + _getData(2), + _getData(5), + _getData(4)); + // Get.back(); // refuseOrder or endTimer should handle navigation if needed. + // If not, add Get.back(); here. + }, + kolor: AppColor.redColor, + ), ), ], - )), - ), - const SizedBox( - height: 5, - ), - Container( - height: Get.height * .3, - width: Get.width * .9, - decoration: AppStyle.boxDecoration1, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 5, vertical: 1), - child: ListView( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Icon( - Icons.arrow_circle_up, - color: AppColor.greenColor, - ), - Text( - orderRequestController.myList[12] + - ' ' + - ' (${orderRequestController.myList[11]}) ', - style: AppStyle.title, - ), - ], - ), - Text( - orderRequestController.myList[29], - style: AppStyle.title, - ), - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Icon( - Icons.arrow_circle_down, - color: AppColor.redColor, - ), - Text( - orderRequestController.myList[5] + - ' ' + - ' (${orderRequestController.myList[4]}) ', - style: AppStyle.title, - ), - ], - ), - Text( - orderRequestController.myList[30], - style: AppStyle.title, - ), - ], - ), - ], + ), ), - ), + ], ), - Padding( - padding: const EdgeInsets.all(8.0), - child: RichText( - text: TextSpan( - text: "Passenger name: " - .tr, // Changed text to be more generic - style: AppStyle.subtitle, - children: [ - TextSpan( - text: orderRequestController.myList[8], - style: AppStyle - .title), // Assuming orderRequestController.myList[8] holds passenger name - TextSpan(text: ' (', style: AppStyle.subtitle), - TextSpan( - text: orderRequestController.myList[33].toString(), - style: AppStyle - .title), // Assuming 'rate' holds the passenger rate - TextSpan(text: ' ⭐)', style: AppStyle.subtitle), - ], + ), + ), + ), + ); + }, + ); + } + + // Helper widget for location cards to reduce repetition and improve readability + Widget _buildLocationCard( + {required IconData icon, + required Color iconColor, + required String title, + required String subtitle, + required String fullAddress}) { + return Card( + elevation: 2, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + margin: const EdgeInsets.symmetric( + vertical: 4), // Add a little vertical margin between cards + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(icon, color: iconColor, size: 28), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "$title $subtitle" + .trim(), // Trim to avoid extra spaces if subtitle is empty + style: AppStyle.title.copyWith(fontWeight: FontWeight.w600), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + if (fullAddress.isNotEmpty) ...[ + const SizedBox(height: 3), + Text( + fullAddress, + style: AppStyle.subtitle + .copyWith(fontSize: 13, color: AppColor.greyColor), + maxLines: 2, // Allow up to 2 lines for address + overflow: TextOverflow.ellipsis, ), - ), - ), - - // Padding( - // padding: const EdgeInsets.all(4), - // child: Container( - // color: AppColor.greenColor.withOpacity(.5), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - // children: [ - // RichText( - // text: TextSpan( - // text: 'Cost Of Trip IS '.tr, - // style: AppStyle.title, - // children: [ - // TextSpan( - // text: orderRequestController.myList[26], style: AppStyle.headTitle2), - // ], - // ), - // ), - // RichText( - // text: TextSpan( - // text: 'Total net'.tr, - // style: AppStyle.title, - // children: [ - // TextSpan( - // text: (double.parse(orderRequestController.myList[2]) - - // double.parse(orderRequestController.myList[32])) - // .toStringAsFixed(2), - // style: AppStyle.headTitle2), - // ], - // ), - // ), - // ], - // ), - // ), - // ), - - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MyElevatedButton( - kolor: AppColor.greenColor, - title: 'Accept Order'.tr, - onPressed: () async { - Get.put(HomeCaptainController()).changeRideId(); - box.write(BoxName.statusDriverLocation, 'on'); - var res = await CRUD().post( - link: AppLink.updateStausFromSpeed, - payload: { - 'id': orderRequestController.myList[16], - 'rideTimeStart': DateTime.now().toString(), - 'status': 'Apply', - 'driver_id': box.read(BoxName.driverID), - }); - if (AppLink.endPoint != AppLink.seferCairoServer) { - CRUD().post( - link: - "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php", - payload: { - 'id': orderRequestController.myList[16], - 'rideTimeStart': DateTime.now().toString(), - 'status': 'Apply', - 'driver_id': box.read(BoxName.driverID), - }); - } - if (res != "failure") { - box.write(BoxName.statusDriverLocation, 'on'); - orderRequestController.changeApplied(); - List bodyToPassenger = [ - box.read(BoxName.driverID).toString(), - box.read(BoxName.nameDriver).toString(), - box.read(BoxName.tokenDriver).toString(), - ]; - FirebaseMessagesController() - .sendNotificationToPassengerToken( - 'Accepted Ride', - 'your ride is applied'.tr, - orderRequestController - .arguments['DriverList'][9] - .toString(), - bodyToPassenger, - 'start.wav'); - CRUD().postFromDialogue( - link: AppLink.addDriverOrder, - payload: { - 'driver_id': orderRequestController.myList[6] - .toString(), - 'order_id': - orderRequestController.body.toString(), - 'status': 'Apply' - }); - // CRUD().postFromDialogue( - // link: AppLink.addDriverOrder, - // payload: { - // 'driver_id': orderRequestController.myList[6] - // .toString(), - // 'order_id': orderRequestController.myList[16] - // .toString(), - // 'status': 'Apply' - // }); - - if (AppLink.endPoint != AppLink.seferCairoServer) { - CRUD().post( - link: - "${AppLink.endPoint}/ride/driver_order/add.php", - payload: { - 'driver_id': orderRequestController - .myList[6] - .toString(), - // box.read(BoxName.driverID).toString(), - 'order_id': orderRequestController - .myList[16] - .toString(), - 'status': 'Apply' - }); - } - - Get.back(); - box.write(BoxName.rideArguments, { - 'passengerLocation': - orderRequestController.myList[0].toString(), - 'passengerDestination': - orderRequestController.myList[1].toString(), - 'Duration': - orderRequestController.myList[4].toString(), - 'totalCost': - orderRequestController.myList[26].toString(), - 'Distance': - orderRequestController.myList[5].toString(), - 'name': - orderRequestController.myList[8].toString(), - 'phone': - orderRequestController.myList[10].toString(), - 'email': - orderRequestController.myList[28].toString(), - 'WalletChecked': - orderRequestController.myList[13].toString(), - 'tokenPassenger': - orderRequestController.myList[9].toString(), - 'direction': - 'https://www.google.com/maps/dir/${orderRequestController.myList[0]}/${orderRequestController.myList[1]}/', - 'DurationToPassenger': - orderRequestController.myList[15].toString(), - 'rideId': - orderRequestController.myList[16].toString(), - 'passengerId': - orderRequestController.myList[7].toString(), - 'driverId': - orderRequestController.myList[18].toString(), - 'durationOfRideValue': - orderRequestController.myList[19].toString(), - 'paymentAmount': - orderRequestController.myList[2].toString(), - 'paymentMethod': orderRequestController.myList[13] - .toString() == - 'true' - ? 'visa' - : 'cash', - 'isHaveSteps': - orderRequestController.myList[20].toString(), - 'step0': - orderRequestController.myList[21].toString(), - 'step1': - orderRequestController.myList[22].toString(), - 'step2': - orderRequestController.myList[23].toString(), - 'step3': - orderRequestController.myList[24].toString(), - 'step4': - orderRequestController.myList[25].toString(), - 'passengerWalletBurc': - orderRequestController.myList[26].toString(), - 'timeOfOrder': DateTime.now().toString(), - 'totalPassenger': - orderRequestController.myList[2].toString(), - 'carType': - orderRequestController.myList[31].toString(), - 'kazan': - orderRequestController.myList[32].toString(), - 'startNameLocation': - orderRequestController.myList[29].toString(), - 'endNameLocation': - orderRequestController.myList[30].toString(), - }); - Get.to(() => PassengerLocationMapPage(), - arguments: box.read(BoxName.rideArguments)); - } else { - Get.defaultDialog( - title: - "This ride is already taken by another driver." - .tr, - middleText: '', - titleStyle: AppStyle.title, - middleTextStyle: AppStyle.title, - confirm: MyElevatedButton( - title: 'Ok'.tr, - onPressed: () { - Get.back(); - Get.back(); - // Get.back(); - })); - } - // }); - // Get.back(); - }, - ), - GetBuilder( - builder: (timerController) { - final isNearEnd = - timerController.remainingTimeSpeed <= - 5; // Define a threshold for "near end" - - return Stack( - alignment: Alignment.center, - children: [ - CircularProgressIndicator( - value: timerController.progressSpeed, - // Set the color based on the "isNearEnd" condition - color: isNearEnd ? Colors.red : Colors.blue, - ), - Text( - '${timerController.remainingTimeSpeed}', - style: AppStyle.number, - ), - ], - ); - }, - ), - MyElevatedButton( - title: 'Refuse Order'.tr, - onPressed: () async { - // Get.defaultDialog( - // title: 'Reject Order'.tr, - // titleStyle: AppStyle.title, - // content: Column( - // children: [ - // IconButton( - // onPressed: () async { - // await Get.find() - // .speakText( - // 'You can decline a request without any cost' - // .tr); - // }, - // icon: const Icon(Icons.headphones), - // ), - // Text( - // 'You can decline a request without any cost' - // .tr, - // style: AppStyle.title, - // ) - // ], - // ), - // confirm: MyElevatedButton( - // title: 'Ok'.tr, - // onPressed: () { - // Get.back(); - // Get.back(); - // })); - orderRequestController.endTimer(); - - orderRequestController.refuseOrder( - orderRequestController.myList[16].toString(), - ); - orderRequestController.addRideToNotificationDriverString( - orderRequestController.myList[16].toString(), - orderRequestController.myList[29].toString(), - orderRequestController.myList[30].toString(), - '${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}', - '${DateTime.now().hour}:${DateTime.now().minute}', - orderRequestController.myList[2].toString(), - orderRequestController.myList[7].toString(), - 'wait', - orderRequestController.myList[31].toString(), - orderRequestController.myList[33].toString(), - orderRequestController.myList[2].toString(), - orderRequestController.myList[5].toString(), - orderRequestController.myList[4].toString()); - }, - kolor: AppColor.redColor, - ), - ], - ), - ) - ], - ) + ] + ], + ), + ), ], - isleading: true); - }); + ), + ), + ); } } diff --git a/lib/views/home/my_wallet/points_captain.dart b/lib/views/home/my_wallet/points_captain.dart index 5ceccce..53a461a 100755 --- a/lib/views/home/my_wallet/points_captain.dart +++ b/lib/views/home/my_wallet/points_captain.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:sefer_driver/constant/colors.dart'; @@ -5,9 +7,13 @@ import 'package:sefer_driver/constant/style.dart'; import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart'; import 'package:sefer_driver/controller/payment/payment_controller.dart'; import 'package:sefer_driver/views/widgets/mydialoug.dart'; +import 'package:webview_flutter/webview_flutter.dart'; import '../../../constant/box_name.dart'; +import '../../../constant/links.dart'; +import '../../../controller/functions/crud.dart'; import '../../../main.dart'; +import '../../../print.dart'; import '../../widgets/elevated_btn.dart'; import '../../widgets/my_textField.dart'; @@ -44,21 +50,34 @@ class PointsCaptain extends StatelessWidget { title: 'Pay with Credit Card'.tr, onPressed: () async { Get.back(); - await paymentController.payWithPayMob( - context, - pricePoint.toStringAsFixed(2), - box.read(BoxName.countryCode) == 'Egypt' - ? 'EGP' - : 'JOD', () async { - // await captainWalletController.getPaymentId( - // 'visa-in', pricePoint); - await captainWalletController.addDriverWallet( - 'visa-in', countPoint, pricePoint); - await captainWalletController.addSeferWallet( - 'visa-in', pricePoint.toString()); - await captainWalletController - .getCaptainWalletFromBuyPoints(); - }); + + var res = await CRUD().postWallet( + // link: AppLink.payWithPayMobWalletPasenger, + link: AppLink.payWithPayMobCardDriver, + payload: { + "amount": pricePoint.toStringAsFixed(2), + "email": box.read(BoxName.emailDriver), + "first_name": (box + .read(BoxName.nameDriver) + .toString() + .split(' ')[0]) + .toString(), + "last_name": (box + .read(BoxName.nameDriver) + .toString() + .split(' ')[1]) + .toString(), + "phone_number": (box.read(BoxName.phoneDriver)), + }); + // var d = jsonDecode(res); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PaymentScreen( + iframeUrl: res['message'], + countPrice: countPoint.toString()), + ), + ); }, //51524 ), // Add some spacing between buttons @@ -88,31 +107,45 @@ class PointsCaptain extends StatelessWidget { BoxName.phoneWallet, paymentController .walletphoneController.text); - await paymentController.payWithPayMobWallet( - context, - pricePoint.toStringAsFixed(2), - box.read(BoxName.countryCode) == 'Egypt' - ? 'EGP' - : 'JOD', () async { - // await captainWalletController - // .getPaymentId('visa-in', pricePoint); - await captainWalletController - .addDriverWallet('visa-in', - countPoint, pricePoint); - await captainWalletController - .addSeferWallet( - 'visa-in', pricePoint.toString()); - await captainWalletController - .getCaptainWalletFromBuyPoints(); - }); + var res = await CRUD().postWallet( + // link: AppLink.payWithPayMobWalletPasenger, + link: AppLink.payWithWallet, + payload: { + "amount": + pricePoint.toStringAsFixed(2), + "email": + box.read(BoxName.emailDriver), + "first_name": (box + .read(BoxName.nameDriver) + .toString() + .split(' ')[0]) + .toString(), + "last_name": (box + .read(BoxName.nameDriver) + .toString() + .split(' ')[1]) + .toString(), + "phone_number": + (box.read(BoxName.phoneWallet)), + }); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + PaymentScreenWallet( + iframeUrl: res['message'], + countPrice: + countPoint.toString()), + ), + ); } - MyDialog().getDialog( - 'phone number is wrong'.tr, - '', - () { - Get.back(); - }, - ); + // MyDialog().getDialog( + // 'phone number is wrong'.tr, + // '', + // () { + // Get.back(); + // }, + // ); // Get.back(); })); }, @@ -173,3 +206,311 @@ class PointsCaptain extends StatelessWidget { ); } } + +class PaymentScreen extends StatefulWidget { + final String iframeUrl; + final String countPrice; + + const PaymentScreen( + {required this.iframeUrl, Key? key, required this.countPrice}) + : super(key: key); + + @override + State createState() => _PaymentScreenState(); +} + +class _PaymentScreenState extends State { + late final WebViewController _controller; + final controller = Get.find(); + @override + void initState() { + super.initState(); + + _controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate(NavigationDelegate( + onPageFinished: (url) { + if (url.contains("success")) { + _fetchPaymentStatus(); // ✅ استدعاء الويب هوك بعد نجاح الدفع + } else if (url.contains("failed")) { + showCustomDialog( + title: "Error".tr, + message: 'Payment Failed'.tr, // يتم جلب رسالة الخطأ من الخادم + isSuccess: false, + ); + } + }, + )) + ..loadRequest(Uri.parse(widget.iframeUrl)); + } + + Future _fetchPaymentStatus() async { + final String userId = box.read(BoxName.phoneDriver); + await Future.delayed(const Duration(seconds: 2)); + + try { + final response = await CRUD().postWallet( + link: AppLink.paymetVerifyDriver, + payload: { + 'user_id': userId, + 'driverID': box.read(BoxName.driverID), + 'paymentMethod': 'visa-in', + }, + ); + + if (response != 'failure' && response != 'token_expired') { + if (response['status'] == 'success') { + final payment = response['message']; + final amount = payment['amount'].toString(); + final bonus = payment['bonus'].toString(); + final paymentID = payment['paymentID'].toString(); + + await controller.getCaptainWalletFromBuyPoints(); + + showCustomDialog( + title: "payment_success".tr, + message: + "${"transaction_id".tr}: $paymentID\n${"amount_paid".tr}: $amount EGP\n${"bonus_added".tr}: $bonus ${"points".tr}", + isSuccess: true, + ); + } else { + showCustomDialog( + title: "transaction_failed".tr, + message: response['message'].toString(), + isSuccess: false, + ); + } + } else { + showCustomDialog( + title: "connection_failed".tr, + message: response.toString(), + isSuccess: false, + ); + } + } catch (e) { + showCustomDialog( + title: "server_error".tr, + message: "server_error_message".tr, + isSuccess: false, + ); + } + } + + void showCustomDialog({ + required String title, + required String message, + required bool isSuccess, + }) { + showDialog( + barrierDismissible: false, + context: Get.context!, + builder: (BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + title: Row( + children: [ + Icon( + isSuccess ? Icons.check_circle : Icons.error, + color: isSuccess ? Colors.green : Colors.red, + ), + const SizedBox(width: 8), + Text( + title, + style: TextStyle( + color: isSuccess ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + content: Text( + message, + style: const TextStyle(fontSize: 16), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + style: TextButton.styleFrom( + backgroundColor: isSuccess ? Colors.green : Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + child: Text( + "OK", + style: const TextStyle(color: Colors.white), + ), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('إتمام الدفع')), + body: WebViewWidget(controller: _controller), + ); + } +} + +class PaymentScreenWallet extends StatefulWidget { + final String iframeUrl; + final String countPrice; + + const PaymentScreenWallet( + {required this.iframeUrl, Key? key, required this.countPrice}) + : super(key: key); + + @override + State createState() => _PaymentScreenWalletState(); +} + +class _PaymentScreenWalletState extends State { + late final WebViewController _controller; + final controller = Get.find(); + @override + void initState() { + super.initState(); + + _controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate(NavigationDelegate( + onPageFinished: (url) { + if (url.contains("success")) { + _fetchPaymentStatus(); // ✅ استدعاء الويب هوك بعد نجاح الدفع + } else if (url.contains("failed")) { + showCustomDialog( + title: "Error".tr, + message: 'Payment Failed'.tr, // يتم جلب رسالة الخطأ من الخادم + isSuccess: false, + ); + } + }, + )) + ..loadRequest(Uri.parse(widget.iframeUrl)); + } + + Future _fetchPaymentStatus() async { + final String userId = '+2' + box.read(BoxName.phoneWallet); + await Future.delayed(const Duration(seconds: 2)); + + try { + final response = await CRUD().postWallet( + link: AppLink.paymetVerifyDriver, + payload: { + 'user_id': userId, + 'driverID': box.read(BoxName.driverID), + 'paymentMethod': 'visa-in', + }, + ); + + if (response != 'failure' && response != 'token_expired') { + if (response['status'] == 'success') { + final payment = response['message']; + final amount = payment['amount'].toString(); + final bonus = payment['bonus'].toString(); + final paymentID = payment['paymentID'].toString(); + + await controller.getCaptainWalletFromBuyPoints(); + + showCustomDialog( + title: "payment_success".tr, + message: + "${"transaction_id".tr}: $paymentID\n${"amount_paid".tr}: $amount EGP\n${"bonus_added".tr}: $bonus ${"points".tr}", + isSuccess: true, + ); + } else { + showCustomDialog( + title: "transaction_failed".tr, + message: response['message'].toString(), + isSuccess: false, + ); + } + } else { + showCustomDialog( + title: "connection_failed".tr, + message: response.toString(), + isSuccess: false, + ); + } + } catch (e) { + showCustomDialog( + title: "server_error".tr, + message: "server_error_message".tr, + isSuccess: false, + ); + } + } + + void showCustomDialog({ + required String title, + required String message, + required bool isSuccess, + }) { + showDialog( + barrierDismissible: false, + context: Get.context!, + builder: (BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + title: Row( + children: [ + Icon( + isSuccess ? Icons.check_circle : Icons.error, + color: isSuccess ? Colors.green : Colors.red, + ), + const SizedBox(width: 8), + Text( + title, + style: TextStyle( + color: isSuccess ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + content: Text( + message, + style: const TextStyle(fontSize: 16), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + style: TextButton.styleFrom( + backgroundColor: isSuccess ? Colors.green : Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + child: Text( + "OK", + style: const TextStyle(color: Colors.white), + ), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('إتمام الدفع')), + body: WebViewWidget(controller: _controller), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index d66cb90..7199232 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,6 +93,7 @@ dependencies: jwt_decoder: ^2.0.1 jailbreak_root_detection: ^1.1.5 device_info_plus: ^11.3.0 + # flutter_isolate: ^2.1.0 # lingo_hunter: ^1.0.3 dev_dependencies: