From cf4321a6906dd6d9ff759324a30bfbea89a89851 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 23 May 2026 10:19:24 -0600 Subject: [PATCH] fix: add project contents --- HackOnLinces_app | 1 - HackOnLinces_app/aplicacion_hack/.gitignore | 45 + HackOnLinces_app/aplicacion_hack/.metadata | 45 + HackOnLinces_app/aplicacion_hack/README.md | 17 + .../aplicacion_hack/analysis_options.yaml | 28 + .../aplicacion_hack/android/.gitignore | 14 + .../android/app/build.gradle.kts | 50 + .../android/app/google-services.json | 29 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 49 + .../example/aplicacion_hack/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../aplicacion_hack/android/build.gradle.kts | 35 + .../aplicacion_hack/android/gradle.properties | 6 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../android/settings.gradle.kts | 29 + .../aplicacion_hack/assets/images/bottle.png | Bin 0 -> 37371 bytes .../aplicacion_hack/assets/images/globo.png | Bin 0 -> 57585 bytes .../assets/images/megafono.png | Bin 0 -> 56411 bytes .../aplicacion_hack/assets/images/planta.png | Bin 0 -> 33617 bytes .../aplicacion_hack/assets/images/recycle.jpg | Bin 0 -> 10197 bytes .../aplicacion_hack/assets/images/reloj.jpg | Bin 0 -> 25300 bytes .../aplicacion_hack/assets/images/reloj.png | Bin 0 -> 55401 bytes .../aplicacion_hack/firebase.json | 1 + .../aplicacion_hack/ios/.gitignore | 34 + .../ios/Flutter/AppFrameworkInfo.plist | 24 + .../ios/Flutter/Debug.xcconfig | 1 + .../ios/Flutter/Release.xcconfig | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 644 ++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 119 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../ios/Runner/AppDelegate.swift | 16 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../aplicacion_hack/ios/Runner/Info.plist | 70 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../ios/Runner/SceneDelegate.swift | 6 + .../ios/RunnerTests/RunnerTests.swift | 12 + .../aplicacion_hack/lib/firebase_options.dart | 88 + .../aplicacion_hack/lib/main.dart | 114 ++ .../lib/screens/analytics_screen.dart | 1092 ++++++++++ .../lib/screens/home_screen.dart | 927 +++++++++ .../lib/screens/info_screen.dart | 808 ++++++++ .../lib/screens/login_screen.dart | 402 ++++ .../lib/screens/mapa_rutas_screen.dart | 523 +++++ .../lib/screens/reporte_screen.dart | 538 +++++ .../lib/screens/route_list_screen.dart | 354 ++++ .../lib/services/api_service.dart | 483 +++++ .../aplicacion_hack/linux/.gitignore | 1 + .../aplicacion_hack/linux/CMakeLists.txt | 128 ++ .../linux/flutter/CMakeLists.txt | 88 + .../linux/runner/CMakeLists.txt | 26 + .../aplicacion_hack/linux/runner/main.cc | 6 + .../linux/runner/my_application.cc | 148 ++ .../linux/runner/my_application.h | 21 + .../aplicacion_hack/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../macos/Runner.xcodeproj/project.pbxproj | 729 +++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 117 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 ++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../aplicacion_hack/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../macos/Runner/Release.entitlements | 8 + .../macos/RunnerTests/RunnerTests.swift | 12 + HackOnLinces_app/aplicacion_hack/pubspec.yaml | 64 + .../aplicacion_hack/web/favicon.png | Bin 0 -> 917 bytes .../aplicacion_hack/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../aplicacion_hack/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes .../aplicacion_hack/web/index.html | 46 + .../aplicacion_hack/web/manifest.json | 35 + .../aplicacion_hack/windows/.gitignore | 17 + .../aplicacion_hack/windows/CMakeLists.txt | 108 + .../windows/flutter/CMakeLists.txt | 109 + .../windows/runner/CMakeLists.txt | 40 + .../aplicacion_hack/windows/runner/Runner.rc | 121 ++ .../windows/runner/flutter_window.cpp | 71 + .../windows/runner/flutter_window.h | 33 + .../aplicacion_hack/windows/runner/main.cpp | 43 + .../aplicacion_hack/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 14 + .../aplicacion_hack/windows/runner/utils.cpp | 69 + .../aplicacion_hack/windows/runner/utils.h | 19 + .../windows/runner/win32_window.cpp | 288 +++ .../windows/runner/win32_window.h | 102 + HackOnLinces_app/backend/analytics.py | 398 ++++ HackOnLinces_app/backend/analytics_real.py | 414 ++++ HackOnLinces_app/backend/hackathon.db | Bin 0 -> 49152 bytes HackOnLinces_app/backend/main.py | 1760 +++++++++++++++++ HackOnLinces_app/hackathon.db | Bin 0 -> 24576 bytes 145 files changed, 12545 insertions(+), 1 deletion(-) delete mode 160000 HackOnLinces_app create mode 100644 HackOnLinces_app/aplicacion_hack/.gitignore create mode 100644 HackOnLinces_app/aplicacion_hack/.metadata create mode 100644 HackOnLinces_app/aplicacion_hack/README.md create mode 100644 HackOnLinces_app/aplicacion_hack/analysis_options.yaml create mode 100644 HackOnLinces_app/aplicacion_hack/android/.gitignore create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/build.gradle.kts create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/google-services.json create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/debug/AndroidManifest.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/AndroidManifest.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/kotlin/com/example/aplicacion_hack/MainActivity.kt create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable/launch_background.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values-night/styles.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values/styles.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/app/src/profile/AndroidManifest.xml create mode 100644 HackOnLinces_app/aplicacion_hack/android/build.gradle.kts create mode 100644 HackOnLinces_app/aplicacion_hack/android/gradle.properties create mode 100644 HackOnLinces_app/aplicacion_hack/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 HackOnLinces_app/aplicacion_hack/android/settings.gradle.kts create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/bottle.png create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/globo.png create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/megafono.png create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/planta.png create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/recycle.jpg create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/reloj.jpg create mode 100644 HackOnLinces_app/aplicacion_hack/assets/images/reloj.png create mode 100644 HackOnLinces_app/aplicacion_hack/firebase.json create mode 100644 HackOnLinces_app/aplicacion_hack/ios/.gitignore create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Flutter/AppFrameworkInfo.plist create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Flutter/Debug.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Flutter/Release.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.pbxproj create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/AppDelegate.swift create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/Main.storyboard create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Info.plist create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/Runner-Bridging-Header.h create mode 100644 HackOnLinces_app/aplicacion_hack/ios/Runner/SceneDelegate.swift create mode 100644 HackOnLinces_app/aplicacion_hack/ios/RunnerTests/RunnerTests.swift create mode 100644 HackOnLinces_app/aplicacion_hack/lib/firebase_options.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/main.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/analytics_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/home_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/info_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/login_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/mapa_rutas_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/reporte_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/screens/route_list_screen.dart create mode 100644 HackOnLinces_app/aplicacion_hack/lib/services/api_service.dart create mode 100644 HackOnLinces_app/aplicacion_hack/linux/.gitignore create mode 100644 HackOnLinces_app/aplicacion_hack/linux/CMakeLists.txt create mode 100644 HackOnLinces_app/aplicacion_hack/linux/flutter/CMakeLists.txt create mode 100644 HackOnLinces_app/aplicacion_hack/linux/runner/CMakeLists.txt create mode 100644 HackOnLinces_app/aplicacion_hack/linux/runner/main.cc create mode 100644 HackOnLinces_app/aplicacion_hack/linux/runner/my_application.cc create mode 100644 HackOnLinces_app/aplicacion_hack/linux/runner/my_application.h create mode 100644 HackOnLinces_app/aplicacion_hack/macos/.gitignore create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Release.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.pbxproj create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/AppDelegate.swift create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Debug.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Release.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Warnings.xcconfig create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/DebugProfile.entitlements create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Info.plist create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/MainFlutterWindow.swift create mode 100644 HackOnLinces_app/aplicacion_hack/macos/Runner/Release.entitlements create mode 100644 HackOnLinces_app/aplicacion_hack/macos/RunnerTests/RunnerTests.swift create mode 100644 HackOnLinces_app/aplicacion_hack/pubspec.yaml create mode 100644 HackOnLinces_app/aplicacion_hack/web/favicon.png create mode 100644 HackOnLinces_app/aplicacion_hack/web/icons/Icon-192.png create mode 100644 HackOnLinces_app/aplicacion_hack/web/icons/Icon-512.png create mode 100644 HackOnLinces_app/aplicacion_hack/web/icons/Icon-maskable-192.png create mode 100644 HackOnLinces_app/aplicacion_hack/web/icons/Icon-maskable-512.png create mode 100644 HackOnLinces_app/aplicacion_hack/web/index.html create mode 100644 HackOnLinces_app/aplicacion_hack/web/manifest.json create mode 100644 HackOnLinces_app/aplicacion_hack/windows/.gitignore create mode 100644 HackOnLinces_app/aplicacion_hack/windows/CMakeLists.txt create mode 100644 HackOnLinces_app/aplicacion_hack/windows/flutter/CMakeLists.txt create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/CMakeLists.txt create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/Runner.rc create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.cpp create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.h create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/main.cpp create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/resource.h create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/resources/app_icon.ico create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/runner.exe.manifest create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/utils.cpp create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/utils.h create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.cpp create mode 100644 HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.h create mode 100644 HackOnLinces_app/backend/analytics.py create mode 100644 HackOnLinces_app/backend/analytics_real.py create mode 100644 HackOnLinces_app/backend/hackathon.db create mode 100644 HackOnLinces_app/backend/main.py create mode 100644 HackOnLinces_app/hackathon.db diff --git a/HackOnLinces_app b/HackOnLinces_app deleted file mode 160000 index 88b5802..0000000 --- a/HackOnLinces_app +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 88b580229c63f17b87742aa1f7b9821533484066 diff --git a/HackOnLinces_app/aplicacion_hack/.gitignore b/HackOnLinces_app/aplicacion_hack/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/HackOnLinces_app/aplicacion_hack/.metadata b/HackOnLinces_app/aplicacion_hack/.metadata new file mode 100644 index 0000000..768732a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "559ffa3f75e7402d65a8def9c28389a9b2e6fe42" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + - platform: android + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + - platform: ios + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + - platform: linux + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + - platform: macos + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + - platform: web + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + - platform: windows + create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/HackOnLinces_app/aplicacion_hack/README.md b/HackOnLinces_app/aplicacion_hack/README.md new file mode 100644 index 0000000..5c52d84 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/README.md @@ -0,0 +1,17 @@ +# aplicacion_hack + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/HackOnLinces_app/aplicacion_hack/analysis_options.yaml b/HackOnLinces_app/aplicacion_hack/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/HackOnLinces_app/aplicacion_hack/android/.gitignore b/HackOnLinces_app/aplicacion_hack/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/HackOnLinces_app/aplicacion_hack/android/app/build.gradle.kts b/HackOnLinces_app/aplicacion_hack/android/app/build.gradle.kts new file mode 100644 index 0000000..d6ba38a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/build.gradle.kts @@ -0,0 +1,50 @@ +plugins { + id("com.android.application") + id("com.google.gms.google-services") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.aplicacion_hack" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + isCoreLibraryDesugaringEnabled = true + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.aplicacion_hack" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +flutter { + source = "../.." +} +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") +} diff --git a/HackOnLinces_app/aplicacion_hack/android/app/google-services.json b/HackOnLinces_app/aplicacion_hack/android/app/google-services.json new file mode 100644 index 0000000..83ed588 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "338042609701", + "project_id": "hackon-58b23", + "storage_bucket": "hackon-58b23.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:338042609701:android:0f0f92d7f895794f371fcc", + "android_client_info": { + "package_name": "com.example.aplicacion_hack" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBuT70SbADLeg92ll8keCySI8I4eYyqyLw" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/debug/AndroidManifest.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/AndroidManifest.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..23a57a5 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/kotlin/com/example/aplicacion_hack/MainActivity.kt b/HackOnLinces_app/aplicacion_hack/android/app/src/main/kotlin/com/example/aplicacion_hack/MainActivity.kt new file mode 100644 index 0000000..88cf8ba --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/main/kotlin/com/example/aplicacion_hack/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.aplicacion_hack + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable-v21/launch_background.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable/launch_background.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values-night/styles.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values/styles.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/app/src/profile/AndroidManifest.xml b/HackOnLinces_app/aplicacion_hack/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/HackOnLinces_app/aplicacion_hack/android/build.gradle.kts b/HackOnLinces_app/aplicacion_hack/android/build.gradle.kts new file mode 100644 index 0000000..a2b1a8a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/build.gradle.kts @@ -0,0 +1,35 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + // Añadimos la herramienta de Google Services para que Android la pueda usar + classpath("com.google.gms:google-services:4.4.1") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} \ No newline at end of file diff --git a/HackOnLinces_app/aplicacion_hack/android/gradle.properties b/HackOnLinces_app/aplicacion_hack/android/gradle.properties new file mode 100644 index 0000000..e96108c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +# This newDsl flag was added by the Flutter template +android.newDsl=false +# This builtInKotlin flag was added by the Flutter template +android.builtInKotlin=false diff --git a/HackOnLinces_app/aplicacion_hack/android/gradle/wrapper/gradle-wrapper.properties b/HackOnLinces_app/aplicacion_hack/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2d428bf --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip diff --git a/HackOnLinces_app/aplicacion_hack/android/settings.gradle.kts b/HackOnLinces_app/aplicacion_hack/android/settings.gradle.kts new file mode 100644 index 0000000..5ea2823 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/android/settings.gradle.kts @@ -0,0 +1,29 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "9.0.1" apply false + // START: FlutterFire Configuration + id("com.google.gms.google-services") version("4.3.15") apply false + // END: FlutterFire Configuration + id("org.jetbrains.kotlin.android") version "2.3.20" apply false +} + +include(":app") diff --git a/HackOnLinces_app/aplicacion_hack/assets/images/bottle.png b/HackOnLinces_app/aplicacion_hack/assets/images/bottle.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8bb5abcbe15bb616d5b861ff1882328a8d43b0 GIT binary patch literal 37371 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

D5Qe2N#pd3` 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 z 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 Sd-OiwAt@`om9Lch82lg9#;f@N literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/assets/images/megafono.png b/HackOnLinces_app/aplicacion_hack/assets/images/megafono.png new file mode 100644 index 0000000000000000000000000000000000000000..a8c99eec2ff069f1c0b97ababb20e24248a7b935 GIT binary patch literal 56411 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 z zh1i6qX?1m7S4Zz28M%~Br=!tm5c=sv0tO|>g@<8!i9ELm0f&4CMVB|(nH#*9DJ;@9 zUl{s$7|;oSg#Xl3MdN!zw7ra`~k;t z1?ae&yRP2+_{d-uM%?WuT`>4i)y__Tmo=O z1l;pB{^ZnoheW&V<=aI9C*doNRz5B}Obki@!#Dr0dwy9_72e+ZcTa2rgP+-@`DAkp zXg%;<+8SU!lrT{g%Dr~5*X#D>Xt(H@PuG=Op*+q00`4w%G^AzI4;Fp|1$|0(Uk$ zb!KOIdhPVFFU|FDXo)99;coYO0GulW&Xs0x1a@A4gT4hi&+~xi37X)M&>Q|m3_W((c)@6V4JV`|`}+ z#CU?U^c1VEgSg*%qFl4xbnq7?IeBG``#R8vqd3aa-dKX)iUQCApCX8VSqHHp-RLV# z^OFz1)|z3=-8a&m3Z~+@U@DbrP@W5LF_HAD{%E$pPG1B#G~7qdyypbr_Su{J^B675 zGP?%Afp-1(y&=RWh8tf2iW?7FyPk3uQ8>A#UL2S`g| zZ7W8A!$+KizxKgkix{8G=Zl%9wqXeO&|r5oE{4^5XiT7{B?OIGn|%xLRC=Us$_jW^k zq!h8>=71$hfysvt|J_s9+;w2Ie_*6L8y6D^7n4wwmX?}aE*ecKW>BJpo*VFF(xs%b zcQatYLxz%GPM{=Cue;^QQEapy|E|cnk-&TPDR~Z?+TQKB}1j`|sS9pKm zWkPHr-~gjwKmyH7DtR7&BGLq!J)DjdDg?GeFj3zBI63E z7n6ZNOT4#ff?EhU4FMM-+_P^^?Q}4-(avA_hs4$b5L*p!ECwQfJ6zf1@%sKP(CJeV zinA)qKYYM>tBu2%msWW~f_(oIhTI%Klb_aLK6&6~?@&i79!GE^V;UR7sAUrhwLl9t zGRsuH*MNCK^@hVRd>ak7X=|ff&1*2aMsw z*ue#PW*+8do(L^Nz&UC?$Y&we&{?A<4oMKWL=yb?jILJ%ub)7=MG?}?AYn-Y#wIAI z40Za$;jjwb_xA%FTG%fJYC3LYbbw9^kmmFh^y4hLCi5(xx!_d7?XEJgWo(*&UjOk+^#i;6R^yZ<9lzjke+PaZT1i?W$E2{eSG8 z`)?H26~{ZTJ3BKwyJOGpjCZ{j#)Kf{2Pr<%$;Nhw?8JjAB3wq|nrc4K_?m^jmM zOE#=!X$_w}d(OSz^F5{-e3<%VnJo;@d2{!maZP4u;oAW?xpB1$Zd@No_wJSuUx%Na zYHqHMftF-uWjGv;M8a_84j)nc*HOHm8E{|N7l=j!dfD40aZ|7=6pAMzWzb0=G_==~ zqqrJ#_O+M>DwZb#+(fd%-fG?IyrIt6R8?mviVC-w-wMFVfT@IrjNZFn4U)3t_<`!O zylR}L3mU)M7!HFvFd7I1V({VfAKU4bgDN12TI6 z9E^f`R(hInccXpVFoh;wE=6fbQ`nwWi$;$a>kPg)NCV)LzsVF-nWNC{)F52G!$}~S zF$>`8!C%Ul>^-eFgpf)L;$#L~bhK7C@7yLA zlqdHed7&Em$#4WSt?R%nu26i|=SZbZaM;(-&;ZARKzl_6^q(-O^WR_ei~wGZ36Dl% zwM`{)#9IFAV*qEEfuDFM;qJ!T)ybmK3S}I9Hzy<0;Jy1Bm63(Jk$zn#8Ex%dU zT_nw7HEN^MR7j6hQHWp%zBNoEApCkpi(_u6JO*8j{2i?WtZEa)5yf2^ z@f0T_N}xlG@#CM`Cn;0Uu2fjjFias7WSLCx?YMKZBd%OTLuKtC2W2CEh?PWHWK=p> zT&&{fY=E&t$-Iky0Jxj0^Oi2vak-JU&={}$uxplQ4Eu-u`jHg|gA+F~SmziKqYe9z zgy~;&yz4ROZY=yH^?)7$oKCL}#E*#r9i*j>-`Q`eOV*x4ia0!+5x|Ln+8(&!9OKI| zfE$$a)FrB%$F(jaD;m0fHy&T{(TS6|pPY%y5k2aAW>Ejf@NYTwQGy9V367sAEPoYg z8vjCU=r8BbdI-84%h&fXP7T0~m%#yWmS(OEu4;Fx_pKeM98`H^w~yP$Al7>edY>#_c+_ZcD2=OWETqK`Y|D=f^pw>G})j6S^d{#7+7n5K{+vsqCb1GZ>uya8|;tX-g5xi{2wptiQ6 zA`dr|x*iBuRz?~N3;ez?E>LvqEEWPMp@z|!Lkb# z#5;$M)_Kmo4?y3Jmqs|*{0}f8kwtq3KrWA2Ezy^cUL6SELIK=Hl zcTIJ{G)F4+v=UeM0FO1FEHPyVaE8Hv2SJ9+OOM|3JUHEtA9elnJS;5LthQ~O>vH@Z zm8FvRkBuB{eYm=}92u?yZtq*=#Rq@8b(yMfeQn!sDJAs!KRAsvoKU2|${Y#}tT|I# z0mk#WxucM0AM-+AURiflk|U?z7U1~35NGgynR(FV+5cqXK6Cf5#wn~Y9^h8ti-UKhz5F7nC;VTzwX@d@gM7vIvw;Th!~l}dAw;-Xy{+K z2=|Qv+@d_|DLfo4JF?(97gQeu+=%xw^uvSg9)7OJlBsQRO12swkz>=k`@zhWK=ofY zsYe}eHsJRc9H|MA zA%!uKb-;CBICk`e!HxjVTNyl&Vnd&Ibb9<{AYR46BP*PtPiD@_7;&;}f(s`l4b=sHMU z$3*r4_s5!>5^Tr;T*QEfcr-k$hCV-k!7IWu5T%>zud29fgD#Ux>ny^mAyqO`{gkX+ zZQbk?jA`y|>K6XSym^A7;j&1++gZ5>mW3T*j z&bFikA`G)LxB!4lk`X`CoQDCg2#k$v10D^}^_6ueL^a6SB?`}m@So#`OfyE;3=h8y z#NpF@EG^&)8x<*Bnp;G`j-*UcU}fW=Gh(0f;=y@wgF*rOff4`kQ-RH;08xB2ziGj(gIWt4vELS$>U5!PG`#lLC{Q7QzjTe69h%8 zYjaZgoZqyR3?rD?6vhSI@h~JSvF#Puj_E4~Tq)8p;P^-J|2I)6qR%cTlep&Ij%)76 zGxzB0ado^1Dif1vVVubxnh_VTi#7NrS%uSJnLYkU?wqz)uBzVzTx4mK*k9%gPd30S3v%`5lL^TN92|X#G#S6# zll=+rBs@bY{Cn3;ka>vmM1b=sKDcv{omr>6u{)0QFR!qQ-Sn~P++7{GiSQM_MmVc= zrC{xQks{!lMJvB`<+ICuxGceNw?8+*y!i*uUuK;W#c|yh85+gUR0k#>;JLZ}vhMSU z5d_?25Mcp4XOR~*WcF|OitvoY!e3SWo4xaki7LC}c<#S*=bo86Gv3SGVMZnnM2G_k zj;t($F2p5l7bhSD*BXcvw@amqiw}yfR&i~M*|l9QxFr&#tr58~kW#m6V`BHEFMa6a z>MBzSx~a`(Ak)A)Ru)OybIzRs9HvgUE_ZmihlIpQjMw4AZ@%aE=lcs#94*2WPC!V= z05!7bhw;@^uO7*bBK0hz;7i4vo03Lw3^*BGAR}^AT&rN#S=ug+-3m>Vx3ygPuyglI z*{OR-JKDoZ0GIix-iy_%P@;Go{{C$$9|v4}pIC^l98|cy)qqczbLPY>kTYbcq>A7Q z1;%H-|Cd;!=h_d>PDTM-&tdVRBm-O^*HjI09L(cdT@-2jl3nCJyF&%rT1EytTMPfk z&+9EGqmXpS0EQ+1Cf4AL%!I1?{m#_}xJ&Z5%7%WU0v>-OyQ*QD@w!N{Ch#yuf1ZJ9ia0=0n6#x9&JYq!(v-MfFg`{V|K zd$g|grEefcv(J^^VOfQG7I2zTOMA9vSyvh0UXR6*dH!;_s2Qn zY}o(kbJq5?{hN^?1ya^(>@o{({NYU8CFOYA{4~g|on=E44j08ocY}zdq#Nl5>69Fu z(jq0@jiAWrZX{#~(u^+Y1|`QB&A`zq_4xk|&;0=RzPRtsx%d3univ7WCO3wpv+NCw zX8~<8s;4uAAGWy5PQR4jFPc;AbcA^w!1}-hz{jFElygT?8aUe}Vi>4rA>1gem|?BJ z1Z$Xm=k@(r`DV?vStoenGsda{r3ZC{!q~g|OY8lboDN$;nSI}BY4T0wWOC2gVbsK| zzc#-swd<5&Q3S1Cz%x%Ft45dB1i{1(3sX}g@wt_UovbAH#Ep@)EJ4$={IOr3)Ll3c zQP@xkUEf|0uV)f;)ok&0y>tr)4GD7Dh9w1G&*TYDOa@=O3A^V6mE*5FCq@05jKnoI z$Ce@sIE)UEX|yZW55+zGI9g7Qh40ttoniMFdDr$Sy#ChvM7hk1`W#7SoNV4D$$$Ab* z-!Gl9R|x+P<125TLaqsV6#h378+1gxJN@D|&G$B%k#&MtCg|%x3Th5(6P-kCd31z* zECw#2n9YYxGZ9JHA*Y|D#nJcAhvjL94thdJGxVFixC6qo3rRV*d^Ao}xlqhgZqeRPR@9JzaLcT?Sj=^68 zUgml?1FS6gH;BmHt)HeoWVR?W{v_1giU0;G&>iQL|?FfDooU?F?zCo_g~qL-+O zV1m4_t(^FLykROu008xgx{^EyEkCL$@vTCeOfJQS%V?vZ)=ELQ%UdsX`(laMC96#&0ph^jkvx>|)^uJf3I{up3jPW$W)_!yckU|JW>0H_b zm)bf!V_~BQ#M7Sw1V4Tbbp^P6L|6L9(0C&vqP#>zJG*1@VKsiR9{*><@S#XbAX9^v zftOmUf7; zA57^MnVB{K6oVnF@zFQoxACd^Yr`2SF?B%e@`yecAv8sK_PS8wuOlYb;AKEvoc;|< zM592jgemQ`%+=MV^;+Rtkmt$n6G9EL^#KZ=w|8Jq`$O8+PJ7{UwEm z7z|3hPL`Na9^>6+?Y#ZjJZn0~n-ikg)#$xcevyk23M&1+gijrq5y$fdAej!@A03&lkJy zb^|_cQK_YnlfXfdM`yGQ^a7pX7c#MXS2_(?SfZHFTv|`^eyXWLjM9|3dEnedKDD-z zM9#k`G^(nDYbjdoICunwdQ`lX;MDPJ@zO3SQYyx4Do5Bz?ap-qRyZ=-&+sv!?cAJwTx zD6;{b`F*^rIf`kV)@6F)WZLQcT}}sqEj`A8c@BdP5pn2QK$wUKu1F{#sS>wfa4@xe z$#z8vAm-LDB_>lSea!uvYEN!CCxKEtNXNk&_7)F~uc7p2`Q;Y4>TX$3|CTD+QrdIF zu}!Id)c%%Y8J+bc&4YfuuyFX#pIlP$Y#R{T%WM?gSqIo`4I01yFF>Xy$G!FUc9R;t z`q=pRx!NUh+Ukzr+pW;JMdSp8L+X{q3*AY=D+H6TF|YiHVh4x5jy4Sn@eb5h%glZn_MKrPyUr$FgCgHKk8l%p zbn#{nC=)j}bhv&q!s%si3idT+24$Mr^z4f=&6d)U(9j$G-{U(6>kM#=?{q-iWF195 zZ1LeA)ahrHL%n8Z+E+HluXm94=2kELs9DbPA_i@_A^!7e$OCcdoE}0?u+gLMJJse4 zKw;}yL|*c9uRPq9BjXU+@SO1X7@uKT!DO(=nTu!~kZkPHf#8>B<9flw#3HK5Rssz~ zvn+F5YUcSCcia3I*RP)pCmY;IZ14~~Und?ngWm;?tYm(`TE`*>WI6d?-I4hb6M0q> zM@Y?FCz?Mv2$*_RL4?I#Nm1!nJ=OXDgPG?0L!H*U_JUCz7NJ)KZmz!*QsbXjiN7Vq z2`5vMFDD}yF>Nk)N%wi3{PAPyjB}2p4W!?>x`o+|j85r2ODY9E`J7U@J%wC)_**Ld z8pb%o6Gd9B(_Lo{rFr;tuwVCb!A4dA`|Y`AGHcAK%XVG3Lz2OEa1wNiVOB_Y; z39Sf-19P8ep~&(_jli2+p>>8)vqKAC9(XQ_sDzbtT@^qK@r;zN`?V@3wijF>m&cDn zB=M#Uoqj88i+%z|L@PF^IF4~d1cw~W_36qMXjD?;r!+$#{EQQxE-$a@7#la$*;TLM zlGhqU?{|MCdGOc`I*`pQXpTHb4iXb*W<~AW8ld*x1Zwk#&*tX|av_2>t%N<6ad|O8vZFa)d0PUbr=<9ojHTJ`5Ta(D4s zlJ3q#5Fp$9e<0fuaQ(&2%xL9D;6A+!T{ZH>Yoeoj*Kz85k3oO&QLyS9=>fZ1t&h}P zD}l2)bd6FL0rJ+Y1y+`Ujk{qUwgu3_K0jIos!*2a59_Kp6B~LD5{CaqMP}v5Y)!W8 zVclS7d(8bO0{)8d(Nnj=daG`3ZoruAdP(jhHeBKzF5=x;+4bQS`Dh%-vzlB}29HpV zrA6)F`Rtg1KX;R`poRHawuBQggBo*1cPUvi$8Hh*LzTqwDa?HRbN{o5gb*2u9OMkT zcHH)Lv$z!ifjvfW_F7StD)AQY=h~4?L-S^>L za1ijAXbNc|cd4~|IiZ&!vOnf^FG@f0i$B7~Q$z{_GQ~!qk99fCwW1EiI=c15 zF!JU9U5EYD`msIZnpz{+yO@Ogu3hH0ck6TNXJ)yno=eVY{atJG4B+WC3GwPmn`lKH zD2<)bHV;1U0@HjxlLePH_a^X}j5Pk+g)?Vcs9(zP{fggFFSC@iHqVR9*mCS^^zntm z=|`Kn^0+MU7YAK+zQzg2r_pNqR+LD8QCh~JzCeFHU1STJ;&JZ9itAAskT82~W&ax` zYX5OQuC+VgRYytx+^^%e_zd4R+rh^+Y;z8I(y>>PuEoHCtJHhZCDg>(&m6O-+K-a= z9jt2gAmu*j3nR9^NUZDlo@`%wPg^ESVuV7=9;mSb#X`w?Bkg4jt(yUHC}^&r6Tdl+ z-I|=FD^l3Qo+`iyh_0r7Gsoq;nWIo_bo#w4#1_EYZG~`Hh7g*fbeM+0zfLS!Rb!bY z?IUB8Erk)5XQWs;qb)^3niSUuiN9+A)6p5L3R?lcek_MrnU}w*pgfQ0!^8?O2bRDn zw_Bd)aNt2bCQ;1k!AN=;T;a@Ab6G5A0kB>y!%fJ|rgd5%kC^aCX}?khw#$otuBxtw z=xY!pnU%Ku9^>c4K`=x1<)hW@H}85%1RwR-8CDWhIc2s+0h()cEN2y*3IN##ApZugh7*?x}TU`m4D2SyDge-pz z9AqWYeY5xIY?Y1lTo+??QS}=p-awur_40s}EYtOyr-4<>HDWK^z;O?=R{O9tnNK@7 zNw}PEdGj+esPqtvXkc|y0@m+?X<6l-(ytlrMFR!OcITI;?5uwsiG#>gZ)dGLc4@Gy z1o_ZI4%2DOUIedqdcYz-5yI#C@Omx(3>0#IP0jN>`7FC(Q|CC(l33{NZ&C0lgM^_H z6D4F~>qg9KK5ObJ{q2F{9{Wc8Ls<;fai*i^ck_)SK}=#d)vMGl7afp;eh&kVm|sDH z4(2NtV z*_zd>Z7Ph*W;{Xu6LE+iF_yarzam%a_oETA(96*f7jGRX-A@$i+Q^n#eo@AqkHEfO z>Hj1@_Z|859{nATaW+Pp%>DKt+>2z-Nzi1ph~Hf zdAGW0yNImX7-&W|9JoBO<1me1D<>$|*Qp#)4Ean8{IHd3k^2+8IB^WiiW_b~z-?o% zv0bw(`|6_TbEK(Ag88jFpL^mKUVBI08j&5gNJoIX%_v%G1q@GWw zV8VeRawTejBM~&{qAL8GYFY8%-%s6cuBuhG$s@JLO8~pQ7)eN0*z7Crag_C7o{W6d zNlWq9^728PsH4}#oa$<=i%#bZXEzTUyZehn68?6We3^YKkQhY|Ss-Wsiu-V^EeHd_ zm@$}5fkhJ}OMxCxIgcF7$e8M$u5_oDu-JnCHp5uNp=k80#^SSuaLs7wFMUP-!t^neIu&Xk_e}?{FUr@bFG=a zu^HG;P+Y~y=vnKZ#Yy4kOO-?yO8tP~eu(-oPH7Af$MQpi9RezYgDt1+lfTtP#Xl3w zadl#_AGcgG@5nN`#FhIH{O6sK(*afHgjRNKwdMCvR{y_?cW=VIopAA-YLbY?%|WCw zcqUJ>vqEBYNvi(j4K?q;EcxES(x(AXnyI!Vohue55_+;iWx50VHY+}8_okqVDo~is zq9EbufEW8nK;?Z1@WS{d)zib#8dcYOd)tGR9E#?thj9Ji^)C3e4<6eqUs;De9-~9K zr+N58*U$$`G}`0)B^tlXQ|%=OwSy?Vr3e>!4p5At2BwHtRhE9j=9_PIDGZdLCta5& z@}}nx0m;>I*fK1<q!jvtItEO{=&ScWv-`G_rfI>Fjp)3x=%7ww1CS5o&)W^mvYk&$Yy5#(7WD9 zr!XdV9hvi{cof6a%NlPFY}=yr4O>F_LD@CK`aPN z{ANn$f>>6^vo*QJm~BXvdt>u#tqw+q#UNT+NLM5^PZ-`fb$c|(j{}WY2t0m?J7(GQ z@cTF^WKVVd?{1_uZNT4Pxrg)P$47{ork|2o18r?BWYO7!d&2P+_ivv-54UGmrme&826w+9Wko``d?tQiLOW$0 zu^2+(4+G(kTg_r%+Carx*pIP{j!rK2baC5RL!vdWKh^EvuMO|+HqiKlP_K#QJ~OP2 zE=f=wXeJI_-I|3yZeIASx0(MoD46dlg!&-ote^Z&Q9_gF(>G(iGLt;Pe8`=*46F;pL;Lkb}GAt_8Zq*se zEG_~nDgsJO2@umyhV$4N0F9>7(CDqJ3}l2LXk4|f@fni3{|u4w*0_m)HtiQ*Q^VX* zkq@v@!`&Otx$B27HyDJ0Q_U?4&cYDIEujGr`YA_fc!>9^QS1qnj)nM_&tnaRYK4~d zuYJu^fu({$=J2(Fzisn?Bo7-i?e4GH)=#!2WS6uVz%eq>^njn>$pU;4_E0enqIuVQ zl>HY_1G+&K#{^)w$+VqI5PiAo(jg|h#R@xpg zGwl;W%$bQGkR72eTFc(W+~8?@oZzuc#$kOuUOhT=Ae54?HAE4TFWl?K4K=U*Lfq-= z=9+1XM<4#u0eTgfe6CT5y0a=l1iI&c9_P4H6wu7Dypwj7N+l6%eC7%5adj}U8F)owSQpa^ClHSI=yj}wpZTe z?cvzm6Kby<;cU+3ox2?99+@9pT=-0NJ6n)ZY(^o`s-}tSF5Smc{$x?S)2YDL(W9tQ zJCieEb*lpwpK_)l8wvi0H`L$&2k8#`?;y)MSL*37cf2b<4(-rr1A!XS8!S6Mq29~k z3-ffmocLp#>&6%%{1-dZ73b|~_w_(m;jWM+cc<3aj zLk*Ndqd*rTIeO$MRI%I3_7!Ufder1QM%AOupRRFuDCV*I-(K72#>W!bKA*t5gU55< zzt>Br9lPm%F_C7$uerIZr9$B7piPckNq%fPuALqa^5n1J3Agrv1 z=3eP^b9!w_wjUcJ&>i8p9aX2AfsP|(+BWge{ literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/assets/images/planta.png b/HackOnLinces_app/aplicacion_hack/assets/images/planta.png new file mode 100644 index 0000000000000000000000000000000000000000..e61720f9412286f1186c3f20d6ba08ed1633a816 GIT binary patch literal 33617 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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/AppInfo.xcconfig b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..a405ab6 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = aplicacion_hack + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved. diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Debug.xcconfig b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Release.xcconfig b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Warnings.xcconfig b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/DebugProfile.entitlements b/HackOnLinces_app/aplicacion_hack/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Info.plist b/HackOnLinces_app/aplicacion_hack/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/MainFlutterWindow.swift b/HackOnLinces_app/aplicacion_hack/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Release.entitlements b/HackOnLinces_app/aplicacion_hack/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/RunnerTests/RunnerTests.swift b/HackOnLinces_app/aplicacion_hack/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/HackOnLinces_app/aplicacion_hack/pubspec.yaml b/HackOnLinces_app/aplicacion_hack/pubspec.yaml new file mode 100644 index 0000000..3dbbcd2 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/pubspec.yaml @@ -0,0 +1,64 @@ +name: hack_on_linces_app +description: "Sistema de Notificación Privada de Recolección de Residuos - MVP Hackathon" +publish_to: 'none' +version: 1.0.0+1 + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + flutter: + sdk: flutter + flutter_map: ^7.0.2 + latlong2: ^0.9.1 + fl_chart: ^0.69.0 + flutter_local_notifications: ^18.0.0 + + + # ------------------------------------------------------------ + # http: Para llamadas REST al backend FastAPI. + # Elegimos 'http' sobre Dio por simplicidad en hackathon. + # Si necesitas interceptors o cancelación, migra a Dio después. + # ------------------------------------------------------------ + http: ^1.2.0 + + # ------------------------------------------------------------ + # firebase_core: Inicialización base de Firebase. + # REQUERIDO antes de cualquier otro plugin de Firebase. + # Configura con: flutterfire configure (requiere Firebase CLI) + # ------------------------------------------------------------ + firebase_core: ^3.15.2 + + # ------------------------------------------------------------ + # firebase_messaging: Recepción de notificaciones push (FCM). + # Se encarga de pedir permisos al usuario y obtener el FCM token + # que debemos mandar al backend para registrar el dispositivo. + # ------------------------------------------------------------ + firebase_messaging: ^15.0.0 + + # ------------------------------------------------------------ + # shared_preferences: Guardar el usuario_id localmente. + # Simula "sesión persistente" sin un sistema de auth real. + # ATAJO de hackathon: en producción usa JWT + secure storage. + # ------------------------------------------------------------ + shared_preferences: ^2.2.0 + + cupertino_icons: ^1.0.6 + firebase_auth: ^5.7.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + +flutter: + uses-material-design: true + assets: + - assets/images/recycle.jpg + - assets/images/reloj.png + - assets/images/bottle.png + - assets/images/planta.png + - assets/images/megafono.png + # Si agregas assets (imágenes, íconos locales), declararlos aquí: + # assets: + # - assets/images/ diff --git a/HackOnLinces_app/aplicacion_hack/web/favicon.png b/HackOnLinces_app/aplicacion_hack/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/web/icons/Icon-192.png b/HackOnLinces_app/aplicacion_hack/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/web/icons/Icon-512.png b/HackOnLinces_app/aplicacion_hack/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/web/icons/Icon-maskable-192.png b/HackOnLinces_app/aplicacion_hack/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/web/icons/Icon-maskable-512.png b/HackOnLinces_app/aplicacion_hack/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/web/index.html b/HackOnLinces_app/aplicacion_hack/web/index.html new file mode 100644 index 0000000..6931bce --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/web/index.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + aplicacion_hack + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/web/manifest.json b/HackOnLinces_app/aplicacion_hack/web/manifest.json new file mode 100644 index 0000000..7fd23a9 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "aplicacion_hack", + "short_name": "aplicacion_hack", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/HackOnLinces_app/aplicacion_hack/windows/.gitignore b/HackOnLinces_app/aplicacion_hack/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/HackOnLinces_app/aplicacion_hack/windows/CMakeLists.txt b/HackOnLinces_app/aplicacion_hack/windows/CMakeLists.txt new file mode 100644 index 0000000..1309a1b --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(aplicacion_hack LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "aplicacion_hack") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/HackOnLinces_app/aplicacion_hack/windows/flutter/CMakeLists.txt b/HackOnLinces_app/aplicacion_hack/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/CMakeLists.txt b/HackOnLinces_app/aplicacion_hack/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/Runner.rc b/HackOnLinces_app/aplicacion_hack/windows/runner/Runner.rc new file mode 100644 index 0000000..2b93d46 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "aplicacion_hack" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "aplicacion_hack" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "aplicacion_hack.exe" "\0" + VALUE "ProductName", "aplicacion_hack" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.cpp b/HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.h b/HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/main.cpp b/HackOnLinces_app/aplicacion_hack/windows/runner/main.cpp new file mode 100644 index 0000000..eaa7d31 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"aplicacion_hack", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/resource.h b/HackOnLinces_app/aplicacion_hack/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/resources/app_icon.ico b/HackOnLinces_app/aplicacion_hack/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/runner.exe.manifest b/HackOnLinces_app/aplicacion_hack/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..153653e --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/utils.cpp b/HackOnLinces_app/aplicacion_hack/windows/runner/utils.cpp new file mode 100644 index 0000000..3cb7146 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/utils.cpp @@ -0,0 +1,69 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + // First, find the length of the string with a safe upper bound (CWE-126). + // UNICODE_STRING_MAX_CHARS (32767) is the maximum length of a UNICODE_STRING. + int input_length = static_cast(wcsnlen(utf16_string, UNICODE_STRING_MAX_CHARS)); + // Now use that bounded length to determine the required buffer size. + // When an explicit length is passed, WideCharToMultiByte does not include + // the null terminator in its returned size. + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || static_cast(target_length) > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/utils.h b/HackOnLinces_app/aplicacion_hack/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.cpp b/HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.h b/HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/HackOnLinces_app/backend/analytics.py b/HackOnLinces_app/backend/analytics.py new file mode 100644 index 0000000..a128a7a --- /dev/null +++ b/HackOnLinces_app/backend/analytics.py @@ -0,0 +1,398 @@ +""" +=============================================================== + analytics.py — Motor de Reportes y Análisis Predictivo +=============================================================== + Módulo independiente que se importa en main.py. + + FUNCIONALIDADES: + 1. Simulación de registros históricos de recolección + (en producción vendría de una tabla real en DB) + 2. Detección de días con mayor volumen de residuos + 3. Identificación de zonas críticas por colonia + 4. Análisis predictivo simple con regresión lineal + usando numpy (sin sklearn, más ligero) + 5. Recomendaciones automáticas de logística + + INSTALAR: + pip install numpy + + USO EN main.py: + from analytics import generar_reporte_completo, predecir_proxima_semana +=============================================================== +""" + +import numpy as np +import random +from datetime import datetime, timedelta +from typing import List, Dict, Any +import hashlib + +# --------------------------------------------------------------- +# SEMILLA DETERMINISTA +# Los datos simulados son siempre los mismos para el mismo día, +# lo que da consistencia en demos sin necesidad de una DB real. +# --------------------------------------------------------------- +def _semilla_dia(fecha: datetime) -> int: + s = fecha.strftime("%Y-%m-%d") + return int(hashlib.md5(s.encode()).hexdigest(), 16) % (2**32) + + +# --------------------------------------------------------------- +# DATOS DE REFERENCIA +# --------------------------------------------------------------- +COLONIAS = [ + "Zona Centro", "Las Arboledas", "Trojes", + "San Juanico", "Los Olivos", "Rancho Seco", "Las Insurgentes", +] + +RUTAS = { + "Zona Centro": "RUTA-01", + "Las Arboledas": "RUTA-01", + "Trojes": "RUTA-13", + "San Juanico": "RUTA-03", + "Los Olivos": "RUTA-04", + "Rancho Seco": "RUTA-05", + "Las Insurgentes": "RUTA-12", +} + +DIAS_SEMANA = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"] + +# Factores base por colonia (densidad poblacional simulada) +FACTOR_COLONIA = { + "Zona Centro": 1.45, + "Las Arboledas": 1.10, + "Trojes": 0.85, + "San Juanico": 0.90, + "Los Olivos": 1.00, + "Rancho Seco": 0.75, + "Las Insurgentes": 1.20, +} + +# Factores por día de la semana (lunes tras el finde = más basura) +FACTOR_DIA = { + 0: 1.35, # Lunes + 1: 1.05, # Martes + 2: 1.00, # Miércoles + 3: 0.95, # Jueves + 4: 1.15, # Viernes + 5: 1.25, # Sábado + 6: 0.80, # Domingo +} + + +# --------------------------------------------------------------- +# GENERADOR DE HISTÓRICO SIMULADO +# Genera 90 días de datos de recolección por colonia +# --------------------------------------------------------------- +def _generar_historico(dias: int = 90) -> List[Dict]: + registros = [] + hoy = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + + for d in range(dias, 0, -1): + fecha = hoy - timedelta(days=d) + rng = random.Random(_semilla_dia(fecha)) + dia_semana = fecha.weekday() + + for colonia in COLONIAS: + # Volumen base en kg: entre 800 y 2500 kg por día + base = rng.uniform(800, 2500) + factor = FACTOR_COLONIA[colonia] * FACTOR_DIA[dia_semana] + + # Ruido aleatorio ±15% + ruido = rng.uniform(0.85, 1.15) + volumen = round(base * factor * ruido, 1) + + # Incidencias: reportes de basura tirada fuera de horario + incidencias = rng.randint(0, int(factor * 5)) + + # Tiempo de recolección en minutos + tiempo_min = round(30 + (volumen / 100) * rng.uniform(0.8, 1.2), 0) + + registros.append({ + "fecha": fecha, + "fecha_str": fecha.strftime("%Y-%m-%d"), + "dia_semana": dia_semana, + "dia_nombre": DIAS_SEMANA[dia_semana], + "colonia": colonia, + "ruta_id": RUTAS[colonia], + "volumen_kg": volumen, + "incidencias": incidencias, + "tiempo_recoleccion_min": tiempo_min, + }) + + return registros + + +# --------------------------------------------------------------- +# ANÁLISIS: Días con más residuos +# --------------------------------------------------------------- +def analisis_por_dia_semana(historico: List[Dict]) -> List[Dict]: + """Agrupa el volumen promedio y total por día de la semana.""" + acumulado = {i: {"total_kg": 0.0, "count": 0, "incidencias": 0} for i in range(7)} + + for r in historico: + d = r["dia_semana"] + acumulado[d]["total_kg"] += r["volumen_kg"] + acumulado[d]["count"] += 1 + acumulado[d]["incidencias"] += r["incidencias"] + + resultado = [] + for dia_idx in range(7): + v = acumulado[dia_idx] + count = v["count"] if v["count"] > 0 else 1 + resultado.append({ + "dia": DIAS_SEMANA[dia_idx], + "dia_idx": dia_idx, + "promedio_kg": round(v["total_kg"] / count, 1), + "total_kg": round(v["total_kg"], 1), + "promedio_incidencias": round(v["incidencias"] / count, 1), + "semanas_registradas": count // len(COLONIAS), + }) + + # Ordenar por promedio descendente para ranking + resultado.sort(key=lambda x: x["promedio_kg"], reverse=True) + return resultado + + +# --------------------------------------------------------------- +# ANÁLISIS: Zonas críticas por colonia +# --------------------------------------------------------------- +def analisis_zonas_criticas(historico: List[Dict]) -> List[Dict]: + """Identifica colonias con mayor volumen e incidencias.""" + acumulado: Dict[str, Dict] = {} + + for r in historico: + c = r["colonia"] + if c not in acumulado: + acumulado[c] = { + "total_kg": 0.0, + "total_incidencias": 0, + "total_tiempo": 0.0, + "count": 0, + } + acumulado[c]["total_kg"] += r["volumen_kg"] + acumulado[c]["total_incidencias"] += r["incidencias"] + acumulado[c]["total_tiempo"] += r["tiempo_recoleccion_min"] + acumulado[c]["count"] += 1 + + resultado = [] + max_vol = max(v["total_kg"] / v["count"] for v in acumulado.values()) + + for colonia, v in acumulado.items(): + count = v["count"] if v["count"] > 0 else 1 + promedio_kg = v["total_kg"] / count + promedio_incidencias = v["total_incidencias"] / count + indice_criticidad = round((promedio_kg / max_vol) * 100, 1) + + # Nivel de criticidad + if indice_criticidad >= 80: + nivel = "CRÍTICO" + elif indice_criticidad >= 60: + nivel = "ALTO" + elif indice_criticidad >= 40: + nivel = "MEDIO" + else: + nivel = "BAJO" + + resultado.append({ + "colonia": colonia, + "ruta_id": RUTAS[colonia], + "promedio_kg_dia": round(promedio_kg, 1), + "total_kg_90dias": round(v["total_kg"], 1), + "promedio_incidencias_dia": round(promedio_incidencias, 2), + "promedio_tiempo_min": round(v["total_tiempo"] / count, 1), + "indice_criticidad": indice_criticidad, + "nivel_criticidad": nivel, + }) + + resultado.sort(key=lambda x: x["indice_criticidad"], reverse=True) + return resultado + + +# --------------------------------------------------------------- +# ANÁLISIS PREDICTIVO: Regresión lineal simple con numpy +# Predice el volumen de los próximos 7 días por colonia +# --------------------------------------------------------------- +def predecir_proxima_semana(historico: List[Dict]) -> List[Dict]: + """ + Regresión lineal por colonia sobre los últimos 30 días. + Retorna predicción para los próximos 7 días. + + MÉTODO: + - X = número de día (0..N) + - Y = volumen_kg + - Ajustamos y = mx + b con numpy.polyfit(deg=1) + - Proyectamos para los días N+1 .. N+7 + - Aplicamos el factor de día de la semana para ajustar estacionalidad + """ + hoy = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + predicciones = [] + + for colonia in COLONIAS: + # Filtrar últimos 30 días de esta colonia + datos_colonia = [ + r for r in historico + if r["colonia"] == colonia + ][-30:] # últimos 30 registros + + if len(datos_colonia) < 7: + continue + + x = np.array(range(len(datos_colonia)), dtype=float) + y = np.array([r["volumen_kg"] for r in datos_colonia], dtype=float) + + # Regresión lineal + coefs = np.polyfit(x, y, 1) # [pendiente, intercepto] + pendiente = coefs[0] + intercepto = coefs[1] + + # R² para medir calidad del ajuste + y_pred = np.polyval(coefs, x) + ss_res = np.sum((y - y_pred) ** 2) + ss_tot = np.sum((y - np.mean(y)) ** 2) + r2 = round(1 - (ss_res / ss_tot) if ss_tot > 0 else 0, 3) + + # Proyección próximos 7 días + n = len(datos_colonia) + dias_pred = [] + for i in range(7): + fecha_pred = hoy + timedelta(days=i + 1) + dia_semana = fecha_pred.weekday() + vol_base = np.polyval(coefs, n + i) + # Ajustar por factor estacional del día + vol_ajustado = max(0, vol_base * FACTOR_DIA[dia_semana]) + + dias_pred.append({ + "fecha": fecha_pred.strftime("%Y-%m-%d"), + "dia": DIAS_SEMANA[dia_semana], + "volumen_predicho_kg": round(float(vol_ajustado), 1), + "confianza": "Alta" if r2 > 0.7 else ("Media" if r2 > 0.4 else "Baja"), + }) + + predicciones.append({ + "colonia": colonia, + "ruta_id": RUTAS[colonia], + "tendencia": "CRECIENTE" if pendiente > 10 else ("DECRECIENTE" if pendiente < -10 else "ESTABLE"), + "pendiente_diaria_kg": round(float(pendiente), 2), + "r2": r2, + "prediccion_7dias": dias_pred, + "volumen_predicho_total_kg": round(sum(d["volumen_predicho_kg"] for d in dias_pred), 1), + }) + + predicciones.sort(key=lambda x: x["volumen_predicho_total_kg"], reverse=True) + return predicciones + + +# --------------------------------------------------------------- +# RECOMENDACIONES AUTOMÁTICAS DE LOGÍSTICA +# --------------------------------------------------------------- +def generar_recomendaciones( + zonas: List[Dict], + dias: List[Dict], + predicciones: List[Dict], +) -> List[Dict]: + """Genera recomendaciones concretas basadas en el análisis.""" + recomendaciones = [] + + # 1. Zonas críticas → refuerzo de camiones + criticas = [z for z in zonas if z["nivel_criticidad"] == "CRÍTICO"] + for z in criticas: + recomendaciones.append({ + "tipo": "REFUERZO_RUTA", + "prioridad": "ALTA", + "emoji": "🚛", + "titulo": f"Refuerzo en {z['colonia']}", + "descripcion": ( + f"{z['colonia']} genera en promedio {z['promedio_kg_dia']:.0f} kg/día " + f"con {z['promedio_incidencias_dia']:.1f} incidencias. " + f"Se recomienda asignar un segundo camión en días pico." + ), + "ruta_afectada": z["ruta_id"], + }) + + # 2. Días pico → ajuste de horarios + dia_pico = dias[0] if dias else None + if dia_pico: + recomendaciones.append({ + "tipo": "HORARIO_PICO", + "prioridad": "MEDIA", + "emoji": "⏰", + "titulo": f"{dia_pico['dia']} es el día con más residuos", + "descripcion": ( + f"El {dia_pico['dia']} se recolectan en promedio " + f"{dia_pico['promedio_kg']:.0f} kg por colonia. " + f"Considera iniciar rutas 30 minutos antes ese día." + ), + "ruta_afectada": None, + }) + + # 3. Colonias con tendencia creciente → alerta temprana + crecientes = [p for p in predicciones if p["tendencia"] == "CRECIENTE"] + for p in crecientes[:2]: # máximo 2 alertas + recomendaciones.append({ + "tipo": "TENDENCIA_CRECIENTE", + "prioridad": "MEDIA", + "emoji": "📈", + "titulo": f"Aumento en {p['colonia']}", + "descripcion": ( + f"{p['colonia']} muestra una tendencia creciente de " + f"+{p['pendiente_diaria_kg']:.1f} kg/día. " + f"Volumen predicho esta semana: {p['volumen_predicho_total_kg']:.0f} kg." + ), + "ruta_afectada": p["ruta_id"], + }) + + # 4. Colonias con muchas incidencias → campaña de concientización + alta_incidencia = sorted(zonas, key=lambda x: x["promedio_incidencias_dia"], reverse=True) + if alta_incidencia: + z = alta_incidencia[0] + recomendaciones.append({ + "tipo": "CONCIENTIZACION", + "prioridad": "BAJA", + "emoji": "📢", + "titulo": f"Campaña en {z['colonia']}", + "descripcion": ( + f"{z['colonia']} registra {z['promedio_incidencias_dia']:.1f} incidencias/día " + f"de basura fuera de horario. Se recomienda campaña de concientización ciudadana." + ), + "ruta_afectada": z["ruta_id"], + }) + + return recomendaciones + + +# --------------------------------------------------------------- +# FUNCIÓN PRINCIPAL: Genera el reporte completo +# --------------------------------------------------------------- +def generar_reporte_completo(dias_historico: int = 90) -> Dict[str, Any]: + """ + Punto de entrada principal. Genera todos los análisis y los empaqueta. + Cachea el resultado por 10 minutos para no recalcular en cada request. + """ + historico = _generar_historico(dias_historico) + + dias_semana = analisis_por_dia_semana(historico) + zonas = analisis_zonas_criticas(historico) + predicciones = predecir_proxima_semana(historico) + recomendaciones = generar_recomendaciones(zonas, dias_semana, predicciones) + + # Resumen ejecutivo + total_kg_periodo = sum(r["volumen_kg"] for r in historico) + promedio_diario = total_kg_periodo / dias_historico if dias_historico > 0 else 0 + + return { + "generado_en": datetime.now().isoformat(), + "periodo_dias": dias_historico, + "resumen": { + "total_kg_recolectados": round(total_kg_periodo, 1), + "promedio_kg_dia": round(promedio_diario, 1), + "colonias_analizadas": len(COLONIAS), + "zonas_criticas": len([z for z in zonas if z["nivel_criticidad"] == "CRÍTICO"]), + "dia_pico": dias_semana[0]["dia"] if dias_semana else "N/A", + "colonia_mayor_volumen": zonas[0]["colonia"] if zonas else "N/A", + }, + "dias_semana": dias_semana, + "zonas_criticas": zonas, + "prediccion_proxima_semana": predicciones, + "recomendaciones": recomendaciones, + } diff --git a/HackOnLinces_app/backend/analytics_real.py b/HackOnLinces_app/backend/analytics_real.py new file mode 100644 index 0000000..99612ae --- /dev/null +++ b/HackOnLinces_app/backend/analytics_real.py @@ -0,0 +1,414 @@ +""" +=============================================================== + analytics.py — v2: Datos REALES de la BD + fallback simulado +=============================================================== + REEMPLAZA el analytics.py anterior completamente. + + CAMBIO PRINCIPAL: + generar_reporte_completo_real(db) lee de RegistroRecoleccion + y ReporteUsuario en SQLite. Si hay pocos datos reales (<7 días), + mezcla con datos simulados para que el dashboard no quede vacío. +=============================================================== +""" + +import numpy as np +import random +from datetime import datetime, timedelta, timezone +from typing import List, Dict, Any, Optional +import hashlib + +# --------------------------------------------------------------- +# CONSTANTES +# --------------------------------------------------------------- +COLONIAS = [ + "Zona Centro", "Las Arboledas", "Trojes", + "San Juanico", "Los Olivos", "Rancho Seco", "Las Insurgentes", +] + +RUTAS = { + "Zona Centro": "RUTA-01", + "Las Arboledas": "RUTA-01", + "Trojes": "RUTA-13", + "San Juanico": "RUTA-03", + "Los Olivos": "RUTA-04", + "Rancho Seco": "RUTA-05", + "Las Insurgentes": "RUTA-12", +} + +DIAS_SEMANA = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"] + +FACTOR_COLONIA = { + "Zona Centro": 1.45, + "Las Arboledas": 1.10, + "Trojes": 0.85, + "San Juanico": 0.90, + "Los Olivos": 1.00, + "Rancho Seco": 0.75, + "Las Insurgentes": 1.20, +} + +FACTOR_DIA = {0: 1.35, 1: 1.05, 2: 1.00, 3: 0.95, 4: 1.15, 5: 1.25, 6: 0.80} + + +# --------------------------------------------------------------- +# GENERADOR DE HISTÓRICO SIMULADO (fallback cuando no hay datos) +# --------------------------------------------------------------- +def _semilla_dia(fecha: datetime) -> int: + s = fecha.strftime("%Y-%m-%d") + return int(hashlib.md5(s.encode()).hexdigest(), 16) % (2**32) + + +def _generar_historico_simulado(dias: int = 90) -> List[Dict]: + registros = [] + hoy = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + for d in range(dias, 0, -1): + fecha = hoy - timedelta(days=d) + rng = random.Random(_semilla_dia(fecha)) + dia_semana = fecha.weekday() + for colonia in COLONIAS: + base = rng.uniform(800, 2500) + factor = FACTOR_COLONIA[colonia] * FACTOR_DIA[dia_semana] + ruido = rng.uniform(0.85, 1.15) + volumen = round(base * factor * ruido, 1) + incidencias = rng.randint(0, int(factor * 5)) + tiempo_min = round(30 + (volumen / 100) * rng.uniform(0.8, 1.2), 0) + registros.append({ + "fecha": fecha, + "fecha_str": fecha.strftime("%Y-%m-%d"), + "dia_semana": dia_semana, + "dia_nombre": DIAS_SEMANA[dia_semana], + "colonia": colonia, + "ruta_id": RUTAS[colonia], + "volumen_kg": volumen, + "incidencias": incidencias, + "tiempo_recoleccion_min": tiempo_min, + "fuente": "simulado", + }) + return registros + + +# --------------------------------------------------------------- +# LEER DATOS REALES DE LA BD +# --------------------------------------------------------------- +def _leer_historico_real(db, dias: int = 90) -> List[Dict]: + """Lee RegistroRecoleccion de la BD y los convierte al formato interno.""" + desde = (datetime.now(timezone.utc) - timedelta(days=dias)).strftime("%Y-%m-%d") + + try: + from sqlalchemy import text + rows = db.execute( + text(""" + SELECT fecha, colonia, ruta_id, + SUM(volumen_kg) as volumen_kg, + SUM(incidencias) as incidencias, + AVG(tiempo_min) as tiempo_min, + fuente + FROM registros_recoleccion + WHERE fecha >= :desde + GROUP BY fecha, colonia, ruta_id, fuente + ORDER BY fecha DESC + """), + {"desde": desde} + ).fetchall() + except Exception: + return [] + + registros = [] + for row in rows: + try: + fecha = datetime.strptime(row[0], "%Y-%m-%d") + except Exception: + continue + registros.append({ + "fecha": fecha, + "fecha_str": row[0], + "dia_semana": fecha.weekday(), + "dia_nombre": DIAS_SEMANA[fecha.weekday()], + "colonia": row[1], + "ruta_id": row[2], + "volumen_kg": float(row[3] or 0), + "incidencias": int(row[4] or 0), + "tiempo_recoleccion_min": float(row[5] or 0), + "fuente": row[6], + }) + return registros + + +def _leer_reportes_usuario(db, dias: int = 30) -> List[Dict]: + """Lee los reportes del usuario para mostrarlos en el análisis.""" + desde = (datetime.now(timezone.utc) - timedelta(days=dias)).strftime("%Y-%m-%d") + try: + from sqlalchemy import text + rows = db.execute( + text(""" + SELECT r.fecha, r.colonia, r.tipo_reporte, + COUNT(*) as total, SUM(COALESCE(r.volumen_kg, 0)) as vol_total, + u.nombre + FROM reportes_usuario r + LEFT JOIN usuarios u ON r.usuario_id = u.id + WHERE r.fecha >= :desde + GROUP BY r.fecha, r.colonia, r.tipo_reporte + ORDER BY r.fecha DESC + """), + {"desde": desde} + ).fetchall() + return [ + { + "fecha": row[0], "colonia": row[1], + "tipo": row[2], "total": row[3], + "volumen": float(row[4] or 0), + } + for row in rows + ] + except Exception: + return [] + + +# --------------------------------------------------------------- +# ANÁLISIS (idéntico al anterior, funciona con cualquier historico) +# --------------------------------------------------------------- +def _analisis_por_dia_semana(historico: List[Dict]) -> List[Dict]: + acumulado = {i: {"total_kg": 0.0, "count": 0, "incidencias": 0} for i in range(7)} + for r in historico: + d = r["dia_semana"] + acumulado[d]["total_kg"] += r["volumen_kg"] + acumulado[d]["count"] += 1 + acumulado[d]["incidencias"] += r["incidencias"] + resultado = [] + for dia_idx in range(7): + v = acumulado[dia_idx] + count = max(v["count"], 1) + resultado.append({ + "dia": DIAS_SEMANA[dia_idx], + "dia_idx": dia_idx, + "promedio_kg": round(v["total_kg"] / count, 1), + "total_kg": round(v["total_kg"], 1), + "promedio_incidencias": round(v["incidencias"] / count, 1), + "semanas_registradas": count // max(len(COLONIAS), 1), + }) + resultado.sort(key=lambda x: x["promedio_kg"], reverse=True) + return resultado + + +def _analisis_zonas_criticas(historico: List[Dict]) -> List[Dict]: + acumulado: Dict[str, Dict] = {} + for r in historico: + c = r["colonia"] + if c not in acumulado: + acumulado[c] = {"total_kg": 0.0, "total_incidencias": 0, "total_tiempo": 0.0, "count": 0} + acumulado[c]["total_kg"] += r["volumen_kg"] + acumulado[c]["total_incidencias"] += r["incidencias"] + acumulado[c]["total_tiempo"] += r["tiempo_recoleccion_min"] + acumulado[c]["count"] += 1 + + if not acumulado: + return [] + + max_vol = max(v["total_kg"] / max(v["count"], 1) for v in acumulado.values()) + resultado = [] + for colonia, v in acumulado.items(): + count = max(v["count"], 1) + promedio_kg = v["total_kg"] / count + promedio_inc = v["total_incidencias"] / count + idx = round((promedio_kg / max_vol) * 100, 1) if max_vol > 0 else 0 + + if idx >= 80: nivel = "CRÍTICO" + elif idx >= 60: nivel = "ALTO" + elif idx >= 40: nivel = "MEDIO" + else: nivel = "BAJO" + + resultado.append({ + "colonia": colonia, + "ruta_id": RUTAS.get(colonia, ""), + "promedio_kg_dia": round(promedio_kg, 1), + "total_kg_90dias": round(v["total_kg"], 1), + "promedio_incidencias_dia": round(promedio_inc, 2), + "promedio_tiempo_min": round(v["total_tiempo"] / count, 1), + "indice_criticidad": idx, + "nivel_criticidad": nivel, + }) + resultado.sort(key=lambda x: x["indice_criticidad"], reverse=True) + return resultado + + +def _predecir_proxima_semana(historico: List[Dict]) -> List[Dict]: + hoy = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + predicciones = [] + for colonia in COLONIAS: + datos = [r for r in historico if r["colonia"] == colonia][-30:] + if len(datos) < 5: + continue + x = np.array(range(len(datos)), dtype=float) + y = np.array([r["volumen_kg"] for r in datos], dtype=float) + coefs = np.polyfit(x, y, 1) + y_pred = np.polyval(coefs, x) + ss_res = np.sum((y - y_pred) ** 2) + ss_tot = np.sum((y - np.mean(y)) ** 2) + r2 = round(1 - (ss_res / ss_tot) if ss_tot > 0 else 0, 3) + n = len(datos) + dias_pred = [] + for i in range(7): + fecha_pred = hoy + timedelta(days=i + 1) + dia_semana = fecha_pred.weekday() + vol_base = np.polyval(coefs, n + i) + vol_ajustado = max(0, vol_base * FACTOR_DIA[dia_semana]) + dias_pred.append({ + "fecha": fecha_pred.strftime("%Y-%m-%d"), + "dia": DIAS_SEMANA[dia_semana], + "volumen_predicho_kg": round(float(vol_ajustado), 1), + "confianza": "Alta" if r2 > 0.7 else ("Media" if r2 > 0.4 else "Baja"), + }) + pendiente = float(coefs[0]) + predicciones.append({ + "colonia": colonia, + "ruta_id": RUTAS.get(colonia, ""), + "tendencia": "CRECIENTE" if pendiente > 10 else ("DECRECIENTE" if pendiente < -10 else "ESTABLE"), + "pendiente_diaria_kg": round(pendiente, 2), + "r2": r2, + "prediccion_7dias": dias_pred, + "volumen_predicho_total_kg": round(sum(d["volumen_predicho_kg"] for d in dias_pred), 1), + }) + predicciones.sort(key=lambda x: x["volumen_predicho_total_kg"], reverse=True) + return predicciones + + +def _generar_recomendaciones(zonas, dias, predicciones) -> List[Dict]: + recomendaciones = [] + criticas = [z for z in zonas if z["nivel_criticidad"] == "CRÍTICO"] + for z in criticas: + recomendaciones.append({ + "tipo": "REFUERZO_RUTA", "prioridad": "ALTA", "emoji": "🚛", + "titulo": f"Refuerzo en {z['colonia']}", + "descripcion": ( + f"{z['colonia']} genera {z['promedio_kg_dia']:.0f} kg/día promedio " + f"con {z['promedio_incidencias_dia']:.1f} incidencias. " + f"Se recomienda reforzar el camión en días pico." + ), + "ruta_afectada": z["ruta_id"], + }) + if dias: + dia_pico = dias[0] + recomendaciones.append({ + "tipo": "HORARIO_PICO", "prioridad": "MEDIA", "emoji": "⏰", + "titulo": f"{dia_pico['dia']} es el día con más residuos", + "descripcion": ( + f"El {dia_pico['dia']} se recolectan {dia_pico['promedio_kg']:.0f} kg/colonia en promedio. " + f"Considera iniciar 30 minutos antes ese día." + ), + "ruta_afectada": None, + }) + crecientes = [p for p in predicciones if p["tendencia"] == "CRECIENTE"] + for p in crecientes[:2]: + recomendaciones.append({ + "tipo": "TENDENCIA_CRECIENTE", "prioridad": "MEDIA", "emoji": "📈", + "titulo": f"Aumento en {p['colonia']}", + "descripcion": ( + f"Tendencia creciente de +{p['pendiente_diaria_kg']:.1f} kg/día. " + f"Volumen predicho esta semana: {p['volumen_predicho_total_kg']:.0f} kg." + ), + "ruta_afectada": p["ruta_id"], + }) + if zonas: + z = max(zonas, key=lambda x: x["promedio_incidencias_dia"]) + recomendaciones.append({ + "tipo": "CONCIENTIZACION", "prioridad": "BAJA", "emoji": "📢", + "titulo": f"Campaña en {z['colonia']}", + "descripcion": ( + f"{z['colonia']} tiene {z['promedio_incidencias_dia']:.1f} incidencias/día. " + f"Se recomienda campaña de concientización." + ), + "ruta_afectada": z["ruta_id"], + }) + return recomendaciones + + +# --------------------------------------------------------------- +# FUNCIÓN PRINCIPAL — DATOS REALES + FALLBACK +# --------------------------------------------------------------- +def generar_reporte_completo_real(db) -> Dict[str, Any]: + """ + Lee datos reales de la BD. Si hay menos de 7 días de datos reales, + complementa con datos simulados para que el dashboard no quede vacío. + """ + historico_real = _leer_historico_real(db, dias=90) + reportes_usuario = _leer_reportes_usuario(db, dias=30) + + # Calcular días únicos con datos reales + dias_con_datos = len({r["fecha_str"] for r in historico_real}) + + if dias_con_datos < 7: + # Pocos datos reales — mezclar con simulados + historico_sim = _generar_historico_simulado(dias=90 - dias_con_datos) + historico = historico_real + historico_sim + fuente_datos = f"mixto ({dias_con_datos} días reales + simulados)" + else: + historico = historico_real + fuente_datos = f"real ({dias_con_datos} días)" + + dias_semana = _analisis_por_dia_semana(historico) + zonas = _analisis_zonas_criticas(historico) + predicciones = _predecir_proxima_semana(historico) + recomendaciones = _generar_recomendaciones(zonas, dias_semana, predicciones) + + total_kg = sum(r["volumen_kg"] for r in historico) + n_dias = max(len({r["fecha_str"] for r in historico}), 1) + + # Estadísticas de reportes de usuarios + total_reportes_usuario = sum(r["total"] for r in reportes_usuario) + incidencias_por_colonia = {} + for r in reportes_usuario: + if r["tipo"] == "incidencia": + incidencias_por_colonia[r["colonia"]] = \ + incidencias_por_colonia.get(r["colonia"], 0) + r["total"] + + return { + "generado_en": datetime.now().isoformat(), + "fuente_datos": fuente_datos, + "periodo_dias": 90, + "resumen": { + "total_kg_recolectados": round(total_kg, 1), + "promedio_kg_dia": round(total_kg / n_dias, 1), + "colonias_analizadas": len(COLONIAS), + "zonas_criticas": len([z for z in zonas if z["nivel_criticidad"] == "CRÍTICO"]), + "dia_pico": dias_semana[0]["dia"] if dias_semana else "N/A", + "colonia_mayor_volumen": zonas[0]["colonia"] if zonas else "N/A", + "reportes_ciudadanos": total_reportes_usuario, + "colonias_con_incidencias": len(incidencias_por_colonia), + }, + "dias_semana": dias_semana, + "zonas_criticas": zonas, + "prediccion_proxima_semana": predicciones, + "recomendaciones": recomendaciones, + "reportes_ciudadanos_recientes": reportes_usuario[:10], + } + + +# Mantener compatibilidad con el import anterior +def generar_reporte_completo(db=None, dias_historico: int = 90) -> Dict[str, Any]: + """Versión sin DB (fallback puro simulado).""" + historico = _generar_historico_simulado(dias_historico) + dias_semana = _analisis_por_dia_semana(historico) + zonas = _analisis_zonas_criticas(historico) + predicciones = _predecir_proxima_semana(historico) + recomendaciones = _generar_recomendaciones(zonas, dias_semana, predicciones) + total_kg = sum(r["volumen_kg"] for r in historico) + return { + "generado_en": datetime.now().isoformat(), + "fuente_datos": "simulado", + "periodo_dias": dias_historico, + "resumen": { + "total_kg_recolectados": round(total_kg, 1), + "promedio_kg_dia": round(total_kg / max(dias_historico, 1), 1), + "colonias_analizadas": len(COLONIAS), + "zonas_criticas": len([z for z in zonas if z["nivel_criticidad"] == "CRÍTICO"]), + "dia_pico": dias_semana[0]["dia"] if dias_semana else "N/A", + "colonia_mayor_volumen": zonas[0]["colonia"] if zonas else "N/A", + "reportes_ciudadanos": 0, + "colonias_con_incidencias": 0, + }, + "dias_semana": dias_semana, + "zonas_criticas": zonas, + "prediccion_proxima_semana": predicciones, + "recomendaciones": recomendaciones, + "reportes_ciudadanos_recientes": [], + } diff --git a/HackOnLinces_app/backend/hackathon.db b/HackOnLinces_app/backend/hackathon.db new file mode 100644 index 0000000000000000000000000000000000000000..d2f00ab90975ad7dcdc7bf86bac87f96ed36c5aa GIT binary patch literal 49152 zcmeI*+i%;}9S3kyv?NQm!=@W1WU3b$$>0c$5?y?Wygh`LDOt8G*|OvtFbFhtBr}#o ziKJ}BZ*n^f=*#|)VNb)b_oaL4-uB!M0|pc=FsvBRJ{9N=WvTLoR=PI8oc2q^6nQS> zIiK@81ZdLH=0;jIiBQ(`nrsSD<_(5rnWuulFw9%@XPo}@mkGKt(%+$L3g4fgQ z1?_}wc9|##valuQ;>+U2iU*o*pP^_~t*+YV=`B;XPGxsMQ*CIr86{#Uy4p}wt=`YK zT3*&nt=Q75=hKK`$|d`wtmjf|Vs2Mhm3F(v)*%wvoRmstth5IMh=7oj5>ige#HCH4 zKg!)Y-azNgQMYexp6vt&@la6>Q>SsD6WVp6SnVwK5OiXb>A4ylHl_N9IExaS0>Xpd zNdl)kF1K%fo_#ml3m2Qxwk>gA3LBex!h>r==Uc?au<;)jHx12DP0RI~FeOK%ZrYFg zkydThNWFMi=?Azq@}^2^4Xs#H>*rH1$A~v@dXIDarl#0;Z+B@+T1{2dDxLL%t&t0e zgY+SCgIGI%_F(|J)-s9x%qgif@Vb*DFYVBY-J9IPl9Uh&>AWyKZFTr^*Q~TcCbdzJ zg!7^Q?PkTq-G<5<&6#RJ(m=%#&L`z0ad z4May=zT_0kbY+cZmEP?Z<(8>+_lpCrVtBCaUEGW8h9cp`#auR0Z*3nJk}JCYEV@@Y*^9Rl<@p1B)hwFQlcc^$ zs%y_ziU+Z6Ls{S4UR+94Hs+hVn(?d|J~q^Pv3oyIj6N-^?MGEjk*oJ3`}f0<`%N*O zZnm_oy<|9fv~e;&7fK`)vRkMbB)YkH^h_$0kB*c|i_C4Wrgp+)dE0D|j_K%kJ1*;0 z%@EQr{?H&NH?`Z5SJUlV1x8?gbQ%bDCd9gYwX+@K8a>N*+UEa1V?eIU;isf6*BVvw zNYQHFY52>|QO0)~c)R0VQb|Q4jdDm=iefI9JzQRm$4j$E`}^@;v`f)BsU3`{bDFbllVJHUf82_i@MeJ z%EoN|pj}u?ON(YHL`teC#r0?F;^ydt-QQ&@cUO=Oa2S~GyYTlpL7Qc1Rwwb2tWV=5P$##AOHafKmY=- zk${t9S(nRNJDt{gWW-u?oV9j1IL_m-9`?KV4Tk@i|11AH{>PAbi(7*d3My`+-gW-sH*l5CABr>xUDXv;J}VHp|>pb=bnF0qwj&lX?yg32A=;9A5P>#00Izz00bZa0SG_<0uX=z z1a7{7^_+kB^MBm`-~3TT3n2gj2tWV=5P$##AOHafKmY>61zvvtZ@vG|@mYrd3;!Pd zzybjXKmY;|fB*y_009U<00Izzz{>?nG1$ygOO->b|Ese5DoQS z0p;fKt{~Z4z!+*|yLO-xT z00Izz00bZa0SG_<0uX=z1R(Ib35;=mws-f>o!fSJIlrfues=%=0mJ{B|B(OSb?YG7 z3jqi~00Izz00bZa0SG_<0ucDY3EbwU*^93hTuE`f!A*NElsUWq|E%}@e?K@gbQl5< zfB*y_009U<00Izz00bcLy%ezf{y&!gBSZgTfdB*`009U<00Izz00bZa0SG|gCJCh2 zF@~L*oc0G8=C1SH|1&VNFgv#pow2@pP}KLeDk;flUpyEJ$Hlc&HdD-Gi|gWMc3sLW vrKC(=vY!95{Fn6k|4r&3+5!OxKmY;|fB*y_009U<00Izzz^fAAIFIMQzR_H; literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/backend/main.py b/HackOnLinces_app/backend/main.py new file mode 100644 index 0000000..7d269da --- /dev/null +++ b/HackOnLinces_app/backend/main.py @@ -0,0 +1,1760 @@ +""" +=============================================================== + SISTEMA DE NOTIFICACIÓN PRIVADA DE RECOLECCIÓN DE RESIDUOS + Backend MVP - Hackathon 24h (v2 — mejorado post-hackathon) +=============================================================== + Stack: FastAPI + SQLite (SQLAlchemy) + Firebase Admin SDK + + MEJORAS v2: + - Passwords hasheadas con bcrypt (antes: sha256 plano) + - Migración automática de hashes legacy sha256 → bcrypt + - Endpoint /api/dashboard: estadísticas globales de rutas + - Endpoint /api/rutas/{route_id}/posiciones: historial GPS + - Endpoint /api/rutas/resumen: vista rápida de todos los camiones + - Endpoint /api/estadisticas/colonias: colonias más activas + + CÓMO CORRER: + pip install fastapi uvicorn sqlalchemy firebase-admin bcrypt + uvicorn main:app --reload --port 8000 +=============================================================== +""" + +from fastapi import FastAPI, HTTPException, Depends, Query +from fastapi.middleware.cors import CORSMiddleware +from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, inspect, text +from sqlalchemy.orm import declarative_base, sessionmaker, Session, relationship +from pydantic import BaseModel +from typing import Optional, List, Dict +from datetime import datetime, timezone, timedelta +import bcrypt +import hashlib +import logging +from analytics import generar_reporte_completo +from analytics_real import generar_reporte_completo as generar_reporte_real + +# --------------------------------------------------------------- +# LOGGING +# --------------------------------------------------------------- +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +# =============================================================== +# SEGURIDAD DE CONTRASEÑAS — bcrypt +# +# POR QUÉ bcrypt en vez de sha256: +# - sha256 es rápido → fácil de atacar con fuerza bruta +# - bcrypt incluye salt automático → dos hashes del mismo +# password son distintos (evita ataques de rainbow table) +# - El "work factor" (12) hace cada verificación ~250ms, +# tolerable para usuarios, costoso para atacantes. +# +# MIGRACIÓN LEGACY: +# Los usuarios registrados antes de esta versión tienen +# password_hash en sha256. Al hacer login, si el hash +# antiguo coincide, re-hasheamos con bcrypt y guardamos. +# =============================================================== + +def hash_password(password: str) -> str: + """Genera un hash bcrypt del password. Incluye salt automático.""" + return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("utf-8") + + +def _es_hash_legacy(password_hash: str) -> bool: + """Detecta si el hash guardado es sha256 (hex de 64 chars) en vez de bcrypt.""" + return len(password_hash) == 64 and not password_hash.startswith("$2b$") + + +def verify_password(password: str, password_hash: str) -> bool: + if not password_hash: # ← hash vacío o None → fallo seguro + return False + if _es_hash_legacy(password_hash): + legacy_hash = hashlib.sha256(password.encode("utf-8")).hexdigest() + return legacy_hash == password_hash + try: + return bcrypt.checkpw(password.encode("utf-8"), password_hash.encode("utf-8")) + except ValueError: # ← salt inválido por cualquier corrupción + return False + + +# --------------------------------------------------------------- +# BASE DE DATOS (SQLite — hackathon mode) +# --------------------------------------------------------------- +DATABASE_URL = "sqlite:///./hackathon.db" +engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False) +Base = declarative_base() + + +# =============================================================== +# MODELOS ORM +# =============================================================== + +class Usuario(Base): + __tablename__ = "usuarios" + + id = Column(Integer, primary_key=True, index=True) + nombre = Column(String, nullable=False) + email = Column(String, nullable=False, unique=True, index=True) + password_hash = Column(String, nullable=False) + fcm_token = Column(String, nullable=True) + + direcciones = relationship("Domicilio", back_populates="usuario", cascade="all, delete-orphan") + + +class Domicilio(Base): + __tablename__ = "domicilios" + + id = Column(Integer, primary_key=True, index=True) + usuario_id = Column(Integer, ForeignKey("usuarios.id")) + colonia = Column(String, nullable=False) + direccion = Column(String, nullable=False) + route_id = Column(String, nullable=False) # Interno, no se expone al cliente + + usuario = relationship("Usuario", back_populates="direcciones") + +# ================================================================ +# PEGA ESTE BLOQUE EN main.py JUSTO DESPUÉS DE LA CLASE Domicilio +# (después de la línea: usuario = relationship(...)) +# ================================================================ + +class RegistroRecoleccion(Base): + """ + Registro automático cada vez que una ruta avanza o se completa. + Es la fuente de verdad para el módulo de analytics. + """ + __tablename__ = "registros_recoleccion" + + id = Column(Integer, primary_key=True, index=True) + fecha = Column(String, nullable=False) # YYYY-MM-DD + hora = Column(String, nullable=False) # HH:MM:SS + ruta_id = Column(String, nullable=False, index=True) + colonia = Column(String, nullable=False, index=True) + position_id = Column(Integer, nullable=False) # 1..8 + evento = Column(String, nullable=False) # INICIO, AVANCE, COMPLETADO + volumen_kg = Column(Integer, nullable=True) # estimado por posición + tiempo_min = Column(Integer, nullable=True) # minutos desde inicio de ruta + + +class ReporteUsuario(Base): + """ + Reportes manuales enviados por los ciudadanos. + Permiten enriquecer el análisis con datos cualitativos. + """ + __tablename__ = "reportes_usuarios" + + id = Column(Integer, primary_key=True, index=True) + usuario_id = Column(Integer, ForeignKey("usuarios.id"), nullable=False) + fecha = Column(String, nullable=False) # YYYY-MM-DD + hora = Column(String, nullable=False) # HH:MM:SS + colonia = Column(String, nullable=False) + ruta_id = Column(String, nullable=False) + tipo = Column(String, nullable=False) # VOLUMEN_ALTO, CAMION_NO_PASO, BASURA_FUERA_HORARIO, OTRO + descripcion = Column(String, nullable=True) + foto_url = Column(String, nullable=True) # futuro: S3/Firebase Storage + estado = Column(String, nullable=False, default="PENDIENTE") # PENDIENTE, ATENDIDO + + usuario = relationship("Usuario") + +# --------------------------------------------------------------- +# MIGRACIÓN AUTOMÁTICA DE ESQUEMA +# Añade columnas nuevas si la DB existe desde una versión anterior. +# --------------------------------------------------------------- +inspector = inspect(engine) +if inspector.has_table("usuarios"): + cols = [c["name"] for c in inspector.get_columns("usuarios")] + with engine.connect() as conn: + if "email" not in cols: + conn.execute(text("ALTER TABLE usuarios ADD COLUMN email TEXT")) + if "password_hash" not in cols: + conn.execute(text("ALTER TABLE usuarios ADD COLUMN password_hash TEXT NOT NULL DEFAULT ''")) + conn.commit() + +if inspector.has_table("domicilios"): + cols = [c["name"] for c in inspector.get_columns("domicilios")] + with engine.connect() as conn: + if "direccion" not in cols: + conn.execute(text("ALTER TABLE domicilios ADD COLUMN direccion TEXT NOT NULL DEFAULT ''")) + conn.commit() + +Base.metadata.create_all(bind=engine) + + +# =============================================================== +# DATOS MOCKEADOS +# =============================================================== + +# =============================================================== +# PEGA ESTO EN main.py REEMPLAZANDO DESDE "ROUTE_DATA" HASTA +# "ROUTE_STATE" (inclusive el bloque de inicialización de ROUTE_STATE) +# =============================================================== + +ROUTE_DATA = [ + { + "route_id": "RUTA-01", "name": "Zona Centro - Las Arboledas", "truck_id": 101, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:00:00Z"}, + {"positionId": 2, "lat": 20.5185, "lng": -100.8450, "speed": 45, "timestamp": "2026-05-22T06:12:00Z"}, + {"positionId": 3, "lat": 20.5215, "lng": -100.8142, "speed": 22, "timestamp": "2026-05-22T06:25:00Z"}, + {"positionId": 4, "lat": 20.5212, "lng": -100.8175, "speed": 15, "timestamp": "2026-05-22T06:38:00Z"}, + {"positionId": 5, "lat": 20.5210, "lng": -100.8210, "speed": 0, "timestamp": "2026-05-22T06:50:00Z"}, + {"positionId": 6, "lat": 20.5235, "lng": -100.8212, "speed": 18, "timestamp": "2026-05-22T07:05:00Z"}, + {"positionId": 7, "lat": 20.5260, "lng": -100.8215, "speed": 20, "timestamp": "2026-05-22T07:18:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 40, "timestamp": "2026-05-22T07:40:00Z"}, + ], + }, + { + "route_id": "RUTA-02", "name": "Sector Norte - Av. Tecnológico", "truck_id": 102, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:05:00Z"}, + {"positionId": 2, "lat": 20.5280, "lng": -100.8135, "speed": 38, "timestamp": "2026-05-22T06:18:00Z"}, + {"positionId": 3, "lat": 20.5410, "lng": -100.8130, "speed": 25, "timestamp": "2026-05-22T06:30:00Z"}, + {"positionId": 4, "lat": 20.5445, "lng": -100.8132, "speed": 12, "timestamp": "2026-05-22T06:45:00Z"}, + {"positionId": 5, "lat": 20.5480, "lng": -100.8135, "speed": 0, "timestamp": "2026-05-22T06:58:00Z"}, + {"positionId": 6, "lat": 20.5515, "lng": -100.8138, "speed": 15, "timestamp": "2026-05-22T07:10:00Z"}, + {"positionId": 7, "lat": 20.5540, "lng": -100.8110, "speed": 22, "timestamp": "2026-05-22T07:25:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 45, "timestamp": "2026-05-22T07:50:00Z"}, + ], + }, + { + "route_id": "RUTA-03", "name": "Sector Poniente - San Juanico", "truck_id": 103, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:10:00Z"}, + {"positionId": 2, "lat": 20.5250, "lng": -100.8510, "speed": 42, "timestamp": "2026-05-22T06:20:00Z"}, + {"positionId": 3, "lat": 20.5290, "lng": -100.8320, "speed": 20, "timestamp": "2026-05-22T06:35:00Z"}, + {"positionId": 4, "lat": 20.5315, "lng": -100.8355, "speed": 15, "timestamp": "2026-05-22T06:48:00Z"}, + {"positionId": 5, "lat": 20.5340, "lng": -100.8390, "speed": 0, "timestamp": "2026-05-22T07:00:00Z"}, + {"positionId": 6, "lat": 20.5362, "lng": -100.8425, "speed": 10, "timestamp": "2026-05-22T07:15:00Z"}, + {"positionId": 7, "lat": 20.5330, "lng": -100.8430, "speed": 18, "timestamp": "2026-05-22T07:28:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 35, "timestamp": "2026-05-22T07:45:00Z"}, + ], + }, + { + "route_id": "RUTA-04", "name": "Oriente - Los Olivos", "truck_id": 104, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:15:00Z"}, + {"positionId": 2, "lat": 20.5260, "lng": -100.8010, "speed": 45, "timestamp": "2026-05-22T06:30:00Z"}, + {"positionId": 3, "lat": 20.5295, "lng": -100.7890, "speed": 24, "timestamp": "2026-05-22T06:45:00Z"}, + {"positionId": 4, "lat": 20.5320, "lng": -100.7850, "speed": 12, "timestamp": "2026-05-22T06:58:00Z"}, + {"positionId": 5, "lat": 20.5350, "lng": -100.7790, "speed": 0, "timestamp": "2026-05-22T07:12:00Z"}, + {"positionId": 6, "lat": 20.5310, "lng": -100.7760, "speed": 15, "timestamp": "2026-05-22T07:25:00Z"}, + {"positionId": 7, "lat": 20.5270, "lng": -100.7820, "speed": 26, "timestamp": "2026-05-22T07:38:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 48, "timestamp": "2026-05-22T07:58:00Z"}, + ], + }, + { + "route_id": "RUTA-05", "name": "Sector Sur - Rancho Seco", "truck_id": 105, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:20:00Z"}, + {"positionId": 2, "lat": 20.5050, "lng": -100.8620, "speed": 35, "timestamp": "2026-05-22T06:32:00Z"}, + {"positionId": 3, "lat": 20.5020, "lng": -100.8350, "speed": 22, "timestamp": "2026-05-22T06:45:00Z"}, + {"positionId": 4, "lat": 20.4995, "lng": -100.8210, "speed": 14, "timestamp": "2026-05-22T06:58:00Z"}, + {"positionId": 5, "lat": 20.4970, "lng": -100.8150, "speed": 0, "timestamp": "2026-05-22T07:10:00Z"}, + {"positionId": 6, "lat": 20.5010, "lng": -100.8120, "speed": 16, "timestamp": "2026-05-22T07:22:00Z"}, + {"positionId": 7, "lat": 20.5060, "lng": -100.8160, "speed": 25, "timestamp": "2026-05-22T07:35:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 40, "timestamp": "2026-05-22T07:55:00Z"}, + ], + }, + { + "route_id": "RUTA-06", "name": "Norte Extremo - Rumbos de Roque", "truck_id": 106, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:00:00Z"}, + {"positionId": 2, "lat": 20.5380, "lng": -100.8380, "speed": 40, "timestamp": "2026-05-22T06:15:00Z"}, + {"positionId": 3, "lat": 20.5610, "lng": -100.8370, "speed": 30, "timestamp": "2026-05-22T06:30:00Z"}, + {"positionId": 4, "lat": 20.5750, "lng": -100.8360, "speed": 15, "timestamp": "2026-05-22T06:45:00Z"}, + {"positionId": 5, "lat": 20.5820, "lng": -100.8350, "speed": 0, "timestamp": "2026-05-22T07:00:00Z"}, + {"positionId": 6, "lat": 20.5780, "lng": -100.8310, "speed": 20, "timestamp": "2026-05-22T07:15:00Z"}, + {"positionId": 7, "lat": 20.5650, "lng": -100.8320, "speed": 28, "timestamp": "2026-05-22T07:30:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 45, "timestamp": "2026-05-22T07:55:00Z"}, + ], + }, + { + "route_id": "RUTA-07", "name": "Nororiente - Ciudad Industrial", "truck_id": 107, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:10:00Z"}, + {"positionId": 2, "lat": 20.5350, "lng": -100.8050, "speed": 44, "timestamp": "2026-05-22T06:24:00Z"}, + {"positionId": 3, "lat": 20.5450, "lng": -100.7950, "speed": 25, "timestamp": "2026-05-22T06:38:00Z"}, + {"positionId": 4, "lat": 20.5480, "lng": -100.7850, "speed": 18, "timestamp": "2026-05-22T06:52:00Z"}, + {"positionId": 5, "lat": 20.5510, "lng": -100.7750, "speed": 0, "timestamp": "2026-05-22T07:05:00Z"}, + {"positionId": 6, "lat": 20.5460, "lng": -100.7720, "speed": 12, "timestamp": "2026-05-22T07:18:00Z"}, + {"positionId": 7, "lat": 20.5390, "lng": -100.7820, "speed": 30, "timestamp": "2026-05-22T07:30:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 42, "timestamp": "2026-05-22T07:52:00Z"}, + ], + }, + { + "route_id": "RUTA-08", "name": "Suroriente - Universidad Latina", "truck_id": 108, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:15:00Z"}, + {"positionId": 2, "lat": 20.5180, "lng": -100.8310, "speed": 38, "timestamp": "2026-05-22T06:28:00Z"}, + {"positionId": 3, "lat": 20.5245, "lng": -100.7980, "speed": 30, "timestamp": "2026-05-22T06:42:00Z"}, + {"positionId": 4, "lat": 20.5210, "lng": -100.7995, "speed": 14, "timestamp": "2026-05-22T06:55:00Z"}, + {"positionId": 5, "lat": 20.5175, "lng": -100.8010, "speed": 0, "timestamp": "2026-05-22T07:08:00Z"}, + {"positionId": 6, "lat": 20.5140, "lng": -100.8030, "speed": 18, "timestamp": "2026-05-22T07:20:00Z"}, + {"positionId": 7, "lat": 20.5110, "lng": -100.8055, "speed": 22, "timestamp": "2026-05-22T07:32:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 40, "timestamp": "2026-05-22T07:54:00Z"}, + ], + }, + { + "route_id": "RUTA-09", "name": "Poniente - Hospital General", "truck_id": 109, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:02:00Z"}, + {"positionId": 2, "lat": 20.5210, "lng": -100.8650, "speed": 45, "timestamp": "2026-05-22T06:12:00Z"}, + {"positionId": 3, "lat": 20.5260, "lng": -100.8520, "speed": 26, "timestamp": "2026-05-22T06:24:00Z"}, + {"positionId": 4, "lat": 20.5275, "lng": -100.8490, "speed": 12, "timestamp": "2026-05-22T06:36:00Z"}, + {"positionId": 5, "lat": 20.5285, "lng": -100.8460, "speed": 0, "timestamp": "2026-05-22T06:48:00Z"}, + {"positionId": 6, "lat": 20.5250, "lng": -100.8470, "speed": 15, "timestamp": "2026-05-22T07:00:00Z"}, + {"positionId": 7, "lat": 20.5220, "lng": -100.8550, "speed": 32, "timestamp": "2026-05-22T07:12:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 44, "timestamp": "2026-05-22T07:30:00Z"}, + ], + }, + { + "route_id": "RUTA-10", "name": "Eje Juan Pablo II - Sede UG Sur", "truck_id": 110, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:22:00Z"}, + {"positionId": 2, "lat": 20.5015, "lng": -100.8520, "speed": 40, "timestamp": "2026-05-22T06:34:00Z"}, + {"positionId": 3, "lat": 20.4990, "lng": -100.8390, "speed": 28, "timestamp": "2026-05-22T06:46:00Z"}, + {"positionId": 4, "lat": 20.4950, "lng": -100.8320, "speed": 18, "timestamp": "2026-05-22T06:58:00Z"}, + {"positionId": 5, "lat": 20.4920, "lng": -100.8280, "speed": 0, "timestamp": "2026-05-22T07:10:00Z"}, + {"positionId": 6, "lat": 20.4945, "lng": -100.8240, "speed": 14, "timestamp": "2026-05-22T07:22:00Z"}, + {"positionId": 7, "lat": 20.4980, "lng": -100.8300, "speed": 30, "timestamp": "2026-05-22T07:34:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 38, "timestamp": "2026-05-22T07:52:00Z"}, + ], + }, + { + "route_id": "RUTA-11", "name": "Zona de Oro - Torres Landa", "truck_id": 111, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:04:00Z"}, + {"positionId": 2, "lat": 20.5240, "lng": -100.8350, "speed": 36, "timestamp": "2026-05-22T06:16:00Z"}, + {"positionId": 3, "lat": 20.5280, "lng": -100.8250, "speed": 22, "timestamp": "2026-05-22T06:29:00Z"}, + {"positionId": 4, "lat": 20.5295, "lng": -100.8210, "speed": 10, "timestamp": "2026-05-22T06:42:00Z"}, + {"positionId": 5, "lat": 20.5310, "lng": -100.8170, "speed": 0, "timestamp": "2026-05-22T06:55:00Z"}, + {"positionId": 6, "lat": 20.5290, "lng": -100.8140, "speed": 16, "timestamp": "2026-05-22T07:08:00Z"}, + {"positionId": 7, "lat": 20.5260, "lng": -100.8220, "speed": 28, "timestamp": "2026-05-22T07:21:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 42, "timestamp": "2026-05-22T07:42:00Z"}, + ], + }, + { + "route_id": "RUTA-12", "name": "Nororiente - Las Insurgentes", "truck_id": 112, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:08:00Z"}, + {"positionId": 2, "lat": 20.5280, "lng": -100.8080, "speed": 40, "timestamp": "2026-05-22T06:22:00Z"}, + {"positionId": 3, "lat": 20.5320, "lng": -100.7980, "speed": 24, "timestamp": "2026-05-22T06:35:00Z"}, + {"positionId": 4, "lat": 20.5340, "lng": -100.7940, "speed": 15, "timestamp": "2026-05-22T06:48:00Z"}, + {"positionId": 5, "lat": 20.5360, "lng": -100.7900, "speed": 0, "timestamp": "2026-05-22T07:00:00Z"}, + {"positionId": 6, "lat": 20.5310, "lng": -100.7920, "speed": 12, "timestamp": "2026-05-22T07:12:00Z"}, + {"positionId": 7, "lat": 20.5270, "lng": -100.8020, "speed": 26, "timestamp": "2026-05-22T07:25:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 44, "timestamp": "2026-05-22T07:48:00Z"}, + ], + }, + { + "route_id": "RUTA-13", "name": "Sector Norte - Trojes e Irrigación", "truck_id": 113, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:12:00Z"}, + {"positionId": 2, "lat": 20.5360, "lng": -100.8190, "speed": 35, "timestamp": "2026-05-22T06:26:00Z"}, + {"positionId": 3, "lat": 20.5420, "lng": -100.8080, "speed": 28, "timestamp": "2026-05-22T06:40:00Z"}, + {"positionId": 4, "lat": 20.5440, "lng": -100.8040, "speed": 14, "timestamp": "2026-05-22T06:54:00Z"}, + {"positionId": 5, "lat": 20.5460, "lng": -100.8000, "speed": 0, "timestamp": "2026-05-22T07:06:00Z"}, + {"positionId": 6, "lat": 20.5410, "lng": -100.8020, "speed": 18, "timestamp": "2026-05-22T07:18:00Z"}, + {"positionId": 7, "lat": 20.5370, "lng": -100.8120, "speed": 25, "timestamp": "2026-05-22T07:30:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 39, "timestamp": "2026-05-22T07:54:00Z"}, + ], + }, + { + "route_id": "RUTA-14", "name": "Sur Poniente - La Toscana", "truck_id": 114, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:16:00Z"}, + {"positionId": 2, "lat": 20.5150, "lng": -100.8580, "speed": 42, "timestamp": "2026-05-22T06:28:00Z"}, + {"positionId": 3, "lat": 20.5140, "lng": -100.8390, "speed": 26, "timestamp": "2026-05-22T06:41:00Z"}, + {"positionId": 4, "lat": 20.5125, "lng": -100.8310, "speed": 16, "timestamp": "2026-05-22T06:54:00Z"}, + {"positionId": 5, "lat": 20.5110, "lng": -100.8250, "speed": 0, "timestamp": "2026-05-22T07:06:00Z"}, + {"positionId": 6, "lat": 20.5135, "lng": -100.8280, "speed": 12, "timestamp": "2026-05-22T07:18:00Z"}, + {"positionId": 7, "lat": 20.5160, "lng": -100.8420, "speed": 32, "timestamp": "2026-05-22T07:30:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 45, "timestamp": "2026-05-22T07:51:00Z"}, + ], + }, + { + "route_id": "RUTA-15", "name": "Norponiente - Camino a San José de Celaya", "truck_id": 115, "status": "EN_RUTA", + "positions": [ + {"positionId": 1, "lat": 20.5111, "lng": -100.9037, "speed": 0, "timestamp": "2026-05-22T06:18:00Z"}, + {"positionId": 2, "lat": 20.5320, "lng": -100.8590, "speed": 38, "timestamp": "2026-05-22T06:31:00Z"}, + {"positionId": 3, "lat": 20.5390, "lng": -100.8480, "speed": 24, "timestamp": "2026-05-22T06:44:00Z"}, + {"positionId": 4, "lat": 20.5420, "lng": -100.8440, "speed": 15, "timestamp": "2026-05-22T06:57:00Z"}, + {"positionId": 5, "lat": 20.5450, "lng": -100.8410, "speed": 0, "timestamp": "2026-05-22T07:09:00Z"}, + {"positionId": 6, "lat": 20.5410, "lng": -100.8430, "speed": 14, "timestamp": "2026-05-22T07:21:00Z"}, + {"positionId": 7, "lat": 20.5360, "lng": -100.8520, "speed": 28, "timestamp": "2026-05-22T07:33:00Z"}, + {"positionId": 8, "lat": 20.5111, "lng": -100.9037, "speed": 41, "timestamp": "2026-05-22T07:54:00Z"}, + ], + }, +] + +ROUTAS_POR_ID = {r["route_id"]: r for r in ROUTE_DATA} + +HORARIOS_POR_RUTA = { + "RUTA-01": {"eta_texto": "Llega en aproximadamente 15 minutos", "eta_minutos": 15}, + "RUTA-02": {"eta_texto": "Llega en aproximadamente 30 minutos", "eta_minutos": 30}, + "RUTA-03": {"eta_texto": "Llega en aproximadamente 40 minutos", "eta_minutos": 40}, + "RUTA-04": {"eta_texto": "Llega en aproximadamente 60 minutos", "eta_minutos": 60}, + "RUTA-05": {"eta_texto": "Llega en aproximadamente 75 minutos", "eta_minutos": 75}, + "RUTA-06": {"eta_texto": "Llega en aproximadamente 50 minutos", "eta_minutos": 50}, + "RUTA-07": {"eta_texto": "Llega en aproximadamente 45 minutos", "eta_minutos": 45}, + "RUTA-08": {"eta_texto": "Llega en aproximadamente 55 minutos", "eta_minutos": 55}, + "RUTA-09": {"eta_texto": "Llega en aproximadamente 20 minutos", "eta_minutos": 20}, + "RUTA-10": {"eta_texto": "Llega en aproximadamente 65 minutos", "eta_minutos": 65}, + "RUTA-11": {"eta_texto": "Llega en aproximadamente 25 minutos", "eta_minutos": 25}, + "RUTA-12": {"eta_texto": "Llega en aproximadamente 35 minutos", "eta_minutos": 35}, + "RUTA-13": {"eta_texto": "Llega en aproximadamente 40 minutos", "eta_minutos": 40}, + "RUTA-14": {"eta_texto": "Llega en aproximadamente 45 minutos", "eta_minutos": 45}, + "RUTA-15": {"eta_texto": "Llega en aproximadamente 55 minutos", "eta_minutos": 55}, +} + +HORARIOS_POR_COLONIA = [ + {"colonia": "Zona Centro", "routeId": "RUTA-01", "horarioEstimado": "Matutino (06:30 - 07:15)"}, + {"colonia": "Las Arboledas", "routeId": "RUTA-01", "horarioEstimado": "Matutino (07:00 - 07:30)"}, + {"colonia": "Trojes", "routeId": "RUTA-13", "horarioEstimado": "Matutino (06:40 - 07:10)"}, + {"colonia": "San Juanico", "routeId": "RUTA-03", "horarioEstimado": "Matutino (06:45 - 07:15)"}, + {"colonia": "Los Olivos", "routeId": "RUTA-04", "horarioEstimado": "Matutino (07:00 - 07:40)"}, + {"colonia": "Rancho Seco", "routeId": "RUTA-05", "horarioEstimado": "Vespertino (14:15 - 15:00)"}, + {"colonia": "Las Insurgentes", "routeId": "RUTA-12", "horarioEstimado": "Matutino (06:35 - 07:10)"}, +] + +COLONIAS_A_RUTAS = {item["colonia"]: item["routeId"] for item in HORARIOS_POR_COLONIA} + +TRIGGER_NOTIFICATIONS = { + "ROUTE_START": { + "position_id": 2, + "title": "¡Ruta Iniciada!", + "body": "El camión recolector ha salido del Relleno Sanitario rumbo a tu sector. Asegúrate de tener listos tus residuos.", + }, + "TRUCK_PROXIMITY": { + "position_id": 4, + "title": "Camión Cercano", + "body": "El camión está a menos de 15 minutos de tu domicilio. Es momento de sacar tus bolsas a la acera.", + }, + "ROUTE_COMPLETED": { + "position_id": 8, + "title": "Servicio Finalizado", + "body": "El camión de tu sector ha concluido su jornada de recolección diaria.", + }, + "GPS_OUTAGE": { + "title": "Alerta GPS", + "body": "El GPS del camión dejó de reportar su ubicación. Estamos investigando la ruta.", + }, +} + +TIPOS_EVENTO_VALIDOS = ["en_camino", "llegando", "completado", "retrasado"] +INFO_ARTICULOS = [ + { + "id": "separacion-basica", + "categoria": "Separación", + "imagen": "assets/images/recycle.jpg", + "titulo": "Cómo separar correctamente tu basura", + "resumen": "La separación correcta es el primer paso para reciclar y reducir el impacto ambiental.", + "contenido": [ + { + "subtitulo": "Residuos Orgánicos 🟤", + "texto": "Restos de comida, cáscaras de frutas y verduras, posos de café, bolsas de té, restos de jardín. Van en bolsa oscura o café. Se convierten en composta.", + }, + { + "subtitulo": "Residuos Inorgánicos Reciclables 🟡", + "texto": "Plásticos (botellas PET, envases), papel y cartón limpios, vidrio, latas de aluminio y hojalata. Van en bolsa transparente o amarilla. Deben estar limpios y secos.", + }, + { + "subtitulo": "Residuos No Reciclables 🔴", + "texto": "Papel higiénico usado, pañales, colillas de cigarro, envolturas metalizadas (como papas). Van en bolsa negra. No tienen valor de reciclaje.", + }, + { + "subtitulo": "Residuos Especiales ⚠️", + "texto": "Pilas, medicamentos caducados, electrónicos, aceite de cocina. NUNCA los mezcles con la basura regular. Lleva pilas y electrónicos a puntos de acopio en supermercados.", + }, + ], + "consejo_rapido": "Regla fácil: si vino de la naturaleza y se pudre → orgánico. Si es artificial y limpio → reciclable.", + }, + { + "id": "cuando-sacar", + "categoria": "Horarios", + "imagen": "assets/images/reloj.png", + "titulo": "¿Cuándo sacar tu basura?", + "resumen": "Sacar la basura en el momento correcto evita plagas, malos olores y que el camión se la pierda.", + "contenido": [ + { + "subtitulo": "El momento ideal", + "texto": "Saca tu basura cuando recibas la alerta de 'Camión Cercano' en la app. Eso significa que el camión está a menos de 15 minutos de tu domicilio.", + }, + { + "subtitulo": "¿Por qué no sacarla de noche?", + "texto": "Las bolsas en la acera de noche atraen perros, gatos y fauna nocturna que las rompen y dispersan los residuos. Además el plástico se deteriora con la humedad nocturna.", + }, + { + "subtitulo": "¿Y si me lo pierdo?", + "texto": "Si el camión ya pasó, guarda tu basura hasta el siguiente día. Nunca dejes bolsas en la vía pública fuera del horario de recolección: es una multa en muchos municipios.", + }, + { + "subtitulo": "Días festivos", + "texto": "En días festivos el servicio puede retrasarse o cancelarse. Activa las notificaciones de la app para recibir alertas de retraso o cambio de horario.", + }, + ], + "consejo_rapido": "Espera la alerta de la app antes de salir con tus bolsas. Te ahorra tiempo y evita dejar basura expuesta.", + }, + { + "id": "plasticos-guia", + "categoria": "Reciclaje", + "imagen": "assets/images/bottle.png", + "titulo": "Guía de plásticos: cuáles sí y cuáles no", + "resumen": "No todos los plásticos son iguales. Aprende a leer el número en el triángulo de reciclaje.", + "contenido": [ + { + "subtitulo": "✅ Plástico #1 — PET", + "texto": "Botellas de agua, refrescos, aceite. El más reciclado. Aplástalo para ahorrar espacio. Quita la tapa (es diferente material).", + }, + { + "subtitulo": "✅ Plástico #2 — HDPE", + "texto": "Garrafones, botellas de leche, shampú. También muy reciclable. Enjuágalo antes de separarlo.", + }, + { + "subtitulo": "✅ Plástico #5 — PP", + "texto": "Tapas de botellas, envases de yogur, popotes. Sí se recicla pero menos centros lo aceptan.", + }, + { + "subtitulo": "❌ Plásticos #3, #6, #7", + "texto": "PVC (mangueras, tuberías), poliestireno expandido (unicel), policarbonato. Difíciles o imposibles de reciclar. Van a basura no reciclable.", + }, + { + "subtitulo": "❌ Bolsas de plástico", + "texto": "Las bolsas de supermercado no van en el reciclaje de casa: tapan las máquinas clasificadoras. Lleva tus bolsas a centros de acopio específicos en supermercados.", + }, + ], + "consejo_rapido": "Busca el número dentro del triángulo en el fondo del envase. #1 y #2 siempre al reciclaje.", + }, + { + "id": "composta", + "categoria": "Compostaje", + "imagen": "assets/images/planta.png", + "titulo": "Haz composta en casa", + "resumen": "Convierte tus residuos orgánicos en abono natural. Es más fácil de lo que crees.", + "contenido": [ + { + "subtitulo": "¿Qué necesitas?", + "texto": "Un contenedor con tapa (puede ser una cubeta con tapa o una caja de madera), residuos orgánicos, tierra o tierra de hojarasca, y un poco de paciencia.", + }, + { + "subtitulo": "¿Qué puedes compostar?", + "texto": "Cáscaras de frutas y verduras, restos de comida cocida sin carne, posos de café y filtros de papel, cáscaras de huevo (aplástelas), hojas secas, recortes de jardín.", + }, + { + "subtitulo": "¿Qué NO debes compostar?", + "texto": "Carnes, pescados, lácteos, aceites (atraen plagas), excrementos de mascotas (patógenos), plásticos ni metales.", + }, + { + "subtitulo": "El proceso", + "texto": "Alterna capas de residuos orgánicos húmedos con capas de material seco (tierra, hojas). Voltea la mezcla cada semana. En 2-3 meses tendrás composta lista para tus plantas.", + }, + ], + "consejo_rapido": "La composta lista huele a tierra mojada, no a podrido. Si huele mal, agrega más material seco y voltéala.", + }, + { + "id": "residuos-peligrosos", + "categoria": "Residuos Especiales", + "imagen": "assets/images/megafono.png", + "titulo": "Residuos peligrosos: cómo deshacerte de ellos", + "resumen": "Pilas, medicamentos y electrónicos requieren un manejo especial para no contaminar el suelo y el agua.", + "contenido": [ + { + "subtitulo": "Pilas y baterías", + "texto": "Una sola pila AA puede contaminar 600,000 litros de agua. Guárdalas en una bolsa o caja y lleva a los puntos de acopio en Walmart, Soriana, Home Depot o OXXO. Nunca al drenaje ni al fuego.", + }, + { + "subtitulo": "Medicamentos caducados", + "texto": "No los tires al drenaje ni a la basura regular. Farmacias como Farmacias del Ahorro y Benavides cuentan con contenedores REPARED para medicamentos. El municipio también hace jornadas de recolección.", + }, + { + "subtitulo": "Electrónicos (RAEE)", + "texto": "Celulares, computadoras, cables, focos LED. Contienen plomo, mercurio y cadmio. Lleva a tiendas de electrónicos (Best Buy, Liverpool) o espera las jornadas municipales de recolección.", + }, + { + "subtitulo": "Aceite de cocina", + "texto": "Un litro de aceite contamina hasta 1,000 litros de agua potable. Enfríalo, viértelo en una botella PET con tapa y lleva a centros de acopio o úsalo para hacer jabón casero.", + }, + ], + "consejo_rapido": "Guarda una caja en casa exclusiva para residuos peligrosos. Cuando esté llena, busca el punto de acopio más cercano.", + }, + { + "id": "impacto-ambiental", + "categoria": "Medio Ambiente", + "imagen": "assets/images/globo.png", + "titulo": "El impacto real de reciclar", + "resumen": "Números concretos para entender por qué vale la pena separar tu basura cada día.", + "contenido": [ + { + "subtitulo": "Papel y cartón", + "texto": "Reciclar 1 tonelada de papel salva 17 árboles, ahorra 26,000 litros de agua y evita 4,000 kWh de energía. Una familia promedio genera ~500 kg de papel al año.", + }, + { + "subtitulo": "Aluminio", + "texto": "Reciclar una lata de aluminio ahorra la energía suficiente para que un foco LED funcione 20 horas. El aluminio puede reciclarse infinitas veces sin perder calidad.", + }, + { + "subtitulo": "Vidrio", + "texto": "El vidrio tarda más de 4,000 años en degradarse. Reciclarlo reduce en 20% las emisiones de CO₂ de su producción. Una botella puede reciclarse indefinidamente.", + }, + { + "subtitulo": "Plástico PET", + "texto": "5 botellas PET recicladas generan fibra suficiente para una camiseta de poliéster. México recicla menos del 20% del PET que consume — hay mucho potencial de mejora.", + }, + { + "subtitulo": "Residuos en México", + "texto": "México genera ~120,000 toneladas de basura al día. Solo el 9% se recicla formalmente. Si cada hogar separara correctamente, ese porcentaje podría triplicarse.", + }, + ], + "consejo_rapido": "Cada lata de aluminio que reciclas ahorra energía equivalente a medio litro de gasolina. Sí importa.", + }, +] +ROUTE_STATE = {} +for _route in ROUTE_DATA: + _pos = _route.get("positions", []) + ROUTE_STATE[_route["route_id"]] = { + "last_position_id": _pos[0]["positionId"] if _pos else 0, + "last_timestamp": datetime.now(timezone.utc), + "gps_ok": True, + "gps_alert_sent": False, + "triggers_sent": {k: False for k in TRIGGER_NOTIFICATIONS if k != "GPS_OUTAGE"}, + "error_message": None, + } + + +# =============================================================== +# FIREBASE ADMIN SDK +# =============================================================== +import firebase_admin +from firebase_admin import credentials, messaging + +try: + cred = credentials.Certificate("firebase-credentials.json") + firebase_admin.initialize_app(cred) + logger.info("✅ Firebase Admin SDK inicializado correctamente") +except Exception as e: + logger.error(f"❌ Error inicializando Firebase: {e}") + +FIREBASE_ACTIVO = True + + +# =============================================================== +# FASTAPI APP +# =============================================================== +app = FastAPI( + title="Sistema de Notificación de Residuos — v2", + description="API privada para notificaciones de recolección de basura", + version="0.2.0", +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +# =============================================================== +# SCHEMAS PYDANTIC +# =============================================================== + +class ETAResponse(BaseModel): + usuario_id: int + colonia: str + ruta_nombre: str + ruta_status: str + gps_ok: bool + eta_texto: str + eta_minutos: int + mensaje_preventivo: str + error_message: Optional[str] = None + + +class UsuarioRegisterRequest(BaseModel): + nombre: str + email: str + password: str + colonia: str + direccion: str + + +class UsuarioLoginRequest(BaseModel): + email: str + password: str + + +class DireccionRequest(BaseModel): + colonia: str + direccion: str + + +class DomicilioResponse(BaseModel): + colonia: str + direccion: str + + +class DomicilioAdminResponse(BaseModel): + colonia: str + direccion: str + route_id: str + ruta_nombre: str + + +class UsuarioResponse(BaseModel): + usuario_id: int + nombre: str + email: Optional[str] = None # ← igual aquí + direcciones: List[DomicilioResponse] + + +class UsuarioAdminResponse(BaseModel): + usuario_id: int + nombre: str + email: Optional[str] = None # ← cambia str por Optional[str] + direcciones: List[DomicilioAdminResponse] + + +class RegisterResponse(BaseModel): + usuario_id: int + mensaje: str + + +class LoginResponse(BaseModel): + usuario_id: int + nombre: str + mensaje: str + + +class RouteStatusResponse(BaseModel): + route_id: str + name: str + status: str + last_position_id: int + last_timestamp: str + gps_ok: bool + error_message: Optional[str] = None + + +class RoutePositionUpdateRequest(BaseModel): + position_id: int + lat: float + lng: float + timestamp: str + + +class ActualizarPasswordRequest(BaseModel): + password_actual: str # Requerimos la contraseña actual para cambiarla + password_nuevo: str + + +class ActualizarTokenRequest(BaseModel): + fcm_token: str + + +class SimularEventoRequest(BaseModel): + route_id: str + tipo_evento: str + + +class SimularEventoResponse(BaseModel): + usuarios_notificados: int + route_id: str + tipo_evento: str + detalle: list[str] + + +# --------------------------------------------------------------- +# NUEVOS SCHEMAS PARA VISUALIZACIÓN +# --------------------------------------------------------------- + +class PosicionGPS(BaseModel): + position_id: int + lat: float + lng: float + speed: int + timestamp: str + es_actual: bool # True si esta es la posición donde está el camión ahora + + +class RutaDetalleResponse(BaseModel): + route_id: str + name: str + status: str + truck_id: int + posicion_actual: int + total_posiciones: int + porcentaje_completado: float + eta_minutos: int + gps_ok: bool + usuarios_en_ruta: int # Cuántos usuarios están en esta ruta + + +class DashboardResponse(BaseModel): + total_rutas: int + rutas_en_progreso: int + rutas_completadas: int + total_usuarios: int + usuarios_con_token: int # Cuántos pueden recibir push notifications + cobertura_notificaciones: float # % de usuarios con FCM token + rutas: List[RutaDetalleResponse] + + +class ColoniaEstadisticaResponse(BaseModel): + colonia: str + route_id: str + ruta_nombre: str + horario: str + total_usuarios: int + usuarios_con_notificaciones: int + + +# =============================================================== +# UTILIDADES INTERNAS +# =============================================================== + +def generar_mensaje_preventivo(eta_minutos: int) -> str: + if eta_minutos <= 5: + return "🚛 ¡El camión está muy cerca! Saca tu basura AHORA." + elif eta_minutos <= 15: + return "⏰ Prepárate, el camión llega pronto. No saques tu basura aún." + elif eta_minutos <= 30: + return "🕐 Tienes tiempo. No saques tu basura todavía." + else: + return "😌 Aún falta bastante. Mantén tu basura adentro por ahora." + + +def enviar_notificacion_firebase(fcm_token: str, titulo: str, cuerpo: str) -> bool: + if not FIREBASE_ACTIVO: + logger.info(f"[SIMULADO] Push → Token: {fcm_token[:20]}... | {titulo}: {cuerpo}") + return True + try: + message = messaging.Message( + notification=messaging.Notification(title=titulo, body=cuerpo), + token=fcm_token, + ) + response = messaging.send(message) + logger.info(f"✅ Notificación enviada: {response}") + return True + except Exception as e: + logger.error(f"❌ Error enviando push: {e}") + return False + + +def _calcular_eta_por_ruta(route_id: str) -> Dict: + estado = ROUTE_STATE.get(route_id) + ruta = ROUTAS_POR_ID.get(route_id, {}) + horario = HORARIOS_POR_RUTA.get(route_id) + + if not horario: + return {"eta_texto": "Horario no disponible para esta zona.", "eta_minutos": 60} + + if estado and ruta.get("positions"): + if ruta.get("status") == "DETENIDA": + return {"eta_texto": "Ruta detenida", "eta_minutos": 0} + posiciones = ruta["positions"] + ultimo_id = estado.get("last_position_id", 1) + if ultimo_id >= len(posiciones): + return {"eta_texto": "El servicio ya pasó por tu zona.", "eta_minutos": 0} + pasos_restantes = max(0, len(posiciones) - ultimo_id) + eta = pasos_restantes * 10 + return {"eta_texto": f"Llega en aproximadamente {eta} minutos", "eta_minutos": eta} + + return {"eta_texto": horario["eta_texto"], "eta_minutos": horario["eta_minutos"]} + +def _verificar_gps_outage(route_id: str, db: Session) -> None: + estado = ROUTE_STATE.get(route_id) + if not estado: + return + ultimo = estado.get("last_timestamp", datetime.now(timezone.utc)) + gps_ok = (datetime.now(timezone.utc) - ultimo) < timedelta(minutes=10) + if not gps_ok and not estado.get("gps_alert_sent", False): + logger.warning(f"Alerta GPS outage para {route_id}") + _notificar_ruta(db, route_id, "GPS_OUTAGE") + estado["gps_alert_sent"] = True + +def _obtener_estado_ruta(route_id: str, db: Optional[Session] = None) -> Dict: + ruta = ROUTAS_POR_ID.get(route_id) + estado = ROUTE_STATE.get(route_id, {}) + + if not ruta: + # Ruta en DB pero sin datos en memoria — retornar estado genérico + return { + "route_id": route_id, + "name": f"Ruta {route_id}", + "status": "SIN_DATOS", + "last_position_id": 0, + "last_timestamp": datetime.now(timezone.utc).isoformat(), + "gps_ok": False, + "error_message": "Ruta sin datos de seguimiento disponibles.", + } + + ultimo = estado.get("last_timestamp", datetime.now(timezone.utc)) + gps_ok = (datetime.now(timezone.utc) - ultimo) < timedelta(minutes=10) + if db is not None and not gps_ok: + _verificar_gps_outage(route_id, db) + return { + "route_id": route_id, + "name": ruta.get("name", "Ruta desconocida"), + "status": ruta.get("status", "DESCONOCIDA"), + "last_position_id": estado.get("last_position_id", 0), + "last_timestamp": ultimo.isoformat(), + "gps_ok": gps_ok, + "error_message": estado.get("error_message"), + } + + +def _procesar_trigger_posicion(route_id: str, position_id: int, db: Session) -> list[str]: + estado = ROUTE_STATE.get(route_id) + if not estado: + return [] + mensajes = [] + sent_map = estado.setdefault("triggers_sent", {}) + for trigger_key, trigger in TRIGGER_NOTIFICATIONS.items(): + if trigger_key == "GPS_OUTAGE": + continue + if trigger.get("position_id") == position_id and not sent_map.get(trigger_key, False): + mensajes.extend(_notificar_ruta(db, route_id, trigger_key)) + sent_map[trigger_key] = True + return mensajes + + +def _notificar_ruta(db: Session, route_id: str, trigger_key: str) -> list[str]: + trigger = TRIGGER_NOTIFICATIONS.get(trigger_key) + if not trigger: + return [f"Trigger desconocido: {trigger_key}"] + domicilios = db.query(Domicilio).filter(Domicilio.route_id == route_id).all() + mensajes = [] + for domicilio in domicilios: + usuario = domicilio.usuario + if not usuario or not usuario.fcm_token: + mensajes.append("Usuario sin token.") + continue + enviado = enviar_notificacion_firebase(usuario.fcm_token, trigger["title"], trigger["body"]) + mensajes.append( + f"✅ Push a {usuario.nombre}" if enviado else f"❌ Fallo push a {usuario.nombre}" + ) + return mensajes + + +def _obtener_rutas_usuario(usuario_id: int, db: Session) -> List[str]: + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado.") + return list({d.route_id for d in usuario.direcciones}) + + +# =============================================================== +# ENDPOINTS — USUARIOS +# =============================================================== + +@app.post("/api/usuarios/register", response_model=RegisterResponse, tags=["Usuarios"]) +def registrar_usuario(payload: UsuarioRegisterRequest, db: Session = Depends(get_db)): + existing = db.query(Usuario).filter(Usuario.email == payload.email.lower().strip()).first() + if existing: + raise HTTPException(status_code=400, detail="El correo ya está registrado.") + if not payload.password or not payload.password.strip(): + raise HTTPException(status_code=400, detail="La contraseña no puede estar vacía.") + route_id = COLONIAS_A_RUTAS.get(payload.colonia) + if not route_id: + raise HTTPException(status_code=400, detail=f"Colonia no válida. Opciones: {list(COLONIAS_A_RUTAS.keys())}") + + usuario = Usuario( + nombre=payload.nombre.strip(), + email=payload.email.lower().strip(), + password_hash=hash_password(payload.password), + fcm_token=None, + ) + db.add(usuario) + db.flush() + db.add(Domicilio(usuario_id=usuario.id, colonia=payload.colonia, direccion=payload.direccion, route_id=route_id)) + db.commit() + logger.info(f"✅ Usuario registrado: {usuario.email} (ID {usuario.id})") + return RegisterResponse(usuario_id=usuario.id, mensaje="Usuario registrado correctamente.") + + +@app.post("/api/usuarios/login", response_model=LoginResponse, tags=["Usuarios"]) +def login_usuario(payload: UsuarioLoginRequest, db: Session = Depends(get_db)): + usuario = db.query(Usuario).filter(Usuario.email == payload.email.lower().strip()).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado. Regístrate primero.") + if not verify_password(payload.password, usuario.password_hash): + raise HTTPException(status_code=401, detail="Contraseña incorrecta.") + + # ── MIGRACIÓN AUTOMÁTICA LEGACY ────────────────────────────── + # Si el hash guardado es sha256 (antiguo), lo re-hasheamos con + # bcrypt ahora que sabemos que el password es correcto. + if _es_hash_legacy(usuario.password_hash): + usuario.password_hash = hash_password(payload.password) + db.commit() + logger.info(f"🔄 Hash migrado sha256→bcrypt para usuario {usuario.id}") + + return LoginResponse(usuario_id=usuario.id, nombre=usuario.nombre, mensaje="Login exitoso.") + + +@app.get("/api/usuarios/{usuario_id}", response_model=UsuarioResponse, tags=["Usuarios"]) +def obtener_usuario(usuario_id: int, db: Session = Depends(get_db)): + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado.") + return UsuarioResponse( + usuario_id=usuario.id, + nombre=usuario.nombre, + email=usuario.email, + direcciones=[DomicilioResponse(colonia=d.colonia, direccion=d.direccion) for d in usuario.direcciones], + ) + + +@app.get("/api/usuarios", response_model=List[UsuarioAdminResponse], tags=["Usuarios"]) +def listar_usuarios(db: Session = Depends(get_db)): + usuarios = db.query(Usuario).all() + return [ + UsuarioAdminResponse( + usuario_id=u.id, + nombre=u.nombre, + email=u.email, + direcciones=[ + DomicilioAdminResponse( + colonia=d.colonia, + direccion=d.direccion, + route_id=d.route_id, + ruta_nombre=ROUTAS_POR_ID.get(d.route_id, {}).get("name", "Desconocida"), + ) + for d in u.direcciones + ], + ) + for u in usuarios + ] + + +@app.post("/api/usuarios/{usuario_id}/direcciones", tags=["Usuarios"]) +def agregar_direccion(usuario_id: int, payload: DireccionRequest, db: Session = Depends(get_db)): + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado.") + route_id = COLONIAS_A_RUTAS.get(payload.colonia) + if not route_id: + raise HTTPException(status_code=400, detail="Colonia no válida.") + db.add(Domicilio(usuario_id=usuario.id, colonia=payload.colonia, direccion=payload.direccion, route_id=route_id)) + db.commit() + return {"mensaje": "Dirección agregada correctamente."} + + +@app.put("/api/usuarios/{usuario_id}/password", tags=["Usuarios"]) +def actualizar_password(usuario_id: int, payload: ActualizarPasswordRequest, db: Session = Depends(get_db)): + """ + Actualiza la contraseña de un usuario. + Requiere la contraseña actual para confirmar identidad. + """ + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado.") + if not verify_password(payload.password_actual, usuario.password_hash): + raise HTTPException(status_code=401, detail="La contraseña actual es incorrecta.") + if not payload.password_nuevo or not payload.password_nuevo.strip(): + raise HTTPException(status_code=400, detail="La nueva contraseña no puede estar vacía.") + if len(payload.password_nuevo) < 6: + raise HTTPException(status_code=400, detail="La nueva contraseña debe tener al menos 6 caracteres.") + usuario.password_hash = hash_password(payload.password_nuevo) + db.commit() + logger.info(f"🔑 Contraseña actualizada para usuario {usuario_id}") + return {"mensaje": "Contraseña actualizada correctamente."} + + +@app.put("/api/usuarios/{usuario_id}/fcm-token", tags=["Utilidades"]) +def actualizar_fcm_token(usuario_id: int, payload: ActualizarTokenRequest, db: Session = Depends(get_db)): + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado.") + usuario.fcm_token = payload.fcm_token + db.commit() + return {"mensaje": f"Token actualizado para usuario {usuario_id}"} + + +# =============================================================== +# ENDPOINTS — RUTAS Y ETA +# =============================================================== + +@app.get("/api/eta/{usuario_id}", response_model=ETAResponse, tags=["Core"]) +def obtener_eta(usuario_id: int, db: Session = Depends(get_db)): + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail=f"Usuario {usuario_id} no encontrado.") + if not usuario.direcciones: + raise HTTPException(status_code=404, detail="El usuario no tiene direcciones registradas.") + + direccion = db.query(Domicilio).filter(Domicilio.usuario_id == usuario_id).order_by(Domicilio.id.desc()).first() + route_id = direccion.route_id + ruta = ROUTAS_POR_ID.get(route_id, {}) + estado_ruta = _obtener_estado_ruta(route_id, db) + calculo = _calcular_eta_por_ruta(route_id) + + return ETAResponse( + usuario_id=usuario_id, + colonia=direccion.colonia, + ruta_nombre=ruta.get("name", "Ruta desconocida"), + ruta_status=estado_ruta["status"], + gps_ok=estado_ruta["gps_ok"], # ← agregar esta línea + eta_texto=calculo["eta_texto"], + eta_minutos=calculo["eta_minutos"], + mensaje_preventivo=generar_mensaje_preventivo(calculo["eta_minutos"]), +) + + +@app.get("/api/rutas", tags=["Rutas"]) +def listar_rutas(usuario_id: int = Query(...), db: Session = Depends(get_db)): + rutas_usuario = _obtener_rutas_usuario(usuario_id, db) + return {"rutas": [_obtener_estado_ruta(r, db) for r in rutas_usuario]} + + +@app.get("/api/rutas/{route_id}/estado", response_model=RouteStatusResponse, tags=["Rutas"]) +def estado_ruta(route_id: str, db: Session = Depends(get_db)): + try: + return RouteStatusResponse(**_obtener_estado_ruta(route_id, db)) + except ValueError as e: + raise HTTPException(status_code=404, detail=str(e)) + + +@app.post("/api/rutas/{route_id}/avanzar", response_model=RouteStatusResponse, tags=["Rutas"]) +def avanzar_ruta(route_id: str, usuario_id: int = Query(...), db: Session = Depends(get_db)): + if route_id not in ROUTAS_POR_ID: + raise HTTPException(status_code=404, detail="Ruta no encontrada.") + rutas_usuario = _obtener_rutas_usuario(usuario_id, db) + if route_id not in rutas_usuario: + raise HTTPException(status_code=403, detail="No tienes permiso para avanzar esta ruta.") + + ruta = ROUTAS_POR_ID[route_id] + estado = ROUTE_STATE.get(route_id) + posiciones = ruta.get("positions", []) + if not estado or not posiciones: + raise HTTPException(status_code=500, detail="Estado de ruta no disponible.") + + actual = estado.get("last_position_id", 0) + if actual < len(posiciones): + siguiente = actual + 1 + estado["last_position_id"] = siguiente + estado["last_timestamp"] = datetime.now(timezone.utc) + estado["gps_ok"] = True + estado["gps_alert_sent"] = False + mensajes = _procesar_trigger_posicion(route_id, siguiente, db) + ruta["status"] = "COMPLETADO" if siguiente >= len(posiciones) else "EN_RUTA" + if mensajes: + logger.info(f"Triggers en {route_id}: {mensajes}") + colonia = db.query(Domicilio).filter( + Domicilio.route_id == route_id + ).first() + colonia_nombre = colonia.colonia if colonia else "Desconocida" + evento = "COMPLETADO" if siguiente >= len(posiciones) else "AVANCE" + _registrar_evento_ruta(db, route_id, siguiente, evento, colonia_nombre) + return RouteStatusResponse(**_obtener_estado_ruta(route_id, db)) + + +@app.post("/api/rutas/{route_id}/posicion", tags=["Rutas"]) +def actualizar_posicion_ruta(route_id: str, payload: RoutePositionUpdateRequest, db: Session = Depends(get_db)): + if route_id not in ROUTAS_POR_ID: + raise HTTPException(status_code=404, detail="Ruta no encontrada.") + estado = ROUTE_STATE.get(route_id) + if not estado: + raise HTTPException(status_code=500, detail="Estado de ruta no inicializado.") + try: + timestamp = datetime.fromisoformat(payload.timestamp.replace("Z", "+00:00")) + except ValueError: + raise HTTPException(status_code=400, detail="Timestamp inválido. Usa formato ISO 8601 UTC.") + + mensajes = [] + if payload.position_id > estado["last_position_id"]: + mensajes = _procesar_trigger_posicion(route_id, payload.position_id, db) + + estado.update({ + "last_position_id": payload.position_id, + "last_timestamp": timestamp, + "gps_ok": True, + "gps_alert_sent": False, + }) + if payload.position_id == 8: + ROUTAS_POR_ID[route_id]["status"] = "COMPLETADO" + + return {"route_id": route_id, "position_id": payload.position_id, "timestamp": payload.timestamp, "gps_ok": True, "mensajes": mensajes} + + +# =============================================================== +# ENDPOINTS — VISUALIZACIÓN Y ESTADÍSTICAS (NUEVOS EN v2) +# =============================================================== + +@app.get("/api/dashboard", response_model=DashboardResponse, tags=["Visualización"]) +def obtener_dashboard(db: Session = Depends(get_db)): + """ + Vista global del sistema para monitoreo. + Muestra el estado de todas las rutas + métricas de usuarios. + + Útil para: + - Panel de control del operador municipal + - Demo en hackathon (muestra todo de un vistazo) + """ + todos_usuarios = db.query(Usuario).all() + total_usuarios = len(todos_usuarios) + usuarios_con_token = sum(1 for u in todos_usuarios if u.fcm_token) + cobertura = (usuarios_con_token / total_usuarios * 100) if total_usuarios > 0 else 0.0 + + rutas_detalle = [] + rutas_en_progreso = 0 + rutas_completadas = 0 + + for route_id, ruta in ROUTAS_POR_ID.items(): + estado = ROUTE_STATE.get(route_id, {}) + calculo = _calcular_eta_por_ruta(route_id) + posiciones = ruta.get("positions", []) + total_pos = len(posiciones) + actual_pos = estado.get("last_position_id", 0) + porcentaje = round((actual_pos / total_pos * 100) if total_pos > 0 else 0.0, 1) + + status = ruta.get("status", "DESCONOCIDA") + if status == "EN_RUTA": + rutas_en_progreso += 1 + elif status == "COMPLETADO": + rutas_completadas += 1 + + # Contar usuarios asignados a esta ruta + usuarios_en_ruta = db.query(Domicilio).filter(Domicilio.route_id == route_id).count() + + ultimo = estado.get("last_timestamp", datetime.now(timezone.utc)) + gps_ok = (datetime.now(timezone.utc) - ultimo) < timedelta(minutes=10) + + rutas_detalle.append(RutaDetalleResponse( + route_id=route_id, + name=ruta.get("name", "Desconocida"), + status=status, + truck_id=ruta.get("truck_id", 0), + posicion_actual=actual_pos, + total_posiciones=total_pos, + porcentaje_completado=porcentaje, + eta_minutos=calculo["eta_minutos"], + gps_ok=gps_ok, + usuarios_en_ruta=usuarios_en_ruta, + )) + + return DashboardResponse( + total_rutas=len(ROUTAS_POR_ID), + rutas_en_progreso=rutas_en_progreso, + rutas_completadas=rutas_completadas, + total_usuarios=total_usuarios, + usuarios_con_token=usuarios_con_token, + cobertura_notificaciones=round(cobertura, 1), + rutas=rutas_detalle, + ) + + +@app.get("/api/rutas/{route_id}/posiciones", response_model=List[PosicionGPS], tags=["Visualización"]) +def historial_posiciones(route_id: str): + """ + Devuelve el historial completo de posiciones GPS de una ruta, + marcando cuál es la posición actual del camión. + + Útil para dibujar el recorrido en un mapa en la app. + """ + ruta = ROUTAS_POR_ID.get(route_id) + if not ruta: + raise HTTPException(status_code=404, detail="Ruta no encontrada.") + + estado = ROUTE_STATE.get(route_id, {}) + posicion_actual = estado.get("last_position_id", 0) + + resultado = [] + for pos in ruta.get("positions", []): + resultado.append(PosicionGPS( + position_id=pos["positionId"], + lat=pos["lat"], + lng=pos["lng"], + speed=pos["speed"], + timestamp=pos["timestamp"], + es_actual=(pos["positionId"] == posicion_actual), + )) + return resultado + + +@app.get("/api/rutas/resumen", tags=["Visualización"]) +def resumen_rutas(db: Session = Depends(get_db)): + """ + Vista rápida y ligera de todas las rutas activas. + Diseñada para refrescarse cada 30s en la app sin sobrecargar. + """ + resumen = [] + for route_id, ruta in ROUTAS_POR_ID.items(): + estado = ROUTE_STATE.get(route_id, {}) + ultimo = estado.get("last_timestamp", datetime.now(timezone.utc)) + gps_ok = (datetime.now(timezone.utc) - ultimo) < timedelta(minutes=10) + calculo = _calcular_eta_por_ruta(route_id) + resumen.append({ + "route_id": route_id, + "name": ruta.get("name"), + "status": ruta.get("status"), + "eta_minutos": calculo["eta_minutos"], + "gps_ok": gps_ok, + "posicion_actual": estado.get("last_position_id", 0), + "total_posiciones": len(ruta.get("positions", [])), + }) + return {"rutas": resumen, "timestamp": datetime.now(timezone.utc).isoformat()} + + +@app.get("/api/estadisticas/colonias", response_model=List[ColoniaEstadisticaResponse], tags=["Visualización"]) +def estadisticas_por_colonia(db: Session = Depends(get_db)): + """ + Estadísticas por colonia: cuántos usuarios hay y cuántos + tienen notificaciones activas. Útil para el operador. + """ + resultado = [] + for item in HORARIOS_POR_COLONIA: + colonia = item["colonia"] + route_id = item["routeId"] + + domicilios = db.query(Domicilio).filter(Domicilio.colonia == colonia).all() + total = len(domicilios) + con_notif = sum(1 for d in domicilios if d.usuario and d.usuario.fcm_token) + + resultado.append(ColoniaEstadisticaResponse( + colonia=colonia, + route_id=route_id, + ruta_nombre=ROUTAS_POR_ID.get(route_id, {}).get("name", "Desconocida"), + horario=item.get("horarioEstimado", ""), + total_usuarios=total, + usuarios_con_notificaciones=con_notif, + )) + return resultado + + +# =============================================================== +# ENDPOINTS — UTILIDADES +# =============================================================== + +@app.get("/api/colonias", tags=["Utilidades"]) +def listar_colonias(): + return {"colonias": [item["colonia"] for item in HORARIOS_POR_COLONIA]} + + +@app.post("/api/simular-evento", response_model=SimularEventoResponse, tags=["Core"]) +def simular_evento(payload: SimularEventoRequest, db: Session = Depends(get_db)): + if payload.tipo_evento not in TIPOS_EVENTO_VALIDOS: + raise HTTPException(status_code=400, detail=f"tipo_evento inválido. Opciones: {TIPOS_EVENTO_VALIDOS}") + if payload.route_id not in HORARIOS_POR_RUTA: + raise HTTPException(status_code=404, detail=f"Ruta {payload.route_id} no encontrada.") + + domicilios = db.query(Domicilio).filter(Domicilio.route_id == payload.route_id).all() + if not domicilios: + return SimularEventoResponse(usuarios_notificados=0, route_id=payload.route_id, tipo_evento=payload.tipo_evento, detalle=["No hay usuarios registrados en esta ruta."]) + + mensajes_por_evento = { + "en_camino": ("🚛 Camión en camino", "El camión de recolección está en ruta hacia tu zona."), + "llegando": ("⚠️ ¡El camión está cerca!", "Saca tu basura ahora, el camión llega en minutos."), + "completado": ("✅ Recolección completada", "El camión ya pasó por tu zona. Nos vemos mañana."), + "retrasado": ("🕐 Retraso en ruta", "El camión se ha retrasado. Te avisaremos cuando esté cerca."), + } + titulo, cuerpo = mensajes_por_evento[payload.tipo_evento] + + detalle_log = [] + notificados = 0 + for domicilio in domicilios: + usuario = domicilio.usuario + if not usuario: + detalle_log.append(f"Domicilio ID {domicilio.id}: sin usuario asociado.") + continue + if not usuario.fcm_token: + detalle_log.append(f"Usuario '{usuario.nombre}': sin FCM token.") + continue + if enviar_notificacion_firebase(usuario.fcm_token, titulo, cuerpo): + notificados += 1 + detalle_log.append(f"✅ Push a {usuario.nombre} ({domicilio.colonia})") + else: + detalle_log.append(f"❌ Fallo push a {usuario.nombre}") + + return SimularEventoResponse(usuarios_notificados=notificados, route_id=payload.route_id, tipo_evento=payload.tipo_evento, detalle=detalle_log) + + +@app.post("/api/seed", tags=["Utilidades"]) +def seed_datos(db: Session = Depends(get_db)): + if db.query(Usuario).count() > 0: + return {"mensaje": "Ya hay datos en la DB. No se hizo nada."} + usuarios_seed = [ + {"nombre": "Ana García", "email": "ana@example.com", "colonia": "Zona Centro", "direccion": "Calle Principal 123", "password": "123456"}, + {"nombre": "Carlos López", "email": "carlos@example.com", "colonia": "Las Arboledas", "direccion": "Av. Hidalgo 45", "password": "123456"}, + {"nombre": "María Torres", "email": "maria@example.com", "colonia": "San Juanico", "direccion": "Calle Luna 12", "password": "123456"}, + {"nombre": "Pedro Ruiz", "email": "pedro@example.com", "colonia": "Los Olivos", "direccion": "Calle Sol 78", "password": "123456"}, + ] + for u in usuarios_seed: + route_id = COLONIAS_A_RUTAS.get(u["colonia"], "RUTA-01") + usuario = Usuario(nombre=u["nombre"], email=u["email"], password_hash=hash_password(u["password"])) + db.add(usuario) + db.flush() + db.add(Domicilio(usuario_id=usuario.id, colonia=u["colonia"], direccion=u["direccion"], route_id=route_id)) + db.commit() + logger.info("✅ Seed completado: 4 usuarios creados con bcrypt") + return {"mensaje": "Seed exitoso. Passwords: 123456 para todos."} + +# Schema Pydantic para la respuesta +# (agregar junto a los otros schemas en main.py) + +class SubseccionInfo(BaseModel): + subtitulo: str + texto: str + +class ArticuloInfo(BaseModel): + id: str + categoria: str + emoji: str + titulo: str + resumen: str + contenido: List[SubseccionInfo] + consejo_rapido: str + + +# --------------------------------------------------------------- +# ENDPOINTS DE INFORMACIÓN +# (agregar en la sección de endpoints de main.py) +# --------------------------------------------------------------- + +@app.get("/api/info", tags=["Información"]) +def listar_articulos(): + """Lista todos los artículos de información relevante (solo metadatos).""" + return { + "articulos": [ + { + "id": a["id"], + "categoria": a["categoria"], + "emoji": a.get("emoji", "♻️"), + "titulo": a["titulo"], + "resumen": a["resumen"], + } + for a in INFO_ARTICULOS + ] + } + + +@app.get("/api/info/{articulo_id}", tags=["Información"]) +def obtener_articulo(articulo_id: str): + """Devuelve el contenido completo de un artículo por su ID.""" + articulo = next((a for a in INFO_ARTICULOS if a["id"] == articulo_id), None) + if not articulo: + raise HTTPException(status_code=404, detail="Artículo no encontrado.") + return articulo +# --------------------------------------------------------------- +# ================================================================ +# PEGA ESTE BLOQUE EN main.py JUSTO ANTES DE: +# if __name__ == "__main__": +# +# Y agrega al inicio de main.py: +# from analytics import generar_reporte_completo +# import time as _time +# ================================================================ + +# Cache simple en memoria para no recalcular en cada request +_reporte_cache: dict = {"data": None, "ts": 0} +_CACHE_TTL = 600 # 10 minutos + + +@app.get("/api/analytics/reporte", tags=["Analytics"]) +def obtener_reporte(): + """ + Reporte completo: días pico, zonas críticas, predicción 7 días + y recomendaciones de logística. + + Cacheado 10 minutos — pesado de calcular, ligero de servir. + """ + import time as _time + ahora = _time.time() + if _reporte_cache["data"] is None or (ahora - _reporte_cache["ts"]) > _CACHE_TTL: + _reporte_cache["data"] = generar_reporte_completo(dias_historico=90) + _reporte_cache["ts"] = ahora + return _reporte_cache["data"] + + +@app.get("/api/analytics/prediccion/{colonia}", tags=["Analytics"]) +def prediccion_colonia(colonia: str): + """ + Predicción de los próximos 7 días solo para una colonia específica. + Más rápido que el reporte completo para consultas frecuentes. + """ + colonias_validas = [ + "Zona Centro", "Las Arboledas", "Trojes", + "San Juanico", "Los Olivos", "Rancho Seco", "Las Insurgentes", + ] + if colonia not in colonias_validas: + raise HTTPException( + status_code=400, + detail=f"Colonia no válida. Opciones: {colonias_validas}" + ) + + reporte = generar_reporte_completo(90) + pred = next( + (p for p in reporte["prediccion_proxima_semana"] if p["colonia"] == colonia), + None + ) + if not pred: + raise HTTPException(status_code=404, detail="Sin datos para esta colonia.") + return pred + + +@app.get("/api/analytics/resumen-ejecutivo", tags=["Analytics"]) +def resumen_ejecutivo(): + """ + Vista rápida: solo el resumen + recomendaciones. + Ideal para mostrar en el dashboard sin cargar todos los datos. + """ + reporte = generar_reporte_completo(90) + return { + "resumen": reporte["resumen"], + "recomendaciones": reporte["recomendaciones"], + "dia_pico": reporte["dias_semana"][0] if reporte["dias_semana"] else None, + "zona_mas_critica": reporte["zonas_criticas"][0] if reporte["zonas_criticas"] else None, + } + +# ================================================================ +# PEGA ESTE BLOQUE EN main.py JUSTO ANTES DE: +# if __name__ == "__main__": +# +# TAMBIÉN agrega estos imports al inicio de main.py: +# from analytics_real import generar_reporte_real +# ================================================================ + +# --------------------------------------------------------------- +# SCHEMAS PARA REPORTES DE USUARIO +# --------------------------------------------------------------- + +class ReporteUsuarioRequest(BaseModel): + colonia: str + tipo: str # VOLUMEN_ALTO | CAMION_NO_PASO | BASURA_FUERA_HORARIO | OTRO + descripcion: Optional[str] = None + +class ReporteUsuarioResponse(BaseModel): + reporte_id: int + fecha: str + colonia: str + tipo: str + descripcion: Optional[str] + estado: str + mensaje: str + +class ReporteListResponse(BaseModel): + reporte_id: int + fecha: str + hora: str + colonia: str + tipo: str + descripcion: Optional[str] + estado: str + +# --------------------------------------------------------------- +# HELPER: Estimar volumen por posición de ruta +# Cada posición representa un tramo; el volumen aumenta gradualmente. +# --------------------------------------------------------------- +def _estimar_volumen_kg(position_id: int, total_positions: int = 8) -> int: + """Estima los kg recolectados hasta esta posición (lineal).""" + base_por_ruta = 1200 # kg promedio total por ruta + return int(base_por_ruta * (position_id / total_positions)) + + +def _registrar_evento_ruta( + db: Session, + route_id: str, + position_id: int, + evento: str, + colonia: str, +) -> None: + """Inserta un registro de recolección en la DB.""" + ahora = datetime.now(timezone.utc) + ruta = ROUTAS_POR_ID.get(route_id, {}) + total_pos = len(ruta.get("positions", [])) or 8 + estado = ROUTE_STATE.get(route_id, {}) + inicio_ts = estado.get("inicio_timestamp") + tiempo_min = None + if inicio_ts: + tiempo_min = int((ahora - inicio_ts).total_seconds() / 60) + + db.add(RegistroRecoleccion( + fecha=ahora.strftime("%Y-%m-%d"), + hora=ahora.strftime("%H:%M:%S"), + ruta_id=route_id, + colonia=colonia, + position_id=position_id, + evento=evento, + volumen_kg=_estimar_volumen_kg(position_id, total_pos), + tiempo_min=tiempo_min, + )) + db.commit() + + +# --------------------------------------------------------------- +# ENDPOINTS — REPORTES DE USUARIO +# --------------------------------------------------------------- + +@app.post("/api/reportes", response_model=ReporteUsuarioResponse, tags=["Reportes"]) +def crear_reporte( + usuario_id: int, + payload: ReporteUsuarioRequest, + db: Session = Depends(get_db), +): + """ + El ciudadano envía un reporte manual: + - Camión no pasó + - Volumen inusualmente alto + - Basura tirada fuera de horario + - Otro (con descripción libre) + """ + tipos_validos = ["VOLUMEN_ALTO", "CAMION_NO_PASO", "BASURA_FUERA_HORARIO", "OTRO"] + if payload.tipo not in tipos_validos: + raise HTTPException( + status_code=400, + detail=f"Tipo inválido. Opciones: {tipos_validos}" + ) + + usuario = db.query(Usuario).filter(Usuario.id == usuario_id).first() + if not usuario: + raise HTTPException(status_code=404, detail="Usuario no encontrado.") + + route_id = COLONIAS_A_RUTAS.get(payload.colonia, "RUTA-01") + ahora = datetime.now(timezone.utc) + + reporte = ReporteUsuario( + usuario_id=usuario_id, + fecha=ahora.strftime("%Y-%m-%d"), + hora=ahora.strftime("%H:%M:%S"), + colonia=payload.colonia, + ruta_id=route_id, + tipo=payload.tipo, + descripcion=payload.descripcion, + estado="PENDIENTE", + ) + db.add(reporte) + db.commit() + db.refresh(reporte) + + logger.info(f"📋 Reporte #{reporte.id} creado por usuario {usuario_id}: {payload.tipo}") + + return ReporteUsuarioResponse( + reporte_id=reporte.id, + fecha=reporte.fecha, + colonia=reporte.colonia, + tipo=reporte.tipo, + descripcion=reporte.descripcion, + estado=reporte.estado, + mensaje="Reporte enviado correctamente. Gracias por contribuir.", + ) + + +@app.get("/api/reportes/usuario/{usuario_id}", response_model=List[ReporteListResponse], tags=["Reportes"]) +def listar_reportes_usuario(usuario_id: int, db: Session = Depends(get_db)): + """Historial de reportes enviados por un usuario específico.""" + reportes = ( + db.query(ReporteUsuario) + .filter(ReporteUsuario.usuario_id == usuario_id) + .order_by(ReporteUsuario.id.desc()) + .all() + ) + return [ + ReporteListResponse( + reporte_id=r.id, + fecha=r.fecha, + hora=r.hora, + colonia=r.colonia, + tipo=r.tipo, + descripcion=r.descripcion, + estado=r.estado, + ) + for r in reportes + ] + + +@app.get("/api/reportes", tags=["Reportes"]) +def listar_todos_reportes( + estado: Optional[str] = None, + colonia: Optional[str] = None, + db: Session = Depends(get_db), +): + """Vista de operador: todos los reportes, filtrables por estado o colonia.""" + query = db.query(ReporteUsuario) + if estado: + query = query.filter(ReporteUsuario.estado == estado) + if colonia: + query = query.filter(ReporteUsuario.colonia == colonia) + reportes = query.order_by(ReporteUsuario.id.desc()).limit(100).all() + return { + "total": len(reportes), + "reportes": [ + { + "reporte_id": r.id, + "usuario_id": r.usuario_id, + "fecha": r.fecha, + "hora": r.hora, + "colonia": r.colonia, + "ruta_id": r.ruta_id, + "tipo": r.tipo, + "descripcion": r.descripcion, + "estado": r.estado, + } + for r in reportes + ], + } + + +@app.put("/api/reportes/{reporte_id}/atender", tags=["Reportes"]) +def atender_reporte(reporte_id: int, db: Session = Depends(get_db)): + """Marca un reporte como atendido (uso del operador).""" + reporte = db.query(ReporteUsuario).filter(ReporteUsuario.id == reporte_id).first() + if not reporte: + raise HTTPException(status_code=404, detail="Reporte no encontrado.") + reporte.estado = "ATENDIDO" + db.commit() + return {"mensaje": f"Reporte #{reporte_id} marcado como atendido."} + + +# --------------------------------------------------------------- +# ENDPOINT: Analytics con datos reales de la DB +# --------------------------------------------------------------- + +@app.get("/api/analytics/reporte-real", tags=["Analytics"]) +def reporte_real(db: Session = Depends(get_db)): + return generar_reporte_real(db) + + +@app.get("/api/analytics/registros", tags=["Analytics"]) +def listar_registros( + ruta_id: Optional[str] = None, + colonia: Optional[str] = None, + fecha_inicio: Optional[str] = None, + db: Session = Depends(get_db), +): + """Lista los registros de recolección reales guardados en la DB.""" + query = db.query(RegistroRecoleccion) + if ruta_id: + query = query.filter(RegistroRecoleccion.ruta_id == ruta_id) + if colonia: + query = query.filter(RegistroRecoleccion.colonia == colonia) + if fecha_inicio: + query = query.filter(RegistroRecoleccion.fecha >= fecha_inicio) + registros = query.order_by(RegistroRecoleccion.id.desc()).limit(500).all() + return { + "total": len(registros), + "registros": [ + { + "id": r.id, + "fecha": r.fecha, + "hora": r.hora, + "ruta_id": r.ruta_id, + "colonia": r.colonia, + "position_id": r.position_id, + "evento": r.evento, + "volumen_kg": r.volumen_kg, + "tiempo_min": r.tiempo_min, + } + for r in registros + ], + } + +if __name__ == "__main__": + import uvicorn + uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) diff --git a/HackOnLinces_app/hackathon.db b/HackOnLinces_app/hackathon.db new file mode 100644 index 0000000000000000000000000000000000000000..9df51770876281d4faa862acc9596e44b5769b7f GIT binary patch literal 24576 zcmeI%!D`zu7{GD6P14nnrH3(}GL%~k+hFtsmS$=emN~1Nl=T$c*aflJrilZ+jk3dF zFFE!mv%Cs7q1yvwe}QbtpDbIyPZ#6!vysYUah)xbFc-(xk!9P~TOlmVsui_SR8w?c zS5s`$_{X_sz5em6*?Ml(o8PTg+IrFa`Se%Oq#%F*0tg_000IagfB*vjtH4FQ;lAqF ziAtmRR^85`EKzf{P}ypxqN>yA2eLPjV(7nL4R8jG$i5$p4_~VK2r=!tJ;T*14>u{;E;?+!8U!7-*ELEYn z=mq`vz0Et8**cH)_{V~ug5k$ra49~>%kri5WK(~5HkG1VJ<8LUPsf2A4tzcIVMqk> zR0h)T%X4vm$Euvl@s4W^chIq|Qg(H-D86=Q;X2RC{zK#*z`2OG3^s+7l5I_I{ z1Q0*~0R#|0009I_0sjB95fDHC0R#|0009ILKmY**5ZHYI{{QcOj#(iB2q1s}0tg_0 i00IagfB*t~|FZ`WKmY**5I_I{1Q0*~0R#}(eSzPZ?9OWd literal 0 HcmV?d00001

{kUR?)3}Eq`3P8e3aOGg~Jm}_@jgQ?BSM8)@ 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≀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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/assets/images/recycle.jpg b/HackOnLinces_app/aplicacion_hack/assets/images/recycle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e09256d5c2fca40c99664943ca5e54b1e96e5d40 GIT binary patch literal 10197 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/assets/images/reloj.jpg b/HackOnLinces_app/aplicacion_hack/assets/images/reloj.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0ff5df8e860ca31406bd8c735d81ea69a0f21e5e GIT binary patch literal 25300 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 jn*W + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Flutter/Debug.xcconfig b/HackOnLinces_app/aplicacion_hack/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/HackOnLinces_app/aplicacion_hack/ios/Flutter/Release.xcconfig b/HackOnLinces_app/aplicacion_hack/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.pbxproj b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f31ea00 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,644 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c3fedb2 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/contents.xcworkspacedata b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/AppDelegate.swift b/HackOnLinces_app/aplicacion_hack/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..c30b367 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/AppDelegate.swift @@ -0,0 +1,16 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } +} diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/LaunchScreen.storyboard b/HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/Main.storyboard b/HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Info.plist b/HackOnLinces_app/aplicacion_hack/ios/Runner/Info.plist new file mode 100644 index 0000000..34758c5 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Info.plist @@ -0,0 +1,70 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Aplicacion Hack + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + aplicacion_hack + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/Runner-Bridging-Header.h b/HackOnLinces_app/aplicacion_hack/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/HackOnLinces_app/aplicacion_hack/ios/Runner/SceneDelegate.swift b/HackOnLinces_app/aplicacion_hack/ios/Runner/SceneDelegate.swift new file mode 100644 index 0000000..b9ce8ea --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/Runner/SceneDelegate.swift @@ -0,0 +1,6 @@ +import Flutter +import UIKit + +class SceneDelegate: FlutterSceneDelegate { + +} diff --git a/HackOnLinces_app/aplicacion_hack/ios/RunnerTests/RunnerTests.swift b/HackOnLinces_app/aplicacion_hack/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/firebase_options.dart b/HackOnLinces_app/aplicacion_hack/lib/firebase_options.dart new file mode 100644 index 0000000..908492c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/firebase_options.dart @@ -0,0 +1,88 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return windows; + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyDfpYR_--oRlGdTjLnAZY6z3RLh3LLz5gk', + appId: '1:338042609701:web:5f0a245f364e7604371fcc', + messagingSenderId: '338042609701', + projectId: 'hackon-58b23', + authDomain: 'hackon-58b23.firebaseapp.com', + storageBucket: 'hackon-58b23.firebasestorage.app', + measurementId: 'G-TBT47P2HX5', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyBuT70SbADLeg92ll8keCySI8I4eYyqyLw', + appId: '1:338042609701:android:0f0f92d7f895794f371fcc', + messagingSenderId: '338042609701', + projectId: 'hackon-58b23', + storageBucket: 'hackon-58b23.firebasestorage.app', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyAB7czRjHfrkHEjiDOvPLiUz9zS1UGiZbw', + appId: '1:338042609701:ios:d1ac418d368d1df5371fcc', + messagingSenderId: '338042609701', + projectId: 'hackon-58b23', + storageBucket: 'hackon-58b23.firebasestorage.app', + iosBundleId: 'com.example.aplicacionHack', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'AIzaSyAB7czRjHfrkHEjiDOvPLiUz9zS1UGiZbw', + appId: '1:338042609701:ios:d1ac418d368d1df5371fcc', + messagingSenderId: '338042609701', + projectId: 'hackon-58b23', + storageBucket: 'hackon-58b23.firebasestorage.app', + iosBundleId: 'com.example.aplicacionHack', + ); + + static const FirebaseOptions windows = FirebaseOptions( + apiKey: 'AIzaSyDfpYR_--oRlGdTjLnAZY6z3RLh3LLz5gk', + appId: '1:338042609701:web:b01c28c419fddd25371fcc', + messagingSenderId: '338042609701', + projectId: 'hackon-58b23', + authDomain: 'hackon-58b23.firebaseapp.com', + storageBucket: 'hackon-58b23.firebasestorage.app', + measurementId: 'G-DYGG6KBJ6C', + ); +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/main.dart b/HackOnLinces_app/aplicacion_hack/lib/main.dart new file mode 100644 index 0000000..05b76a8 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/main.dart @@ -0,0 +1,114 @@ +// ================================================================ +// main.dart — EcoTrack +// Sistema de Notificacion Privada de Recoleccion de Residuos +// ================================================================ + +import 'package:flutter/material.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'screens/login_screen.dart'; +import 'screens/home_screen.dart'; +import 'screens/route_list_screen.dart'; +import 'screens/info_screen.dart'; +import 'screens/analytics_screen.dart'; +import 'screens/reporte_screen.dart'; +import 'screens/mapa_rutas_screen.dart'; +import 'firebase_options.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + +const AndroidNotificationChannel _canal = AndroidNotificationChannel( + 'ecotrack_canal', + 'EcoTrack Notificaciones', + description: 'Notificaciones del camión de basura', + importance: Importance.high, +); + +final FlutterLocalNotificationsPlugin _localNotifications = + FlutterLocalNotificationsPlugin(); + +@pragma('vm:entry-point') +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); +} + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + + // DESPUÉS + final androidPlugin = + _localNotifications.resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>(); + await androidPlugin?.createNotificationChannel(_canal); + + await _localNotifications.initialize( + const InitializationSettings( + android: AndroidInitializationSettings('@mipmap/ic_launcher'), + ), + ); + + // ← EL QUE TE FALTABA + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + final notification = message.notification; + if (notification == null) return; + _localNotifications.show( + notification.hashCode, + notification.title, + notification.body, + NotificationDetails( + android: AndroidNotificationDetails( + _canal.id, + _canal.name, + channelDescription: _canal.description, + importance: Importance.high, + priority: Priority.high, + icon: '@mipmap/ic_launcher', + ), + ), + ); + }); + + await FirebaseMessaging.instance.requestPermission( + alert: true, + badge: true, + sound: true, + ); + + runApp(const EcoTrackApp()); +} + +class EcoTrackApp extends StatelessWidget { + const EcoTrackApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'EcoTrack', + debugShowCheckedModeBanner: false, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF2E7D32), + brightness: Brightness.light, + ), + useMaterial3: true, + fontFamily: 'Roboto', + appBarTheme: const AppBarTheme( + centerTitle: false, + elevation: 0, + ), + ), + initialRoute: '/', + routes: { + '/': (context) => const LoginScreen(), + '/home': (context) => const HomeScreen(), + '/routes': (context) => const RouteListScreen(), + '/info': (context) => const InfoScreen(), + '/analytics': (context) => const AnalyticsScreen(), + '/reporte': (context) => const ReporteScreen(), + '/mapa': (context) => const MapaRutasScreen(), + }, + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/analytics_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/analytics_screen.dart new file mode 100644 index 0000000..3e31487 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/analytics_screen.dart @@ -0,0 +1,1092 @@ +// ================================================================ +// lib/screens/analytics_screen.dart +// Dashboard de Reportes y Análisis Predictivo +// ================================================================ +// +// DEPENDENCIAS (agregar en pubspec.yaml): +// fl_chart: ^0.69.0 +// +// NAVEGAR DESDE main.dart: +// '/analytics': (context) => const AnalyticsScreen() +// +// SECCIONES: +// 1. Resumen ejecutivo (KPIs principales) +// 2. Días con más residuos (BarChart) +// 3. Zonas críticas (mapa de calor horizontal) +// 4. Predicción próxima semana (LineChart) +// 5. Recomendaciones de logística +// ================================================================ + +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:http/http.dart' as http; + +// ---------------------------------------------------------------- +// MODELOS +// ---------------------------------------------------------------- + +class ResumenReporte { + final double totalKg; + final double promedioKgDia; + final int coloniasAnalizadas; + final int zonasCriticas; + final String diaPico; + final String coloniaMayorVolumen; + + ResumenReporte({ + required this.totalKg, + required this.promedioKgDia, + required this.coloniasAnalizadas, + required this.zonasCriticas, + required this.diaPico, + required this.coloniaMayorVolumen, + }); + + factory ResumenReporte.fromJson(Map j) => ResumenReporte( + totalKg: (j['total_kg_recolectados'] as num).toDouble(), + promedioKgDia: (j['promedio_kg_dia'] as num).toDouble(), + coloniasAnalizadas: j['colonias_analizadas'], + zonasCriticas: j['zonas_criticas'], + diaPico: j['dia_pico'], + coloniaMayorVolumen: j['colonia_mayor_volumen'], + ); +} + +class DiaSemana { + final String dia; + final double promedioKg; + final double promedioIncidencias; + + DiaSemana({ + required this.dia, + required this.promedioKg, + required this.promedioIncidencias, + }); + + factory DiaSemana.fromJson(Map j) => DiaSemana( + dia: j['dia'], + promedioKg: (j['promedio_kg'] as num).toDouble(), + promedioIncidencias: (j['promedio_incidencias'] as num).toDouble(), + ); +} + +class ZonaCritica { + final String colonia; + final String rutaId; + final double promedioKgDia; + final double promedioIncidencias; + final double indiceCriticidad; + final String nivelCriticidad; + + ZonaCritica({ + required this.colonia, + required this.rutaId, + required this.promedioKgDia, + required this.promedioIncidencias, + required this.indiceCriticidad, + required this.nivelCriticidad, + }); + + factory ZonaCritica.fromJson(Map j) => ZonaCritica( + colonia: j['colonia'], + rutaId: j['ruta_id'], + promedioKgDia: (j['promedio_kg_dia'] as num).toDouble(), + promedioIncidencias: (j['promedio_incidencias_dia'] as num).toDouble(), + indiceCriticidad: (j['indice_criticidad'] as num).toDouble(), + nivelCriticidad: j['nivel_criticidad'], + ); +} + +class DiaPred { + final String fecha; + final String dia; + final double volumenKg; + final String confianza; + + DiaPred({ + required this.fecha, + required this.dia, + required this.volumenKg, + required this.confianza, + }); + + factory DiaPred.fromJson(Map j) => DiaPred( + fecha: j['fecha'], + dia: j['dia'], + volumenKg: (j['volumen_predicho_kg'] as num).toDouble(), + confianza: j['confianza'], + ); +} + +class PrediccionColonia { + final String colonia; + final String tendencia; + final double pendienteDiaria; + final List dias; + final double totalPredichoKg; + + PrediccionColonia({ + required this.colonia, + required this.tendencia, + required this.pendienteDiaria, + required this.dias, + required this.totalPredichoKg, + }); + + factory PrediccionColonia.fromJson(Map j) => PrediccionColonia( + colonia: j['colonia'], + tendencia: j['tendencia'], + pendienteDiaria: (j['pendiente_diaria_kg'] as num).toDouble(), + dias: (j['prediccion_7dias'] as List).map((d) => DiaPred.fromJson(d)).toList(), + totalPredichoKg: (j['volumen_predicho_total_kg'] as num).toDouble(), + ); +} + +class Recomendacion { + final String tipo; + final String prioridad; + final String emoji; + final String titulo; + final String descripcion; + final String? rutaAfectada; + + Recomendacion({ + required this.tipo, + required this.prioridad, + required this.emoji, + required this.titulo, + required this.descripcion, + this.rutaAfectada, + }); + + factory Recomendacion.fromJson(Map j) => Recomendacion( + tipo: j['tipo'], + prioridad: j['prioridad'], + emoji: j['emoji'], + titulo: j['titulo'], + descripcion: j['descripcion'], + rutaAfectada: j['ruta_afectada'], + ); +} + +// ================================================================ +// PANTALLA PRINCIPAL +// ================================================================ +class AnalyticsScreen extends StatefulWidget { + const AnalyticsScreen({super.key}); + + @override + State createState() => _AnalyticsScreenState(); +} + +class _AnalyticsScreenState extends State + with SingleTickerProviderStateMixin { + static const _baseUrl = 'http://192.168.198.55:8000'; + + bool _cargando = true; + String? _error; + late TabController _tabController; + + ResumenReporte? _resumen; + List _diasSemana = []; + List _zonas = []; + List _predicciones = []; + List _recomendaciones = []; + + // Colonia seleccionada para el detalle de predicción + int _coloniaIdx = 0; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 4, vsync: this); + _cargarReporte(); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + Future _cargarReporte() async { + setState(() { _cargando = true; _error = null; }); + try { + final resp = await http + .get(Uri.parse('$_baseUrl/api/analytics/reporte-real')) + .timeout(const Duration(seconds: 20)); + + if (resp.statusCode != 200) throw Exception('Error ${resp.statusCode}'); + final data = json.decode(resp.body); + + setState(() { + _resumen = ResumenReporte.fromJson(data['resumen']); + _diasSemana = (data['dias_semana'] as List) + .map((d) => DiaSemana.fromJson(d)) + .toList(); + _zonas = (data['zonas_criticas'] as List) + .map((z) => ZonaCritica.fromJson(z)) + .toList(); + _predicciones = (data['prediccion_proxima_semana'] as List) + .map((p) => PrediccionColonia.fromJson(p)) + .toList(); + _recomendaciones = (data['recomendaciones'] as List) + .map((r) => Recomendacion.fromJson(r)) + .toList(); + _cargando = false; + }); + } catch (e) { + setState(() { _error = e.toString(); _cargando = false; }); + } + } + + // ================================================================ + // BUILD + // ================================================================ + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF3F6F3), + appBar: AppBar( + title: const Text('Análisis y Reportes'), + backgroundColor: const Color(0xFF1B5E20), + foregroundColor: Colors.white, + actions: [ + IconButton(icon: const Icon(Icons.refresh), onPressed: _cargarReporte), + ], + bottom: _cargando || _error != null + ? null + : TabBar( + controller: _tabController, + indicatorColor: Colors.white, + labelColor: Colors.white, + unselectedLabelColor: Colors.white60, + isScrollable: true, + tabs: const [ + Tab(text: 'Resumen'), + Tab(text: 'Días pico'), + Tab(text: 'Zonas'), + Tab(text: 'Predicción'), + ], + ), + ), + body: _cargando + ? _buildCargando() + : _error != null + ? _buildError() + : TabBarView( + controller: _tabController, + children: [ + _buildResumen(), + _buildDiasPico(), + _buildZonas(), + _buildPrediccion(), + ], + ), + ); + } + + Widget _buildCargando() => const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(color: Color(0xFF2E7D32)), + SizedBox(height: 16), + Text('Calculando análisis predictivo...', style: TextStyle(color: Colors.grey)), + SizedBox(height: 8), + Text('Procesando 90 días de datos', style: TextStyle(color: Colors.grey, fontSize: 12)), + ], + ), + ); + + Widget _buildError() => Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.bar_chart, size: 72, color: Colors.grey), + const SizedBox(height: 16), + const Text('No se pudo cargar el reporte', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + Text('Verifica que el backend esté corriendo', + style: TextStyle(color: Colors.grey.shade600)), + const SizedBox(height: 24), + ElevatedButton.icon( + onPressed: _cargarReporte, + icon: const Icon(Icons.refresh), + label: const Text('Reintentar'), + style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF2E7D32)), + ), + ], + ), + ), + ); + + // ================================================================ + // TAB 1: RESUMEN EJECUTIVO + // ================================================================ + Widget _buildResumen() { + if (_resumen == null) return const SizedBox(); + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // KPIs en grid 2x2 + GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisSpacing: 12, + mainAxisSpacing: 12, + childAspectRatio: 1.4, + children: [ + _KpiCard( + emoji: '🗑️', + valor: '${(_resumen!.totalKg / 1000).toStringAsFixed(1)} t', + etiqueta: 'Total recolectado\n(90 días)', + color: const Color(0xFF1565C0), + ), + _KpiCard( + emoji: '📅', + valor: '${_resumen!.promedioKgDia.toStringAsFixed(0)} kg', + etiqueta: 'Promedio\npor día', + color: const Color(0xFF2E7D32), + ), + _KpiCard( + emoji: '⚠️', + valor: '${_resumen!.zonasCriticas}', + etiqueta: 'Zonas\ncríticas', + color: const Color(0xFFE65100), + ), + _KpiCard( + emoji: '📆', + valor: _resumen!.diaPico, + etiqueta: 'Día con más\nresiduos', + color: const Color(0xFF6A1B9A), + ), + ], + ), + + const SizedBox(height: 20), + + // Datos destacados + _SeccionTitulo('Hallazgos principales'), + _InfoCard( + emoji: '🏆', + titulo: 'Colonia con mayor volumen', + subtitulo: _resumen!.coloniaMayorVolumen, + color: const Color(0xFFC62828), + ), + const SizedBox(height: 8), + _InfoCard( + emoji: '📈', + titulo: 'Día crítico de la semana', + subtitulo: '${_resumen!.diaPico} — mayor acumulación tras el fin de semana', + color: const Color(0xFF1565C0), + ), + + const SizedBox(height: 20), + _SeccionTitulo('Recomendaciones'), + ..._recomendaciones.map((r) => _TarjetaRecomendacion(rec: r)), + ], + ); + } + + // ================================================================ + // TAB 2: DÍAS PICO — BarChart + // ================================================================ + Widget _buildDiasPico() { + // Ordenar por día de la semana (lun-dom) + final diasOrdenados = List.from(_diasSemana); + const orden = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']; + diasOrdenados.sort((a, b) => orden.indexOf(a.dia).compareTo(orden.indexOf(b.dia))); + + final maxKg = diasOrdenados.map((d) => d.promedioKg).reduce((a, b) => a > b ? a : b); + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + _SeccionTitulo('Promedio de residuos por día'), + const Text( + 'Basado en 90 días de histórico. El lunes concentra mayor volumen por acumulación del fin de semana.', + style: TextStyle(color: Colors.grey, fontSize: 13), + ), + const SizedBox(height: 20), + + // Gráfica de barras + Container( + height: 260, + padding: const EdgeInsets.fromLTRB(8, 16, 16, 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.06), blurRadius: 8)], + ), + child: BarChart( + BarChartData( + maxY: maxKg * 1.15, + barTouchData: BarTouchData( + touchTooltipData: BarTouchTooltipData( + getTooltipItem: (group, groupIndex, rod, rodIndex) { + final d = diasOrdenados[group.x]; + return BarTooltipItem( + '${d.dia}\n${rod.toY.toStringAsFixed(0)} kg', + const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12), + ); + }, + ), + ), + titlesData: FlTitlesData( + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (val, meta) { + final abrevs = ['L', 'Ma', 'Mi', 'J', 'V', 'S', 'D']; + final idx = val.toInt(); + if (idx < 0 || idx >= abrevs.length) return const SizedBox(); + return Text(abrevs[idx], + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600)); + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 52, + getTitlesWidget: (val, meta) => Text( + '${(val / 1000).toStringAsFixed(1)}t', + style: const TextStyle(fontSize: 10, color: Colors.grey), + ), + ), + ), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + ), + gridData: FlGridData( + drawVerticalLine: false, + horizontalInterval: maxKg / 4, + getDrawingHorizontalLine: (v) => FlLine(color: Colors.grey.shade200, strokeWidth: 1), + ), + borderData: FlBorderData(show: false), + barGroups: List.generate(diasOrdenados.length, (i) { + final d = diasOrdenados[i]; + final esPico = d.dia == _resumen?.diaPico; + return BarChartGroupData( + x: i, + barRods: [ + BarChartRodData( + toY: d.promedioKg, + color: esPico ? const Color(0xFFE65100) : const Color(0xFF2E7D32), + width: 28, + borderRadius: const BorderRadius.vertical(top: Radius.circular(6)), + ), + ], + ); + }), + ), + ), + ), + + const SizedBox(height: 20), + _SeccionTitulo('Incidencias por día'), + const Text( + 'Reportes de basura tirada fuera de horario.', + style: TextStyle(color: Colors.grey, fontSize: 13), + ), + const SizedBox(height: 12), + + // Tabla de incidencias + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6)], + ), + child: Column( + children: diasOrdenados.asMap().entries.map((entry) { + final i = entry.key; + final d = entry.value; + final maxInc = diasOrdenados + .map((x) => x.promedioIncidencias) + .reduce((a, b) => a > b ? a : b); + final pct = maxInc > 0 ? d.promedioIncidencias / maxInc : 0.0; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration( + border: i < diasOrdenados.length - 1 + ? Border(bottom: BorderSide(color: Colors.grey.shade100)) + : null, + ), + child: Row( + children: [ + SizedBox(width: 60, child: Text(d.dia.substring(0, 3), + style: const TextStyle(fontWeight: FontWeight.w600))), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: pct, + backgroundColor: Colors.grey.shade100, + valueColor: AlwaysStoppedAnimation( + pct > 0.7 ? const Color(0xFFC62828) : const Color(0xFF2E7D32)), + minHeight: 10, + ), + ), + ), + const SizedBox(width: 12), + Text('${d.promedioIncidencias.toStringAsFixed(1)}/día', + style: const TextStyle(fontSize: 12, color: Colors.grey)), + ], + ), + ); + }).toList(), + ), + ), + ], + ); + } + + // ================================================================ + // TAB 3: ZONAS CRÍTICAS + // ================================================================ + Widget _buildZonas() { + return ListView( + padding: const EdgeInsets.all(16), + children: [ + _SeccionTitulo('Mapa de calor por colonia'), + const Text( + 'Índice de criticidad basado en volumen e incidencias (0–100).', + style: TextStyle(color: Colors.grey, fontSize: 13), + ), + const SizedBox(height: 16), + ..._zonas.map((z) => _TarjetaZona(zona: z)), + const SizedBox(height: 16), + _SeccionTitulo('Tiempo promedio de recolección'), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6)], + ), + child: Column( + children: _zonas.asMap().entries.map((entry) { + final i = entry.key; + final z = entry.value; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + border: i < _zonas.length - 1 + ? Border(bottom: BorderSide(color: Colors.grey.shade100)) + : null, + ), + child: Row( + children: [ + Expanded(child: Text(z.colonia, + style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 13))), + Text('${z.promedioKgDia.toStringAsFixed(0)} kg/día', + style: TextStyle(color: Colors.grey.shade600, fontSize: 12)), + ], + ), + ); + }).toList(), + ), + ), + ], + ); + } + + // ================================================================ + // TAB 4: PREDICCIÓN — LineChart + // ================================================================ + Widget _buildPrediccion() { + if (_predicciones.isEmpty) return const Center(child: Text('Sin predicciones')); + final pred = _predicciones[_coloniaIdx]; + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + _SeccionTitulo('Predicción próximos 7 días'), + const Text( + 'Regresión lineal sobre 90 días históricos, ajustada por estacionalidad semanal.', + style: TextStyle(color: Colors.grey, fontSize: 13), + ), + const SizedBox(height: 12), + + // Selector de colonia + SizedBox( + height: 36, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: _predicciones.length, + itemBuilder: (context, i) { + final p = _predicciones[i]; + final sel = i == _coloniaIdx; + return GestureDetector( + onTap: () => setState(() => _coloniaIdx = i), + child: AnimatedContainer( + duration: const Duration(milliseconds: 150), + margin: const EdgeInsets.only(right: 8), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), + decoration: BoxDecoration( + color: sel ? const Color(0xFF2E7D32) : Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: sel ? const Color(0xFF2E7D32) : Colors.grey.shade300, + ), + ), + child: Text( + p.colonia.split(' ').first, + style: TextStyle( + color: sel ? Colors.white : Colors.grey.shade700, + fontWeight: FontWeight.w600, + fontSize: 12, + ), + ), + ), + ); + }, + ), + ), + + const SizedBox(height: 16), + + // Badge de tendencia + Row( + children: [ + _TendenciaBadge(tendencia: pred.tendencia, pendiente: pred.pendienteDiaria), + const SizedBox(width: 8), + Text('Total predicho: ${pred.totalPredichoKg.toStringAsFixed(0)} kg', + style: TextStyle(color: Colors.grey.shade600, fontSize: 13)), + ], + ), + + const SizedBox(height: 16), + + // Gráfica de línea + Container( + height: 240, + padding: const EdgeInsets.fromLTRB(8, 16, 16, 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.06), blurRadius: 8)], + ), + child: LineChart( + LineChartData( + minY: 0, + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + getTooltipItems: (spots) => spots.map((s) { + final d = pred.dias[s.x.toInt()]; + return LineTooltipItem( + '${d.dia}\n${s.y.toStringAsFixed(0)} kg', + const TextStyle(color: Colors.white, fontSize: 12), + ); + }).toList(), + ), + ), + titlesData: FlTitlesData( + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (val, meta) { + final idx = val.toInt(); + if (idx < 0 || idx >= pred.dias.length) return const SizedBox(); + return Text( + pred.dias[idx].dia.substring(0, 2), + style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600), + ); + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 52, + getTitlesWidget: (val, meta) => Text( + '${(val / 1000).toStringAsFixed(1)}t', + style: const TextStyle(fontSize: 10, color: Colors.grey), + ), + ), + ), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + ), + gridData: FlGridData( + drawVerticalLine: false, + getDrawingHorizontalLine: (v) => + FlLine(color: Colors.grey.shade100, strokeWidth: 1), + ), + borderData: FlBorderData(show: false), + lineBarsData: [ + LineChartBarData( + spots: pred.dias.asMap().entries.map((e) => + FlSpot(e.key.toDouble(), e.value.volumenKg)).toList(), + isCurved: true, + color: const Color(0xFF2E7D32), + barWidth: 3, + dotData: FlDotData( + getDotPainter: (spot, pct, bar, idx) => FlDotCirclePainter( + radius: 5, + color: Colors.white, + strokeWidth: 2.5, + strokeColor: const Color(0xFF2E7D32), + ), + ), + belowBarData: BarAreaData( + show: true, + color: const Color(0xFF2E7D32).withValues(alpha: 0.1), + ), + ), + ], + ), + ), + ), + + const SizedBox(height: 16), + + // Tabla de predicciones + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6)], + ), + child: Column( + children: pred.dias.asMap().entries.map((entry) { + final i = entry.key; + final d = entry.value; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration( + border: i < pred.dias.length - 1 + ? Border(bottom: BorderSide(color: Colors.grey.shade100)) + : null, + ), + child: Row( + children: [ + SizedBox( + width: 32, + child: Text(d.dia.substring(0, 2), + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13)), + ), + const SizedBox(width: 4), + Text(d.fecha, style: TextStyle(color: Colors.grey.shade500, fontSize: 12)), + const Spacer(), + Text('${d.volumenKg.toStringAsFixed(0)} kg', + style: const TextStyle(fontWeight: FontWeight.w600)), + const SizedBox(width: 8), + _ConfianzaBadge(confianza: d.confianza), + ], + ), + ); + }).toList(), + ), + ), + ], + ); + } +} + +// ================================================================ +// WIDGETS AUXILIARES +// ================================================================ + +class _KpiCard extends StatelessWidget { + final String emoji; + final String valor; + final String etiqueta; + final Color color; + + const _KpiCard({ + required this.emoji, + required this.valor, + required this.etiqueta, + required this.color, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.06), blurRadius: 8)], + border: Border(left: BorderSide(color: color, width: 4)), + ), + child: ClipRect( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(emoji, style: const TextStyle(fontSize: 20)), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(valor, + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold, color: color)), + Text(etiqueta, + style: TextStyle(fontSize: 10, color: Colors.grey.shade600, height: 1.2)), + ], + ), + ], + ), + ), + ); + } +} + +class _InfoCard extends StatelessWidget { + final String emoji; + final String titulo; + final String subtitulo; + final Color color; + + const _InfoCard({ + required this.emoji, + required this.titulo, + required this.subtitulo, + required this.color, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(14), + margin: const EdgeInsets.only(bottom: 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6)], + ), + child: Row( + children: [ + Text(emoji, style: const TextStyle(fontSize: 28)), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(titulo, + style: TextStyle(fontWeight: FontWeight.bold, color: color, fontSize: 13)), + const SizedBox(height: 2), + Text(subtitulo, style: TextStyle(color: Colors.grey.shade600, fontSize: 13)), + ], + ), + ), + ], + ), + ); + } +} + +class _SeccionTitulo extends StatelessWidget { + final String titulo; + const _SeccionTitulo(this.titulo); + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Text(titulo, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A))), + ); +} + +class _TarjetaZona extends StatelessWidget { + final ZonaCritica zona; + const _TarjetaZona({required this.zona}); + + Color get _color { + switch (zona.nivelCriticidad) { + case 'CRÍTICO': return const Color(0xFFC62828); + case 'ALTO': return const Color(0xFFE65100); + case 'MEDIO': return const Color(0xFFF57F17); + default: return const Color(0xFF2E7D32); + } + } + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6)], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text(zona.colonia, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: _color.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(8), + ), + child: Text(zona.nivelCriticidad, + style: TextStyle(color: _color, fontWeight: FontWeight.bold, fontSize: 11)), + ), + ], + ), + const SizedBox(height: 8), + // Barra de criticidad + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: zona.indiceCriticidad / 100, + backgroundColor: Colors.grey.shade100, + valueColor: AlwaysStoppedAnimation(_color), + minHeight: 8, + ), + ), + const SizedBox(height: 6), + Row( + children: [ + Text('${zona.promedioKgDia.toStringAsFixed(0)} kg/día', + style: TextStyle(color: Colors.grey.shade600, fontSize: 12)), + const Spacer(), + Text('Índice: ${zona.indiceCriticidad.toStringAsFixed(0)}/100', + style: TextStyle(color: _color, fontWeight: FontWeight.w600, fontSize: 12)), + ], + ), + ], + ), + ); + } +} + +class _TarjetaRecomendacion extends StatelessWidget { + final Recomendacion rec; + const _TarjetaRecomendacion({required this.rec}); + + Color get _colorPrioridad { + switch (rec.prioridad) { + case 'ALTA': return const Color(0xFFC62828); + case 'MEDIA': return const Color(0xFFE65100); + default: return const Color(0xFF2E7D32); + } + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border(left: BorderSide(color: _colorPrioridad, width: 4)), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6)], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text(rec.emoji, style: const TextStyle(fontSize: 20)), + const SizedBox(width: 8), + Expanded( + child: Text(rec.titulo, + style: TextStyle( + fontWeight: FontWeight.bold, + color: _colorPrioridad, + fontSize: 14)), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: _colorPrioridad.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Text(rec.prioridad, + style: TextStyle(color: _colorPrioridad, fontSize: 10, fontWeight: FontWeight.bold)), + ), + ], + ), + const SizedBox(height: 8), + Text(rec.descripcion, + style: TextStyle(color: Colors.grey.shade600, fontSize: 13, height: 1.4)), + if (rec.rutaAfectada != null) ...[ + const SizedBox(height: 6), + Text('Ruta: ${rec.rutaAfectada}', + style: TextStyle(color: _colorPrioridad, fontSize: 12, fontWeight: FontWeight.w600)), + ], + ], + ), + ); + } +} + +class _TendenciaBadge extends StatelessWidget { + final String tendencia; + final double pendiente; + const _TendenciaBadge({required this.tendencia, required this.pendiente}); + + @override + Widget build(BuildContext context) { + final color = tendencia == 'CRECIENTE' + ? const Color(0xFFC62828) + : tendencia == 'DECRECIENTE' + ? const Color(0xFF2E7D32) + : const Color(0xFF1565C0); + final icon = tendencia == 'CRECIENTE' + ? Icons.trending_up + : tendencia == 'DECRECIENTE' + ? Icons.trending_down + : Icons.trending_flat; + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: color.withValues(alpha: 0.3)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, color: color, size: 16), + const SizedBox(width: 4), + Text(tendencia, + style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 12)), + ], + ), + ); + } +} + +class _ConfianzaBadge extends StatelessWidget { + final String confianza; + const _ConfianzaBadge({required this.confianza}); + + @override + Widget build(BuildContext context) { + final color = confianza == 'Alta' + ? const Color(0xFF2E7D32) + : confianza == 'Media' + ? const Color(0xFFE65100) + : Colors.grey; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Text(confianza, + style: TextStyle(color: color, fontSize: 10, fontWeight: FontWeight.w600)), + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/home_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/home_screen.dart new file mode 100644 index 0000000..090f95a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/home_screen.dart @@ -0,0 +1,927 @@ +// ================================================================ +// lib/screens/home_screen.dart (v3 — rediseño visual) +// ================================================================ + +import 'dart:async'; +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import '../services/api_service.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State with TickerProviderStateMixin { + int? _usuarioId; + String _nombreUsuario = ''; + ETAInfo? _etaInfo; + List _direcciones = []; + List _colonias = []; + bool _cargando = true; + bool _cargandoColonias = true; + String? _error; + String? _errorColonias; + + Timer? _refreshTimer; + final TextEditingController _nuevaDireccionController = TextEditingController(); + String? _nuevaColoniaSeleccionada; + + late AnimationController _pulseController; + late AnimationController _fadeController; + late Animation _pulseAnimation; + late Animation _fadeAnimation; + + final ApiService _apiService = ApiService(); + + @override + void initState() { + super.initState(); + _pulseController = AnimationController( + vsync: this, + duration: const Duration(seconds: 2), + )..repeat(reverse: true); + _pulseAnimation = Tween(begin: 1.0, end: 1.06).animate( + CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut), + ); + + _fadeController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 600), + ); + _fadeAnimation = CurvedAnimation(parent: _fadeController, curve: Curves.easeOut); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (_usuarioId == null) { + final args = ModalRoute.of(context)?.settings.arguments; + if (args is int) { + _usuarioId = args; + _inicializar(); + } else { + _cargarDesdeStorage(); + } + } + } + + @override + void dispose() { + _pulseController.dispose(); + _fadeController.dispose(); + _refreshTimer?.cancel(); + _nuevaDireccionController.dispose(); + super.dispose(); + } + + Future _inicializar() async { + final prefs = await SharedPreferences.getInstance(); + if (mounted) setState(() => _nombreUsuario = prefs.getString('nombre') ?? ''); + _cargarETA(); + _iniciarAutoRefresh(); + _registrarFCMToken(); + _cargarUsuario(); + _cargarColonias(); + } + + Future _cargarDesdeStorage() async { + final prefs = await SharedPreferences.getInstance(); + final id = prefs.getInt('usuario_id'); + if (id != null) { + _usuarioId = id; + _inicializar(); + } else { + if (mounted) Navigator.pushReplacementNamed(context, '/'); + } + } + + Future _cargarETA() async { + if (!mounted) return; + setState(() { _cargando = true; _error = null; }); + try { + final prefs = await SharedPreferences.getInstance(); + final usuarioId = prefs.getInt('usuario_id') ?? 1; + final etaInfo = await _apiService.obtenerETA(usuarioId); + if (mounted) { + setState(() { _etaInfo = etaInfo; _cargando = false; }); + _fadeController.forward(from: 0); + } + } catch (e) { + if (mounted) { + setState(() { + _etaInfo = ETAInfo( + usuarioId: 1, + colonia: "Centro", + rutaNombre: "Ruta Poniente - Camión #4", + rutaStatus: "EN_RUTA", + gpsOk: true, + etaTexto: "12 minutos aprox.", + etaMinutos: 12, + mensajePreventivo: "⚠️ El camión está a 3 cuadras. ¡Prepara tus bolsas!", + ); + _cargando = false; + }); + _fadeController.forward(from: 0); + } + } + } + + Future _cargarUsuario() async { + if (_usuarioId == null) return; + try { + final usuario = await _apiService.obtenerUsuario(_usuarioId!); + if (mounted) { + setState(() { + _direcciones = usuario.direcciones; + if (usuario.nombre.isNotEmpty) _nombreUsuario = usuario.nombre; + }); + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('nombre', usuario.nombre); + } + } catch (e) { + debugPrint('Error cargando usuario: $e'); + } + } + + Future _cargarColonias() async { + try { + final colonias = await _apiService.obtenerColonias(); + if (mounted) setState(() { _colonias = colonias; _cargandoColonias = false; }); + } catch (e) { + if (mounted) { + setState(() { + _colonias = ['Zona Centro', 'Las Arboledas', 'Trojes', 'San Juanico', 'Los Olivos', 'Rancho Seco', 'Las Insurgentes']; + _cargandoColonias = false; + _errorColonias = 'Sin conexión. Usando lista local.'; + }); + } + } + } + + void _iniciarAutoRefresh() { + _refreshTimer = Timer.periodic(const Duration(seconds: 60), (_) => _cargarETA()); + } + + Future _registrarFCMToken() async { + try { + final messaging = FirebaseMessaging.instance; + final settings = await messaging.requestPermission(alert: true, sound: true, badge: true); + if (settings.authorizationStatus == AuthorizationStatus.authorized) { + final token = await messaging.getToken(); + if (token != null && _usuarioId != null) { + await _apiService.registrarFcmToken(_usuarioId!, token); + } + } + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + if (message.notification != null && mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('🚛 ${message.notification!.body}'), + backgroundColor: Colors.green.shade700, + duration: const Duration(seconds: 5), + behavior: SnackBarBehavior.floating, + )); + _cargarETA(); + } + }); + } catch (e) { + debugPrint('Error FCM: $e'); + } + } + + // ── DIÁLOGOS ──────────────────────────────────────────────────── + + Future _confirmarCerrarSesion() async { + final confirmar = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + title: const Text('Cerrar sesión'), + content: const Text('¿Seguro que quieres cerrar sesión?'), + actions: [ + TextButton(onPressed: () => Navigator.of(ctx).pop(false), child: const Text('Cancelar')), + ElevatedButton( + onPressed: () => Navigator.of(ctx).pop(true), + style: ElevatedButton.styleFrom(backgroundColor: Colors.red.shade700, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))), + child: const Text('Cerrar sesión', style: TextStyle(color: Colors.white)), + ), + ], + ), + ); + if (confirmar == true) { + _refreshTimer?.cancel(); + final prefs = await SharedPreferences.getInstance(); + await prefs.clear(); + if (mounted) Navigator.pushReplacementNamed(context, '/'); + } + } + + Future _mostrarCambioPassword() async { + final actualCtrl = TextEditingController(); + final nuevoCtrl = TextEditingController(); + bool mostrarActual = false; + bool mostrarNuevo = false; + + await showDialog( + context: context, + builder: (ctx) => StatefulBuilder( + builder: (ctx, setS) => AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + title: const Text('Cambiar contraseña'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: actualCtrl, + obscureText: !mostrarActual, + decoration: InputDecoration( + labelText: 'Contraseña actual', + prefixIcon: const Icon(Icons.lock_outline), + suffixIcon: IconButton( + icon: Icon(mostrarActual ? Icons.visibility_off : Icons.visibility), + onPressed: () => setS(() => mostrarActual = !mostrarActual), + ), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), + ), + ), + const SizedBox(height: 12), + TextField( + controller: nuevoCtrl, + obscureText: !mostrarNuevo, + decoration: InputDecoration( + labelText: 'Nueva contraseña', + hintText: 'Mínimo 6 caracteres', + prefixIcon: const Icon(Icons.lock_reset), + suffixIcon: IconButton( + icon: Icon(mostrarNuevo ? Icons.visibility_off : Icons.visibility), + onPressed: () => setS(() => mostrarNuevo = !mostrarNuevo), + ), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), + ), + ), + ], + ), + actions: [ + TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('Cancelar')), + ElevatedButton( + onPressed: () async { + if (_usuarioId == null) return; + if (actualCtrl.text.isEmpty || nuevoCtrl.text.isEmpty) return; + if (nuevoCtrl.text.length < 6) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Mínimo 6 caracteres.'))); + return; + } + try { + await _apiService.actualizarPassword(_usuarioId!, actualCtrl.text, nuevoCtrl.text); + if (mounted) { + Navigator.of(ctx).pop(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('✅ Contraseña actualizada.'), backgroundColor: Colors.green), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString().replaceFirst('Exception: ', '')), backgroundColor: Colors.red.shade700), + ); + } + } + }, + child: const Text('Guardar'), + ), + ], + ), + ), + ); + actualCtrl.dispose(); + nuevoCtrl.dispose(); + } + + Future _agregarDireccion() async { + if (_usuarioId == null) return; + final messenger = ScaffoldMessenger.of(context); + final direccion = _nuevaDireccionController.text.trim(); + if (_nuevaColoniaSeleccionada == null) { + messenger.showSnackBar(const SnackBar(content: Text('Selecciona una colonia.'))); + return; + } + if (direccion.isEmpty) { + messenger.showSnackBar(const SnackBar(content: Text('Ingresa la dirección.'))); + return; + } + try { + await _apiService.agregarDireccion(_usuarioId!, _nuevaColoniaSeleccionada!, direccion); + _nuevaDireccionController.clear(); + _nuevaColoniaSeleccionada = null; + await _cargarUsuario(); + await _cargarETA(); + if (mounted) messenger.showSnackBar(const SnackBar(content: Text('✅ Dirección agregada.'))); + } catch (e) { + if (mounted) messenger.showSnackBar(const SnackBar(content: Text('No se pudo agregar la dirección.'))); + } + } + + Future _mostrarAgregarDireccionDialog() async { + if (_cargandoColonias) await _cargarColonias(); + if (!mounted) return; + _nuevaDireccionController.clear(); + _nuevaColoniaSeleccionada = null; + + await showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + title: const Text('Agregar dirección'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (_errorColonias != null) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text(_errorColonias!, style: TextStyle(color: Colors.orange.shade700, fontSize: 12)), + ), + TextField( + controller: _nuevaDireccionController, + decoration: const InputDecoration(labelText: 'Dirección', hintText: 'Calle, número'), + ), + const SizedBox(height: 16), + DropdownButtonFormField( + initialValue: _nuevaColoniaSeleccionada, + hint: const Text('Selecciona tu colonia'), + items: _colonias.map((c) => DropdownMenuItem(value: c, child: Text(c))).toList(), + onChanged: (v) => setState(() => _nuevaColoniaSeleccionada = v), + ), + ], + ), + ), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Cancelar')), + ElevatedButton( + onPressed: () async { + final nav = Navigator.of(context); + await _agregarDireccion(); + if (mounted) nav.pop(); + }, + child: const Text('Guardar'), + ), + ], + ), + ); + } + + // ── HELPERS ───────────────────────────────────────────────────── + + Color _colorSegunETA(int eta) { + if (eta <= 5) return const Color(0xFFB71C1C); + if (eta <= 15) return const Color(0xFFE65100); + if (eta <= 30) return const Color(0xFF1B5E20); + return const Color(0xFF1A237E); + } + + String _emojiSegunETA(int eta) { + if (eta <= 5) return '🔴'; + if (eta <= 15) return '🟡'; + if (eta <= 30) return '🟢'; + return '🔵'; + } + + String _textoEstado(int eta) { + if (eta <= 5) return '¡Saca tu basura AHORA!'; + if (eta <= 15) return 'Prepárate, viene pronto'; + if (eta <= 30) return 'En camino a tu zona'; + return 'Aún falta un rato'; + } + + // ================================================================ + // BUILD + // ================================================================ + + @override + Widget build(BuildContext context) { + final baseColor = _etaInfo != null + ? _colorSegunETA(_etaInfo!.etaMinutos) + : const Color(0xFF1B5E20); + + return Scaffold( + body: AnimatedContainer( + duration: const Duration(milliseconds: 800), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [baseColor, baseColor.withValues(alpha: 0.85), const Color(0xFF0D1B0F)], + stops: const [0.0, 0.5, 1.0], + ), + ), + child: SafeArea( + child: _cargando + ? _buildCargando() + : _error != null + ? _buildError() + : FadeTransition(opacity: _fadeAnimation, child: _buildContenido()), + ), + ), + ); + } + + Widget _buildCargando() => const Center( + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + CircularProgressIndicator(color: Colors.white, strokeWidth: 2), + SizedBox(height: 16), + Text('Consultando estado del camión...', style: TextStyle(color: Colors.white60, fontSize: 15)), + ]), + ); + + Widget _buildError() => Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + const Icon(Icons.wifi_off_rounded, size: 72, color: Colors.white38), + const SizedBox(height: 16), + Text(_error!, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontSize: 17)), + const SizedBox(height: 24), + ElevatedButton.icon( + onPressed: _cargarETA, + icon: const Icon(Icons.refresh), + label: const Text('Reintentar'), + style: ElevatedButton.styleFrom(backgroundColor: Colors.white, foregroundColor: Colors.red.shade700), + ), + ]), + ), + ); + + Widget _buildContenido() { + if (_etaInfo == null) return const SizedBox.shrink(); + final eta = _etaInfo!; + + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + // ── HEADER ────────────────────────────────────────────── + Padding( + padding: const EdgeInsets.fromLTRB(20, 12, 8, 0), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_nombreUsuario.isNotEmpty) + Text( + 'Hola, ${_nombreUsuario.split(' ').first} 👋', + style: const TextStyle(color: Colors.white60, fontSize: 13), + ), + Row( + children: [ + const Icon(Icons.location_on_rounded, color: Colors.white70, size: 15), + const SizedBox(width: 3), + Text( + eta.colonia, + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 17), + ), + ], + ), + ], + ), + ), + IconButton( + onPressed: () => Navigator.pushNamed(context, '/mapa'), + icon: const Icon(Icons.map_rounded, color: Colors.white60), + tooltip: 'Ver mapa', + ), + IconButton( + onPressed: () => Navigator.pushNamed(context, '/routes'), + icon: const Icon(Icons.local_shipping_rounded, color: Colors.white60), + tooltip: 'Ver rutas', + ), + PopupMenuButton( + icon: const Icon(Icons.more_vert, color: Colors.white60), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + onSelected: (v) { + if (v == 'password') _mostrarCambioPassword(); + if (v == 'logout') _confirmarCerrarSesion(); + }, + itemBuilder: (_) => [ + const PopupMenuItem( + value: 'password', + child: Row(children: [Icon(Icons.lock_reset, size: 18), SizedBox(width: 10), Text('Cambiar contraseña')]), + ), + const PopupMenuDivider(), + const PopupMenuItem( + value: 'logout', + child: Row(children: [Icon(Icons.logout, size: 18, color: Colors.red), SizedBox(width: 10), Text('Cerrar sesión', style: TextStyle(color: Colors.red))]), + ), + ], + ), + ], + ), + ), + + // ── CHIP DE ESTADO GPS ─────────────────────────────────── + if (!eta.gpsOk) + Padding( + padding: const EdgeInsets.fromLTRB(20, 8, 20, 0), + child: _GlassChip( + icon: Icons.gps_off, + label: 'GPS del camión desconectado', + color: Colors.red.shade300, + ), + ), + + // ── CÍRCULO ETA PRINCIPAL ──────────────────────────────── + const SizedBox(height: 24), + ScaleTransition( + scale: _pulseAnimation, + child: Stack( + alignment: Alignment.center, + children: [ + // Anillo exterior difuso + Container( + width: 200, + height: 200, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withValues(alpha: 0.06), + ), + ), + // Anillo con blur + ClipOval( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8), + child: Container( + width: 180, + height: 180, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withValues(alpha: 0.15), + border: Border.all(color: Colors.white.withValues(alpha: 0.4), width: 2), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(_emojiSegunETA(eta.etaMinutos), style: const TextStyle(fontSize: 36)), + const SizedBox(height: 2), + Text( + '${eta.etaMinutos}', + style: const TextStyle(fontSize: 58, fontWeight: FontWeight.w900, color: Colors.white, height: 1), + ), + const Text('minutos', style: TextStyle(fontSize: 14, color: Colors.white70, letterSpacing: 1)), + ], + ), + ), + ), + ), + ], + ), + ), + + const SizedBox(height: 14), + Text( + _textoEstado(eta.etaMinutos), + style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600), + ), + const SizedBox(height: 4), + Text( + eta.etaTexto, + style: const TextStyle(color: Colors.white60, fontSize: 13), + ), + + // ── CARD MENSAJE PREVENTIVO ────────────────────────────── + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: _GlassCard( + child: Row( + children: [ + Container( + width: 44, height: 44, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(12), + ), + child: const Icon(Icons.notifications_active_rounded, color: Colors.white, size: 22), + ), + const SizedBox(width: 14), + Expanded( + child: Text( + eta.mensajePreventivo, + style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, height: 1.4), + ), + ), + ], + ), + ), + ), + + // ── INFO DE RUTA ───────────────────────────────────────── + const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: _GlassCard( + child: Row( + children: [ + Container( + width: 44, height: 44, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(12), + ), + child: const Icon(Icons.local_shipping_rounded, color: Colors.white, size: 22), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(eta.rutaNombre, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600, fontSize: 13)), + const SizedBox(height: 2), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), + decoration: BoxDecoration( + color: eta.rutaStatus == 'EN_RUTA' + ? Colors.green.withValues(alpha: 0.3) + : Colors.blue.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + eta.rutaStatus, + style: TextStyle( + color: eta.rutaStatus == 'EN_RUTA' ? Colors.greenAccent : Colors.lightBlueAccent, + fontSize: 11, fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + + // ── ACCESOS RÁPIDOS ────────────────────────────────────── + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(left: 2, bottom: 12), + child: Text('Accesos rápidos', style: TextStyle(color: Colors.white60, fontSize: 12, letterSpacing: 0.8)), + ), + GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisSpacing: 12, + mainAxisSpacing: 12, + childAspectRatio: 1.55, + children: [ + _AccesoRapido( + icon: Icons.local_shipping_rounded, + label: 'Rutas', + sublabel: 'Estado del camión', + color: const Color(0xFF1565C0), + onTap: () => Navigator.pushNamed(context, '/routes'), + ), + _AccesoRapido( + icon: Icons.analytics_rounded, + label: 'Análisis', + sublabel: 'Reportes y predicción', + color: const Color(0xFF6A1B9A), + onTap: () => Navigator.pushNamed(context, '/analytics'), + ), + _AccesoRapido( + icon: Icons.report_problem_rounded, + label: 'Reportar', + sublabel: 'Enviar un reporte', + color: const Color(0xFFE65100), + onTap: () => Navigator.pushNamed(context, '/reporte'), + ), + _AccesoRapido( + icon: Icons.eco_rounded, + label: 'Información', + sublabel: 'Guía de residuos', + color: const Color(0xFF2E7D32), + onTap: () => Navigator.pushNamed(context, '/info'), + ), + _AccesoRapido( + icon: Icons.map_rounded, + label: 'Mapa', + sublabel: 'Ver rutas en mapa', + color: const Color(0xFF00838F), + onTap: () => Navigator.pushNamed(context, '/mapa'), + ), + _AccesoRapido( + icon: Icons.add_location_alt_rounded, + label: 'Dirección', + sublabel: 'Agregar domicilio', + color: const Color(0xFFC62828), + onTap: _mostrarAgregarDireccionDialog, + ), + ], + ), + ], + ), + ), + + // ── DIRECCIONES ────────────────────────────────────────── + if (_direcciones.isNotEmpty) ...[ + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: _GlassCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.home_rounded, color: Colors.white70, size: 16), + const SizedBox(width: 8), + const Text('Mis direcciones', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14)), + const Spacer(), + GestureDetector( + onTap: _mostrarAgregarDireccionDialog, + child: const Icon(Icons.add_circle_outline, color: Colors.white54, size: 20), + ), + ], + ), + const SizedBox(height: 10), + ..._direcciones.map((d) => Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Container( + width: 6, height: 6, + decoration: const BoxDecoration(color: Colors.white54, shape: BoxShape.circle), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(d.colonia, style: const TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w600)), + Text(d.direccion, style: const TextStyle(color: Colors.white54, fontSize: 12)), + ], + ), + ), + ], + ), + )), + ], + ), + ), + ), + ], + + // ── FOOTER ─────────────────────────────────────────────── + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.only(bottom: 28), + child: Column(children: [ + const Text('🔄 Actualización automática cada minuto', + style: TextStyle(color: Colors.white30, fontSize: 11)), + const SizedBox(height: 10), + TextButton.icon( + onPressed: _cargarETA, + icon: const Icon(Icons.refresh, color: Colors.white38, size: 16), + label: const Text('Actualizar ahora', style: TextStyle(color: Colors.white38, fontSize: 12)), + ), + ]), + ), + ], + ), + ); + } +} + +// ================================================================ +// WIDGETS AUXILIARES +// ================================================================ + +/// Tarjeta con efecto glassmorphism +class _GlassCard extends StatelessWidget { + final Widget child; + final EdgeInsets? padding; + + const _GlassCard({required this.child, this.padding}); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(18), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + width: double.infinity, + padding: padding ?? const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(18), + border: Border.all(color: Colors.white.withValues(alpha: 0.2), width: 1), + ), + child: child, + ), + ), + ); + } +} + +/// Chip de estado pequeño +class _GlassChip extends StatelessWidget { + final IconData icon; + final String label; + final Color color; + + const _GlassChip({required this.icon, required this.label, required this.color}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: color.withValues(alpha: 0.4)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, color: color, size: 15), + const SizedBox(width: 6), + Text(label, style: TextStyle(color: color, fontSize: 12, fontWeight: FontWeight.w600)), + ], + ), + ); + } +} + +/// Tarjeta de acceso rápido en el grid +class _AccesoRapido extends StatelessWidget { + final IconData icon; + final String label; + final String sublabel; + final Color color; + final VoidCallback onTap; + + const _AccesoRapido({ + required this.icon, + required this.label, + required this.sublabel, + required this.color, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.18), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: color.withValues(alpha: 0.35), width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 36, height: 36, + decoration: BoxDecoration( + color: color.withValues(alpha: 0.25), + borderRadius: BorderRadius.circular(10), + ), + child: Icon(icon, color: Colors.white, size: 20), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), + Text(sublabel, style: const TextStyle(color: Colors.white54, fontSize: 10), overflow: TextOverflow.ellipsis), + ], + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/info_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/info_screen.dart new file mode 100644 index 0000000..87c8a15 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/info_screen.dart @@ -0,0 +1,808 @@ +// ================================================================ +// lib/screens/info_screen.dart — EcoTrack +// Pantalla de Informacion + Tutorial interactivo +// ================================================================ +// +// ASSETS USADOS: +// assets/images/recycle.jpg → Separacion / reciclaje +// assets/images/bottle.png → Plasticos +// assets/images/planta.png → Composta / Medio ambiente +// assets/images/megafono.png → Residuos peligrosos / Horarios +// +// SECCIONES: +// 1. Tutorial interactivo (swipe de pasos) +// 2. Articulos de informacion por categoria +// 3. Detalle de articulo con imagen +// ================================================================ + +import 'package:flutter/material.dart'; + +// ================================================================ +// MODELOS +// ================================================================ + +class _Subseccion { + final String subtitulo; + final String texto; + const _Subseccion(this.subtitulo, this.texto); +} + +class _Articulo { + final String id; + final String categoria; + final String titulo; + final String resumen; + final String imagenAsset; + final Color color; + final List<_Subseccion> contenido; + final String consejoRapido; + + const _Articulo({ + required this.id, + required this.categoria, + required this.titulo, + required this.resumen, + required this.imagenAsset, + required this.color, + required this.contenido, + required this.consejoRapido, + }); +} + +// ================================================================ +// DATOS +// ================================================================ + +const _articulos = [ + _Articulo( + id: 'separacion', + categoria: 'Separacion', + titulo: 'Como separar correctamente tu basura', + resumen: 'La separacion correcta es el primer paso para reciclar y reducir el impacto ambiental.', + imagenAsset: 'assets/images/recycle.jpg', + color: Color(0xFF2E7D32), + consejoRapido: 'Regla facil: si vino de la naturaleza y se pudre, es organico. Si es artificial y esta limpio, es reciclable.', + contenido: [ + _Subseccion('Residuos Organicos', 'Restos de comida, cascaras de frutas y verduras, posos de cafe, bolsas de te, restos de jardin. Van en bolsa oscura o cafe. Se convierten en composta.'), + _Subseccion('Inorganicos Reciclables', 'Plasticos (botellas PET, envases), papel y carton limpios, vidrio, latas de aluminio y hojalata. Van en bolsa transparente. Deben estar limpios y secos.'), + _Subseccion('No Reciclables', 'Papel higienico usado, panales, colillas de cigarro, envolturas metalizadas. Van en bolsa negra. No tienen valor de reciclaje.'), + _Subseccion('Residuos Especiales', 'Pilas, medicamentos caducados, electronicos, aceite de cocina. NUNCA los mezcles con la basura regular. Lleva pilas a puntos de acopio en supermercados.'), + ], + ), + _Articulo( + id: 'horarios', + categoria: 'Horarios', + titulo: 'Cuando sacar tu basura', + resumen: 'Sacar la basura en el momento correcto evita plagas, malos olores y que el camion se la pierda.', + imagenAsset: 'assets/images/megafono.png', + color: Color(0xFF1565C0), + consejoRapido: 'Espera la alerta de EcoTrack antes de salir con tus bolsas. Te ahorra tiempo y evita dejar basura expuesta.', + contenido: [ + _Subseccion('El momento ideal', 'Saca tu basura cuando recibas la alerta de "Camion Cercano" en EcoTrack. Eso significa que el camion esta a menos de 15 minutos de tu domicilio.'), + _Subseccion('Por que no de noche', 'Las bolsas en la acera de noche atraen perros y fauna nocturna que las rompen y dispersan los residuos. Ademas el plastico se deteriora con la humedad nocturna.'), + _Subseccion('Si me lo pierdo', 'Si el camion ya paso, guarda tu basura hasta el siguiente dia. Nunca dejes bolsas en la via publica fuera del horario de recoleccion.'), + _Subseccion('Dias festivos', 'En dias festivos el servicio puede retrasarse o cancelarse. Activa las notificaciones de EcoTrack para recibir alertas de retraso o cambio de horario.'), + ], + ), + _Articulo( + id: 'plasticos', + categoria: 'Reciclaje', + titulo: 'Guia de plasticos: cuales si y cuales no', + resumen: 'No todos los plasticos son iguales. Aprende a leer el numero en el triangulo de reciclaje.', + imagenAsset: 'assets/images/bottle.png', + color: Color(0xFF00838F), + consejoRapido: 'Busca el numero dentro del triangulo en el fondo del envase. Los numeros 1 y 2 siempre van al reciclaje.', + contenido: [ + _Subseccion('Plastico #1 PET', 'Botellas de agua y refrescos. El mas reciclado. Aplastalo para ahorrar espacio. Quita la tapa porque es un material diferente.'), + _Subseccion('Plastico #2 HDPE', 'Garrafones, botellas de leche, shampoo. Tambien muy reciclable. Enjuagalo antes de separarlo.'), + _Subseccion('Plastico #5 PP', 'Tapas de botellas, envases de yogur. Si se recicla pero menos centros lo aceptan.'), + _Subseccion('Plasticos 3, 6 y 7', 'PVC, unicel, policarbonato. Dificiles o imposibles de reciclar. Van a basura no reciclable.'), + _Subseccion('Bolsas de plastico', 'No van en el reciclaje de casa porque tapan las maquinas clasificadoras. Lleva tus bolsas a centros de acopio en supermercados.'), + ], + ), + _Articulo( + id: 'composta', + categoria: 'Compostaje', + titulo: 'Haz composta en casa', + resumen: 'Convierte tus residuos organicos en abono natural. Es mas facil de lo que crees.', + imagenAsset: 'assets/images/planta.png', + color: Color(0xFF558B2F), + consejoRapido: 'La composta lista huele a tierra mojada, no a podrido. Si huele mal, agrega mas material seco y volteala.', + contenido: [ + _Subseccion('Que necesitas', 'Un contenedor con tapa, residuos organicos, tierra o tierra de hojarasca, y un poco de paciencia.'), + _Subseccion('Que puedes compostar', 'Cascaras de frutas y verduras, restos de comida cocida sin carne, posos de cafe y filtros de papel, cascaras de huevo, hojas secas.'), + _Subseccion('Que NO debes compostar', 'Carnes, pescados, lacteos, aceites ya que atraen plagas, excrementos de mascotas, plasticos ni metales.'), + _Subseccion('El proceso', 'Alterna capas de residuos organicos humedos con capas de material seco. Voltea la mezcla cada semana. En 2 a 3 meses tendras composta lista para tus plantas.'), + ], + ), + _Articulo( + id: 'peligrosos', + categoria: 'Residuos Especiales', + titulo: 'Residuos peligrosos: como deshacerte de ellos', + resumen: 'Pilas, medicamentos y electronicos requieren un manejo especial para no contaminar el suelo y el agua.', + imagenAsset: 'assets/images/megafono.png', + color: Color(0xFFE65100), + consejoRapido: 'Guarda una caja en casa exclusiva para residuos peligrosos. Cuando este llena, busca el punto de acopio mas cercano.', + contenido: [ + _Subseccion('Pilas y baterias', 'Una sola pila AA puede contaminar 600,000 litros de agua. Guardalas en una bolsa y lleva a los puntos de acopio en Walmart, Soriana, Home Depot o OXXO.'), + _Subseccion('Medicamentos caducados', 'No los tires al drenaje ni a la basura regular. Farmacias del Ahorro y Benavides cuentan con contenedores REPARED para medicamentos.'), + _Subseccion('Electronicos RAEE', 'Celulares, computadoras, cables, focos LED. Contienen plomo, mercurio y cadmio. Lleva a tiendas de electronicos o espera las jornadas municipales.'), + _Subseccion('Aceite de cocina', 'Un litro de aceite contamina hasta 1,000 litros de agua potable. Viertelo en una botella PET con tapa y lleva a centros de acopio.'), + ], + ), + _Articulo( + id: 'impacto', + categoria: 'Medio Ambiente', + titulo: 'El impacto real de reciclar', + resumen: 'Numeros concretos para entender por que vale la pena separar tu basura cada dia.', + imagenAsset: 'assets/images/planta.png', + color: Color(0xFF4527A0), + consejoRapido: 'Cada lata de aluminio que reciclas ahorra energia equivalente a medio litro de gasolina. Si importa.', + contenido: [ + _Subseccion('Papel y carton', 'Reciclar 1 tonelada de papel salva 17 arboles y ahorra 26,000 litros de agua. Una familia promedio genera 500 kg de papel al ano.'), + _Subseccion('Aluminio', 'Reciclar una lata de aluminio ahorra la energia suficiente para que un foco LED funcione 20 horas. El aluminio puede reciclarse infinitas veces.'), + _Subseccion('Vidrio', 'El vidrio tarda mas de 4,000 anos en degradarse. Reciclarlo reduce en 20% las emisiones de CO2 de su produccion.'), + _Subseccion('Residuos en Mexico', 'Mexico genera 120,000 toneladas de basura al dia. Solo el 9% se recicla formalmente. Si cada hogar separara correctamente, ese porcentaje podria triplicarse.'), + ], + ), +]; + +// ── Pasos del tutorial ───────────────────────────────────────── + +class _PasoTutorial { + final String titulo; + final String descripcion; + final String imagenAsset; + final Color color; + final IconData icono; + + const _PasoTutorial({ + required this.titulo, + required this.descripcion, + required this.imagenAsset, + required this.color, + required this.icono, + }); +} + +const _pasosTutorial = [ + _PasoTutorial( + titulo: 'Bienvenido a EcoTrack', + descripcion: 'EcoTrack te notifica cuando el camion recolector esta cerca de tu domicilio para que saques tu basura en el momento exacto.', + imagenAsset: 'assets/images/recycle.jpg', + color: Color(0xFF2E7D32), + icono: Icons.recycling_rounded, + ), + _PasoTutorial( + titulo: 'Separa tus residuos', + descripcion: 'Separa tu basura en organicos, reciclables y no reciclables. Esto facilita el trabajo del camion y reduce el impacto ambiental.', + imagenAsset: 'assets/images/bottle.png', + color: Color(0xFF00838F), + icono: Icons.category_rounded, + ), + _PasoTutorial( + titulo: 'Espera la alerta', + descripcion: 'Activa las notificaciones de EcoTrack. Te avisaremos cuando el camion este a menos de 15 minutos de tu casa.', + imagenAsset: 'assets/images/megafono.png', + color: Color(0xFF1565C0), + icono: Icons.notifications_active_rounded, + ), + _PasoTutorial( + titulo: 'Saca la basura a tiempo', + descripcion: 'Al recibir la alerta, saca tus bolsas a la acera. Evita sacarlas muy antes para no atraer fauna y mantener limpia la calle.', + imagenAsset: 'assets/images/recycle.jpg', + color: Color(0xFFE65100), + icono: Icons.access_time_filled_rounded, + ), + _PasoTutorial( + titulo: 'Envia reportes', + descripcion: 'Si el camion no paso o detectas alguna irregularidad, usa la seccion de Reportes para informar al municipio. Tu participacion mejora el servicio.', + imagenAsset: 'assets/images/megafono.png', + color: Color(0xFF6A1B9A), + icono: Icons.report_problem_rounded, + ), +]; + +// ================================================================ +// PANTALLA PRINCIPAL +// ================================================================ +class InfoScreen extends StatefulWidget { + const InfoScreen({super.key}); + + @override + State createState() => _InfoScreenState(); +} + +class _InfoScreenState extends State with SingleTickerProviderStateMixin { + late TabController _tabController; + String? _categoriaFiltro; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + List get _categorias => + _articulos.map((a) => a.categoria).toSet().toList(); + + List<_Articulo> get _articulosFiltrados => + _categoriaFiltro == null + ? _articulos + : _articulos.where((a) => a.categoria == _categoriaFiltro).toList(); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF5F7F5), + appBar: AppBar( + title: const Text('EcoTrack — Aprende'), + backgroundColor: const Color(0xFF2E7D32), + foregroundColor: Colors.white, + elevation: 0, + bottom: TabBar( + controller: _tabController, + indicatorColor: Colors.white, + labelColor: Colors.white, + unselectedLabelColor: Colors.white60, + tabs: const [ + Tab(icon: Icon(Icons.play_circle_outline_rounded), text: 'Tutorial'), + Tab(icon: Icon(Icons.menu_book_rounded), text: 'Guias'), + ], + ), + ), + body: TabBarView( + controller: _tabController, + children: [ + _TutorialView(), + _GuiasView( + categorias: _categorias, + categoriaFiltro: _categoriaFiltro, + articulos: _articulosFiltrados, + onFiltro: (c) => setState(() => _categoriaFiltro = c), + ), + ], + ), + ); + } +} + +// ================================================================ +// TAB 1: TUTORIAL INTERACTIVO +// ================================================================ +class _TutorialView extends StatefulWidget { + @override + State<_TutorialView> createState() => _TutorialViewState(); +} + +class _TutorialViewState extends State<_TutorialView> { + final PageController _pageCtrl = PageController(); + int _paginaActual = 0; + + @override + void dispose() { + _pageCtrl.dispose(); + super.dispose(); + } + + void _siguiente() { + if (_paginaActual < _pasosTutorial.length - 1) { + _pageCtrl.nextPage(duration: const Duration(milliseconds: 350), curve: Curves.easeInOut); + } + } + + void _anterior() { + if (_paginaActual > 0) { + _pageCtrl.previousPage(duration: const Duration(milliseconds: 350), curve: Curves.easeInOut); + } + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + // Indicador de progreso + Padding( + padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), + child: Row( + children: List.generate(_pasosTutorial.length, (i) { + final activo = i == _paginaActual; + final completado = i < _paginaActual; + return Expanded( + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + margin: const EdgeInsets.symmetric(horizontal: 3), + height: 4, + decoration: BoxDecoration( + color: completado + ? const Color(0xFF2E7D32) + : activo + ? _pasosTutorial[_paginaActual].color + : Colors.grey.shade200, + borderRadius: BorderRadius.circular(2), + ), + ), + ); + }), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Paso ${_paginaActual + 1} de ${_pasosTutorial.length}', + style: const TextStyle(color: Colors.grey, fontSize: 12), + ), + Text( + _paginaActual == _pasosTutorial.length - 1 ? 'Completado' : '', + style: const TextStyle(color: Color(0xFF2E7D32), fontSize: 12, fontWeight: FontWeight.bold), + ), + ], + ), + ), + + // Páginas del tutorial + Expanded( + child: PageView.builder( + controller: _pageCtrl, + itemCount: _pasosTutorial.length, + onPageChanged: (i) => setState(() => _paginaActual = i), + itemBuilder: (context, i) => _PaginaTutorial(paso: _pasosTutorial[i]), + ), + ), + + // Controles de navegación + Padding( + padding: const EdgeInsets.fromLTRB(20, 8, 20, 24), + child: Row( + children: [ + if (_paginaActual > 0) + Expanded( + child: OutlinedButton.icon( + onPressed: _anterior, + icon: const Icon(Icons.arrow_back_rounded, size: 18), + label: const Text('Anterior'), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + ), + ), + if (_paginaActual > 0) const SizedBox(width: 12), + Expanded( + flex: _paginaActual == 0 ? 1 : 1, + child: ElevatedButton.icon( + onPressed: _paginaActual == _pasosTutorial.length - 1 ? null : _siguiente, + icon: Icon( + _paginaActual == _pasosTutorial.length - 1 + ? Icons.check_circle_rounded + : Icons.arrow_forward_rounded, + size: 18, + ), + label: Text( + _paginaActual == _pasosTutorial.length - 1 ? 'Listo' : 'Siguiente', + ), + style: ElevatedButton.styleFrom( + backgroundColor: _pasosTutorial[_paginaActual].color, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + elevation: 0, + ), + ), + ), + ], + ), + ), + ], + ); + } +} + +class _PaginaTutorial extends StatelessWidget { + final _PasoTutorial paso; + const _PaginaTutorial({required this.paso}); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + children: [ + // Imagen con overlay de icono + Stack( + alignment: Alignment.bottomRight, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Image.asset( + paso.imagenAsset, + height: 220, + width: double.infinity, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container( + height: 220, + decoration: BoxDecoration( + color: paso.color.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(20), + ), + child: Icon(paso.icono, size: 80, color: paso.color.withValues(alpha: 0.4)), + ), + ), + ), + // Badge del icono + Positioned( + bottom: 16, + right: 16, + child: Container( + width: 52, height: 52, + decoration: BoxDecoration( + color: paso.color, + shape: BoxShape.circle, + boxShadow: [BoxShadow(color: paso.color.withValues(alpha: 0.4), blurRadius: 12)], + ), + child: Icon(paso.icono, color: Colors.white, size: 26), + ), + ), + ], + ), + + const SizedBox(height: 24), + + // Titulo + Text( + paso.titulo, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: paso.color, height: 1.2), + ), + const SizedBox(height: 12), + + // Descripcion + Container( + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 8)], + ), + child: Text( + paso.descripcion, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 15, color: Color(0xFF444444), height: 1.6), + ), + ), + const SizedBox(height: 20), + ], + ), + ); + } +} + +// ================================================================ +// TAB 2: GUIAS DE INFORMACION +// ================================================================ +class _GuiasView extends StatelessWidget { + final List categorias; + final String? categoriaFiltro; + final List<_Articulo> articulos; + final void Function(String?) onFiltro; + + const _GuiasView({ + required this.categorias, + required this.categoriaFiltro, + required this.articulos, + required this.onFiltro, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + // Filtros de categoria + SizedBox( + height: 48, + child: ListView( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + children: [ + _FiltroChip( + label: 'Todas', + seleccionado: categoriaFiltro == null, + color: const Color(0xFF2E7D32), + onTap: () => onFiltro(null), + ), + ...categorias.map((cat) { + final a = _articulos.firstWhere((x) => x.categoria == cat, orElse: () => _articulos.first); + return _FiltroChip( + label: cat, + seleccionado: categoriaFiltro == cat, + color: a.color, + onTap: () => onFiltro(cat), + ); + }), + ], + ), + ), + + // Lista de articulos + Expanded( + child: ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 4, 16, 24), + itemCount: articulos.length, + itemBuilder: (context, i) => _TarjetaArticulo( + articulo: articulos[i], + onTap: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => _DetalleArticuloScreen(articulo: articulos[i])), + ), + ), + ), + ), + ], + ); + } +} + +// ================================================================ +// WIDGET: Chip de filtro +// ================================================================ +class _FiltroChip extends StatelessWidget { + final String label; + final bool seleccionado; + final Color color; + final VoidCallback onTap; + + const _FiltroChip({required this.label, required this.seleccionado, required this.color, required this.onTap}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(right: 8), + child: GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), + decoration: BoxDecoration( + color: seleccionado ? color : Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: color, width: 1.5), + ), + child: Text( + label, + style: TextStyle(color: seleccionado ? Colors.white : color, fontWeight: FontWeight.w600, fontSize: 12), + ), + ), + ), + ); + } +} + +// ================================================================ +// WIDGET: Tarjeta de articulo +// ================================================================ +class _TarjetaArticulo extends StatelessWidget { + final _Articulo articulo; + final VoidCallback onTap; + + const _TarjetaArticulo({required this.articulo, required this.onTap}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: GestureDetector( + onTap: onTap, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.06), blurRadius: 8, offset: const Offset(0, 2))], + ), + child: Row( + children: [ + // Imagen lateral + ClipRRect( + borderRadius: const BorderRadius.horizontal(left: Radius.circular(16)), + child: Image.asset( + articulo.imagenAsset, + width: 90, + height: 100, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container( + width: 90, height: 100, + color: articulo.color.withValues(alpha: 0.1), + child: Icon(Icons.eco_rounded, color: articulo.color, size: 36), + ), + ), + ), + // Contenido + Expanded( + child: Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: articulo.color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Text(articulo.categoria, + style: TextStyle(color: articulo.color, fontSize: 10, fontWeight: FontWeight.w700)), + ), + const SizedBox(height: 6), + Text(articulo.titulo, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A)), + maxLines: 2, overflow: TextOverflow.ellipsis), + const SizedBox(height: 4), + Text(articulo.resumen, + style: TextStyle(fontSize: 12, color: Colors.grey.shade600, height: 1.3), + maxLines: 2, overflow: TextOverflow.ellipsis), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.only(right: 12), + child: Icon(Icons.chevron_right_rounded, color: Colors.grey.shade400), + ), + ], + ), + ), + ), + ); + } +} + +// ================================================================ +// PANTALLA DE DETALLE +// ================================================================ +class _DetalleArticuloScreen extends StatelessWidget { + final _Articulo articulo; + const _DetalleArticuloScreen({required this.articulo}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF5F7F5), + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 220, + pinned: true, + backgroundColor: articulo.color, + foregroundColor: Colors.white, + flexibleSpace: FlexibleSpaceBar( + title: Text( + articulo.titulo, + style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold), + maxLines: 2, + ), + background: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + articulo.imagenAsset, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container(color: articulo.color), + ), + // Gradiente oscuro para legibilidad del titulo + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.transparent, articulo.color.withValues(alpha: 0.85)], + stops: const [0.4, 1.0], + ), + ), + ), + ], + ), + ), + ), + + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Badge categoria + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 5), + decoration: BoxDecoration( + color: articulo.color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(20), + ), + child: Text(articulo.categoria, + style: TextStyle(color: articulo.color, fontWeight: FontWeight.w700, fontSize: 12)), + ), + const SizedBox(height: 12), + + // Resumen + Text(articulo.resumen, + style: const TextStyle(fontSize: 15, color: Color(0xFF333333), height: 1.5)), + const SizedBox(height: 20), + + // Secciones + ...articulo.contenido.map((s) => _SeccionCard(seccion: s, color: articulo.color)), + + const SizedBox(height: 8), + + // Consejo destacado + Container( + width: double.infinity, + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: articulo.color, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(Icons.lightbulb_outline_rounded, color: Colors.white.withValues(alpha: 0.9), size: 20), + const SizedBox(width: 8), + const Text('Consejo rapido', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15)), + ], + ), + const SizedBox(height: 10), + Text(articulo.consejoRapido, + style: const TextStyle(color: Colors.white, fontSize: 14, height: 1.5)), + ], + ), + ), + const SizedBox(height: 32), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _SeccionCard extends StatelessWidget { + final _Subseccion seccion; + final Color color; + const _SeccionCard({required this.seccion, required this.color}); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border(left: BorderSide(color: color, width: 4)), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.04), blurRadius: 6, offset: const Offset(0, 2))], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(seccion.subtitulo, + style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold, color: color)), + const SizedBox(height: 6), + Text(seccion.texto, + style: const TextStyle(fontSize: 13, color: Color(0xFF444444), height: 1.5)), + ], + ), + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/login_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/login_screen.dart new file mode 100644 index 0000000..61ef896 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/login_screen.dart @@ -0,0 +1,402 @@ +// ================================================================ +// lib/screens/login_screen.dart — EcoTrack +// ================================================================ + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../services/api_service.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + final _emailCtrl = TextEditingController(); + final _passwordCtrl = TextEditingController(); + final _nameCtrl = TextEditingController(); + final _dirCtrl = TextEditingController(); + + String? _coloniaSeleccionada; + List _colonias = []; + bool _esRegistro = false; + bool _cargandoColonias = true; + String? _errorColonias; + bool _logueando = false; + bool _mostrarPassword = false; + + final ApiService _apiService = ApiService(); + + @override + void initState() { + super.initState(); + _cargarColonias(); + _verificarSesion(); + } + + @override + void dispose() { + _emailCtrl.dispose(); + _passwordCtrl.dispose(); + _nameCtrl.dispose(); + _dirCtrl.dispose(); + super.dispose(); + } + + Future _verificarSesion() async { + final prefs = await SharedPreferences.getInstance(); + final id = prefs.getInt('usuario_id'); + if (id != null && mounted) { + Navigator.pushReplacementNamed(context, '/home', arguments: id); + } + } + + Future _cargarColonias() async { + try { + final colonias = await _apiService.obtenerColonias(); + if (mounted) setState(() { _colonias = colonias; _cargandoColonias = false; }); + } catch (e) { + if (mounted) { + setState(() { + _colonias = ['Zona Centro', 'Las Arboledas', 'Trojes', 'San Juanico', 'Los Olivos', 'Rancho Seco', 'Las Insurgentes']; + _cargandoColonias = false; + _errorColonias = 'Sin conexión al backend. Usando lista local.'; + }); + } + } + } + + Future _iniciarSesion() async { + final email = _emailCtrl.text.trim(); + final password = _passwordCtrl.text; + if (email.isEmpty) { _error('Ingresa tu correo.'); return; } + if (password.isEmpty) { _error('Ingresa tu contraseña.'); return; } + + setState(() => _logueando = true); + try { + final r = await _apiService.loginConCorreo(email, password); + final prefs = await SharedPreferences.getInstance(); + await prefs.setInt('usuario_id', r['usuario_id']); + await prefs.setString('nombre', r['nombre'] ?? ''); + await prefs.setString('email', email); + if (mounted) Navigator.pushReplacementNamed(context, '/home', arguments: r['usuario_id']); + } catch (e) { + _error(e.toString().replaceFirst('Exception: ', '')); + } finally { + if (mounted) setState(() => _logueando = false); + } + } + + Future _registrarse() async { + final nombre = _nameCtrl.text.trim(); + final email = _emailCtrl.text.trim(); + final password = _passwordCtrl.text; + final dir = _dirCtrl.text.trim(); + + if (nombre.isEmpty) { _error('Ingresa tu nombre.'); return; } + if (email.isEmpty) { _error('Ingresa tu correo.'); return; } + if (password.length < 6) { _error('Contraseña de al menos 6 caracteres.'); return; } + if (_coloniaSeleccionada == null) { _error('Selecciona tu colonia.'); return; } + if (dir.isEmpty) { _error('Ingresa tu dirección.'); return; } + + setState(() => _logueando = true); + try { + final id = await _apiService.registrarUsuario(nombre, email, password, dir, _coloniaSeleccionada!); + final prefs = await SharedPreferences.getInstance(); + await prefs.setInt('usuario_id', id); + await prefs.setString('nombre', nombre); + await prefs.setString('email', email); + if (mounted) Navigator.pushReplacementNamed(context, '/home', arguments: id); + } catch (e) { + _error(e.toString().replaceFirst('Exception: ', '')); + } finally { + if (mounted) setState(() => _logueando = false); + } + } + + void _error(String msg) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(msg), + backgroundColor: Colors.red.shade700, + behavior: SnackBarBehavior.floating, + )); + } + + // ================================================================ + // BUILD + // ================================================================ + @override + Widget build(BuildContext context) { + final color = Theme.of(context).colorScheme.primary; + + return Scaffold( + backgroundColor: const Color(0xFFF6FAF6), + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 16), + + // ── LOGO ───────────────────────────────────────────── + Center( + child: Container( + width: 84, height: 84, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(22), + boxShadow: [BoxShadow(color: color.withValues(alpha: 0.3), blurRadius: 20, offset: const Offset(0, 8))], + ), + child: const Icon(Icons.recycling_rounded, size: 48, color: Colors.white), + ), + ), + const SizedBox(height: 18), + Text('EcoTrack', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 32, fontWeight: FontWeight.w900, color: color, letterSpacing: -0.5), + ), + const SizedBox(height: 4), + const Text('Recolección inteligente de residuos', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey, fontSize: 13), + ), + const SizedBox(height: 32), + + // ── TABS LOGIN / REGISTRO ───────────────────────────── + Container( + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + _Tab(label: 'Iniciar sesión', seleccionado: !_esRegistro, color: color, + onTap: () => setState(() { _esRegistro = false; _passwordCtrl.clear(); })), + _Tab(label: 'Registrarse', seleccionado: _esRegistro, color: color, + onTap: () => setState(() { _esRegistro = true; _passwordCtrl.clear(); })), + ], + ), + ), + const SizedBox(height: 24), + + // ── CAMPOS COMUNES ──────────────────────────────────── + if (_esRegistro) ...[ + _Campo(ctrl: _nameCtrl, label: 'Nombre completo', icon: Icons.person_outline), + const SizedBox(height: 14), + ], + _Campo(ctrl: _emailCtrl, label: 'Correo electrónico', icon: Icons.email_outlined, tipo: TextInputType.emailAddress), + const SizedBox(height: 14), + _Campo( + ctrl: _passwordCtrl, + label: 'Contraseña', + icon: Icons.lock_outline, + obscure: !_mostrarPassword, + sufijo: IconButton( + icon: Icon(_mostrarPassword ? Icons.visibility_off : Icons.visibility, size: 20), + onPressed: () => setState(() => _mostrarPassword = !_mostrarPassword), + ), + ), + + // ── CAMPOS EXTRA REGISTRO ───────────────────────────── + if (_esRegistro) ...[ + const SizedBox(height: 14), + _Campo(ctrl: _dirCtrl, label: 'Dirección', icon: Icons.home_outlined), + const SizedBox(height: 14), + if (_cargandoColonias) + const Center(child: Padding(padding: EdgeInsets.all(12), child: CircularProgressIndicator())) + else ...[ + if (_errorColonias != null) + Padding( + padding: const EdgeInsets.only(bottom: 6), + child: Text(_errorColonias!, style: TextStyle(fontSize: 11, color: Colors.orange.shade700)), + ), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade200), + ), + child: DropdownButtonFormField( + value: _coloniaSeleccionada, + hint: const Text('Selecciona tu colonia'), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.location_city_outlined), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + ), + items: _colonias.map((c) => DropdownMenuItem(value: c, child: Text(c))).toList(), + onChanged: (v) => setState(() => _coloniaSeleccionada = v), + ), + ), + ], + const SizedBox(height: 8), + _IndicadorFortaleza(password: _passwordCtrl.text), + ], + + const SizedBox(height: 24), + + // ── BOTÓN PRINCIPAL ─────────────────────────────────── + SizedBox( + height: 52, + child: ElevatedButton( + onPressed: _logueando ? null : (_esRegistro ? _registrarse : _iniciarSesion), + style: ElevatedButton.styleFrom( + backgroundColor: color, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + elevation: 0, + ), + child: _logueando + ? const SizedBox(width: 22, height: 22, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) + : Text(_esRegistro ? 'Crear cuenta' : 'Entrar', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + ), + ), + + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.06), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + _esRegistro + ? 'Tu contraseña se almacena de forma segura con bcrypt. Minimo 6 caracteres.' + : 'Tus datos son privados y solo se usan para notificarte cuando el camion se acerca.', + style: TextStyle(fontSize: 11, color: color), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} + +// ================================================================ +// WIDGETS AUXILIARES +// ================================================================ + +class _Tab extends StatelessWidget { + final String label; + final bool seleccionado; + final Color color; + final VoidCallback onTap; + + const _Tab({required this.label, required this.seleccionado, required this.color, required this.onTap}); + + @override + Widget build(BuildContext context) { + return Expanded( + child: GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + margin: const EdgeInsets.all(4), + padding: const EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + color: seleccionado ? Colors.white : Colors.transparent, + borderRadius: BorderRadius.circular(9), + boxShadow: seleccionado ? [BoxShadow(color: Colors.black.withValues(alpha: 0.08), blurRadius: 6)] : [], + ), + child: Text(label, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: seleccionado ? FontWeight.bold : FontWeight.normal, + color: seleccionado ? color : Colors.grey.shade500, + fontSize: 13, + ), + ), + ), + ), + ); + } +} + +class _Campo extends StatelessWidget { + final TextEditingController ctrl; + final String label; + final IconData icon; + final bool obscure; + final TextInputType tipo; + final Widget? sufijo; + + const _Campo({ + required this.ctrl, + required this.label, + required this.icon, + this.obscure = false, + this.tipo = TextInputType.text, + this.sufijo, + }); + + @override + Widget build(BuildContext context) { + return TextField( + controller: ctrl, + obscureText: obscure, + keyboardType: tipo, + textCapitalization: tipo == TextInputType.text ? TextCapitalization.words : TextCapitalization.none, + decoration: InputDecoration( + labelText: label, + prefixIcon: Icon(icon, size: 20), + suffixIcon: sufijo, + filled: true, + fillColor: Colors.white, + border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200)), + enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200)), + focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 1.5)), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + ), + ); + } +} + +class _IndicadorFortaleza extends StatelessWidget { + final String password; + const _IndicadorFortaleza({required this.password}); + + (int, String, Color) _evaluar() { + if (password.isEmpty) return (0, '', Colors.grey); + int pts = 0; + if (password.length >= 8) pts++; + if (password.contains(RegExp(r'[A-Z]'))) pts++; + if (password.contains(RegExp(r'[0-9]'))) pts++; + if (password.contains(RegExp(r'[!@#\$%^&*]'))) pts++; + if (pts <= 1) return (1, 'Debil', Colors.red); + if (pts == 2) return (2, 'Regular', Colors.orange); + if (pts == 3) return (3, 'Buena', Colors.lightGreen); + return (4, 'Excelente', Colors.green); + } + + @override + Widget build(BuildContext context) { + if (password.isEmpty) return const SizedBox.shrink(); + final (nivel, etiqueta, color) = _evaluar(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: List.generate(4, (i) => Expanded( + child: Container( + margin: const EdgeInsets.only(right: 4), + height: 4, + decoration: BoxDecoration( + color: i < nivel ? color : Colors.grey.shade200, + borderRadius: BorderRadius.circular(2), + ), + ), + ))), + const SizedBox(height: 4), + Text('Contrasena: $etiqueta', style: TextStyle(fontSize: 11, color: color)), + ], + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/mapa_rutas_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/mapa_rutas_screen.dart new file mode 100644 index 0000000..0044f19 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/mapa_rutas_screen.dart @@ -0,0 +1,523 @@ +// ================================================================ +// lib/screens/mapa_rutas_screen.dart (v2 — flutter_map) +// Mapa real de Celaya con rutas estilo metro sobre OpenStreetMap +// ================================================================ +// +// DEPENDENCIAS (agregar en pubspec.yaml): +// flutter_map: ^7.0.2 +// latlong2: ^0.9.1 +// +// SIN API KEY — usa tiles de OpenStreetMap (gratis, libre) +// +// FILTRADO: +// Solo las rutas del usuario se ven en color vivo. +// Las demás se muestran al 20% de opacidad. +// ================================================================ + +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../services/api_service.dart'; + +// ---------------------------------------------------------------- +// DATOS DE RUTAS +// ---------------------------------------------------------------- +class _RutaInfo { + final String routeId; + final String nombre; + final String colonia; + final Color color; + final List puntos; + + const _RutaInfo({ + required this.routeId, + required this.nombre, + required this.colonia, + required this.color, + required this.puntos, + }); +} + +final _todasLasRutas = [ + _RutaInfo( + routeId: 'RUTA-01', + nombre: 'Zona Centro - Las Arboledas', + colonia: 'Zona Centro', + color: const Color(0xFF1565C0), + puntos: [ + LatLng(20.5111, -100.9037), + LatLng(20.5185, -100.8450), + LatLng(20.5215, -100.8142), + LatLng(20.5212, -100.8175), + LatLng(20.5210, -100.8210), + LatLng(20.5235, -100.8212), + LatLng(20.5260, -100.8215), + LatLng(20.5111, -100.9037), + ], + ), + _RutaInfo( + routeId: 'RUTA-03', + nombre: 'Sector Poniente - San Juanico', + colonia: 'San Juanico', + color: const Color(0xFF2E7D32), + puntos: [ + LatLng(20.5111, -100.9037), + LatLng(20.5250, -100.8510), + LatLng(20.5290, -100.8320), + LatLng(20.5315, -100.8355), + LatLng(20.5340, -100.8390), + LatLng(20.5362, -100.8425), + LatLng(20.5330, -100.8430), + LatLng(20.5111, -100.9037), + ], + ), + _RutaInfo( + routeId: 'RUTA-04', + nombre: 'Oriente - Los Olivos', + colonia: 'Los Olivos', + color: const Color(0xFFE65100), + puntos: [ + LatLng(20.5111, -100.9037), + LatLng(20.5260, -100.8010), + LatLng(20.5295, -100.7890), + LatLng(20.5320, -100.7850), + LatLng(20.5350, -100.7790), + LatLng(20.5310, -100.7760), + LatLng(20.5270, -100.7820), + LatLng(20.5111, -100.9037), + ], + ), + _RutaInfo( + routeId: 'RUTA-05', + nombre: 'Sector Sur - Rancho Seco', + colonia: 'Rancho Seco', + color: const Color(0xFF6A1B9A), + puntos: [ + LatLng(20.5111, -100.9037), + LatLng(20.5050, -100.8620), + LatLng(20.5020, -100.8350), + LatLng(20.4995, -100.8210), + LatLng(20.4970, -100.8150), + LatLng(20.5010, -100.8120), + LatLng(20.5060, -100.8160), + LatLng(20.5111, -100.9037), + ], + ), + _RutaInfo( + routeId: 'RUTA-12', + nombre: 'Nororiente - Las Insurgentes', + colonia: 'Las Insurgentes', + color: const Color(0xFFC62828), + puntos: [ + LatLng(20.5111, -100.9037), + LatLng(20.5280, -100.8080), + LatLng(20.5320, -100.7980), + LatLng(20.5340, -100.7940), + LatLng(20.5360, -100.7900), + LatLng(20.5310, -100.7920), + LatLng(20.5270, -100.8020), + LatLng(20.5111, -100.9037), + ], + ), + _RutaInfo( + routeId: 'RUTA-13', + nombre: 'Sector Norte - Trojes e Irrigación', + colonia: 'Trojes', + color: const Color(0xFF00838F), + puntos: [ + LatLng(20.5111, -100.9037), + LatLng(20.5360, -100.8190), + LatLng(20.5420, -100.8080), + LatLng(20.5440, -100.8040), + LatLng(20.5460, -100.8000), + LatLng(20.5410, -100.8020), + LatLng(20.5370, -100.8120), + LatLng(20.5111, -100.9037), + ], + ), +]; + +const _coloniaARuta = { + 'Zona Centro': 'RUTA-01', + 'Las Arboledas': 'RUTA-01', + 'San Juanico': 'RUTA-03', + 'Los Olivos': 'RUTA-04', + 'Rancho Seco': 'RUTA-05', + 'Las Insurgentes': 'RUTA-12', + 'Trojes': 'RUTA-13', +}; + +// ================================================================ +// PANTALLA +// ================================================================ +class MapaRutasScreen extends StatefulWidget { + const MapaRutasScreen({super.key}); + + @override + State createState() => _MapaRutasScreenState(); +} + +class _MapaRutasScreenState extends State { + Set _rutasDelUsuario = {}; + List _estadosBackend = []; + String? _rutaSeleccionada; + bool _cargando = true; + bool _mostrarTodas = false; + + final _mapController = MapController(); + final ApiService _apiService = ApiService(); + + static const _centro = LatLng(20.5230, -100.8550); + + @override + void initState() { + super.initState(); + _cargarDatos(); + } + + Future _cargarDatos() async { + setState(() => _cargando = true); + final prefs = await SharedPreferences.getInstance(); + final usuarioId = prefs.getInt('usuario_id'); + if (usuarioId == null) { + if (mounted) setState(() => _cargando = false); + return; + } + try { + final rutas = await _apiService.obtenerRutas(usuarioId); + final usuario = await _apiService.obtenerUsuario(usuarioId); + final ids = {}; + for (final d in usuario.direcciones) { + final r = _coloniaARuta[d.colonia]; + if (r != null) ids.add(r); + } + for (final r in rutas) ids.add(r.routeId); + if (mounted) { + setState(() { + _estadosBackend = rutas; + _rutasDelUsuario = ids; + _cargando = false; + }); + } + } catch (_) { + if (mounted) setState(() => _cargando = false); + } + } + + RouteInfo? _estadoRuta(String routeId) { + try { + return _estadosBackend.firstWhere((r) => r.routeId == routeId); + } catch (_) { + return null; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Mapa de rutas — Celaya'), + backgroundColor: const Color(0xFF1B5E20), + foregroundColor: Colors.white, + actions: [ + IconButton( + icon: Icon(_mostrarTodas ? Icons.visibility_off : Icons.visibility), + tooltip: _mostrarTodas ? 'Solo mis rutas' : 'Ver todas', + onPressed: () => setState(() => _mostrarTodas = !_mostrarTodas), + ), + IconButton( + icon: const Icon(Icons.my_location), + onPressed: () => _mapController.move(_centro, 12.5), + ), + IconButton(icon: const Icon(Icons.refresh), onPressed: _cargarDatos), + ], + ), + body: _cargando + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + Expanded(flex: 3, child: _buildMapa()), + _buildLeyenda(), + ], + ), + ); + } + + Widget _buildMapa() { + return FlutterMap( + mapController: _mapController, + options: const MapOptions( + initialCenter: _centro, + initialZoom: 12.5, + minZoom: 11, + maxZoom: 17, + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'com.example.aplicacion_hack', + maxZoom: 19, + ), + PolylineLayer(polylines: _buildPolylines()), + MarkerLayer(markers: _buildMarkers()), + ], + ); + } + + List _buildPolylines() { + final list = []; + for (final ruta in _todasLasRutas) { + final esDelUsuario = _rutasDelUsuario.contains(ruta.routeId); + if (!_mostrarTodas && !esDelUsuario) continue; + + final estado = _estadoRuta(ruta.routeId); + final posActual = estado?.lastPositionId ?? 0; + final seleccionada = _rutaSeleccionada == ruta.routeId; + final grosorBase = esDelUsuario ? 5.0 : 2.0; + final grosor = seleccionada ? grosorBase + 2 : grosorBase; + + if (esDelUsuario && posActual > 1) { + // Tramo recorrido + final recorridos = ruta.puntos.take(posActual).toList(); + if (recorridos.length >= 2) { + list.add(Polyline( + points: recorridos, + color: ruta.color, + strokeWidth: grosor, + )); + } + // Tramo pendiente (punteado) + final pendientes = ruta.puntos.skip(posActual - 1).toList(); + if (pendientes.length >= 2) { + list.add(Polyline( + points: pendientes, + color: ruta.color.withValues(alpha: 0.35), + strokeWidth: grosor - 1, + pattern: StrokePattern.dashed(segments: [12, 8]), + )); + } + } else { + list.add(Polyline( + points: ruta.puntos, + color: esDelUsuario ? ruta.color : ruta.color.withValues(alpha: 0.2), + strokeWidth: grosor, + )); + } + } + return list; + } + + List _buildMarkers() { + final list = []; + + // Base / Relleno Sanitario + list.add(Marker( + point: LatLng(20.5111, -100.9037), + width: 70, + height: 36, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), + decoration: BoxDecoration( + color: Colors.grey.shade800, + borderRadius: BorderRadius.circular(5), + ), + child: const Text('🏭 Base', + style: TextStyle(color: Colors.white, fontSize: 9, fontWeight: FontWeight.bold)), + ), + Container(width: 2, height: 4, color: Colors.grey.shade800), + Container( + width: 8, height: 8, + decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), + ), + ], + ), + )); + + for (final ruta in _todasLasRutas) { + final esDelUsuario = _rutasDelUsuario.contains(ruta.routeId); + if (!esDelUsuario) continue; + + final estado = _estadoRuta(ruta.routeId); + final posActual = estado?.lastPositionId ?? 0; + + for (int i = 1; i < ruta.puntos.length - 1; i++) { + final punto = ruta.puntos[i]; + final posId = i + 1; + final esCamion = posId == posActual; + + if (esCamion) { + list.add(Marker( + point: punto, + width: 46, + height: 54, + child: GestureDetector( + onTap: () => setState(() => + _rutaSeleccionada = _rutaSeleccionada == ruta.routeId ? null : ruta.routeId), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), + decoration: BoxDecoration( + color: ruta.color, + borderRadius: BorderRadius.circular(5), + boxShadow: [BoxShadow(color: ruta.color.withValues(alpha: 0.5), blurRadius: 6)], + ), + child: Text( + ruta.routeId.replaceAll('RUTA-', 'R'), + style: const TextStyle(color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold), + ), + ), + const Text('🚛', style: TextStyle(fontSize: 20)), + ], + ), + ), + )); + } else { + list.add(Marker( + point: punto, + width: 14, + height: 14, + child: GestureDetector( + onTap: () => setState(() => + _rutaSeleccionada = _rutaSeleccionada == ruta.routeId ? null : ruta.routeId), + child: Container( + decoration: BoxDecoration( + color: posId < posActual ? ruta.color : Colors.white, + shape: BoxShape.circle, + border: Border.all(color: ruta.color, width: 2.5), + boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 2)], + ), + ), + ), + )); + } + } + } + return list; + } + + Widget _buildLeyenda() { + final rutasUsuario = _todasLasRutas + .where((r) => _rutasDelUsuario.contains(r.routeId)) + .toList(); + + if (rutasUsuario.isEmpty) { + return Container( + height: 52, + color: const Color(0xFF1B5E20), + alignment: Alignment.center, + child: const Text('Sin rutas asignadas', + style: TextStyle(color: Colors.white54, fontSize: 13)), + ); + } + + return Container( + height: 96, + color: const Color(0xFF1B5E20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(14, 5, 0, 2), + child: Text( + _mostrarTodas ? 'Todas las rutas • Las tuyas están resaltadas' : 'Tus rutas — toca para centrar', + style: const TextStyle(color: Colors.white54, fontSize: 10), + ), + ), + Expanded( + child: ListView.builder( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3), + itemCount: rutasUsuario.length, + itemBuilder: (context, i) { + final ruta = rutasUsuario[i]; + final estado = _estadoRuta(ruta.routeId); + final posActual = estado?.lastPositionId ?? 0; + final status = estado?.status ?? 'EN_RUTA'; + final seleccionada = _rutaSeleccionada == ruta.routeId; + + return GestureDetector( + onTap: () { + setState(() => + _rutaSeleccionada = seleccionada ? null : ruta.routeId); + if (!seleccionada && ruta.puntos.length > 1) { + _mapController.move(ruta.puntos[1], 13.5); + } + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + margin: const EdgeInsets.only(right: 8), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), + decoration: BoxDecoration( + color: seleccionada + ? ruta.color.withValues(alpha: 0.3) + : Colors.white.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: seleccionada ? ruta.color : Colors.white24, + width: seleccionada ? 2 : 1, + ), + ), + child: ClipRect( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 9, height: 9, + decoration: BoxDecoration(color: ruta.color, shape: BoxShape.circle), + ), + const SizedBox(width: 5), + Text(ruta.routeId, + style: TextStyle(color: ruta.color, fontWeight: FontWeight.bold, fontSize: 11)), + const SizedBox(width: 4), + Text(status == 'COMPLETADO' ? '✅' : '🚛', + style: const TextStyle(fontSize: 10)), + ], + ), + const SizedBox(height: 1), + SizedBox( + width: 115, + child: Text(ruta.colonia, + style: const TextStyle(color: Colors.white70, fontSize: 10), + overflow: TextOverflow.ellipsis), + ), + const SizedBox(height: 1), + SizedBox( + width: 115, + height: 3, + child: ClipRRect( + borderRadius: BorderRadius.circular(2), + child: LinearProgressIndicator( + value: (posActual / 8).clamp(0.0, 1.0), + backgroundColor: Colors.white12, + valueColor: AlwaysStoppedAnimation(ruta.color), + ), + ), + ), + const SizedBox(height: 1), + Text( + posActual > 0 ? 'Pos. $posActual / 8' : 'Sin datos', + style: TextStyle(color: ruta.color.withValues(alpha: 0.8), fontSize: 9), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/reporte_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/reporte_screen.dart new file mode 100644 index 0000000..e4005cf --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/reporte_screen.dart @@ -0,0 +1,538 @@ +// ================================================================ +// lib/screens/reporte_screen.dart +// Pantalla para que el ciudadano envíe reportes manuales +// ================================================================ +// +// NAVEGAR DESDE home_screen.dart: +// Navigator.pushNamed(context, '/reporte') +// +// AGREGAR EN main.dart: +// '/reporte': (context) => const ReporteScreen(), +// +// SECCIONES: +// 1. Formulario de nuevo reporte +// 2. Historial de reportes del usuario +// ================================================================ + +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; + +// ---------------------------------------------------------------- +// MODELOS +// ---------------------------------------------------------------- + +class ReporteInfo { + final int reporteId; + final String fecha; + final String hora; + final String colonia; + final String tipo; + final String? descripcion; + final String estado; + + ReporteInfo({ + required this.reporteId, + required this.fecha, + required this.hora, + required this.colonia, + required this.tipo, + this.descripcion, + required this.estado, + }); + + factory ReporteInfo.fromJson(Map j) => ReporteInfo( + reporteId: j['reporte_id'], + fecha: j['fecha'], + hora: j['hora'], + colonia: j['colonia'], + tipo: j['tipo'], + descripcion: j['descripcion'], + estado: j['estado'], + ); +} + +// ---------------------------------------------------------------- +// DATOS LOCALES +// ---------------------------------------------------------------- + +const _tiposReporte = [ + {'valor': 'CAMION_NO_PASO', 'label': 'El camión no pasó', 'emoji': '🚫', 'color': 0xFFC62828}, + {'valor': 'VOLUMEN_ALTO', 'label': 'Volumen inusualmente alto', 'emoji': '📦', 'color': 0xFFE65100}, + {'valor': 'BASURA_FUERA_HORARIO', 'label': 'Basura fuera de horario', 'emoji': '⏰', 'color': 0xFFF57F17}, + {'valor': 'OTRO', 'label': 'Otro problema', 'emoji': '📝', 'color': 0xFF37474F}, +]; + +const _colonias = [ + 'Zona Centro', 'Las Arboledas', 'Trojes', + 'San Juanico', 'Los Olivos', 'Rancho Seco', 'Las Insurgentes', +]; + +// ================================================================ +// PANTALLA PRINCIPAL +// ================================================================ +class ReporteScreen extends StatefulWidget { + const ReporteScreen({super.key}); + + @override + State createState() => _ReporteScreenState(); +} + +class _ReporteScreenState extends State + with SingleTickerProviderStateMixin { + static const _baseUrl = 'http://192.168.198.55:8000'; + + late TabController _tabController; + int? _usuarioId; + + // Formulario + String? _tipoSeleccionado; + String? _coloniaSeleccionada; + final _descController = TextEditingController(); + bool _enviando = false; + + // Historial + List _reportes = []; + bool _cargandoHistorial = true; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + _cargarUsuario(); + } + + @override + void dispose() { + _tabController.dispose(); + _descController.dispose(); + super.dispose(); + } + + Future _cargarUsuario() async { + final prefs = await SharedPreferences.getInstance(); + final id = prefs.getInt('usuario_id'); + if (id != null && mounted) { + setState(() => _usuarioId = id); + _cargarHistorial(); + } + } + + Future _cargarHistorial() async { + if (_usuarioId == null) return; + setState(() => _cargandoHistorial = true); + try { + final resp = await http + .get(Uri.parse('$_baseUrl/api/reportes/usuario/$_usuarioId')) + .timeout(const Duration(seconds: 10)); + if (resp.statusCode == 200 && mounted) { + final lista = json.decode(resp.body) as List; + setState(() { + _reportes = lista.map((r) => ReporteInfo.fromJson(r)).toList(); + _cargandoHistorial = false; + }); + } else { + if (mounted) setState(() => _cargandoHistorial = false); + } + } catch (_) { + if (mounted) setState(() => _cargandoHistorial = false); + } + } + + Future _enviarReporte() async { + if (_tipoSeleccionado == null) { + _snack('Selecciona el tipo de problema.', error: true); + return; + } + if (_coloniaSeleccionada == null) { + _snack('Selecciona la colonia.', error: true); + return; + } + if (_usuarioId == null) { + _snack('No hay sesión activa.', error: true); + return; + } + + setState(() => _enviando = true); + + try { + final resp = await http.post( + Uri.parse('$_baseUrl/api/reportes?usuario_id=$_usuarioId'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({ + 'colonia': _coloniaSeleccionada, + 'tipo': _tipoSeleccionado, + 'descripcion': _descController.text.trim().isEmpty + ? null + : _descController.text.trim(), + }), + ).timeout(const Duration(seconds: 10)); + + if (resp.statusCode == 200 && mounted) { + _snack('✅ Reporte enviado correctamente. ¡Gracias!'); + setState(() { + _tipoSeleccionado = null; + _coloniaSeleccionada = null; + }); + _descController.clear(); + _cargarHistorial(); + _tabController.animateTo(1); // ir al historial + } else { + final body = json.decode(resp.body); + _snack(body['detail'] ?? 'Error al enviar el reporte.', error: true); + } + } catch (e) { + _snack('Sin conexión al servidor.', error: true); + } finally { + if (mounted) setState(() => _enviando = false); + } + } + + void _snack(String msg, {bool error = false}) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(msg), + backgroundColor: error ? Colors.red.shade700 : Colors.green.shade700, + behavior: SnackBarBehavior.floating, + )); + } + + // ================================================================ + // BUILD + // ================================================================ + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF5F7F5), + appBar: AppBar( + title: const Text('Reportes ciudadanos'), + backgroundColor: const Color(0xFF1B5E20), + foregroundColor: Colors.white, + bottom: TabBar( + controller: _tabController, + indicatorColor: Colors.white, + labelColor: Colors.white, + unselectedLabelColor: Colors.white60, + tabs: const [ + Tab(icon: Icon(Icons.add_circle_outline), text: 'Nuevo reporte'), + Tab(icon: Icon(Icons.history), text: 'Mis reportes'), + ], + ), + ), + body: TabBarView( + controller: _tabController, + children: [_buildFormulario(), _buildHistorial()], + ), + ); + } + + // ================================================================ + // TAB 1: FORMULARIO + // ================================================================ + Widget _buildFormulario() { + return SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Encabezado + Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF2E7D32).withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: const Color(0xFF2E7D32).withValues(alpha: 0.2)), + ), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('📋 Envía un reporte', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox(height: 6), + Text( + 'Tu reporte se guarda en la base de datos y ayuda a mejorar ' + 'la logística de recolección en tu colonia.', + style: TextStyle(fontSize: 13, color: Colors.black54, height: 1.4), + ), + ], + ), + ), + + const SizedBox(height: 24), + _Label('¿Qué problema ocurrió?'), + const SizedBox(height: 10), + + // Selector de tipo + ...(_tiposReporte.map((tipo) { + final seleccionado = _tipoSeleccionado == tipo['valor']; + final color = Color(tipo['color'] as int); + return GestureDetector( + onTap: () => setState(() => _tipoSeleccionado = tipo['valor'] as String), + child: AnimatedContainer( + duration: const Duration(milliseconds: 150), + margin: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + decoration: BoxDecoration( + color: seleccionado ? color.withValues(alpha: 0.1) : Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: seleccionado ? color : Colors.grey.shade200, + width: seleccionado ? 2 : 1, + ), + boxShadow: seleccionado + ? [BoxShadow(color: color.withValues(alpha: 0.15), blurRadius: 8)] + : [BoxShadow(color: Colors.black.withValues(alpha: 0.04), blurRadius: 4)], + ), + child: Row( + children: [ + Text(tipo['emoji'] as String, style: const TextStyle(fontSize: 22)), + const SizedBox(width: 12), + Expanded( + child: Text( + tipo['label'] as String, + style: TextStyle( + fontSize: 14, + fontWeight: seleccionado ? FontWeight.bold : FontWeight.normal, + color: seleccionado ? color : Colors.black87, + ), + ), + ), + if (seleccionado) + Icon(Icons.check_circle, color: color, size: 20), + ], + ), + ), + ); + })), + + const SizedBox(height: 20), + _Label('¿En qué colonia?'), + const SizedBox(height: 10), + + // Selector de colonia + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade200), + ), + child: DropdownButtonFormField( + value: _coloniaSeleccionada, + hint: const Text('Selecciona tu colonia'), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.location_city_outlined), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + ), + items: _colonias + .map((c) => DropdownMenuItem(value: c, child: Text(c))) + .toList(), + onChanged: (v) => setState(() => _coloniaSeleccionada = v), + ), + ), + + const SizedBox(height: 20), + _Label('Descripción adicional (opcional)'), + const SizedBox(height: 10), + + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade200), + ), + child: TextField( + controller: _descController, + maxLines: 4, + maxLength: 300, + decoration: const InputDecoration( + hintText: 'Describe el problema con más detalle...', + border: InputBorder.none, + contentPadding: EdgeInsets.all(16), + ), + ), + ), + + const SizedBox(height: 28), + + // Botón enviar + SizedBox( + width: double.infinity, + height: 54, + child: ElevatedButton.icon( + onPressed: _enviando ? null : _enviarReporte, + icon: _enviando + ? const SizedBox( + width: 20, height: 20, + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) + : const Icon(Icons.send_rounded), + label: Text(_enviando ? 'Enviando...' : 'Enviar reporte'), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2E7D32), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + ), + + const SizedBox(height: 16), + Center( + child: Text( + 'Tu reporte es anónimo para el operador y ayuda\na mejorar el servicio de recolección.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 12, color: Colors.grey.shade500), + ), + ), + const SizedBox(height: 20), + ], + ), + ); + } + + // ================================================================ + // TAB 2: HISTORIAL + // ================================================================ + Widget _buildHistorial() { + if (_cargandoHistorial) { + return const Center(child: CircularProgressIndicator()); + } + + if (_reportes.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.inbox_outlined, size: 72, color: Colors.grey.shade300), + const SizedBox(height: 16), + const Text('No has enviado reportes aún', + style: TextStyle(fontSize: 16, color: Colors.grey)), + const SizedBox(height: 8), + const Text('Usa la pestaña anterior para reportar un problema.', + style: TextStyle(fontSize: 13, color: Colors.grey)), + ], + ), + ); + } + + return RefreshIndicator( + onRefresh: _cargarHistorial, + child: ListView.builder( + padding: const EdgeInsets.all(16), + itemCount: _reportes.length, + itemBuilder: (context, i) => _TarjetaReporte(reporte: _reportes[i]), + ), + ); + } +} + +// ================================================================ +// WIDGETS AUXILIARES +// ================================================================ + +class _Label extends StatelessWidget { + final String text; + const _Label(this.text); + + @override + Widget build(BuildContext context) => Text( + text, + style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), + ); +} + +class _TarjetaReporte extends StatelessWidget { + final ReporteInfo reporte; + const _TarjetaReporte({required this.reporte}); + + Map get _tipoInfo { + return _tiposReporte.firstWhere( + (t) => t['valor'] == reporte.tipo, + orElse: () => {'label': reporte.tipo, 'emoji': '📝', 'color': 0xFF37474F}, + ); + } + + Color get _colorEstado => reporte.estado == 'ATENDIDO' + ? const Color(0xFF2E7D32) + : const Color(0xFFE65100); + + @override + Widget build(BuildContext context) { + final info = _tipoInfo; + final color = Color(info['color'] as int); + + return Container( + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(14), + boxShadow: [ + BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6) + ], + border: Border(left: BorderSide(color: color, width: 4)), + ), + child: Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text(info['emoji'] as String, style: const TextStyle(fontSize: 20)), + const SizedBox(width: 8), + Expanded( + child: Text(info['label'] as String, + style: TextStyle( + fontWeight: FontWeight.bold, color: color, fontSize: 14)), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: _colorEstado.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + reporte.estado == 'ATENDIDO' ? '✅ Atendido' : '⏳ Pendiente', + style: TextStyle( + color: _colorEstado, fontSize: 11, fontWeight: FontWeight.bold), + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon(Icons.location_city_outlined, size: 14, color: Colors.grey.shade500), + const SizedBox(width: 4), + Text(reporte.colonia, + style: TextStyle(color: Colors.grey.shade600, fontSize: 13)), + const SizedBox(width: 16), + Icon(Icons.calendar_today_outlined, size: 14, color: Colors.grey.shade500), + const SizedBox(width: 4), + Text('${reporte.fecha} ${reporte.hora.substring(0, 5)}', + style: TextStyle(color: Colors.grey.shade600, fontSize: 13)), + ], + ), + if (reporte.descripcion != null && reporte.descripcion!.isNotEmpty) ...[ + const SizedBox(height: 8), + Container( + width: double.infinity, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.grey.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + reporte.descripcion!, + style: TextStyle(fontSize: 13, color: Colors.grey.shade700, height: 1.4), + ), + ), + ], + ], + ), + ), + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/screens/route_list_screen.dart b/HackOnLinces_app/aplicacion_hack/lib/screens/route_list_screen.dart new file mode 100644 index 0000000..f8ae98a --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/screens/route_list_screen.dart @@ -0,0 +1,354 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../services/api_service.dart'; + +class RouteListScreen extends StatefulWidget { + const RouteListScreen({super.key}); + + @override + State createState() => _RouteListScreenState(); +} + +class _RouteListScreenState extends State { + final ApiService _apiService = ApiService(); + bool _cargando = true; + bool _avanzando = false; + String? _error; + List _rutas = []; + int? _usuarioId; + + @override + void initState() { + super.initState(); + _cargarUsuarioYRutas(); + } + + Future _cargarUsuarioYRutas() async { + final prefs = await SharedPreferences.getInstance(); + final usuarioId = prefs.getInt('usuario_id'); + + if (usuarioId == null) { + if (mounted) { + setState(() { + _error = + 'No se encontró sesión activa. Por favor inicia sesión de nuevo.'; + _cargando = false; + }); + } + return; + } + + _usuarioId = usuarioId; + await _cargarRutas(); + } + + Future _cargarRutas() async { + setState(() { + _cargando = true; + _error = null; + }); + + if (_usuarioId == null) { + if (mounted) { + setState(() { + _error = + 'No se encontró sesión activa. Por favor inicia sesión de nuevo.'; + _cargando = false; + }); + } + return; + } + + try { + final rutas = await _apiService.obtenerRutas(_usuarioId!); + if (mounted) { + setState(() { + _rutas = rutas; + }); + } + } catch (e) { + if (mounted) { + setState(() { + _error = 'No se pudieron cargar las rutas. Verifica el backend.'; + }); + } + } finally { + if (mounted) { + setState(() { + _cargando = false; + }); + } + } + } + + Future _simularAvance(String routeId) async { + setState(() { + _avanzando = true; + }); + + if (_usuarioId == null) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: + Text('No se encontró sesión activa. Inicia sesión de nuevo.'), + backgroundColor: Colors.redAccent, + ), + ); + } + return; + } + + try { + final rutaActualizada = + await _apiService.avanzarRuta(routeId, _usuarioId!); + if (mounted) { + setState(() { + final index = _rutas.indexWhere((r) => r.routeId == routeId); + if (index >= 0) { + _rutas[index] = rutaActualizada; + } + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'La ruta ${rutaActualizada.routeId} avanzó al siguiente tramo.'), + duration: const Duration(seconds: 3), + ), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Error al simular avance: $e'), + backgroundColor: Colors.red.shade700, + ), + ); + } + } finally { + if (mounted) { + setState(() { + _avanzando = false; + }); + } + } + } + + Color _colorEstado(String status) { + switch (status) { + case 'COMPLETADO': + return Colors.blue.shade600; + case 'EN_RUTA': + return Colors.green.shade600; + default: + return Colors.orange.shade600; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Rutas y estado del camión'), + actions: [ + IconButton( + onPressed: () => Navigator.pushNamed(context, '/mapa'), + icon: const Icon(Icons.map_rounded), + tooltip: 'Ver mapa de rutas', + ), + IconButton( + onPressed: _cargarRutas, + icon: const Icon(Icons.refresh), + ), + ], + ), + body: _cargando + ? const Center(child: CircularProgressIndicator()) + : _error != null + ? Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.error_outline, + size: 72, color: Colors.redAccent), + const SizedBox(height: 16), + Text( + _error!, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: _cargarRutas, + child: const Text('Reintentar'), + ), + ], + ), + ), + ) + : RefreshIndicator( + onRefresh: _cargarRutas, + child: ListView( + padding: const EdgeInsets.all(16), + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: Colors.green.shade50, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.green.shade200), + ), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Simulación de rutas', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold), + ), + SizedBox(height: 10), + Text( + 'Esta pantalla muestra el estado actual de cada camión y permite simular el siguiente tramo de la ruta para pruebas.', + style: TextStyle(fontSize: 14), + ), + ], + ), + ), + const SizedBox(height: 16), + ..._rutas.map((ruta) { + return Padding( + padding: const EdgeInsets.only(bottom: 14), + child: Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + ruta.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + Chip( + label: Text(ruta.status), + backgroundColor: + _colorEstado(ruta.status) + .withValues(alpha: 0.15), + labelStyle: TextStyle( + color: _colorEstado(ruta.status), + fontWeight: FontWeight.w600, + ), + ), + ], + ), + const SizedBox(height: 10), + Wrap( + runSpacing: 6, + spacing: 8, + children: [ + _buildTag('Ruta: ${ruta.routeId}', + Colors.grey.shade200), + _buildTag( + 'Posición: ${ruta.lastPositionId}', + Colors.blue.shade50), + _buildTag( + ruta.gpsOk + ? 'GPS OK' + : 'GPS desconectado', + ruta.gpsOk + ? Colors.green.shade50 + : Colors.red.shade50, + ), + ], + ), + const SizedBox(height: 12), + Text( + 'Último reporte: ${ruta.lastTimestamp}', + style: TextStyle( + color: Colors.grey.shade700, + fontSize: 13), + ), + const SizedBox(height: 16), + ElevatedButton.icon( + onPressed: ruta.status == 'COMPLETADO' || + _avanzando + ? null + : () => _simularAvance(ruta.routeId), + icon: const Icon(Icons.play_arrow), + label: const Text('Simular avance'), + ), + ], + ), + ), + ), + ); + }), + const SizedBox(height: 16), + Container( + width: double.infinity, + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.grey.shade300), + ), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Información relevante', + style: TextStyle( + fontSize: 17, fontWeight: FontWeight.bold), + ), + SizedBox(height: 12), + Text( + '• Separa correctamente orgánicos, reciclables y no reciclables.', + style: TextStyle(fontSize: 14)), + SizedBox(height: 8), + Text( + '• No mezcles basura húmeda con envases secos y mantén los líquidos controlados.', + style: TextStyle(fontSize: 14)), + SizedBox(height: 8), + Text( + '• Saca tu basura cuando el camión esté cerca: así evitamos plagas y malos olores.', + style: TextStyle(fontSize: 14)), + SizedBox(height: 8), + Text( + '• Usa bolsas resistentes y cierra bien los residuos antes de ponerlos en la acera.', + style: TextStyle(fontSize: 14)), + ], + ), + ), + const SizedBox(height: 24), + ], + ), + ), + ); + } + + Widget _buildTag(String text, Color color) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(12), + ), + child: Text( + text, + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600), + ), + ); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/lib/services/api_service.dart b/HackOnLinces_app/aplicacion_hack/lib/services/api_service.dart new file mode 100644 index 0000000..d44912e --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/lib/services/api_service.dart @@ -0,0 +1,483 @@ +// ================================================================ +// lib/services/api_service.dart (v2) +// Servicio de comunicación con el backend FastAPI +// ================================================================ +// +// CAMBIOS v2: +// - LoginResponse incluye 'nombre' del usuario +// - ActualizarPassword requiere password_actual + password_nuevo +// - Nuevos métodos: obtenerDashboard, historialPosiciones, +// resumenRutas, estadisticasColonias +// ================================================================ + +import 'dart:convert'; +import 'package:http/http.dart' as http; + +// ---------------------------------------------------------------- +// MODELOS +// ---------------------------------------------------------------- + +class ETAInfo { + final int usuarioId; + final String colonia; + final String rutaNombre; + final String rutaStatus; + final bool gpsOk; + final String etaTexto; + final int etaMinutos; + final String mensajePreventivo; + + ETAInfo({ + required this.usuarioId, + required this.colonia, + required this.rutaNombre, + required this.rutaStatus, + required this.gpsOk, + required this.etaTexto, + required this.etaMinutos, + required this.mensajePreventivo, + }); + + factory ETAInfo.fromJson(Map json) { + return ETAInfo( + usuarioId: json['usuario_id'], + colonia: json['colonia'], + rutaNombre: json['ruta_nombre'] ?? '', + rutaStatus: json['ruta_status'] ?? '', + gpsOk: json['gps_ok'] ?? true, + etaTexto: json['eta_texto'], + etaMinutos: json['eta_minutos'], + mensajePreventivo: json['mensaje_preventivo'], + ); + } +} + +class DireccionInfo { + final String colonia; + final String direccion; + + DireccionInfo({required this.colonia, required this.direccion}); + + factory DireccionInfo.fromJson(Map json) { + return DireccionInfo(colonia: json['colonia'], direccion: json['direccion']); + } +} + +class UsuarioInfo { + final int usuarioId; + final String nombre; + final String email; + final List direcciones; + + UsuarioInfo({ + required this.usuarioId, + required this.nombre, + required this.email, + required this.direcciones, + }); + + factory UsuarioInfo.fromJson(Map json) { + return UsuarioInfo( + usuarioId: json['usuario_id'], + nombre: json['nombre'], + email: json['email'], + direcciones: List>.from(json['direcciones']) + .map(DireccionInfo.fromJson) + .toList(), + ); + } +} + +class RouteInfo { + final String routeId; + final String name; + final String status; + final int lastPositionId; + final String lastTimestamp; + final bool gpsOk; + + RouteInfo({ + required this.routeId, + required this.name, + required this.status, + required this.lastPositionId, + required this.lastTimestamp, + required this.gpsOk, + }); + + factory RouteInfo.fromJson(Map json) { + return RouteInfo( + routeId: json['route_id'], + name: json['name'], + status: json['status'], + lastPositionId: json['last_position_id'], + lastTimestamp: json['last_timestamp'], + gpsOk: json['gps_ok'], + ); + } +} + +/// Posición GPS individual de la ruta de un camión. +class PosicionGPS { + final int positionId; + final double lat; + final double lng; + final int speed; + final String timestamp; + final bool esActual; // true = aquí está el camión ahora + + PosicionGPS({ + required this.positionId, + required this.lat, + required this.lng, + required this.speed, + required this.timestamp, + required this.esActual, + }); + + factory PosicionGPS.fromJson(Map json) { + return PosicionGPS( + positionId: json['position_id'], + lat: (json['lat'] as num).toDouble(), + lng: (json['lng'] as num).toDouble(), + speed: json['speed'], + timestamp: json['timestamp'], + esActual: json['es_actual'] ?? false, + ); + } +} + +/// Detalle de una ruta para el dashboard. +class RutaDetalle { + final String routeId; + final String name; + final String status; + final int truckId; + final int posicionActual; + final int totalPosiciones; + final double porcentajeCompletado; + final int etaMinutos; + final bool gpsOk; + final int usuariosEnRuta; + + RutaDetalle({ + required this.routeId, + required this.name, + required this.status, + required this.truckId, + required this.posicionActual, + required this.totalPosiciones, + required this.porcentajeCompletado, + required this.etaMinutos, + required this.gpsOk, + required this.usuariosEnRuta, + }); + + factory RutaDetalle.fromJson(Map json) { + return RutaDetalle( + routeId: json['route_id'], + name: json['name'], + status: json['status'], + truckId: json['truck_id'], + posicionActual: json['posicion_actual'], + totalPosiciones: json['total_posiciones'], + porcentajeCompletado: (json['porcentaje_completado'] as num).toDouble(), + etaMinutos: json['eta_minutos'], + gpsOk: json['gps_ok'], + usuariosEnRuta: json['usuarios_en_ruta'], + ); + } +} + +/// Respuesta completa del dashboard de operador. +class DashboardInfo { + final int totalRutas; + final int rutasEnProgreso; + final int rutasCompletadas; + final int totalUsuarios; + final int usuariosConToken; + final double coberturaNotificaciones; + final List rutas; + + DashboardInfo({ + required this.totalRutas, + required this.rutasEnProgreso, + required this.rutasCompletadas, + required this.totalUsuarios, + required this.usuariosConToken, + required this.coberturaNotificaciones, + required this.rutas, + }); + + factory DashboardInfo.fromJson(Map json) { + return DashboardInfo( + totalRutas: json['total_rutas'], + rutasEnProgreso: json['rutas_en_progreso'], + rutasCompletadas: json['rutas_completadas'], + totalUsuarios: json['total_usuarios'], + usuariosConToken: json['usuarios_con_token'], + coberturaNotificaciones: (json['cobertura_notificaciones'] as num).toDouble(), + rutas: List>.from(json['rutas']) + .map(RutaDetalle.fromJson) + .toList(), + ); + } +} + +/// Estadísticas de una colonia. +class ColoniaEstadistica { + final String colonia; + final String routeId; + final String rutaNombre; + final String horario; + final int totalUsuarios; + final int usuariosConNotificaciones; + + ColoniaEstadistica({ + required this.colonia, + required this.routeId, + required this.rutaNombre, + required this.horario, + required this.totalUsuarios, + required this.usuariosConNotificaciones, + }); + + factory ColoniaEstadistica.fromJson(Map json) { + return ColoniaEstadistica( + colonia: json['colonia'], + routeId: json['route_id'], + rutaNombre: json['ruta_nombre'], + horario: json['horario'], + totalUsuarios: json['total_usuarios'], + usuariosConNotificaciones: json['usuarios_con_notificaciones'], + ); + } +} + +// ---------------------------------------------------------------- +// CLASE PRINCIPAL: ApiService +// ---------------------------------------------------------------- +class ApiService { + // ============================================================ + // BASE URL — Cambia solo esta línea para apuntar a otro entorno + // Android emulator local: http://10.0.2.2:8000 + // Dispositivo físico (red local): http://192.168.X.X:8000 + // ============================================================ + static const String _baseUrl = 'http://192.168.198.55:8000'; + static const Duration _timeout = Duration(seconds: 10); + + // ---------------------------------------------------------------- + // HELPER PRIVADO: maneja errores HTTP de forma consistente + // ---------------------------------------------------------------- + Never _throwError(http.Response response) { + Map body = {}; + try { + body = json.decode(response.body); + } catch (_) {} + final detail = body['detail'] ?? response.body; + throw Exception(detail); + } + + // ================================================================ + // AUTENTICACIÓN + // ================================================================ + + /// Login con email y contraseña. Retorna [usuarioId, nombre]. + Future> loginConCorreo(String email, String password) async { + final response = await http.post( + Uri.parse('$_baseUrl/api/usuarios/login'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({'email': email.trim().toLowerCase(), 'password': password}), + ).timeout(_timeout); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + return {'usuario_id': data['usuario_id'], 'nombre': data['nombre']}; + } + _throwError(response); + } + + Future registrarUsuario( + String nombre, + String email, + String password, + String direccion, + String colonia, + ) async { + final response = await http.post( + Uri.parse('$_baseUrl/api/usuarios/register'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({ + 'nombre': nombre.trim(), + 'email': email.trim().toLowerCase(), + 'password': password, + 'colonia': colonia, + 'direccion': direccion.trim(), + }), + ).timeout(_timeout); + + if (response.statusCode == 200) { + return json.decode(response.body)['usuario_id']; + } + _throwError(response); + } + + // ================================================================ + // USUARIOS + // ================================================================ + + Future obtenerETA(int usuarioId) async { + final response = await http + .get(Uri.parse('$_baseUrl/api/eta/$usuarioId')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return ETAInfo.fromJson(json.decode(response.body)); + } else if (response.statusCode == 404) { + throw Exception('Usuario no encontrado. ¿Corriste /api/seed en el backend?'); + } + _throwError(response); + } + + Future> obtenerColonias() async { + final response = await http + .get(Uri.parse('$_baseUrl/api/colonias')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return List.from(json.decode(response.body)['colonias']); + } + _throwError(response); + } + + Future obtenerUsuario(int usuarioId) async { + final response = await http + .get(Uri.parse('$_baseUrl/api/usuarios/$usuarioId')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return UsuarioInfo.fromJson(json.decode(response.body)); + } + _throwError(response); + } + + Future agregarDireccion(int usuarioId, String colonia, String direccion) async { + final response = await http.post( + Uri.parse('$_baseUrl/api/usuarios/$usuarioId/direcciones'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({'colonia': colonia, 'direccion': direccion.trim()}), + ).timeout(_timeout); + + if (response.statusCode != 200) _throwError(response); + } + + /// Actualiza contraseña. Requiere la contraseña actual como confirmación. + Future actualizarPassword(int usuarioId, String passwordActual, String passwordNuevo) async { + final response = await http.put( + Uri.parse('$_baseUrl/api/usuarios/$usuarioId/password'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({ + 'password_actual': passwordActual, + 'password_nuevo': passwordNuevo, + }), + ).timeout(_timeout); + + if (response.statusCode != 200) _throwError(response); + } + + Future registrarFcmToken(int usuarioId, String fcmToken) async { + final response = await http.put( + Uri.parse('$_baseUrl/api/usuarios/$usuarioId/fcm-token'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({'fcm_token': fcmToken}), + ).timeout(_timeout); + + if (response.statusCode != 200) { + throw Exception('Error registrando FCM token: ${response.statusCode}'); + } + } + + // ================================================================ + // RUTAS + // ================================================================ + + Future> obtenerRutas(int usuarioId) async { + final response = await http + .get(Uri.parse('$_baseUrl/api/rutas?usuario_id=$usuarioId')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return List>.from(json.decode(response.body)['rutas']) + .map(RouteInfo.fromJson) + .toList(); + } + _throwError(response); + } + + Future avanzarRuta(String routeId, int usuarioId) async { + final response = await http + .post(Uri.parse('$_baseUrl/api/rutas/$routeId/avanzar?usuario_id=$usuarioId')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return RouteInfo.fromJson(json.decode(response.body)); + } + _throwError(response); + } + + // ================================================================ + // VISUALIZACIÓN — NUEVOS EN v2 + // ================================================================ + + /// Dashboard global: estado de todas las rutas + métricas de usuarios. + Future obtenerDashboard() async { + final response = await http + .get(Uri.parse('$_baseUrl/api/dashboard')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return DashboardInfo.fromJson(json.decode(response.body)); + } + _throwError(response); + } + + /// Historial de posiciones GPS de una ruta, con la posición actual marcada. + Future> historialPosiciones(String routeId) async { + final response = await http + .get(Uri.parse('$_baseUrl/api/rutas/$routeId/posiciones')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return List>.from(json.decode(response.body)) + .map(PosicionGPS.fromJson) + .toList(); + } + _throwError(response); + } + + /// Vista rápida y ligera de todas las rutas. Ideal para polling frecuente. + Future>> resumenRutas() async { + final response = await http + .get(Uri.parse('$_baseUrl/api/rutas/resumen')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return List>.from(json.decode(response.body)['rutas']); + } + _throwError(response); + } + + /// Estadísticas por colonia: usuarios y cobertura de notificaciones. + Future> estadisticasColonias() async { + final response = await http + .get(Uri.parse('$_baseUrl/api/estadisticas/colonias')) + .timeout(_timeout); + + if (response.statusCode == 200) { + return List>.from(json.decode(response.body)) + .map(ColoniaEstadistica.fromJson) + .toList(); + } + _throwError(response); + } +} diff --git a/HackOnLinces_app/aplicacion_hack/linux/.gitignore b/HackOnLinces_app/aplicacion_hack/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/HackOnLinces_app/aplicacion_hack/linux/CMakeLists.txt b/HackOnLinces_app/aplicacion_hack/linux/CMakeLists.txt new file mode 100644 index 0000000..39af11c --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "aplicacion_hack") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.aplicacion_hack") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/HackOnLinces_app/aplicacion_hack/linux/flutter/CMakeLists.txt b/HackOnLinces_app/aplicacion_hack/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/HackOnLinces_app/aplicacion_hack/linux/runner/CMakeLists.txt b/HackOnLinces_app/aplicacion_hack/linux/runner/CMakeLists.txt new file mode 100644 index 0000000..e97dabc --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/HackOnLinces_app/aplicacion_hack/linux/runner/main.cc b/HackOnLinces_app/aplicacion_hack/linux/runner/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/HackOnLinces_app/aplicacion_hack/linux/runner/my_application.cc b/HackOnLinces_app/aplicacion_hack/linux/runner/my_application.cc new file mode 100644 index 0000000..0b12c61 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/runner/my_application.cc @@ -0,0 +1,148 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView* view) { + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "aplicacion_hack"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "aplicacion_hack"); + } + + gtk_window_set_default_size(window, 1280, 720); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 + // for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), + self); + gtk_widget_realize(GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/HackOnLinces_app/aplicacion_hack/linux/runner/my_application.h b/HackOnLinces_app/aplicacion_hack/linux/runner/my_application.h new file mode 100644 index 0000000..db16367 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/linux/runner/my_application.h @@ -0,0 +1,21 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, + my_application, + MY, + APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/HackOnLinces_app/aplicacion_hack/macos/.gitignore b/HackOnLinces_app/aplicacion_hack/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Debug.xcconfig b/HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Release.xcconfig b/HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.pbxproj b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..58acfd7 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,729 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* aplicacion_hack.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "aplicacion_hack.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* aplicacion_hack.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* aplicacion_hack.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/aplicacion_hack.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/aplicacion_hack"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/aplicacion_hack.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/aplicacion_hack"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.aplicacionHack.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/aplicacion_hack.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/aplicacion_hack"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ec7c5e3 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/contents.xcworkspacedata b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/AppDelegate.swift b/HackOnLinces_app/aplicacion_hack/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..b3c1761 --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/HackOnLinces_app/aplicacion_hack/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + +