From 6e7d3d3d45f37fd057017d6158d65e4e291feaec Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 17 Oct 2026 05:15:58 +0300 Subject: [PATCH 01/26] Morsel: start from a renamed copy of Wayline Ride becomes Delivery, Rider Customer, Driver Courier and Driving Onboarding; the order domain is built on top in the commits that follow. Co-Authored-By: Claude Opus 5.5 --- scripts/morsel/LICENSE | 22 + scripts/morsel/README.adoc | 219 +++ .../morsel/backend/application-dev.properties | 30 + .../backend/application-test.properties | 20 + scripts/morsel/backend/application.properties | 98 + scripts/morsel/backend/pom.xml | 160 ++ scripts/morsel/backend/server.sh | 112 ++ .../codenameone/examples/morsel/Caller.java | 63 + .../com/codenameone/examples/morsel/Days.java | 123 ++ .../com/codenameone/examples/morsel/Ids.java | 53 + .../codenameone/examples/morsel/Roles.java | 31 + .../com/codenameone/examples/morsel/Rows.java | 78 + .../examples/morsel/SecurityConfig.java | 413 ++++ .../codenameone/examples/morsel/Services.java | 242 +++ .../examples/morsel/SignInCodePage.java | 67 + .../com/codenameone/examples/morsel/Text.java | 83 + .../morsel/account/AccountCleanup.java | 119 ++ .../morsel/account/AccountEndpoint.java | 124 ++ .../examples/morsel/account/Accounts.java | 650 +++++++ .../examples/morsel/account/DemoAccounts.java | 306 +++ .../examples/morsel/account/Grants.java | 91 + .../examples/morsel/account/Moderation.java | 98 + .../morsel/account/PhoneVerification.java | 262 +++ .../examples/morsel/account/Preferences.java | 143 ++ .../examples/morsel/admin/AdminEndpoint.java | 193 ++ .../examples/morsel/admin/Statistics.java | 312 +++ .../morsel/courier/CourierEndpoint.java | 134 ++ .../examples/morsel/courier/Couriers.java | 326 ++++ .../examples/morsel/courier/Earnings.java | 296 +++ .../morsel/delivery/CustomerEndpoint.java | 94 + .../examples/morsel/delivery/Deliveries.java | 1066 +++++++++++ .../morsel/delivery/DeliveryStateMachine.java | 99 + .../morsel/delivery/DemoDeliveries.java | 296 +++ .../examples/morsel/delivery/Fares.java | 218 +++ .../examples/morsel/delivery/Matcher.java | 261 +++ .../examples/morsel/domain/ClosedAccount.java | 54 + .../morsel/domain/CourierApplication.java | 251 +++ .../morsel/domain/CourierDocument.java | 99 + .../examples/morsel/domain/CourierHours.java | 65 + .../examples/morsel/domain/CourierState.java | 124 ++ .../examples/morsel/domain/Delivery.java | 413 ++++ .../morsel/domain/DeliveryActivity.java | 90 + .../examples/morsel/domain/DeliveryEvent.java | 87 + .../morsel/domain/DeliveryPassed.java | 54 + .../examples/morsel/domain/LiveTicket.java | 64 + .../morsel/domain/ModerationEvent.java | 97 + .../examples/morsel/domain/Payment.java | 152 ++ .../morsel/domain/PaymentCustomer.java | 53 + .../examples/morsel/domain/PaymentMethod.java | 131 ++ .../examples/morsel/domain/PaymentSetup.java | 86 + .../examples/morsel/domain/Payout.java | 97 + .../examples/morsel/domain/PayoutAccount.java | 75 + .../examples/morsel/domain/PhoneCode.java | 122 ++ .../examples/morsel/domain/Preference.java | 163 ++ .../examples/morsel/domain/Pricing.java | 163 ++ .../examples/morsel/domain/Profile.java | 199 ++ .../examples/morsel/domain/PushDevice.java | 101 + .../codenameone/examples/morsel/geo/Geo.java | 46 + .../examples/morsel/geo/GeoEndpoint.java | 75 + .../examples/morsel/geo/Geocoder.java | 38 + .../examples/morsel/geo/PhotonGeocoder.java | 130 ++ .../examples/morsel/geo/StubGeocoder.java | 86 + .../examples/morsel/live/LiveEndpoint.java | 117 ++ .../examples/morsel/live/LiveHub.java | 182 ++ .../examples/morsel/live/LiveTickets.java | 103 + .../morsel/onboarding/Applications.java | 660 +++++++ .../morsel/onboarding/OnboardingEndpoint.java | 68 + .../morsel/pay/MisconfiguredPayments.java | 96 + .../examples/morsel/pay/PayReturnPage.java | 53 + .../morsel/pay/PaymentDeclinedException.java | 31 + .../examples/morsel/pay/PaymentEndpoint.java | 96 + .../examples/morsel/pay/PaymentProvider.java | 123 ++ .../examples/morsel/pay/Payments.java | 1116 +++++++++++ .../morsel/pay/SimulatedPayments.java | 210 ++ .../examples/morsel/pay/StripePayments.java | 390 ++++ .../examples/morsel/push/CloudPushSender.java | 80 + .../morsel/push/ConsolePushSender.java | 43 + .../examples/morsel/push/DeliveryPushes.java | 543 ++++++ .../morsel/push/MisconfiguredPushSender.java | 47 + .../examples/morsel/push/PushDevices.java | 172 ++ .../examples/morsel/push/PushSender.java | 42 + .../morsel/push/RecordingPushSender.java | 123 ++ .../repository/ClosedAccountRepository.java | 29 + .../CourierApplicationRepository.java | 103 + .../repository/CourierDocumentRepository.java | 69 + .../repository/CourierHoursRepository.java | 44 + .../repository/CourierStateRepository.java | 91 + .../DeliveryActivityRepository.java | 62 + .../repository/DeliveryEventRepository.java | 46 + .../repository/DeliveryPassedRepository.java | 41 + .../morsel/repository/DeliveryRepository.java | 345 ++++ .../repository/LiveTicketRepository.java | 39 + .../repository/ModerationEventRepository.java | 41 + .../repository/PaymentCustomerRepository.java | 38 + .../repository/PaymentMethodRepository.java | 48 + .../morsel/repository/PaymentRepository.java | 49 + .../repository/PaymentSetupRepository.java | 51 + .../repository/PayoutAccountRepository.java | 40 + .../morsel/repository/PayoutRepository.java | 39 + .../repository/PhoneCodeRepository.java | 56 + .../repository/PreferenceRepository.java | 30 + .../morsel/repository/PricingRepository.java | 51 + .../morsel/repository/ProfileRepository.java | 76 + .../repository/PushDeviceRepository.java | 35 + .../examples/morsel/sms/ConsoleSmsSender.java | 38 + .../morsel/sms/MessageBirdSmsSender.java | 63 + .../morsel/sms/MisconfiguredSmsSender.java | 52 + .../examples/morsel/sms/SmsForm.java | 42 + .../examples/morsel/sms/SmsSender.java | 35 + .../examples/morsel/sms/TwilioSmsSender.java | 70 + .../examples/morsel/sms/VonageSmsSender.java | 83 + .../resources/db/migration/V1__accounts.sql | 90 + .../resources/db/migration/V2__couriers.sql | 51 + .../resources/db/migration/V4__deliveries.sql | 65 + .../resources/db/migration/V5__payments.sql | 82 + .../db/migration/V6__pricing_row.sql | 8 + .../main/resources/db/migration/V7__push.sql | 31 + .../migration/mysql/V3__courier_documents.sql | 19 + .../postgresql/V3__courier_documents.sql | 19 + .../sqlite/V3__courier_documents.sql | 19 + .../examples/morsel/AccessMatrixTest.java | 173 ++ .../examples/morsel/AccessTest.java | 150 ++ .../examples/morsel/AccountTest.java | 245 +++ .../codenameone/examples/morsel/DaysTest.java | 51 + .../examples/morsel/DeliveryFlowTest.java | 295 +++ .../examples/morsel/DeliveryRaceTest.java | 258 +++ .../morsel/DeliveryStateMachineTest.java | 79 + .../examples/morsel/LiveSocketTest.java | 240 +++ .../examples/morsel/MatchingTest.java | 225 +++ .../examples/morsel/ModerationTest.java | 228 +++ .../morsel/OnboardingApplicationTest.java | 346 ++++ .../examples/morsel/PaymentsTest.java | 772 ++++++++ .../codenameone/examples/morsel/People.java | 88 + .../examples/morsel/PricingAndStatsTest.java | 235 +++ .../examples/morsel/ProcessorFaultsTest.java | 215 +++ .../codenameone/examples/morsel/PushTest.java | 584 ++++++ .../examples/morsel/ScriptedPayments.java | 71 + .../examples/morsel/SettingsTest.java | 234 +++ .../codenameone/examples/morsel/SignIn.java | 100 + .../examples/morsel/StripePaymentsTest.java | 332 ++++ .../codenameone/examples/morsel/Trips.java | 123 ++ .../examples/morsel/WebSignInTest.java | 267 +++ .../morsel/delivery/OfferRecheckTest.java | 127 ++ scripts/morsel/check-template.sh | 81 + .../common/codenameone_settings.properties | 58 + scripts/morsel/common/icon.png | Bin 0 -> 123269 bytes scripts/morsel/common/pom.xml | 1375 ++++++++++++++ .../src/desktop/resources/NativeTheme.res | Bin 0 -> 300158 bytes scripts/morsel/common/src/main/css/theme.css | 1692 +++++++++++++++++ .../examples/morsel/AppConfig.java | 155 ++ .../codenameone/examples/morsel/Morsel.java | 89 + .../codenameone/examples/morsel/Prefs.java | 372 ++++ .../examples/morsel/Telemetry.java | 359 ++++ .../morsel/admin/AdminDeliveriesForm.java | 184 ++ .../examples/morsel/admin/AdminForm.java | 188 ++ .../morsel/admin/ApplicationsForm.java | 257 +++ .../examples/morsel/admin/Console.java | 436 +++++ .../examples/morsel/admin/Dashboard.java | 337 ++++ .../examples/morsel/admin/FleetForm.java | 144 ++ .../examples/morsel/admin/Panes.java | 170 ++ .../examples/morsel/admin/PaymentsForm.java | 96 + .../examples/morsel/admin/PricingForm.java | 165 ++ .../examples/morsel/admin/UsersForm.java | 336 ++++ .../morsel/courier/ApplicationForm.java | 477 +++++ .../examples/morsel/courier/ApplyForm.java | 230 +++ .../examples/morsel/courier/CourierForm.java | 560 ++++++ .../examples/morsel/courier/EarningsForm.java | 181 ++ .../examples/morsel/courier/Shift.java | 175 ++ .../morsel/customer/CustomerForm.java | 906 +++++++++ .../morsel/customer/DeliveryCard.java | 206 ++ .../morsel/customer/PlaceSearchForm.java | 146 ++ .../examples/morsel/live/LiveChannel.java | 225 +++ .../examples/morsel/map/CarMarker.java | 148 ++ .../examples/morsel/map/Locator.java | 276 +++ .../examples/morsel/map/MapStage.java | 264 +++ .../codenameone/examples/morsel/map/Maps.java | 285 +++ .../examples/morsel/map/Routes.java | 133 ++ .../examples/morsel/net/Account.java | 426 +++++ .../codenameone/examples/morsel/net/Api.java | 87 + .../codenameone/examples/morsel/net/Net.java | 102 + .../examples/morsel/net/Session.java | 517 +++++ .../examples/morsel/notify/Notifications.java | 148 ++ .../examples/morsel/pay/CardForm.java | 205 ++ .../examples/morsel/pay/Cards.java | 133 ++ .../examples/morsel/pay/ReceiptForm.java | 233 +++ .../examples/morsel/pay/WalletForm.java | 164 ++ .../examples/morsel/ui/BottomSheet.java | 214 +++ .../codenameone/examples/morsel/ui/Chart.java | 181 ++ .../examples/morsel/ui/ConsentForm.java | 67 + .../examples/morsel/ui/DeliveryListForm.java | 90 + .../codenameone/examples/morsel/ui/Lang.java | 131 ++ .../examples/morsel/ui/Layouts.java | 200 ++ .../codenameone/examples/morsel/ui/Look.java | 58 + .../codenameone/examples/morsel/ui/Menu.java | 187 ++ .../codenameone/examples/morsel/ui/Nav.java | 272 +++ .../examples/morsel/ui/Photos.java | 128 ++ .../examples/morsel/ui/ProfileForm.java | 116 ++ .../codenameone/examples/morsel/ui/Pulse.java | 99 + .../examples/morsel/ui/RegisterForm.java | 97 + .../codenameone/examples/morsel/ui/Ring.java | 127 ++ .../examples/morsel/ui/ServerForm.java | 49 + .../examples/morsel/ui/SettingsForm.java | 323 ++++ .../examples/morsel/ui/SignInForm.java | 69 + .../examples/morsel/ui/Skeleton.java | 90 + .../examples/morsel/ui/StatRow.java | 276 +++ .../codenameone/examples/morsel/ui/Ui.java | 732 +++++++ .../examples/morsel/ui/VerifyPhoneForm.java | 95 + .../examples/morsel/ui/WelcomeForm.java | 59 + .../common/src/main/l10n/Bundle.properties | 3 + .../common/src/main/l10n/Bundle_de.properties | 532 ++++++ .../common/src/main/l10n/Bundle_en.properties | 532 ++++++ .../common/src/main/l10n/Bundle_es.properties | 532 ++++++ .../common/src/main/l10n/Bundle_fr.properties | 532 ++++++ .../common/src/main/l10n/Bundle_he.properties | 532 ++++++ .../common/src/main/resources/surfaces.json | 3 + .../codenameone/examples/morsel/Actor.java | 143 ++ .../examples/morsel/AdminDesktopTest.java | 210 ++ .../examples/morsel/AdminModerationTest.java | 106 ++ .../examples/morsel/AdminTest.java | 150 ++ .../examples/morsel/AnalyticsTest.java | 198 ++ .../examples/morsel/CourierModeTest.java | 79 + .../morsel/CourierOnboardingTest.java | 149 ++ .../examples/morsel/DeliveryFlowTest.java | 72 + .../com/codenameone/examples/morsel/E2e.java | 522 +++++ .../examples/morsel/PaymentsTest.java | 122 ++ .../examples/morsel/RegisterTest.java | 63 + .../examples/morsel/SettingsTest.java | 97 + .../examples/morsel/SignInTest.java | 84 + scripts/morsel/pom.xml | 314 +++ scripts/morsel/run-e2e.sh | 131 ++ scripts/morsel/run-web-e2e.sh | 109 ++ scripts/morsel/shared/pom.xml | 49 + .../examples/morsel/api/AccountApi.java | 89 + .../morsel/api/AccountClosureDto.java | 26 + .../examples/morsel/api/AdminApi.java | 108 ++ .../examples/morsel/api/AdminStatsDto.java | 34 + .../examples/morsel/api/CardDto.java | 28 + .../examples/morsel/api/CourierApi.java | 89 + .../morsel/api/CourierApplicationDto.java | 60 + .../examples/morsel/api/CourierDto.java | 49 + .../examples/morsel/api/CourierStatusDto.java | 26 + .../examples/morsel/api/CustomerApi.java | 69 + .../examples/morsel/api/DayAmountDto.java | 28 + .../examples/morsel/api/DayStatsDto.java | 35 + .../morsel/api/DeliveryActivityDto.java | 27 + .../morsel/api/DeliveryActivityKeys.java | 51 + .../examples/morsel/api/DeliveryDto.java | 109 ++ .../morsel/api/DeliveryRequestDto.java | 57 + .../examples/morsel/api/DeliveryState.java | 63 + .../morsel/api/DocumentContentDto.java | 26 + .../examples/morsel/api/DocumentDto.java | 31 + .../morsel/api/DocumentUploadDto.java | 29 + .../examples/morsel/api/EarningsDto.java | 33 + .../morsel/api/EarningsSummaryDto.java | 46 + .../examples/morsel/api/FareOptionDto.java | 41 + .../examples/morsel/api/FareQuoteDto.java | 40 + .../examples/morsel/api/FlagDto.java | 26 + .../examples/morsel/api/GeoApi.java | 42 + .../morsel/api/ModerationEventDto.java | 30 + .../examples/morsel/api/NameCountDto.java | 26 + .../examples/morsel/api/NearbyCourierDto.java | 27 + .../examples/morsel/api/OnboardingApi.java | 57 + .../morsel/api/PasswordChangeDto.java | 26 + .../examples/morsel/api/PaymentApi.java | 67 + .../examples/morsel/api/PaymentConfigDto.java | 28 + .../examples/morsel/api/PaymentMethodDto.java | 37 + .../examples/morsel/api/PaymentSetupDto.java | 30 + .../examples/morsel/api/PayoutAccountDto.java | 27 + .../examples/morsel/api/PayoutDto.java | 30 + .../morsel/api/PhoneChallengeDto.java | 31 + .../examples/morsel/api/PhoneCodeDto.java | 26 + .../examples/morsel/api/PlaceDto.java | 26 + .../examples/morsel/api/PreferencesDto.java | 48 + .../examples/morsel/api/PricingDto.java | 40 + .../examples/morsel/api/ProfileDto.java | 34 + .../examples/morsel/api/PushDeviceDto.java | 30 + .../examples/morsel/api/RatingDto.java | 26 + .../examples/morsel/api/ReasonDto.java | 27 + .../examples/morsel/api/ReceiptDto.java | 55 + .../examples/morsel/api/RegisterDto.java | 42 + .../examples/morsel/api/RoleChangeDto.java | 26 + .../examples/morsel/api/StatsSeriesDto.java | 46 + .../examples/morsel/api/SuspendDto.java | 28 + .../examples/morsel/api/TicketDto.java | 30 + .../examples/morsel/api/TipDto.java | 26 + .../examples/morsel/api/UserDetailDto.java | 42 + .../examples/morsel/api/UserDto.java | 58 + scripts/morsel/web-e2e.mjs | 326 ++++ 288 files changed, 46199 insertions(+) create mode 100644 scripts/morsel/LICENSE create mode 100644 scripts/morsel/README.adoc create mode 100644 scripts/morsel/backend/application-dev.properties create mode 100644 scripts/morsel/backend/application-test.properties create mode 100644 scripts/morsel/backend/application.properties create mode 100644 scripts/morsel/backend/pom.xml create mode 100755 scripts/morsel/backend/server.sh create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Caller.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Days.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Ids.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Rows.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Services.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SignInCodePage.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Text.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountCleanup.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/DemoAccounts.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Grants.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Moderation.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/PhoneVerification.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Preferences.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/AdminEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/Statistics.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/CourierEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Couriers.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Earnings.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/CustomerEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Deliveries.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DeliveryStateMachine.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DemoDeliveries.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Fares.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Matcher.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ClosedAccount.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierApplication.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierDocument.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierHours.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierState.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Delivery.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryActivity.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryEvent.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryPassed.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/LiveTicket.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ModerationEvent.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payment.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentCustomer.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentMethod.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentSetup.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payout.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PayoutAccount.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PhoneCode.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Preference.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Pricing.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Profile.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PushDevice.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geo.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/GeoEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geocoder.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/PhotonGeocoder.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/StubGeocoder.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveHub.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveTickets.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/Applications.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/OnboardingEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/MisconfiguredPayments.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PayReturnPage.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentDeclinedException.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentEndpoint.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentProvider.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/Payments.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/SimulatedPayments.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/StripePayments.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/CloudPushSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/ConsolePushSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/DeliveryPushes.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/MisconfiguredPushSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushDevices.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/RecordingPushSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ClosedAccountRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierApplicationRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierDocumentRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierHoursRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierStateRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryActivityRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryEventRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryPassedRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/LiveTicketRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ModerationEventRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentCustomerRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentMethodRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentSetupRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutAccountRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PhoneCodeRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PreferenceRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PricingRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ProfileRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PushDeviceRepository.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/ConsoleSmsSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MessageBirdSmsSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MisconfiguredSmsSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsForm.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/TwilioSmsSender.java create mode 100644 scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/VonageSmsSender.java create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/V1__accounts.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/V2__couriers.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/V4__deliveries.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/V5__payments.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/V6__pricing_row.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/V7__push.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/mysql/V3__courier_documents.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/postgresql/V3__courier_documents.sql create mode 100644 scripts/morsel/backend/src/main/resources/db/migration/sqlite/V3__courier_documents.sql create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessMatrixTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccountTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DaysTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryRaceTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryStateMachineTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/LiveSocketTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/MatchingTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ModerationTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/OnboardingApplicationTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/People.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PricingAndStatsTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ProcessorFaultsTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PushTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ScriptedPayments.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SettingsTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SignIn.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/StripePaymentsTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/Trips.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/WebSignInTest.java create mode 100644 scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/delivery/OfferRecheckTest.java create mode 100755 scripts/morsel/check-template.sh create mode 100644 scripts/morsel/common/codenameone_settings.properties create mode 100644 scripts/morsel/common/icon.png create mode 100644 scripts/morsel/common/pom.xml create mode 100644 scripts/morsel/common/src/desktop/resources/NativeTheme.res create mode 100644 scripts/morsel/common/src/main/css/theme.css create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java create mode 100644 scripts/morsel/common/src/main/l10n/Bundle.properties create mode 100644 scripts/morsel/common/src/main/l10n/Bundle_de.properties create mode 100644 scripts/morsel/common/src/main/l10n/Bundle_en.properties create mode 100644 scripts/morsel/common/src/main/l10n/Bundle_es.properties create mode 100644 scripts/morsel/common/src/main/l10n/Bundle_fr.properties create mode 100644 scripts/morsel/common/src/main/l10n/Bundle_he.properties create mode 100644 scripts/morsel/common/src/main/resources/surfaces.json create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java create mode 100644 scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java create mode 100644 scripts/morsel/pom.xml create mode 100755 scripts/morsel/run-e2e.sh create mode 100755 scripts/morsel/run-web-e2e.sh create mode 100644 scripts/morsel/shared/pom.xml create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountClosureDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminStatsDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CardDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApplicationDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierStatusDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CustomerApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayAmountDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayStatsDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityKeys.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryRequestDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryState.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentContentDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentUploadDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsSummaryDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareOptionDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareQuoteDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FlagDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/GeoApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ModerationEventDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NameCountDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NearbyCourierDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/OnboardingApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PasswordChangeDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentApi.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentConfigDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentMethodDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentSetupDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutAccountDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneChallengeDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneCodeDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PlaceDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PreferencesDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PricingDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ProfileDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PushDeviceDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RatingDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReasonDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReceiptDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RegisterDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RoleChangeDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/StatsSeriesDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/SuspendDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TicketDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TipDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDetailDto.java create mode 100644 scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java create mode 100644 scripts/morsel/web-e2e.mjs diff --git a/scripts/morsel/LICENSE b/scripts/morsel/LICENSE new file mode 100644 index 00000000000..200c0c412d0 --- /dev/null +++ b/scripts/morsel/LICENSE @@ -0,0 +1,22 @@ +MIT No Attribution + +Copyright (c) 2026, Codename One and/or its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This applies to the Morsel sample and to everything Codename One Initializr +writes into a generated project: the application templates under +maven/cn1app-archetype/src/main/resources/archetype-resources, the agent skill +under scripts/initializr/common/src/main/resources/skill, and the Initializr +template archives built from them. diff --git a/scripts/morsel/README.adoc b/scripts/morsel/README.adoc new file mode 100644 index 00000000000..8ca66855f96 --- /dev/null +++ b/scripts/morsel/README.adoc @@ -0,0 +1,219 @@ += Morsel + +A delivery-hailing application and its server, in one Maven project and one language. +A customer asks for a delivery, the nearest suitable courier is offered it, both follow the +trip on a map, the delivery is paid for when it ends, and an administrator runs the +service. Which of the three a person sees is decided by the roles the server gives +their account: a customer opens on the map, a courier on the onboarding screen, an +administrator on the operations console. + +It is a sample of how a Codename One application and a Codename One Backend fit +together, and the project the Initializr generates when you start from the +delivery-hailing template. + +== Start here + +Two chapters of the developer guide cover this project: + +* https://www.codenameone.com/developer-guide/#morsel-users-guide[The user's guide] + (link:../../docs/developer-guide/Morsel-Users-Guide.asciidoc[source]) shows the + application as a customer, a courier and an administrator see it, screen by screen, + with the demo accounts and test cards. +* https://www.codenameone.com/developer-guide/#morsel-implementors-guide[The implementor's guide] + (link:../../docs/developer-guide/Morsel-Implementors-Guide.asciidoc[source]) + explains how the project is built and what to change to make it yours: a + delivery-sharing service or the dispatch service of a small taxi station, real + billing, your own map data or native maps, sign-in through another identity + provider, real text messages, analytics, and the database: the engines it + runs on, changing the schema, and hosting it. + +To see it running first, the two commands under <> are all it takes. + +== The modules + +[cols="1,4"] +|=== +| `shared` +| The contract: the data classes both sides exchange and the `@RestClient` + interfaces that name every call. The app's build generates a client from them + and the server's build generates the interfaces it implements, so a change the + other side did not follow is a compile error. + +| `common` +| The application: sign-in, the customer, courier and administrator screens, the map, + the wallet, the settings and the live channel. Styled in + `src/main/css/theme.css`, whose first block of variables holds the colors and + the corner radius everything else is drawn with. The text is in + `src/main/l10n`, in English, Spanish, French, German and Hebrew. + What it reports to Codename One analytics about how it is used, once the user + has agreed, is all in `Telemetry`. + +| `backend` +| The server: accounts and roles, phone verification, courier applications and + their documents, moderation, fares and pricing, matching, the delivery state + machine, payments and payouts, statistics, and a WebSocket that pushes changes + as they happen. + The data is entity classes in `domain`, read and written by the repository + beside each service. The schema is the migration scripts in + `src/main/resources/db/migration`, and the same server runs on SQLite, + PostgreSQL, MySQL and MariaDB. + +|=== + +There are no platform modules: `common` builds the application for the +simulator, the desktop, Android, iOS and the browser itself. + +== Running it + +Start the server on the development profile. It needs nothing installed: the +database is in memory, and text messages are printed on the console. + +[source,bash] +---- +CN1_PROFILE=dev backend/server.sh run +---- + +Then run the application in the simulator, from another terminal: + +[source,bash] +---- +mvn verify -Psimulator -DskipTests -Dcodename1.platform=javase +---- + +The development profile creates these accounts, all with the password +`morsel-demo`, and a fortnight of deliveries for the charts to draw: + +[cols="2,3"] +|=== +| `customer@morsel.example`, `customer2@morsel.example` | request deliveries +| `customer3@morsel.example` | a customer an administrator has flagged +| `courier@morsel.example`, `courier2@morsel.example` | approved couriers +| `courier3@morsel.example`, `courier4@morsel.example` | approved couriers of the + larger kinds of car +| `applicant@morsel.example` | has applied to drive and is waiting for a decision +| `admin@morsel.example` | runs the service +|=== + +Sign in as the customer in one simulator and as a courier in another to follow a delivery +from both ends. On a phone, set the server's address on the welcome screen: +`localhost` on a phone is the phone. + +=== In a browser, with nothing installed + +The server can host the application itself. Build the browser version once, and +again when the application changes; it takes about a minute: + +[source,bash] +---- +backend/server.sh web +CN1_PROFILE=dev backend/server.sh run +---- + +Then open `http://localhost:8080/`. `server.sh run --web` does both in one step. + +`server.sh web` runs `cn1:backend-webapp`, which builds the application for the +browser and stages it in `backend/target/webapp`; a server that finds it there +serves it at `/`, after its own routes. In the browser the application takes the +page's address as the server's, signs in through the same authorization-code +flow with the code sent back to `/signin/code` on that server, and opens its live +channel to the same origin. To deploy it, put `backend/target/webapp` beside the +server binary as `webapp`. + +A browser gives a page less than a phone gives an app, and the application says +so where it matters: calling and texting a courier are offered only where the +device can dial, and the map opens on a default position when the browser will +not say where it is. + +== What each role does + +A customer picks a destination, a kind of car and a way to pay, and may ask for a +quiet delivery, an accessible vehicle, a car that takes pets or a woman courier. The +wallet holds their cards and receipts, and a tip can be added after the delivery. + +Anyone can apply to drive. The application asks for the courier's details, the +vehicle, and five photographs: an identity document, the licence, a selfie, the +vehicle registration and the insurance. Nobody drives until an administrator +approves the application; a rejection carries a reason, and the applicant can +correct it and send it again. An approved courier goes online, is offered deliveries, +and sees what they earned and cashes it out. + +An administrator sees the service in charts, reviews applications with their +documents, flags or blocks an account with a reason that is kept in its history, +cancels and refunds deliveries, and sets the prices. + +== What is real and what is simulated + +Maps, search and routes are real and need no key: the map tiles come from +OpenFreeMap, routes from the public OSRM server, and place search from Photon, +which the server calls so the app never talks to a third party directly. + +Text messages go through the provider `morsel.sms.provider` names: `twilio`, +`vonage` or `messagebird`, each with its own credentials. On a development +profile they are printed on the console instead, and the app is handed the code +so a sign-up can finish. Any other profile needs a provider; one whose +credentials are missing is logged as a configuration error, naming the missing +settings, and the app is told the code could not be sent. + +Payments are simulated on a development profile. Simulated, the card +`4242 4242 4242 4242` is accepted, `4000 0000 0000 0002` is declined, and no +money moves. With `morsel.payments.provider=stripe` and +`MORSEL_PAYMENTS_STRIPE_SECRET` set in the server's environment, a card is +added on Stripe's own page, so its number never reaches the app or this server, +and a delivery is charged when it completes. The Stripe provider is an untested +stub: it is covered by tests that answer for Stripe, has never been run against +a live account, and has to be tried with a Stripe test key before it is trusted +with money. Paying a +courier out is a ledger entry in both cases; moving that money to a bank is left +to the deployment. + +`backend/application.properties` lists every setting a deployment supplies. The +database is the one named by `DATABASE_URL`: a `postgres://` or `mysql://` +address, or the path of a SQLite file. The SMS and payment settings are listed +there too, each also read from the environment in upper case with `_` for every +dot (`MORSEL_SMS_TWILIO_TOKEN`). + +== Tests + +[source,bash] +---- +mvn -pl shared,backend -Dcodename1.platform=backend test # <1> +mvn -pl shared,backend -Dcodename1.platform=backend test -Dcn1.backend.compiledTests=true # <2> +./run-e2e.sh # <3> +./check-template.sh # <4> +./run-web-e2e.sh # <5> +---- +<1> The server's tests, on the JVM. +<2> The same tests again, translated to a native binary, which is how the server + ships. +<3> Starts the server on the `test` profile and runs the application's tests + against it in the simulator: they register, apply to drive and are approved, + request a delivery, drive it, pay for it and administer it, and compare the + screens in `screenshots/`. Pass `--native` to run them against the compiled + server. On Linux they then run a second time with the simulator as a desktop + window, where the admin console is laid out side by side under the + platform's own theme. +<4> Generates a project from the Initializr template, under another name and + package, and builds it. +<5> Builds the browser version of the application, starts the server hosting + it, and drives it in a headless browser: the application loads from the + server, signs in as the demo customer, reaches the home screen and opens its + live channel, then signs out and signs in as the demo admin. Needs Node with Playwright and its Chromium (`npm ci` and + `npx playwright install chromium` in `scripts/`). Screenshots and logs go + to `target/web-e2e`. + +`.github/workflows/morsel.yml` runs all five, and builds the application for +Android and iOS through `scripts/build-android-app.sh` and +`scripts/build-ios-app.sh` with `CN1_APP_DIR=scripts/morsel`. + +== The Initializr template + +`scripts/sync-initializr-morsel.py --write` copies this project into the +Initializr's resources; without `--write` it fails when they differ, which is what +CI runs. Change the project here, never the copies. + +== Licence + +Morsel is released under MIT No Attribution (see `LICENSE`), not under the +GPLv2 with Classpath Exception that covers Codename One itself. Take any of it +into your own application, under any licence, with no notice to keep. The same +holds for everything Initializr writes into a generated project. diff --git a/scripts/morsel/backend/application-dev.properties b/scripts/morsel/backend/application-dev.properties new file mode 100644 index 00000000000..92bfe0a5322 --- /dev/null +++ b/scripts/morsel/backend/application-dev.properties @@ -0,0 +1,30 @@ +# The development profile, chosen with CN1_PROFILE=dev. +# +# An in-memory SQLite database, so the server starts with nothing installed: the +# migrations create the tables at start-up, and the demo accounts are added to +# them. Point this at a file to keep the data between runs: +# +# cn1.datasource.url=./target/app.db +# +# Production points cn1.datasource.url at the real database instead -- a +# postgres:// or mysql:// URL -- and the same code runs against it. +cn1.datasource.url=:memory: + +# On this profile the server also serves, without a token: +# +# /manage/health, /manage/metrics, /manage/prometheus, /manage/jobs +# /mcp -- the development MCP tools, for an agent working on this backend +# +# Neither is served outside a development profile unless it is turned on and +# given a token (cn1.management.token, cn1.mcp.token). + +# Text messages and push notifications are printed on the console and cards are +# simulated. All three are the defaults of a development profile anyway, and are +# spelled out to show where the choice is made. The console senders are refused +# outside a development profile. See application.properties for the real +# providers. +# tag::morsel-properties-dev-providers[] +morsel.sms.provider=console +morsel.payments.provider=simulated +morsel.push.provider=console +# end::morsel-properties-dev-providers[] diff --git a/scripts/morsel/backend/application-test.properties b/scripts/morsel/backend/application-test.properties new file mode 100644 index 00000000000..9bfa8d84825 --- /dev/null +++ b/scripts/morsel/backend/application-test.properties @@ -0,0 +1,20 @@ +# The test profile, chosen with CN1_PROFILE=test. It is what run-e2e.sh starts the +# server on for the app's end-to-end tests, and what @BackendTest runs the +# server's own tests on. +# +# Like `dev` -- an in-memory database, the demo accounts, text messages that are +# printed and not sent -- and one thing more: places are looked up in a list of +# ten the server carries, so a test run asks no public geocoder for anything. +cn1.datasource.url=:memory: + +# The tests sign several accounts in from one address, one after another, which +# is exactly what the sign-in rate limit is there to stop. +morsel.signin.per-minute=1000 + +# Codes printed on the console (and handed back to the test as demoCode), cards +# simulated: no test reaches an SMS provider or a payment processor. +morsel.sms.provider=console +morsel.payments.provider=simulated + +# Push notifications are kept for the tests to read, not sent. +morsel.push.provider=recording diff --git a/scripts/morsel/backend/application.properties b/scripts/morsel/backend/application.properties new file mode 100644 index 00000000000..3e9e474761d --- /dev/null +++ b/scripts/morsel/backend/application.properties @@ -0,0 +1,98 @@ +# The settings this server reads at start-up, and the layer with the lowest +# precedence. A system property of the same name wins over anything here, then +# the environment variable the key maps to (cn1.datasource.url is read from +# CN1_DATASOURCE_URL, and also from DATABASE_URL, which is what a platform sets +# for you), then application-.properties, then this file. +# +# It sits in the module root on purpose. This runtime has no classpath to load a +# resource from, so Config opens application*.properties as FILES relative to the +# working directory: the module directory under `cn1:backend`, and wherever the +# binary is started once it is packaged. +# +# Choose a profile with CN1_PROFILE. The one beside this file is `dev`: +# +# CN1_PROFILE=dev mvn -pl backend -Dcodename1.platform=backend cn1:backend +# +# A development profile (dev, test, local) needs nothing else: an in-memory +# database, a signing key made at start-up, text messages printed on the +# console, simulated cards, and a set of demo accounts with a fortnight of deliveries behind them. A real +# deployment sets, here or in its environment: +# +# cn1.datasource.url the database +# cn1.security.authorizationserver.issuer the public address of this server +# cn1.security.authorizationserver.jwk.keys the key tokens are signed with +# morsel.admin.email, morsel.admin.password the first admin, who grants the rest +# morsel.sms.provider and its settings below +# morsel.payments.provider and its settings below +# morsel.push.app, morsel.push.key push notifications, below +# +# and may change the fares (morsel.fare.*), the currency (morsel.currency) and +# the geocoder (morsel.geocoder.url). +# +# Text messages. morsel.sms.provider names the provider verification codes are +# sent through, and each needs its own settings: +# +# twilio morsel.sms.twilio.sid, morsel.sms.twilio.token, +# morsel.sms.twilio.from +# vonage morsel.sms.vonage.key, morsel.sms.vonage.secret, +# morsel.sms.vonage.from +# messagebird morsel.sms.messagebird.key, morsel.sms.messagebird.originator +# console prints the code; development profiles only +# +# Unset, it is console on a development profile and twilio on any other. A +# provider whose settings are missing is a configuration error: it is logged at +# start-up and on every send, naming the missing settings, and the app is told +# the code could not be sent. Nothing falls back to printing codes in +# production. +# +# Payments. morsel.payments.provider is simulated or stripe; unset, it is +# simulated on a development profile and stripe on any other. Simulated cards: +# 4242 4242 4242 4242 is accepted, 4000 0000 0000 0002 is declined, and nothing +# is charged. Stripe is an untested stub over Stripe's REST API, a starting +# point to try against a test-mode key: cards are added on Stripe's own page, so +# a card number never reaches the app or this server, and deliveries are charged +# when they complete. It needs the secret key, and without it every payment +# call fails with a configuration error in the log. Keep the key and the SMS +# credentials out of this file and out of the repository; set them in the +# environment of the deployment instead, where each setting is read in upper +# case with _ for every dot: +# +# MORSEL_PAYMENTS_STRIPE_SECRET a restricted or secret key (sk_...) +# MORSEL_SMS_TWILIO_TOKEN and so on +# +# Stripe's page sends the browser back to /pay/return on this server when it is +# done, so the server has to know the address it is reached at from a phone. It +# uses cn1.security.authorizationserver.issuer, which a deployment sets anyway; +# morsel.public.url overrides that when the two differ. +# +# Push notifications. morsel.push.provider is cloud, console or recording; +# unset, it is recording on the test profile, console on the other development +# profiles and cloud on any other. cloud sends through the Codename One push +# service, which delivers to Firebase, Apple, Huawei and the browsers for you, +# and needs two settings from the Push section of the Codename One console: +# +# morsel.push.app the app's push id +# morsel.push.key a server key with the push scope; put it in the +# environment as MORSEL_PUSH_KEY, like the other secrets +# +# Without them the server still starts and serves deliveries; the missing settings +# are logged at start-up and again for every notification that is not sent. +# morsel.push.activity.seconds (45) is how often, at most, a moving car +# updates the customer's Live Activity. +# +# The prices are settings only until an admin changes them in the app. From +# then on they are in the database (morsel.fare.* is the starting point, and +# morsel.fare.serviceFeePercent and morsel.fare.commissionPercent join it). + +# ${DATABASE_URL} is resolved when the value is READ, so this line is fine in a +# checkout where that variable is unset: the dev profile overrides the key, and +# nothing reads this one. SQLite (a file path), postgres:// and mysql:// all work. +cn1.datasource.url=${DATABASE_URL} + +# The port. A platform that sets PORT wins over this, which is what lets the same +# build deploy anywhere. +cn1.server.port=8080 + +# Creates the tables the accounts, the registered app and the tokens are kept in. +# Read by the build, so it has to be here and not in a profile's file. +cn1.security.schema.enabled=true diff --git a/scripts/morsel/backend/pom.xml b/scripts/morsel/backend/pom.xml new file mode 100644 index 00000000000..a6b2ff5f49f --- /dev/null +++ b/scripts/morsel/backend/pom.xml @@ -0,0 +1,160 @@ + + + 4.0.0 + + com.codenameone.examples.morsel + morsel + 1.0-SNAPSHOT + + com.codenameone.examples.morsel + morsel-backend + 1.0-SNAPSHOT + jar + + morsel-backend + + + + + com.codenameone + codenameone-backend + ${cn1.version} + + + + ${project.groupId} + morsel-shared + ${project.version} + + + + org.xerial + sqlite-jdbc + 3.46.1.0 + runtime + + + + com.codenameone + codenameone-backend-test + ${cn1.version} + test + + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + + + + + + com.codenameone + codenameone-maven-plugin + ${cn1.plugin.version} + + + + cn1-generate-annotation-stubs + generate-sources + + + generate-annotation-stubs + + + + cn1-process-annotations + process-classes + + + process-annotations + + + + cn1-process-test-annotations + process-test-classes + + + process-test-annotations + + + + cn1-compiled-tests + test + + + backend-test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + false + + + + + diff --git a/scripts/morsel/backend/server.sh b/scripts/morsel/backend/server.sh new file mode 100755 index 00000000000..595633bf4ac --- /dev/null +++ b/scripts/morsel/backend/server.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# +# Builds and runs the Morsel server. +# +# server.sh build [--jvm|--native] [--web] +# server.sh run [--jvm|--native] [--web] [--prebuilt] [--port N] +# server.sh web +# +# --jvm (the default) runs the server on this JDK, which is how you work on it. +# --native compiles it to a native binary, which is how it ships. +# +# The profile comes from CN1_PROFILE. `test` and `dev` need nothing else: an +# in-memory database, four demo accounts, a geocoder that knows ten places +# (`test`) and text messages that are printed, not sent. Anything else is a +# deployment and reads its settings from the environment -- see +# application.properties. +# +# `web` builds the app for the browser and stages it in target/webapp, which +# takes about a minute; `--web` does that as part of a build or a run. A server +# that finds the app there serves it at /, so http://localhost:8080/ is Morsel +# with nothing installed. It stays staged until the next `mvn clean`: build it +# once, and again when the app changed. +# +# The server prints "listening on http port N" when it accepts connections. +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +APP="$(cd "$HERE/.." && pwd)" +# The project's own Maven wrapper where it has one. +if [ -z "${MVN:-}" ]; then + if [ -x "$APP/mvnw" ]; then MVN="$APP/mvnw"; else MVN="mvn"; fi +fi + +log() { echo "[morsel-server] $*" >&2; } + +build_jvm() { + log "building the server for the JVM" + "$MVN" -B -q -f "$APP/pom.xml" -Dcodename1.platform=backend -pl shared,backend \ + -Dmaven.test.skip=true package dependency:copy-dependencies \ + -DincludeScope=runtime -DoutputDirectory="$APP/backend/target/server-lib" >&2 +} + +build_native() { + log "translating the server to a native binary" + "$MVN" -B -q -f "$APP/pom.xml" -Dcodename1.platform=backend -pl shared,backend \ + -Dmaven.test.skip=true package cn1:backend-package \ + -Dcn1.backend.output="$APP/backend/target/morsel-server" >&2 +} + +build_web() { + log "building the app for the browser" + "$MVN" -B -q -f "$APP/pom.xml" -Dcodename1.platform=backend -pl backend \ + cn1:backend-webapp >&2 +} + +# The server looks for the app in `webapp` beside its settings; here it is build +# output, so it is under target and the server is told. An address set by the +# caller is left alone. +serve_web() { + if [ -z "${CN1_WEBAPP_ROOT:-}" ] && [ -f "$APP/backend/target/webapp/index.html" ]; then + export CN1_WEBAPP_ROOT="target/webapp" + log "serving the web app in backend/target/webapp" + fi +} + +command="${1:-run}" +shift || true +mode="jvm" +port="${PORT:-8080}" +prebuilt=0 +web=0 +while [ $# -gt 0 ]; do + case "$1" in + --jvm) mode="jvm" ;; + --native) mode="native" ;; + --prebuilt) prebuilt=1 ;; + --web) web=1 ;; + --port) port="$2"; shift ;; + *) log "unknown argument $1"; exit 2 ;; + esac + shift +done + +case "$command" in + build) + if [ "$mode" = "native" ]; then build_native; else build_jvm; fi + [ "$web" = 0 ] || build_web + ;; + web) + build_web + ;; + run) + # application.properties is read from the working directory. + cd "$APP/backend" + if [ "$mode" = "native" ]; then + [ "$prebuilt" = 1 ] || build_native + [ "$web" = 0 ] || [ "$prebuilt" = 1 ] || build_web + serve_web + PORT="$port" exec "$APP/backend/target/morsel-server" + fi + [ "$prebuilt" = 1 ] || build_jvm + [ "$web" = 0 ] || [ "$prebuilt" = 1 ] || build_web + serve_web + main="$(tr -d '\r\n' < "$APP/backend/target/classes/META-INF/cn1-backend-main")" + log "running $main on port $port" + PORT="$port" exec java -cp "$APP/backend/target/classes:$APP/backend/target/server-lib/*" "$main" + ;; + *) + log "unknown command $command (build, run, web)" + exit 2 + ;; +esac diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Caller.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Caller.java new file mode 100644 index 00000000000..691a9affc0a --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Caller.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.security.Authentication; +import com.codename1.backend.security.GrantedAuthority; +import com.codename1.backend.security.SecurityContextHolder; + +import java.util.Iterator; + +/// Who is making the request being served. +/// +/// The endpoints implement interfaces generated from the shared contract, whose +/// methods take what the app sends and nothing else, so the caller is read from +/// the security context of the request and not from a parameter. +public final class Caller { + private Caller() { + } + + /// The signed-in user's name, which is the e-mail address of the account. + /// Answers 401 when nobody is signed in; the filter chain has normally done + /// so already, and this is what stands behind it. + public static String name() { + Authentication who = SecurityContextHolder.getContext().getAuthentication(); + if (who == null || !who.isAuthenticated() || who.getName() == null) { + throw new ResponseStatusException(401, "Sign in first"); + } + return who.getName(); + } + + /// Whether the caller holds `role`, named without its `ROLE_` prefix. + public static boolean hasRole(String role) { + Authentication who = SecurityContextHolder.getContext().getAuthentication(); + if (who == null || who.getAuthorities() == null) { + return false; + } + String wanted = "ROLE_" + role; + Iterator it = who.getAuthorities().iterator(); + while (it.hasNext()) { + if (wanted.equals(it.next().getAuthority())) { + return true; + } + } + return false; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Days.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Days.java new file mode 100644 index 00000000000..8f8a069aeec --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Days.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +/// Days, counted in UTC. +/// +/// The charts and the earnings are by day, and a day has to mean the same on +/// SQLite, PostgreSQL and MySQL, whose date functions agree on nothing. So the +/// tables hold milliseconds since the epoch, and the arithmetic is done here. +/// UTC throughout: a service in one city would shift by that city's offset. +public final class Days { + public static final long MILLIS = 86400000L; + + private Days() { + } + + /// The number of the day `millis` falls in: whole days since the epoch. + public static long number(long millis) { + return millis / MILLIS; + } + + /// The first millisecond of the day `millis` falls in. + public static long start(long millis) { + return millis - millis % MILLIS; + } + + /// The stretch of time from `from` to `to` cut at each midnight: the + /// number of each day it touches followed by how many of its milliseconds + /// fall in that day, day after day. Empty when `to` is not after `from`. + /// + /// Time on line is credited to the days it was spent in. Credited whole + /// to the day it ended in, a courier on line across midnight had the + /// minutes before it counted as the next day's. + public static long[] split(long from, long to) { + if (to <= from) { + return new long[0]; + } + long[] out = new long[(int) (number(to - 1) - number(from) + 1) * 2]; + long at = from; + for (int iter = 0; iter < out.length; iter += 2) { + long end = Math.min(to, start(at) + MILLIS); + out[iter] = number(at); + out[iter + 1] = end - at; + at = end; + } + return out; + } + + /// The day `millis` falls in, as `yyyy-MM-dd`. + public static String iso(long millis) { + // Days since the epoch to a civil date, by the usual era arithmetic: + // shift the year to start in March so the leap day is its last day. + long z = number(millis) + 719468L; + long era = z / 146097L; + long dayOfEra = z - era * 146097L; + long yearOfEra = (dayOfEra - dayOfEra / 1460L + dayOfEra / 36524L - dayOfEra / 146096L) + / 365L; + long dayOfYear = dayOfEra - (365L * yearOfEra + yearOfEra / 4L - yearOfEra / 100L); + long shifted = (5L * dayOfYear + 2L) / 153L; + long day = dayOfYear - (153L * shifted + 2L) / 5L + 1L; + long month = shifted < 10L ? shifted + 3L : shifted - 9L; + long year = yearOfEra + era * 400L + (month <= 2L ? 1L : 0L); + StringBuilder out = new StringBuilder(10); + out.append(year).append('-'); + if (month < 10L) { + out.append('0'); + } + out.append(month).append('-'); + if (day < 10L) { + out.append('0'); + } + return out.append(day).toString(); + } + + /// Whether `text` is a date written `yyyy-MM-dd`, with a month and a day + /// that could exist. + public static boolean isIso(String text) { + if (text == null || text.length() != 10 || text.charAt(4) != '-' || text.charAt(7) != '-') { + return false; + } + for (int iter = 0; iter < 10; iter++) { + char c = text.charAt(iter); + if (iter != 4 && iter != 7 && (c < '0' || c > '9')) { + return false; + } + } + int year = 0; + for (int iter = 0; iter < 4; iter++) { + year = year * 10 + (text.charAt(iter) - '0'); + } + int month = (text.charAt(5) - '0') * 10 + (text.charAt(6) - '0'); + int day = (text.charAt(8) - '0') * 10 + (text.charAt(9) - '0'); + // The day is held to its own month: these strings are compared as + // text and never parsed, so a 31st of February accepted here would be + // stored, and approved, as somebody's date of birth. + return month >= 1 && month <= 12 && day >= 1 && day <= length(year, month); + } + + // How many days a month has, by the Gregorian rule for leap years. + private static int length(int year, int month) { + if (month == 2) { + boolean leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + return leap ? 29 : 28; + } + return month == 4 || month == 6 || month == 9 || month == 11 ? 30 : 31; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Ids.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Ids.java new file mode 100644 index 00000000000..c2d45205d01 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Ids.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Crypto; + +import java.io.IOException; + +/// Identifiers and secrets made of random bytes. +public final class Ids { + private static final char[] HEX = "0123456789abcdef".toCharArray(); + + private Ids() { + } + + /// A new identifier: 32 hex digits from 16 random bytes. Random and not a + /// counter, so the id of one delivery says nothing about the id of another, and + /// so no table needs a generated key, which every database spells its own way. + public static String next() throws IOException { + return hex(Crypto.randomBytes(16)); + } + + public static String hex(byte[] bytes) { + char[] out = new char[bytes.length * 2]; + for (int iter = 0; iter < bytes.length; iter++) { + out[iter * 2] = HEX[(bytes[iter] >> 4) & 0xf]; + out[iter * 2 + 1] = HEX[bytes[iter] & 0xf]; + } + return new String(out); + } + + /// The SHA-256 of `text`, in hex. What is stored in place of a secret that + /// only ever needs to be compared. + public static String sha256(String text) throws IOException { + return hex(Crypto.sha256(text.getBytes("UTF-8"))); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java new file mode 100644 index 00000000000..36ea24c95df --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +/// The roles an account can hold. Every account is a customer; onboarding and +/// administering are granted on top of that, and the app offers a mode for each +/// role the account holds. +public final class Roles { + public static final String CUSTOMER = "CUSTOMER"; + public static final String COURIER = "COURIER"; + public static final String ADMIN = "ADMIN"; + + private Roles() { + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Rows.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Rows.java new file mode 100644 index 00000000000..f017bb79c27 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Rows.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.api.DeliveryState; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// Reading the rows a repository answers for a select of several values, +/// `select r.courier, count(r) ...`, which come back as `Object[]`. +/// +/// What a column holds is checked and never cast blindly: a count is a `Long` +/// on one database and an `Integer` on another, and a state is the enum or +/// its name depending on how the column was read. +public final class Rows { + private Rows() { + } + + /// A column as a whole number; 0 for null or anything that is not a + /// number. + public static long number(Object value) { + return value instanceof Number ? ((Number) value).longValue() : 0L; + } + + /// A column as text; empty for null. + public static String text(Object value) { + return value == null ? "" : String.valueOf(value); + } + + /// A column holding a delivery's state, or null for one that names none. + public static DeliveryState state(Object value) { + if (value instanceof DeliveryState) { + return (DeliveryState) value; + } + String name = text(value); + DeliveryState[] all = DeliveryState.values(); + for (int iter = 0; iter < all.length; iter++) { + if (all[iter].name().equals(name)) { + return all[iter]; + } + } + return null; + } + + /// Rows of `{name, count}` as a map from the name to the count. + public static Map counts(List rows) { + Map out = new HashMap(); + for (int iter = 0; iter < rows.size(); iter++) { + Object[] row = rows.get(iter); + out.put(text(row[0]), Long.valueOf(number(row[1]))); + } + return out; + } + + /// A single number a repository answers, which is null for a sum or a + /// maximum of no rows. + public static long number(Long value) { + return value == null ? 0L : value.longValue(); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java new file mode 100644 index 00000000000..2fc76ed7999 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Config; +import com.codename1.backend.DataSource; +import com.codename1.backend.HttpServer; +import com.codename1.backend.annotations.Bean; +import com.codename1.backend.annotations.Configuration; +import com.codename1.backend.annotations.Order; +import com.codename1.backend.annotations.Value; +import com.codename1.backend.security.AntPathRequestMatcher; +import com.codename1.backend.security.DisabledException; +import com.codename1.backend.security.GrantedAuthority; +import com.codename1.backend.security.HttpSecurity; +import com.codename1.backend.security.HttpStatusReturningLogoutSuccessHandler; +import com.codename1.backend.security.LogoutFilter; +import com.codename1.backend.security.SecurityExchange; +import com.codename1.backend.security.SecurityFilter; +import com.codename1.backend.security.SecurityFilterChain; +import com.codename1.backend.security.SessionCreationPolicy; +import com.codename1.backend.security.core.userdetails.JdbcUserDetailsManager; +import com.codename1.backend.security.core.userdetails.UserDetails; +import com.codename1.backend.security.crypto.JwkSource; +import com.codename1.backend.security.crypto.PasswordEncoder; +import com.codename1.backend.security.crypto.PasswordEncoderFactories; +import com.codename1.backend.security.oauth2.core.AuthorizationGrantType; +import com.codename1.backend.security.oauth2.core.ClientAuthenticationMethod; +import com.codename1.backend.security.oauth2.jwt.DefaultJwtDecoder; +import com.codename1.backend.security.oauth2.jwt.JwtAudienceValidator; +import com.codename1.backend.security.oauth2.jwt.JwtClaimValidator; +import com.codename1.backend.security.oauth2.jwt.JwtIssuerValidator; +import com.codename1.backend.security.oauth2.jwt.JwtValidators; +import com.codename1.backend.security.oauth2.server.authorization.AuthorizationServerKeys; +import com.codename1.backend.security.oauth2.server.authorization.AuthorizationServerSettings; +import com.codename1.backend.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService; +import com.codename1.backend.security.oauth2.server.authorization.JdbcRegisteredClientRepository; +import com.codename1.backend.security.oauth2.server.authorization.OAuth2AuthorizationService; +import com.codename1.backend.security.oauth2.server.authorization.RegisteredClient; +import com.codename1.backend.security.oauth2.server.authorization.RegisteredClientRepository; +import com.codename1.backend.security.oauth2.server.resource.JwtAuthenticationConverter; +import com.codename1.backend.security.ratelimit.InMemoryRateLimiter; +import com.codename1.backend.security.ratelimit.RateLimitKeys; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +/// Who may call what. +/// +/// This server is two things at once, and has a filter chain for each: +/// +/// - **An authorization server.** The app signs a user in here -- e-mail and +/// password, then the authorization-code flow with PKCE -- and gets a +/// short-lived access token and a refresh token. Nothing but this server ever +/// sees the password. +/// - **An API.** Everything under `/api` takes that access token as a bearer +/// token, keeps no session, and decides from the account's roles what the +/// caller may do. +/// +/// The roles are CUSTOMER, COURIER and ADMIN ([Roles]). `/api/courier` is for +/// couriers and `/api/admin` for admins; the rest is for anyone signed in, and +/// acts on the caller's own account, cards, receipts and onboarding application. +/// `/api/onboarding` is that last one, and is deliberately not under +/// `/api/courier`: applying to drive is what someone does before they may. +@Configuration +public class SecurityConfig { + /// The id the app signs in as. A public client: it holds no secret, since + /// anything shipped inside an app can be read out of it, and proves itself + /// with PKCE instead. + public static final String CLIENT_ID = "morsel-app"; + /// Where the authorization code is sent back to. The app makes the requests + /// of the flow itself and reads the code out of the redirect, so this is + /// never opened; it is the loopback address because that is the one address + /// nobody else can register a listener on. An app that signs in through the + /// system browser adds its own address scheme here with `redirectUri`. + public static final String LOOPBACK_REDIRECT = "http://127.0.0.1/callback"; + /// Where the code is sent back to when the app runs in a browser: a path on + /// this server, which answers with the code it was given. See + /// [#withWebRedirect]. + public static final String WEB_REDIRECT_PATH = "/signin/code"; + /// Whom the access tokens are for. The API accepts no token issued for + /// anything else. + public static final String AUDIENCE = "urn:morsel:api"; + public static final String ISSUER_KEY = "cn1.security.authorizationserver.issuer"; + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + /// The accounts, in the tables `cn1.security.schema.enabled` creates. + @Bean + public JdbcUserDetailsManager users(DataSource db) { + return new JdbcUserDetailsManager(db); + } + + @Bean + public RegisteredClientRepository clients(DataSource db, + @Value("${" + ISSUER_KEY + ":}") final String issuer) throws IOException { + final JdbcRegisteredClientRepository clients = new JdbcRegisteredClientRepository(db); + if (clients.findByClientId(CLIENT_ID) == null) { + clients.save(RegisteredClient.withId("morsel").clientId(CLIENT_ID) + .clientName("Morsel") + .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .redirectUri(LOOPBACK_REDIRECT) + .scope("openid").scope("profile") + .build()); + } + // The app in a browser is the same client with one more address: this + // server's own. It is worked out and not stored, because on a + // development profile the server has no one address to store -- and a + // row written by an earlier version would not have it either. + return new RegisteredClientRepository() { + @Override + public void save(RegisteredClient client) { + clients.save(client); + } + + @Override + public RegisteredClient findByClientId(String clientId) { + return withWebRedirect(clients.findByClientId(clientId), issuer); + } + + @Override + public RegisteredClient findById(String id) { + return withWebRedirect(clients.findById(id), issuer); + } + }; + } + + /// The app's registration with [#WEB_REDIRECT_PATH] on this server added + /// to the addresses a code may be sent back to. + /// + /// A browser will not show a page the answer to a request that was + /// redirected to another origin, so the app running in one cannot read its + /// code off a redirect to the loopback address the way the installed app + /// does. It asks for the code to be sent to this server instead, which + /// hands it straight back ([SignInCodePage]). + /// + /// "This server" is the configured issuer where there is one, and is then + /// the only web address accepted. With none -- a development profile -- it + /// is the address the request arrived at, the same rule + /// [#issuedAtThisAddress] holds a token to: a code is only ever sent back + /// to the server the browser was already talking to. + static RegisteredClient withWebRedirect(RegisteredClient client, String issuer) { + if (client == null || !CLIENT_ID.equals(client.getClientId())) { + return client; + } + String here = here(issuer); + if (here == null) { + return client; + } + RegisteredClient.Builder copy = RegisteredClient.withId(client.getId()) + .clientId(client.getClientId()) + .clientName(client.getClientName()) + .clientSettings(client.getClientSettings()) + .tokenSettings(client.getTokenSettings()) + .redirectUri(here + WEB_REDIRECT_PATH); + for (ClientAuthenticationMethod method : client.getClientAuthenticationMethods()) { + copy.clientAuthenticationMethod(method); + } + for (AuthorizationGrantType grant : client.getAuthorizationGrantTypes()) { + copy.authorizationGrantType(grant); + } + for (String uri : client.getRedirectUris()) { + copy.redirectUri(uri); + } + for (String scope : client.getScopes()) { + copy.scope(scope); + } + for (String resource : client.getResources()) { + copy.resource(resource); + } + return copy.build(); + } + + /// Refuses a request that changes something and was sent by a page of + /// another site. + /// + /// A browser names the site a request came from in `Origin` on every + /// request that is not a plain read, and a page cannot change or drop it. + /// The installed app is not a browser and sends none, so a request without + /// one is let through: there is no cookie jar of somebody else's for it to + /// be riding on. + static SecurityFilter sameOrigin(final String issuer) { + return (request, chain) -> { + String method = request.getMethod(); + if ("GET".equals(method) || "HEAD".equals(method) || "OPTIONS".equals(method)) { + return chain.doFilter(request); + } + String origin = request.getHeader("Origin"); + if (origin == null || origin.length() == 0) { + return chain.doFilter(request); + } + String here = here(issuer); + if (here != null && sameOrigin(origin, here)) { + return chain.doFilter(request); + } + return new HttpServer.Response(403, "application/json", + "{\"error\":\"cross_origin\"}".getBytes("UTF-8")) + .header("Cache-Control", "no-store"); + }; + } + + /// This server's address: the configured issuer, and with none -- a + /// development profile -- the address the request in hand arrived at. Null + /// outside a request that says. + static String here(String issuer) { + if (issuer != null && issuer.length() > 0) { + return issuer; + } + SecurityExchange exchange = SecurityExchange.current(); + String host = exchange == null ? null : exchange.getRequest().getHeader("Host"); + if (host == null || host.length() == 0) { + return null; + } + return (exchange.isSecure() ? "https://" : "http://") + host; + } + + /// Whether `origin` is the origin of `address`: the same scheme, host and + /// port, whatever path the address goes on to name. Scheme and host are + /// compared without regard to case, and without folding it -- that would + /// depend on the language the server runs in. + static boolean sameOrigin(String origin, String address) { + int path = address.indexOf('/', address.indexOf("://") + 3); + String root = path < 0 ? address : address.substring(0, path); + return origin.equalsIgnoreCase(root); + } + + /// Kept in the database, so a refresh token outlives a restart and is good + /// on every instance of the server. + @Bean + public OAuth2AuthorizationService authorizations(DataSource db) { + return new JdbcOAuth2AuthorizationService(db); + } + + /// The keys the tokens are signed with: the files named by + /// `cn1.security.authorizationserver.jwk.keys`. On a development profile + /// with none configured, a key made at start-up -- which is why signing in + /// again is needed after restarting a development server. + @Bean + public JwkSource signingKeys(Config config) throws IOException { + return AuthorizationServerKeys.load(config); + } + + /// Signing in. A session exists here and only here, for the few requests + /// between the password being accepted and the code being handed over; + /// the app ends it with `POST /logout` once it has its tokens, and one it + /// never ends runs out after `cn1.session.timeout` seconds without a + /// request, half an hour unless set. + @Bean + @Order(1) + public SecurityFilterChain signIn(HttpSecurity http, + @Value("${" + ISSUER_KEY + ":}") final String issuer, + @Value("${morsel.signin.per-minute:10}") int signInsPerMinute) throws IOException { + http.securityMatcher("/oauth2/**", "/.well-known/**", "/userinfo", "/login", "/login/**", + "/logout") + .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) + // No CSRF token: the app posts the sign-in form itself, from its own + // screen, and would have to fetch a token first to send one. What a + // token is for still has to be done, because the app also runs in a + // browser, where the session is a cookie the browser keeps and would + // send along with a form another site submitted -- signing its + // visitor in as somebody else. Two things stand in for it. The + // session cookie is `SameSite=Lax`, the server's default, so it does + // not travel with another site's request; and [#sameOrigin] below + // refuses any request that changes something and says it came from + // another site, which is the one a forged sign-in is. A server that + // signs people in through a page it serves keeps CSRF on. + .csrf(csrf -> csrf.disable()) + .addFilterBefore(sameOrigin(issuer), LogoutFilter.class) + // Signing out is a POST and nothing else -- with CSRF off the + // default takes a GET too, which an image tag on any page can send + // -- and is answered with a status, for the reason signing in is. + .logout(logout -> logout + .logoutRequestMatcher(AntPathRequestMatcher.antMatcher("POST", "/logout")) + .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(204))) + // And it is answered with a status, not a redirect to a page: + // the app in a browser cannot see a redirect or the cookie, only + // how the request ended, so that has to be the answer. The session + // cookie is set either way; the installed app reads it, a browser + // keeps it. + .formLogin(form -> form + .successHandler((request, authentication) -> new HttpServer.Response(200, + "application/json", "{\"signedIn\":true}".getBytes("UTF-8")) + .header("Cache-Control", "no-store")) + .failureHandler((request, refused) -> new HttpServer.Response(401, + "application/json", "{\"signedIn\":false}".getBytes("UTF-8")) + .header("Cache-Control", "no-store"))) + // Guessing passwords is the attack on this chain. Ten tries a minute + // from one address, unless `morsel.signin.per-minute` says otherwise: + // the end-to-end tests are many people signing in from one address, + // and their profile raises it. + .rateLimit("/login", RateLimitKeys.clientAddress(), + new InMemoryRateLimiter(signInsPerMinute, 60)) + .authorizationServer(server -> server + .settings(AuthorizationServerSettings.builder() + .defaultAudience(AUDIENCE).build())); + return http.build(); + } + + /// The API. Stateless: every request stands on its own bearer token. + @Bean + @Order(2) + public SecurityFilterChain api(HttpSecurity http, JwkSource signingKeys, + @Value("${" + ISSUER_KEY + ":}") final String issuer, + JdbcUserDetailsManager users) throws IOException { + DefaultJwtDecoder decoder = DefaultJwtDecoder.withJwkSource(signingKeys).build(); + decoder.setJwtValidator(JwtValidators.createDefaultWithValidators( + new JwtAudienceValidator(AUDIENCE), + issuer.length() > 0 ? new JwtIssuerValidator(issuer) + : new JwtClaimValidator("iss", SecurityConfig::issuedAtThisAddress))); + + // The token says who the caller is and nothing about what they may do. + // The roles are read from the account on every request instead of being + // copied into the token, for one reason: an admin who withdraws a role + // or suspends an account means now, not when the token runs out. It + // costs a lookup by primary key per request. + JwtAuthenticationConverter roles = new JwtAuthenticationConverter(); + roles.setJwtGrantedAuthoritiesConverter(jwt -> { + String name = jwt.getSubject(); + if (name == null || !users.userExists(name)) { + throw new DisabledException("No such account"); + } + UserDetails account = users.loadUserByUsername(name); + if (!account.isEnabled() || !account.isAccountNonLocked()) { + throw new DisabledException("This account is suspended"); + } + Collection held = new ArrayList(); + held.addAll(account.getAuthorities()); + return held; + }); + + http.securityMatcher("/api/**") + .sessionManagement(session -> + session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // Opening accounts and sending text messages are the two things + // here that cost something each time and that a stranger can ask + // for. One limiter per rule: a shared one would share its count. + .rateLimit("/api/account/register", RateLimitKeys.clientAddress(), + new InMemoryRateLimiter(20, 3600)) + .rateLimit("/api/account/phone/**", + RateLimitKeys.firstOf(RateLimitKeys.principal(), RateLimitKeys.clientAddress()), + new InMemoryRateLimiter(10, 600)) + // Guessing the current password with a token in hand, and filling + // the database with pictures: an hour's allowance of each is more + // than anyone honest needs. + .rateLimit("/api/account/password", + RateLimitKeys.firstOf(RateLimitKeys.principal(), RateLimitKeys.clientAddress()), + new InMemoryRateLimiter(10, 3600)) + // Every search and every dropped pin is a request this server + // makes of somebody else's geocoder, in the service's name. A + // person typing an address makes a few a second at most; a client + // in a loop would get the whole service refused by the provider. + .rateLimit("/api/geo/**", + RateLimitKeys.firstOf(RateLimitKeys.principal(), RateLimitKeys.clientAddress()), + new InMemoryRateLimiter(120, 60)) + .rateLimit("/api/onboarding/application/documents", + RateLimitKeys.firstOf(RateLimitKeys.principal(), RateLimitKeys.clientAddress()), + new InMemoryRateLimiter(60, 3600)) + // Every card setup is a session made at the payment processor. + // `Payments` caps how many an account has open; this caps how + // often it may start one, so abandoning setups in a loop is slow. + .rateLimit("/api/payments/methods/setup", + RateLimitKeys.firstOf(RateLimitKeys.principal(), RateLimitKeys.clientAddress()), + new InMemoryRateLimiter(30, 3600)) + // tag::morsel-api-rules[] + .authorizeHttpRequests(auth -> auth + .requestMatchers(AntPathRequestMatcher.antMatcher("POST", + "/api/account/register")).permitAll() + .requestMatchers("/api/courier/**").hasRole(Roles.COURIER) + .requestMatchers("/api/admin/**").hasRole(Roles.ADMIN) + .anyRequest().authenticated()) + // end::morsel-api-rules[] + .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(decoder) + .jwtAuthenticationConverter(roles))); + return http.build(); + } + + /// Whether a token's issuer is the address this request came to. + /// + /// Used only when no issuer is configured, which the server allows only on + /// a development profile: it then issues tokens in the name of whatever + /// address each request arrived at, so that is the name to hold a token to. + static boolean issuedAtThisAddress(Object issuer) { + SecurityExchange exchange = SecurityExchange.current(); + if (exchange == null || !(issuer instanceof String)) { + return false; + } + String host = exchange.getRequest().getHeader("Host"); + return host != null && ((exchange.isSecure() ? "https://" : "http://") + host) + .equals(issuer); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Services.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Services.java new file mode 100644 index 00000000000..42da36d9880 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Services.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Config; +import com.codename1.backend.annotations.Bean; +import com.codename1.backend.annotations.Configuration; +import com.codename1.backend.annotations.Value; +import com.codename1.backend.push.CloudPush; +import com.codenameone.examples.morsel.geo.Geocoder; +import com.codenameone.examples.morsel.geo.PhotonGeocoder; +import com.codenameone.examples.morsel.geo.StubGeocoder; +import com.codenameone.examples.morsel.pay.MisconfiguredPayments; +import com.codenameone.examples.morsel.pay.PaymentProvider; +import com.codenameone.examples.morsel.pay.SimulatedPayments; +import com.codenameone.examples.morsel.pay.StripePayments; +import com.codenameone.examples.morsel.push.CloudPushSender; +import com.codenameone.examples.morsel.push.ConsolePushSender; +import com.codenameone.examples.morsel.push.MisconfiguredPushSender; +import com.codenameone.examples.morsel.push.PushSender; +import com.codenameone.examples.morsel.push.RecordingPushSender; +import com.codenameone.examples.morsel.sms.ConsoleSmsSender; +import com.codenameone.examples.morsel.sms.MessageBirdSmsSender; +import com.codenameone.examples.morsel.sms.MisconfiguredSmsSender; +import com.codenameone.examples.morsel.sms.SmsSender; +import com.codenameone.examples.morsel.sms.TwilioSmsSender; +import com.codenameone.examples.morsel.sms.VonageSmsSender; + +import java.io.IOException; + +/// The services this server reaches outside itself for, each behind an +/// interface and each chosen here from the settings. +/// +/// Each is named by one setting -- `morsel.sms.provider`, +/// `morsel.payments.provider` and `morsel.push.provider` -- and, when that is +/// not set, a development profile (dev, test, local) gets the one that needs no +/// account: codes and notifications printed on the console, cards simulated. Any other profile gets the real provider, +/// and when its settings are missing the server still starts, logs a +/// configuration error naming each missing setting, and fails every call to +/// that provider with the same message. It never quietly falls back to a +/// development stand-in. +/// +/// Every setting is also read from the environment, in upper case with `_` +/// for each `.` (`MORSEL_SMS_TWILIO_TOKEN`), which is where secrets belong. +@Configuration +public class Services { + /// `morsel.sms.provider` is one of: + /// + /// - `twilio`: `morsel.sms.twilio.sid`, `.token` and `.from` + /// - `vonage`: `morsel.sms.vonage.key`, `.secret` and `.from` + /// - `messagebird`: `morsel.sms.messagebird.key` and `.originator` + /// - `console`: prints the message, on a development profile only + /// + /// Unset, it is `console` on a development profile and `twilio` otherwise. + @Bean + public SmsSender smsSender(Config config, + @Value("${morsel.sms.provider:}") String chosen, + @Value("${morsel.sms.twilio.sid:}") String twilioSid, + @Value("${morsel.sms.twilio.token:}") String twilioToken, + @Value("${morsel.sms.twilio.from:}") String twilioFrom, + @Value("${morsel.sms.vonage.key:}") String vonageKey, + @Value("${morsel.sms.vonage.secret:}") String vonageSecret, + @Value("${morsel.sms.vonage.from:}") String vonageFrom, + @Value("${morsel.sms.messagebird.key:}") String birdKey, + @Value("${morsel.sms.messagebird.originator:}") String birdOriginator) { + String kind = chosen.trim().length() > 0 ? chosen.trim() + : config.isDevelopmentProfile() ? "console" : "twilio"; + if ("console".equalsIgnoreCase(kind)) { + if (!config.isDevelopmentProfile()) { + return new MisconfiguredSmsSender("morsel.sms.provider=console only prints codes " + + "and is refused on the '" + config.getProfile() + "' profile; set it to " + + "twilio, vonage or messagebird"); + } + return new ConsoleSmsSender(); + } + if ("twilio".equalsIgnoreCase(kind)) { + String missing = missing("morsel.sms.twilio.", new String[] {"sid", twilioSid, + "token", twilioToken, "from", twilioFrom}); + return missing.length() > 0 ? new MisconfiguredSmsSender(unset("SMS", "twilio", + missing)) : new TwilioSmsSender(twilioSid, twilioToken, twilioFrom); + } + // tag::morsel-sms-choice[] + if ("vonage".equalsIgnoreCase(kind)) { + String missing = missing("morsel.sms.vonage.", new String[] {"key", vonageKey, + "secret", vonageSecret, "from", vonageFrom}); + return missing.length() > 0 ? new MisconfiguredSmsSender(unset("SMS", "vonage", + missing)) : new VonageSmsSender(vonageKey, vonageSecret, vonageFrom); + } + // end::morsel-sms-choice[] + if ("messagebird".equalsIgnoreCase(kind)) { + String missing = missing("morsel.sms.messagebird.", new String[] {"key", birdKey, + "originator", birdOriginator}); + return missing.length() > 0 ? new MisconfiguredSmsSender(unset("SMS", "messagebird", + missing)) : new MessageBirdSmsSender(birdKey, birdOriginator); + } + return new MisconfiguredSmsSender("morsel.sms.provider is '" + kind + "', which is " + + "none of twilio, vonage, messagebird or console"); + } + + /// Photon, at `morsel.geocoder.url`. `morsel.geocoder=stub` selects the + /// one that knows ten places and asks nobody, and it is what the `test` + /// profile gets unless told otherwise. + @Bean + public Geocoder geocoder(Config config, @Value("${morsel.geocoder:}") String chosen, + @Value("${morsel.geocoder.url:https://photon.komoot.io}") String address) + throws IOException { + String kind = chosen.length() > 0 ? chosen + : "test".equals(config.getProfile()) ? "stub" : "photon"; + if ("stub".equals(kind)) { + return new StubGeocoder(); + } + return new PhotonGeocoder(address); + } + + // tag::morsel-payment-choice[] + /// `morsel.payments.provider` is one of: + /// + /// - `stripe`: [StripePayments], an untested stub over Stripe's REST API. + /// It needs `morsel.payments.stripe.secret`. + /// - `simulated`: test card numbers, and no money moves. + /// + /// Unset, it is `simulated` on a development profile and `stripe` otherwise. + /// + /// With Stripe a card is typed into Stripe's own page, which then sends the + /// browser back to `/pay/return` on this server. That address has to be the + /// one a phone can reach, so it is `morsel.public.url` when that is set + /// and the issuer -- the public address of this server, which a deployment + /// sets anyway -- when it is not. + @Bean + public PaymentProvider paymentProvider(Config config, + @Value("${morsel.payments.provider:}") String chosen, + @Value("${morsel.payments.stripe.secret:}") String secret, + @Value("${morsel.public.url:}") String publicUrl, + @Value("${" + SecurityConfig.ISSUER_KEY + ":}") String issuer) { + String kind = chosen.trim().length() > 0 ? chosen.trim() + : config.isDevelopmentProfile() ? "simulated" : "stripe"; + if ("simulated".equalsIgnoreCase(kind)) { + return new SimulatedPayments(); + } + if (!"stripe".equalsIgnoreCase(kind)) { + return new MisconfiguredPayments(kind, "morsel.payments.provider is '" + kind + + "', which is neither stripe nor simulated"); + } + String address = publicUrl.length() > 0 ? publicUrl : issuer; + String missing = missing("", new String[] {"morsel.payments.stripe.secret", secret, + "morsel.public.url (or " + SecurityConfig.ISSUER_KEY + ")", address}); + if (missing.length() > 0) { + return new MisconfiguredPayments("stripe", unset("payments", "stripe", missing)); + } + while (address.endsWith("/")) { + address = address.substring(0, address.length() - 1); + } + return new StripePayments(secret, address + "/pay/return"); + } + // end::morsel-payment-choice[] + + // tag::morsel-push-choice[] + /// `morsel.push.provider` is one of: + /// + /// - `cloud`: the Codename One push service. It needs `morsel.push.app`, + /// the app's push id, and `morsel.push.key`, a server key with the push + /// scope; both are in the Push section of the Codename One console. + /// - `console`: prints each notification, on a development profile only. + /// - `recording`: keeps each notification for the tests to read, on a + /// development profile only. + /// + /// Unset, it is `recording` on the `test` profile, `console` on the other + /// development profiles and `cloud` otherwise. A server whose push settings + /// are missing still starts and still serves deliveries: it logs a configuration + /// error at start-up and again for every notification it could not send. + @Bean + public PushSender pushSender(Config config, + @Value("${morsel.push.provider:}") String chosen, + @Value("${morsel.push.app:}") String app, + @Value("${morsel.push.key:}") String key, + @Value("${morsel.push.url:}") String url) { + String kind = chosen.trim().length() > 0 ? chosen.trim() + : "test".equals(config.getProfile()) ? "recording" + : config.isDevelopmentProfile() ? "console" : "cloud"; + if ("console".equalsIgnoreCase(kind) || "recording".equalsIgnoreCase(kind)) { + if (!config.isDevelopmentProfile()) { + return new MisconfiguredPushSender("morsel.push.provider=" + kind + + " sends nothing and is refused on the '" + config.getProfile() + + "' profile; set it to cloud"); + } + return "console".equalsIgnoreCase(kind) ? new ConsolePushSender() + : new RecordingPushSender(); + } + if (!"cloud".equalsIgnoreCase(kind)) { + return new MisconfiguredPushSender("morsel.push.provider is '" + kind + + "', which is none of cloud, console or recording"); + } + String missing = missing("morsel.push.", new String[] {"app", app, "key", key}); + if (missing.length() > 0) { + return new MisconfiguredPushSender(unset("push", "cloud", missing)); + } + try { + CloudPush push = new CloudPush(app.trim(), key.trim()); + return new CloudPushSender(url.trim().length() > 0 ? push.withEndpoint(url.trim()) + : push); + } catch (IllegalArgumentException err) { + return new MisconfiguredPushSender("push provider 'cloud' is misconfigured: " + + err.getMessage()); + } + } + // end::morsel-push-choice[] + + /// The names, after `prefix`, of the settings in `namesAndValues` whose + /// value is empty, separated by commas. + private static String missing(String prefix, String[] namesAndValues) { + StringBuilder out = new StringBuilder(); + for (int iter = 0; iter + 1 < namesAndValues.length; iter += 2) { + if (namesAndValues[iter + 1] == null || namesAndValues[iter + 1].trim().length() == 0) { + if (out.length() > 0) { + out.append(", "); + } + out.append(prefix).append(namesAndValues[iter]); + } + } + return out.toString(); + } + + private static String unset(String service, String provider, String missing) { + return service + " provider '" + provider + "' is not configured; set " + missing; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SignInCodePage.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SignInCodePage.java new file mode 100644 index 00000000000..1d5f08a1a79 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SignInCodePage.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.HttpServer; +import com.codename1.backend.Json; +import com.codename1.backend.annotations.GetMapping; +import com.codename1.backend.annotations.RequestParam; +import com.codename1.backend.annotations.RestController; + +import java.util.LinkedHashMap; +import java.util.Map; + +/// Where a sign-in from a browser ends: the authorization code, handed back. +/// +/// The installed app asks for its code to be sent to a loopback address and +/// reads it off the redirect without following it. A page in a browser cannot +/// do that -- the browser follows the redirect itself and shows the page only +/// what it found at the end -- so the app running in one asks for the code to +/// be sent here, and what is at the end is the code. +/// +/// Nothing is decided here and nothing is trusted: the code arrived in the +/// address, so repeating it tells the caller nothing it did not have, and it +/// is worth nothing without the PKCE verifier only the app that started the +/// sign-in holds. That is why this needs no sign-in of its own. +@RestController +public class SignInCodePage { + @GetMapping(SecurityConfig.WEB_REDIRECT_PATH) + public HttpServer.Response code( + @RequestParam(value = "code", required = false) String code, + @RequestParam(value = "state", required = false) String state, + @RequestParam(value = "error", required = false) String error) throws Exception { + Map answer = new LinkedHashMap(); + if (code != null) { + answer.put("code", code); + } + if (state != null) { + answer.put("state", state); + } + if (error != null) { + answer.put("error", error); + } + return new HttpServer.Response(code == null ? 400 : 200, "application/json", + Json.write(answer).getBytes("UTF-8")) + // A code is good once, for a few minutes; nothing should keep it. + .header("Cache-Control", "no-store") + // And nothing should read it as anything but data: the values + // come from the address, which anyone can write a link to. + .header("X-Content-Type-Options", "nosniff"); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Text.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Text.java new file mode 100644 index 00000000000..a11c6d87edb --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Text.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.ResponseStatusException; + +/// What the app sends, made safe to store: trimmed, bounded, and one of the +/// words a field allows. +public final class Text { + private Text() { + } + + /// `value` trimmed, or empty for null; answers 400 when longer than `max`. + public static String optional(String value, int max, String what) { + String text = value == null ? "" : value.trim(); + if (text.length() > max) { + throw new ResponseStatusException(400, "That is too long for " + what); + } + return text; + } + + /// `value` trimmed; answers 400 when it is empty or longer than `max`. + public static String required(String value, int max, String what) { + String text = optional(value, max, what); + if (text.length() == 0) { + throw new ResponseStatusException(400, "Tell us " + what); + } + return text; + } + + /// `value` when it is one of `allowed`, and `fallback` when it is empty; + /// answers 400 for anything else. The words are compared as written: they + /// are the app's own constants, not something a person types. + public static String oneOf(String value, String[] allowed, String fallback, String what) { + String text = value == null ? "" : value.trim(); + if (text.length() == 0) { + return fallback; + } + for (int iter = 0; iter < allowed.length; iter++) { + if (allowed[iter].equals(text)) { + return text; + } + } + throw new ResponseStatusException(400, "That is not a value for " + what); + } + + /// The first letters of the first and last words of a name, in upper case + /// when they are ASCII letters: "Dana Courier" is "DD". + public static String initials(String name) { + String text = name == null ? "" : name.trim(); + if (text.length() == 0) { + return ""; + } + int last = text.lastIndexOf(' '); + StringBuilder out = new StringBuilder(2); + out.append(upper(text.charAt(0))); + if (last > 0 && last + 1 < text.length()) { + out.append(upper(text.charAt(last + 1))); + } + return out.toString(); + } + + private static char upper(char c) { + // By hand: toUpperCase() follows the server's locale. + return c >= 'a' && c <= 'z' ? (char) (c - ('a' - 'A')) : c; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountCleanup.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountCleanup.java new file mode 100644 index 00000000000..1775be4aef0 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountCleanup.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.repository.CourierHoursRepository; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.LiveTicketRepository; +import com.codenameone.examples.morsel.repository.PayoutAccountRepository; +import com.codenameone.examples.morsel.repository.PhoneCodeRepository; +import com.codenameone.examples.morsel.repository.PreferenceRepository; +import com.codenameone.examples.morsel.repository.PushDeviceRepository; +import com.codenameone.examples.morsel.repository.DeliveryActivityRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; + +/// What closing or restricting an account does to the rows other parts of the +/// server keep under its name. +/// +/// These are questions and statements over whole sets of rows -- everything +/// of one account in a table -- and not the work of the services that own +/// those tables, which is why they are gathered here. Each deletion is a bulk +/// statement, so whatever the session had loaded before it is stale +/// afterwards and must be read again before it is used. It has no +/// transaction of its own: [Accounts] calls it inside one. +@Component +public class AccountCleanup { + /// The states of a delivery that somebody is still waiting on. + private static final DeliveryState[] UNDER_WAY = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + /// The states of a delivery a courier has taken and has yet to finish. + private static final DeliveryState[] TAKEN = {DeliveryState.ACCEPTED, DeliveryState.ARRIVED, + DeliveryState.IN_PROGRESS}; + + private final DeliveryRepository deliveries; + private final CourierStateRepository couriers; + private final CourierHoursRepository hours; + private final PreferenceRepository preferences; + private final PayoutAccountRepository payoutAccounts; + private final PhoneCodeRepository codes; + private final LiveTicketRepository tickets; + private final PushDeviceRepository pushDevices; + private final DeliveryActivityRepository activities; + + public AccountCleanup(DeliveryRepository deliveries, CourierStateRepository couriers, + CourierHoursRepository hours, PreferenceRepository preferences, + PayoutAccountRepository payoutAccounts, PhoneCodeRepository codes, + LiveTicketRepository tickets, PushDeviceRepository pushDevices, + DeliveryActivityRepository activities) { + this.deliveries = deliveries; + this.couriers = couriers; + this.hours = hours; + this.preferences = preferences; + this.payoutAccounts = payoutAccounts; + this.codes = codes; + this.tickets = tickets; + this.pushDevices = pushDevices; + this.activities = activities; + } + + /// Whether the account is the customer or the courier of a delivery that has not + /// ended. + public boolean hasDeliveryUnderWay(String username) { + return deliveries.existsByCustomerAndStateInOrCourierAndStateIn(username, UNDER_WAY, username, + UNDER_WAY); + } + + /// Whether the account is onboarding a delivery it has taken and not finished. An + /// offer it has not answered is not one. + public boolean drivesDeliveryUnderWay(String username) { + return deliveries.existsByCourierAndStateIn(username, TAKEN); + } + + /// Whether the account is in a delivery that has not ended: as its customer, from + /// the moment it is asked for, or as its courier, once taken. An offer the + /// courier has not answered is not one; it passes to the next courier. + public boolean inDeliveryUnderWay(String username) { + return deliveries.existsByCustomerAndStateInOrCourierAndStateIn(username, UNDER_WAY, username, + TAKEN); + } + + /// Takes a courier off line, when the account stops being allowed to drive. + /// Does nothing for an account that never drove. + public void takeOffLine(String username) { + couriers.takeOffLine(username); + } + + /// Deletes what an account leaves in the tables that are keyed by it and + /// belong to nobody's `forget`: its preferences, where it is paid, the + /// hours it drove, its place on the map, a code it was texted, the + /// tickets it held for the live connection, and the phones it was sent + /// notifications on. + public void removeEverythingOf(String username) { + preferences.deleteByUsername(username); + payoutAccounts.deleteByUsername(username); + hours.deleteByUsername(username); + couriers.deleteByUsername(username); + codes.deleteByUsername(username); + tickets.deleteByUsername(username); + pushDevices.deleteByUsername(username); + activities.deleteByUsername(username); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountEndpoint.java new file mode 100644 index 00000000000..4487b930765 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/AccountEndpoint.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.Caller; +import com.codenameone.examples.morsel.api.AccountApiServer; +import com.codenameone.examples.morsel.api.AccountClosureDto; +import com.codenameone.examples.morsel.api.PasswordChangeDto; +import com.codenameone.examples.morsel.api.PhoneChallengeDto; +import com.codenameone.examples.morsel.api.PhoneCodeDto; +import com.codenameone.examples.morsel.api.PreferencesDto; +import com.codenameone.examples.morsel.api.ProfileDto; +import com.codenameone.examples.morsel.api.PushDeviceDto; +import com.codenameone.examples.morsel.api.RegisterDto; +import com.codenameone.examples.morsel.api.TicketDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.live.LiveTickets; +import com.codenameone.examples.morsel.push.PushDevices; + +/// The server's half of `AccountApi`. +/// +/// `AccountApiServer` is generated from the contract the app calls through, and +/// the build routes each of the contract's paths to this class. Change a path or +/// a type in the contract and both sides stop compiling until they agree. +@Component +public class AccountEndpoint implements AccountApiServer { + private final Accounts accounts; + private final PhoneVerification phones; + private final LiveTickets tickets; + private final Preferences preferences; + private final PushDevices devices; + + public AccountEndpoint(Accounts accounts, PhoneVerification phones, LiveTickets tickets, + Preferences preferences, PushDevices devices) { + this.accounts = accounts; + this.phones = phones; + this.tickets = tickets; + this.preferences = preferences; + this.devices = devices; + } + + @Override + public UserDto register(RegisterDto request) throws Exception { + return accounts.register(request); + } + + @Override + public UserDto me() throws Exception { + return accounts.describe(Caller.name()); + } + + @Override + public PhoneChallengeDto startPhoneVerification() throws Exception { + return phones.start(Caller.name()); + } + + @Override + public UserDto verifyPhone(PhoneCodeDto code) throws Exception { + return phones.verify(Caller.name(), code == null ? null : code.code()); + } + + @Override + public TicketDto liveTicket() throws Exception { + return tickets.issue(Caller.name()); + } + + @Override + public PreferencesDto preferences() throws Exception { + return preferences.get(Caller.name()); + } + + @Override + public PreferencesDto savePreferences(PreferencesDto wanted) throws Exception { + return preferences.save(Caller.name(), wanted); + } + + @Override + public ProfileDto profile() throws Exception { + return accounts.profile(Caller.name()); + } + + @Override + public UserDto saveProfile(ProfileDto profile) throws Exception { + return accounts.saveProfile(Caller.name(), profile); + } + + @Override + public UserDto changePassword(PasswordChangeDto change) throws Exception { + return accounts.changePassword(Caller.name(), change == null ? null : change.current(), + change == null ? null : change.replacement()); + } + + @Override + public UserDto deleteAccount(AccountClosureDto closure) throws Exception { + return accounts.close(Caller.name(), closure == null ? null : closure.password()); + } + + @Override + public PushDeviceDto registerPush(PushDeviceDto device) throws Exception { + return devices.register(Caller.name(), device); + } + + @Override + public PushDeviceDto unregisterPush(PushDeviceDto device) throws Exception { + return devices.unregister(Caller.name(), device); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java new file mode 100644 index 00000000000..2adc7e51c98 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.security.GrantedAuthority; +import com.codename1.backend.security.core.userdetails.JdbcUserDetailsManager; +import com.codename1.backend.security.core.userdetails.User; +import com.codename1.backend.security.core.userdetails.UserDetails; +import com.codename1.backend.security.core.userdetails.UsernameNotFoundException; +import com.codename1.backend.security.crypto.PasswordEncoder; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.Roles; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.api.ProfileDto; +import com.codenameone.examples.morsel.api.RegisterDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.domain.ClosedAccount; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.courier.Earnings; +import com.codenameone.examples.morsel.onboarding.Applications; +import com.codenameone.examples.morsel.live.LiveHub; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.repository.ClosedAccountRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/// Accounts: opening one, describing one, and what an admin may change. +/// +/// An account is two things kept under one name. Its sign-in -- password hash, +/// roles, whether it is enabled -- is the backend's own user store. Its profile +/// -- display name, phone, car -- is this application's table. The name is the +/// e-mail address, in lower case. +/// +/// Onboarding is not something an account is opened with. Asking to drive opens +/// an application ([Applications]), and the COURIER role arrives when an admin +/// approves it. +@Component +public class Accounts { + public static final String[] GENDERS = {"unspecified", "female", "male", "nonbinary"}; + + private final ProfileRepository profiles; + private final AccountCleanup cleanup; + private final JdbcUserDetailsManager users; + private final PasswordEncoder encoder; + private final Applications applications; + private final Moderation moderation; + private final Payments payments; + private final LiveHub live; + private final ClosedAccountRepository closed; + private final Grants grants; + private final Earnings earnings; + + public Accounts(ProfileRepository profiles, AccountCleanup cleanup, + JdbcUserDetailsManager users, PasswordEncoder encoder, Applications applications, + Moderation moderation, Payments payments, LiveHub live, + ClosedAccountRepository closed, Grants grants, Earnings earnings) { + this.earnings = earnings; + this.closed = closed; + this.grants = grants; + this.profiles = profiles; + this.cleanup = cleanup; + this.users = users; + this.encoder = encoder; + this.applications = applications; + this.moderation = moderation; + this.payments = payments; + this.live = live; + } + + /// Opens an account. Answers 400 for a request that cannot be an account + /// and 409 when the e-mail already has one. + /// + /// Not a transaction itself, so that it can look again after one failed. + /// Two requests for the same e-mail can both find it free; the name's + /// unique key then refuses the second, and what the database says about + /// that differs with the database. So nothing here reads the error: if the + /// account exists once the transaction is over, that is why it failed, and + /// the answer is the same 409 the check gives. + public UserDto register(RegisterDto request) throws IOException { + try { + return open(request); + } catch (ResponseStatusException answered) { + throw answered; + } catch (IOException failed) { + throw taken(request, failed); + } catch (RuntimeException failed) { + throw taken(request, failed); + } + } + + /// The 409 for a registration that failed because the account was opened + /// meanwhile, or the failure itself when it was something else. + private T taken(RegisterDto request, T failed) { + if (request != null && request.email() != null + && users.userExists(email(request.email()))) { + throw new ResponseStatusException(409, "That e-mail already has an account"); + } + return failed; + } + + @Transactional(rollbackFor = IOException.class) + private UserDto open(RegisterDto request) throws IOException { + if (request == null) { + throw new ResponseStatusException(400, "Nothing to register"); + } + String username = email(request.email()); + password(request.password()); + String gender = Text.oneOf(request.gender(), GENDERS, GENDERS[0], "gender"); + String displayName = required(request.displayName(), 120, "your name"); + String phone = phone(request.phone()); + String vehicle = ""; + String plate = ""; + if (request.courier()) { + vehicle = required(request.vehicle(), 120, "the car you drive"); + plate = required(request.plate(), 32, "its plate"); + } + if (users.userExists(username)) { + throw new ResponseStatusException(409, "That e-mail already has an account"); + } + // An address whose account was closed is not given to anyone again: + // the deliveries that account took are still filed under it. See + // [#retire]. + if (closed.existsById(Ids.sha256(username))) { + throw new ResponseStatusException(409, + "That e-mail belonged to an account that was closed. Use another"); + } + // Every account opens as a customer. Asking to drive -- `courier` is how an + // older app asks -- begins an application, and the role waits for an + // admin to approve it. + create(username, request.password(), displayName, phone, false, vehicle, plate, false, + false, gender, System.currentTimeMillis()); + if (request.courier() || request.wantsToDrive()) { + applications.open(username, vehicle, plate); + } + return describe(username); + } + + /// Creates the sign-in and the profile. The caller has checked the name is + /// free. An account created as a courier is an approved one: this is how the + /// demo accounts are made, not how anyone signs up. + @Transactional(rollbackFor = IOException.class) + void create(String username, String password, String displayName, String phone, + boolean phoneVerified, String vehicle, String plate, boolean courier, boolean admin) + throws IOException { + create(username, password, displayName, phone, phoneVerified, vehicle, plate, courier, + admin, GENDERS[0], System.currentTimeMillis()); + } + + /// As the method above, for a caller that also knows the gender and when + /// the account was opened; the demo data is the one caller with a past to + /// write. The profile is stored whole, in one go, and not corrected + /// afterwards. + @Transactional(rollbackFor = IOException.class) + void create(String username, String password, String displayName, String phone, + boolean phoneVerified, String vehicle, String plate, boolean courier, boolean admin, + String gender, long openedAt) throws IOException { + users.createUser(User.withUsername(username).password(encoder.encode(password)) + .roles(roles(courier, admin)).build()); + Profile profile = new Profile(); + profile.setUsername(username); + profile.setDisplayName(displayName); + profile.setPhone(phone); + profile.setPhoneVerified(phoneVerified); + profile.setVehicle(vehicle); + profile.setPlate(plate); + profile.setCreatedAt(openedAt); + profile.setGender(gender); + profiles.insert(profile); + if (courier) { + applications.grant(username, username); + } + } + + public boolean exists(String username) { + return users.userExists(username); + } + + /// The account as the app sees it. Answers 404 for a name with no account. + @Transactional(readOnly = true) + public UserDto describe(String username) throws IOException { + Profile profile = profiles.findById(username); + if (profile == null || !users.userExists(username)) { + throw new ResponseStatusException(404, "No such account"); + } + UserDetails signIn = users.loadUserByUsername(username); + return toDto(profile, authorities(signIn), signIn.isEnabled(), + applications.status(username)); + } + + /// Every account, newest first, for the admin. + @Transactional(readOnly = true) + public List all() throws IOException { + Map onboarding = applications.statuses(); + List out = new ArrayList(); + List everyone = profiles.newestFirst(); + for (int iter = 0; iter < everyone.size(); iter++) { + Profile profile = everyone.get(iter); + // One sign-in read per account. The user store answers for one name + // at a time and has no way to list its users or to load several at + // once, and its tables are its own, not this application's to + // query. That is two statements an account, which the admin's list + // can afford; a store that could load them together would be asked + // once. + List held = new ArrayList(); + boolean enabled = true; + try { + UserDetails signIn = users.loadUserByUsername(profile.getUsername()); + held = authorities(signIn); + enabled = signIn.isEnabled(); + } catch (UsernameNotFoundException gone) { + // A profile whose sign-in was deleted under it is listed with + // no roles rather than failing the whole list. + enabled = true; + } + String status = onboarding.get(profile.getUsername()); + out.add(toDto(profile, held, enabled, status == null ? Applications.NONE : status)); + } + return out; + } + + /// Grants or withdraws onboarding and administering. `by` is the admin doing + /// it, who cannot withdraw their own admin role: that is how a service ends + /// up with nobody able to administer it. + @Transactional(rollbackFor = IOException.class) + public UserDto setRoles(String by, String username, boolean courier, boolean admin) + throws IOException { + actingAdmin(by, username); + UserDetails signIn = load(username); + if (!admin && username.equals(by)) { + throw new ResponseStatusException(400, "You cannot withdraw your own admin role"); + } + // Not from under a customer. The role is read on every request, so a + // courier who lost it mid-delivery could no longer arrive, start, complete + // or cancel, and the trip would sit there until somebody noticed. The + // admin ends the delivery first, or waits for it to end. + if (!courier && cleanup.drivesDeliveryUnderWay(username)) { + throw new ResponseStatusException(409, + "That courier is on a delivery. Withdraw the role when it has ended"); + } + // A courier can lose the role with earnings not yet cashed out, and + // the payouts are courier routes. The money is still theirs in the + // ledger, and settling it is the admin's to arrange -- the role given + // back for the cash-out, or paid outside the app. A removal for cause + // must not wait on a payout, so it is not refused here. + users.updateUser(User.withUserDetails(signIn).roles(roles(courier, admin)).build()); + // The role and the approval go together, whichever way it was given: + // being approved is what lets a courier go on line. + if (courier) { + applications.grant(by, username); + } else { + applications.withdraw(by, username); + cleanup.takeOffLine(username); + } + moderation.record(username, by, Moderation.ROLES, + (courier ? "courier" : "customer") + (admin ? ", admin" : "")); + return describe(username); + } + + /// Blocks an account or lifts the block. A blocked account cannot sign in, + /// and a token it already holds stops working at once, because the API + /// reads the account on every request. + @Transactional(rollbackFor = IOException.class) + public UserDto suspend(String by, String username, boolean suspended, String reason) + throws IOException { + actingAdmin(by, username); + UserDetails signIn = load(username); + if (suspended && username.equals(by)) { + throw new ResponseStatusException(400, "You cannot suspend your own account"); + } + String why = Text.optional(reason, 255, "the reason"); + // Not in the middle of a delivery, for the reason a courier's role is not + // withdrawn then: a blocked account's requests are all refused, so a + // courier could no longer arrive, start, finish or cancel, and a customer + // could not cancel, and the other person would be left with a delivery + // nobody can end. The admin cancels the delivery first, or waits. + if (suspended && signIn.isEnabled() && cleanup.inDeliveryUnderWay(username)) { + throw new ResponseStatusException(409, + "That account is on a delivery. Block it when the delivery has ended"); + } + users.updateUser(User.withUserDetails(signIn).disabled(suspended).build()); + if (suspended) { + cleanup.takeOffLine(username); + } + if (suspended == signIn.isEnabled()) { + // Only a change is an event; saying the same thing twice is not. + moderation.record(username, by, suspended ? Moderation.BLOCK : Moderation.UNBLOCK, why); + } + return describe(username); + } + + /// Marks an account for the admins' attention, or clears the mark. A flag + /// changes nothing the account can do; blocking is what does that. + @Transactional(rollbackFor = IOException.class) + public UserDto flag(String by, String username, boolean flagged, String reason) + throws IOException { + // The profile row first, as the other admin changes hold it: a flag + // racing the account's closure either lands before erase() (which + // then forgets it with the rest) or finds no account. Otherwise the + // moderation row recorded below would outlive the closure's cleanup. + if (profiles.hold(username) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + load(username); + String why = Text.optional(reason, 255, "the reason"); + if (flagged && why.length() == 0) { + throw new ResponseStatusException(400, "Say why the account is flagged"); + } + Profile profile = profiles.findById(username); + if (profile != null) { + profile.setFlagged(flagged); + profile.setFlagReason(flagged ? why : ""); + } + moderation.record(username, by, flagged ? Moderation.FLAG : Moderation.UNFLAG, why); + return describe(username); + } + + // ------------------------------------------------------------ one's own + + @Transactional(readOnly = true) + public ProfileDto profile(String username) throws IOException { + Profile row = profiles.findById(username); + if (row == null) { + throw new ResponseStatusException(404, "No such account"); + } + return new ProfileDto(row.getDisplayName(), row.getGender(), row.getEmergencyName(), + row.getEmergencyPhone(), row.getHomeAddress(), row.getWorkAddress()); + } + + @Transactional(rollbackFor = IOException.class) + public UserDto saveProfile(String username, ProfileDto profile) throws IOException { + if (profile == null) { + throw new ResponseStatusException(400, "Nothing to save"); + } + String contact = profile.emergencyContactPhone() == null ? "" + : profile.emergencyContactPhone().trim(); + // Everything is checked before anything is assigned: the row is written + // from what it holds when the transaction commits. + String name = required(profile.name(), 120, "your name"); + String gender = Text.oneOf(profile.gender(), GENDERS, GENDERS[0], "gender"); + String emergencyName = Text.optional(profile.emergencyContactName(), 120, + "the contact's name"); + String emergencyPhone = contact.length() == 0 ? "" : phone(contact); + String home = Text.optional(profile.homeAddress(), 255, "an address"); + String work = Text.optional(profile.workAddress(), 255, "an address"); + Profile row = profiles.findById(username); + if (row == null) { + throw new ResponseStatusException(404, "No such account"); + } + row.setDisplayName(name); + row.setGender(gender); + row.setEmergencyName(emergencyName); + row.setEmergencyPhone(emergencyPhone); + row.setHomeAddress(home); + row.setWorkAddress(work); + return describe(username); + } + + /// Changes a password, for someone who knows the one it replaces. A token + /// in hand is not enough: it is what a borrowed phone has. The account's + /// other sign-ins end with the old password. + @Transactional(rollbackFor = IOException.class) + public UserDto changePassword(String username, String current, String replacement) + throws IOException { + // Before the password is read. Two changes at once would otherwise + // both check the same old password and both succeed, the last one + // winning: whoever else knows the old password could race the owner's + // change and keep the account. Behind the hold the second change + // checks against the password the first one set. + profiles.hold(username); + UserDetails signIn = load(username); + if (current == null || !encoder.matches(current, signIn.getPassword())) { + throw new ResponseStatusException(403, "That is not your current password"); + } + password(replacement); + users.updatePassword(signIn, encoder.encode(replacement)); + // A password is changed, as often as not, because someone else may + // have the old one or a device that was signed in with it. A refresh + // token asks for no password, so every sign-in but this one ends here. + grants.endOthers(username); + return describe(username); + } + + /// Closes an account, for good: the sign-in, the profile, the saved cards + /// and the onboarding application with its documents are deleted, not hidden. + /// The deliveries it took stay, as the other party's history and the service's + /// accounts, under a name that no longer has anyone behind it. + /// + /// Refused while a delivery is under way, which has a courier or a customer waiting + /// on it, and for an admin, so that a service is not left with none. + /// + /// `password` is the account's own, asked for again as a change of + /// password asks for it. A token alone is not enough for something that + /// cannot be undone: a phone left unlocked, or a token that leaked, would + /// otherwise be all it takes to destroy an account. + public UserDto close(String username, String password) throws IOException { + UserDto was = erase(username, password); + // After the commit and not inside it: what is said over a live + // connection is acted on at once, and must not be said about a change + // that could still be rolled back. + live.drop(username); + // The account as it was, answered as what it now is: unable to sign in. + return new UserDto(was.username(), was.displayName(), was.phone(), was.phoneVerified(), + was.customer(), was.courier(), was.admin(), true, was.vehicle(), was.plate(), + was.gender(), was.courierStatus(), was.flagged(), was.flagReason(), + was.language()); + } + + /// Everything closing an account deletes, as one transaction: either the + /// account is gone from every table or it is in all of them still. The + /// user store works on the same database and so takes part in it. + @Transactional(rollbackFor = IOException.class) + UserDto erase(String username, String password) throws IOException { + // Before anything is read. Asking for a delivery holds the same row, so + // of a delivery and a closure sent together one waits for the other: the + // closure then sees the delivery and is refused, or the delivery finds no + // profile. Without it both could pass, leaving a delivery under way that + // belongs to nobody who can cancel it. + profiles.hold(username); + UserDetails signIn = load(username); + if (password == null || !encoder.matches(password, signIn.getPassword())) { + throw new ResponseStatusException(403, "That is not your current password"); + } + UserDto was = describe(username); + if (was.admin()) { + throw new ResponseStatusException(409, "Hand the admin role on before closing"); + } + if (cleanup.hasDeliveryUnderWay(username)) { + throw new ResponseStatusException(409, "Finish your delivery before closing the account"); + } + // Money this courier is owed. The deliveries and the payout ledger outlive + // the account, but the sign-in and the payout account do not, and + // the name is retired: once closed, nobody could cash it out. So the + // courier cashes out first. No delivery is under way, so nothing can add + // to the balance between this check and the commit. + if (earnings.balance(username) > 0L) { + throw new ResponseStatusException(409, "Cash out your earnings before closing the account"); + } + payments.forget(username); + applications.forget(username); + moderation.forget(username); + cleanup.removeEverythingOf(username); + // Read again: the statements above each cleared what the session had + // loaded, the profile that was described among it. + Profile profile = profiles.findById(username); + if (profile != null) { + profiles.deleteById(username); + } + // The address is retired with the account, in the same transaction: + // nobody can register it and inherit the deliveries kept under it. + retire(username); + // Last: the tokens in hand stop working when the sign-in is gone. + users.deleteUser(username); + grants.endAll(username); + return was; + } + + /// Retires an e-mail for good, as only a hash of it. + /// + /// A delivery outlives the account that took it, under the e-mail that account + /// signed in with, and so does a token until it expires. Were the address + /// free to register again, whoever took it would be handed the deliveries, the + /// receipts and the ratings of the person who left, and that person's old + /// token would sign in to the new account. Only a hash is kept: enough to + /// refuse it, and not a list of who has left. + private void retire(String username) throws IOException { + String hash = Ids.sha256(username); + if (!closed.existsById(hash)) { + ClosedAccount row = new ClosedAccount(); + row.setUsernameHash(hash); + row.setClosedAt(System.currentTimeMillis()); + closed.insert(row); + } + } + + /// Makes the rest of the caller's transaction take its turn with any other + /// that holds the same account. See [ProfileRepository#hold]. Answers 404 + /// when the account is gone: it was closed while the request was under + /// way, its clean-up has run, and a row the caller went on to insert would + /// never be deleted. + public void hold(String username) { + if (profiles.hold(username) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + } + + @Transactional(rollbackFor = IOException.class) + public void markPhoneVerified(String username) throws IOException { + Profile profile = profiles.findById(username); + if (profile != null) { + profile.setPhoneVerified(true); + } + } + + /// Makes a change one admin is making to an account take its turn with any + /// other touching either of the two, and then checks `by` still is an + /// admin. Call it before anything is read. + /// + /// Without it two admins could each take the other's role, or block each + /// other, at the same moment. Each request was authorized before the other + /// committed and neither touches its own account, so both would go through + /// and the service would be left with nobody to administer it. Both + /// profiles are held, in the order of their names so that the two requests + /// queue and cannot deadlock; whichever comes second then reads what the + /// first left, finds it is no longer an admin, and is refused. + private void actingAdmin(String by, String username) { + if (by == null || username == null) { + return; + } + String first = by.compareTo(username) <= 0 ? by : username; + String second = first.equals(by) ? username : by; + profiles.hold(first); + if (!second.equals(first)) { + profiles.hold(second); + } + if (!users.userExists(by)) { + throw new ResponseStatusException(403, "You are no longer an admin"); + } + UserDetails acting = users.loadUserByUsername(by); + if (!acting.isEnabled() || !authorities(acting).contains("ROLE_ADMIN")) { + throw new ResponseStatusException(403, "You are no longer an admin"); + } + } + + private UserDetails load(String username) { + if (username == null || !users.userExists(username)) { + throw new ResponseStatusException(404, "No such account"); + } + return users.loadUserByUsername(username); + } + + private static String[] roles(boolean courier, boolean admin) { + List roles = new ArrayList(); + roles.add(Roles.CUSTOMER); + if (courier) { + roles.add(Roles.COURIER); + } + if (admin) { + roles.add(Roles.ADMIN); + } + return roles.toArray(new String[roles.size()]); + } + + private static void password(String value) { + if (value == null || value.length() < 8) { + throw new ResponseStatusException(400, "Choose a password of at least 8 characters"); + } + if (value.length() > 200) { + throw new ResponseStatusException(400, "That password is too long"); + } + } + + private static List authorities(UserDetails signIn) { + List held = new ArrayList(); + Iterator it = signIn.getAuthorities().iterator(); + while (it.hasNext()) { + held.add(it.next().getAuthority()); + } + return held; + } + + private static UserDto toDto(Profile profile, List authorities, boolean enabled, + String courierStatus) { + boolean customer = authorities.contains("ROLE_" + Roles.CUSTOMER); + boolean courier = authorities.contains("ROLE_" + Roles.COURIER); + boolean admin = authorities.contains("ROLE_" + Roles.ADMIN); + return new UserDto(profile.getUsername(), profile.getDisplayName(), profile.getPhone(), + profile.isPhoneVerified(), customer, courier, admin, !enabled, profile.getVehicle(), + profile.getPlate(), profile.getGender(), courierStatus, profile.isFlagged(), + profile.getFlagReason(), profile.getLanguage()); + } + + /// An e-mail address as an account name: trimmed, and its ASCII letters in + /// lower case. Folded by hand, because `toLowerCase()` follows the server's + /// locale and a key must not: under a Turkish locale the `I` of `INFO@` would + /// fold to a dotless i and name a different account. + public static String email(String value) { + String text = value == null ? "" : value.trim(); + int at = text.indexOf('@'); + if (at < 1 || at != text.lastIndexOf('@') || at > text.length() - 4 + || text.indexOf('.', at) < 0 || text.length() > 190) { + throw new ResponseStatusException(400, "That is not an e-mail address"); + } + char[] chars = text.toCharArray(); + for (int iter = 0; iter < chars.length; iter++) { + char c = chars[iter]; + if (c <= ' ' || c > '~') { + throw new ResponseStatusException(400, "That is not an e-mail address"); + } + if (c >= 'A' && c <= 'Z') { + chars[iter] = (char) (c + ('a' - 'A')); + } + } + return new String(chars); + } + + /// A phone number in the form it is sent to and stored in: a `+` and its + /// digits, with the spaces, dashes and brackets people type taken out. + static String phone(String value) { + String text = value == null ? "" : value.trim(); + StringBuilder digits = new StringBuilder(); + for (int iter = 0; iter < text.length(); iter++) { + char c = text.charAt(iter); + if (c >= '0' && c <= '9') { + digits.append(c); + } else if (c != ' ' && c != '-' && c != '(' && c != ')' && c != '.' + && !(c == '+' && iter == 0)) { + throw new ResponseStatusException(400, "That is not a phone number"); + } + } + if (digits.length() < 7 || digits.length() > 15) { + throw new ResponseStatusException(400, "That is not a phone number"); + } + return "+" + digits; + } + + private static String required(String value, int max, String what) { + String text = value == null ? "" : value.trim(); + if (text.length() == 0) { + throw new ResponseStatusException(400, "Tell us " + what); + } + if (text.length() > max) { + throw new ResponseStatusException(400, "That is too long for " + what); + } + return text; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/DemoAccounts.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/DemoAccounts.java new file mode 100644 index 00000000000..03a181ad0ae --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/DemoAccounts.java @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.Config; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.PostConstruct; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.annotations.Value; +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.api.CardDto; +import com.codenameone.examples.morsel.api.DocumentUploadDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.api.PaymentSetupDto; +import com.codenameone.examples.morsel.domain.CourierApplication; +import com.codenameone.examples.morsel.domain.CourierDocument; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.onboarding.Applications; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.repository.CourierApplicationRepository; +import com.codenameone.examples.morsel.repository.CourierDocumentRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.delivery.DemoDeliveries; +import com.codenameone.examples.morsel.delivery.Fares; + +import java.io.IOException; + +/// The accounts a server starts with. +/// +/// On a development profile, enough of each kind that every screen of the app +/// has something to show straight after a checkout: customers, approved couriers of +/// each kind of car, an admin, someone whose application to drive is waiting +/// for an answer, an account the admins have flagged, and a fortnight of deliveries +/// between them ([DemoDeliveries]). +/// +/// Anywhere else, at most one: the admin named by `morsel.admin.email` and +/// `morsel.admin.password` (or the same two names in the environment, in upper +/// case with `_` for each `.`), because a new deployment needs somebody able to +/// grant the first roles. It is created once; changing the setting later does +/// not change the password of an account that exists. +@Component +public class DemoAccounts { + public static final String CUSTOMER = "customer@morsel.example"; + public static final String COURIER = "courier@morsel.example"; + public static final String SECOND_COURIER = "courier2@morsel.example"; + public static final String ADMIN = "admin@morsel.example"; + /// A courier of each of the larger kinds of car; the second takes a + /// wheelchair. That one and the first courier of all take pets. + public static final String COMFORT_COURIER = "courier3@morsel.example"; + public static final String XL_COURIER = "courier4@morsel.example"; + public static final String SECOND_CUSTOMER = "customer2@morsel.example"; + /// A customer the admins have flagged. + public static final String FLAGGED_CUSTOMER = "customer3@morsel.example"; + /// Someone whose application to drive is complete and waiting for an admin. + public static final String APPLICANT = "applicant@morsel.example"; + /// The password of every demo account. Not a secret: these accounts exist + /// only where the profile says the server is not a real one. + public static final String PASSWORD = "morsel-demo"; + + /// A small grey chequered PNG, standing in for every photographed document + /// of the demo accounts. + static final String PLACEHOLDER_PICTURE = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAeCAAAAABPz8IdAAAAJUlE" + + "QVR42mN4AgUnoIAQn2FEaiBWIYw/MjWMpqXRtDSaluioAQAjjbP9bRBUyQAAAABJRU5ErkJggg=="; + + private final Accounts accounts; + private final Applications applications; + private final Moderation moderation; + private final Payments payments; + private final DemoDeliveries deliveries; + private final ProfileRepository profiles; + private final CourierApplicationRepository forms; + private final CourierDocumentRepository documents; + private final Config config; + private final String adminEmail; + private final String adminPassword; + + public DemoAccounts(Accounts accounts, Applications applications, Moderation moderation, + Payments payments, DemoDeliveries deliveries, ProfileRepository profiles, + CourierApplicationRepository forms, CourierDocumentRepository documents, Config config, + @Value("${morsel.admin.email:}") String adminEmail, + @Value("${morsel.admin.password:}") String adminPassword) { + this.accounts = accounts; + this.applications = applications; + this.moderation = moderation; + this.payments = payments; + this.deliveries = deliveries; + this.profiles = profiles; + this.forms = forms; + this.documents = documents; + this.config = config; + this.adminEmail = adminEmail; + this.adminPassword = adminPassword; + } + + /// Runs once, when the server starts. Not a transaction itself: each + /// account is made by a transactional method -- [Accounts]' or one of this + /// class's own -- so a server stopped half way through start-up is left + /// with whole accounts, and the next start adds the ones that are missing. + @PostConstruct + public void seed() throws IOException { + if (!config.isDevelopmentProfile()) { + if (adminEmail.length() > 0 && adminPassword.length() >= 8) { + add(Accounts.email(adminEmail), adminPassword, "Administrator", "+10000000", "", + ""); + } + return; + } + // The fortnight is written once, with the accounts it is shared among, + // and not again when a server with a database that persists restarts. + // The marker is an account made before the deliveries, so a start-up that + // failed between the two leaves a development database with the + // accounts and no fortnight. That is demonstration data on a + // developer's machine, where the answer is to drop the database and + // start again; a marker written with the deliveries would be one more + // table for the sample to carry. + boolean first = !accounts.exists(COMFORT_COURIER); + // The tests sign in as the first three and expect to find them as new + // accounts are: no deliveries behind them, no card saved, cash by default. + // Under the `test` profile the fortnight is therefore the other + // accounts' alone. + boolean everyone = !"test".equals(config.getProfile()); + + customer(CUSTOMER, "Riley Customer", "+15550100", "unspecified", 0, everyone); + courier(COURIER, "Dana Courier", "+15550101", "female", Fares.STANDARD, "Blue", "Toyota", + "Prius", "WAY 101", 4, false, true, 0); + courier(SECOND_COURIER, "Devon Courier", "+15550102", "male", Fares.STANDARD, "White", "Kia", + "Niro", "WAY 102", 4, false, false, 0); + add(ADMIN, PASSWORD, "Alex Admin", "+15550103", "", ""); + courier(COMFORT_COURIER, "Carmen Courier", "+15550104", "female", Fares.COMFORT, "Black", + "Tesla", "Model 3", "WAY 103", 4, false, false, 12); + courier(XL_COURIER, "Dmitri Courier", "+15550105", "male", Fares.XL, "Silver", "Toyota", + "Sienna", "WAY 104", 6, true, true, 9); + customer(SECOND_CUSTOMER, "Robin Customer", "+15550106", "nonbinary", 11, true); + boolean flag = !accounts.exists(FLAGGED_CUSTOMER); + customer(FLAGGED_CUSTOMER, "Rae Customer", "+15550107", "female", 6, false); + if (flag) { + flag(FLAGGED_CUSTOMER, "Three no-shows at the pickup in one week", 3); + } + applicant(); + if (first) { + deliveries.seed(everyone ? new String[] {CUSTOMER, SECOND_CUSTOMER, FLAGGED_CUSTOMER} + : new String[] {SECOND_CUSTOMER, FLAGGED_CUSTOMER}, + everyone ? new String[] {COURIER, SECOND_COURIER, COMFORT_COURIER, XL_COURIER} + : new String[] {COMFORT_COURIER, XL_COURIER}, everyone); + } + } + + /// Marks an account as one the admins flagged some days ago, with the + /// event that says who did and why. + @Transactional(rollbackFor = IOException.class) + void flag(String username, String why, int daysAgo) throws IOException { + Profile profile = profiles.findById(username); + profile.setFlagged(true); + profile.setFlagReason(why); + moderation.record(username, ADMIN, Moderation.FLAG, why, + System.currentTimeMillis() - daysAgo * Days.MILLIS); + } + + /// An admin, or the plainest kind of account. + private void add(String username, String password, String name, String phone, String vehicle, + String plate) throws IOException { + if (accounts.exists(username)) { + return; + } + try { + accounts.create(username, password, name, phone, true, vehicle, plate, false, + ADMIN.equals(username) || !config.isDevelopmentProfile()); + } catch (IOException | RuntimeException failed) { + // Two servers started together against a new database both find + // no admin and both make one; the second insert fails on the name. + // create() is one transaction, so the loser left nothing behind, + // and the account the other server made is the one wanted. Any + // other failure leaves no account, and is the start-up's to report. + if (!accounts.exists(username)) { + throw failed; + } + } + } + + private void customer(String username, String name, String phone, String gender, int daysAgo, + boolean card) throws IOException { + if (accounts.exists(username)) { + return; + } + accounts.create(username, PASSWORD, name, phone, true, "", "", false, false, gender, + opened(daysAgo)); + if (card) { + PaymentSetupDto setup = payments.startSetup(username, name); + // Only a simulated card can be made up. With a real processor + // configured the demo customers pay cash until someone adds a card. + if (!setup.hosted()) { + CardDto visa = new CardDto("4242424242424242", 12, year() + 3, "123", name); + payments.completeSetup(username, setup.id(), visa); + } + } + } + + /// An approved courier, with the application an approval implies: filled + /// in, its documents accepted. + private void courier(String username, String name, String phone, String gender, String product, + String colour, String make, String model, String plate, int seats, + boolean wheelchair, boolean pets, int daysAgo) throws IOException { + if (accounts.exists(username)) { + return; + } + accounts.create(username, PASSWORD, name, phone, true, colour + " " + make + " " + model, + plate, true, false, gender, opened(daysAgo)); + approved(username, name, product, colour, make, model, plate, seats, wheelchair, pets, + daysAgo); + } + + /// Fills in the application an account created as a courier was given, and + /// adds its documents, already accepted. + @Transactional(rollbackFor = IOException.class) + void approved(String username, String name, String product, String colour, String make, + String model, String plate, int seats, boolean wheelchair, boolean pets, int daysAgo) + throws IOException { + long handedIn = System.currentTimeMillis() - (daysAgo + 1) * Days.MILLIS; + CourierApplication form = forms.findById(username); + form.setLegalName(name); + form.setDateOfBirth("1988-04-12"); + form.setLicenceNumber("D" + (4400000 + plate.charAt(plate.length() - 1) * 7919)); + form.setLicenceExpiry((year() + 2) + "-06-30"); + form.setVehicleMake(make); + form.setVehicleModel(model); + form.setVehicleYear(year() - 3); + form.setVehicleColor(colour); + form.setVehiclePlate(plate); + form.setVehicleSeats(seats); + form.setProduct(product); + form.setWheelchair(wheelchair); + form.setPets(pets); + form.setSubmittedAt(handedIn); + form.setReviewedBy(ADMIN); + for (int iter = 0; iter < Applications.KINDS.length; iter++) { + CourierDocument picture = new CourierDocument(); + picture.setUsername(username); + picture.setKind(Applications.KINDS[iter]); + picture.setContentType("image/png"); + picture.setStatus("accepted"); + picture.setUploadedAt(handedIn); + picture.setData(PLACEHOLDER_PICTURE); + documents.insert(picture); + } + } + + /// The applicant, taken through the same steps the app takes: a form, five + /// pictures, and handing it in. Which also means the demo application is + /// one the server itself accepts as complete. + private void applicant() throws IOException { + if (accounts.exists(APPLICANT)) { + return; + } + accounts.create(APPLICANT, PASSWORD, "Avery Applicant", "+15550108", true, "", "", false, + false, "male", opened(4)); + applications.open(APPLICANT, "", ""); + // What an applicant fills in. The rest (who, the status, the documents, + // the dates) is the server's to say and is not read from a form. + CourierApplicationDto form = new CourierApplicationDto(null, null, null, + "Avery Jordan Applicant", "1992-09-03", "D4471902", (year() + 3) + "-03-31", + "Honda", "Civic", year() - 2, "Grey", "WAY 207", 4, Fares.STANDARD, false, + false, null, null, 0L, 0L, null); + applications.save(APPLICANT, form); + for (int iter = 0; iter < Applications.KINDS.length; iter++) { + DocumentUploadDto picture = new DocumentUploadDto(Applications.KINDS[iter], + "image/png", PLACEHOLDER_PICTURE); + applications.upload(APPLICANT, picture); + } + applications.submit(APPLICANT); + handedIn(APPLICANT, 2); + } + + /// Dates an application that was just handed in to some days ago, so that + /// the admins' queue shows one that has been waiting. + @Transactional(rollbackFor = IOException.class) + void handedIn(String username, int daysAgo) throws IOException { + CourierApplication form = forms.findById(username); + form.setSubmittedAt(System.currentTimeMillis() - daysAgo * Days.MILLIS); + } + + /// When an account opened that many days ago was opened, so that the chart + /// of new accounts is not one bar. + private static long opened(int daysAgo) { + return System.currentTimeMillis() - daysAgo * Days.MILLIS; + } + + private static int year() { + return Integer.parseInt(Days.iso(System.currentTimeMillis()).substring(0, 4)); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Grants.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Grants.java new file mode 100644 index 00000000000..70256762d06 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Grants.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.DataSource; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.security.Authentication; +import com.codename1.backend.security.SecurityContextHolder; +import com.codename1.backend.security.oauth2.server.authorization.OAuth2AuthorizationService; +import com.codename1.backend.security.oauth2.server.resource.JwtAuthenticationToken; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/// The sign-ins an account has open, and ending them. +/// +/// Signing in leaves a grant in the authorization server's own table, and a +/// refresh token is good for as long as its grant is there: refreshing asks +/// for no password. So a password changed because a phone was lost changes +/// nothing for whoever holds that phone, unless the grants go too. +/// +/// An access token already issued is not recalled by this. It is a signed +/// token that is checked without the table, and it runs out on its own within +/// its few minutes; what ends here is the means of getting another. +@Component +public class Grants { + private final DataSource db; + private final OAuth2AuthorizationService authorizations; + + public Grants(DataSource db, OAuth2AuthorizationService authorizations) { + this.db = db; + this.authorizations = authorizations; + } + + /// Ends every sign-in of an account but the one this request was made + /// with, so the device a password was changed on stays signed in and every + /// other has to give the new password. + public void endOthers(String username) throws IOException { + end(username, current()); + } + + /// Ends every sign-in of an account. + public void endAll(String username) throws IOException { + end(username, ""); + } + + private void end(String username, String kept) throws IOException { + // The table is the authorization server's, which offers a grant by its + // id and no list of an account's: this reads the ids and leaves the + // removing, which takes the grant's tokens with it, to the service. + List rows = db.query("SELECT id FROM cn1_oauth2_authorization WHERE principal_name = ?", + new Object[] {username}); + for (int iter = 0; rows != null && iter < rows.size(); iter++) { + Object row = rows.get(iter); + Object id = row instanceof Map ? ((Map) row).get("id") : null; + if (id != null && !kept.equals(String.valueOf(id))) { + authorizations.remove(String.valueOf(id)); + } + } + } + + /// The id of the grant the request's token was issued under, or empty for + /// a request that carried no such token. The server writes the grant's id + /// before the first dot of the token's own. + private static String current() { + Authentication who = SecurityContextHolder.getContext().getAuthentication(); + if (!(who instanceof JwtAuthenticationToken)) { + return ""; + } + String id = ((JwtAuthenticationToken) who).getToken().getId(); + int dot = id == null ? -1 : id.indexOf('.'); + return dot > 0 ? id.substring(0, dot) : ""; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Moderation.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Moderation.java new file mode 100644 index 00000000000..2adcaf53e03 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Moderation.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.ModerationEventDto; +import com.codenameone.examples.morsel.domain.ModerationEvent; +import com.codenameone.examples.morsel.repository.ModerationEventRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/// The record of what the admins did to an account, and why. +/// +/// Flagging, blocking, approving and rejecting all change an account's state, +/// and the state alone does not say who decided it or on what grounds. This +/// does. A row is written with the change and never altered afterwards. +@Component +public class Moderation { + public static final String FLAG = "flag"; + public static final String UNFLAG = "unflag"; + public static final String BLOCK = "block"; + public static final String UNBLOCK = "unblock"; + public static final String APPROVE = "approve"; + public static final String REJECT = "reject"; + public static final String ROLES = "roles"; + + private final ModerationEventRepository events; + + public Moderation(ModerationEventRepository events) { + this.events = events; + } + + @Transactional + public void record(String username, String by, String action, String reason) + throws IOException { + // Two changes made in the same millisecond -- a block and the flag that + // came with it -- would otherwise be listed in either order. The later + // one is stamped after the one before it, so the list is the order in + // which things were done. + Long newest = events.newest(username); + long after = newest == null ? 0L : newest.longValue() + 1L; + record(username, by, action, reason, Math.max(System.currentTimeMillis(), after)); + } + + /// As [#record(String, String, String, String)], at a time of the caller's + /// choosing; the demo data is the one caller that has a past to write. + @Transactional + public void record(String username, String by, String action, String reason, long at) + throws IOException { + String why = reason == null ? "" : reason.trim(); + ModerationEvent event = new ModerationEvent(); + event.setId(Ids.next()); + event.setUsername(username); + event.setCreatedAt(at); + event.setActor(by); + event.setAction(action); + event.setReason(why.length() > 255 ? why.substring(0, 255) : why); + events.insert(event); + } + + /// An account's events, newest first. + @Transactional(readOnly = true) + public List events(String username) throws IOException { + List rows = events.findTop100ByUsernameOrderByCreatedAtDescIdAsc(username); + List out = new ArrayList(); + for (int iter = 0; iter < rows.size(); iter++) { + ModerationEvent row = rows.get(iter); + out.add(new ModerationEventDto(row.getCreatedAt(), row.getActor(), row.getAction(), + row.getReason())); + } + return out; + } + + @Transactional + public void forget(String username) throws IOException { + events.deleteByUsername(username); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/PhoneVerification.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/PhoneVerification.java new file mode 100644 index 00000000000..aeec641fd5d --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/PhoneVerification.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.Config; +import com.codename1.backend.Crypto; +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.PhoneChallengeDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.domain.PhoneCode; +import com.codenameone.examples.morsel.repository.PhoneCodeRepository; +import com.codenameone.examples.morsel.sms.SmsSender; + +import java.io.IOException; + +/// Proves an account's phone number by texting it a code. +/// +/// The server makes the code, keeps only its hash, and is the one that decides +/// whether what the user typed matches. The app never sees the code it is asking +/// the user for, so it cannot be talked into accepting one. +@Component +public class PhoneVerification { + /// How long a code stays good. + static final int CODE_SECONDS = 300; + /// Wrong guesses allowed before the code is void. Six digits and five + /// guesses, each against at most two live codes (see [#store]), is one + /// chance in 100,000 per code sent. + static final int MAX_ATTEMPTS = 5; + /// The least time between two codes for one account. Each one is a text + /// message somebody pays for and somebody receives. + static final int RESEND_SECONDS = 30; + + private final PhoneCodeRepository codes; + private final Accounts accounts; + private final SmsSender sms; + private final Config config; + + public PhoneVerification(PhoneCodeRepository codes, Accounts accounts, SmsSender sms, + Config config) { + this.codes = codes; + this.accounts = accounts; + this.sms = sms; + this.config = config; + } + + /// Sends a new code, replacing the one before it. + /// + /// Not one transaction, because a text message is sent in the middle and a + /// transaction must not be held open across a call that leaves the server. + /// The code is stored and committed, then sent, and taken back in a second + /// transaction when it could not be. + public PhoneChallengeDto start(String username) throws IOException { + UserDto user = accounts.describe(username); + String code = newCode(); + String codeHash = hash(username, code); + store(username, codeHash, System.currentTimeMillis()); + try { + sms.send(user.phone(), "Your Morsel code is " + code); + } catch (IOException failed) { + System.err.println("could not send a verification code: " + failed.getMessage()); + discard(username, codeHash); + throw new ResponseStatusException(503, "We could not send the code. Try again shortly"); + } + // Only a server that cannot send a text message, and only on a + // development profile, tells the app what the code was. Both halves + // matter: a production server whose SMS settings were forgotten must fail + // to verify anyone, not verify everyone. + String demoCode = !sms.delivers() && config.isDevelopmentProfile() ? code : null; + return new PhoneChallengeDto(CODE_SECONDS, demoCode); + } + + /// Keeps the hash of a new code in place of the one before it. Answers 429 + /// when the one before it was sent a moment ago. + /// + /// The code before it stays good until it would have expired anyway. A + /// second code is asked for when the first is slow to arrive, and text + /// messages are not delivered in order: the first may arrive last, and the + /// newest message on the phone must not be a code the server refuses. Only + /// one is kept, so at most two codes are live, and only one whose guesses + /// were not used up. + @Transactional + void store(String username, String codeHash, long now) throws IOException { + // Before the code is read: two requests for one account take turns, + // so the second finds the code the first just stored and is told to + // wait. Otherwise both pass the check below and both send a text. + accounts.hold(username); + PhoneCode pending = codes.findById(username); + if (pending != null && now - pending.getSentAt() < RESEND_SECONDS * 1000L) { + throw new ResponseStatusException(429, "Wait a moment before asking for another code"); + } + boolean first = pending == null; + if (first) { + pending = new PhoneCode(); + pending.setUsername(username); + } else if (pending.getExpiresAt() >= now && pending.getAttempts() < MAX_ATTEMPTS) { + // With the guesses it has used: asking for another code must not + // buy the old one a fresh set. + pending.setPreviousHash(pending.getCodeHash()); + pending.setPreviousExpiresAt(pending.getExpiresAt()); + pending.setPreviousAttempts(pending.getAttempts()); + } else { + pending.setPreviousHash(""); + pending.setPreviousExpiresAt(0L); + pending.setPreviousAttempts(0); + } + pending.setCodeHash(codeHash); + pending.setExpiresAt(now + CODE_SECONDS * 1000L); + pending.setAttempts(0); + pending.setSentAt(now); + if (first) { + codes.insert(pending); + } + } + + /// Takes back a code that could not be sent, if it is still the one the + /// account is waiting on. A send can take longer to fail than the wait + /// between two codes, and by then a later request may have stored a code + /// of its own and had it delivered: that one is the user's, and stays. + /// The code before it, when it is still good, is the one waited on again, + /// and another may be asked for at once. + @Transactional + void discard(String username, String codeHash) throws IOException { + // The hold store() takes, before the row is read: a resend storing its + // code meanwhile commits first, and this then finds that code and + // leaves it, where read before the wait it would overwrite or delete it. + accounts.hold(username); + PhoneCode pending = codes.findById(username); + if (pending == null || !codeHash.equals(pending.getCodeHash())) { + return; + } + if (pending.getPreviousHash().length() == 0 + || pending.getPreviousExpiresAt() < System.currentTimeMillis()) { + codes.deleteById(username); + return; + } + pending.setCodeHash(pending.getPreviousHash()); + pending.setExpiresAt(pending.getPreviousExpiresAt()); + pending.setAttempts(pending.getPreviousAttempts()); + pending.setPreviousHash(""); + pending.setPreviousExpiresAt(0L); + pending.setPreviousAttempts(0); + pending.setSentAt(0L); + } + + /// Checks the code the user typed. Answers 400 for a wrong or expired one + /// and 429 once the guesses are used up. + /// + /// The refusal of a wrong code is thrown here, outside the transaction that + /// counted the guess. Thrown inside it, the error would roll the count back + /// and every code could be guessed at for ever. + public UserDto verify(String username, String code) throws IOException { + if (!guess(username, code)) { + throw new ResponseStatusException(400, "That is not the code we sent"); + } + return accounts.describe(username); + } + + /// Takes one guess at an account's code and answers whether it was right. + /// A right one uses the code up and marks the phone verified; a wrong one + /// leaves the guess counted, which is why this returns false where it + /// could have thrown. + @Transactional + boolean guess(String username, String code) throws IOException { + // The hold store() and discard() take, and in the same order as a + // closure, which holds the profile and then deletes the code: taken + // the other way round, a guess and a closure could each wait on the + // row the other has. + accounts.hold(username); + PhoneCode pending = codes.findById(username); + if (pending == null || pending.getExpiresAt() < System.currentTimeMillis()) { + throw new ResponseStatusException(400, "That code has expired. Ask for a new one"); + } + if (pending.getAttempts() >= MAX_ATTEMPTS) { + throw new ResponseStatusException(429, "Too many wrong codes. Ask for a new one"); + } + String expected = pending.getCodeHash(); + // Taken before the comparison, and by the database, which also holds + // the limit: the count read above is what it was when this request + // arrived, and any number of requests arriving together all read the + // same one. Only a request that is given a guess has its code looked at. + // And only of the code read above: one sent meanwhile is not this one. + if (codes.takeGuess(username, expected, MAX_ATTEMPTS) != 1) { + throw new ResponseStatusException(429, "Too many wrong codes. Ask for a new one"); + } + // Read again after the guess was taken, which the update did under + // the row's lock: the count of the code before is then this guess's + // own, and within its limit only when this guess was one it had left. + PhoneCode counted = codes.findById(username); + String typed = code == null ? "" : code.trim(); + byte[] given = hash(username, typed).getBytes("UTF-8"); + // Both compared every time, so the answer takes as long either way. The + // code before is good only until it would have expired; it was sent + // first, so it never outlives the current one checked above. + // + // `counted.getPreviousAttempts()` already includes every guess made + // since the resend: takeGuess adds one to `previousAttempts` in the + // same statement that adds one to `attempts`, so it is the old code's + // whole count, not the snapshot store() copied. A code that used four + // guesses before the resend reads 5 on its first guess after it (still + // allowed: its fifth) and 6 on the next (refused), so its budget stays + // five. AccountTest.theCodeBeforeKeepsTheGuessesItHadLeft holds that. + boolean current = Crypto.equalsConstantTime(given, expected.getBytes("UTF-8")); + boolean previous = Crypto.equalsConstantTime(given, + pending.getPreviousHash().getBytes("UTF-8")) + && pending.getPreviousHash().length() > 0 + && pending.getPreviousExpiresAt() >= System.currentTimeMillis() + && counted != null && counted.getPreviousAttempts() <= MAX_ATTEMPTS; + if (!current && !previous) { + return false; + } + pending = codes.findById(username); + if (pending != null) { + codes.deleteById(username); + } + accounts.markPhoneVerified(username); + return true; + } + + /// Six digits, each value as likely as the next: a draw that would favour + /// the low numbers is thrown away and drawn again. + private static String newCode() throws IOException { + while (true) { + byte[] random = Crypto.randomBytes(4); + int value = ((random[0] & 0x7f) << 24) | ((random[1] & 0xff) << 16) + | ((random[2] & 0xff) << 8) | (random[3] & 0xff); + if (value < 2147000000) { + String digits = String.valueOf(1000000 + value % 1000000); + return digits.substring(1); + } + } + } + + /// An unkeyed digest, kept so on purpose. Somebody who can read the + /// `phone_code` table could try every code within the five minutes one + /// lasts, but somebody who can read the database already owns what a + /// phone check guards: it proves a number, and grants nothing on its own. + /// A keyed digest under a server secret is a hardening step for a + /// production deployment, listed in the implementor's guide among what the + /// sample leaves out. + private static String hash(String username, String code) throws IOException { + return Ids.sha256(username + ":" + code); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Preferences.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Preferences.java new file mode 100644 index 00000000000..b177b83fce9 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Preferences.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.account; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.api.PreferencesDto; +import com.codenameone.examples.morsel.domain.Preference; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.repository.PreferenceRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; + +import java.io.IOException; + +/// How one person wants the app and their deliveries: language, theme, units, what +/// they are told about, and what a delivery is asked for with unless they say +/// otherwise. +/// +/// Kept on the server so that they follow the account from one phone to the +/// next. Two of them live elsewhere, because something else reads them: the +/// language is in the profile, where the admin sees it, and the default way to +/// pay is the mark on the saved card itself, so that removing a card cannot +/// leave a preference pointing at it. +@Component +public class Preferences { + private static final String[] LANGUAGES = {"en", "es", "fr", "de", "he"}; + private static final String[] THEMES = {"system", "light", "dark"}; + private static final String[] UNITS = {"km", "mi"}; + private static final String[] COURIER_GENDERS = {"any", "women"}; + + private final ProfileRepository profiles; + private final PreferenceRepository preferences; + private final Payments payments; + + public Preferences(ProfileRepository profiles, PreferenceRepository preferences, + Payments payments) { + this.profiles = profiles; + this.preferences = preferences; + this.payments = payments; + } + + @Transactional(readOnly = true) + public PreferencesDto get(String username) throws IOException { + Profile profile = profiles.findById(username); + if (profile == null) { + throw new ResponseStatusException(404, "No such account"); + } + Preference row = preferences.findById(username); + String language = profile.getLanguage(); + String method = payments.defaultMethod(username); + if (row == null) { + // What someone who never opened the settings gets. + return new PreferencesDto(language, THEMES[0], UNITS[0], true, true, false, + COURIER_GENDERS[0], false, false, false, method, 0, ""); + } + return new PreferencesDto(language, row.getTheme(), row.getUnits(), row.isNotifyDeliveries(), + row.isNotifyReceipts(), row.isNotifyPromotions(), row.getCourierGender(), + row.isQuietDelivery(), row.isWheelchair(), row.isPetFriendly(), method, + row.getDefaultTip(), row.getShareTripsWith()); + } + + /// Saves all of them. Each is checked against the values it can take: these + /// are words the server later acts on, not text to show back. One + /// transaction, so a value that is refused leaves none of the others saved. + @Transactional + public PreferencesDto save(String username, PreferencesDto wanted) throws IOException { + if (wanted == null) { + throw new ResponseStatusException(400, "Nothing to save"); + } + // First, before anything is read: two saves sent together for an + // account with no row of settings yet would each find none and each + // insert one, and the second insert fails on the key. The profile is + // there from registration, so held, one save waits and finds the + // other's row. + profiles.hold(username); + int tip = wanted.defaultTipPercent(); + if (tip != 0 && tip != 10 && tip != 15 && tip != 20) { + throw new ResponseStatusException(400, "A default tip is 0, 10, 15 or 20 percent"); + } + String share = wanted.shareTripsWith() == null ? "" : wanted.shareTripsWith().trim(); + String theme = Text.oneOf(wanted.theme(), THEMES, THEMES[0], "the theme"); + String units = Text.oneOf(wanted.units(), UNITS, UNITS[0], "the units"); + String courierGender = Text.oneOf(wanted.courierGender(), COURIER_GENDERS, COURIER_GENDERS[0], + "the courier"); + String shareWith = share.length() == 0 ? "" : Accounts.phone(share); + String language = Text.oneOf(wanted.language(), LANGUAGES, LANGUAGES[0], "the language"); + String method = wanted.defaultPaymentMethodId() == null ? "" + : wanted.defaultPaymentMethodId().trim(); + if (method.length() > 0 && !method.equals(payments.defaultMethod(username))) { + // Answers 404 for a card that is not this user's, before anything + // else is saved. + payments.makeDefault(username, method); + } + // Read after the card was marked, not before: marking one may be a + // statement over every card of the account, and what was read before + // such a statement is no longer the session's to write. + Profile profile = profiles.findById(username); + if (profile == null) { + throw new ResponseStatusException(404, "No such account"); + } + profile.setLanguage(language); + Preference row = preferences.findById(username); + boolean unsaved = row == null; + if (unsaved) { + row = new Preference(); + row.setUsername(username); + } + row.setTheme(theme); + row.setUnits(units); + row.setNotifyDeliveries(wanted.notifyDeliveryUpdates()); + row.setNotifyReceipts(wanted.notifyReceipts()); + row.setNotifyPromotions(wanted.notifyPromotions()); + row.setCourierGender(courierGender); + row.setQuietDelivery(wanted.quietDelivery()); + row.setWheelchair(wanted.accessibleVehicle()); + row.setPetFriendly(wanted.petFriendly()); + row.setDefaultTip(tip); + row.setShareTripsWith(shareWith); + if (unsaved) { + preferences.insert(row); + } + return get(username); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/AdminEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/AdminEndpoint.java new file mode 100644 index 00000000000..4a024204cfd --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/AdminEndpoint.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.PreAuthorize; +import com.codenameone.examples.morsel.Caller; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.api.AdminApiServer; +import com.codenameone.examples.morsel.api.AdminStatsDto; +import com.codenameone.examples.morsel.api.DocumentContentDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.api.CourierDto; +import com.codenameone.examples.morsel.api.FlagDto; +import com.codenameone.examples.morsel.api.PricingDto; +import com.codenameone.examples.morsel.api.ReasonDto; +import com.codenameone.examples.morsel.api.ReceiptDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.RoleChangeDto; +import com.codenameone.examples.morsel.api.StatsSeriesDto; +import com.codenameone.examples.morsel.api.SuspendDto; +import com.codenameone.examples.morsel.api.UserDetailDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.courier.Couriers; +import com.codenameone.examples.morsel.onboarding.Applications; +import com.codenameone.examples.morsel.live.LiveHub; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.delivery.Fares; +import com.codenameone.examples.morsel.delivery.Deliveries; + +import java.util.List; + +/// The server's half of `AdminApi`. Guarded by path in the filter chain and by +/// role here, as `CourierEndpoint` is. +/// +/// This is the one place a name in the path chooses whose data is read: an +/// applicant's documents, a customer's receipts. Everywhere else the caller is +/// the subject, and only an admin gets to name somebody else. +@Component +@PreAuthorize("hasRole('ADMIN')") +public class AdminEndpoint implements AdminApiServer { + private final Accounts accounts; + private final Deliveries deliveries; + private final Couriers couriers; + private final Fares fares; + private final LiveHub live; + private final Applications applications; + private final Payments payments; + private final Statistics statistics; + + public AdminEndpoint(Accounts accounts, Deliveries deliveries, Couriers couriers, + Fares fares, LiveHub live, Applications applications, Payments payments, + Statistics statistics) { + this.applications = applications; + this.payments = payments; + this.statistics = statistics; + this.accounts = accounts; + this.deliveries = deliveries; + this.couriers = couriers; + this.fares = fares; + this.live = live; + } + + @Override + public AdminStatsDto stats() throws Exception { + return statistics.dashboard(); + } + + @Override + public List deliveries() throws Exception { + return deliveries.recent(); + } + + @Override + public List users() throws Exception { + return accounts.all(); + } + + @Override + public List couriers() throws Exception { + return couriers.all(); + } + + @Override + public UserDto setRoles(String username, RoleChangeDto roles) throws Exception { + UserDto user = accounts.setRoles(Caller.name(), username, roles != null && roles.courier(), + roles != null && roles.admin()); + // A live channel keeps the roles it was opened with: one opened by an + // admin goes on receiving where every courier is after the role is + // taken away, since it asks for nothing again. Closed here, the app + // opens another with a fresh ticket and gets the frames of the roles + // the account has now. + live.drop(username); + return user; + } + + @Override + public UserDto suspend(String username, SuspendDto suspend) throws Exception { + UserDto user = accounts.suspend(Caller.name(), username, + suspend != null && suspend.suspended(), suspend == null ? "" : suspend.reason()); + if (user.suspended()) { + // The API turns a suspended account away on its next request. A + // live channel already open makes no further request, so it is + // closed here. + live.drop(username); + } + return user; + } + + @Override + public StatsSeriesDto statsSeries(int days) throws Exception { + return statistics.series(days); + } + + @Override + public UserDetailDto user(String username) throws Exception { + return statistics.user(username); + } + + @Override + public UserDto flag(String username, FlagDto flag) throws Exception { + return accounts.flag(Caller.name(), username, flag != null && flag.flagged(), + flag == null ? "" : flag.reason()); + } + + @Override + public List applications(String status) throws Exception { + return applications.list(status); + } + + @Override + public CourierApplicationDto application(String username) throws Exception { + return applications.review(username); + } + + @Override + public DocumentContentDto document(String username, String kind) throws Exception { + return applications.document(username, kind); + } + + @Override + public CourierApplicationDto approve(String username) throws Exception { + return applications.approve(Caller.name(), username); + } + + @Override + public CourierApplicationDto reject(String username, ReasonDto reason) throws Exception { + return applications.reject(Caller.name(), username, reason == null ? "" : reason.reason()); + } + + @Override + public DeliveryDto cancelDelivery(String id, ReasonDto reason) throws Exception { + return deliveries.cancelByAdmin(Caller.name(), id, reason == null ? "" : reason.reason()); + } + + @Override + public ReceiptDto refundDelivery(String id, ReasonDto reason) throws Exception { + return payments.refund(id, Text.required(reason == null ? "" : reason.reason(), 255, + "why it is refunded")); + } + + @Override + public List payments() throws Exception { + return payments.allReceipts(); + } + + @Override + public PricingDto pricing() throws Exception { + return fares.pricing(); + } + + @Override + public PricingDto savePricing(PricingDto pricing) throws Exception { + return fares.save(Caller.name(), pricing); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/Statistics.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/Statistics.java new file mode 100644 index 00000000000..8c8d242985d --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/admin/Statistics.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.Rows; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.Moderation; +import com.codenameone.examples.morsel.api.AdminStatsDto; +import com.codenameone.examples.morsel.api.DayStatsDto; +import com.codenameone.examples.morsel.api.CourierDto; +import com.codenameone.examples.morsel.api.NameCountDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.api.StatsSeriesDto; +import com.codenameone.examples.morsel.api.UserDetailDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.courier.Couriers; +import com.codenameone.examples.morsel.courier.Earnings; +import com.codenameone.examples.morsel.onboarding.Applications; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryEventRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; +import com.codenameone.examples.morsel.delivery.Fares; +import com.codenameone.examples.morsel.delivery.Matcher; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// The numbers behind the admin's dashboard and charts, and the account of one +/// user. +/// +/// Whatever can be counted by a column is counted by the database: deliveries by +/// state, by product and by courier, and every sum of money. The charts by day +/// and by hour of the day are counted here instead, from one row per delivery of +/// the period. Times are stored as milliseconds and a day is a division +/// ([Days]); [DeliveryRepository#timeline] says why that division is not left +/// to a query. A sample's ninety days fit in memory. A service whose do not +/// keeps a table of daily totals, and this class is where it would be read. +@Component +public class Statistics { + static final int DEFAULT_DAYS = 14; + static final int MAX_DAYS = 90; + private static final int TOP_COURIERS = 5; + private static final DeliveryState[] STATES = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS, DeliveryState.COMPLETED, + DeliveryState.CANCELLED_BY_CUSTOMER, DeliveryState.CANCELLED_BY_COURIER, + DeliveryState.CANCELLED_BY_ADMIN, DeliveryState.NO_COURIERS}; + + private static final String PAID = "PAID"; + private static final DeliveryState[] ACTIVE = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + + private final ProfileRepository profiles; + private final CourierStateRepository cars; + private final DeliveryRepository deliveries; + private final DeliveryEventRepository events; + private final Accounts accounts; + private final Couriers couriers; + private final Moderation moderation; + private final Fares fares; + private final Applications applications; + private final Earnings earnings; + + public Statistics(ProfileRepository profiles, CourierStateRepository cars, + DeliveryRepository deliveries, DeliveryEventRepository events, Accounts accounts, + Couriers couriers, Moderation moderation, Fares fares, Applications applications, + Earnings earnings) { + this.earnings = earnings; + this.profiles = profiles; + this.cars = cars; + this.deliveries = deliveries; + this.events = events; + this.applications = applications; + this.accounts = accounts; + this.couriers = couriers; + this.moderation = moderation; + this.fares = fares; + } + + /// The handful of totals at the top of the admin's dashboard. + @Transactional(readOnly = true) + public AdminStatsDto dashboard() throws IOException { + long now = System.currentTimeMillis(); + int users = (int) profiles.count(); + // Who holds a role is the sign-in store's to say and not a table of + // this application's, so the couriers are counted from the accounts as + // the accounts service describes them. + int onboarding = 0; + List everyone = accounts.all(); + for (int iter = 0; iter < everyone.size(); iter++) { + if (everyone.get(iter).courier()) { + onboarding++; + } + } + int online = (int) cars.countByOnlineTrueAndLastSeenGreaterThanEqual( + now - Matcher.FRESH_MILLIS); + int today = (int) deliveries.countByRequestedAtGreaterThanEqual(Days.start(now)); + int active = (int) deliveries.countByStateIn(ACTIVE); + int done = (int) deliveries.countByStateIn(new DeliveryState[] {DeliveryState.COMPLETED}); + long revenue = Rows.number(deliveries.fares(DeliveryState.COMPLETED, PAID)); + return new AdminStatsDto(users, onboarding, online, today, active, done, revenue, + fares.currency()); + } + + /// The last `days` days, today included and last. Fewer than one is taken + /// as the default and more than [#MAX_DAYS] as that many. + @Transactional(readOnly = true) + public StatsSeriesDto series(int days) throws IOException { + int span = days < 1 ? DEFAULT_DAYS : Math.min(days, MAX_DAYS); + long today = Days.start(System.currentTimeMillis()); + long since = today - (span - 1) * Days.MILLIS; + // Each day's figures, added up here and made into a DayStatsDto at the end. + int[] dayDeliveries = new int[span]; + int[] dayCompleted = new int[span]; + int[] dayCancelled = new int[span]; + long[] dayRevenue = new long[span]; + int[] dayNewUsers = new int[span]; + int[] byHour = new int[24]; + // By day and by hour, counted here from one row per delivery. + Map requestedAt = new HashMap(); + List timeline = deliveries.timeline(since); + for (int iter = 0; iter < timeline.size(); iter++) { + Object[] delivery = timeline.get(iter); + long at = whole(delivery[1]); + DeliveryState state = Rows.state(delivery[2]); + requestedAt.put(Rows.text(delivery[0]), Long.valueOf(at)); + int index = (int) ((at - since) / Days.MILLIS); + byHour[(int) (at % Days.MILLIS / 3600000L)]++; + if (index < 0 || index >= span) { + continue; + } + dayDeliveries[index]++; + if (DeliveryState.COMPLETED == state) { + dayCompleted[index]++; + if (PAID.equals(delivery[4])) { + dayRevenue[index] += whole(delivery[3]); + } + } else if (cancelled(state)) { + dayCancelled[index]++; + } + } + // By state, by product and by courier, counted by the database. + int[] byState = new int[STATES.length]; + long completed = 0L; + long finished = 0L; + List states = deliveries.countByState(since); + for (int iter = 0; iter < states.size(); iter++) { + DeliveryState state = Rows.state(states.get(iter)[0]); + long count = whole(states.get(iter)[1]); + for (int s = 0; s < STATES.length; s++) { + if (STATES[s] == state) { + byState[s] += (int) count; + } + } + if (DeliveryState.COMPLETED == state) { + completed += count; + finished += count; + } else if (cancelled(state) || DeliveryState.NO_COURIERS == state) { + finished += count; + } + } + int[] byProduct = new int[Fares.PRODUCTS.length]; + List products = deliveries.countByProduct(since); + for (int iter = 0; iter < products.size(); iter++) { + for (int p = 0; p < Fares.PRODUCTS.length; p++) { + if (Fares.PRODUCTS[p].equals(products.get(iter)[0])) { + byProduct[p] += (int) whole(products.get(iter)[1]); + } + } + } + Map completedBy = new HashMap(); + List perCourier = deliveries.countByCourier(since, DeliveryState.COMPLETED); + for (int iter = 0; iter < perCourier.size(); iter++) { + completedBy.put(Rows.text(perCourier.get(iter)[0]), + new int[] {(int) whole(perCourier.get(iter)[1])}); + } + long fareSum = Rows.number(deliveries.faresSince(since, DeliveryState.COMPLETED)); + List hours = new ArrayList(); + for (int iter = 0; iter < byHour.length; iter++) { + hours.add(Integer.valueOf(byHour[iter])); + } + List statesSeen = new ArrayList(); + for (int iter = 0; iter < STATES.length; iter++) { + // A state no delivery is in is left out; a chart has no slice for it. + if (byState[iter] > 0) { + statesSeen.add(new NameCountDto(STATES[iter].name(), byState[iter])); + } + } + List productsSold = new ArrayList(); + for (int iter = 0; iter < Fares.PRODUCTS.length; iter++) { + productsSold.add(new NameCountDto(Fares.PRODUCTS[iter], byProduct[iter])); + } + // Tested before dividing, and not left to an exception: dividing a + // whole number by zero does not raise one on every runtime this runs on. + long averageFare = completed == 0L ? 0L : fareSum / completed; + double completionRate = finished == 0L ? 0d + : Math.round(completed * 100d / finished) / 100d; + + List signUps = profiles.signUps(since); + for (int iter = 0; iter < signUps.size(); iter++) { + int index = (int) ((signUps.get(iter).longValue() - since) / Days.MILLIS); + if (index >= 0 && index < span) { + dayNewUsers[index]++; + } + } + List daily = new ArrayList(); + for (int iter = 0; iter < span; iter++) { + daily.add(new DayStatsDto(Days.iso(since + iter * Days.MILLIS), dayDeliveries[iter], + dayCompleted[iter], dayCancelled[iter], dayRevenue[iter], + dayNewUsers[iter])); + } + + // How long a customer waited for a courier to take the delivery: each time one + // was accepted, set against when it was asked for. + List waits = events.reached(since, DeliveryState.ACCEPTED); + long waited = 0L; + int answered = 0; + for (int iter = 0; iter < waits.size(); iter++) { + Long asked = requestedAt.get(Rows.text(waits.get(iter)[0])); + if (asked != null) { + waited += Math.max(0L, whole(waits.get(iter)[1]) - asked.longValue()); + answered++; + } + } + double averageWaitSeconds = answered == 0 ? 0d + : Math.round(waited / 100d / answered) / 10d; + + List topCouriers = new ArrayList(); + for (int place = 0; place < TOP_COURIERS && !completedBy.isEmpty(); place++) { + String best = null; + for (Map.Entry entry : completedBy.entrySet()) { + int[] top = best == null ? null : completedBy.get(best); + // Ties go to the name that sorts first, so the order is the + // same every time the chart is drawn. + if (top == null || entry.getValue()[0] > top[0] || (entry.getValue()[0] == top[0] + && entry.getKey().compareTo(best) < 0)) { + best = entry.getKey(); + } + } + int rode = completedBy.remove(best)[0]; + if (accounts.exists(best)) { + // Within the period, which is what the chart is of. + topCouriers.add(couriers.describe(best, rode)); + } + } + return new StatsSeriesDto(daily, hours, statesSeen, productsSold, topCouriers, + averageFare, averageWaitSeconds, completionRate, applications.waiting(), + (int) profiles.countByFlaggedTrue(), fares.currency()); + } + + /// One account, with what it has done and what was done to it. + @Transactional(readOnly = true) + public UserDetailDto user(String username) throws IOException { + UserDto user = accounts.describe(username); + int taken = (int) deliveries.countByCustomerOrCourier(username, username); + int cancellations = (int) deliveries.countByCustomerAndStateOrCourierAndState(username, + DeliveryState.CANCELLED_BY_CUSTOMER, username, DeliveryState.CANCELLED_BY_COURIER); + CourierState car = username == null || username.length() == 0 + || username.length() > 190 ? null : cars.findById(username); + // A rating is a courier's; an account that has never driven has none. + double rating = car == null ? 0d : car.rating(); + long spent = sum(deliveries.spent(username, DeliveryState.COMPLETED, PAID)); + // What the courier's own screens call earned, from the same sum: a + // card delivery whose charge failed, is pending or was refunded, and a + // tip whose charge has not come back, are not money the courier has. + long earned = earnings.earned(username); + return new UserDetailDto(user, taken, cancellations, rating, spent, earned, + moderation.events(username)); + } + + /// Whether a delivery in `state` was called off, by whoever did it. + private static boolean cancelled(DeliveryState state) { + return DeliveryState.CANCELLED_BY_CUSTOMER == state || DeliveryState.CANCELLED_BY_COURIER == state + || DeliveryState.CANCELLED_BY_ADMIN == state; + } + + /// A count or a sum as a query answers it. + private static long whole(Object value) { + return value instanceof Number ? ((Number) value).longValue() : 0L; + } + + private static long sum(Object[] values) { + long total = 0L; + for (int iter = 0; values != null && iter < values.length; iter++) { + total += whole(values[iter]); + } + return total; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/CourierEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/CourierEndpoint.java new file mode 100644 index 00000000000..a90cb85a809 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/CourierEndpoint.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.PreAuthorize; +import com.codenameone.examples.morsel.Caller; +import com.codenameone.examples.morsel.api.CourierApiServer; +import com.codenameone.examples.morsel.api.CourierDto; +import com.codenameone.examples.morsel.api.CourierStatusDto; +import com.codenameone.examples.morsel.api.EarningsDto; +import com.codenameone.examples.morsel.api.EarningsSummaryDto; +import com.codenameone.examples.morsel.api.PayoutAccountDto; +import com.codenameone.examples.morsel.api.PayoutDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.delivery.Deliveries; + +import java.util.List; + +/// The server's half of `CourierApi`. +/// +/// Guarded twice, on purpose. The filter chain refuses `/api/courier/**` to +/// anyone who is not a courier, by path; `@PreAuthorize` refuses these methods to +/// anyone who is not a courier, whatever path led to them. Either alone would do +/// today. The second is what still holds after someone moves a path. +@Component +@PreAuthorize("hasRole('COURIER')") +public class CourierEndpoint implements CourierApiServer { + private final Couriers couriers; + private final Deliveries deliveries; + private final Earnings earnings; + + public CourierEndpoint(Couriers couriers, Deliveries deliveries, Earnings earnings) { + this.earnings = earnings; + this.couriers = couriers; + this.deliveries = deliveries; + } + + @Override + public CourierDto status(CourierStatusDto status) throws Exception { + return couriers.report(Caller.name(), status); + } + + @Override + public CourierDto me() throws Exception { + return couriers.describe(Caller.name()); + } + + @Override + public DeliveryDto active() throws Exception { + return deliveries.activeForCourier(Caller.name()); + } + + @Override + public DeliveryDto accept(String id) throws Exception { + return deliveries.accept(Caller.name(), id); + } + + @Override + public DeliveryDto decline(String id) throws Exception { + return deliveries.decline(Caller.name(), id); + } + + @Override + public DeliveryDto arrived(String id) throws Exception { + return deliveries.arrived(Caller.name(), id); + } + + @Override + public DeliveryDto start(String id) throws Exception { + return deliveries.start(Caller.name(), id); + } + + @Override + public DeliveryDto complete(String id) throws Exception { + return deliveries.complete(Caller.name(), id); + } + + @Override + public DeliveryDto cancel(String id) throws Exception { + return deliveries.cancelByCourier(Caller.name(), id); + } + + @Override + public EarningsDto earnings() throws Exception { + return couriers.earnings(Caller.name()); + } + + @Override + public EarningsSummaryDto earningsSummary() throws Exception { + return earnings.summary(Caller.name()); + } + + @Override + public List payouts() throws Exception { + return earnings.payouts(Caller.name()); + } + + @Override + public PayoutDto cashOut() throws Exception { + return earnings.cashOut(Caller.name()); + } + + @Override + public PayoutAccountDto payoutAccount() throws Exception { + return earnings.account(Caller.name()); + } + + @Override + public PayoutAccountDto savePayoutAccount(PayoutAccountDto account) throws Exception { + return earnings.saveAccount(Caller.name(), account); + } + + @Override + public List history() throws Exception { + return deliveries.historyForCourier(Caller.name()); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Couriers.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Couriers.java new file mode 100644 index 00000000000..68885a6519e --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Couriers.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.Rows; +import com.codenameone.examples.morsel.api.CourierDto; +import com.codenameone.examples.morsel.api.CourierStatusDto; +import com.codenameone.examples.morsel.api.EarningsDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierApplication; +import com.codenameone.examples.morsel.domain.CourierHours; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.onboarding.Applications; +import com.codenameone.examples.morsel.geo.Geo; +import com.codenameone.examples.morsel.live.LiveHub; +import com.codenameone.examples.morsel.push.DeliveryPushes; +import com.codenameone.examples.morsel.delivery.Fares; +import com.codenameone.examples.morsel.repository.CourierApplicationRepository; +import com.codenameone.examples.morsel.repository.CourierHoursRepository; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryEventRepository; +import com.codenameone.examples.morsel.repository.DeliveryPassedRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; +import com.codenameone.examples.morsel.delivery.Matcher; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// Couriers' live state and what they have earned. +@Component +public class Couriers { + private static final long DAY_MILLIS = 86400000L; + + /// The states of a delivery a courier has taken and has yet to finish. + private static final DeliveryState[] TAKEN = {DeliveryState.ACCEPTED, DeliveryState.ARRIVED, + DeliveryState.IN_PROGRESS}; + + private final CourierStateRepository couriers; + private final CourierHoursRepository hours; + private final CourierApplicationRepository made; + private final DeliveryRepository deliveries; + private final DeliveryEventRepository events; + private final DeliveryPassedRepository passes; + private final ProfileRepository profiles; + private final LiveHub live; + private final DeliveryPushes pushes; + private final Fares fares; + private final Applications applications; + + public Couriers(CourierStateRepository couriers, CourierHoursRepository hours, + CourierApplicationRepository made, DeliveryRepository deliveries, DeliveryEventRepository events, + DeliveryPassedRepository passes, ProfileRepository profiles, LiveHub live, DeliveryPushes pushes, + Fares fares, Applications applications) { + this.couriers = couriers; + this.hours = hours; + this.made = made; + this.deliveries = deliveries; + this.events = events; + this.passes = passes; + this.profiles = profiles; + this.applications = applications; + this.live = live; + this.pushes = pushes; + this.fares = fares; + } + + /// Records whether a courier is on line and where the car is. The app sends + /// this every few seconds while on line; it is both the position customers + /// follow and the sign the courier is still there. + /// + /// The customer being driven and the admin's fleet view are told once the + /// report is stored, and not before: what they are told sends them here to + /// read it. + public CourierDto report(String courier, CourierStatusDto status) throws IOException { + if (status == null || (status.online() && !Geo.isPoint(status.lat(), status.lng()))) { + throw new ResponseStatusException(400, "Going on line needs the car's position"); + } + Report made = store(courier, status); + CourierDto dto = made.courier; + if (made.deliveryId != null) { + live.courierMoved(made.customer, made.deliveryId, dto.lat(), dto.lng(), dto.heading()); + pushes.courierMoved(made.deliveryId, dto.lat(), dto.lng()); + } + live.fleetChanged(dto.username(), dto.online(), dto.lat(), dto.lng(), dto.heading()); + return dto; + } + + /// A report as it was stored, and the delivery the courier is on, if any. Not + /// private: the class the build writes to begin and commit the transaction + /// of [#store] has to be able to name what it answers. + static final class Report { + CourierDto courier; + String deliveryId; + String customer; + } + + @Transactional + private Report store(String courier, CourierStatusDto status) throws IOException { + // Two reports from one courier take turns, before anything is read: read together, + // both would count the same stretch since the last one as time on + // line. The clock is read after the wait, so reports are also stored + // in the order they were let through and none moves the time back. + // + // A courier's first report has no state row to hold, so two first + // reports would both find none and both insert one under the same + // name. They take turns on the profile instead, the order blocking an + // account takes them in, and the second then finds the first's row. + // No profile either means the account was closed meanwhile, after its + // clean-up deleted the state row; inserting one now would leave it + // behind for good. + if (couriers.hold(courier) == 0 && profiles.hold(courier) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + // The role lets a courier into this part of the API; the approval is + // what lets them take deliveries. They are granted together, but they are + // kept apart, and it is the approval that is asked for here. + if (status.online() && !applications.approved(courier)) { + throw new ResponseStatusException(403, "Your application to drive is not approved"); + } + Delivery delivery = deliveries.findFirstByCourierAndStateIn(courier, TAKEN); + if (!status.online() && delivery != null) { + throw new ResponseStatusException(409, "Finish the delivery before going off line"); + } + long now = System.currentTimeMillis(); + CourierState state = couriers.findById(courier); + // The time since the last report was time on line, unless it was so + // long ago that the app had plainly stopped reporting. It is counted + // here, from the row as it was, and added last, once the row has been + // changed. Adding it is a bulk update that leaves the session as it + // is, so the row read here stays managed and its change is written + // either way; a statement marked clearAutomatically would detach it, + // and a change made after that would be lost. + long since = state != null && state.isOnline() ? now - state.getLastSeen() : 0L; + boolean first = state == null; + if (first) { + state = new CourierState(); + state.setUsername(courier); + } + state.setOnline(status.online()); + state.setLastSeen(now); + // Off line keeps the last position: there is none to report. A first + // report stores whatever came with it. + if (status.online() || first) { + state.setLat(status.lat()); + state.setLng(status.lng()); + state.setHeading(status.heading()); + } + if (first) { + couriers.insert(state); + } + if (since > 0L && since < Matcher.FRESH_MILLIS) { + // Cut at midnight: a stretch on line across it belongs half to + // each day, not all to the new one. + long[] days = Days.split(now - since, now); + for (int iter = 0; iter < days.length; iter += 2) { + addHours(courier, days[iter], days[iter + 1]); + } + } + Report made = new Report(); + made.courier = view(courier, state); + if (delivery != null) { + made.deliveryId = delivery.getId(); + made.customer = delivery.getCustomer(); + } + return made; + } + + @Transactional(readOnly = true) + public CourierDto describe(String courier) throws IOException { + return view(courier, couriers.findById(courier)); + } + + /// A courier with `deliveries` given rather than counted: the deliveries of a period + /// the caller is describing, not of all time. + @Transactional(readOnly = true) + public CourierDto describe(String courier, int deliveries) throws IOException { + return view(courier, couriers.findById(courier), deliveries); + } + + private CourierDto view(String courier, CourierState state) { + return view(courier, state, (int) deliveries.countByCourierAndState(courier, + DeliveryState.COMPLETED)); + } + + private CourierDto view(String courier, CourierState state, int completed) { + Profile profile = profiles.findById(courier); + if (profile == null) { + throw new ResponseStatusException(404, "No such courier"); + } + return toDto(profile, made.findById(courier), state, completed, + acceptanceRate(events.countByActorAndState(courier, DeliveryState.ACCEPTED), + passes.countByCourier(courier))); + } + + /// Adds time on line to a courier's day. The addition is the database's, + /// so reports that overlap are both counted. + private void addHours(String courier, long dayNumber, long millis) { + if (hours.addMillis(courier, dayNumber, millis) == 0) { + CourierHours day = new CourierHours(); + day.setUsername(courier); + day.setDayNumber(dayNumber); + day.setMillis(millis); + hours.insert(day); + } + } + + /// The share of the offers a courier answered that they took: 1 before they + /// have answered any. An offer left to lapse counts as passed on. + static double acceptanceRate(long accepted, long passed) { + long offers = accepted + passed; + return offers == 0L ? 1d : Math.round(accepted * 100d / offers) / 100d; + } + + /// Every account that has driven or gone on line, for the admin's fleet + /// view; those on line first. + @Transactional(readOnly = true) + public List all() throws IOException { + Map done = Rows.counts(deliveries.countByCourier(DeliveryState.COMPLETED)); + Map accepted = Rows.counts(events.countByActor(DeliveryState.ACCEPTED)); + Map passed = Rows.counts(passes.countByCourier()); + Map people = new HashMap(); + List profilesOfAll = profiles.couriers(); + for (int iter = 0; iter < profilesOfAll.size(); iter++) { + people.put(profilesOfAll.get(iter).getUsername(), profilesOfAll.get(iter)); + } + Map forms = new HashMap(); + List applicationsOfAll = made.ofCouriers(); + for (int iter = 0; iter < applicationsOfAll.size(); iter++) { + forms.put(applicationsOfAll.get(iter).getUsername(), applicationsOfAll.get(iter)); + } + List states = couriers.onLineFirst(); + List out = new ArrayList(); + for (int iter = 0; iter < states.size(); iter++) { + CourierState state = states.get(iter); + Profile profile = people.get(state.getUsername()); + if (profile == null) { + // A state left behind by an account that is gone. + continue; + } + Long rode = done.get(state.getUsername()); + Long took = accepted.get(state.getUsername()); + Long left = passed.get(state.getUsername()); + out.add(toDto(profile, forms.get(state.getUsername()), state, + rode == null ? 0 : (int) rode.longValue(), + acceptanceRate(took == null ? 0L : took.longValue(), + left == null ? 0L : left.longValue()))); + } + return out; + } + + /// What a courier's completed, paid deliveries earned them: their share of + /// each fare, without tips. "Today" is the day in UTC; a service in one + /// city would use that city's midnight. + @Transactional(readOnly = true) + public EarningsDto earnings(String courier) throws IOException { + long now = System.currentTimeMillis(); + // The day's trips and the day's money from one query, so the header + // that shows them together counts the deliveries that made the sum. + Object[] day = deliveries.paidTotalsSince(courier, now - now % DAY_MILLIS); + int todayCount = day == null ? 0 : (int) Rows.number(day[0]); + long today = day == null ? 0L : Rows.number(day[1]); + long week = Rows.number(deliveries.paidSince(courier, now - 7 * DAY_MILLIS)); + Object[] total = deliveries.paidTotals(courier); + int count = total == null ? 0 : (int) Rows.number(total[0]); + long all = total == null ? 0L : Rows.number(total[1]); + return new EarningsDto(today, week, all, count, todayCount, fares.currency()); + } + + /// @param application the account's application to drive, or null + /// @param car the account's state, or null when it has never gone on line + private static CourierDto toDto(Profile profile, CourierApplication application, + CourierState car, int deliveries, double acceptanceRate) { + boolean approved = application != null + && Applications.APPROVED.equals(application.getStatus()); + // An account with no application has no product; the app is told the + // standard one so that it always has a word it knows. + String product = application == null ? "" : application.getProduct(); + boolean accessible = application != null && application.isWheelchair(); + boolean online = false; + double lat = 0d; + double lng = 0d; + double heading = 0d; + double rating = 5d; + long lastSeen = 0L; + if (car != null) { + lastSeen = car.getLastSeen(); + // On line means said so and heard from lately; see Matcher. + online = car.isOnline() + && System.currentTimeMillis() - lastSeen < Matcher.FRESH_MILLIS; + lat = car.getLat(); + lng = car.getLng(); + heading = car.getHeading(); + rating = car.rating(); + } + return new CourierDto(profile.getUsername(), profile.getDisplayName(), + profile.getVehicle(), profile.getPlate(), online, lat, lng, heading, rating, + lastSeen, deliveries, approved, product.length() == 0 ? Fares.STANDARD : product, + profile.getGender(), accessible, acceptanceRate, profile.isFlagged()); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Earnings.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Earnings.java new file mode 100644 index 00000000000..ac8d1316cb8 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/courier/Earnings.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codename1.orm.session.Identifier; +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.Rows; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.api.DayAmountDto; +import com.codenameone.examples.morsel.api.EarningsSummaryDto; +import com.codenameone.examples.morsel.api.PayoutAccountDto; +import com.codenameone.examples.morsel.api.PayoutDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierHours; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.domain.Payout; +import com.codenameone.examples.morsel.domain.PayoutAccount; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.repository.CourierHoursRepository; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.PayoutAccountRepository; +import com.codenameone.examples.morsel.repository.PayoutRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryEventRepository; +import com.codenameone.examples.morsel.repository.DeliveryPassedRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; +import com.codenameone.examples.morsel.delivery.Fares; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/// What a courier has earned, and what has been paid out to them. +/// +/// A completed delivery earns its courier the fare less the service's commission, +/// fixed when the delivery was asked for, plus the whole of any tip. The balance +/// is everything earned less everything paid out, and cashing out moves all +/// of it. +/// +/// Payouts are a ledger and no more: a row says the money was sent, and no +/// bank is asked to send it. For the same reason a cash delivery is counted like +/// any other, although its courier was handed the money in the car. A service +/// that moves real money settles cash deliveries the other way, and that sum +/// belongs with the transfer this sample does not make. +@Component +public class Earnings { + private static final int DAYS = 14; + + private final PayoutRepository payouts; + private final PayoutAccountRepository accounts; + private final DeliveryRepository deliveries; + private final DeliveryEventRepository events; + private final DeliveryPassedRepository passes; + private final CourierStateRepository couriers; + private final CourierHoursRepository hours; + private final ProfileRepository profiles; + private final Fares fares; + + public Earnings(PayoutRepository payouts, PayoutAccountRepository accounts, + DeliveryRepository deliveries, DeliveryEventRepository events, DeliveryPassedRepository passes, + CourierStateRepository couriers, CourierHoursRepository hours, + ProfileRepository profiles, Fares fares) { + this.payouts = payouts; + this.accounts = accounts; + this.deliveries = deliveries; + this.events = events; + this.passes = passes; + this.couriers = couriers; + this.hours = hours; + this.profiles = profiles; + this.fares = fares; + } + + @Transactional(readOnly = true) + public EarningsSummaryDto summary(String courier) throws IOException { + long now = System.currentTimeMillis(); + long today = Days.start(now); + long month = 0L; + long week = 0L; + long earnedToday = 0L; + // Each day's money and trips, oldest first. + long[] dayAmount = new long[DAYS]; + int[] dayTrips = new int[DAYS]; + // Thirty days of deliveries, added up here: "group by day" is spelt + // differently by every database, and a courier's month is a few hundred + // rows. + long monthStart = today - 29 * Days.MILLIS; + long weekStart = today - 6 * Days.MILLIS; + List done = deliveries.findByCourierAndStateAndUpdatedAtGreaterThanEqual(courier, + DeliveryState.COMPLETED, monthStart); + // A card tip is on its delivery from the moment it is asked for, and is + // still there when the processor never answered. Only one whose charge + // is on record is earned, here as in the balance: otherwise a day's + // total would show money that cannot be cashed out. + Set charged = new HashSet( + deliveries.tipChargedSince(courier, monthStart)); + for (int iter = 0; iter < done.size(); iter++) { + Delivery delivery = done.get(iter); + long at = delivery.getUpdatedAt(); + boolean tipped = "cash".equals(delivery.getMethodId()) || charged.contains(delivery.getId()); + // The fare likewise counts only once it is paid, as in the balance: + // a card charge that failed, was refunded or has not come back is + // no money of the courier's. The trip still counts as driven. + boolean paid = Payments.PAID.equals(delivery.getPaymentStatus()); + long earned = (paid ? delivery.getCourierCents() : 0L) + (tipped ? delivery.getTipCents() : 0L); + month += earned; + if (at >= weekStart) { + week += earned; + } + if (at >= today) { + earnedToday += earned; + } + int index = DAYS - 1 - (int) (Days.number(today) - Days.number(at)); + if (index >= 0 && index < DAYS) { + dayAmount[index] += earned; + dayTrips[index]++; + } + } + List daily = new ArrayList(); + for (int iter = 0; iter < DAYS; iter++) { + daily.add(new DayAmountDto(Days.iso(today - (DAYS - 1 - iter) * Days.MILLIS), + dayAmount[iter], dayTrips[iter])); + } + int trips = (int) deliveries.countByCourierAndState(courier, DeliveryState.COMPLETED); + CourierHours day = hours.findById(Identifier.of(courier, Long.valueOf(Days.number(now)))); + int onlineMinutes = day == null ? 0 : (int) (day.getMillis() / 60000L); + CourierState car = couriers.findById(courier); + double rating = car == null ? 5d : car.rating(); + double acceptanceRate = Couriers.acceptanceRate(events.countByActorAndState(courier, + DeliveryState.ACCEPTED), passes.countByCourier(courier)); + return new EarningsSummaryDto(fares.currency(), earnedToday, week, month, + balance(courier), trips, tips(courier), onlineMinutes, rating, acceptanceRate, + daily); + } + + /// The payouts made to a courier, newest first. + @Transactional(readOnly = true) + public List payouts(String courier) throws IOException { + List rows = payouts.findTop100ByCourierOrderByCreatedAtDescIdAsc(courier); + List out = new ArrayList(); + for (int iter = 0; iter < rows.size(); iter++) { + out.add(toDto(rows.get(iter))); + } + return out; + } + + /// Pays out the whole balance to the courier's account. Answers 409 when + /// there is no account to pay it to, or nothing to pay. + /// + /// Two requests at once would both read the same balance and both pay + /// it. So the first thing the transaction does is hold the courier's + /// payout account, the one row every cash-out of theirs needs: the second + /// request waits there until the first has committed, then reads a + /// balance that already has the first payout taken off it, and is told + /// there is nothing left. The hold has to come before any read, or the + /// second request would have looked at the ledger before it waited. + @Transactional + public PayoutDto cashOut(String courier) throws IOException { + if (accounts.hold(courier) == 0) { + throw new ResponseStatusException(409, "Add the account to be paid into first"); + } + PayoutAccount account = accounts.findById(courier); + long amount = balance(courier); + if (amount <= 0L) { + throw new ResponseStatusException(409, "There is nothing to cash out"); + } + Payout row = new Payout(); + row.setId(Ids.next()); + row.setCourier(courier); + row.setAmountCents(amount); + row.setStatus("paid"); + row.setCreatedAt(System.currentTimeMillis()); + row.setDestination(account.getBankName() + " ****" + account.getLast4()); + payouts.insert(row); + return toDto(row); + } + + /// Where a courier is paid; every field empty until they have said. + @Transactional(readOnly = true) + public PayoutAccountDto account(String courier) throws IOException { + return toDto(accounts.findById(courier)); + } + + /// Saves where a courier is paid. Only the last four digits of the account + /// are taken, which is all a ledger with no bank behind it has a use for. + @Transactional + public PayoutAccountDto saveAccount(String courier, PayoutAccountDto wanted) + throws IOException { + if (wanted == null) { + throw new ResponseStatusException(400, "Nothing to save"); + } + String last4 = wanted.accountLast4() == null ? "" : wanted.accountLast4().trim(); + boolean digits = last4.length() == 4; + for (int iter = 0; digits && iter < 4; iter++) { + digits = last4.charAt(iter) >= '0' && last4.charAt(iter) <= '9'; + } + if (!digits) { + throw new ResponseStatusException(400, "Give the last four digits of the account"); + } + String holder = Text.required(wanted.holder(), 120, "the account holder's name"); + String bankName = Text.required(wanted.bankName(), 80, "the bank's name"); + // Before the row is looked for. Two first saves at once would both + // find none and both insert one under the same name, and one would + // fail; behind the profile's hold the second finds the first's row + // and updates it. The payout account itself cannot be held: until + // this inserts it there is no row. With no profile to hold the account + // was closed meanwhile and its clean-up has run: a row inserted now + // would keep the closed account's bank details with nobody able to + // remove them. + if (profiles.hold(courier) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + PayoutAccount row = accounts.findById(courier); + boolean added = row == null; + if (added) { + row = new PayoutAccount(); + row.setUsername(courier); + } + row.setHolder(holder); + row.setBankName(bankName); + row.setLast4(last4); + if (added) { + accounts.insert(row); + } + return toDto(row); + } + + /// Everything earned less everything paid out. + /// + /// The dashboard's totals count the same deliveries as [#earned]. Closing an + /// account asks it too, and is refused while it is above zero. + public long balance(String courier) { + return earned(courier) - Rows.number(payouts.paidOut(courier)); + } + + /// Everything a courier has earned, before anything was paid out: their + /// share of the fares of every delivery they completed and that was paid for, + /// plus their tips as [#tips] counts them. + /// + /// The administrator's view of an account shows this figure, and takes it + /// from here rather than from a sum of its own, so the two can never + /// disagree about a charge that failed, a refund or a tip whose charge has + /// not come back. + /// + /// @param courier the courier's user name + /// @return the cents earned + public long earned(String courier) { + return Rows.number(deliveries.courierShare(courier)) + tips(courier); + } + + /// The tips a courier has been given: those handed over in cash, and those + /// put on a card whose charge has come back. + /// + /// A card tip sits on its delivery from the moment it is asked for, and stays + /// there with no charge recorded when the processor did not answer. It is + /// not money yet -- the card may never have been charged -- so it is not + /// counted, and so cannot be cashed out, until the charge is on record. + private long tips(String courier) { + return Rows.number(deliveries.cashTips(courier)) + Rows.number(deliveries.chargedTips(courier)); + } + + private static PayoutAccountDto toDto(PayoutAccount row) { + if (row == null) { + return new PayoutAccountDto("", "", ""); + } + return new PayoutAccountDto(row.getHolder(), row.getBankName(), row.getLast4()); + } + + private static PayoutDto toDto(Payout row) { + return new PayoutDto(row.getId(), row.getAmountCents(), row.getStatus(), + row.getCreatedAt(), row.getDestination()); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/CustomerEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/CustomerEndpoint.java new file mode 100644 index 00000000000..45dc2e92f03 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/CustomerEndpoint.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.Caller; +import com.codenameone.examples.morsel.Roles; +import com.codenameone.examples.morsel.api.FareQuoteDto; +import com.codenameone.examples.morsel.api.NearbyCourierDto; +import com.codenameone.examples.morsel.api.RatingDto; +import com.codenameone.examples.morsel.api.DeliveryActivityDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryRequestDto; +import com.codenameone.examples.morsel.api.CustomerApiServer; +import com.codenameone.examples.morsel.push.DeliveryPushes; + +import java.util.List; + +// tag::morsel-contract-endpoint[] +/// The server's half of `CustomerApi`. Every method acts as the signed-in user and +/// on that user's deliveries only: the customer is never a parameter. +@Component +public class CustomerEndpoint implements CustomerApiServer { + private final Deliveries deliveries; + private final DeliveryPushes pushes; + + public CustomerEndpoint(Deliveries deliveries, DeliveryPushes pushes) { + this.deliveries = deliveries; + this.pushes = pushes; + } + + @Override + public FareQuoteDto quote(DeliveryRequestDto request) throws Exception { + return deliveries.quote(request); + } + + @Override + public DeliveryDto request(DeliveryRequestDto request) throws Exception { + return deliveries.request(Caller.name(), request); + } + // end::morsel-contract-endpoint[] + + @Override + public DeliveryDto active() throws Exception { + return deliveries.activeForCustomer(Caller.name()); + } + + @Override + public List history() throws Exception { + return deliveries.historyForCustomer(Caller.name()); + } + + @Override + public DeliveryDto delivery(String id) throws Exception { + return deliveries.get(Caller.name(), Caller.hasRole(Roles.ADMIN), id); + } + + @Override + public DeliveryDto cancel(String id) throws Exception { + return deliveries.cancelByCustomer(Caller.name(), id); + } + + @Override + public List nearby(double lat, double lng) throws Exception { + Caller.name(); + return deliveries.nearbyCouriers(lat, lng); + } + + @Override + public DeliveryDto rate(String id, RatingDto rating) throws Exception { + return deliveries.rate(Caller.name(), id, rating == null ? 0 : rating.stars()); + } + + @Override + public DeliveryActivityDto activity(String id, DeliveryActivityDto activity) throws Exception { + return pushes.attach(Caller.name(), id, activity); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Deliveries.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Deliveries.java new file mode 100644 index 00000000000..23e73edfd73 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Deliveries.java @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codename1.backend.ConcurrencyFailureException; +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Scheduled; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.annotations.Value; +import com.codename1.orm.repository.PageRequest; +import com.codename1.orm.session.Identifier; +import com.codename1.orm.session.PersistenceException; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.account.Moderation; +import com.codenameone.examples.morsel.api.FareOptionDto; +import com.codenameone.examples.morsel.api.FareQuoteDto; +import com.codenameone.examples.morsel.api.NearbyCourierDto; +import com.codenameone.examples.morsel.api.PricingDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryRequestDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.domain.DeliveryEvent; +import com.codenameone.examples.morsel.domain.DeliveryPassed; +import com.codenameone.examples.morsel.geo.Geo; +import com.codenameone.examples.morsel.live.LiveHub; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.push.DeliveryPushes; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryEventRepository; +import com.codenameone.examples.morsel.repository.DeliveryPassedRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// Deliveries: asking for one, finding it a courier, and taking it through to the end. +/// +/// Every change of state goes through [#move], which makes it with a single +/// conditional update ([DeliveryStateMachine]) and records it. The people concerned +/// are then told over the live channel. What they are told is only that the +/// delivery changed; they read the delivery itself from here, so there is one account +/// of a delivery and the live channel cannot disagree with it. +/// +/// That is also why the telling waits for the change to be committed. An app +/// told of a change asks for the delivery at once, over another connection, and +/// must find it changed. So the methods that change a delivery come in pairs: a +/// private one that is the transaction, and the public one that calls it and, +/// once it has returned, tells whoever is concerned. +@Component +public class Deliveries { + /// Deliveries shorter than this are not deliveries, and longer than this are not this + /// service's. + static final double MIN_METERS = 100d; + static final double MAX_METERS = 150000d; + private static final String[] COURIER_GENDERS = {"any", "women"}; + private static final String WOMEN = "women"; + /// What a customer is told about each kind of delivery: its name, a line about + /// it, and how many it seats. In the order of [Fares#PRODUCTS]. + private static final String[][] PRODUCT_TEXT = { + {"Morsel", "Affordable everyday deliveries", "4"}, + {"Comfort", "Newer cars with extra legroom", "4"}, + {"XL", "Room for up to six", "6"}}; + /// How many couriers one search may choose before it leaves the delivery to the + /// next tick; see [#search]. + private static final int SEARCH_TRIES = 3; + /// The two people a move can be held to; see [#tryMove]. + private static final String CUSTOMER = "customer"; + private static final String COURIER = "courier"; + private static final DeliveryState[] ACTIVE = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + private static final DeliveryState[] WITH_COURIER = {DeliveryState.OFFERED, DeliveryState.ACCEPTED, + DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + private static final DeliveryState[] ENDED_WITH_COURIER = {DeliveryState.COMPLETED, + DeliveryState.CANCELLED_BY_CUSTOMER, DeliveryState.CANCELLED_BY_COURIER, + DeliveryState.CANCELLED_BY_ADMIN}; + + private final DeliveryRepository deliveries; + private final DeliveryEventRepository events; + private final DeliveryPassedRepository passes; + private final CourierStateRepository couriers; + private final ProfileRepository profiles; + private final Matcher matcher; + private final Fares fares; + private final Payments payments; + private final LiveHub live; + private final DeliveryPushes pushes; + private final Moderation moderation; + private final long offerMillis; + private final long searchMillis; + + /// @param offerSeconds how long a courier has to answer an offer + /// @param searchSeconds how long a delivery looks for a courier before giving up + public Deliveries(DeliveryRepository deliveries, DeliveryEventRepository events, DeliveryPassedRepository passes, + CourierStateRepository couriers, ProfileRepository profiles, Matcher matcher, Fares fares, Payments payments, LiveHub live, DeliveryPushes pushes, + Moderation moderation, + @Value("${morsel.offer.seconds:15}") int offerSeconds, + @Value("${morsel.search.seconds:90}") int searchSeconds) { + this.deliveries = deliveries; + this.events = events; + this.passes = passes; + this.couriers = couriers; + this.profiles = profiles; + this.moderation = moderation; + this.matcher = matcher; + this.fares = fares; + this.payments = payments; + this.live = live; + this.pushes = pushes; + offerMillis = offerSeconds * 1000L; + searchMillis = searchSeconds * 1000L; + } + + // ---------------------------------------------------------------- customers + + @Transactional(readOnly = true) + public FareQuoteDto quote(DeliveryRequestDto request) throws IOException { + validate(request); + double distanceMeters = fares.distanceMeters(request); + double durationSeconds = fares.durationSeconds(request, distanceMeters); + String wanted = Fares.product(request.product()); + PricingDto pricing = fares.pricing(); + // The couriers who could take it as the customer asked for it, of every + // kind of car, nearest first; each kind then counts its own. + List near = matcher.nearby(request.pickupLat(), request.pickupLng(), + Matcher.FAR_METERS, null, WOMEN.equals(courierGender(request)), + request.accessibleVehicle(), request.petFriendly()); + List options = new ArrayList(); + long amountCents = 0L; + int couriersNearby = 0; + int etaSeconds = 0; + for (int iter = 0; iter < Fares.PRODUCTS.length; iter++) { + String product = Fares.PRODUCTS[iter]; + Fares.Price price = Fares.price(pricing, distanceMeters, durationSeconds, product); + int nearby = 0; + double nearest = -1d; + for (int d = 0; d < near.size(); d++) { + if (product.equals(near.get(d).product)) { + nearby++; + if (nearest < 0d) { + nearest = near.get(d).meters; + } + } + } + int eta = nearest < 0d ? 0 : (int) Math.max(60d, nearest * Fares.ROAD_FACTOR + / Fares.METERS_PER_SECOND); + options.add(new FareOptionDto(product, PRODUCT_TEXT[iter][0], PRODUCT_TEXT[iter][1], + Integer.parseInt(PRODUCT_TEXT[iter][2]), price.fare, price.fee, + price.fare + price.fee, nearby, (eta + 59) / 60)); + if (product.equals(wanted)) { + // The quote's own fields are the kind of delivery that was asked + // about, which is the standard one for an app that names none. + amountCents = price.fare; + couriersNearby = nearby; + etaSeconds = eta; + } + } + return new FareQuoteDto(amountCents, fares.currency(), distanceMeters, durationSeconds, + couriersNearby, etaSeconds, options); + } + + /// The cars near a point, for the map a customer looks at before asking: + /// where they are and what kind, and nothing about who drives them. The + /// positions are rounded to about ten metres, and there are at most twenty. + @Transactional(readOnly = true) + public List nearbyCouriers(double lat, double lng) throws IOException { + if (!Geo.isPoint(lat, lng)) { + throw new ResponseStatusException(400, "That is not a place"); + } + List near = matcher.nearby(lat, lng, Matcher.FAR_METERS); + List out = new ArrayList(); + for (int iter = 0; iter < near.size() && iter < 20; iter++) { + Matcher.Candidate car = near.get(iter); + out.add(new NearbyCourierDto(Math.round(car.lat * 10000d) / 10000d, + Math.round(car.lng * 10000d) / 10000d, car.heading, car.product)); + } + return out; + } + + /// Asks for a delivery and offers it to the nearest courier straight away. + public DeliveryDto request(String customer, DeliveryRequestDto request) throws IOException { + validate(request); + String id = attempt(() -> open(customer, request)); + live.deliveryChanged(id, DeliveryState.REQUESTED, customer, "", ""); + search(id); + return read(id, customer); + } + + @Transactional + private String open(String customer, DeliveryRequestDto request) throws IOException { + // Before anything is read: two requests from one customer take turns + // here, so the second looks for a delivery under way only once the first + // has committed its own. Without it both look, both find none, and + // the customer has two deliveries. + profiles.hold(customer); + Profile profile = profiles.findById(customer); + if (profile == null || !profile.isPhoneVerified()) { + throw new ResponseStatusException(403, "Verify your phone number before you delivery"); + } + if (deliveries.findFirstByCustomerAndStateIn(customer, ACTIVE) != null) { + throw new ResponseStatusException(409, "You already have a delivery under way"); + } + String product = Fares.product(request.product()); + boolean womenOnly = WOMEN.equals(courierGender(request)); + // Refused here, before there is a delivery, when the card named is not the + // customer's. With none named it is their default, which is cash until + // they save a card. + String[] method = payments.choose(customer, request.paymentMethodId()); + double meters = fares.distanceMeters(request); + double seconds = fares.durationSeconds(request, meters); + PricingDto pricing = fares.pricing(); + Fares.Price price = Fares.price(pricing, meters, seconds, product); + long now = System.currentTimeMillis(); + // The fare is fixed here, from the server's own arithmetic, and so is + // the courier's share of it: a change of prices or of commission made + // while the delivery is under way is for the deliveries after it. The quote the + // customer was shown is not sent back and would not be believed. + Delivery delivery = new Delivery(); + delivery.setId(Ids.next()); + delivery.setState(DeliveryState.REQUESTED); + delivery.setCustomer(customer); + delivery.setPickupLat(request.pickupLat()); + delivery.setPickupLng(request.pickupLng()); + delivery.setPickupAddress(address(request.pickupAddress())); + delivery.setDropoffLat(request.dropoffLat()); + delivery.setDropoffLng(request.dropoffLng()); + delivery.setDropoffAddress(address(request.dropoffAddress())); + delivery.setFareCents(price.fare); + delivery.setCurrency(fares.currency()); + delivery.setDistanceMeters(meters); + delivery.setDurationSeconds(seconds); + delivery.setRequestedAt(now); + delivery.setUpdatedAt(now); + delivery.setSearchUntil(now + searchMillis); + delivery.setPaymentStatus(Payments.PENDING); + delivery.setProduct(product); + delivery.setMethodId(method[0]); + delivery.setMethodLabel(method[1]); + delivery.setBaseCents(price.base); + delivery.setDistanceCents(price.distance); + delivery.setTimeCents(price.time); + delivery.setFeeCents(price.fee); + delivery.setCourierCents(price.fare - Fares.commission(pricing, price.fare)); + delivery.setWomenOnly(womenOnly); + delivery.setQuiet(request.quietDelivery()); + delivery.setWheelchair(request.accessibleVehicle()); + delivery.setNote(address(request.note())); + delivery.setPets(request.petFriendly()); + deliveries.insert(delivery); + event(delivery.getId(), DeliveryState.REQUESTED, customer); + return delivery.getId(); + } + + // tag::morsel-service-readonly[] + @Transactional(readOnly = true) + public DeliveryDto activeForCustomer(String customer) throws IOException { + Delivery delivery = deliveries.findFirstByCustomerAndStateIn(customer, ACTIVE); + return delivery == null ? none() : toDto(delivery, customer, new HashMap()); + } + // end::morsel-service-readonly[] + + @Transactional(readOnly = true) + public List historyForCustomer(String customer) throws IOException { + return list(deliveries.findTop50ByCustomerOrderByRequestedAtDesc(customer), customer); + } + + // tag::morsel-service-transaction[] + public DeliveryDto cancelByCustomer(String customer, String id) throws IOException { + Change change = attempt(() -> cancelAsCustomer(customer, id)); + // The courier it was offered to or who had taken it is told as well. + return tell(change); + } + + @Transactional + private Change cancelAsCustomer(String customer, String id) throws IOException { + Delivery before = owned(id, CUSTOMER, customer); + move(id, DeliveryState.CANCELLED_BY_CUSTOMER, customer, CUSTOMER, null, null); + return new Change(id, DeliveryState.CANCELLED_BY_CUSTOMER, customer, before.getCourier(), "", + view(id, customer)); + } + // end::morsel-service-transaction[] + + /// Rates a completed delivery, once, and counts it toward the courier's rating. + public DeliveryDto rate(String customer, String id, int stars) throws IOException { + if (stars < 1 || stars > 5) { + throw new ResponseStatusException(400, "A rating is 1 to 5 stars"); + } + return attempt(() -> rated(customer, id, stars)); + } + + @Transactional + private DeliveryDto rated(String customer, String id, int stars) throws IOException { + String courier = owned(id, CUSTOMER, customer).getCourier(); + if (deliveries.rate(id, customer, stars) != 1) { + throw new ResponseStatusException(409, "That delivery cannot be rated now"); + } + couriers.addRating(courier, stars); + return view(id, customer); + } + + // --------------------------------------------------------------- couriers + + /// The offer waiting for this courier's answer, or the delivery they are on. + @Transactional(readOnly = true) + public DeliveryDto activeForCourier(String courier) throws IOException { + Delivery delivery = deliveries.findFirstByCourierAndStateIn(courier, WITH_COURIER); + return delivery == null ? none() : toDto(delivery, courier, new HashMap()); + } + + @Transactional(readOnly = true) + public List historyForCourier(String courier) throws IOException { + return list(deliveries.findTop50ByCourierAndStateInOrderByRequestedAtDesc(courier, + ENDED_WITH_COURIER), courier); + } + + public DeliveryDto accept(String courier, String id) throws IOException { + return tell(attempt(() -> step(courier, id, DeliveryState.ACCEPTED))); + } + + /// Passes on an offer. The delivery goes back to looking, and not at this + /// courier again. + public DeliveryDto decline(String courier, String id) throws IOException { + tell(attempt(() -> pass(courier, id))); + search(id); + return none(); + } + + @Transactional + private Change pass(String courier, String id) throws IOException { + Delivery delivery = owned(id, COURIER, courier); + move(id, DeliveryState.REQUESTED, courier, COURIER, "", null); + passed(id, courier); + return new Change(id, DeliveryState.REQUESTED, delivery.getCustomer(), "", courier, null); + } + + public DeliveryDto arrived(String courier, String id) throws IOException { + return tell(attempt(() -> step(courier, id, DeliveryState.ARRIVED))); + } + + public DeliveryDto start(String courier, String id) throws IOException { + return tell(attempt(() -> step(courier, id, DeliveryState.IN_PROGRESS))); + } + + /// Ends the trip and charges the customer. + /// + /// In three steps, and only the first and the last are transactions. The + /// charge between them may be a call to a payment provider, which can take + /// seconds, and a transaction held open across it would hold a connection + /// for as long. + public DeliveryDto complete(String courier, String id) throws IOException { + Delivery delivery = attempt(() -> finish(courier, id)); + // After the move, and only by the request whose update made it: a delivery + // completes once, so it is charged once. + String status = payments.settle(delivery); + return tell(settled(courier, id, status)); + } + + @Transactional + private Delivery finish(String courier, String id) throws IOException { + Delivery delivery = owned(id, COURIER, courier); + move(id, DeliveryState.COMPLETED, courier, COURIER, null, null); + return delivery; + } + + /// How long a completed delivery may wait for its payment before it is taken + /// to have been left behind. Longer than any charge takes. + private static final long UNSETTLED_MILLIS = 2L * 60L * 1000L; + + /// How long a completed delivery's charge goes on being asked for. Short of + /// the day a processor remembers the key a charge was first sent under. + private static final long RETRY_MILLIS = Payments.RETRY_MILLIS; + + /// Finishes the payment of deliveries that completed and were never settled. + /// + /// Completing a delivery commits before the card is charged, and what came of + /// the charge is written after it. A server stopped between the two leaves + /// a delivery that is over and says its payment is pending, and nothing would + /// come back for it: the courier's request cannot be made again, the delivery + /// having completed. This does. Settling is safe to repeat -- see + /// [Payments#settle] -- so a delivery whose card was charged just before the + /// server stopped is recorded as paid and not charged again. + /// + /// A delivery whose charge failed without the processor saying whether it took + /// the money is left pending too, and is asked about again here, every + /// minute for [#RETRY_MILLIS]. After that it stays pending and is not asked + /// about: the key that makes asking twice safe is kept by a processor for a + /// day, and a charge sent under it later than that is a second charge. What + /// became of such a delivery is for a person to find in the processor's records. + @Scheduled(fixedDelay = 60000, initialDelay = 30000, lock = "morsel-settlement") + public void settleLeftBehind() throws IOException { + long now = System.currentTimeMillis(); + List unsettled = unsettled(now - RETRY_MILLIS, now - UNSETTLED_MILLIS); + Exception failed = null; + for (int iter = 0; iter < unsettled.size(); iter++) { + Delivery delivery = unsettled.get(iter); + try { + tell(settled(delivery.getCourier(), delivery.getId(), payments.settle(delivery))); + } catch (IOException err) { + failed = failed == null ? err : failed; + } catch (RuntimeException err) { + failed = failed == null ? err : failed; + } + } + if (failed instanceof IOException) { + throw (IOException) failed; + } + if (failed instanceof RuntimeException) { + throw (RuntimeException) failed; + } + } + + @Transactional(readOnly = true) + private List unsettled(long after, long before) { + return deliveries.unsettled(after, before, new PageRequest(0, 50)); + } + + @Transactional + private Change settled(String courier, String id, String status) throws IOException { + deliveries.paymentStatus(id, status); + Delivery delivery = find(id); + return new Change(id, delivery.getState(), delivery.getCustomer(), delivery.getCourier(), "", + toDto(delivery, courier, new HashMap())); + } + + public DeliveryDto cancelByCourier(String courier, String id) throws IOException { + return tell(attempt(() -> step(courier, id, DeliveryState.CANCELLED_BY_COURIER))); + } + + /// One of a courier's own moves of a delivery that is theirs, and the delivery as + /// they see it afterwards. + @Transactional + private Change step(String courier, String id, DeliveryState to) throws IOException { + if (DeliveryState.ACCEPTED == to) { + // Blocking a courier is refused while they have a delivery taken, and + // takes the courier's profile to look. Taking an offer holds the + // same row first and then asks whether the account is still + // allowed: a block that committed meanwhile is seen here, and a + // block that comes after this commits sees the accepted delivery. + // Either way no delivery is left with a courier who can no longer + // drive it. + profiles.hold(courier); + if (!matcher.enabled(courier)) { + throw new ResponseStatusException(403, "This account has been blocked"); + } + } + owned(id, COURIER, courier); + move(id, to, courier, COURIER, null, null); + Delivery delivery = find(id); + return new Change(id, delivery.getState(), delivery.getCustomer(), delivery.getCourier(), "", + toDto(delivery, courier, new HashMap())); + } + + // ----------------------------------------------------------------- admin + + /// Ends a delivery that has not ended, whoever has it and whatever it was + /// doing. Nothing is charged: a delivery pays when it completes, and this one + /// does not. The reason is kept with the customer's account. + public DeliveryDto cancelByAdmin(String by, String id, String reason) throws IOException { + String why = Text.required(reason, 255, "why the delivery is cancelled"); + return tell(attempt(() -> cancelAsAdmin(by, id, why))); + } + + @Transactional + private Change cancelAsAdmin(String by, String id, String why) throws IOException { + Delivery before = find(id); + if (before == null) { + throw new ResponseStatusException(404, "No such delivery"); + } + String customer = before.getCustomer(); + String courier = before.getCourier(); + move(id, DeliveryState.CANCELLED_BY_ADMIN, by, null, null, null); + moderation.record(customer, by, "cancel_delivery", why); + return new Change(id, DeliveryState.CANCELLED_BY_ADMIN, customer, courier, "", + toDto(find(id), courier, new HashMap(), false)); + } + + // ---------------------------------------------------------------- anyone + + /// A delivery, for someone entitled to see it: its customer, its courier, or an + /// admin. Anyone else is told it does not exist, which is all they should + /// learn about it. + @Transactional(readOnly = true) + public DeliveryDto get(String viewer, boolean admin, String id) throws IOException { + Delivery delivery = find(id); + if (delivery == null || !(admin || viewer.equals(delivery.getCustomer()) + || viewer.equals(delivery.getCourier()))) { + throw new ResponseStatusException(404, "No such delivery"); + } + return toDto(delivery, admin ? delivery.getCourier() : viewer, new HashMap(), + !admin); + } + + /// The most recent deliveries, for the admin. + @Transactional(readOnly = true) + public List recent() throws IOException { + List rows = deliveries.newest(new PageRequest(0, 100)); + Map cache = new HashMap(); + List out = new ArrayList(); + for (int iter = 0; iter < rows.size(); iter++) { + Delivery delivery = rows.get(iter); + out.add(toDto(delivery, delivery.getCourier(), cache, false)); + } + return out; + } + + // -------------------------------------------------------------- matching + + /// Offers a waiting delivery to the nearest courier who has not passed on it. + /// Does nothing when nobody is in reach; [#tick] tries again. + /// + /// In two transactions: one that chooses a courier and one that makes the + /// offer. Choosing reads who is free, and two deliveries asked for together + /// each read the same courier as free; the second transaction is where + /// they take turns over that courier, and it has to open with that -- see + /// [#offer]. + /// + /// A courier the offer finds no longer fit for the delivery is passed over for + /// the next, as a decline or a lapse would be, without waiting for the + /// next tick: choosing again reads them as they now are, and does not pick + /// them. Bounded, so couriers changing faster than this can choose cost the + /// delivery a tick and no more. + void search(String id) throws IOException { + String[] chosen = new String[1]; + Change change = null; + for (int tries = 0; tries < SEARCH_TRIES; tries++) { + change = attempt(() -> choose(id, chosen)); + if (chosen[0] == null) { + break; + } + change = attempt(() -> offer(id, chosen[0])); + if (change != null) { + break; + } + } + tell(change); + } + + /// Gives up on a delivery that has waited too long, or names the courier to + /// offer it to in `chosen`. Returns what it changed, or null for nothing. + @Transactional + private Change choose(String id, String[] chosen) throws IOException { + chosen[0] = null; + Delivery delivery = find(id); + if (delivery == null || DeliveryState.REQUESTED != delivery.getState()) { + return null; + } + long now = System.currentTimeMillis(); + if (now > delivery.getSearchUntil()) { + return tryMove(id, DeliveryState.NO_COURIERS, "", null, null, null) + ? new Change(id, DeliveryState.NO_COURIERS, delivery.getCustomer(), "", "", null) : null; + } + chosen[0] = matcher.pick(delivery, now - delivery.getRequestedAt()); + return null; + } + + // tag::morsel-delivery-offer[] + /// Offers a delivery to the courier chosen for it, unless they were given + /// another in the meantime. Returns what it changed, or null for nothing, + /// in which case [#tick] looks again. + /// + /// The courier is held before anything is read, so two deliveries that chose + /// the same courier come through here one after the other, and the second + /// reads the offer the first one committed. The update of the delivery cannot + /// do this on its own: it is conditional on its own delivery's row, and each + /// of the two has a row of its own. + /// + /// Everything that made the courier the choice is asked again once they + /// are held: a courier who went off line, drove out of reach, lost their + /// approval or was blocked since [#choose] read them would otherwise get + /// the offer and keep the customer waiting for it to lapse. Not private, so + /// that a test can make the offer to a courier who changed in between. + @Transactional + Change offer(String id, String courier) throws IOException { + if (couriers.hold(courier) == 0 + || deliveries.findFirstByCourierAndStateIn(courier, WITH_COURIER) != null) { + return null; + } + Delivery delivery = find(id); + if (delivery == null || DeliveryState.REQUESTED != delivery.getState()) { + return null; + } + long now = System.currentTimeMillis(); + if (!matcher.fits(delivery, courier, now - delivery.getRequestedAt())) { + return null; + } + return tryMove(id, DeliveryState.OFFERED, "", null, courier, Long.valueOf(now + offerMillis)) + ? new Change(id, DeliveryState.OFFERED, delivery.getCustomer(), courier, "", null) : null; + } + // end::morsel-delivery-offer[] + + /// Takes back the offers nobody answered and looks again for every delivery + /// still waiting. Runs every two seconds, on one instance at a time: `lock` + /// keeps a second server from offering the same delivery to two couriers. + /// + /// Not a transaction itself. Each delivery is dealt with in one of its own, so + /// a delivery that cannot be dealt with costs nobody else their turn; what went + /// wrong with the first such delivery is reported once the rest are done. + @Scheduled(fixedDelay = 2000, initialDelay = 2000, lock = "morsel-matching") + public void tick() throws IOException { + Exception failed = null; + List lapsed = lapsedOffers(System.currentTimeMillis()); + for (int iter = 0; iter < lapsed.size(); iter++) { + Delivery delivery = lapsed.get(iter); + try { + tell(attempt(() -> lapse(delivery.getId(), delivery.getCustomer(), delivery.getCourier()))); + } catch (IOException err) { + failed = failed == null ? err : failed; + } catch (RuntimeException err) { + failed = failed == null ? err : failed; + } + } + List waiting = waitingDeliveries(); + for (int iter = 0; iter < waiting.size(); iter++) { + try { + search(waiting.get(iter).getId()); + } catch (IOException err) { + failed = failed == null ? err : failed; + } catch (RuntimeException err) { + failed = failed == null ? err : failed; + } + } + if (failed instanceof IOException) { + throw (IOException) failed; + } + if (failed instanceof RuntimeException) { + throw (RuntimeException) failed; + } + } + + @Transactional(readOnly = true) + private List lapsedOffers(long now) throws IOException { + return deliveries.findByStateAndOfferExpiresAtLessThan(DeliveryState.OFFERED, now); + } + + @Transactional(readOnly = true) + private List waitingDeliveries() throws IOException { + return deliveries.findByStateOrderByRequestedAtAsc(DeliveryState.REQUESTED); + } + + /// Takes a lapsed offer back from the courier it was made to, or changes + /// nothing and answers null. + @Transactional + private Change lapse(String id, String customer, String courier) throws IOException { + // Held to the courier it lapsed for: if they accepted in the instant + // since the query, this changes nothing and the delivery is theirs. + if (!tryMove(id, DeliveryState.REQUESTED, courier, COURIER, "", null)) { + return null; + } + passed(id, courier); + return new Change(id, DeliveryState.REQUESTED, customer, "", courier, null); + } + + // ------------------------------------------------------------- internals + + /// What a transaction did to a delivery, for telling the people concerned once + /// it has committed, and the delivery as the caller is to see it. Not private: + /// the transactional methods answer it, and the class the build writes to + /// begin and commit their transactions has to be able to name it. + static final class Change { + final String id; + final DeliveryState state; + final String customer; + final String courier; + /// Someone else to tell: the courier an offer was taken back from. + final String also; + final DeliveryDto view; + + Change(String id, DeliveryState state, String customer, String courier, String also, DeliveryDto view) { + this.id = id; + this.state = state; + this.customer = customer; + this.courier = courier; + this.also = also; + this.view = view; + } + } + + /// Tells the people concerned about a change that has been committed, and + /// answers the view that came with it. Nothing changed, nothing said. + private DeliveryDto tell(Change change) { + if (change == null) { + return null; + } + live.deliveryChanged(change.id, change.state, change.customer, change.courier, change.also); + // The same moment, and for the same reason: a phone told of a change + // must find it made. + pushes.deliveryChanged(change.id, change.state, change.customer, change.courier); + return change.view; + } + + /// One of the transactions of this class, for [#attempt] to run. + private interface Work { + T run() throws IOException; + } + + /// Runs a transaction that changes a delivery, and runs it again if the database + /// ended it because another transaction changed the same delivery first. + /// + /// Every change here is a delivery read and then moved by a conditional update, + /// and two of them racing are meant to be settled by that update: the loser + /// changes no rows. Most databases do exactly that. One that holds a + /// transaction to the snapshot it first read from -- MariaDB, as it is + /// configured out of the box -- refuses the loser's update instead, because + /// the delivery it read is no longer the delivery that is there, and ends its + /// transaction with a [ConcurrencyFailureException]. + /// + /// That is the race lost and not a fault, so it gets the answer the other + /// databases give. The transaction was rolled back as the exception left + /// it, so nothing of it remains; run again, it reads the delivery as the winner + /// left it and its update changes no rows. This is the one place that + /// happens, and it has to be outside the transaction: the database has + /// already ended that one, and a statement sent after the failure would run + /// on its own or be refused, depending on the database. + /// + /// Three attempts, because each one lost means someone else moved the delivery, + /// and a delivery has only so many moves in it. + private static T attempt(Work work) throws IOException { + for (int tries = 1; ; tries++) { + try { + return work.run(); + } catch (PersistenceException err) { + // The session reports what the database said as its own + // unchecked exception, with the database's as the cause. + if (tries == 3 || !ConcurrencyFailureException.isCauseOf(err)) { + throw err; + } + } catch (ConcurrencyFailureException err) { + if (tries == 3) { + throw err; + } + } + } + } + + /// Moves a delivery to `to`, or answers 409 when its state does not allow it. + private void move(String id, DeliveryState to, String actor, String ownerField, String newCourier, + Long newOfferExpiresAt) throws IOException { + if (!tryMove(id, to, actor, ownerField, newCourier, newOfferExpiresAt)) { + Delivery now = find(id); + throw new ResponseStatusException(409, "That delivery is " + + (now == null ? "gone" : describe(now.getState()))); + } + } + + /// The conditional update behind every change of state. True when this call + /// is the one that made the change. + /// + /// The session is empty afterwards, every move in `DeliveryRepository` being + /// marked `clearAutomatically`: a delivery read before this is no longer the + /// session's, and is read again by whoever needs it as it is now. + /// + /// @param ownerField [#CUSTOMER] or [#COURIER] to hold the change to deliveries + /// whose customer or courier is `actor`, or null for a change the server + /// makes itself + /// @param newCourier the courier to write with the change, or null to leave + /// it as it is + /// @param newOfferExpiresAt when the offer this change makes lapses, or null + // tag::morsel-delivery-trymove[] + private boolean tryMove(String id, DeliveryState to, String actor, String ownerField, + String newCourier, Long newOfferExpiresAt) throws IOException { + DeliveryState[] sources = DeliveryStateMachine.sources(to); + if (sources.length == 0) { + return false; + } + long now = System.currentTimeMillis(); + int moved; + if (newOfferExpiresAt != null) { + moved = deliveries.offer(id, sources, to, now, newCourier, newOfferExpiresAt.longValue()); + } else if (CUSTOMER.equals(ownerField)) { + moved = deliveries.moveAsCustomer(id, sources, to, now, actor); + } else if (!COURIER.equals(ownerField)) { + moved = deliveries.moveByServer(id, sources, to, now); + } else if (newCourier != null) { + moved = deliveries.giveBack(id, sources, to, now, actor, newCourier); + } else if (DeliveryState.ACCEPTED == to) { + // An offer that has lapsed cannot be taken, even in the moment + // before the sweep takes it back. + moved = deliveries.takeOffer(id, sources, to, now, actor); + } else { + moved = deliveries.moveAsCourier(id, sources, to, now, actor); + } + if (moved != 1) { + return false; + } + event(id, to, actor); + return true; + } + // end::morsel-delivery-trymove[] + + private void event(String id, DeliveryState state, String actor) throws IOException { + DeliveryEvent event = new DeliveryEvent(); + event.setId(Ids.next()); + event.setDeliveryId(id); + event.setState(state); + event.setActor(actor); + event.setCreatedAt(System.currentTimeMillis()); + events.insert(event); + } + + private void passed(String id, String courier) { + if (!passes.existsById(Identifier.of(id, courier))) { + DeliveryPassed passed = new DeliveryPassed(); + passed.setDeliveryId(id); + passed.setCourier(courier); + passes.insert(passed); + } + } + + /// The delivery, when `actor` is its customer or its courier, whichever `field` + /// names; 404 otherwise. + private Delivery owned(String id, String field, String actor) { + Delivery delivery = find(id); + if (delivery == null || !actor.equals(CUSTOMER.equals(field) ? delivery.getCustomer() + : delivery.getCourier())) { + throw new ResponseStatusException(404, "No such delivery"); + } + return delivery; + } + + /// The delivery of this id, or null when there is none. An id that cannot be + /// one is not looked for. + private Delivery find(String id) { + if (id == null || id.length() == 0 || id.length() > 32) { + return null; + } + return deliveries.findById(id); + } + + /// The delivery as `viewer` sees it, read in a transaction of its own. + @Transactional(readOnly = true) + private DeliveryDto read(String id, String viewer) throws IOException { + return view(id, viewer); + } + + private DeliveryDto view(String id, String viewer) throws IOException { + return toDto(find(id), viewer, new HashMap()); + } + + private List list(List rows, String viewer) throws IOException { + Map cache = new HashMap(); + List out = new ArrayList(); + for (int iter = 0; iter < rows.size(); iter++) { + out.add(toDto(rows.get(iter), viewer, cache)); + } + return out; + } + + static DeliveryDto none() { + return DeliveryDto.none(); + } + + private DeliveryDto toDto(Delivery delivery, String viewer, Map cache) + throws IOException { + return toDto(delivery, viewer, cache, true); + } + + /// @param party whether `viewer` is asking as the delivery's customer or courier. + /// An admin is shown a delivery as its courier sees it, and is not a party: + /// the two phone numbers are for the two people who have to find each + /// other, and only while they do. + private DeliveryDto toDto(Delivery delivery, String viewer, Map cache, boolean party) + throws IOException { + DeliveryState state = delivery.getState(); + // An account's name is the e-mail it signs in with. The other party + // to a delivery is shown a display name, and a phone number while the two + // are meeting; the address is for its owner and for an admin. + boolean asCustomer = !party || (viewer != null && viewer.equals(delivery.getCustomer())); + boolean asCourier = !party || (viewer != null && viewer.equals(delivery.getCourier())); + Profile customer = profile(delivery.getCustomer(), cache); + // What is known of the courier, filled in below for a delivery that has one + // this viewer may see. + String courierUsername = null; + String courierName = null; + String courierPhotoInitials = ""; + String vehicle = null; + String plate = null; + String courierPhone = null; + String customerPhone = null; + double courierRating = 0d; + double courierLat = 0d; + double courierLng = 0d; + double courierHeading = 0d; + int etaMinutes = 0; + int offerExpiresInSeconds = 0; + String courier = delivery.getCourier(); + boolean offered = DeliveryState.OFFERED == state; + // An offer is between the server and one courier. Until it is taken the + // customer has no courier, and is not shown who was asked. + if (courier.length() > 0 && (!offered || courier.equals(viewer))) { + if (asCourier) { + courierUsername = courier; + } + Profile who = profile(courier, cache); + if (who != null) { + courierName = who.getDisplayName(); + courierPhotoInitials = Text.initials(courierName); + vehicle = who.getVehicle(); + plate = who.getPlate(); + } + boolean meeting = DeliveryState.ACCEPTED == state + || DeliveryState.ARRIVED == state + || DeliveryState.IN_PROGRESS == state; + if (party && meeting && viewer != null) { + if (viewer.equals(delivery.getCustomer()) && who != null) { + courierPhone = who.getPhone(); + } else if (viewer.equals(courier) && customer != null) { + customerPhone = customer.getPhone(); + } + } + CourierState car = couriers.findById(courier); + if (car != null) { + courierRating = car.rating(); + } + // Where the car is belongs to the delivery while it brings the two + // together and carries one of them. A delivery that has ended stays in + // its customer's history for good, and the courier goes on reporting + // where they are for other people's deliveries: read from here, a past + // customer could follow them indefinitely. + if (car != null && meeting) { + courierLat = car.getLat(); + courierLng = car.getLng(); + courierHeading = car.getHeading(); + // How long until the car is where it is going next: the pickup + // until the customer is in it, the destination after. + if (DeliveryState.ACCEPTED == state) { + etaMinutes = etaMinutes(courierLat, courierLng, delivery.getPickupLat(), + delivery.getPickupLng()); + } else if (DeliveryState.IN_PROGRESS == state) { + etaMinutes = etaMinutes(courierLat, courierLng, delivery.getDropoffLat(), + delivery.getDropoffLng()); + } + } + } + if (offered) { + offerExpiresInSeconds = (int) Math.max(0L, + (delivery.getOfferExpiresAt() - System.currentTimeMillis()) / 1000L); + } + long fareCents = delivery.getFareCents(); + long serviceFee = delivery.getFeeCents(); + long tip = delivery.getTipCents(); + return new DeliveryDto( + delivery.getId(), + state, + asCustomer ? delivery.getCustomer() : null, + customer == null ? "" : customer.getDisplayName(), + courierUsername, + courierName, + vehicle, + plate, + courierRating, + delivery.getPickupLat(), + delivery.getPickupLng(), + delivery.getPickupAddress(), + delivery.getDropoffLat(), + delivery.getDropoffLng(), + delivery.getDropoffAddress(), + courierLat, + courierLng, + courierHeading, + fareCents, + delivery.getCurrency(), + delivery.getDistanceMeters(), + delivery.getDurationSeconds(), + delivery.getRequestedAt(), + delivery.getUpdatedAt(), + delivery.getStars(), + delivery.getPaymentStatus(), + offerExpiresInSeconds, + delivery.getProduct(), + delivery.getMethodLabel(), + Payments.CASH.equals(delivery.getMethodId()), + serviceFee, + tip, + fareCents + serviceFee + tip, + courierPhotoInitials, + etaMinutes, + delivery.isWomenOnly() ? WOMEN : COURIER_GENDERS[0], + delivery.isQuiet(), + delivery.isWheelchair(), + delivery.isPets(), + delivery.getNote(), + courierPhone, + customerPhone); + } + + /// Minutes for a car to drive from one point to another, by the same + /// estimate the fares are made with. Never less than one. + public static int etaMinutes(double fromLat, double fromLng, double toLat, double toLng) { + double seconds = Geo.meters(fromLat, fromLng, toLat, toLng) * Fares.ROAD_FACTOR + / Fares.METERS_PER_SECOND; + return Math.max(1, (int) Math.ceil(seconds / 60d)); + } + + private static String courierGender(DeliveryRequestDto request) { + return Text.oneOf(request.courierGender(), COURIER_GENDERS, COURIER_GENDERS[0], "the courier"); + } + + private Profile profile(String username, Map cache) { + if (cache.containsKey(username)) { + return cache.get(username); + } + Profile row = profiles.findById(username); + cache.put(username, row); + return row; + } + + private static void validate(DeliveryRequestDto request) { + if (request == null || !Geo.isPoint(request.pickupLat(), request.pickupLng()) + || !Geo.isPoint(request.dropoffLat(), request.dropoffLng())) { + throw new ResponseStatusException(400, "Choose where the delivery starts and ends"); + } + double meters = Geo.meters(request.pickupLat(), request.pickupLng(), request.dropoffLat(), + request.dropoffLng()); + if (meters < MIN_METERS) { + throw new ResponseStatusException(400, "That is too close to need a delivery"); + } + if (meters > MAX_METERS) { + throw new ResponseStatusException(400, "That is further than we go"); + } + } + + private static String address(String value) { + String text = value == null ? "" : value.trim(); + return text.length() > 255 ? text.substring(0, 255) : text; + } + + private static String describe(DeliveryState state) { + if (DeliveryState.COMPLETED == state) { + return "already completed"; + } + if (DeliveryState.IN_PROGRESS == state) { + return "already under way"; + } + if (DeliveryState.ACCEPTED == state || DeliveryState.ARRIVED == state) { + return "already taken"; + } + if (DeliveryState.REQUESTED == state || DeliveryState.OFFERED == state) { + return "no longer on offer to you"; + } + return "no longer available"; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DeliveryStateMachine.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DeliveryStateMachine.java new file mode 100644 index 00000000000..86de838afa1 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DeliveryStateMachine.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codenameone.examples.morsel.api.DeliveryState; + +/// Which state a delivery may move to from which. +/// +/// ``` +/// REQUESTED -> OFFERED -> ACCEPTED -> ARRIVED -> IN_PROGRESS -> COMPLETED +/// ^ | +/// +-----------+ a courier passes, or lets the offer lapse +/// ``` +/// +/// The customer can cancel until the trip starts, the courier once they have taken +/// it and until it starts, and a search that finds nobody ends in NO_COURIERS. +/// An admin can cancel a delivery at any point before it ends. +/// Nothing leaves a finished state. +/// +/// This table is the only place the rules are written. A move is made by one +/// `UPDATE ... WHERE state IN (the states it may come from)`, so the database +/// applies the rule and settles a race in the same statement: of two couriers +/// accepting at once, or a customer cancelling as a courier accepts, one statement +/// changes a row and the other changes none. +public final class DeliveryStateMachine { + private static final DeliveryState[] NONE = {}; + private static final DeliveryState[] FROM_REQUESTED = {DeliveryState.REQUESTED}; + private static final DeliveryState[] FROM_OFFERED = {DeliveryState.OFFERED}; + private static final DeliveryState[] FROM_ACCEPTED = {DeliveryState.ACCEPTED}; + private static final DeliveryState[] FROM_ARRIVED = {DeliveryState.ARRIVED}; + private static final DeliveryState[] FROM_IN_PROGRESS = {DeliveryState.IN_PROGRESS}; + private static final DeliveryState[] BEFORE_THE_TRIP = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED}; + private static final DeliveryState[] UNDER_WAY = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + private static final DeliveryState[] TAKEN_NOT_STARTED = {DeliveryState.ACCEPTED, DeliveryState.ARRIVED}; + + private DeliveryStateMachine() { + } + + /// The states a delivery may be in to move to `to`. Empty for a state nothing + /// moves to. + public static DeliveryState[] sources(DeliveryState to) { + if (DeliveryState.OFFERED == to) { + return FROM_REQUESTED; + } + if (DeliveryState.REQUESTED == to || DeliveryState.ACCEPTED == to) { + return FROM_OFFERED; + } + if (DeliveryState.ARRIVED == to) { + return FROM_ACCEPTED; + } + if (DeliveryState.IN_PROGRESS == to) { + return FROM_ARRIVED; + } + if (DeliveryState.COMPLETED == to) { + return FROM_IN_PROGRESS; + } + if (DeliveryState.CANCELLED_BY_CUSTOMER == to) { + return BEFORE_THE_TRIP; + } + if (DeliveryState.CANCELLED_BY_COURIER == to) { + return TAKEN_NOT_STARTED; + } + if (DeliveryState.CANCELLED_BY_ADMIN == to) { + return UNDER_WAY; + } + if (DeliveryState.NO_COURIERS == to) { + return FROM_REQUESTED; + } + return NONE; + } + + public static boolean allowed(DeliveryState from, DeliveryState to) { + DeliveryState[] sources = sources(to); + for (int iter = 0; iter < sources.length; iter++) { + if (sources[iter] == from) { + return true; + } + } + return false; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DemoDeliveries.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DemoDeliveries.java new file mode 100644 index 00000000000..d30285c481e --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/DemoDeliveries.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.PricingDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierApplication; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.domain.Payment; +import com.codenameone.examples.morsel.domain.Payout; +import com.codenameone.examples.morsel.domain.PayoutAccount; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.domain.DeliveryEvent; +import com.codenameone.examples.morsel.domain.DeliveryPassed; +import com.codenameone.examples.morsel.geo.Geo; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.repository.CourierApplicationRepository; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.PaymentRepository; +import com.codenameone.examples.morsel.repository.PayoutAccountRepository; +import com.codenameone.examples.morsel.repository.PayoutRepository; +import com.codenameone.examples.morsel.repository.DeliveryEventRepository; +import com.codenameone.examples.morsel.repository.DeliveryPassedRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; + +import java.io.IOException; + +/// Two weeks of deliveries that were never taken, so that the admin's charts, a +/// courier's earnings and a customer's receipts have something in them the first +/// time a development server is opened. +/// +/// Stored whole, as finished deliveries with their events and payments, in one +/// transaction: there is no way to take a delivery last Tuesday through the API. The +/// numbers come from a fixed sequence and not from a random source, so every +/// server starts with the same fortnight, moved to end today. +@Component +public class DemoDeliveries { + private static final int DAYS = 14; + private static final Object[][] PLACES = { + {"Ferry Building", 37.7955, -122.3937}, + {"Pier 39", 37.8087, -122.4098}, + {"Coit Tower", 37.8024, -122.4058}, + {"Ghirardelli Square", 37.8059, -122.4229}, + {"Lombard Street", 37.8021, -122.4187}, + {"Palace of Fine Arts", 37.8029, -122.4484}, + {"Union Square", 37.7880, -122.4075}, + {"Oracle Park", 37.7786, -122.3893}, + {"Fort Mason", 37.8066, -122.4310}, + {"Transamerica Pyramid", 37.7952, -122.4028}}; + /// The hours deliveries are asked for at, the busy ones more than once. + private static final int[] HOURS = {7, 8, 8, 9, 12, 13, 15, 17, 18, 18, 19, 20, 22, 23}; + + private final DeliveryRepository deliveries; + private final DeliveryEventRepository events; + private final DeliveryPassedRepository passes; + private final PaymentRepository paid; + private final CourierStateRepository cars; + private final CourierApplicationRepository applications; + private final PayoutAccountRepository payoutAccounts; + private final PayoutRepository payouts; + private final Fares fares; + private final Payments payments; + private long sequence = 20240229L; + + public DemoDeliveries(DeliveryRepository deliveries, DeliveryEventRepository events, + DeliveryPassedRepository passes, PaymentRepository paid, CourierStateRepository cars, + CourierApplicationRepository applications, PayoutAccountRepository payoutAccounts, + PayoutRepository payouts, Fares fares, Payments payments) { + this.deliveries = deliveries; + this.events = events; + this.passes = passes; + this.paid = paid; + this.cars = cars; + this.applications = applications; + this.payoutAccounts = payoutAccounts; + this.payouts = payouts; + this.fares = fares; + this.payments = payments; + } + + /// Writes the fortnight, shared out among these customers and couriers. The + /// couriers are approved ones, and each delivery is of its courier's kind of car. + /// + /// @param untilToday false ends the fortnight yesterday. A test that + /// compares a chart with a picture of it asks for that: deliveries of today + /// are moved back to before now, so today's bars would depend on the + /// hour the server was started at. + @Transactional + public void seed(String[] customers, String[] couriers, boolean untilToday) throws IOException { + long now = System.currentTimeMillis(); + long today = Days.start(now) - (untilToday ? 0L : Days.MILLIS); + PricingDto pricing = fares.pricing(); + long[] stars = new long[couriers.length]; + long[] rated = new long[couriers.length]; + long[] earnedEarlier = new long[couriers.length]; + long[] lastSeen = new long[couriers.length]; + String[] products = new String[couriers.length]; + for (int iter = 0; iter < couriers.length; iter++) { + CourierApplication made = applications.findById(couriers[iter]); + products[iter] = made == null ? Fares.STANDARD : made.getProduct(); + } + int count = 0; + for (int day = DAYS - 1; day >= 0; day--) { + int asked = 3 + next(4); + for (int number = 0; number < asked; number++, count++) { + int customerIndex = next(customers.length); + int courierIndex = next(couriers.length); + String customer = customers[customerIndex]; + String courier = couriers[courierIndex]; + int from = next(PLACES.length); + int to = (from + 1 + next(PLACES.length - 1)) % PLACES.length; + double meters = Geo.meters(real(from, 1), real(from, 2), real(to, 1), real(to, 2)) + * Fares.ROAD_FACTOR; + double seconds = meters / Fares.METERS_PER_SECOND; + Fares.Price price = Fares.price(pricing, meters, seconds, products[courierIndex]); + long wait = (20 + next(100)) * 1000L; + long pickup = (120 + next(240)) * 1000L; + long whole = wait + pickup + (long) (seconds * 1000d); + long at = today - day * Days.MILLIS + HOURS[next(HOURS.length)] * 3600000L + + next(3600) * 1000L; + // Nothing here happens in the future: a delivery that would end + // after now is moved back until it has ended. + at = Math.min(at, now - whole - 60000L); + + int roll = next(20); + DeliveryState state = roll < 15 ? DeliveryState.COMPLETED + : roll == 17 ? DeliveryState.CANCELLED_BY_COURIER + : roll == 18 ? DeliveryState.NO_COURIERS : DeliveryState.CANCELLED_BY_CUSTOMER; + boolean completed = DeliveryState.COMPLETED == state; + // A customer who cancels does it before a courier is found half the + // time, and after one has taken the delivery the other half. + boolean taken = completed || DeliveryState.CANCELLED_BY_COURIER == state + || (DeliveryState.CANCELLED_BY_CUSTOMER == state && next(2) == 0); + String[] method = next(3) == 0 ? new String[] {Payments.CASH, Payments.CASH_LABEL} + : payments.choose(customer, null); + boolean cash = Payments.CASH.equals(method[0]); + long tip = completed && !cash && next(3) == 0 ? price.fare * 15L / 100L : 0L; + int given = 0; + if (completed && next(5) != 0) { + int liked = next(10); + given = liked == 0 ? 3 : liked < 4 ? 4 : 5; + stars[courierIndex] += given; + rated[courierIndex]++; + } + long ended = completed ? at + whole : taken ? at + wait + pickup / 2L + : DeliveryState.NO_COURIERS == state ? at + 90000L : at + 30000L; + String id = Ids.next(); + Delivery delivery = new Delivery(); + delivery.setId(id); + delivery.setState(state); + delivery.setCustomer(customer); + delivery.setCourier(taken ? courier : ""); + delivery.setPickupLat(real(from, 1)); + delivery.setPickupLng(real(from, 2)); + delivery.setPickupAddress(String.valueOf(PLACES[from][0])); + delivery.setDropoffLat(real(to, 1)); + delivery.setDropoffLng(real(to, 2)); + delivery.setDropoffAddress(String.valueOf(PLACES[to][0])); + delivery.setFareCents(price.fare); + delivery.setCurrency(fares.currency()); + delivery.setDistanceMeters(meters); + delivery.setDurationSeconds(seconds); + delivery.setRequestedAt(at); + delivery.setUpdatedAt(ended); + delivery.setSearchUntil(at + 90000L); + delivery.setStars(given); + delivery.setPaymentStatus(completed ? Payments.PAID : Payments.PENDING); + delivery.setProduct(products[courierIndex]); + delivery.setMethodId(method[0]); + delivery.setMethodLabel(method[1]); + delivery.setBaseCents(price.base); + delivery.setDistanceCents(price.distance); + delivery.setTimeCents(price.time); + delivery.setFeeCents(price.fee); + delivery.setTipCents(tip); + delivery.setCourierCents(price.fare - Fares.commission(pricing, price.fare)); + deliveries.insert(delivery); + event(id, DeliveryState.REQUESTED, customer, at); + if (taken) { + event(id, DeliveryState.OFFERED, "", at + 1000L); + event(id, DeliveryState.ACCEPTED, courier, at + wait); + if (count % 7 == 3 && couriers.length > 1) { + // Now and then the first courier asked let it go by. + DeliveryPassed passed = new DeliveryPassed(); + passed.setDeliveryId(id); + passed.setCourier(couriers[(courierIndex + 1) % couriers.length]); + passes.insert(passed); + } + } + if (completed) { + event(id, DeliveryState.ARRIVED, courier, at + wait + pickup); + event(id, DeliveryState.IN_PROGRESS, courier, at + wait + pickup + 60000L); + event(id, state, courier, ended); + Payment payment = new Payment(); + payment.setDeliveryId(id); + payment.setAmountCents(price.fare + price.fee); + payment.setCurrency(fares.currency()); + payment.setStatus(Payments.PAID); + payment.setProviderRef(cash ? Payments.CASH : "sim_ch_delivery-" + id); + if (tip > 0L) { + // A tip counts as paid -- in the courier's earnings, + // and for a refund -- once its charge has a reference. + payment.setTipRef("sim_ch_tip-" + id); + } + payment.setCreatedAt(ended); + paid.insert(payment); + lastSeen[courierIndex] = Math.max(lastSeen[courierIndex], ended); + if (ended < now - 7L * Days.MILLIS) { + earnedEarlier[courierIndex] += price.fare + - Fares.commission(pricing, price.fare) + tip; + } + } else { + event(id, state, DeliveryState.CANCELLED_BY_COURIER == state ? courier + : DeliveryState.NO_COURIERS == state ? "" : customer, ended); + } + } + } + for (int iter = 0; iter < couriers.length; iter++) { + // Off line, parked where the last delivery might have ended, with the + // rating those deliveries gave. + CourierState car = cars.findById(couriers[iter]); + boolean first = car == null; + if (first) { + car = new CourierState(); + car.setUsername(couriers[iter]); + } + car.setLat(real(iter % PLACES.length, 1)); + car.setLng(real(iter % PLACES.length, 2)); + car.setLastSeen(lastSeen[iter]); + car.setRatingSum(stars[iter]); + car.setRatingCount(rated[iter]); + if (first) { + cars.insert(car); + } + if (earnedEarlier[iter] > 0L) { + // What the first week earned was cashed out at the end of it. + PayoutAccount account = new PayoutAccount(); + account.setUsername(couriers[iter]); + account.setHolder(couriers[iter]); + account.setBankName("Bay Credit Union"); + account.setLast4("67" + (10 + iter)); + payoutAccounts.insert(account); + Payout payout = new Payout(); + payout.setId(Ids.next()); + payout.setCourier(couriers[iter]); + payout.setAmountCents(earnedEarlier[iter]); + payout.setStatus("paid"); + payout.setCreatedAt(now - 7L * Days.MILLIS); + payout.setDestination("Bay Credit Union ****67" + (10 + iter)); + payouts.insert(payout); + } + } + } + + private void event(String delivery, DeliveryState state, String actor, long at) throws IOException { + DeliveryEvent event = new DeliveryEvent(); + event.setId(Ids.next()); + event.setDeliveryId(delivery); + event.setState(state); + event.setActor(actor); + event.setCreatedAt(at); + events.insert(event); + } + + private static double real(int place, int column) { + return ((Double) PLACES[place][column]).doubleValue(); + } + + /// The next number of the sequence, from 0 to `below` less one: a linear + /// congruential generator, which is all a made-up fortnight needs and is + /// the same on every runtime. + private int next(int below) { + sequence = (sequence * 1103515245L + 12345L) & 0x7fffffffL; + return (int) ((sequence >> 8) % below); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Fares.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Fares.java new file mode 100644 index 00000000000..c895a57e09a --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Fares.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.annotations.Value; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.api.PricingDto; +import com.codenameone.examples.morsel.api.DeliveryRequestDto; +import com.codenameone.examples.morsel.domain.Pricing; +import com.codenameone.examples.morsel.geo.Geo; +import com.codenameone.examples.morsel.repository.PricingRepository; + +import java.io.IOException; + +/// What a delivery costs. +/// +/// A base fare, plus so much a kilometre and so much a minute, with a floor; +/// the larger products multiply all of it, and so does a surge. On top of the +/// fare the customer pays a service fee, and out of it the service keeps a +/// commission; the rest is the courier's. +/// +/// Money is whole cents in a `long` from end to end: a fare is never a floating +/// point number, here or in the app. +/// +/// The prices start as settings (`morsel.fare.*`) and are the admin's to +/// change, which stores them in the database. They are read from there for +/// every quote and not kept in memory, so a change made through one instance +/// of the server is the price on all of them. +@Component +public class Fares { + public static final String STANDARD = "standard"; + public static final String COMFORT = "comfort"; + public static final String XL = "xl"; + /// Cheapest first, which is the order a quote lists them in. + public static final String[] PRODUCTS = {STANDARD, COMFORT, XL}; + + /// How much longer than the straight line a road route is taken to be when + /// the app did not send one. + static final double ROAD_FACTOR = 1.3d; + /// City onboarding, for a duration when the app did not send one: 30 km/h. + static final double METERS_PER_SECOND = 8.33d; + + private final PricingRepository prices; + private final PricingDto defaults; + + public Fares(PricingRepository prices, + @Value("${morsel.currency:USD}") String currency, + @Value("${morsel.fare.baseCents:250}") int baseCents, + @Value("${morsel.fare.perKmCents:120}") int perKmCents, + @Value("${morsel.fare.perMinuteCents:30}") int perMinuteCents, + @Value("${morsel.fare.minimumCents:500}") int minimumCents, + @Value("${morsel.fare.serviceFeePercent:10}") int serviceFeePercent, + @Value("${morsel.fare.commissionPercent:20}") int commissionPercent) { + this.prices = prices; + defaults = new PricingDto(currency, baseCents, perKmCents, perMinuteCents, minimumCents, + serviceFeePercent, commissionPercent, 1d, 1.35d, 1.7d); + } + + public String currency() { + return defaults.currency(); + } + + /// The prices in force: the admin's when they have set any, the settings' + /// otherwise. + @Transactional(readOnly = true) + public PricingDto pricing() throws IOException { + return toDto(prices.findById(PricingRepository.ONLY)); + } + + private PricingDto toDto(Pricing row) { + // The row a migration created and nobody has saved over yet. + if (row == null || row.getUpdatedAt() == 0L) { + return defaults; + } + // The currency is a setting and stays one. Deliveries already taken are in + // it, and changing it would not convert them. + return new PricingDto(defaults.currency(), row.getBaseCents(), row.getPerKmCents(), + row.getPerMinuteCents(), row.getMinimumCents(), row.getFeePercent(), + row.getCommissionPercent(), row.getSurge(), row.getComfort(), row.getXl()); + } + + /// Changes the prices, for the deliveries asked for from now on. A delivery already + /// asked for keeps the price it was quoted. Answers 400 for prices that + /// could only be a mistake. + @Transactional + public PricingDto save(String by, PricingDto wanted) throws IOException { + if (wanted == null) { + throw new ResponseStatusException(400, "No prices to save"); + } + amount(wanted.baseFare(), "the base fare"); + amount(wanted.perKm(), "the price per kilometre"); + amount(wanted.perMinute(), "the price per minute"); + amount(wanted.minimumFare(), "the minimum fare"); + between(wanted.serviceFeePercent(), 0d, 50d, "the service fee"); + between(wanted.commissionPercent(), 0d, 90d, "the commission"); + between(wanted.surgeMultiplier(), 1d, 5d, "the surge multiplier"); + between(wanted.comfortMultiplier(), 1d, 5d, "the comfort multiplier"); + between(wanted.xlMultiplier(), 1d, 5d, "the XL multiplier"); + // The row is always there (V6 creates it), so this only ever updates + // it. Inserting it on the first save was a race: two admins saving + // before anyone had both inserted id 1, and one of them failed. + if (prices.change(PricingRepository.ONLY.intValue(), wanted.baseFare(), wanted.perKm(), + wanted.perMinute(), wanted.minimumFare(), wanted.serviceFeePercent(), + wanted.commissionPercent(), wanted.surgeMultiplier(), + wanted.comfortMultiplier(), wanted.xlMultiplier(), System.currentTimeMillis(), + by) == 0) { + throw new IllegalStateException("The pricing row is missing; the V6 migration " + + "creates it"); + } + return new PricingDto(defaults.currency(), wanted.baseFare(), wanted.perKm(), + wanted.perMinute(), wanted.minimumFare(), wanted.serviceFeePercent(), + wanted.commissionPercent(), wanted.surgeMultiplier(), wanted.comfortMultiplier(), + wanted.xlMultiplier()); + } + + /// The length of the delivery to price, in metres. + /// + /// The app sends the length of the route it drew, and that is the better + /// number -- but it is also a number the caller chose, and the fare follows + /// from it. So it is believed only between the straight line and four times + /// it; outside that the server prices from its own estimate. + public double distanceMeters(DeliveryRequestDto request) { + double straight = Geo.meters(request.pickupLat(), request.pickupLng(), request.dropoffLat(), + request.dropoffLng()); + if (request.distanceMeters() >= straight && request.distanceMeters() <= straight * 4) { + return request.distanceMeters(); + } + return straight * ROAD_FACTOR; + } + + /// The duration to price, in seconds; believed on the same terms. + public double durationSeconds(DeliveryRequestDto request, double distanceMeters) { + double estimate = distanceMeters / METERS_PER_SECOND; + if (distanceMeters == request.distanceMeters() && request.durationSeconds() >= estimate / 4 + && request.durationSeconds() <= estimate * 4) { + return request.durationSeconds(); + } + return estimate; + } + + /// A delivery of this length and duration, taken as `product`, at `pricing`. + public static Price price(PricingDto pricing, double distanceMeters, double durationSeconds, + String product) { + double times = pricing.surgeMultiplier(); + if (COMFORT.equals(product)) { + times *= pricing.comfortMultiplier(); + } else if (XL.equals(product)) { + times *= pricing.xlMultiplier(); + } + Price price = new Price(); + price.base = Math.round(pricing.baseFare() * times); + price.distance = Math.round(distanceMeters / 1000d * pricing.perKm() * times); + price.time = Math.round(durationSeconds / 60d * pricing.perMinute() * times); + price.fare = price.base + price.distance + price.time; + long minimum = Math.round(pricing.minimumFare() * times); + if (price.fare < minimum) { + // The floor is a larger base fare, so the three parts still add up + // to what is charged. + price.base += minimum - price.fare; + price.fare = minimum; + } + price.fee = Math.round(price.fare * pricing.serviceFeePercent() / 100d); + return price; + } + + /// What the service keeps of a fare. + public static long commission(PricingDto pricing, long fareCents) { + return Math.round(fareCents * pricing.commissionPercent() / 100d); + } + + /// The product a request names; standard when it names none. Answers 400 + /// for one that does not exist. + public static String product(String value) { + return Text.oneOf(value, PRODUCTS, STANDARD, "the kind of delivery"); + } + + /// The parts of a price, in cents. `fare` is the first three added up, and + /// `fee` is paid on top of it. + public static final class Price { + public long base; + public long distance; + public long time; + public long fare; + public long fee; + } + + private static void amount(long cents, String what) { + if (cents < 0L || cents > 1000000L) { + throw new ResponseStatusException(400, "That is not an amount for " + what); + } + } + + private static void between(double value, double low, double high, String what) { + // Written so that NaN, which compares false to everything, is refused. + if (!(value >= low && value <= high)) { + throw new ResponseStatusException(400, "That is not a value for " + what); + } + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Matcher.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Matcher.java new file mode 100644 index 00000000000..de98efbd418 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/delivery/Matcher.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.security.core.userdetails.JdbcUserDetailsManager; +import com.codename1.backend.security.core.userdetails.UsernameNotFoundException; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierApplication; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.domain.DeliveryPassed; +import com.codenameone.examples.morsel.onboarding.Applications; +import com.codenameone.examples.morsel.geo.Geo; +import com.codenameone.examples.morsel.repository.CourierApplicationRepository; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryPassedRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// Finds the couriers near a point. +/// +/// No spatial index and no spatial database: the query asks for the couriers on +/// line inside a box of latitude and longitude around the point, which an +/// ordinary index answers, and the handful it returns are measured and sorted +/// here. That runs unchanged on SQLite, PostgreSQL and MySQL, and it is enough +/// until one city has more couriers on line than fit in a query result -- at +/// which point the box becomes a geohash column and nothing else changes. +/// +/// The box does not wrap around the 180th meridian. A service operating across +/// it would need a second box. +@Component +public class Matcher { + /// A courier not heard from for this long is not there, whatever their + /// status says: the app reports its position every few seconds while on + /// line, and a phone that lost its signal cannot go off line. + public static final long FRESH_MILLIS = 60000L; + /// How far to look at first, and how far once the first look found nobody. + static final double NEAR_METERS = 3000d; + static final double FAR_METERS = 8000d; + /// How long a delivery waits before the search widens. + static final long WIDEN_AFTER_MILLIS = 20000L; + + /// The states of a delivery that keep its courier from being offered another. + private static final DeliveryState[] WITH_DELIVERY = {DeliveryState.OFFERED, DeliveryState.ACCEPTED, + DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + private static final String FEMALE = "female"; + + private final CourierStateRepository couriers; + private final CourierApplicationRepository applications; + private final ProfileRepository profiles; + private final DeliveryPassedRepository passes; + private final JdbcUserDetailsManager users; + + public Matcher(CourierStateRepository couriers, CourierApplicationRepository applications, + ProfileRepository profiles, DeliveryPassedRepository passes, + JdbcUserDetailsManager users) { + this.couriers = couriers; + this.applications = applications; + this.profiles = profiles; + this.passes = passes; + this.users = users; + } + + /// A courier who could be offered a delivery: where the car is, what kind it + /// is, and how far it is from the point that was asked about. + public static final class Candidate { + public final String username; + public final double lat; + public final double lng; + public final double heading; + /// The kind of car, as the courier's application has it. + public final String product; + public final double meters; + /// The courier's state as it was read, for whoever wants more of it. + public final CourierState state; + + Candidate(CourierState state, String product, double meters) { + this.state = state; + this.username = state.getUsername(); + this.lat = state.getLat(); + this.lng = state.getLng(); + this.heading = state.getHeading(); + this.product = product; + this.meters = meters; + } + } + + /// The radius to search for a delivery that has been waiting `waitedMillis`. + static double radius(long waitedMillis) { + return waitedMillis < WIDEN_AFTER_MILLIS ? NEAR_METERS : FAR_METERS; + } + + /// The couriers on line and free within `radiusMeters` of a point, nearest + /// first, of any kind. + @Transactional(readOnly = true) + public List nearby(double lat, double lng, double radiusMeters) + throws IOException { + return nearby(lat, lng, radiusMeters, null, false, false, false); + } + + /// The couriers a delivery could be offered to: on line, free, in reach, and + /// fit for it. + /// + /// A courier is offered deliveries only while their application stands approved + /// and their account is not blocked. Both are read here, each time, and + /// not trusted to have been put right elsewhere: an approval withdrawn or + /// an account blocked takes a courier out of matching at once, whatever + /// their state still says. The approval is part of the query. What the + /// customer asked of the car and the courier is read from the applications + /// and profiles of the handful the query answers. Whether the account is + /// blocked is the sign-in's to say, and its tables are its own, so each + /// courier still fit for the delivery is asked about there. + /// + /// @param product the kind of car the delivery asks for, or null for any + /// @param womenOnly whether the customer asked for a woman courier + /// @param wheelchair whether the delivery needs a car that takes a wheelchair + /// @param pets whether the delivery needs a courier who takes pets + @Transactional(readOnly = true) + public List nearby(double lat, double lng, double radiusMeters, String product, + boolean womenOnly, boolean wheelchair, boolean pets) throws IOException { + double dLat = radiusMeters / Geo.METERS_PER_DEGREE; + double shrink = Math.max(0.01d, Math.cos(Math.toRadians(lat))); + double dLng = radiusMeters / (Geo.METERS_PER_DEGREE * shrink); + List boxed = couriers.freeInBox(System.currentTimeMillis() - FRESH_MILLIS, + lat - dLat, lat + dLat, lng - dLng, lng + dLng, Applications.APPROVED, WITH_DELIVERY); + List inReach = new ArrayList(); + List names = new ArrayList(); + for (int iter = 0; iter < boxed.size(); iter++) { + CourierState state = boxed.get(iter); + if (Geo.meters(lat, lng, state.getLat(), state.getLng()) > radiusMeters) { + // Inside the box but outside the circle: a corner of it. + continue; + } + inReach.add(state); + names.add(state.getUsername()); + } + if (inReach.isEmpty()) { + return new ArrayList(); + } + Map cars = new HashMap(); + List rows = applications.findByUsernameIn(names); + for (int iter = 0; iter < rows.size(); iter++) { + cars.put(rows.get(iter).getUsername(), rows.get(iter)); + } + Map people = new HashMap(); + if (womenOnly) { + List found = profiles.findByUsernameIn(names); + for (int iter = 0; iter < found.size(); iter++) { + people.put(found.get(iter).getUsername(), found.get(iter)); + } + } + List near = new ArrayList(); + for (int iter = 0; iter < inReach.size(); iter++) { + CourierState state = inReach.get(iter); + CourierApplication car = cars.get(state.getUsername()); + if (car == null) { + // The application went between the two queries, and the + // approval with it. + continue; + } + if (product != null && !product.equals(car.getProduct()) + || wheelchair && !car.isWheelchair() || pets && !car.isPets()) { + continue; + } + if (womenOnly) { + Profile who = people.get(state.getUsername()); + if (who == null || !FEMALE.equals(who.getGender())) { + continue; + } + } + if (!enabled(state.getUsername())) { + continue; + } + double meters = Geo.meters(lat, lng, state.getLat(), state.getLng()); + int at = 0; + while (at < near.size() && near.get(at).meters <= meters) { + at++; + } + near.add(at, new Candidate(state, car.getProduct(), meters)); + } + return near; + } + + /// Whether `courier` is still one a delivery could be offered to: on line and + /// heard from lately, free, in reach, approved, not blocked, and fit for + /// what the customer asked. [#pick] read all of that in a transaction of its + /// own, and any of it can have changed before the offer is made; the + /// offer asks again once it holds the courier. + @Transactional(readOnly = true) + public boolean fits(Delivery delivery, String courier, long waitedMillis) throws IOException { + List near = nearby(delivery.getPickupLat(), delivery.getPickupLng(), + radius(waitedMillis), delivery.getProduct(), delivery.isWomenOnly(), + delivery.isWheelchair(), delivery.isPets()); + for (int iter = 0; iter < near.size(); iter++) { + if (courier.equals(near.get(iter).username)) { + return true; + } + } + return false; + } + + /// Whether the account of this name exists and has not been blocked. + /// + /// Read once: an account closed between asking whether it exists and + /// loading it would otherwise throw, and turn a customer's request into a + /// server error over a courier who is simply no longer there. + boolean enabled(String username) { + try { + return users.loadUserByUsername(username).isEnabled(); + } catch (UsernameNotFoundException gone) { + return false; + } + } + + /// The nearest free courier fit for a delivery who has not already passed on it, + /// or null when there is none in reach. + @Transactional(readOnly = true) + public String pick(Delivery delivery, long waitedMillis) throws IOException { + List near = nearby(delivery.getPickupLat(), delivery.getPickupLng(), radius(waitedMillis), + delivery.getProduct(), delivery.isWomenOnly(), delivery.isWheelchair(), delivery.isPets()); + if (near.isEmpty()) { + return null; + } + List rows = passes.findByDeliveryId(delivery.getId()); + List passed = new ArrayList(); + for (int iter = 0; iter < rows.size(); iter++) { + passed.add(rows.get(iter).getCourier()); + } + for (int iter = 0; iter < near.size(); iter++) { + String courier = near.get(iter).username; + if (!passed.contains(courier)) { + return courier; + } + } + return null; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ClosedAccount.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ClosedAccount.java new file mode 100644 index 00000000000..75ee11f903c --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ClosedAccount.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// What is left of an account that was closed: the hash of its e-mail, kept so +/// that the address cannot be registered again. +@Entity +public class ClosedAccount { + @Id(autoIncrement = false) + @Column(nullable = false) + private String usernameHash = ""; + + @Column(nullable = false) + private long closedAt; + + public ClosedAccount() { + } + + public String getUsernameHash() { + return usernameHash; + } + + public void setUsernameHash(String usernameHash) { + this.usernameHash = usernameHash; + } + + public long getClosedAt() { + return closedAt; + } + + public void setClosedAt(long closedAt) { + this.closedAt = closedAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierApplication.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierApplication.java new file mode 100644 index 00000000000..dc294f0f49a --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierApplication.java @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// An account's application to drive, and what became of it. +@Entity +public class CourierApplication { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String status = ""; + + @Column(nullable = false) + private String legalName = ""; + + @Column(nullable = false) + private String dateOfBirth = ""; + + @Column(nullable = false) + private String licenceNumber = ""; + + @Column(nullable = false) + private String licenceExpiry = ""; + + @Column(nullable = false) + private String vehicleMake = ""; + + @Column(nullable = false) + private String vehicleModel = ""; + + @Column(nullable = false) + private int vehicleYear; + + @Column(nullable = false) + private String vehicleColor = ""; + + @Column(nullable = false) + private String vehiclePlate = ""; + + @Column(nullable = false) + private int vehicleSeats; + + @Column(nullable = false) + private String product = ""; + + @Column(nullable = false) + private boolean wheelchair; + + @Column(nullable = false) + private boolean pets; + + @Column(nullable = false) + private String rejectionReason = ""; + + @Column(nullable = false) + private long createdAt; + + @Column(nullable = false) + private long submittedAt; + + @Column(nullable = false) + private long reviewedAt; + + @Column(nullable = false) + private String reviewedBy = ""; + + public CourierApplication() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getLegalName() { + return legalName; + } + + public void setLegalName(String legalName) { + this.legalName = legalName; + } + + public String getDateOfBirth() { + return dateOfBirth; + } + + public void setDateOfBirth(String dateOfBirth) { + this.dateOfBirth = dateOfBirth; + } + + public String getLicenceNumber() { + return licenceNumber; + } + + public void setLicenceNumber(String licenceNumber) { + this.licenceNumber = licenceNumber; + } + + public String getLicenceExpiry() { + return licenceExpiry; + } + + public void setLicenceExpiry(String licenceExpiry) { + this.licenceExpiry = licenceExpiry; + } + + public String getVehicleMake() { + return vehicleMake; + } + + public void setVehicleMake(String vehicleMake) { + this.vehicleMake = vehicleMake; + } + + public String getVehicleModel() { + return vehicleModel; + } + + public void setVehicleModel(String vehicleModel) { + this.vehicleModel = vehicleModel; + } + + public int getVehicleYear() { + return vehicleYear; + } + + public void setVehicleYear(int vehicleYear) { + this.vehicleYear = vehicleYear; + } + + public String getVehicleColor() { + return vehicleColor; + } + + public void setVehicleColor(String vehicleColor) { + this.vehicleColor = vehicleColor; + } + + public String getVehiclePlate() { + return vehiclePlate; + } + + public void setVehiclePlate(String vehiclePlate) { + this.vehiclePlate = vehiclePlate; + } + + public int getVehicleSeats() { + return vehicleSeats; + } + + public void setVehicleSeats(int vehicleSeats) { + this.vehicleSeats = vehicleSeats; + } + + public String getProduct() { + return product; + } + + public void setProduct(String product) { + this.product = product; + } + + public boolean isWheelchair() { + return wheelchair; + } + + public void setWheelchair(boolean wheelchair) { + this.wheelchair = wheelchair; + } + + public boolean isPets() { + return pets; + } + + public void setPets(boolean pets) { + this.pets = pets; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public long getSubmittedAt() { + return submittedAt; + } + + public void setSubmittedAt(long submittedAt) { + this.submittedAt = submittedAt; + } + + public long getReviewedAt() { + return reviewedAt; + } + + public void setReviewedAt(long reviewedAt) { + this.reviewedAt = reviewedAt; + } + + public String getReviewedBy() { + return reviewedBy; + } + + public void setReviewedBy(String reviewedBy) { + this.reviewedBy = reviewedBy; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierDocument.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierDocument.java new file mode 100644 index 00000000000..d98ae9253ab --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierDocument.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// A document an applicant uploaded, one of each kind. `data` is the file in +/// Base64, which is text on every database. +@Entity +public class CourierDocument { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Id(autoIncrement = false) + @Column(nullable = false) + private String kind = ""; + + @Column(nullable = false) + private String contentType = ""; + + @Column(nullable = false) + private String status = ""; + + @Column(nullable = false) + private long uploadedAt; + + @Column(nullable = false) + private String data = ""; + + public CourierDocument() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getKind() { + return kind; + } + + public void setKind(String kind) { + this.kind = kind; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public long getUploadedAt() { + return uploadedAt; + } + + public void setUploadedAt(long uploadedAt) { + this.uploadedAt = uploadedAt; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierHours.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierHours.java new file mode 100644 index 00000000000..aba7d4dca2e --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierHours.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// How long a courier was on line on one day, counted in UTC days. +@Entity +public class CourierHours { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Id(autoIncrement = false) + @Column(nullable = false) + private long dayNumber; + + @Column(nullable = false) + private long millis; + + public CourierHours() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public long getDayNumber() { + return dayNumber; + } + + public void setDayNumber(long dayNumber) { + this.dayNumber = dayNumber; + } + + public long getMillis() { + return millis; + } + + public void setMillis(long millis) { + this.millis = millis; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierState.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierState.java new file mode 100644 index 00000000000..856267427f1 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/CourierState.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// Where a courier's car is, whether they are on line, and what customers made of them. +@Entity +public class CourierState { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private boolean online; + + @Column(nullable = false) + private double lat; + + @Column(nullable = false) + private double lng; + + @Column(nullable = false) + private double heading; + + @Column(nullable = false) + private long lastSeen; + + @Column(nullable = false) + private long ratingSum; + + @Column(nullable = false) + private long ratingCount; + + public CourierState() { + } + + /// The mean of the stars given, or 5 before any were. + public double rating() { + return ratingCount == 0 ? 5d : Math.round(ratingSum * 10d / ratingCount) / 10d; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public boolean isOnline() { + return online; + } + + public void setOnline(boolean online) { + this.online = online; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getLng() { + return lng; + } + + public void setLng(double lng) { + this.lng = lng; + } + + public double getHeading() { + return heading; + } + + public void setHeading(double heading) { + this.heading = heading; + } + + public long getLastSeen() { + return lastSeen; + } + + public void setLastSeen(long lastSeen) { + this.lastSeen = lastSeen; + } + + public long getRatingSum() { + return ratingSum; + } + + public void setRatingSum(long ratingSum) { + this.ratingSum = ratingSum; + } + + public long getRatingCount() { + return ratingCount; + } + + public void setRatingCount(long ratingCount) { + this.ratingCount = ratingCount; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Delivery.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Delivery.java new file mode 100644 index 00000000000..928bbc2b54d --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Delivery.java @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; +import com.codenameone.examples.morsel.api.DeliveryState; + +// tag::morsel-entity[] +/// A delivery, from the request to its end. +/// +/// `customer` and `courier` are account names and not references: a delivery outlives the +/// accounts that took it, and `courier` is empty until one is offered it. +@Entity +public class Delivery { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private DeliveryState state = DeliveryState.REQUESTED; + + @Column(nullable = false) + private String customer = ""; + + @Column(nullable = false) + private String courier = ""; + // end::morsel-entity[] + + @Column(nullable = false) + private double pickupLat; + + @Column(nullable = false) + private double pickupLng; + + @Column(nullable = false) + private String pickupAddress = ""; + + @Column(nullable = false) + private double dropoffLat; + + @Column(nullable = false) + private double dropoffLng; + + @Column(nullable = false) + private String dropoffAddress = ""; + + @Column(nullable = false) + private long fareCents; + + @Column(nullable = false) + private String currency = ""; + + @Column(nullable = false) + private double distanceMeters; + + @Column(nullable = false) + private double durationSeconds; + + @Column(nullable = false) + private long requestedAt; + + @Column(nullable = false) + private long updatedAt; + + @Column(nullable = false) + private long offerExpiresAt; + + @Column(nullable = false) + private long searchUntil; + + @Column(nullable = false) + private int stars; + + @Column(nullable = false) + private String paymentStatus = ""; + + @Column(nullable = false) + private String product = "standard"; + + @Column(nullable = false) + private String methodId = "cash"; + + @Column(nullable = false) + private String methodLabel = "Cash"; + + @Column(nullable = false) + private long baseCents; + + @Column(nullable = false) + private long distanceCents; + + @Column(nullable = false) + private long timeCents; + + @Column(nullable = false) + private long feeCents; + + @Column(nullable = false) + private long tipCents; + + @Column(nullable = false) + private long courierCents; + + @Column(nullable = false) + private boolean womenOnly; + + @Column(nullable = false) + private boolean quiet; + + @Column(nullable = false) + private boolean wheelchair; + + // tag::morsel-column-entity[] + @Column(nullable = false) + private boolean pets; + // end::morsel-column-entity[] + + @Column(nullable = false) + private String note = ""; + + public Delivery() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public DeliveryState getState() { + return state; + } + + public void setState(DeliveryState state) { + this.state = state; + } + + public String getCustomer() { + return customer; + } + + public void setCustomer(String customer) { + this.customer = customer; + } + + public String getCourier() { + return courier; + } + + public void setCourier(String courier) { + this.courier = courier; + } + + public double getPickupLat() { + return pickupLat; + } + + public void setPickupLat(double pickupLat) { + this.pickupLat = pickupLat; + } + + public double getPickupLng() { + return pickupLng; + } + + public void setPickupLng(double pickupLng) { + this.pickupLng = pickupLng; + } + + public String getPickupAddress() { + return pickupAddress; + } + + public void setPickupAddress(String pickupAddress) { + this.pickupAddress = pickupAddress; + } + + public double getDropoffLat() { + return dropoffLat; + } + + public void setDropoffLat(double dropoffLat) { + this.dropoffLat = dropoffLat; + } + + public double getDropoffLng() { + return dropoffLng; + } + + public void setDropoffLng(double dropoffLng) { + this.dropoffLng = dropoffLng; + } + + public String getDropoffAddress() { + return dropoffAddress; + } + + public void setDropoffAddress(String dropoffAddress) { + this.dropoffAddress = dropoffAddress; + } + + public long getFareCents() { + return fareCents; + } + + public void setFareCents(long fareCents) { + this.fareCents = fareCents; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public double getDistanceMeters() { + return distanceMeters; + } + + public void setDistanceMeters(double distanceMeters) { + this.distanceMeters = distanceMeters; + } + + public double getDurationSeconds() { + return durationSeconds; + } + + public void setDurationSeconds(double durationSeconds) { + this.durationSeconds = durationSeconds; + } + + public long getRequestedAt() { + return requestedAt; + } + + public void setRequestedAt(long requestedAt) { + this.requestedAt = requestedAt; + } + + public long getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(long updatedAt) { + this.updatedAt = updatedAt; + } + + public long getOfferExpiresAt() { + return offerExpiresAt; + } + + public void setOfferExpiresAt(long offerExpiresAt) { + this.offerExpiresAt = offerExpiresAt; + } + + public long getSearchUntil() { + return searchUntil; + } + + public void setSearchUntil(long searchUntil) { + this.searchUntil = searchUntil; + } + + public int getStars() { + return stars; + } + + public void setStars(int stars) { + this.stars = stars; + } + + public String getPaymentStatus() { + return paymentStatus; + } + + public void setPaymentStatus(String paymentStatus) { + this.paymentStatus = paymentStatus; + } + + public String getProduct() { + return product; + } + + public void setProduct(String product) { + this.product = product; + } + + public String getMethodId() { + return methodId; + } + + public void setMethodId(String methodId) { + this.methodId = methodId; + } + + public String getMethodLabel() { + return methodLabel; + } + + public void setMethodLabel(String methodLabel) { + this.methodLabel = methodLabel; + } + + public long getBaseCents() { + return baseCents; + } + + public void setBaseCents(long baseCents) { + this.baseCents = baseCents; + } + + public long getDistanceCents() { + return distanceCents; + } + + public void setDistanceCents(long distanceCents) { + this.distanceCents = distanceCents; + } + + public long getTimeCents() { + return timeCents; + } + + public void setTimeCents(long timeCents) { + this.timeCents = timeCents; + } + + public long getFeeCents() { + return feeCents; + } + + public void setFeeCents(long feeCents) { + this.feeCents = feeCents; + } + + public long getTipCents() { + return tipCents; + } + + public void setTipCents(long tipCents) { + this.tipCents = tipCents; + } + + public long getCourierCents() { + return courierCents; + } + + public void setCourierCents(long courierCents) { + this.courierCents = courierCents; + } + + public boolean isWomenOnly() { + return womenOnly; + } + + public void setWomenOnly(boolean womenOnly) { + this.womenOnly = womenOnly; + } + + public boolean isQuiet() { + return quiet; + } + + public void setQuiet(boolean quiet) { + this.quiet = quiet; + } + + public boolean isWheelchair() { + return wheelchair; + } + + public void setWheelchair(boolean wheelchair) { + this.wheelchair = wheelchair; + } + + public boolean isPets() { + return pets; + } + + public void setPets(boolean pets) { + this.pets = pets; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryActivity.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryActivity.java new file mode 100644 index 00000000000..7d515690a3f --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryActivity.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// The Live Activity a customer's phone shows a delivery in, while the delivery lasts. +@Entity +public class DeliveryActivity { + @Id(autoIncrement = false) + @Column(nullable = false) + private String deliveryId = ""; + + @Column(nullable = false) + private String username = ""; + + /// The id the phone's platform gave the activity. + @Column(nullable = false) + private String activityId = ""; + + /// When the server last sent it a state, for spacing out the updates. + @Column(nullable = false) + private long sentAt; + + /// When its delivery ended, 0 while the delivery lasts. An ended activity is kept + /// until the phone has been told to end it. + @Column(nullable = false) + private long endedAt; + + public DeliveryActivity() { + } + + public String getDeliveryId() { + return deliveryId; + } + + public void setDeliveryId(String deliveryId) { + this.deliveryId = deliveryId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getActivityId() { + return activityId; + } + + public void setActivityId(String activityId) { + this.activityId = activityId; + } + + public long getSentAt() { + return sentAt; + } + + public void setSentAt(long sentAt) { + this.sentAt = sentAt; + } + + public long getEndedAt() { + return endedAt; + } + + public void setEndedAt(long endedAt) { + this.endedAt = endedAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryEvent.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryEvent.java new file mode 100644 index 00000000000..8f02f737818 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryEvent.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; +import com.codenameone.examples.morsel.api.DeliveryState; + +/// One change of a delivery's state: what it became, who made it so, and when. +@Entity +public class DeliveryEvent { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private String deliveryId = ""; + + @Column(nullable = false) + private DeliveryState state; + + @Column(nullable = false) + private String actor = ""; + + @Column(nullable = false) + private long createdAt; + + public DeliveryEvent() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeliveryId() { + return deliveryId; + } + + public void setDeliveryId(String deliveryId) { + this.deliveryId = deliveryId; + } + + public DeliveryState getState() { + return state; + } + + public void setState(DeliveryState state) { + this.state = state; + } + + public String getActor() { + return actor; + } + + public void setActor(String actor) { + this.actor = actor; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryPassed.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryPassed.java new file mode 100644 index 00000000000..b43102d3d84 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/DeliveryPassed.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// A courier who passed on a delivery, or let its offer lapse, and is not asked again. +@Entity +public class DeliveryPassed { + @Id(autoIncrement = false) + @Column(nullable = false) + private String deliveryId = ""; + + @Id(autoIncrement = false) + @Column(nullable = false) + private String courier = ""; + + public DeliveryPassed() { + } + + public String getDeliveryId() { + return deliveryId; + } + + public void setDeliveryId(String deliveryId) { + this.deliveryId = deliveryId; + } + + public String getCourier() { + return courier; + } + + public void setCourier(String courier) { + this.courier = courier; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/LiveTicket.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/LiveTicket.java new file mode 100644 index 00000000000..652a72d91fd --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/LiveTicket.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// A ticket for one connection to the live channel, as its hash. +@Entity +public class LiveTicket { + @Id(autoIncrement = false) + @Column(nullable = false) + private String ticketHash = ""; + + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private long expiresAt; + + public LiveTicket() { + } + + public String getTicketHash() { + return ticketHash; + } + + public void setTicketHash(String ticketHash) { + this.ticketHash = ticketHash; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public long getExpiresAt() { + return expiresAt; + } + + public void setExpiresAt(long expiresAt) { + this.expiresAt = expiresAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ModerationEvent.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ModerationEvent.java new file mode 100644 index 00000000000..6096c318759 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/ModerationEvent.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// One thing an admin did to an account, and why. Written once, never changed. +@Entity +public class ModerationEvent { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private long createdAt; + + @Column(nullable = false) + private String actor = ""; + + @Column(nullable = false) + private String action = ""; + + @Column(nullable = false) + private String reason = ""; + + public ModerationEvent() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public String getActor() { + return actor; + } + + public void setActor(String actor) { + this.actor = actor; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payment.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payment.java new file mode 100644 index 00000000000..bfc682b2536 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payment.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// What a completed delivery was charged, and what was given back. +@Entity +public class Payment { + @Id(autoIncrement = false) + @Column(nullable = false) + private String deliveryId = ""; + + @Column(nullable = false) + private long amountCents; + + @Column(nullable = false) + private String currency = ""; + + @Column(nullable = false) + private String status = ""; + + @Column(nullable = false) + private String providerRef = ""; + + @Column(nullable = false) + private long createdAt; + + @Column(nullable = false) + private String tipRef = ""; + + @Column(nullable = false) + private String refundRef = ""; + + @Column(nullable = false) + private String tipRefundRef = ""; + + @Column(nullable = false) + private String refundReason = ""; + + @Column(nullable = false) + private long refundedAt; + + public Payment() { + } + + public String getDeliveryId() { + return deliveryId; + } + + public void setDeliveryId(String deliveryId) { + this.deliveryId = deliveryId; + } + + public long getAmountCents() { + return amountCents; + } + + public void setAmountCents(long amountCents) { + this.amountCents = amountCents; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getProviderRef() { + return providerRef; + } + + public void setProviderRef(String providerRef) { + this.providerRef = providerRef; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public String getTipRef() { + return tipRef; + } + + public void setTipRef(String tipRef) { + this.tipRef = tipRef; + } + + public String getRefundRef() { + return refundRef; + } + + public void setRefundRef(String refundRef) { + this.refundRef = refundRef; + } + + public String getTipRefundRef() { + return tipRefundRef; + } + + public void setTipRefundRef(String tipRefundRef) { + this.tipRefundRef = tipRefundRef; + } + + public String getRefundReason() { + return refundReason; + } + + public void setRefundReason(String refundReason) { + this.refundReason = refundReason; + } + + public long getRefundedAt() { + return refundedAt; + } + + public void setRefundedAt(long refundedAt) { + this.refundedAt = refundedAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentCustomer.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentCustomer.java new file mode 100644 index 00000000000..74c634666cf --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentCustomer.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// The payment provider's name for an account. +@Entity +public class PaymentCustomer { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String reference = ""; + + public PaymentCustomer() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentMethod.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentMethod.java new file mode 100644 index 00000000000..2f09cb0235c --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentMethod.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// A saved card: what the provider calls it and what to show for it. The +/// number itself is never here. +@Entity +public class PaymentMethod { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String brand = ""; + + @Column(nullable = false) + private String last4 = ""; + + @Column(nullable = false) + private int expMonth; + + @Column(nullable = false) + private int expYear; + + @Column(nullable = false) + private String token = ""; + + @Column(nullable = false) + private boolean preferred; + + @Column(nullable = false) + private long createdAt; + + public PaymentMethod() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getBrand() { + return brand; + } + + public void setBrand(String brand) { + this.brand = brand; + } + + public String getLast4() { + return last4; + } + + public void setLast4(String last4) { + this.last4 = last4; + } + + public int getExpMonth() { + return expMonth; + } + + public void setExpMonth(int expMonth) { + this.expMonth = expMonth; + } + + public int getExpYear() { + return expYear; + } + + public void setExpYear(int expYear) { + this.expYear = expYear; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public boolean isPreferred() { + return preferred; + } + + public void setPreferred(boolean preferred) { + this.preferred = preferred; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentSetup.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentSetup.java new file mode 100644 index 00000000000..83d28a42f81 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PaymentSetup.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// A card being added on the provider's page, until the browser comes back. +@Entity +public class PaymentSetup { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String reference = ""; + + @Column(nullable = false) + private long createdAt; + + @Column(nullable = false) + private boolean completed; + + public PaymentSetup() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public boolean isCompleted() { + return completed; + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payout.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payout.java new file mode 100644 index 00000000000..1e05930b30f --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Payout.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// One payment of earnings to a courier. +@Entity +public class Payout { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private String courier = ""; + + @Column(nullable = false) + private long amountCents; + + @Column(nullable = false) + private String status = ""; + + @Column(nullable = false) + private long createdAt; + + @Column(nullable = false) + private String destination = ""; + + public Payout() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCourier() { + return courier; + } + + public void setCourier(String courier) { + this.courier = courier; + } + + public long getAmountCents() { + return amountCents; + } + + public void setAmountCents(long amountCents) { + this.amountCents = amountCents; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public String getDestination() { + return destination; + } + + public void setDestination(String destination) { + this.destination = destination; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PayoutAccount.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PayoutAccount.java new file mode 100644 index 00000000000..ee346bc8b5c --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PayoutAccount.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// Where a courier's earnings are paid out to. +@Entity +public class PayoutAccount { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String holder = ""; + + @Column(nullable = false) + private String bankName = ""; + + @Column(nullable = false) + private String last4 = ""; + + public PayoutAccount() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getHolder() { + return holder; + } + + public void setHolder(String holder) { + this.holder = holder; + } + + public String getBankName() { + return bankName; + } + + public void setBankName(String bankName) { + this.bankName = bankName; + } + + public String getLast4() { + return last4; + } + + public void setLast4(String last4) { + this.last4 = last4; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PhoneCode.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PhoneCode.java new file mode 100644 index 00000000000..12e9ee93f4b --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PhoneCode.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// The verification code an account was last sent, as its hash. +@Entity +public class PhoneCode { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String codeHash = ""; + + @Column(nullable = false) + private long expiresAt; + + @Column(nullable = false) + private int attempts; + + @Column(nullable = false) + private long sentAt; + + /// The code sent before this one, while it is still good, or empty. + @Column(nullable = false) + private String previousHash = ""; + + @Column(nullable = false) + private long previousExpiresAt; + + /// The guesses already taken at the code before, which keeps the budget + /// it had rather than starting a new one when another code is sent. + @Column(nullable = false) + private int previousAttempts; + + public PhoneCode() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCodeHash() { + return codeHash; + } + + public void setCodeHash(String codeHash) { + this.codeHash = codeHash; + } + + public long getExpiresAt() { + return expiresAt; + } + + public void setExpiresAt(long expiresAt) { + this.expiresAt = expiresAt; + } + + public int getAttempts() { + return attempts; + } + + public void setAttempts(int attempts) { + this.attempts = attempts; + } + + public long getSentAt() { + return sentAt; + } + + public void setSentAt(long sentAt) { + this.sentAt = sentAt; + } + + public String getPreviousHash() { + return previousHash; + } + + public void setPreviousHash(String previousHash) { + this.previousHash = previousHash; + } + + public long getPreviousExpiresAt() { + return previousExpiresAt; + } + + public void setPreviousExpiresAt(long previousExpiresAt) { + this.previousExpiresAt = previousExpiresAt; + } + + public int getPreviousAttempts() { + return previousAttempts; + } + + public void setPreviousAttempts(int previousAttempts) { + this.previousAttempts = previousAttempts; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Preference.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Preference.java new file mode 100644 index 00000000000..278e399eed1 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Preference.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// How one person wants the app and their deliveries, when they have said. +@Entity +public class Preference { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String theme = ""; + + @Column(nullable = false) + private String units = ""; + + @Column(nullable = false) + private boolean notifyDeliveries; + + @Column(nullable = false) + private boolean notifyReceipts; + + @Column(nullable = false) + private boolean notifyPromotions; + + @Column(nullable = false) + private String courierGender = ""; + + @Column(nullable = false) + private boolean quietDelivery; + + @Column(nullable = false) + private boolean wheelchair; + + @Column(nullable = false) + private boolean petFriendly; + + @Column(nullable = false) + private int defaultTip; + + @Column(nullable = false) + private String shareTripsWith = ""; + + public Preference() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getTheme() { + return theme; + } + + public void setTheme(String theme) { + this.theme = theme; + } + + public String getUnits() { + return units; + } + + public void setUnits(String units) { + this.units = units; + } + + public boolean isNotifyDeliveries() { + return notifyDeliveries; + } + + public void setNotifyDeliveries(boolean notifyDeliveries) { + this.notifyDeliveries = notifyDeliveries; + } + + public boolean isNotifyReceipts() { + return notifyReceipts; + } + + public void setNotifyReceipts(boolean notifyReceipts) { + this.notifyReceipts = notifyReceipts; + } + + public boolean isNotifyPromotions() { + return notifyPromotions; + } + + public void setNotifyPromotions(boolean notifyPromotions) { + this.notifyPromotions = notifyPromotions; + } + + public String getCourierGender() { + return courierGender; + } + + public void setCourierGender(String courierGender) { + this.courierGender = courierGender; + } + + public boolean isQuietDelivery() { + return quietDelivery; + } + + public void setQuietDelivery(boolean quietDelivery) { + this.quietDelivery = quietDelivery; + } + + public boolean isWheelchair() { + return wheelchair; + } + + public void setWheelchair(boolean wheelchair) { + this.wheelchair = wheelchair; + } + + public boolean isPetFriendly() { + return petFriendly; + } + + public void setPetFriendly(boolean petFriendly) { + this.petFriendly = petFriendly; + } + + public int getDefaultTip() { + return defaultTip; + } + + public void setDefaultTip(int defaultTip) { + this.defaultTip = defaultTip; + } + + public String getShareTripsWith() { + return shareTripsWith; + } + + public void setShareTripsWith(String shareTripsWith) { + this.shareTripsWith = shareTripsWith; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Pricing.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Pricing.java new file mode 100644 index 00000000000..11d2a2f8151 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Pricing.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// The prices an admin set. One row, with the id 1, once they have set any. +@Entity +public class Pricing { + @Id(autoIncrement = false) + @Column(nullable = false) + private int id; + + @Column(nullable = false) + private long baseCents; + + @Column(nullable = false) + private long perKmCents; + + @Column(nullable = false) + private long perMinuteCents; + + @Column(nullable = false) + private long minimumCents; + + @Column(nullable = false) + private double feePercent; + + @Column(nullable = false) + private double commissionPercent; + + @Column(nullable = false) + private double surge; + + @Column(nullable = false) + private double comfort; + + @Column(nullable = false) + private double xl; + + @Column(nullable = false) + private long updatedAt; + + @Column(nullable = false) + private String updatedBy = ""; + + public Pricing() { + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getBaseCents() { + return baseCents; + } + + public void setBaseCents(long baseCents) { + this.baseCents = baseCents; + } + + public long getPerKmCents() { + return perKmCents; + } + + public void setPerKmCents(long perKmCents) { + this.perKmCents = perKmCents; + } + + public long getPerMinuteCents() { + return perMinuteCents; + } + + public void setPerMinuteCents(long perMinuteCents) { + this.perMinuteCents = perMinuteCents; + } + + public long getMinimumCents() { + return minimumCents; + } + + public void setMinimumCents(long minimumCents) { + this.minimumCents = minimumCents; + } + + public double getFeePercent() { + return feePercent; + } + + public void setFeePercent(double feePercent) { + this.feePercent = feePercent; + } + + public double getCommissionPercent() { + return commissionPercent; + } + + public void setCommissionPercent(double commissionPercent) { + this.commissionPercent = commissionPercent; + } + + public double getSurge() { + return surge; + } + + public void setSurge(double surge) { + this.surge = surge; + } + + public double getComfort() { + return comfort; + } + + public void setComfort(double comfort) { + this.comfort = comfort; + } + + public double getXl() { + return xl; + } + + public void setXl(double xl) { + this.xl = xl; + } + + public long getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(long updatedAt) { + this.updatedAt = updatedAt; + } + + public String getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Profile.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Profile.java new file mode 100644 index 00000000000..96f1eb3453c --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/Profile.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// An account's profile: who it is to the people it deliveries with. +/// +/// The sign-in -- password hash, roles, whether it is enabled -- is the backend's +/// own user store, under the same name. +@Entity +public class Profile { + @Id(autoIncrement = false) + @Column(nullable = false) + private String username = ""; + + @Column(nullable = false) + private String displayName = ""; + + @Column(nullable = false) + private String phone = ""; + + @Column(nullable = false) + private boolean phoneVerified; + + @Column(nullable = false) + private String vehicle = ""; + + @Column(nullable = false) + private String plate = ""; + + @Column(nullable = false) + private long createdAt; + + @Column(nullable = false) + private String gender = "unspecified"; + + @Column(nullable = false) + private String language = "en"; + + @Column(nullable = false) + private boolean flagged; + + @Column(nullable = false) + private String flagReason = ""; + + @Column(nullable = false) + private String emergencyName = ""; + + @Column(nullable = false) + private String emergencyPhone = ""; + + @Column(nullable = false) + private String homeAddress = ""; + + @Column(nullable = false) + private String workAddress = ""; + + public Profile() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public boolean isPhoneVerified() { + return phoneVerified; + } + + public void setPhoneVerified(boolean phoneVerified) { + this.phoneVerified = phoneVerified; + } + + public String getVehicle() { + return vehicle; + } + + public void setVehicle(String vehicle) { + this.vehicle = vehicle; + } + + public String getPlate() { + return plate; + } + + public void setPlate(String plate) { + this.plate = plate; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public boolean isFlagged() { + return flagged; + } + + public void setFlagged(boolean flagged) { + this.flagged = flagged; + } + + public String getFlagReason() { + return flagReason; + } + + public void setFlagReason(String flagReason) { + this.flagReason = flagReason; + } + + public String getEmergencyName() { + return emergencyName; + } + + public void setEmergencyName(String emergencyName) { + this.emergencyName = emergencyName; + } + + public String getEmergencyPhone() { + return emergencyPhone; + } + + public void setEmergencyPhone(String emergencyPhone) { + this.emergencyPhone = emergencyPhone; + } + + public String getHomeAddress() { + return homeAddress; + } + + public void setHomeAddress(String homeAddress) { + this.homeAddress = homeAddress; + } + + public String getWorkAddress() { + return workAddress; + } + + public void setWorkAddress(String workAddress) { + this.workAddress = workAddress; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PushDevice.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PushDevice.java new file mode 100644 index 00000000000..4373370ba4a --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/domain/PushDevice.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.domain; + +import com.codename1.annotations.Column; +import com.codename1.annotations.Entity; +import com.codename1.annotations.Id; + +/// A device an account is sent push notifications on. +/// +/// Keyed by the hash of its provider and token, so a token is on one account at +/// a time: the phone belongs to whoever signed in on it last. +@Entity +public class PushDevice { + @Id(autoIncrement = false) + @Column(nullable = false) + private String id = ""; + + @Column(nullable = false) + private String username = ""; + + /// `fcm`, `apns`, `huawei`, `wns` or `web`. + @Column(nullable = false) + private String provider = ""; + + @Column(nullable = false) + private String token = ""; + + @Column(nullable = false) + private String platform = ""; + + @Column(nullable = false) + private long updatedAt; + + public PushDevice() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getPlatform() { + return platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public long getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(long updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geo.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geo.java new file mode 100644 index 00000000000..0e76bbf41fe --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geo.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.geo; + +/// Distances on the ground. +public final class Geo { + private static final double EARTH_RADIUS_METERS = 6371000d; + /// Metres in a degree of latitude, anywhere. A degree of longitude is this + /// times the cosine of the latitude. + public static final double METERS_PER_DEGREE = 111320d; + + private Geo() { + } + + /// The great-circle distance between two points, in metres. + public static double meters(double lat1, double lng1, double lat2, double lng2) { + double dLat = Math.toRadians(lat2 - lat1); + double dLng = Math.toRadians(lng2 - lng1); + double sinLat = Math.sin(dLat / 2); + double sinLng = Math.sin(dLng / 2); + double a = sinLat * sinLat + Math.cos(Math.toRadians(lat1)) + * Math.cos(Math.toRadians(lat2)) * sinLng * sinLng; + return 2 * EARTH_RADIUS_METERS * Math.atan2(Math.sqrt(a), Math.sqrt(Math.max(0d, 1 - a))); + } + + public static boolean isPoint(double lat, double lng) { + return lat >= -90d && lat <= 90d && lng >= -180d && lng <= 180d + && !(lat == 0d && lng == 0d); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/GeoEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/GeoEndpoint.java new file mode 100644 index 00000000000..f0910a19f29 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/GeoEndpoint.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.geo; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.api.GeoApiServer; +import com.codenameone.examples.morsel.api.PlaceDto; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/// The server's half of `GeoApi`. +@Component +public class GeoEndpoint implements GeoApiServer { + private final Geocoder geocoder; + + public GeoEndpoint(Geocoder geocoder) { + this.geocoder = geocoder; + } + + @Override + public List search(String q, double lat, double lng) throws Exception { + String text = q == null ? "" : q.trim(); + // Two letters match half the map; nothing useful comes back, and each + // keystroke would be a request to the service behind this. + if (text.length() < 3 || text.length() > 120) { + return new ArrayList(); + } + try { + return geocoder.search(text, lat, lng); + } catch (IOException failed) { + System.err.println("geocoder search failed: " + failed.getMessage()); + throw new ResponseStatusException(502, "Search is unavailable right now"); + } + } + + @Override + public PlaceDto reverse(double lat, double lng) throws Exception { + if (!Geo.isPoint(lat, lng)) { + throw new ResponseStatusException(400, "That is not a point on the map"); + } + PlaceDto found; + try { + found = geocoder.reverse(lat, lng); + } catch (IOException failed) { + System.err.println("geocoder reverse failed: " + failed.getMessage()); + found = null; + } + // The point asked about, not the centre of whatever was found near it. + if (found == null) { + // A pin can be dropped anywhere, and a delivery can start there: an + // address the service does not know is a point without a name. + return new PlaceDto("Dropped pin", "", lat, lng); + } + return new PlaceDto(found.name(), found.address(), lat, lng); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geocoder.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geocoder.java new file mode 100644 index 00000000000..704bcc840f3 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/Geocoder.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.geo; + +import com.codenameone.examples.morsel.api.PlaceDto; + +import java.io.IOException; +import java.util.List; + +/// Turns text into places and a point into an address. +/// +/// The app never talks to a geocoding service itself. It asks this server, +/// which asks whichever service is configured: no key ships inside the app, the +/// service can be replaced without a new release, and the server is where its +/// use can be limited. +public interface Geocoder { + /// Places matching `text`, best first, favouring those near the point. + List search(String text, double lat, double lng) throws IOException; + + /// The address at a point, or null when the service knows of none. + PlaceDto reverse(double lat, double lng) throws IOException; +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/PhotonGeocoder.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/PhotonGeocoder.java new file mode 100644 index 00000000000..73abe791eaf --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/PhotonGeocoder.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.geo; + +import com.codename1.backend.Json; +import com.codename1.backend.Web; +import com.codenameone.examples.morsel.api.PlaceDto; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/// Geocoding by Photon, the open-source geocoder over OpenStreetMap data. +/// +/// The address defaults to the public instance, which needs no key and asks in +/// return for moderate use. Run your own, or point `morsel.geocoder.url` at +/// another Photon, before sending it a real service's traffic. +public class PhotonGeocoder implements Geocoder { + private final String base; + + public PhotonGeocoder(String base) { + this.base = base.endsWith("/") ? base.substring(0, base.length() - 1) : base; + } + + @Override + public List search(String text, double lat, double lng) throws IOException { + String url = base + "/api?limit=8&q=" + URLEncoder.encode(text, "UTF-8"); + if (Geo.isPoint(lat, lng)) { + url += "&lat=" + lat + "&lon=" + lng; + } + return places(url); + } + + @Override + public PlaceDto reverse(double lat, double lng) throws IOException { + List found = places(base + "/reverse?lat=" + lat + "&lon=" + lng); + return found.isEmpty() ? null : found.get(0); + } + + private static List places(String url) throws IOException { + Web.Result answer = Web.get(url); + if (!answer.isSuccess()) { + throw new IOException("The geocoder answered " + answer.getStatus()); + } + List out = new ArrayList(); + Object features = Json.parseObject(answer.getBodyAsString()).get("features"); + if (!(features instanceof List)) { + return out; + } + List list = (List) features; + for (int iter = 0; iter < list.size(); iter++) { + PlaceDto place = place(list.get(iter)); + if (place != null) { + out.add(place); + } + } + return out; + } + + /// One GeoJSON feature as a place, or null when it has no usable point. + private static PlaceDto place(Object feature) { + if (!(feature instanceof Map)) { + return null; + } + Object geometry = ((Map) feature).get("geometry"); + Object properties = ((Map) feature).get("properties"); + if (!(geometry instanceof Map) || !(properties instanceof Map)) { + return null; + } + Object coordinates = ((Map) geometry).get("coordinates"); + if (!(coordinates instanceof List) || ((List) coordinates).size() < 2) { + return null; + } + Object lng = ((List) coordinates).get(0); + Object lat = ((List) coordinates).get(1); + if (!(lng instanceof Number) || !(lat instanceof Number)) { + return null; + } + Map p = (Map) properties; + String street = text(p, "street"); + if (street.length() > 0 && text(p, "housenumber").length() > 0) { + street = text(p, "housenumber") + " " + street; + } + String name = text(p, "name"); + if (name.length() == 0) { + name = street.length() > 0 ? street : text(p, "city"); + } + if (name.length() == 0) { + return null; + } + StringBuilder address = new StringBuilder(); + append(address, name.equals(street) ? "" : street); + append(address, text(p, "city")); + append(address, text(p, "country")); + return new PlaceDto(name, address.toString(), ((Number) lat).doubleValue(), + ((Number) lng).doubleValue()); + } + + private static String text(Map properties, String key) { + Object value = properties.get(key); + return value instanceof String ? (String) value : ""; + } + + private static void append(StringBuilder to, String part) { + if (part.length() > 0) { + if (to.length() > 0) { + to.append(", "); + } + to.append(part); + } + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/StubGeocoder.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/StubGeocoder.java new file mode 100644 index 00000000000..c09a12373e6 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/geo/StubGeocoder.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.geo; + +import com.codenameone.examples.morsel.api.PlaceDto; + +import java.util.ArrayList; +import java.util.List; + +/// A geocoder that knows ten places and asks nobody. What the tests run +/// against, so they neither depend on a public service nor lean on one. +public class StubGeocoder implements Geocoder { + private static final Object[][] PLACES = { + {"Ferry Building", "1 Ferry Building, San Francisco", 37.7955, -122.3937}, + {"Pier 39", "The Embarcadero, San Francisco", 37.8087, -122.4098}, + {"Coit Tower", "1 Telegraph Hill Blvd, San Francisco", 37.8024, -122.4058}, + {"Ghirardelli Square", "900 North Point St, San Francisco", 37.8059, -122.4229}, + {"Lombard Street", "Lombard St, San Francisco", 37.8021, -122.4187}, + {"Palace of Fine Arts", "3601 Lyon St, San Francisco", 37.8029, -122.4484}, + {"Union Square", "333 Post St, San Francisco", 37.7880, -122.4075}, + {"Oracle Park", "24 Willie Mays Plaza, San Francisco", 37.7786, -122.3893}, + {"Fort Mason", "2 Marina Blvd, San Francisco", 37.8066, -122.4310}, + {"Transamerica Pyramid", "600 Montgomery St, San Francisco", 37.7952, -122.4028}, + }; + + @Override + public List search(String text, double lat, double lng) { + List out = new ArrayList(); + String wanted = text == null ? "" : text.trim(); + for (int iter = 0; iter < PLACES.length; iter++) { + if (contains((String) PLACES[iter][0], wanted)) { + out.add(place(iter)); + } + } + return out; + } + + @Override + public PlaceDto reverse(double lat, double lng) { + int nearest = 0; + double best = Double.MAX_VALUE; + for (int iter = 0; iter < PLACES.length; iter++) { + double meters = Geo.meters(lat, lng, ((Double) PLACES[iter][2]).doubleValue(), + ((Double) PLACES[iter][3]).doubleValue()); + if (meters < best) { + best = meters; + nearest = iter; + } + } + return place(nearest); + } + + private static PlaceDto place(int index) { + return new PlaceDto((String) PLACES[index][0], (String) PLACES[index][1], + ((Double) PLACES[index][2]).doubleValue(), + ((Double) PLACES[index][3]).doubleValue()); + } + + /// Whether `text` contains `part`, whatever the case of either. Compared a + /// character at a time and not by folding both, which would follow the + /// server's locale. + private static boolean contains(String text, String part) { + for (int at = 0; at + part.length() <= text.length(); at++) { + if (text.regionMatches(true, at, part, 0, part.length())) { + return true; + } + } + return false; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveEndpoint.java new file mode 100644 index 00000000000..f459e18009e --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveEndpoint.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.live; + +import com.codename1.backend.WebSocket; +import com.codename1.backend.WebSocketSession; +import com.codename1.backend.annotations.WebSocketMapping; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.api.UserDto; + +/// The live channel: one WebSocket per signed-in app, down which the server +/// says what changed ([LiveHub]). +/// +/// No filter chain covers this path, deliberately: the handshake cannot carry +/// the bearer token on every platform. The connection is let in by the ticket +/// in its address instead ([LiveTickets]), and one that presents none, or one +/// already used, is closed before it is told anything. +@WebSocketMapping("/ws/live") +public class LiveEndpoint implements WebSocket { + private final LiveTickets tickets; + private final Accounts accounts; + private final LiveHub hub; + + public LiveEndpoint(LiveTickets tickets, Accounts accounts, LiveHub hub) { + this.tickets = tickets; + this.accounts = accounts; + this.hub = hub; + } + + @Override + public void onOpen(WebSocketSession session) throws Exception { + String username = tickets.redeem(ticket(session.getQuery())); + UserDto user = username == null ? null : accounts.describe(username); + if (user == null || user.suspended()) { + session.close(4401, "ticket"); + return; + } + // Who this connection is, kept on the connection: the one piece of + // state a WebSocket has that a request does not. + session.setAttachment(username); + hub.add(username, user.admin(), session); + // Read again now that the connection is in the hub. A block or a + // withdrawn admin role commits and then drops the account's + // connections; one that committed after the read above and dropped + // before the add found nothing to drop, and this connection would + // carry what was read before -- for an admin, every courier's position. + // Either the change is seen here, or its drop comes after the add and + // closes this connection. + UserDto now = accounts.describe(username); + if (now == null || now.suspended() || now.admin() != user.admin()) { + hub.remove(username, session); + session.close(4403, "changed"); + return; + } + session.sendText("{\"type\":\"ready\"}"); + } + + @Override + public void onText(WebSocketSession session, String message) throws Exception { + // The app says one thing, to learn the connection is still alive on a + // network that drops idle ones without telling either end. + if (session.getAttachment() != null && message != null + && message.indexOf("\"ping\"") >= 0) { + session.sendText("{\"type\":\"pong\"}"); + } + } + + @Override + public void onBinary(WebSocketSession session, byte[] message, int offset, int length) + throws Exception { + session.close(1003, "text only"); + } + + @Override + public void onClose(WebSocketSession session, int code, String reason) { + Object username = session.getAttachment(); + if (username instanceof String) { + hub.remove((String) username, session); + } + } + + /// The `ticket` parameter of a query string, or null. A ticket is hex, so + /// there is nothing in it to decode. + static String ticket(String query) { + if (query == null) { + return null; + } + int at = 0; + while (at < query.length()) { + int end = query.indexOf('&', at); + if (end < 0) { + end = query.length(); + } + if (query.startsWith("ticket=", at)) { + return query.substring(at + 7, end); + } + at = end + 1; + } + return null; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveHub.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveHub.java new file mode 100644 index 00000000000..135e4cd9c1d --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveHub.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.live; + +import com.codename1.backend.Json; +import com.codename1.backend.WebSocketSession; +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.api.DeliveryState; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/// The open live connections, and what is pushed down them. +/// +/// Three frames, each a small JSON object: +/// +/// - `{"type":"delivery","id":...,"state":...}` -- a delivery changed. Sent to its +/// customer, its courier and the admins, who then read the delivery from the API. +/// The frame carries no more than that on purpose: the API is the one account +/// of a delivery, and a client that missed a frame is put right by its next read. +/// - `{"type":"courier","deliveryId":...,"lat":...,"lng":...,"heading":...}` -- the +/// courier of a delivery moved. Sent to its customer. +/// - `{"type":"fleet","username":...,"online":...,"lat":...}` -- a courier's +/// status or position changed. Sent to the admins. +/// +/// The connections are those of this process. Two instances of the server +/// would each know only their own, and a change made on one would not reach a +/// client connected to the other: running more than one needs a channel +/// between them -- a database LISTEN/NOTIFY, a message broker -- feeding the +/// same three methods. +@Component +public class LiveHub { + private final Map> byUser = + new HashMap>(); + private final List admins = new ArrayList(); + + synchronized void add(String username, boolean admin, WebSocketSession session) { + List open = byUser.get(username); + if (open == null) { + open = new ArrayList(); + byUser.put(username, open); + } + open.add(session); + if (admin) { + admins.add(session); + } + } + + synchronized void remove(String username, WebSocketSession session) { + List open = byUser.get(username); + if (open != null) { + open.remove(session); + if (open.isEmpty()) { + byUser.remove(username); + } + } + admins.remove(session); + } + + /// How many connections are open, for the tests. + public synchronized int connections() { + int total = 0; + for (List open : byUser.values()) { + total += open.size(); + } + return total; + } + + /// Closes every connection an account has open. + public void drop(String username) { + List open; + synchronized (this) { + open = byUser.remove(username); + if (open != null) { + admins.removeAll(open); + } + } + for (int iter = 0; open != null && iter < open.size(); iter++) { + open.get(iter).close(4403, "suspended"); + } + } + + /// Tells a delivery's customer and courier, and the admins, that it changed. + /// `also` is one more account to tell, or empty: the courier an offer was + /// taken back from is no longer the delivery's courier, and still has to hear. + public void deliveryChanged(String id, DeliveryState state, String customer, String courier, String also) { + Map frame = new LinkedHashMap(); + frame.put("type", "delivery"); + frame.put("id", id); + frame.put("state", state.name()); + String text = Json.write(frame); + List to = new ArrayList(); + synchronized (this) { + collect(to, customer); + collect(to, courier); + collect(to, also); + for (int iter = 0; iter < admins.size(); iter++) { + if (!to.contains(admins.get(iter))) { + to.add(admins.get(iter)); + } + } + } + send(to, text); + } + + public void courierMoved(String customer, String deliveryId, double lat, double lng, double heading) { + Map frame = new LinkedHashMap(); + frame.put("type", "courier"); + frame.put("deliveryId", deliveryId); + frame.put("lat", Double.valueOf(lat)); + frame.put("lng", Double.valueOf(lng)); + frame.put("heading", Double.valueOf(heading)); + List to = new ArrayList(); + synchronized (this) { + collect(to, customer); + } + send(to, Json.write(frame)); + } + + public void fleetChanged(String username, boolean online, double lat, double lng, + double heading) { + List to; + synchronized (this) { + if (admins.isEmpty()) { + return; + } + to = new ArrayList(admins); + } + Map frame = new LinkedHashMap(); + frame.put("type", "fleet"); + frame.put("username", username); + frame.put("online", Boolean.valueOf(online)); + frame.put("lat", Double.valueOf(lat)); + frame.put("lng", Double.valueOf(lng)); + frame.put("heading", Double.valueOf(heading)); + send(to, Json.write(frame)); + } + + /// Must be called holding the lock. + private void collect(List to, String username) { + List open = username == null ? null : byUser.get(username); + for (int iter = 0; open != null && iter < open.size(); iter++) { + if (!to.contains(open.get(iter))) { + to.add(open.get(iter)); + } + } + } + + /// Sends outside the lock: a slow client holds up its own frame, not + /// everyone's. One that cannot be written to is dropped; its close callback + /// takes it out of the maps. + private static void send(List to, String text) { + for (int iter = 0; iter < to.size(); iter++) { + WebSocketSession session = to.get(iter); + try { + session.sendText(text); + } catch (IOException gone) { + session.abort(); + } + } + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveTickets.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveTickets.java new file mode 100644 index 00000000000..e78f9c70e8c --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/live/LiveTickets.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.live; + +import com.codename1.backend.Crypto; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Scheduled; +import com.codename1.backend.annotations.Transactional; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.TicketDto; +import com.codenameone.examples.morsel.domain.LiveTicket; +import com.codenameone.examples.morsel.repository.LiveTicketRepository; + +import java.io.IOException; + +/// Tickets for the live channel. +/// +/// A WebSocket handshake is one request the app cannot attach its bearer token +/// to on every platform -- a browser's WebSocket takes no headers at all -- and a +/// token in the address would end up in every access log on the way. So the app +/// asks for a ticket over the authenticated API and presents that instead: it +/// is random, it names nothing, it is good for one connection, and it is stale +/// in half a minute. +@Component +public class LiveTickets { + static final int TICKET_SECONDS = 30; + + private final LiveTicketRepository tickets; + + public LiveTickets(LiveTicketRepository tickets) { + this.tickets = tickets; + } + + /// Not held against the account being closed, as the rows that last are. + /// A ticket issued in the instant an account is erased outlives it by at + /// most [#TICKET_SECONDS] plus one [#sweep], and opens nothing meanwhile: + /// `LiveEndpoint` checks the account again when the ticket is redeemed. + /// A row lock on every socket connect is not worth that minute. + @Transactional + public TicketDto issue(String username) throws IOException { + String ticket = Ids.hex(Crypto.randomBytes(24)); + LiveTicket row = new LiveTicket(); + row.setTicketHash(Ids.sha256(ticket)); + row.setUsername(username); + row.setExpiresAt(System.currentTimeMillis() + TICKET_SECONDS * 1000L); + tickets.insert(row); + return new TicketDto(ticket, TICKET_SECONDS); + } + + /// The account a ticket was issued to, or null when it is unknown, used or + /// stale. Redeeming it uses it up: the row is deleted, and only the + /// connection whose delete removed it is let in, so a ticket presented twice + /// at the same moment still opens one connection. + @Transactional + public String redeem(String ticket) throws IOException { + if (ticket == null || ticket.length() == 0 || ticket.length() > 128) { + return null; + } + String hash = Ids.sha256(ticket); + LiveTicket row = tickets.findById(hash); + if (row == null) { + return null; + } + String username = row.getUsername(); + long expiresAt = row.getExpiresAt(); + // One delete statement: the count it answers is what says this + // connection, and not another presenting the same ticket, used it. + int removed = tickets.deleteByTicketHash(hash); + if (removed != 1 || expiresAt < System.currentTimeMillis()) { + return null; + } + return username; + } + + /// Clears the tickets nobody came back for. + @Scheduled(fixedDelay = 60000, initialDelay = 60000) + @Transactional + public void sweep() throws IOException { + tickets.deleteByExpiresAtLessThan(System.currentTimeMillis()); + } + + /// Forgets the tickets of an account that is being closed. + @Transactional + public void forget(String username) { + tickets.deleteByUsername(username); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/Applications.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/Applications.java new file mode 100644 index 00000000000..040e62c49f5 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/Applications.java @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.onboarding; + +import com.codename1.backend.Base64; +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.security.GrantedAuthority; +import com.codename1.backend.security.core.userdetails.JdbcUserDetailsManager; +import com.codename1.backend.security.core.userdetails.User; +import com.codename1.backend.security.core.userdetails.UserDetails; +import com.codename1.orm.repository.PageRequest; +import com.codename1.orm.session.Identifier; +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.Roles; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.account.Moderation; +import com.codenameone.examples.morsel.api.DocumentContentDto; +import com.codenameone.examples.morsel.api.DocumentDto; +import com.codenameone.examples.morsel.api.DocumentUploadDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.domain.CourierApplication; +import com.codenameone.examples.morsel.domain.CourierDocument; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.repository.CourierApplicationRepository; +import com.codenameone.examples.morsel.repository.CourierDocumentRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.delivery.Fares; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/// Applications to drive, from the first field filled in to an admin's answer. +/// +/// ``` +/// (none) -> draft -> pending -> approved +/// ^ | +/// +---------+-> rejected -> pending corrected and handed in again +/// ``` +/// +/// The applicant fills in a form and uploads five documents, in any order and +/// over as many visits as it takes; nothing is checked for completeness until +/// they hand it in. An admin then approves it, which is the one thing that +/// grants the COURIER role, or rejects it with a reason the applicant reads +/// before correcting it and handing it in again. +/// +/// `approved` is what the rest of the server reads: going on line and being +/// offered deliveries both ask for it here, and not for the role alone. +@Component +public class Applications { + public static final String NONE = "none"; + public static final String DRAFT = "draft"; + public static final String PENDING = "pending"; + public static final String APPROVED = "approved"; + public static final String REJECTED = "rejected"; + /// The documents an application needs, in the order the app asks for them. + public static final String[] KINDS = {"photo_id", "licence", "selfie", "vehicle_registration", + "insurance"}; + /// The largest picture taken, decoded: 1.5 MB. The app shrinks a photograph + /// well below this before sending it; the cap is for whoever does not. + static final int MAX_DOCUMENT_BYTES = 1572864; + + /// The most pixels a document may have on a side, read from its header. + static final int MAX_DOCUMENT_SIDE = 4096; + + private static final String[] KIND_NAMES = {"a photo of your ID", "a photo of your licence", + "a selfie", "the vehicle's registration", "proof of insurance"}; + + private static final int LIST_LIMIT = 200; + + private final CourierApplicationRepository applications; + private final CourierDocumentRepository documents; + private final ProfileRepository profiles; + private final JdbcUserDetailsManager users; + private final Moderation moderation; + + public Applications(CourierApplicationRepository applications, + CourierDocumentRepository documents, ProfileRepository profiles, + JdbcUserDetailsManager users, Moderation moderation) { + this.applications = applications; + this.documents = documents; + this.profiles = profiles; + this.users = users; + this.moderation = moderation; + } + + // ------------------------------------------------------------- applicant + + /// Where an account stands: one of the five statuses. + @Transactional(readOnly = true) + public String status(String username) throws IOException { + CourierApplication application = find(username); + return application == null ? NONE : application.getStatus(); + } + + @Transactional(readOnly = true) + public boolean approved(String username) throws IOException { + return APPROVED.equals(status(username)); + } + + /// The status of every account that has an application, by name. + @Transactional(readOnly = true) + public Map statuses() throws IOException { + Map out = new HashMap(); + List rows = applications.statuses(); + for (int iter = 0; iter < rows.size(); iter++) { + Object[] row = rows.get(iter); + out.put((String) row[0], (String) row[1]); + } + return out; + } + + /// An account's application, or one whose status is `none`. + @Transactional(readOnly = true) + public CourierApplicationDto get(String username) throws IOException { + Profile profile = profiles.findById(username); + return toDto(username, profile == null ? "" : profile.getDisplayName(), + find(username), documents.summaries(username), 0); + } + + /// Begins an application as a draft, when the account has none. `vehicle` + /// and `plate` are what an older app collected at registration. + @Transactional + public void open(String username, String vehicle, String plate) throws IOException { + if (find(username) == null) { + applications.insert(blank(username, DRAFT, vehicle, plate)); + } + } + + /// Saves the form. Nothing has to be complete, but what is there has to be + /// well formed. An application being reviewed or already approved is not + /// the applicant's to change. + /// + /// A refusal does not undo the transaction: the draft a first visit begins + /// is kept whether or not what came with it was well formed. Nothing else + /// is written before the last check has passed. + @Transactional(noRollbackFor = ResponseStatusException.class) + public CourierApplicationDto save(String username, CourierApplicationDto form) + throws IOException { + if (form == null) { + throw new ResponseStatusException(400, "Nothing to save"); + } + // Held first, as an upload holds it: what is saved here must not land + // on an application that was handed in after it was read. + hold(username); + CourierApplication application = editable(username); + String birth = date(form.dateOfBirth(), "your date of birth"); + String expiry = date(form.licenceExpiry(), "the licence's expiry date"); + if (form.vehicleYear() < 0 || form.vehicleYear() > 2200 || form.vehicleSeats() < 0 + || form.vehicleSeats() > 20) { + throw new ResponseStatusException(400, "Check the vehicle's year and seats"); + } + // Every field is checked before the first is assigned: the application + // is a managed entity, and a field assigned ahead of a refusal would be + // written with the draft. + String legalName = Text.optional(form.legalName(), 120, "your name"); + String licenceNumber = Text.optional(form.licenceNumber(), 40, "the licence number"); + String make = Text.optional(form.vehicleMake(), 40, "the vehicle's make"); + String model = Text.optional(form.vehicleModel(), 40, "the vehicle's model"); + String color = Text.optional(form.vehicleColor(), 24, "the vehicle's colour"); + String plate = Text.optional(form.vehiclePlate(), 32, "the vehicle's plate"); + String product = Fares.product(form.product()); + application.setLegalName(legalName); + application.setDateOfBirth(birth); + application.setLicenceNumber(licenceNumber); + application.setLicenceExpiry(expiry); + application.setVehicleMake(make); + application.setVehicleModel(model); + application.setVehicleYear(form.vehicleYear()); + application.setVehicleColor(color); + application.setVehiclePlate(plate); + application.setVehicleSeats(form.vehicleSeats()); + application.setProduct(product); + application.setWheelchair(form.accessible()); + application.setPets(form.petFriendly()); + return get(username); + } + + /// Stores a document, in place of an earlier one of its kind. + /// + /// What arrives is checked to be what it says: base64 that decodes, to no + /// more than the cap, to bytes that begin as a JPEG or a PNG does. The + /// declared type alone is the caller's word, and this is later sent to an + /// admin's screen to be drawn. + @Transactional + public CourierApplicationDto upload(String username, DocumentUploadDto upload) + throws IOException { + if (upload == null) { + throw new ResponseStatusException(400, "No document to store"); + } + String kind = Text.oneOf(upload.kind(), KINDS, "", "the kind of document"); + if (kind.length() == 0) { + throw new ResponseStatusException(400, "Say which document this is"); + } + String data = upload.dataBase64() == null ? "" : upload.dataBase64().trim(); + // Refused by length first, so an oversized upload is not decoded at all. + if (data.length() == 0 || data.length() > (MAX_DOCUMENT_BYTES / 3 + 1) * 4) { + throw new ResponseStatusException(data.length() == 0 ? 400 : 413, + data.length() == 0 ? "The document is empty" : "That picture is too large"); + } + byte[] bytes = Base64.decode(data); + if (bytes == null) { + throw new ResponseStatusException(400, "The document is not base64"); + } + if (bytes.length > MAX_DOCUMENT_BYTES) { + throw new ResponseStatusException(413, "That picture is too large"); + } + String type; + if (bytes.length > 3 && (bytes[0] & 0xff) == 0xff && (bytes[1] & 0xff) == 0xd8 + && (bytes[2] & 0xff) == 0xff) { + type = "image/jpeg"; + } else if (bytes.length > 8 && (bytes[0] & 0xff) == 0x89 && bytes[1] == 'P' + && bytes[2] == 'N' && bytes[3] == 'G') { + type = "image/png"; + } else { + throw new ResponseStatusException(400, "Send a JPEG or a PNG picture"); + } + // The size the picture says it has, before anyone decodes it. A few + // kilobytes can declare a picture of millions of pixels, and an admin's + // app decoding that runs out of memory: the app sends at most 1280 on + // a side, so anything far past that is refused. + int[] size = "image/png".equals(type) ? pngSize(bytes) : jpegSize(bytes); + if (size == null || size[0] <= 0 || size[1] <= 0) { + throw new ResponseStatusException(400, "Send a JPEG or a PNG picture"); + } + if (size[0] > MAX_DOCUMENT_SIDE || size[1] > MAX_DOCUMENT_SIDE) { + throw new ResponseStatusException(413, "That picture is too large"); + } + // Held before it is read. The check below says the application may + // still be changed, and the picture is stored after it: without the + // hold, an application handed in between the two had a document + // replaced while it was under review, perhaps after an admin had + // looked at the one it replaced. + hold(username); + editable(username); + long now = System.currentTimeMillis(); + // Replaced with a statement where there is one already, so the picture + // it held is not read only to be overwritten. + if (documents.replace(username, kind, type, "uploaded", now, data) == 0) { + CourierDocument document = new CourierDocument(); + document.setUsername(username); + document.setKind(kind); + document.setContentType(type); + document.setStatus("uploaded"); + document.setUploadedAt(now); + document.setData(data); + documents.insert(document); + } + return get(username); + } + + /// A stored document, picture included. The caller decides who may ask: + /// the applicant for their own, an admin for anyone's. + @Transactional(readOnly = true) + public DocumentContentDto document(String username, String kind) throws IOException { + CourierDocument stored = kind == null || kind.length() == 0 || kind.length() > 24 + || username == null || username.length() == 0 ? null + : documents.findById(Identifier.of(username, kind)); + if (stored == null) { + throw new ResponseStatusException(404, "No such document"); + } + return new DocumentContentDto(stored.getKind(), stored.getContentType(), + stored.getData()); + } + + /// Hands the application in. Answers 400, naming what is missing, until it + /// is complete. As in [#save], that refusal keeps the draft a first visit + /// began. + @Transactional(noRollbackFor = ResponseStatusException.class) + public CourierApplicationDto submit(String username) throws IOException { + // Held before the form is read and checked: a save that lands between + // the check and the move below would otherwise hand in an application + // emptied after it was found complete, where it can no longer be + // corrected. Behind the hold a save either went first, and is what is + // checked, or comes after and finds it handed in. + hold(username); + String was = editable(username).getStatus(); + CourierApplicationDto form = get(username); + List missing = new ArrayList(); + String today = Days.iso(System.currentTimeMillis()); + need(missing, form.legalName().length() > 0, "your legal name"); + // Dates in this form compare as text. Eighteen years ago is today with + // the year lowered. + String adult = (Integer.parseInt(today.substring(0, 4)) - 18) + today.substring(4); + need(missing, form.dateOfBirth().length() > 0 && form.dateOfBirth().compareTo(adult) <= 0, + "a date of birth that makes you 18"); + need(missing, form.licenceNumber().length() > 0, "your licence number"); + need(missing, form.licenceExpiry().compareTo(today) >= 0, "a licence that has not expired"); + need(missing, form.vehicleMake().length() > 0 && form.vehicleModel().length() > 0, + "the vehicle's make and model"); + need(missing, form.vehicleYear() >= 1990, "the vehicle's year"); + need(missing, form.vehicleColor().length() > 0, "the vehicle's colour"); + need(missing, form.vehiclePlate().length() > 0, "the vehicle's plate"); + need(missing, form.vehicleSeats() >= (Fares.XL.equals(form.product()) ? 6 : 4), + Fares.XL.equals(form.product()) ? "six seats for XL" : "the number of seats"); + for (int iter = 0; iter < form.documents().size(); iter++) { + need(missing, !"missing".equals(form.documents().get(iter).status()), KIND_NAMES[iter]); + } + if (!missing.isEmpty()) { + StringBuilder what = new StringBuilder("Still needed: "); + for (int iter = 0; iter < missing.size(); iter++) { + what.append(iter == 0 ? "" : ", ").append(missing.get(iter)); + } + throw new ResponseStatusException(400, what.toString()); + } + // Held to the status it was read in: handed in twice, it is handed in once. + applications.submit(username, was, PENDING, System.currentTimeMillis()); + return get(username); + } + + // ----------------------------------------------------------------- admin + + /// Applications, the longest waiting first among those waiting. `status` + /// narrows them to one status; empty lists all of them. + @Transactional(readOnly = true) + public List list(String status) throws IOException { + String wanted = Text.oneOf(status, new String[] {DRAFT, PENDING, APPROVED, REJECTED}, "", + "an application's status"); + PageRequest page = new PageRequest(0, LIST_LIMIT); + List rows = wanted.length() == 0 ? applications.newest(page) + : applications.inStatus(wanted, page); + List out = new ArrayList(); + if (rows.isEmpty()) { + return out; + } + List owners = new ArrayList(); + for (int iter = 0; iter < rows.size(); iter++) { + owners.add(rows.get(iter).getUsername()); + } + // The applicants' names in one query, and the documents of all of them + // in another, without the pictures. + Map names = new HashMap(); + List named = profiles.displayNames(owners); + for (int iter = 0; iter < named.size(); iter++) { + Object[] row = named.get(iter); + names.put((String) row[0], (String) row[1]); + } + Map> byOwner = new HashMap>(); + List all = documents.summaries(owners); + for (int iter = 0; iter < all.size(); iter++) { + Object[] row = all.get(iter); + String owner = (String) row[0]; + List held = byOwner.get(owner); + if (held == null) { + held = new ArrayList(); + byOwner.put(owner, held); + } + held.add(row); + } + for (int iter = 0; iter < rows.size(); iter++) { + CourierApplication row = rows.get(iter); + String name = names.get(row.getUsername()); + List held = byOwner.get(row.getUsername()); + out.add(toDto(row.getUsername(), name == null ? "" : name, row, + held == null ? new ArrayList() : held, 1)); + } + return out; + } + + /// How many applications are waiting for an admin's answer. + @Transactional(readOnly = true) + public int waiting() throws IOException { + return (int) applications.countByStatus(PENDING); + } + + /// An application for an admin: 404 for an account that never began one. + @Transactional(readOnly = true) + public CourierApplicationDto review(String username) throws IOException { + if (find(username) == null) { + throw new ResponseStatusException(404, "No such application"); + } + return get(username); + } + + /// Approves an application that was handed in: the account becomes a + /// courier, with the car on the application as the car customers look for. + @Transactional + public CourierApplicationDto approve(String by, String username) throws IOException { + // The account first, the lock blocking an account takes too: role() + // rewrites the whole user row, its enabled flag with it, so an approval + // that read the account before a block and wrote it after would undo + // the block. Behind the hold it reads what the block left. + profiles.hold(username); + decide(by, username, APPROVED, ""); + documents.markAll(username, "accepted"); + // Read after markAll, which empties the session. + CourierApplication application = find(username); + Profile profile = profiles.findById(username); + if (application.getVehicleMake().length() > 0 && profile != null) { + String car = (application.getVehicleColor() + " " + application.getVehicleMake() + " " + + application.getVehicleModel()).trim(); + profile.setVehicle(car.length() > 120 ? car.substring(0, 120) : car); + profile.setPlate(application.getVehiclePlate()); + } + role(username, true); + moderation.record(username, by, Moderation.APPROVE, ""); + return get(username); + } + + /// Rejects an application that was handed in, with the reason the + /// applicant is shown. + @Transactional + public CourierApplicationDto reject(String by, String username, String reason) + throws IOException { + String why = Text.required(reason, 255, "why it is rejected"); + decide(by, username, REJECTED, why); + moderation.record(username, by, Moderation.REJECT, why); + return get(username); + } + + /// Makes an account an approved courier without an application having been + /// handed in: the admin's own switch, and the demo accounts. Whatever + /// application the account has is marked approved, and one is made if it + /// has none, so that "approved" has one meaning everywhere. + @Transactional + public void grant(String by, String username) throws IOException { + if (find(username) == null) { + applications.insert(blank(username, APPROVED, "", "")); + return; + } + applications.approveUnlessApproved(username, APPROVED, System.currentTimeMillis(), by); + } + + /// Takes the approval back, when an admin withdraws the COURIER role. The + /// application is left rejected, so it can be corrected and handed in again. + @Transactional + public void withdraw(String by, String username) throws IOException { + applications.move(username, APPROVED, REJECTED, "Onboarding was withdrawn by an admin", + System.currentTimeMillis(), by); + } + + /// Everything kept about an account's application, for an account being + /// closed. + @Transactional + public void forget(String username) throws IOException { + documents.deleteByUsername(username); + applications.deleteByUsername(username); + } + + // ------------------------------------------------------------- internals + + /// The one conditional update behind both answers: it changes an + /// application only while it is waiting, so of two admins answering at + /// once one decides and the other is told it has been decided. + private void decide(String by, String username, String to, String reason) + throws IOException { + if (find(username) == null) { + throw new ResponseStatusException(404, "No such application"); + } + if (applications.move(username, PENDING, to, reason, System.currentTimeMillis(), by) + != 1) { + throw new ResponseStatusException(409, "That application is not waiting for an answer"); + } + } + + /// Grants or withdraws the COURIER role, leaving the account's other roles + /// as they are. + private void role(String username, boolean courier) { + if (!users.userExists(username)) { + throw new ResponseStatusException(404, "No such account"); + } + UserDetails signIn = users.loadUserByUsername(username); + String wanted = "ROLE_" + Roles.COURIER; + List held = new ArrayList(); + Iterator it = signIn.getAuthorities().iterator(); + while (it.hasNext()) { + String authority = it.next().getAuthority(); + if (!wanted.equals(authority)) { + held.add(authority); + } + } + if (courier) { + held.add(wanted); + } + users.updateUser(User.withUserDetails(signIn) + .authorities(held.toArray(new String[held.size()])).build()); + } + + /// The application, made as a draft if there is none, when the applicant + /// may still change it; 409 otherwise. + /// Takes turns with every other change to an account's application. One + /// that has none yet has no row to hold: the first save, upload or + /// hand-in makes it, and two at once would both insert one under the same + /// name, so they take turns on the profile instead. + /// + /// No profile to hold means Accounts.erase committed first, after + /// forget() had already deleted the application and its documents: going + /// on would make a new draft and store identity documents for an account + /// that no longer exists, which nothing would ever delete. 404 instead, as + /// PushDevices.register does. + private void hold(String username) { + if (applications.hold(username) == 0 && profiles.hold(username) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + } + + /// An account's application, or null when it has none. + private CourierApplication find(String username) { + if (username == null || username.length() == 0 || username.length() > 190) { + return null; + } + return applications.findById(username); + } + + private CourierApplication editable(String username) { + CourierApplication application = find(username); + if (application == null) { + application = blank(username, DRAFT, "", ""); + applications.insert(application); + } + if (PENDING.equals(application.getStatus())) { + throw new ResponseStatusException(409, "Your application is being reviewed"); + } + if (APPROVED.equals(application.getStatus())) { + throw new ResponseStatusException(409, "Your application was approved"); + } + return application; + } + + /// A new application with nothing filled in but what an older app + /// collected at registration. One made already approved was reviewed the + /// moment it was made. + private static CourierApplication blank(String username, String status, String vehicle, + String plate) { + long now = System.currentTimeMillis(); + CourierApplication application = new CourierApplication(); + application.setUsername(username); + application.setStatus(status); + application.setVehicleModel(vehicle.length() > 40 ? vehicle.substring(0, 40) : vehicle); + application.setVehiclePlate(plate); + application.setVehicleSeats(4); + application.setProduct(Fares.STANDARD); + application.setCreatedAt(now); + application.setReviewedAt(APPROVED.equals(status) ? now : 0L); + return application; + } + + private static void need(List missing, boolean present, String what) { + if (!present) { + missing.add(what); + } + } + + /// A date field: empty, or `yyyy-MM-dd`. + private static String date(String value, String what) { + String text = value == null ? "" : value.trim(); + if (text.length() > 0 && !Days.isIso(text)) { + throw new ResponseStatusException(400, "Write " + what + " as yyyy-MM-dd"); + } + return text; + } + + /// `documents` holds `{kind, status, uploadedAt}` from the index `from` on: + /// a list read for several accounts has the owner's name in front. + private static CourierApplicationDto toDto(String username, String name, + CourierApplication row, List documents, int from) { + List listed = new ArrayList(); + for (int kind = 0; kind < KINDS.length; kind++) { + String status = "missing"; + long uploadedAt = 0L; + for (int iter = 0; iter < documents.size(); iter++) { + Object[] stored = documents.get(iter); + if (KINDS[kind].equals(stored[from])) { + status = (String) stored[from + 1]; + Object at = stored[from + 2]; + uploadedAt = at instanceof Number ? ((Number) at).longValue() : 0L; + } + } + listed.add(new DocumentDto(KINDS[kind], status, uploadedAt)); + } + if (row == null) { + // Nobody has applied: an empty form, on the defaults it opens with. + return new CourierApplicationDto(username, name, NONE, "", "", "", "", "", "", 0, "", + "", 4, Fares.STANDARD, false, false, listed, "", 0L, 0L, ""); + } + return new CourierApplicationDto(username, name, row.getStatus(), row.getLegalName(), + row.getDateOfBirth(), row.getLicenceNumber(), row.getLicenceExpiry(), + row.getVehicleMake(), row.getVehicleModel(), row.getVehicleYear(), + row.getVehicleColor(), row.getVehiclePlate(), row.getVehicleSeats(), + row.getProduct(), row.isWheelchair(), row.isPets(), listed, + row.getRejectionReason(), row.getSubmittedAt(), row.getReviewedAt(), + row.getReviewedBy()); + } + + /// Width and height from a PNG's header chunk, or null without one. + static int[] pngSize(byte[] bytes) { + if (bytes.length < 24 || bytes[12] != 'I' || bytes[13] != 'H' || bytes[14] != 'D' + || bytes[15] != 'R') { + return null; + } + return new int[] {int32(bytes, 16), int32(bytes, 20)}; + } + + /// Width and height from a JPEG's frame header, or null when the scan + /// starts, or the bytes run out, before one. + static int[] jpegSize(byte[] bytes) { + int at = 2; + while (at + 3 < bytes.length) { + if ((bytes[at] & 0xff) != 0xff) { + return null; + } + int marker = bytes[at + 1] & 0xff; + if (marker == 0xff) { + // Fill before a marker. + at++; + continue; + } + if (marker == 0x01 || (marker >= 0xd0 && marker <= 0xd8)) { + // Markers that carry no length. + at += 2; + continue; + } + if (marker == 0xda || marker == 0xd9) { + return null; + } + int length = ((bytes[at + 2] & 0xff) << 8) | (bytes[at + 3] & 0xff); + boolean frame = marker >= 0xc0 && marker <= 0xcf && marker != 0xc4 + && marker != 0xc8 && marker != 0xcc; + if (frame) { + if (at + 8 >= bytes.length) { + return null; + } + int height = ((bytes[at + 5] & 0xff) << 8) | (bytes[at + 6] & 0xff); + int width = ((bytes[at + 7] & 0xff) << 8) | (bytes[at + 8] & 0xff); + return new int[] {width, height}; + } + if (length < 2) { + return null; + } + at += 2 + length; + } + return null; + } + + private static int int32(byte[] bytes, int at) { + return ((bytes[at] & 0xff) << 24) | ((bytes[at + 1] & 0xff) << 16) + | ((bytes[at + 2] & 0xff) << 8) | (bytes[at + 3] & 0xff); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/OnboardingEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/OnboardingEndpoint.java new file mode 100644 index 00000000000..167f9852fd8 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/onboarding/OnboardingEndpoint.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.onboarding; + +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.Caller; +import com.codenameone.examples.morsel.api.DocumentContentDto; +import com.codenameone.examples.morsel.api.DocumentUploadDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.api.OnboardingApiServer; + +/// The server's half of `OnboardingApi`. +/// +/// Open to anyone signed in, which is why its paths are not under +/// `/api/courier`: applying is what someone does before they hold the COURIER +/// role. What keeps one applicant out of another's application -- and out of +/// the identity documents in it -- is that no method here takes a name. Each +/// acts on the application of whoever is signed in, and there is no parameter +/// to change. +@Component +public class OnboardingEndpoint implements OnboardingApiServer { + private final Applications applications; + + public OnboardingEndpoint(Applications applications) { + this.applications = applications; + } + + @Override + public CourierApplicationDto application() throws Exception { + return applications.get(Caller.name()); + } + + @Override + public CourierApplicationDto save(CourierApplicationDto application) throws Exception { + return applications.save(Caller.name(), application); + } + + @Override + public CourierApplicationDto upload(DocumentUploadDto document) throws Exception { + return applications.upload(Caller.name(), document); + } + + @Override + public DocumentContentDto document(String kind) throws Exception { + return applications.document(Caller.name(), kind); + } + + @Override + public CourierApplicationDto submit() throws Exception { + return applications.submit(Caller.name()); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/MisconfiguredPayments.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/MisconfiguredPayments.java new file mode 100644 index 00000000000..0dfd750ba17 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/MisconfiguredPayments.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codenameone.examples.morsel.api.CardDto; + +import java.io.IOException; + +/// The payment provider a server gets when the one it was told to use cannot +/// be set up: every call fails, and says why. +/// +/// The problem is logged when the server starts and again on every call, +/// naming the settings to fix. Each call throws an `IOException`, as an outage +/// of the processor would, so a customer is told payments are unavailable and no +/// delivery is charged to a card nobody can reach. +public class MisconfiguredPayments implements PaymentProvider { + private final String provider; + private final String problem; + + /// @param provider the provider that was asked for, which is what [#name] + /// answers so that stored references keep naming it + /// @param problem what is wrong, naming the settings to fix + public MisconfiguredPayments(String provider, String problem) { + this.provider = provider; + this.problem = problem; + System.err.println("[config] " + problem); + } + + @Override + public String name() { + return provider; + } + + @Override + public String createCustomer(String username, String displayName) throws IOException { + throw fail(); + } + + @Override + public void forgetCustomer(String customer) throws IOException { + throw fail(); + } + + @Override + public Setup startCardSetup(String customer, String setupId) throws IOException { + throw fail(); + } + + @Override + public Card finishCardSetup(String customer, String reference, CardDto typed) + throws IOException { + throw fail(); + } + + @Override + public void detachCard(String customer, String token) throws IOException { + throw fail(); + } + + @Override + public String charge(String key, String customer, String token, long amountCents, + String currency, String description) throws IOException { + throw fail(); + } + + @Override + public Refund refund(String chargeReference, long amountCents) throws IOException { + throw fail(); + } + + @Override + public String refundStatus(String refundReference) throws IOException { + throw fail(); + } + + private IOException fail() { + System.err.println("[config] a payment call was refused: " + problem); + return new IOException(problem); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PayReturnPage.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PayReturnPage.java new file mode 100644 index 00000000000..2245692a6d9 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PayReturnPage.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.backend.HttpServer; +import com.codename1.backend.annotations.GetMapping; +import com.codename1.backend.annotations.RequestParam; +import com.codename1.backend.annotations.RestController; + +/// The page a hosted card setup ends on. +/// +/// The payment processor's page sends the browser here when the user has added +/// a card or given up. There is nothing to do here but say so: the app is what +/// finishes the setup, by asking the server to read it back, once the user +/// returns to it. That is also why the page needs no sign-in and takes nothing +/// from its address but which of two sentences to show -- being sent here +/// proves nothing and changes nothing. +@RestController +public class PayReturnPage { + @GetMapping("/pay/return") + public HttpServer.Response page( + @RequestParam(value = "result", required = false) String result) throws Exception { + boolean done = "done".equals(result); + String html = "" + + "" + + "Morsel" + + "

" + (done ? "Card added" : "No card was added") + "

" + + (done ? "You can close this page and go back to Morsel to finish." + : "You can close this page and go back to Morsel to try again.") + + "

"; + return new HttpServer.Response(200, "text/html; charset=utf-8", html.getBytes("UTF-8")); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentDeclinedException.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentDeclinedException.java new file mode 100644 index 00000000000..cd995852fb7 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentDeclinedException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import java.io.IOException; + +/// A card was refused, or a setup was not finished: the processor was asked +/// and said no. The message is written for the person paying and is safe to +/// show them -- unlike that of a plain `IOException` from a provider, which +/// describes a fault and goes to the log. +public class PaymentDeclinedException extends IOException { + public PaymentDeclinedException(String message) { + super(message); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentEndpoint.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentEndpoint.java new file mode 100644 index 00000000000..a7da42aa0c1 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentEndpoint.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.backend.annotations.Component; +import com.codenameone.examples.morsel.Caller; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.api.CardDto; +import com.codenameone.examples.morsel.api.PaymentApiServer; +import com.codenameone.examples.morsel.api.PaymentConfigDto; +import com.codenameone.examples.morsel.api.PaymentMethodDto; +import com.codenameone.examples.morsel.api.PaymentSetupDto; +import com.codenameone.examples.morsel.api.ReceiptDto; +import com.codenameone.examples.morsel.api.TipDto; +import com.codenameone.examples.morsel.delivery.Fares; + +import java.util.List; + +/// The server's half of `PaymentApi`. Every method acts as the signed-in user +/// and on that user's own cards and receipts: whose they are is never a +/// parameter, so there is no id to change to reach someone else's. +@Component +public class PaymentEndpoint implements PaymentApiServer { + private final Payments payments; + private final Accounts accounts; + private final Fares fares; + + public PaymentEndpoint(Payments payments, Accounts accounts, Fares fares) { + this.payments = payments; + this.accounts = accounts; + this.fares = fares; + } + + @Override + public PaymentConfigDto config() throws Exception { + Caller.name(); + return payments.config(fares.currency()); + } + + @Override + public List methods() throws Exception { + return payments.methods(Caller.name()); + } + + @Override + public PaymentSetupDto startSetup() throws Exception { + String who = Caller.name(); + return payments.startSetup(who, accounts.describe(who).displayName()); + } + + @Override + public PaymentMethodDto completeSetup(String id, CardDto card) throws Exception { + return payments.completeSetup(Caller.name(), id, card); + } + + @Override + public PaymentMethodDto makeDefault(String id) throws Exception { + return payments.makeDefault(Caller.name(), id); + } + + @Override + public PaymentMethodDto remove(String id) throws Exception { + return payments.remove(Caller.name(), id); + } + + @Override + public List receipts() throws Exception { + return payments.receipts(Caller.name()); + } + + @Override + public ReceiptDto receipt(String deliveryId) throws Exception { + return payments.receipt(Caller.name(), false, deliveryId); + } + + @Override + public ReceiptDto tip(String id, TipDto tip) throws Exception { + return payments.tip(Caller.name(), id, tip == null ? 0L : tip.amount()); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentProvider.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentProvider.java new file mode 100644 index 00000000000..5faf1787af7 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/PaymentProvider.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codenameone.examples.morsel.api.CardDto; + +import java.io.IOException; + +/// A payment processor. The one place the server touches one. +/// +/// The shape is the one real processors share. A customer is a *customer* of the +/// processor; a card is saved to that customer once, in a *setup*, and comes +/// back as a token; from then on the server charges the token with nobody +/// present, which is what lets a delivery be paid for as it ends. The server keeps +/// the token, a brand and four digits. It never keeps a card number, and with +/// a hosted setup it never sees one. +// tag::morsel-payment-provider[] +public interface PaymentProvider { + /// `simulated` or `stripe`: what the app is told, so it knows how to add a + /// card. + String name(); + + /// Opens the processor's record of a customer and returns its reference. + String createCustomer(String username, String displayName) throws IOException; + + /// Deletes the processor's record of a customer, and the cards saved to it + /// with it. A record that is already gone is deleted: this is asked again + /// when the first answer was lost. + void forgetCustomer(String customer) throws IOException; + + /// Begins saving a card to `customer`. `setupId` is the server's own id + /// for the attempt, for the processor to hand back. + Setup startCardSetup(String customer, String setupId) throws IOException; + + /// Finishes a setup and returns the card it saved. + /// + /// @param reference what [#startCardSetup] returned as the setup's reference + /// @param typed the card as typed into the app, for a processor whose setup + /// is not hosted; ignored by one whose setup is + /// - `PaymentDeclinedException`: when the card was refused or the setup was + /// not finished + Card finishCardSetup(String customer, String reference, CardDto typed) throws IOException; + + /// Takes a saved card off `customer`, so that it can't be charged again. + /// A card that is already off, or gone, is detached: this is asked again + /// when the first answer was lost. + /// + /// @param token what [#finishCardSetup] returned as the card's token + void detachCard(String customer, String token) throws IOException; + + /// Charges a saved card with its owner absent. + /// + /// @param key names this charge and no other. A processor asked twice with + /// the same key charges once, so a request repeated after a timeout + /// cannot take the money twice. + /// @return the processor's reference for the charge + /// - `PaymentDeclinedException`: when the card was refused + /// - `IOException`: when the processor could not be asked + String charge(String key, String customer, String token, long amountCents, String currency, + String description) throws IOException; + + /// Returns `amountCents` of a charge to the card it came from. + /// + /// A processor may take the refund and only later say whether the bank + /// returned the money: such a refund comes back [Refund#pending], and is + /// asked about with [#refundStatus] until it has an answer. + /// + /// @return the refund as the processor took it + /// - `IOException`: when the processor could not be asked, or refused the + /// refund outright + Refund refund(String chargeReference, long amountCents) throws IOException; + + /// What became of a refund that was [Refund#pending]: [#REFUND_SUCCEEDED], + /// [#REFUND_PENDING] while the processor still does not know, or + /// [#REFUND_FAILED] when the money did not go back. + String refundStatus(String refundReference) throws IOException; + // end::morsel-payment-provider[] + + String REFUND_SUCCEEDED = "succeeded"; + String REFUND_PENDING = "pending"; + String REFUND_FAILED = "failed"; + + /// A refund the processor took. + final class Refund { + public String reference = ""; + /// Whether the money is not yet known to be back on the card. + public boolean pending; + } + + /// A setup under way. + final class Setup { + /// Whether the card is typed into the processor's own page, at `url`. + public boolean hosted; + public String url = ""; + /// What to hand to [PaymentProvider#finishCardSetup]. + public String reference = ""; + } + + /// A saved card: what may be shown of it, and the token it is charged by. + final class Card { + public String brand = ""; + public String last4 = ""; + public int expMonth; + public int expYear; + public String token = ""; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/Payments.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/Payments.java new file mode 100644 index 00000000000..cf008cfd890 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/Payments.java @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Scheduled; +import com.codename1.backend.annotations.Transactional; +import com.codename1.orm.repository.PageRequest; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.CardDto; +import com.codenameone.examples.morsel.api.PaymentConfigDto; +import com.codenameone.examples.morsel.api.PaymentMethodDto; +import com.codenameone.examples.morsel.api.PaymentSetupDto; +import com.codenameone.examples.morsel.api.ReceiptDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.Payment; +import com.codenameone.examples.morsel.domain.PaymentCustomer; +import com.codenameone.examples.morsel.domain.PaymentMethod; +import com.codenameone.examples.morsel.domain.PaymentSetup; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.live.LiveHub; +import com.codenameone.examples.morsel.repository.PaymentCustomerRepository; +import com.codenameone.examples.morsel.repository.PaymentMethodRepository; +import com.codenameone.examples.morsel.repository.PaymentRepository; +import com.codenameone.examples.morsel.repository.PaymentSetupRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// The ways a customer can pay, and what each delivery came to. +/// +/// A delivery is paid for when it completes: the fare and the service fee, charged +/// to the method the delivery was asked for with. A tip is a second charge, made +/// when the customer leaves one. A delivery paid in cash is recorded and nothing is +/// charged. What the courier earns from all this is `courier/Earnings`' business. +/// +/// Everything here that takes a `username` acts on that user's own methods and +/// deliveries and no one else's; the endpoints pass the signed-in user. +/// +/// The processor is somewhere else, and a call to it can take seconds, so none +/// is made with a transaction open. Whatever talks to it is three steps: a +/// transaction that reads, or claims, what the call needs and commits; the +/// call; and a transaction that records what came of it, or gives the claim +/// back. +@Component +public class Payments { + public static final String PENDING = "PENDING"; + /// How long a completed delivery's charge goes on being asked for. Short of + /// the day a processor remembers the key a charge was first sent under. + public static final long RETRY_MILLIS = 23L * 60L * 60L * 1000L; + public static final String PAID = "PAID"; + public static final String FAILED = "FAILED"; + public static final String REFUNDED = "REFUNDED"; + /// Refunded at the processor, which has not yet said that the money is + /// back on the card. See [#settleRefunds]. + public static final String REFUND_PENDING = "REFUND_PENDING"; + /// A refund the processor took and then failed or canceled: the customer has + /// not had the money back. + public static final String REFUND_FAILED = "REFUND_FAILED"; + /// The id and the label of the method every account has. + public static final String CASH = "cash"; + public static final String CASH_LABEL = "Cash"; + private static final int MAX_CARDS = 8; + /// A setup not finished within this long is abandoned. + private static final long SETUP_MILLIS = 3600000L; + /// How many setups an account may have open at once. Each is a session + /// made at the processor and a row here; someone adding a card has one, + /// or two after giving up on the first. + private static final int MAX_OPEN_SETUPS = 5; + private static final long MAX_TIP_CENTS = 20000L; + /// The states of a delivery that has not ended. + private static final DeliveryState[] UNDER_WAY = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS}; + + private final PaymentMethodRepository cards; + private final PaymentSetupRepository setups; + private final PaymentCustomerRepository customers; + private final PaymentRepository payments; + private final DeliveryRepository deliveries; + private final ProfileRepository profiles; + private final PaymentProvider provider; + private final LiveHub live; + + public Payments(PaymentMethodRepository cards, PaymentSetupRepository setups, + PaymentCustomerRepository customers, PaymentRepository payments, + DeliveryRepository deliveries, ProfileRepository profiles, PaymentProvider provider, + LiveHub live) { + this.cards = cards; + this.setups = setups; + this.customers = customers; + this.payments = payments; + this.deliveries = deliveries; + this.profiles = profiles; + this.provider = provider; + this.live = live; + } + + public PaymentConfigDto config(String currency) { + return new PaymentConfigDto(provider.name(), currency); + } + + // --------------------------------------------------------------- methods + + /// The saved cards, oldest first, then cash. Exactly one of them is the + /// default: a card when one is marked, cash otherwise. + @Transactional(readOnly = true) + public List methods(String username) throws IOException { + List rows = cards.findByUsernameOrderByCreatedAtAscIdAsc(username); + List out = new ArrayList(); + boolean marked = false; + for (int iter = 0; iter < rows.size(); iter++) { + PaymentMethodDto dto = card(rows.get(iter)); + marked |= dto.isDefault(); + out.add(dto); + } + out.add(cash(!marked)); + return out; + } + + /// The id of the user's default method. + @Transactional(readOnly = true) + public String defaultMethod(String username) throws IOException { + PaymentMethod row = cards.findFirstByUsernameAndPreferredTrue(username); + return row == null ? CASH : row.getId(); + } + + /// Begins adding a card. + public PaymentSetupDto startSetup(String username, String displayName) throws IOException { + String customer = customerForSetup(username); + String id = Ids.next(); + PaymentProvider.Setup setup; + try { + if (customer == null) { + String created = provider.createCustomer(username, displayName); + customer = keepCustomer(username, created); + if (!created.equals(customer)) { + // Not kept: the account was closed while the processor was + // asked (its clean-up found no customer to delete, so this + // one is ours to delete), or another request kept its own + // first. Either way the record made here would otherwise + // stay at the processor with the customer's name on it. + forgetQuietly(created); + } + if (customer == null) { + throw new ResponseStatusException(404, "No such account"); + } + } + setup = provider.startCardSetup(customer, id); + } catch (IOException failed) { + throw unavailable("card setup", failed); + } + keepSetup(username, id, setup.reference); + return new PaymentSetupDto(id, setup.hosted, setup.url); + } + + /// The customer's reference at the processor, or null when they have none + /// yet. Answers 409 for an account that has all the cards it may, and + /// 429 for one with as many setups open as it may have. + @Transactional(readOnly = true) + private String customerForSetup(String username) { + if (cards.countByUsername(username) >= MAX_CARDS) { + throw new ResponseStatusException(409, "Remove a card before adding another"); + } + // Refused before the processor is asked: every setup is a session + // made there, so a loop of these would otherwise make sessions + // without end in the service's name. + refuseTooManySetups(username); + PaymentCustomer row = customers.findById(username); + return row == null ? null : row.getReference(); + } + + /// Answers 429 when the account already has [#MAX_OPEN_SETUPS] setups + /// that are neither finished nor abandoned. + private void refuseTooManySetups(String username) { + if (setups.countByUsernameAndCompletedFalseAndCreatedAtGreaterThan(username, + System.currentTimeMillis() - SETUP_MILLIS) >= MAX_OPEN_SETUPS) { + throw new ResponseStatusException(429, + "Too many cards are being added. Finish one, or try again later"); + } + } + + /// Deletes the setups nobody can use any more: the finished ones, whose + /// card is saved, and the abandoned ones. Without it every setup ever + /// started would stay, one row for each tap of "Add card". + /// + /// An abandoned setup is kept for twice the time it can be finished in. + /// One finished just inside the hour is still at the processor when the + /// hour passes, and its row is what saves the card when the answer comes. + @Scheduled(fixedDelay = 600000, initialDelay = 60000, lock = "morsel-card-setups") + @Transactional + public void sweepSetups() throws IOException { + setups.deleteByCompletedTrue(); + setups.deleteByCreatedAtLessThan(System.currentTimeMillis() - 2L * SETUP_MILLIS); + } + + /// Keeps the reference the processor made for a customer on first use, and + /// returns the one the account has: another request may have kept its own + /// while this one was asking for it. Null when the account is gone. + @Transactional + private String keepCustomer(String username, String reference) { + // Two first cards started together both find no row. Taking turns on + // the account lets the second see the first's row and use it, where + // it would otherwise insert a second and fail on the key. With no + // profile to hold the account was closed meanwhile, and its clean-up + // has already run: a row kept now would never be deleted. + if (profiles.hold(username) == 0) { + return null; + } + PaymentCustomer row = customers.findById(username); + if (row == null) { + row = new PaymentCustomer(); + row.setUsername(username); + row.setReference(reference); + customers.insert(row); + } + return row.getReference(); + } + + @Transactional + private void keepSetup(String username, String id, String reference) { + // As in keepCustomer: an account closed while the processor was asked + // gets no row its clean-up has already missed. + if (profiles.hold(username) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + // Counted again with the account held: several requests sent together + // all pass the count made before the processor was asked. + // + // The session a refusal here leaves at the processor is not cleaned + // up, on purpose. Its address is never answered, so nobody can open + // it; a card entered there anyway could not be saved, because saving + // claims this row and there is none; and the processor expires an + // unfinished session by itself. Only the requests already past the + // first count when the limit was reached can leave one, so a loop + // of them still makes no more than a handful. + refuseTooManySetups(username); + PaymentSetup row = new PaymentSetup(); + row.setId(id); + row.setUsername(username); + row.setReference(reference); + row.setCreatedAt(System.currentTimeMillis()); + row.setCompleted(false); + setups.insert(row); + } + + /// Finishes adding a card and returns it. The first card an account saves + /// becomes its default: someone who adds a card means to pay with it. + /// Answers 402 when the card was refused, with the reason. + public PaymentMethodDto completeSetup(String username, String setupId, CardDto typed) + throws IOException { + String[] references = openSetup(username, setupId); + PaymentProvider.Card card; + try { + card = provider.finishCardSetup(references[0], references[1], typed); + } catch (PaymentDeclinedException declined) { + throw new ResponseStatusException(402, declined.getMessage()); + } catch (IOException failed) { + throw unavailable("card setup", failed); + } + try { + return keepCard(username, setupId, Ids.next(), card); + } catch (ResponseStatusException refused) { + // The processor attached the card when the setup finished. Refused + // here by the card limit -- two setups finishing together for the + // last slot, or a slot filled meanwhile -- it has no row, so the + // customer has nothing to remove it through: it is detached here + // instead. keepCard has rolled back by now, so this runs outside + // its transaction and nothing undoes it. + // + // Undone after the fact rather than a slot reserved before the + // processor is asked: a reservation is one more row with a life of + // its own, given back when the processor declines, times out or + // the server stops between the two, and swept when nothing gives + // it back. A refusal is the rare case, and this costs only it. + // + // Only the limit's 409. The 404 is a setup claimed by a request + // sent twice, whose other copy may have saved this very card. + if (refused.getStatus() == 409) { + discard(username, references[0], card); + } + throw refused; + } + } + + /// Detaches a card the processor attached that this server did not keep. + /// A failure is logged and not answered: the customer's answer is the refusal + /// that brought us here, and the card is no worse off than before. + private void discard(String username, String customer, PaymentProvider.Card card) { + try { + provider.detachCard(customer, card.token); + } catch (IOException failed) { + System.err.println("a card refused for " + username + " is still attached at the " + + "processor, as " + card.token + ": " + failed.getMessage()); + } + } + + /// What the processor calls the customer and a setup of theirs that is still + /// open, in that order. Answers 404 for a setup that is not. + @Transactional(readOnly = true) + private String[] openSetup(String username, String setupId) { + PaymentSetup setup = setupId == null || setupId.length() == 0 || setupId.length() > 32 + ? null : setups.findFirstByIdAndUsername(setupId, username); + if (setup == null || setup.isCompleted() + || setup.getCreatedAt() < System.currentTimeMillis() - SETUP_MILLIS) { + throw new ResponseStatusException(404, "No such card setup"); + } + PaymentCustomer customer = customers.findById(username); + return new String[] {customer == null ? "" : customer.getReference(), setup.getReference()}; + } + + /// Closes the setup and saves its card, together or not at all. + @Transactional + private PaymentMethodDto keepCard(String username, String setupId, String id, + PaymentProvider.Card card) { + // Claimed with a conditional update, so a request sent twice saves the + // card once. + if (setups.claim(setupId) != 1) { + throw new ResponseStatusException(404, "No such card setup"); + } + // Counted again here, where the card is saved: starting a setup checks + // the limit too, but several can be started while there is room for + // one. The account's row at the processor is written first, so two + // setups finishing together take turns and the second counts the + // first one's card. Refusing rolls the claim back with everything else. + customers.hold(username); + if (cards.countByUsername(username) >= MAX_CARDS) { + throw new ResponseStatusException(409, "Remove a card before adding another"); + } + boolean first = cards.findFirstByUsernameAndPreferredTrue(username) == null; + // Cards are listed in the order they were added, and two added in the + // same millisecond would have no order: the later is stamped after + // the one before it. + Long newest = cards.newestAt(username); + long after = newest == null ? 0L : newest.longValue() + 1L; + PaymentMethod row = new PaymentMethod(); + row.setId(id); + row.setUsername(username); + row.setBrand(cut(card.brand, 24)); + row.setLast4(cut(card.last4, 4)); + row.setExpMonth(card.expMonth); + row.setExpYear(card.expYear); + row.setToken(card.token); + row.setPreferred(first); + row.setCreatedAt(Math.max(System.currentTimeMillis(), after)); + cards.insert(row); + return card(row); + } + + /// Makes a method the one deliveries are paid with unless they say otherwise. + @Transactional + public PaymentMethodDto makeDefault(String username, String id) throws IOException { + // One at a time for an account: two choices made together would each + // clear the cards it had read and mark its own, and leave two marked. + profiles.hold(username); + PaymentMethod chosen = CASH.equals(id) ? null : owned(username, id); + // The cards are few, and each is written only if its mark changed. + List rows = cards.findByUsernameOrderByCreatedAtAscIdAsc(username); + for (int iter = 0; iter < rows.size(); iter++) { + PaymentMethod row = rows.get(iter); + row.setPreferred(chosen != null && row.getId().equals(chosen.getId())); + } + if (chosen == null) { + return cash(true); + } + chosen.setPreferred(true); + return card(chosen); + } + + /// Forgets a card, here and at the processor. Cash cannot be removed, and + /// neither can the card a delivery under way is to be paid with. + /// + /// The card is detached at the processor before its row is deleted, and + /// inside the transaction that deletes it, as [#forget] deletes a + /// customer: the token is the only way back to the card, so a processor + /// that cannot be reached fails the removal and the card stays, to be + /// removed again. Detaching a card that is already detached succeeds, so + /// a removal whose commit failed after the processor answered finishes + /// the second time. The account's row is held across the request, which + /// holds back only that customer's own delivery requests, and removing a card is + /// rare enough for that. + @Transactional + public PaymentMethodDto remove(String username, String id) throws IOException { + if (CASH.equals(id)) { + throw new ResponseStatusException(400, "Cash is always available"); + } + // Asking for a delivery holds the same row before it picks its card. So a + // delivery and a removal sent together take turns: either the delivery is + // there to be seen below, or it no longer finds the card. + profiles.hold(username); + PaymentMethod row = owned(username, id); + // What is answered is the card as it was, no longer anyone's default. + PaymentMethodDto dto = card(row, false); + if (deliveries.existsByCustomerAndMethodIdAndStateIn(username, id, UNDER_WAY)) { + throw new ResponseStatusException(409, "That card is paying for a delivery under way"); + } + // A delivery that ended with nobody able to say whether its card was + // charged is asked about again, by the card's token. With the card + // gone the question could not be put: the delivery would be written down + // as unpaid, whether or not the first attempt took the money. + if (awaitsCharge(username, id, retriedSince())) { + throw new ResponseStatusException(409, + "That card is still paying for a delivery. Try again in a few minutes"); + } + PaymentCustomer customer = customers.findById(username); + if (customer != null && customer.getReference().length() > 0) { + try { + provider.detachCard(customer.getReference(), row.getToken()); + } catch (IOException failed) { + // A runtime exception, so the transaction rolls back with it. + throw unavailable("card removal", failed); + } + } + cards.deleteById(row.getId()); + return dto; + } + + /// The method a delivery is asked for with: the one named, or the default when + /// none is. Answers 400 for a method that is not the customer's. + /// + /// @return the method's id and its label, in that order + @Transactional(readOnly = true) + public String[] choose(String username, String wanted) throws IOException { + String id = wanted == null || wanted.trim().length() == 0 ? defaultMethod(username) + : wanted.trim(); + if (CASH.equals(id)) { + return new String[] {CASH, CASH_LABEL}; + } + PaymentMethod row = card(username, id); + if (row == null) { + throw new ResponseStatusException(400, "Choose a way to pay"); + } + return new String[] {id, label(row)}; + } + + /// Everything of a user's that this class keeps, for an account being + /// closed, here and at the processor. Answers 409 while a delivery of theirs + /// is still to be charged; see [#remove]. + /// + /// The processor is asked last, and inside the transaction that deletes + /// the rest. It holds the customer's address, their name and their cards, and + /// the reference deleted here is the only way back to them: forgotten + /// first, a request that then failed would leave that record with nothing + /// pointing at it. So a processor that cannot be reached fails the closure + /// whole and the account stays as it was, to be closed again. The other + /// way round -- the processor has deleted and the commit then fails -- is + /// mended by the same second attempt, since a customer already gone counts + /// as deleted. A closure is rare enough to wait on a request with a row + /// held, which nothing else in this class does. + @Transactional(rollbackFor = IOException.class) + public void forget(String username) throws IOException { + // Only charges still being retried hold a closure up. One left + // pending past the retry window has already been handed to a person + // to settle against the processor's own records, which keep the + // charge and the card used after the customer is deleted here. + // Counting it would refuse the closure for good over something the + // customer can do nothing about. + if (awaitsCharge(username, null, retriedSince())) { + throw new ResponseStatusException(409, + "A delivery is still being paid for. Try again in a few minutes"); + } + PaymentCustomer customer = customers.findById(username); + String reference = customer == null ? "" : customer.getReference(); + cards.deleteByUsername(username); + setups.deleteByUsername(username); + customers.deleteByUsername(username); + if (reference != null && reference.length() > 0) { + provider.forgetCustomer(reference); + } + } + + private static long retriedSince() { + return System.currentTimeMillis() - RETRY_MILLIS; + } + + // --------------------------------------------------------------- charges + + /// Settles a delivery that has just completed and returns [#PAID], [#FAILED], + /// or [#PENDING] when nobody can say yet. + /// A delivery is settled once: its payment is keyed by the delivery, and a delivery + /// completes once. Called with no transaction open, since the card is + /// charged here; what came of the charge is then written in one. + /// + /// Safe to ask for again, which is how a delivery whose settlement was cut + /// short is finished later: the charge carries the delivery as its key, so + /// the processor answers a second request with the first one's charge, and + /// a payment already written is the answer without asking anyone. + /// + /// Only a card the processor refused is a payment that failed. Any other + /// failure -- the processor not reached, its answer lost on the way back -- + /// says nothing about whether the card was charged, so nothing is written + /// and the delivery stays pending for `Deliveries.settleLeftBehind` to ask again + /// under the same key. Written down as failed, a charge that did go through + /// would never be looked for, and the customer would have paid for a delivery + /// that says they did not. + public String settle(Delivery delivery) throws IOException { + Payment kept = payment(delivery.getId()); + if (kept != null) { + return kept.getStatus(); + } + long amount = delivery.getFareCents() + delivery.getFeeCents(); + String status = PAID; + String reference = CASH; + if (!CASH.equals(delivery.getMethodId())) { + try { + reference = charge("delivery-" + delivery.getId(), delivery.getCustomer(), delivery.getMethodId(), amount, + delivery.getCurrency(), "Morsel delivery"); + } catch (PaymentDeclinedException declined) { + // The delivery and the amount, never the card. + System.err.println("payment for delivery " + delivery.getId() + " failed: " + + declined.getMessage()); + status = FAILED; + reference = ""; + } catch (IOException unknown) { + System.err.println("payment for delivery " + delivery.getId() + " is not known yet: " + + unknown.getMessage()); + return PENDING; + } + } + keepPayment(delivery.getId(), amount, delivery.getCurrency(), status, reference); + return status; + } + + @Transactional + private void keepPayment(String deliveryId, long amount, String currency, String status, + String reference) { + if (payments.findById(deliveryId) != null) { + // Written while this request was at the processor. + return; + } + Payment row = new Payment(); + row.setDeliveryId(deliveryId); + row.setAmountCents(amount); + row.setCurrency(currency); + row.setStatus(status); + row.setProviderRef(reference); + row.setCreatedAt(System.currentTimeMillis()); + payments.insert(row); + } + + /// Tips the courier of a completed delivery, once. Charged to the method the + /// delivery was paid with; a cash delivery's tip is recorded as given in cash. + /// + /// The tip is taken off the delivery again only when the card was refused. A + /// charge that failed any other way may have gone through, so the tip + /// stays on the delivery with no charge recorded for it: the customer is told to + /// try again, and trying again with the same amount asks the processor + /// under the same key, which answers with the first charge if there was + /// one. Until then the delivery cannot be refunded -- see [#claimRefund]. + public ReceiptDto tip(String customer, String deliveryId, long amountCents) throws IOException { + Delivery delivery = claimTip(customer, deliveryId, amountCents); + if (!CASH.equals(delivery.getMethodId())) { + String reference; + try { + reference = charge("tip-" + deliveryId, customer, delivery.getMethodId(), amountCents, + delivery.getCurrency(), "Morsel tip"); + } catch (PaymentDeclinedException declined) { + releaseTip(deliveryId); + throw new ResponseStatusException(402, declined.getMessage()); + } catch (IOException unknown) { + throw unavailable("tip", unknown); + } + keepTip(deliveryId, reference); + } + // The courier's earnings changed; their app reads them again. + live.deliveryChanged(deliveryId, delivery.getState(), customer, delivery.getCourier(), ""); + return receipt(customer, false, deliveryId); + } + + /// Sets the tip on the customer's delivery and returns the delivery as it was just + /// before. Committed before the card is charged. + @Transactional + private Delivery claimTip(String customer, String deliveryId, long amountCents) { + Delivery delivery = delivery(deliveryId); + if (delivery == null || !customer.equals(delivery.getCustomer())) { + throw new ResponseStatusException(404, "No such delivery"); + } + if (amountCents < 1L || amountCents > MAX_TIP_CENTS) { + throw new ResponseStatusException(400, "That is not an amount for a tip"); + } + // Not for a courier who has closed the account: the tip would be + // credited to a name nobody can sign in as or cash out. A closure can + // still land while this tip's charge is at the processor, since the + // balance counts a tip only once its charge is recorded; the courier + // would have to close in that second or two, just after cashing out. + // Holding closures until every tip in flight settles would need a + // state of its own, and one that never settles would hold the + // account open for good. + String courier = delivery.getCourier(); + if (courier == null || courier.length() == 0 || profiles.findById(courier) == null) { + throw new ResponseStatusException(409, "That courier can no longer be tipped"); + } + // Claimed before the charge: of two requests, one changes the row and + // only that one charges. + if (deliveries.claimTip(deliveryId, customer, amountCents) != 1 && !tipLeftUnknown(delivery, amountCents)) { + throw new ResponseStatusException(409, "That delivery cannot be tipped now"); + } + return delivery; + } + + /// Whether the delivery already carries this very tip with no charge recorded + /// for it: one whose charge was asked for and never answered. Asking again + /// is safe only for the same amount, the key being the delivery's. + private boolean tipLeftUnknown(Delivery delivery, long amountCents) { + if (delivery.getTipCents() != amountCents || CASH.equals(delivery.getMethodId()) + || !PAID.equals(delivery.getPaymentStatus())) { + return false; + } + Payment payment = payments.findById(delivery.getId()); + return payment != null && (payment.getTipRef() == null || payment.getTipRef().length() == 0); + } + + @Transactional + private void releaseTip(String deliveryId) { + deliveries.releaseTip(deliveryId); + } + + @Transactional + private void keepTip(String deliveryId, String reference) { + Payment payment = payments.findById(deliveryId); + if (payment != null) { + payment.setTipRef(reference); + } + } + + /// Returns what a delivery cost to the card that paid for it, tip included. + /// For an admin. A cash delivery has nothing to return, and a delivery is refunded + /// once. + public ReceiptDto refund(String deliveryId, String reason) throws IOException { + Delivery delivery = claimRefund(deliveryId); + Payment payment = payment(deliveryId); + String why = cut(reason == null ? "" : reason.trim(), 255); + PaymentProvider.Refund fare; + PaymentProvider.Refund tip = null; + try { + fare = provider.refund(payment.getProviderRef(), payment.getAmountCents()); + } catch (IOException failed) { + releaseRefund(deliveryId); + throw unavailable("refund", failed); + } + if (payment.getTipRef().length() > 0) { + try { + tip = provider.refund(payment.getTipRef(), delivery.getTipCents()); + } catch (IOException failed) { + // The fare is back with the customer already. Paid again, the + // delivery would show that money as paid and its courier could cash + // it out; so the fare's refund is kept and the payment marked + // failed, which is what it is until a person with the + // processor's records settles the tip -- see settleRefunds. + keepPartialRefund(deliveryId, fare, why); + throw unavailable("tip refund", failed); + } + } + keepRefund(deliveryId, fare, tip, why); + live.deliveryChanged(deliveryId, delivery.getState(), delivery.getCustomer(), delivery.getCourier(), ""); + return receipt("", true, deliveryId); + } + + /// Asks the processor how each refund it took and had not finished came + /// out, and records the ones that have an answer. + /// + /// A processor can take a refund and only say later whether the bank + /// returned the money; Stripe answers such a refund `pending`, and it can + /// still end `failed` or `canceled`. Recorded as refunded at once, a + /// refund that then failed would show the customer and the admin money that + /// never came back, and nothing would look at it again. So it is kept as + /// [#REFUND_PENDING] and asked about here until it ends: [#REFUNDED] once + /// the fare's refund and the tip's have both succeeded, [#REFUND_FAILED] + /// as soon as either has not. A failed refund is not tried again from + /// here. Part of the money may have gone back, and the processor may + /// answer a repeat with the refund that failed, so what to do about it is + /// for a person with the processor's records. + /// + /// Not a transaction itself, as `Deliveries.settleLeftBehind` is not: the + /// processor is asked with none open, each refund is recorded in one of + /// its own, and what went wrong with the first refund that could not be + /// asked about is reported once the rest have been. + @Scheduled(fixedDelay = 60000, initialDelay = 45000, lock = "morsel-refunds") + public void settleRefunds() throws IOException { + List pending = refundsPending(); + Exception failed = null; + for (int iter = 0; iter < pending.size(); iter++) { + Payment payment = pending.get(iter); + try { + String outcome = refundOutcome(payment); + Delivery delivery = outcome == null ? null : endRefund(payment.getDeliveryId(), outcome); + if (delivery != null) { + live.deliveryChanged(delivery.getId(), delivery.getState(), delivery.getCustomer(), delivery.getCourier(), ""); + } + } catch (IOException err) { + failed = failed == null ? err : failed; + } catch (RuntimeException err) { + failed = failed == null ? err : failed; + } + } + if (failed instanceof IOException) { + throw (IOException) failed; + } + if (failed instanceof RuntimeException) { + throw (RuntimeException) failed; + } + } + + /// How a pending refund came out: [#REFUNDED], [#REFUND_FAILED], or null + /// while the processor still does not know. + private String refundOutcome(Payment payment) throws IOException { + String fare = provider.refundStatus(payment.getRefundRef()); + String tip = payment.getTipRefundRef().length() == 0 ? PaymentProvider.REFUND_SUCCEEDED + : provider.refundStatus(payment.getTipRefundRef()); + if (PaymentProvider.REFUND_FAILED.equals(fare) + || PaymentProvider.REFUND_FAILED.equals(tip)) { + return REFUND_FAILED; + } + if (PaymentProvider.REFUND_SUCCEEDED.equals(fare) + && PaymentProvider.REFUND_SUCCEEDED.equals(tip)) { + return REFUNDED; + } + return null; + } + + @Transactional(readOnly = true) + private List refundsPending() { + return payments.findByStatusOrderByCreatedAtAsc(REFUND_PENDING, new PageRequest(0, 50)); + } + + /// Records how a refund ended and returns its delivery, or null when another + /// pass recorded it first. + @Transactional + private Delivery endRefund(String deliveryId, String outcome) { + if (payments.endRefund(deliveryId, outcome, + REFUNDED.equals(outcome) ? System.currentTimeMillis() : 0L) != 1) { + return null; + } + deliveries.paymentStatus(deliveryId, outcome); + return delivery(deliveryId); + } + + /// Marks the delivery refunded and returns it as it was just before. Committed + /// before the processor is asked for the money: of two requests, one + /// changes the row and only that one refunds. + /// + /// The update comes first and the delivery is read after it. A tip is claimed + /// by an update of the same row, so the two take turns, and the delivery read + /// here says whether a tip got in before this did. One that did and has no + /// charge recorded yet is still at the processor: refunding now would + /// return the fare, miss the tip, and leave a tip charged to a refunded + /// delivery. The refund is refused until the tip has settled either way, and + /// a tip asked for after this commits finds the delivery no longer paid. + /// + /// A tip whose charge never reports back keeps its delivery from being + /// refunded here. That is deliberate: nobody knows whether that card was + /// charged, and it takes a person and the processor's records to say. + @Transactional + private Delivery claimRefund(String deliveryId) { + boolean claimed = deliveries.claimRefund(deliveryId) == 1; + Delivery delivery = delivery(deliveryId); + if (delivery == null) { + throw new ResponseStatusException(404, "No such delivery"); + } + if (CASH.equals(delivery.getMethodId())) { + throw new ResponseStatusException(409, "A delivery paid in cash has nothing to refund"); + } + if (REFUND_PENDING.equals(delivery.getPaymentStatus())) { + throw new ResponseStatusException(409, + "That delivery's refund is still with the payment processor"); + } + if (REFUND_FAILED.equals(delivery.getPaymentStatus())) { + throw new ResponseStatusException(409, + "That delivery's refund failed. Check it with the payment processor"); + } + if (!claimed && !refundUnfinished(delivery)) { + throw new ResponseStatusException(409, "That delivery has no payment to refund"); + } + if (delivery.getTipCents() > 0L) { + Payment payment = payments.findById(deliveryId); + if (payment == null || payment.getTipRef() == null || payment.getTipRef().length() == 0) { + throw new ResponseStatusException(409, + "A tip on that delivery is still being charged. Try again in a moment"); + } + } + return delivery; + } + + /// Whether `delivery` was marked refunded by a request that never recorded + /// the refund: the server stopped between asking the processor and + /// writing the answer down, so neither [#keepRefund] nor [#releaseRefund] + /// ran. Such a delivery would otherwise be refunded in name only and refused + /// for good. It is refunded again instead, which is safe because a refund + /// is keyed by the charge it returns: the processor answers a repeat with + /// the refund it already made, and makes one if it never did. + /// + /// It cannot tell such a delivery from one whose refund is still at the + /// processor, so it also lets a second request through while the first + /// is under way. That is safe for the same reason, and [#releaseRefund] + /// and [#keepRefund] keep the delivery agreeing with the payment however the + /// two end. + private boolean refundUnfinished(Delivery delivery) { + if (!REFUNDED.equals(delivery.getPaymentStatus())) { + return false; + } + Payment payment = payments.findById(delivery.getId()); + return payment != null && !REFUNDED.equals(payment.getStatus()); + } + + @Transactional(readOnly = true) + private Payment payment(String deliveryId) { + return payments.findById(deliveryId); + } + + /// Gives back the claim of a refund the processor did not take: the delivery + /// is paid again -- unless another request has recorded a refund of it + /// meanwhile. + /// + /// Two refunds can overlap: the second is let through by + /// [#refundUnfinished] while the first is at the processor, which is + /// what lets a refund cut short be asked for again. Should one of them + /// record a refund and the other then fail, putting the delivery back to paid + /// would leave a refunded payment on a delivery its courier is paid for, and + /// the fare would be cashed out after it went back to the customer. So the + /// payment decides: the delivery is paid again only while its payment is. + /// + /// The delivery's row is written first, as in [#keepRefund], so the two take + /// turns on it: whichever comes second sees what the first wrote. + @Transactional + private void releaseRefund(String deliveryId) { + if (deliveries.releaseRefund(deliveryId) != 1) { + // No longer marked refunded: a refund was recorded as pending. + return; + } + Payment payment = payments.findById(deliveryId); + if (payment != null && !PAID.equals(payment.getStatus())) { + deliveries.paymentStatus(deliveryId, payment.getStatus()); + } + } + + /// Records the refunds the processor took. Refunded when both are known + /// to have returned the money; otherwise the payment and its delivery are + /// [#REFUND_PENDING], for [#settleRefunds] to finish. + /// + /// The delivery is written as well, and first, although its claim already + /// marked it refunded: an overlapping request that failed may have put it + /// back to paid in the meantime. See [#releaseRefund]. + @Transactional + private void keepRefund(String deliveryId, PaymentProvider.Refund fare, + PaymentProvider.Refund tip, String reason) { + boolean pending = fare.pending || (tip != null && tip.pending); + deliveries.paymentStatus(deliveryId, pending ? REFUND_PENDING : REFUNDED); + Payment payment = payments.findById(deliveryId); + payment.setStatus(pending ? REFUND_PENDING : REFUNDED); + payment.setRefundRef(cut(fare.reference, 64)); + payment.setTipRefundRef(tip == null ? "" : cut(tip.reference, 64)); + payment.setRefundReason(reason); + payment.setRefundedAt(pending ? 0L : System.currentTimeMillis()); + } + + /// Records a refund of the fare whose tip could not be refunded: the delivery + /// and its payment are [#REFUND_FAILED], with the fare's refund kept. + @Transactional + private void keepPartialRefund(String deliveryId, PaymentProvider.Refund fare, String reason) { + deliveries.paymentStatus(deliveryId, REFUND_FAILED); + Payment payment = payments.findById(deliveryId); + payment.setStatus(REFUND_FAILED); + payment.setRefundRef(cut(fare.reference, 64)); + payment.setTipRefundRef(""); + payment.setRefundReason(reason); + payment.setRefundedAt(0L); + } + + // -------------------------------------------------------------- receipts + + /// A customer's receipts, newest first: one for every delivery that completed. + @Transactional(readOnly = true) + public List receipts(String customer) throws IOException { + return receipts(deliveries.findByCustomerAndStateOrderByRequestedAtDesc(customer, DeliveryState.COMPLETED, + new PageRequest(0, 50)), false); + } + + /// Every receipt, newest first, for the admin. + @Transactional(readOnly = true) + public List allReceipts() throws IOException { + return receipts(deliveries.findByStateOrderByRequestedAtDesc(DeliveryState.COMPLETED, + new PageRequest(0, 200)), true); + } + + /// The receipt of one delivery: what it will cost while it is under way, what + /// it cost once it completed. Answers 404 for a delivery that is not the + /// viewer's, and for one that ended without completing, which cost nothing. + @Transactional(readOnly = true) + public ReceiptDto receipt(String viewer, boolean admin, String deliveryId) throws IOException { + Delivery delivery = delivery(deliveryId); + DeliveryState state = delivery == null ? DeliveryState.NONE : delivery.getState(); + if (delivery == null || !(admin || viewer.equals(delivery.getCustomer())) + || DeliveryState.CANCELLED_BY_CUSTOMER == state || DeliveryState.CANCELLED_BY_COURIER == state + || DeliveryState.CANCELLED_BY_ADMIN == state || DeliveryState.NO_COURIERS == state) { + throw new ResponseStatusException(404, "No such receipt"); + } + Payment payment = payments.findById(deliveryId); + return receipt(delivery, payment == null ? 0L : payment.getCreatedAt(), admin, + new HashMap()); + } + + /// The deliveries' receipts. When each was paid is on its payment and not on + /// the delivery, so the payments of all of them are read at once and matched + /// to their deliveries here. + private List receipts(List completed, boolean admin) { + Map paidAt = new HashMap(); + if (completed.size() > 0) { + List ids = new ArrayList(); + for (int iter = 0; iter < completed.size(); iter++) { + ids.add(completed.get(iter).getId()); + } + List rows = payments.findByDeliveryIdIn(ids); + for (int iter = 0; iter < rows.size(); iter++) { + Payment payment = rows.get(iter); + paidAt.put(payment.getDeliveryId(), Long.valueOf(payment.getCreatedAt())); + } + } + Map couriers = new HashMap(); + List out = new ArrayList(); + for (int iter = 0; iter < completed.size(); iter++) { + Delivery delivery = completed.get(iter); + Long at = paidAt.get(delivery.getId()); + out.add(receipt(delivery, at == null ? 0L : at.longValue(), admin, couriers)); + } + return out; + } + + private ReceiptDto receipt(Delivery delivery, long paidAt, boolean admin, + Map couriers) { + long fare = delivery.getFareCents(); + long distanceFare = delivery.getDistanceCents(); + long timeFare = delivery.getTimeCents(); + // Whatever of the fare the other two parts do not account for, which + // for a delivery asked for before the parts were kept is all of it. + long baseFare = fare - distanceFare - timeFare; + long serviceFee = delivery.getFeeCents(); + long tip = delivery.getTipCents(); + long discount = 0L; + String paid = delivery.getPaymentStatus(); + boolean cash = CASH.equals(delivery.getMethodId()); + String status; + if (PAID.equals(paid)) { + status = cash ? "cash" : "paid"; + } else if (FAILED.equals(paid)) { + status = "failed"; + } else if (REFUNDED.equals(paid)) { + status = "refunded"; + } else if (REFUND_PENDING.equals(paid)) { + status = "refund_pending"; + } else if (REFUND_FAILED.equals(paid)) { + status = "refund_failed"; + } else { + status = "pending"; + } + // Every refund state follows a payment that was made. + long paidOn = PAID.equals(paid) || (paid != null && paid.startsWith("REFUND")) + ? paidAt : 0L; + String courierName = ""; + String vehicle = ""; + String courier = delivery.getCourier(); + if (courier.length() > 0 && DeliveryState.OFFERED != delivery.getState()) { + Profile who = couriers.get(courier); + if (who == null && !couriers.containsKey(courier)) { + who = profiles.findById(courier); + couriers.put(courier, who); + } + if (who != null) { + courierName = who.getDisplayName(); + vehicle = who.getVehicle(); + } + } + return new ReceiptDto(delivery.getId(), delivery.getCurrency(), baseFare, distanceFare, + timeFare, serviceFee, tip, discount, fare + serviceFee + tip - discount, + delivery.getMethodLabel(), status, paidOn, delivery.getPickupAddress(), + delivery.getDropoffAddress(), delivery.getDistanceMeters(), delivery.getDurationSeconds(), + courierName, vehicle, admin ? delivery.getCustomer() : ""); + } + + // ------------------------------------------------------------- internals + + /// Charges one of a customer's saved cards. What the charge needs is read in + /// a transaction that has ended by the time the processor is called. + private String charge(String key, String username, String methodId, long amountCents, + String currency, String description) throws IOException { + String[] references = chargeable(username, methodId); + if (references == null) { + throw new PaymentDeclinedException("The card is no longer on the account"); + } + return cut(provider.charge(key, references[0], references[1], amountCents, currency, + description), 64); + } + + /// What the processor calls the customer and one of their cards, in that + /// order, or null when either is gone. + @Transactional(readOnly = true) + private String[] chargeable(String username, String methodId) { + PaymentMethod method = card(username, methodId); + PaymentCustomer customer = customers.findById(username); + if (method == null || customer == null) { + return null; + } + return new String[] {customer.getReference(), method.getToken()}; + } + + private PaymentMethod owned(String username, String id) { + PaymentMethod row = card(username, id); + if (row == null) { + // Someone else's card is told apart from no card by nobody. + throw new ResponseStatusException(404, "No such payment method"); + } + return row; + } + + private static PaymentMethodDto card(PaymentMethod row) { + return card(row, row.isPreferred()); + } + + private static PaymentMethodDto card(PaymentMethod row, boolean isDefault) { + return new PaymentMethodDto(row.getId(), "card", row.getBrand(), row.getLast4(), + row.getExpMonth(), row.getExpYear(), isDefault, label(row)); + } + + private static PaymentMethodDto cash(boolean isDefault) { + return new PaymentMethodDto(CASH, "cash", "", "", 0, 0, isDefault, CASH_LABEL); + } + + private static String label(PaymentMethod method) { + return method.getBrand() + " " + method.getLast4(); + } + + private static String cut(String text, int max) { + return text.length() > max ? text.substring(0, max) : text; + } + + /// Deletes a customer record this server made and did not keep. A failure + /// is logged rather than answered: the request has its own answer, and + /// the record holds no card. + private void forgetQuietly(String reference) { + try { + provider.forgetCustomer(reference); + } catch (IOException failed) { + System.err.println("could not delete an unused payment customer: " + + failed.getMessage()); + } + } + + /// A fault at the processor, as the app is told of it. What went wrong goes + /// to the log; it describes the request, never a card or a key. + private static ResponseStatusException unavailable(String what, IOException failed) { + System.err.println("payment provider failed during " + what + ": " + failed.getMessage()); + return new ResponseStatusException(502, "Payments are unavailable right now"); + } + + /// A delivery, or null when there is none. An id that cannot be one is not + /// looked for. + private Delivery delivery(String id) { + if (id == null || id.length() == 0 || id.length() > 32) { + return null; + } + return deliveries.findById(id); + } + + /// One of an account's cards, or null when the id is not a card of theirs. + private PaymentMethod card(String username, String id) { + if (id == null || id.length() == 0 || id.length() > 32) { + return null; + } + return cards.findFirstByIdAndUsername(id, username); + } + + /// Whether a completed delivery of the customer's is still to be charged to this + /// method -- or to any card, for a null `methodId` -- and will be asked + /// about again: its payment is pending and it ended after `since`. + /// + /// A card tip left unknown counts too, with no time limit: see [#tip]. + /// Its retry charges the delivery's card under the same key, so with the card + /// gone a tip that may have been charged would be cleared as refused. + private boolean awaitsCharge(String customer, String methodId, long since) { + List tipped = deliveries.findByCustomerAndPaymentStatusAndTipCentsGreaterThan(customer, PAID, + 0L); + for (Delivery delivery : tipped) { + if ((methodId == null || methodId.equals(delivery.getMethodId())) + && tipLeftUnknown(delivery, delivery.getTipCents())) { + return true; + } + } + if (methodId == null) { + return deliveries.existsByCustomerAndStateAndPaymentStatusAndUpdatedAtGreaterThanAndMethodIdNot( + customer, DeliveryState.COMPLETED, PENDING, since, CASH); + } + return deliveries.existsByCustomerAndStateAndPaymentStatusAndUpdatedAtGreaterThanAndMethodId( + customer, DeliveryState.COMPLETED, PENDING, since, methodId); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/SimulatedPayments.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/SimulatedPayments.java new file mode 100644 index 00000000000..e273018e642 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/SimulatedPayments.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codenameone.examples.morsel.Days; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.CardDto; + +import java.io.IOException; + +/// A processor that moves no money. What the sample runs on until it is given +/// a real one's key, and what every test but that provider's own runs against. +/// +/// It behaves like the real thing where the app can tell the difference: a +/// card has to look like a card, and the well-known test numbers do what they +/// do at a real processor's test mode. +/// +/// - `4242 4242 4242 4242` is accepted, as is any other number that passes the +/// check digit. +/// - `4000 0000 0000 0002` is declined when it is added. +/// - `4000 0000 0000 0341` is accepted and then declined when it is charged, +/// which is how a payment that fails at the end of a delivery is tried out. +/// - `4000 0000 0000 0119` is accepted and then cannot be charged at all: the +/// processor fails without saying whether it took the money, which is how +/// a payment nobody can vouch for is tried out. +/// - `4000 0000 0000 7726` is charged like any other, and a refund of it is +/// pending when it is made and has succeeded when it is next asked about, +/// which is how a refund the bank has not answered yet is tried out. +/// +/// A refund that is pending and then fails has no card number: a card whose +/// token starts `sim_pm_lost_` does that, and only a test makes one. +/// +/// The card number is read here, in this method, and goes no further: the +/// brand and the last four digits are all that is returned or stored. +public class SimulatedPayments implements PaymentProvider { + static final String DECLINED_NUMBER = "4000000000000002"; + static final String FAILS_LATER_NUMBER = "4000000000000341"; + static final String FAULTY_NUMBER = "4000000000000119"; + static final String SLOW_REFUND_NUMBER = "4000000000007726"; + private static final String FAILING_TOKEN = "sim_pm_fail_"; + private static final String FAULTY_TOKEN = "sim_pm_fault_"; + private static final String SLOW_REFUND_TOKEN = "sim_pm_slow_"; + private static final String LOST_REFUND_TOKEN = "sim_pm_lost_"; + private static final String SLOW_REFUND_CHARGE = "sim_ch_slow_"; + private static final String LOST_REFUND_CHARGE = "sim_ch_lost_"; + private static final String REFUND = "sim_re_"; + + @Override + public String name() { + return "simulated"; + } + + @Override + public String createCustomer(String username, String displayName) throws IOException { + return "sim_cus_" + Ids.next(); + } + + @Override + public Setup startCardSetup(String customer, String setupId) { + Setup setup = new Setup(); + setup.reference = "sim_seti_" + setupId; + return setup; + } + + @Override + public Card finishCardSetup(String customer, String reference, CardDto typed) + throws IOException { + if (typed == null) { + throw new PaymentDeclinedException("Enter the card's details"); + } + String number = digits(typed.number()); + if (number.length() < 12 || number.length() > 19 || !luhn(number)) { + throw new PaymentDeclinedException("That is not a card number"); + } + int year = typed.expYear() < 100 ? typed.expYear() + 2000 : typed.expYear(); + String today = Days.iso(System.currentTimeMillis()); + int thisYear = Integer.parseInt(today.substring(0, 4)); + int thisMonth = Integer.parseInt(today.substring(5, 7)); + if (typed.expMonth() < 1 || typed.expMonth() > 12 || year > thisYear + 25 + || year < thisYear || (year == thisYear && typed.expMonth() < thisMonth)) { + throw new PaymentDeclinedException("That card has expired"); + } + String cvc = typed.cvc() == null ? "" : typed.cvc().trim(); + if (cvc.length() < 3 || cvc.length() > 4 || digits(cvc).length() != cvc.length()) { + throw new PaymentDeclinedException("Check the card's security code"); + } + if (DECLINED_NUMBER.equals(number)) { + throw new PaymentDeclinedException("Your card was declined"); + } + Card card = new Card(); + card.brand = brand(number); + card.last4 = number.substring(number.length() - 4); + card.expMonth = typed.expMonth(); + card.expYear = year; + card.token = (FAILS_LATER_NUMBER.equals(number) ? FAILING_TOKEN + : FAULTY_NUMBER.equals(number) ? FAULTY_TOKEN + : SLOW_REFUND_NUMBER.equals(number) ? SLOW_REFUND_TOKEN : "sim_pm_") + Ids.next(); + return card; + } + + @Override + public void detachCard(String customer, String token) throws IOException { + // Nothing is kept of a card here; its token is all there is, and the + // server deletes that. + } + + @Override + public String charge(String key, String customer, String token, long amountCents, + String currency, String description) throws IOException { + if (token.startsWith(FAILING_TOKEN)) { + throw new PaymentDeclinedException("Your card was declined"); + } + if (token.startsWith(FAULTY_TOKEN)) { + throw new IOException("The simulated processor did not answer"); + } + // A charge says how its refund is to go, as the card did: the refund + // has only the charge to go by. + return (token.startsWith(SLOW_REFUND_TOKEN) ? SLOW_REFUND_CHARGE + : token.startsWith(LOST_REFUND_TOKEN) ? LOST_REFUND_CHARGE : "sim_ch_") + key; + } + + @Override + public void forgetCustomer(String customer) { + // Nothing is kept of one here. + } + + @Override + public Refund refund(String chargeReference, long amountCents) throws IOException { + // Named after the charge, as a real processor's refund is keyed by + // it: asking twice is one refund, which is what lets a refund whose + // answer was lost be asked for again. + Refund refund = new Refund(); + refund.reference = REFUND + chargeReference; + refund.pending = chargeReference.startsWith(SLOW_REFUND_CHARGE) + || chargeReference.startsWith(LOST_REFUND_CHARGE); + return refund; + } + + @Override + public String refundStatus(String refundReference) throws IOException { + return refundReference.startsWith(REFUND + LOST_REFUND_CHARGE) ? REFUND_FAILED + : REFUND_SUCCEEDED; + } + + /// The digits of what was typed, with the spaces and dashes people put in a + /// card number taken out. Anything else in it leaves it too short to pass. + private static String digits(String value) { + StringBuilder out = new StringBuilder(); + for (int iter = 0; value != null && iter < value.length(); iter++) { + char c = value.charAt(iter); + if (c >= '0' && c <= '9') { + out.append(c); + } else if (c != ' ' && c != '-') { + return ""; + } + } + return out.toString(); + } + + /// The check digit every card number carries, which catches a mistyped one. + private static boolean luhn(String number) { + int sum = 0; + boolean doubled = false; + for (int iter = number.length() - 1; iter >= 0; iter--) { + int digit = number.charAt(iter) - '0'; + if (doubled) { + digit *= 2; + if (digit > 9) { + digit -= 9; + } + } + sum += digit; + doubled = !doubled; + } + return sum % 10 == 0; + } + + private static String brand(String number) { + if (number.startsWith("4")) { + return "Visa"; + } + if (number.startsWith("34") || number.startsWith("37")) { + return "Amex"; + } + if (number.startsWith("6011") || number.startsWith("65")) { + return "Discover"; + } + int two = Integer.parseInt(number.substring(0, 2)); + if ((two >= 51 && two <= 55) || (two >= 22 && two <= 27)) { + return "Mastercard"; + } + return "Card"; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/StripePayments.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/StripePayments.java new file mode 100644 index 00000000000..bfe61271056 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/pay/StripePayments.java @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.backend.Json; +import com.codename1.backend.Web; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.api.CardDto; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/// Payments by Stripe, over its REST API. No SDK: a few requests, form encoded, +/// with the account's secret key as a bearer token. +/// +/// **This is an untested stub.** It has never been run against a live Stripe +/// account: the requests follow Stripe's published API, and the tests hold it +/// to canned answers through [Transport], but nothing here has seen a real +/// card, a bank asking its cardholder to approve a charge, or a refund settle. +/// Treat it as a starting point, and run it against a Stripe test-mode key +/// before any money depends on it. +/// +/// Selected with `morsel.payments.provider=stripe`. +/// +/// - **A customer** is made the first time a customer adds a card. +/// - **A card is added on Stripe's own page.** [#startCardSetup] opens a +/// Checkout Session in `setup` mode and hands back its address; the app +/// opens it in the browser. The card is typed there, so its number never +/// reaches the app or this server, and neither falls under the card +/// industry's rules for systems that handle one. +/// - **The setup is read back** when the app says the user returned: the +/// session's SetupIntent names the PaymentMethod Stripe saved, and its brand +/// and last four digits are all this server keeps beside its id. +/// - **A delivery is charged** with a PaymentIntent that is `off_session` and +/// confirmed in the same request, because the customer is in a car and not at a +/// payment form. +/// - **A refund** returns a charge to the card. +/// - **A card removed** in the app is detached from the customer, so Stripe +/// no longer offers it for a charge. +/// +/// The secret key is a setting of the server (`morsel.payments.stripe.secret`) +/// and is used for the `Authorization` header and nothing else: it is never +/// logged and never part of an error. +/// +/// Everything Stripe-specific is in [#form] strings and the reading of the +/// answers. The network is behind [Transport], which is what lets the tests +/// hold both to canned answers without a key. +public class StripePayments implements PaymentProvider { + private static final String API = "https://api.stripe.com"; + + private final Transport transport; + private final String returnUrl; + + /// @param secret the account's secret key + /// @param returnUrl the address of this server's `/pay/return` page, which + /// Stripe sends the browser to when the user is done + public StripePayments(String secret, String returnUrl) { + this(new WebTransport(secret), returnUrl); + } + + public StripePayments(Transport transport, String returnUrl) { + this.transport = transport; + this.returnUrl = returnUrl; + } + + @Override + public String name() { + return "stripe"; + } + + @Override + public String createCustomer(String username, String displayName) throws IOException { + // Two first cards started together both find the account without a + // customer and both ask for one. Only one reference is kept, and the + // other would be left at the processor with nothing pointing at it. + // With the same key the second request is answered with the first + // one's customer. The key is a digest, since the header is logged in + // places an address should not be, and it covers the name too: the + // processor refuses a key it has seen with other fields, and a customer + // who renamed themselves between two attempts must not be refused. + // The price is one orphan in a case that takes two mishaps: the + // answer to the first attempt is lost, and the customer renames + // themselves before trying again. The second customer is the one + // kept; the first holds no card and no money, and is left at the + // processor rather than looked up by address on every first card. + String key = "customer-" + Ids.sha256(username + "\n" + displayName); + Map made = ask("POST", "/v1/customers", form("email", username, "name", displayName, + "metadata[morsel_user]", username), key); + return id(made); + } + + @Override + public void forgetCustomer(String customer) throws IOException { + // Deleting a customer detaches its payment methods too. 404 is the + // answer for one deleted by a request whose answer never arrived. + Reply reply = transport.send("DELETE", "/v1/customers/" + + URLEncoder.encode(customer, "UTF-8"), null, ""); + if (reply.status != 404 && (reply.status < 200 || reply.status >= 300)) { + throw new IOException("The payment processor answered " + reply.status + + " to deleting a customer"); + } + } + + // tag::morsel-payment-hosted[] + @Override + public Setup startCardSetup(String customer, String setupId) throws IOException { + Map session = ask("POST", "/v1/checkout/sessions", form("mode", "setup", + "customer", customer, "payment_method_types[0]", "card", + "client_reference_id", setupId, + "success_url", returnUrl + "?result=done", + "cancel_url", returnUrl + "?result=cancelled"), ""); + Setup setup = new Setup(); + setup.hosted = true; + setup.reference = id(session); + setup.url = text(session, "url"); + if (setup.url.length() == 0) { + throw new IOException("The payment processor returned no page to open"); + } + return setup; + } + // end::morsel-payment-hosted[] + + @Override + public Card finishCardSetup(String customer, String reference, CardDto typed) + throws IOException { + Map session = ask("GET", "/v1/checkout/sessions/" + URLEncoder.encode(reference, "UTF-8") + + "?expand%5B%5D=setup_intent.payment_method", null, ""); + Object intent = session.get("setup_intent"); + Object method = intent instanceof Map ? ((Map) intent).get("payment_method") : null; + if (!"complete".equals(text(session, "status")) || !(intent instanceof Map) + || !"succeeded".equals(text((Map) intent, "status")) || !(method instanceof Map)) { + throw new PaymentDeclinedException("The card was not added. Try again."); + } + // The session is looked up by a reference this server stored for this + // customer, so this cannot differ -- unless something is very wrong, in + // which case the card is not attached to anybody. + if (!customer.equals(text(session, "customer"))) { + throw new IOException("The payment processor's session is for another customer"); + } + Card card = new Card(); + card.token = id((Map) method); + Object details = ((Map) method).get("card"); + if (details instanceof Map) { + card.brand = brand(text((Map) details, "brand")); + card.last4 = text((Map) details, "last4"); + card.expMonth = (int) number((Map) details, "exp_month"); + card.expYear = (int) number((Map) details, "exp_year"); + } + return card; + } + + @Override + public void detachCard(String customer, String token) throws IOException { + String path = "/v1/payment_methods/" + URLEncoder.encode(token, "UTF-8"); + Reply reply = transport.send("POST", path + "/detach", "", ""); + if ((reply.status >= 200 && reply.status < 300) || reply.status == 404) { + // Detached, or no such card: 404 is the answer for one deleted + // with its customer, or never there. + return; + } + // Stripe refuses to detach a card that is attached to nobody, which + // is what a second request finds after the first one's answer was + // lost. Rather than read that from an error's wording, the card is + // looked at: no longer this customer's, it is detached. + Reply now = transport.send("GET", path, null, ""); + if (now.status == 404) { + return; + } + if (now.status >= 200 && now.status < 300) { + Map method; + try { + method = Json.parseObject(now.body == null ? "" : now.body); + } catch (IOException unreadable) { + method = null; + } + if (method != null && !customer.equals(text(method, "customer"))) { + return; + } + } + throw new IOException("The payment processor answered " + reply.status + + " to detaching a card"); + } + + @Override + public String charge(String key, String customer, String token, long amountCents, + String currency, String description) throws IOException { + Map intent = ask("POST", "/v1/payment_intents", form("amount", String.valueOf(amountCents), + "currency", asciiLower(currency), "customer", customer, "payment_method", token, + "off_session", "true", "confirm", "true", "description", description, + "metadata[morsel_charge]", key), key); + if (!"succeeded".equals(text(intent, "status"))) { + // Most often `requires_action`: the bank wants the cardholder to + // approve it, and there is nobody at a screen to do so. + throw new PaymentDeclinedException("The card needs its owner's approval"); + } + return id(intent); + } + + @Override + public Refund refund(String chargeReference, long amountCents) throws IOException { + Map answer = ask("POST", "/v1/refunds", form("payment_intent", chargeReference, + "amount", String.valueOf(amountCents)), "refund-" + chargeReference); + String status = refundState(text(answer, "status")); + if (REFUND_FAILED.equals(status)) { + throw new IOException("The refund was not accepted: " + text(answer, "status")); + } + Refund refund = new Refund(); + refund.reference = id(answer); + // A pending refund can still fail or be canceled, so it is not yet a + // refund to record as made: the caller keeps asking. + refund.pending = REFUND_PENDING.equals(status); + return refund; + } + + @Override + public String refundStatus(String refundReference) throws IOException { + return refundState(text(ask("GET", "/v1/refunds/" + + URLEncoder.encode(refundReference, "UTF-8"), null, null), "status")); + } + + /// Stripe's five refund states as three. `requires_action` waits on the + /// cardholder like `pending` waits on the bank, and `canceled` returned no + /// more money than `failed` did. A state this does not know is taken as + /// still pending: asked about again, it is never recorded as money back. + private static String refundState(String stripe) { + if ("succeeded".equals(stripe)) { + return REFUND_SUCCEEDED; + } + if ("failed".equals(stripe) || "canceled".equals(stripe)) { + return REFUND_FAILED; + } + return REFUND_PENDING; + } + + /// Makes a request and returns the object in the answer. + /// + /// A card Stripe refuses is answered with status 402 and an error of type + /// `card_error`, whose message is written for the cardholder; that becomes + /// a [PaymentDeclinedException]. Any other failure is a fault of ours or + /// theirs and is reported by status and code only. + private Map ask(String method, String path, String form, String key) throws IOException { + Reply reply = transport.send(method, path, form, key); + Map body; + try { + body = Json.parseObject(reply.body == null ? "" : reply.body); + } catch (IOException unreadable) { + throw new IOException("The payment processor answered " + reply.status + + " with something that is not JSON"); + } + if (reply.status >= 200 && reply.status < 300) { + return body; + } + Object error = body.get("error"); + if (error instanceof Map) { + Map details = (Map) error; + if ("card_error".equals(text(details, "type"))) { + String message = text(details, "message"); + throw new PaymentDeclinedException(message.length() > 0 ? message + : "Your card was declined"); + } + throw new IOException("The payment processor answered " + reply.status + " " + + text(details, "type") + " " + text(details, "code")); + } + throw new IOException("The payment processor answered " + reply.status); + } + + /// A form body from names and values in turn. Both are escaped: a name + /// such as `metadata[morsel_user]` has brackets in it. A space is written + /// as `+`, which a form body allows. + static String form(String... pairs) throws IOException { + StringBuilder out = new StringBuilder(); + for (int iter = 0; iter + 1 < pairs.length; iter += 2) { + if (out.length() > 0) { + out.append('&'); + } + out.append(URLEncoder.encode(pairs[iter], "UTF-8")).append('=') + .append(URLEncoder.encode(pairs[iter + 1] == null ? "" : pairs[iter + 1], + "UTF-8")); + } + return out.toString(); + } + + private static String id(Map object) throws IOException { + String id = text(object, "id"); + if (id.length() == 0) { + throw new IOException("The payment processor's answer has no id"); + } + return id; + } + + private static String text(Map object, String key) { + Object value = object.get(key); + return value instanceof String ? (String) value : ""; + } + + private static long number(Map object, String key) { + Object value = object.get(key); + return value instanceof Number ? ((Number) value).longValue() : 0L; + } + + /// Stripe names brands in lower case (`visa`, `mastercard`, `amex`); this is + /// the name with its first letter raised, which is how a person writes it. + private static String brand(String name) { + if (name.length() == 0) { + return "Card"; + } + char first = name.charAt(0); + if (first >= 'a' && first <= 'z') { + first = (char) (first - ('a' - 'A')); + } + return first + name.substring(1); + } + + /// Stripe wants a currency code in lower case. Folded by hand, because + /// `toLowerCase()` follows the server's locale and a currency code must not. + private static String asciiLower(String text) { + char[] chars = text.toCharArray(); + for (int iter = 0; iter < chars.length; iter++) { + if (chars[iter] >= 'A' && chars[iter] <= 'Z') { + chars[iter] = (char) (chars[iter] + ('a' - 'A')); + } + } + return new String(chars); + } + + /// The network, as this class uses it: one request to the API, one answer. + public interface Transport { + /// @param path the path and query, from `/v1` + /// @param form the form-encoded body, or null for a request without one + /// @param idempotencyKey sent as `Idempotency-Key` when not empty, so a + /// request repeated with the same key is carried out once + Reply send(String method, String path, String form, String idempotencyKey) + throws IOException; + } + + /// An answer: its status and its body, whatever the status. + public static final class Reply { + public final int status; + public final String body; + + public Reply(int status, String body) { + this.status = status; + this.body = body; + } + } + + /// The real network: HTTPS to `api.stripe.com`, as [Web] makes it. + static final class WebTransport implements Transport { + private final String secret; + + WebTransport(String secret) { + this.secret = secret; + } + + @Override + public Reply send(String method, String path, String form, String idempotencyKey) + throws IOException { + List headers = new ArrayList(); + headers.add("Authorization: Bearer " + secret); + if (form != null) { + headers.add("Content-Type: application/x-www-form-urlencoded"); + } + if (idempotencyKey != null && idempotencyKey.length() > 0) { + headers.add("Idempotency-Key: " + idempotencyKey); + } + Web.Result answer = Web.request(method, API + path, headers, + form == null ? null : form.getBytes("UTF-8")); + return new Reply(answer.getStatus(), answer.getBodyAsString()); + } + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/CloudPushSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/CloudPushSender.java new file mode 100644 index 00000000000..ce2e747f196 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/CloudPushSender.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.Tasks; +import com.codename1.backend.push.CloudPush; +import com.codename1.backend.push.PushNotification; +import com.codename1.backend.push.PushResult; +import com.codename1.backend.push.PushTarget; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +// tag::morsel-push-cloud[] +/// Sends through the Codename One push service, which delivers to Firebase, +/// Apple, Huawei and the browsers on the app's behalf. +/// +/// The call to the service is made on a thread of its own, so the request that +/// changed a delivery answers without waiting for it, and a refusal is logged and +/// not thrown: the customer is still told over the live channel and on their next +/// look at the delivery. +public class CloudPushSender implements PushSender { + private final CloudPush push; + + /// @param push the client for the service, holding the app's id and the + /// server key + public CloudPushSender(CloudPush push) { + this.push = push; + } + + @Override + public void send(List targets, final PushNotification message, + final Runnable delivered) { + if (targets == null || targets.isEmpty()) { + return; + } + final List to = new ArrayList(targets); + Tasks.platform(() -> deliver(to, message, delivered)); + } + + private void deliver(List to, PushNotification message, Runnable delivered) { + boolean accepted = false; + try { + PushResult result = push.send(message, to); + accepted = result.isAccepted(); + if (!accepted) { + System.err.println("[push] the push service refused a notification: " + + result.getStatus() + " " + result.getError()); + } + } catch (IOException | RuntimeException err) { + System.err.println("[push] a notification was not sent: " + err); + } + if (accepted && delivered != null) { + try { + delivered.run(); + } catch (RuntimeException err) { + System.err.println("[push] a notification was sent, and what follows it failed: " + + err); + } + } + } +} +// end::morsel-push-cloud[] diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/ConsolePushSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/ConsolePushSender.java new file mode 100644 index 00000000000..78ea9e9a787 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/ConsolePushSender.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.push.PushNotification; +import com.codename1.backend.push.PushTarget; + +import java.util.List; + +/// Prints each notification on the server's console instead of sending it. +/// +/// For development only: [com.codenameone.examples.morsel.Services] hands it +/// out on a development profile alone. +public class ConsolePushSender implements PushSender { + @Override + public void send(List targets, PushNotification message, Runnable delivered) { + if (targets == null || targets.isEmpty()) { + return; + } + System.out.println("[push] to " + targets.size() + " device(s): " + + message.toJson()); + // Printed is as delivered as this sender gets. + if (delivered != null) { + delivered.run(); + } + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/DeliveryPushes.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/DeliveryPushes.java new file mode 100644 index 00000000000..89781dec150 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/DeliveryPushes.java @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Scheduled; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.annotations.Value; +import com.codename1.backend.push.PushNotification; +import com.codename1.backend.push.PushTarget; +import com.codename1.orm.repository.PageRequest; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.api.DeliveryActivityKeys; +import com.codenameone.examples.morsel.api.DeliveryActivityDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierState; +import com.codenameone.examples.morsel.domain.Preference; +import com.codenameone.examples.morsel.domain.Profile; +import com.codenameone.examples.morsel.domain.Delivery; +import com.codenameone.examples.morsel.domain.DeliveryActivity; +import com.codenameone.examples.morsel.repository.CourierStateRepository; +import com.codenameone.examples.morsel.repository.PreferenceRepository; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.DeliveryActivityRepository; +import com.codenameone.examples.morsel.repository.DeliveryRepository; +import com.codenameone.examples.morsel.delivery.Deliveries; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// The push notifications a delivery sends, and the Live Activity it keeps up to +/// date on the customer's lock screen. +/// +/// [Deliveries] calls [#deliveryChanged] once a change has been committed, the same +/// moment it tells the live channel; [com.codenameone.examples.morsel.courier.Couriers] +/// calls [#courierMoved] for every position a courier on a delivery reports. A +/// notification is a courtesy on top of what the app reads from the server, +/// so nothing here ever fails the change it is about: whatever goes wrong is +/// logged. +/// +/// The live channel already tells an app that is open. A notification is for +/// the phone in a pocket: the customer whose courier has arrived, the courier who +/// has been offered a delivery. +/// +/// The **Notifications** switches in the app's settings decide what is said: +/// delivery updates for every step of a delivery, receipts for its end. A switch that +/// is off silences the notification and nothing else, so the Live Activity +/// still follows the delivery and still ends with it. +@Component +public class DeliveryPushes { + private static final String CUSTOMER = "customer"; + private static final String COURIER = "courier"; + + /// How long an end the phone was not told of is tried again. iOS ends a + /// Live Activity on its own within twelve hours, so past that there is + /// nothing left on the lock screen to end. + private static final long END_RETRY_MILLIS = 12L * 60 * 60 * 1000; + + /// How old an end has to be before the sweep sends it again: the call + /// that ended the delivery is still sending it until then. + private static final long END_GRACE_MILLIS = 60000L; + + private final PushSender sender; + private final PushDevices devices; + private final DeliveryRepository deliveries; + private final DeliveryActivityRepository activities; + private final ProfileRepository profiles; + private final CourierStateRepository couriers; + private final PreferenceRepository preferences; + private final long activityMillis; + private final int offerSeconds; + + /// @param activitySeconds how often, at most, a moving car updates the + /// customer's Live Activity + /// @param offerSeconds how long a courier has to answer an offer, which is + /// how long the notification of it is worth delivering + public DeliveryPushes(PushSender sender, PushDevices devices, DeliveryRepository deliveries, + DeliveryActivityRepository activities, ProfileRepository profiles, + CourierStateRepository couriers, PreferenceRepository preferences, + @Value("${morsel.push.activity.seconds:45}") int activitySeconds, + @Value("${morsel.offer.seconds:15}") int offerSeconds) { + this.sender = sender; + this.devices = devices; + this.deliveries = deliveries; + this.activities = activities; + this.profiles = profiles; + this.couriers = couriers; + this.preferences = preferences; + this.activityMillis = activitySeconds * 1000L; + this.offerSeconds = offerSeconds; + } + + /// One notification and the devices it goes to. + static final class Outgoing { + final List to; + final PushNotification message; + /// The delivery whose Live Activity this ends, forgotten once the push + /// service takes it; null for every other message. + final String ends; + + Outgoing(List to, PushNotification message) { + this(to, message, null); + } + + Outgoing(List to, PushNotification message, String ends) { + this.to = to; + this.message = message; + this.ends = ends; + } + } + + // tag::morsel-push-delivery-changed[] + /// Tells the people concerned about a change to a delivery that has been + /// committed. Called after the transaction, so what it reads is the delivery as + /// it now is, and a phone that opens the app at once finds it so. + public void deliveryChanged(String id, DeliveryState state, String customer, String courier) { + try { + send(compose(id, state, customer, courier)); + } catch (IOException | RuntimeException err) { + System.err.println("[push] nothing was sent for delivery " + id + " (" + state + "): " + + err); + } + } + // end::morsel-push-delivery-changed[] + + /// Sends again the ends of Live Activities the push provider did not take + /// when their delivery ended, and forgets those too old to be on screen. + /// + /// Not a transaction itself, as `Deliveries.settleLeftBehind` is not: the + /// provider is called with none open, and what went wrong with the first + /// end that failed again is reported once the rest have been tried. + @Scheduled(fixedDelay = 60000, initialDelay = 50000, lock = "morsel-activity-ends") + public void endLeftBehind() throws IOException { + long now = System.currentTimeMillis(); + dropEndedBefore(now - END_RETRY_MILLIS); + List left = leftBehind(now - END_GRACE_MILLIS); + Exception failed = null; + for (int iter = 0; iter < left.size(); iter++) { + String id = left.get(iter).getDeliveryId(); + try { + send(ending(id)); + } catch (IOException err) { + failed = failed == null ? err : failed; + } catch (RuntimeException err) { + failed = failed == null ? err : failed; + } + } + if (failed instanceof IOException) { + throw (IOException) failed; + } + if (failed instanceof RuntimeException) { + throw (RuntimeException) failed; + } + } + + @Transactional + private void dropEndedBefore(long before) { + activities.dropEndedBefore(before); + } + + @Transactional(readOnly = true) + private List leftBehind(long before) { + return activities.leftBehind(before, new PageRequest(0, 50)); + } + + @Transactional + private void forget(String id) { + activities.deleteByDeliveryId(id); + } + + /// The end of the Live Activity of delivery `id`, sent quietly: the customer + /// was told of the delivery's end, or chose not to be, when it happened. + @Transactional(readOnly = true) + private List ending(String id) throws IOException { + List out = new ArrayList(); + DeliveryActivity activity = activities.findById(id); + Delivery delivery = deliveries.findById(id); + if (activity == null || delivery == null) { + return out; + } + String courier = delivery.getCourier(); + Profile who = courier == null || courier.length() == 0 ? null : profiles.findById(courier); + String name = who == null ? "Your courier" : who.getDisplayName(); + out.add(new Outgoing(devices.targetsOf(delivery.getCustomer()), quiet(id, delivery.getState()) + .surface(endOf(activity.getActivityId(), delivery.getState(), name, who)).build(), id)); + return out; + } + + /// Moves the customer's Live Activity on as the car moves: its minutes to + /// the pickup or the destination. At most once every + /// `morsel.push.activity.seconds`, and only for a delivery that has one. + public void courierMoved(String deliveryId, double lat, double lng) { + try { + send(moved(deliveryId, lat, lng)); + } catch (IOException | RuntimeException err) { + System.err.println("[push] the Live Activity of delivery " + deliveryId + + " was not updated: " + err); + } + } + + // tag::morsel-push-activity[] + /// Records the Live Activity the customer's phone shows `deliveryId` in. Only for + /// the delivery's customer, and only while a courier is bringing or carrying them: + /// that is when the activity is on screen. + @Transactional + public DeliveryActivityDto attach(String customer, String deliveryId, DeliveryActivityDto activity) + throws IOException { + String activityId = Text.required(activity == null ? null : activity.activityId(), 190, + "the activity"); + Delivery delivery = deliveries.findById(deliveryId); + if (delivery == null || !delivery.getCustomer().equals(customer)) { + throw new ResponseStatusException(404, "No such delivery"); + } + // The state check and the insert have to be one step against the delivery + // ending: checked from the delivery read above, a courier completing it or + // an admin cancelling it could commit in between, its compose() would + // find no activity to end, and this one would be inserted after it and + // stay on the lock screen. holdWhile checks the state in the database + // and keeps the delivery's row locked until this commits, so an ending + // either happened first (409 here) or waits and then finds the row. + if (deliveries.holdWhile(deliveryId, customer, MEETING) == 0) { + throw new ResponseStatusException(409, "That delivery has no courier on the way"); + } + // One activity per delivery, the last one attached. A customer following + // their own delivery on two phones at once is not a case the sample + // serves: the earlier phone's activity stops moving and the system + // removes it. A row per device is listed in the implementor's guide + // among what a product adds. + DeliveryActivity row = activities.findById(deliveryId); + if (row == null) { + row = new DeliveryActivity(); + row.setDeliveryId(deliveryId); + row.setUsername(customer); + row.setActivityId(activityId); + row.setSentAt(System.currentTimeMillis()); + activities.insert(row); + } else { + row.setActivityId(activityId); + row.setSentAt(System.currentTimeMillis()); + } + return new DeliveryActivityDto(activityId); + } + // end::morsel-push-activity[] + + /// Hands each message to the sender. The end of a Live Activity forgets + /// it only once the push service has taken the message, which the sender + /// says later, on its own thread. Until then the row stays marked ended, + /// so a refusal, an outage or a server stopped first leaves it for + /// [#endLeftBehind]: the delivery's transition is committed and never retried, + /// so nothing else would end the activity on the lock screen. + private void send(List outgoing) { + for (Outgoing each : outgoing) { + final String ends = each.ends; + if (each.to.isEmpty()) { + // No phone left to tell: nothing will ever take it. + if (ends != null) { + forget(ends); + } + continue; + } + sender.send(each.to, each.message, ends == null ? null : () -> forget(ends)); + } + } + + /// What to send about a change, read and decided in one transaction. A + /// delivery that has ended marks its Live Activity ended here; [#deliveryChanged] + /// forgets it once the end has been sent. + @Transactional + private List compose(String id, DeliveryState state, String customer, String courier) + throws IOException { + List out = new ArrayList(); + Delivery delivery = deliveries.findById(id); + if (delivery == null) { + return out; + } + // The change this was called for may no longer be the delivery's latest. + // Each transition commits and then composes on its own thread, so two + // in quick succession can compose out of order: an acceptance commits, + // the customer cancels and that commits, and only then does the + // acceptance get here. Switching on `state` would tell the customer their + // courier is on the way to a delivery they just cancelled. When what was + // committed since differs from `state`, nothing is said: the later + // transition calls this with its own state and says what is true. + // An offer is compared by its courier too, because a lapsed offer goes + // back to REQUESTED and on to OFFERED again to somebody else. + // + // Nothing is cleaned up here either. A delivery that has ended since is + // ended by that later call, which reads the Live Activity, sends its + // live-end and then forgets it; deleting the row here first would + // leave that call nothing to end and the activity on the lock screen. + if (delivery.getState() != state || (DeliveryState.OFFERED == state + && (courier == null || !courier.equals(delivery.getCourier())))) { + return out; + } + Profile who = courier == null || courier.length() == 0 ? null : profiles.findById(courier); + String name = who == null ? "Your courier" : who.getDisplayName(); + String car = who == null ? "" : describe(who); + DeliveryActivity activity = activities.findById(id); + String activityId = activity == null ? null : activity.getActivityId(); + Map surface = null; + PushNotification.Builder toCustomer = null; + PushNotification.Builder toCourier = null; + // tag::morsel-push-messages[] + switch (state) { + case OFFERED: + toCourier = message(id, state, COURIER, "New delivery request", + "Pickup at " + delivery.getPickupAddress() + ". Open Morsel to accept.") + .ttlSeconds(offerSeconds); + break; + case ACCEPTED: + toCustomer = message(id, state, CUSTOMER, "Your courier is on the way", + name + " is coming" + (car.length() > 0 ? " in a " + car : "") + ". About " + + eta(delivery, courier, false) + " min."); + break; + case ARRIVED: + toCustomer = message(id, state, CUSTOMER, "Your courier is here", + car.length() > 0 ? "Look for " + car + "." : name + " is waiting for you."); + surface = update(activityId, state(state, name, who, 0)); + break; + case IN_PROGRESS: + toCustomer = message(id, state, CUSTOMER, "Your trip has started", + "Arriving at " + delivery.getDropoffAddress() + " in about " + + eta(delivery, courier, true) + " min."); + surface = update(activityId, state(state, name, who, eta(delivery, courier, true))); + break; + case COMPLETED: + toCustomer = message(id, state, CUSTOMER, "You've arrived", + "Thanks for riding with Morsel. Your receipt is in the app."); + surface = endOf(activityId, state, name, who); + break; + case CANCELLED_BY_COURIER: + toCustomer = message(id, state, CUSTOMER, "Your courier canceled", + "Sorry about that. Request again to find another courier."); + surface = endOf(activityId, state, name, who); + break; + case CANCELLED_BY_ADMIN: + toCustomer = message(id, state, CUSTOMER, "Your delivery was canceled", + "Morsel support canceled this delivery."); + toCourier = courier == null || courier.length() == 0 ? null + : message(id, state, COURIER, "Delivery canceled", + "Morsel support canceled this delivery."); + surface = endOf(activityId, state, name, who); + break; + case CANCELLED_BY_CUSTOMER: + toCourier = courier == null || courier.length() == 0 ? null + : message(id, state, COURIER, "Delivery canceled", + "The customer canceled. You're available for new requests."); + surface = endOf(activityId, state, name, who); + break; + case NO_COURIERS: + toCustomer = message(id, state, CUSTOMER, "No couriers available", + "Nobody could take your delivery right now. Try again in a few minutes."); + break; + default: + break; + } + // end::morsel-push-messages[] + if (!state.isActive() && activity != null) { + activities.markEnded(id, System.currentTimeMillis()); + } + if (toCustomer != null && !wants(customer, DeliveryState.COMPLETED == state)) { + toCustomer = null; + } + if (toCourier != null && !wants(courier, false)) { + toCourier = null; + } + if (toCustomer != null) { + if (surface != null) { + toCustomer.surface(surface); + } + out.add(new Outgoing(devices.targetsOf(customer), toCustomer.build(), + surface != null && !state.isActive() ? id : null)); + } else if (surface != null) { + // Nothing to say to the customer, but their lock screen still has to + // let go of the delivery. + out.add(new Outgoing(devices.targetsOf(customer), quiet(id, state).surface(surface) + .build(), state.isActive() ? null : id)); + } + if (toCourier != null) { + out.add(new Outgoing(devices.targetsOf(courier), toCourier.build())); + } + return out; + } + + // tag::morsel-push-preferences[] + /// Whether `username` asked to be told: of receipts when `receipt`, of + /// delivery updates otherwise. An account that never saved its settings gets + /// both, as the settings screen shows it. + private boolean wants(String username, boolean receipt) throws IOException { + Preference row = preferences.findById(username); + if (row == null) { + return true; + } + return receipt ? row.isNotifyReceipts() : row.isNotifyDeliveries(); + } + // end::morsel-push-preferences[] + + /// The silent update of a moving car, when one is due. + @Transactional + private List moved(String deliveryId, double lat, double lng) throws IOException { + List out = new ArrayList(); + DeliveryActivity activity = activities.findById(deliveryId); + if (activity == null) { + return out; + } + long now = System.currentTimeMillis(); + // A conditional update, so that two reports arriving together send one + // update between them. + if (activities.claim(deliveryId, now, now - activityMillis) != 1) { + return out; + } + Delivery delivery = deliveries.findById(deliveryId); + if (delivery == null || !(DeliveryState.ACCEPTED == delivery.getState() + || DeliveryState.IN_PROGRESS == delivery.getState())) { + return out; + } + Profile who = profiles.findById(delivery.getCourier()); + String name = who == null ? "Your courier" : who.getDisplayName(); + boolean toDropoff = DeliveryState.IN_PROGRESS == delivery.getState(); + int minutes = toDropoff + ? Deliveries.etaMinutes(lat, lng, delivery.getDropoffLat(), delivery.getDropoffLng()) + : Deliveries.etaMinutes(lat, lng, delivery.getPickupLat(), delivery.getPickupLng()); + out.add(new Outgoing(devices.targetsOf(delivery.getCustomer()), quiet(deliveryId, delivery.getState()) + .surface(PushNotification.liveUpdate(activity.getActivityId(), + state(delivery.getState(), name, who, minutes))) + .build())); + return out; + } + + /// A notification for the customer or the courier of a delivery: `role` is which, + /// for the app to open the side it is about. + private static PushNotification.Builder message(String id, DeliveryState state, String role, + String title, String body) { + return PushNotification.builder() + .title(title) + .body(body) + .collapseKey("delivery-" + id + "-" + role) + .data("deliveryId", id) + .data("state", state.name()) + .data("role", role); + } + + /// A message the phone acts on without showing anything. + private static PushNotification.Builder quiet(String id, DeliveryState state) { + return PushNotification.builder() + .silent(true) + .collapseKey("delivery-" + id + "-activity") + .data("deliveryId", id) + .data("state", state.name()) + .data("role", CUSTOMER); + } + + private static Map update(String activityId, Map state) { + return activityId == null ? null : PushNotification.liveUpdate(activityId, state); + } + + private static Map end(String activityId, Map state, + boolean dismiss) { + return activityId == null ? null : PushNotification.liveEnd(activityId, state, dismiss); + } + + /// The end of a Live Activity whose delivery ended in `state`: a completed + /// delivery stays on the lock screen a while showing its arrival, a cancelled + /// one goes at once. + private static Map endOf(String activityId, DeliveryState state, String name, + Profile who) { + return DeliveryState.COMPLETED == state + ? end(activityId, state(state, name, who, 0), false) + : end(activityId, null, true); + } + + /// The Live Activity's state, in the keys the app laid it out with. + static Map state(DeliveryState state, String name, Profile who, int minutes) { + Map out = new HashMap(); + String status; + double progress; + if (DeliveryState.ARRIVED == state) { + status = "Your courier is here"; + progress = DeliveryActivityKeys.ARRIVED; + } else if (DeliveryState.IN_PROGRESS == state) { + status = "On the trip"; + progress = DeliveryActivityKeys.IN_TRIP; + } else if (DeliveryState.COMPLETED == state) { + status = "You've arrived"; + progress = DeliveryActivityKeys.DONE; + } else { + status = "On the way"; + progress = DeliveryActivityKeys.ON_THE_WAY; + } + out.put(DeliveryActivityKeys.STATUS, status); + out.put(DeliveryActivityKeys.PROGRESS, Double.valueOf(progress)); + out.put(DeliveryActivityKeys.COURIER, name); + out.put(DeliveryActivityKeys.CAR, who == null ? "" : who.getVehicle()); + out.put(DeliveryActivityKeys.PLATE, who == null ? "" : who.getPlate()); + out.put(DeliveryActivityKeys.ETA, minutes > 0 ? minutes + " min" : ""); + return out; + } + + private int eta(Delivery delivery, String courier, boolean toDropoff) { + CourierState car = courier == null || courier.length() == 0 ? null : couriers.findById(courier); + if (car == null) { + return 1; + } + return toDropoff + ? Deliveries.etaMinutes(car.getLat(), car.getLng(), delivery.getDropoffLat(), + delivery.getDropoffLng()) + : Deliveries.etaMinutes(car.getLat(), car.getLng(), delivery.getPickupLat(), + delivery.getPickupLng()); + } + + private static final DeliveryState[] MEETING = { + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS + }; + + private static String describe(Profile who) { + String vehicle = who.getVehicle() == null ? "" : who.getVehicle().trim(); + String plate = who.getPlate() == null ? "" : who.getPlate().trim(); + if (vehicle.length() == 0) { + return plate; + } + return plate.length() == 0 ? vehicle : vehicle + ", " + plate; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/MisconfiguredPushSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/MisconfiguredPushSender.java new file mode 100644 index 00000000000..d7801dd6b10 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/MisconfiguredPushSender.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.push.PushNotification; +import com.codename1.backend.push.PushTarget; + +import java.util.List; + +/// What the server sends with when the push settings are incomplete: nothing +/// goes out, and the log says why. +/// +/// The problem is logged once when the server starts and again on every send, +/// naming the settings to fix. Unlike a text message, a notification that does +/// not go out costs nobody a sign-in, so it is not an error to the caller. +public class MisconfiguredPushSender implements PushSender { + private final String problem; + + /// @param problem what is wrong, naming the settings to fix + public MisconfiguredPushSender(String problem) { + this.problem = problem; + System.err.println("[config] " + problem); + } + + @Override + public void send(List targets, PushNotification message, Runnable delivered) { + if (targets != null && !targets.isEmpty()) { + System.err.println("[config] a push notification was not sent: " + problem); + } + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushDevices.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushDevices.java new file mode 100644 index 00000000000..74f53899a41 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushDevices.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Component; +import com.codename1.backend.annotations.Transactional; +import com.codename1.backend.push.PushTarget; +import com.codenameone.examples.morsel.Ids; +import com.codenameone.examples.morsel.Text; +import com.codenameone.examples.morsel.api.PushDeviceDto; +import com.codenameone.examples.morsel.domain.PushDevice; +import com.codenameone.examples.morsel.repository.ProfileRepository; +import com.codenameone.examples.morsel.repository.PushDeviceRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +// tag::morsel-push-devices[] +/// The devices each account is sent notifications on. +/// +/// The app registers its token when somebody signs in and removes it when they +/// sign out. A token registered by a second account moves to it: the phone +/// belongs to whoever signed in on it last, and the one before is not to be +/// told about the new owner's deliveries. +@Component +public class PushDevices { + /// How many devices one account keeps. A new one past this replaces the + /// one heard from longest ago: a token the platform has replaced is never + /// removed by its app, and would otherwise be sent to for good. + static final int MAX_PER_ACCOUNT = 8; + + private final PushDeviceRepository devices; + private final ProfileRepository profiles; + + public PushDevices(PushDeviceRepository devices, ProfileRepository profiles) { + this.devices = devices; + this.profiles = profiles; + } + + @Transactional + public PushDeviceDto register(String username, PushDeviceDto device) throws IOException { + PushTarget target = target(device); + String id = key(target); + long now = System.currentTimeMillis(); + // Before anything is read, the account's profile row is held: a no-op + // update that answers 0 when the profile is gone and otherwise keeps + // the row locked until this commits. It settles two races. + // + // Against closing the account: Accounts.erase holds the same row + // first and deletes it before it commits, so a registration either + // runs first (and erase's cleanup then deletes the device with the + // rest) or waits, finds no profile and is refused. Without it, a + // registration authorized just before the closure could insert its + // device after erase removed the account's, leaving a device of an + // account that no longer exists and that nobody can unregister. + // + // Against itself: two devices registering at once for an account one + // short of the cap would both count the same rows in makeRoom(), both + // skip the eviction and both insert, one past the cap. Behind the hold + // the second counts after the first committed, and evicts. + if (profiles.hold(username) == 0) { + throw new ResponseStatusException(404, "No such account"); + } + // The device row itself is not locked. Two accounts registering one + // token at the same moment -- a registration of the account that + // signed out still in flight as the next one signs in -- can leave it + // with the earlier account, but not for long: the app registers again + // each time it comes to the foreground, for whoever is signed in then. + PushDevice row = devices.findById(id); + if (row == null || !username.equals(row.getUsername())) { + // A device new to this account, whether its token is new or moves + // here from another account, takes a place under the cap. + makeRoom(username); + } + if (row == null) { + row = new PushDevice(); + row.setId(id); + row.setProvider(target.getProvider()); + row.setToken(target.getToken()); + fill(row, username, device, now); + devices.insert(row); + } else { + fill(row, username, device, now); + } + return new PushDeviceDto(target.getProvider(), target.getToken(), row.getPlatform()); + } + + /// Removes `username`'s devices heard from longest ago until one more fits + /// under [#MAX_PER_ACCOUNT]. + private void makeRoom(String username) { + List held = new ArrayList(devices.findByUsername(username)); + while (held.size() >= MAX_PER_ACCOUNT) { + PushDevice oldest = held.get(0); + for (PushDevice each : held) { + if (each.getUpdatedAt() < oldest.getUpdatedAt()) { + oldest = each; + } + } + devices.deleteById(oldest.getId()); + held.remove(oldest); + } + } + + /// Forgets a device, if it is this account's. Somebody else's is left alone. + @Transactional + public PushDeviceDto unregister(String username, PushDeviceDto device) throws IOException { + PushTarget target = target(device); + devices.deleteByIdAndUsername(key(target), username); + return new PushDeviceDto(target.getProvider(), target.getToken(), ""); + } + + /// Where to reach `username`: every device they registered. + @Transactional(readOnly = true) + public List targets(String username) { + return targetsOf(username); + } + + /// [#targets] for a caller already inside a transaction. + List targetsOf(String username) { + List out = new ArrayList(); + if (username == null || username.length() == 0) { + return out; + } + for (PushDevice each : devices.findByUsername(username)) { + out.add(PushTarget.of(each.getProvider(), each.getToken())); + } + return out; + } + // end::morsel-push-devices[] + + private static void fill(PushDevice row, String username, PushDeviceDto device, long now) { + row.setUsername(username); + row.setPlatform(Text.optional(device.platform(), 32, "the platform")); + row.setUpdatedAt(now); + } + + /// The device as a target of the push service, which also spells its + /// provider the one way: `gcm` and `fcm` are one device. + private static PushTarget target(PushDeviceDto device) { + if (device == null) { + throw new ResponseStatusException(400, "Tell us the device"); + } + String token = Text.required(device.token(), 1024, "the device's token"); + try { + return PushTarget.of(Text.required(device.provider(), 16, "the push provider"), token); + } catch (IllegalArgumentException err) { + throw new ResponseStatusException(400, err.getMessage()); + } + } + + private static String key(PushTarget target) throws IOException { + return Ids.sha256(target.getProvider() + ":" + target.getToken()); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushSender.java new file mode 100644 index 00000000000..a03c23171f6 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/PushSender.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.push.PushNotification; +import com.codename1.backend.push.PushTarget; + +import java.util.List; + +// tag::morsel-push-sender[] +/// Sends push notifications to devices. +/// +/// [com.codenameone.examples.morsel.Services] chooses the implementation from +/// `morsel.push.provider`. A notification is a courtesy on top of what the +/// app reads from the server, so sending one never fails the delivery it is about: +/// an implementation logs what went wrong and returns. +public interface PushSender { + /// Sends `message` to every one of `targets`. Returns at once; the sending + /// itself may happen later, on another thread. + /// + /// @param delivered run once the push service has taken the message, on + /// whatever thread learnt it; never when it was refused or could not be + /// sent. Null when nobody needs to know. + void send(List targets, PushNotification message, Runnable delivered); +} +// end::morsel-push-sender[] diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/RecordingPushSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/RecordingPushSender.java new file mode 100644 index 00000000000..85197da7117 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/push/RecordingPushSender.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.push; + +import com.codename1.backend.push.PushNotification; +import com.codename1.backend.push.PushTarget; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/// Keeps every notification instead of sending it, for the tests to read. +/// +/// What the `test` profile gets: a test signs a phone in, drives a delivery, and +/// asks this what each device was sent. Refused on any profile that is not a +/// development one. +public class RecordingPushSender implements PushSender { + private final List sent = new ArrayList(); + private int refusals; + private int unconfirmed; + + /// One notification as one device was sent it. + public static final class Sent { + private final String provider; + private final String token; + private final Map message; + + Sent(String provider, String token, Map message) { + this.provider = provider; + this.token = token; + this.message = message; + } + + public String provider() { + return provider; + } + + public String token() { + return token; + } + + /// The notification as it would go on the wire: `title`, `body`, + /// `data`, `surface` and the rest. + public Map message() { + return message; + } + } + + @Override + public void send(List targets, PushNotification message, Runnable delivered) { + // Outside the lock: what follows a delivery writes to the database. + if (record(targets, message) && delivered != null) { + delivered.run(); + } + } + + /// Records a send, and answers whether the push service would confirm it. + private synchronized boolean record(List targets, PushNotification message) { + if (refusals > 0) { + refusals--; + throw new IllegalStateException("The push provider is unavailable"); + } + if (targets != null) { + for (PushTarget target : targets) { + sent.add(new Sent(target.getProvider(), target.getToken(), message.toMap())); + } + } + if (unconfirmed > 0) { + unconfirmed--; + return false; + } + return true; + } + + /// What the device with `token` was sent, oldest first. + public synchronized List sentTo(String token) { + List out = new ArrayList(); + for (Sent each : sent) { + if (each.token.equals(token)) { + out.add(each); + } + } + return out; + } + + /// Everything sent so far, oldest first. + public synchronized List all() { + return new ArrayList(sent); + } + + public synchronized void clear() { + sent.clear(); + refusals = 0; + unconfirmed = 0; + } + + /// Makes the next `count` sends go out and never be confirmed, as a + /// service that refuses them later, on its own thread, does. + public synchronized void leaveUnconfirmed(int count) { + unconfirmed = count; + } + + /// Makes the next `count` sends throw, as a provider that is down does. + public synchronized void refuse(int count) { + refusals = count; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ClosedAccountRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ClosedAccountRepository.java new file mode 100644 index 00000000000..ee741c6c77e --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ClosedAccountRepository.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.ClosedAccount; + +/// The hashes of the e-mails that had an account once and may not have another. +@Repository(entity = ClosedAccount.class, key = String.class) +public interface ClosedAccountRepository extends CrudRepository { + +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierApplicationRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierApplicationRepository.java new file mode 100644 index 00000000000..d8dc73e31a0 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierApplicationRepository.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.PageRequest; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.CourierApplication; + +import java.util.List; + +/// Applications to drive, one per account at most, keyed by the account. +/// +/// Every move of an application from one status to another is one conditional +/// statement answering how many rows it changed, so that of two callers moving +/// the same application out of the same status the count is 1 for one of them +/// and 0 for the other. Each is marked `clearAutomatically`, which empties the +/// session after it, so an application read before a move is read again after +/// it, as the move left it. +@Repository(entity = CourierApplication.class, key = String.class) +public interface CourierApplicationRepository extends CrudRepository { + /// The applications of these accounts; none for a name that made none. + List findByUsernameIn(List usernames); + + /// The applications of every account that has a courier's state. + @Query("select a from CourierApplication a where a.username in " + + "(select d.username from CourierState d)") + List ofCouriers(); + + /// The name and status of every application, as `{username, status}`. + @Query("select a.username, a.status from CourierApplication a") + List statuses(); + + long countByStatus(String status); + + /// Applications of every status, the latest handed in first. Only those + /// of accounts that still have a profile, as the list names the + /// applicant. + @Query("select a from CourierApplication a where a.username in " + + "(select p.username from Profile p) order by a.submittedAt desc, a.username asc") + List newest(PageRequest page); + + /// Applications in one status, the longest waiting first; only those of + /// accounts that still have a profile. + @Query("select a from CourierApplication a where a.status = :status " + + "and a.username in (select p.username from Profile p) " + + "order by a.submittedAt asc, a.username asc") + List inStatus(@Param("status") String status, PageRequest page); + + /// Moves an application from status `from` to `to`, recording the review. + @Modifying(clearAutomatically = true) + @Query("update CourierApplication a set a.status = :to, a.rejectionReason = :reason, " + + "a.reviewedAt = :at, a.reviewedBy = :by " + + "where a.username = :username and a.status = :from") + int move(@Param("username") String username, @Param("from") String from, + @Param("to") String to, @Param("reason") String reason, @Param("at") long at, + @Param("by") String by); + + /// Marks an application `approved` unless it already is. + @Modifying(clearAutomatically = true) + @Query("update CourierApplication a set a.status = :to, a.rejectionReason = '', " + + "a.reviewedAt = :at, a.reviewedBy = :by " + + "where a.username = :username and a.status <> :to") + int approveUnlessApproved(@Param("username") String username, + @Param("to") String approved, @Param("at") long at, @Param("by") String by); + + /// Hands an application in: from the status it was read in to `to`, with + /// the last rejection's reason cleared. + @Modifying(clearAutomatically = true) + @Query("update CourierApplication a set a.status = :to, a.submittedAt = :at, " + + "a.rejectionReason = '' where a.username = :username and a.status = :from") + int submit(@Param("username") String username, @Param("from") String from, + @Param("to") String to, @Param("at") long at); + + /// Makes the rest of the transaction wait its turn behind any other that + /// holds, or moves, the same application. + /// + /// @return 1, or 0 for an account that has no application yet + @Modifying + @Query("update CourierApplication a set a.status = a.status where a.username = :username") + int hold(@Param("username") String username); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierDocumentRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierDocumentRepository.java new file mode 100644 index 00000000000..a5b18357fab --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierDocumentRepository.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codename1.orm.session.Identifier; +import com.codenameone.examples.morsel.domain.CourierDocument; + +import java.util.List; + +/// The documents of applications to drive, keyed by +/// `Identifier.of(username, kind)`. +/// +/// A document's row carries the picture, as Base64 text that can run to a +/// couple of megabytes. Only `findById` reads it. Everything that wants to know +/// which documents there are and how they stand selects those fields alone, so +/// listing two hundred applications does not pull two hundred sets of +/// photographs through the server. +@Repository(entity = CourierDocument.class, key = Identifier.class) +public interface CourierDocumentRepository extends CrudRepository { + /// Which documents an account has uploaded, as `{kind, status, + /// uploadedAt}`, without the pictures. + @Query("select d.kind, d.status, d.uploadedAt from CourierDocument d " + + "where d.username = :username") + List summaries(@Param("username") String username); + + /// The same for several accounts at once, as `{username, kind, status, + /// uploadedAt}`. + @Query("select d.username, d.kind, d.status, d.uploadedAt from CourierDocument d " + + "where d.username in :names") + List summaries(@Param("names") List usernames); + + /// Puts a new picture in place of a stored document's, and answers how + /// many rows that changed: 0 says there was none of that kind to replace. + /// A statement, so that the picture being replaced is never read. + @Modifying(clearAutomatically = true) + @Query("update CourierDocument d set d.contentType = :type, d.status = :status, " + + "d.uploadedAt = :at, d.data = :data where d.username = :username and d.kind = :kind") + int replace(@Param("username") String username, @Param("kind") String kind, + @Param("type") String contentType, @Param("status") String status, + @Param("at") long at, @Param("data") String data); + + /// Sets the status of all of an account's documents. + @Modifying(clearAutomatically = true) + @Query("update CourierDocument d set d.status = :status where d.username = :username") + int markAll(@Param("username") String username, @Param("status") String status); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierHoursRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierHoursRepository.java new file mode 100644 index 00000000000..d935e163a76 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierHoursRepository.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codename1.orm.session.Identifier; +import com.codenameone.examples.morsel.domain.CourierHours; + +/// The time each courier spent on line, a row for each day, keyed by +/// `Identifier.of(username, dayNumber)`. +@Repository(entity = CourierHours.class, key = Identifier.class) +public interface CourierHoursRepository extends CrudRepository { + /// Adds time on line to a day that already has a row. The addition is the + /// database's, so reports that overlap are both counted. + /// + /// @return 1, or 0 when the day has no row yet + @Modifying + @Query("update CourierHours h set h.millis = h.millis + :millis " + + "where h.username = :username and h.dayNumber = :day") + int addMillis(@Param("username") String username, @Param("day") long dayNumber, + @Param("millis") long millis); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierStateRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierStateRepository.java new file mode 100644 index 00000000000..a8d2b4f7b23 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/CourierStateRepository.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.CourierState; + +import java.util.List; + +/// Couriers' live state: whether each is on line, where the car is, and the +/// rating. +@Repository(entity = CourierState.class, key = String.class) +public interface CourierStateRepository extends CrudRepository { + /// Every account that has a state, those on line first and then the ones + /// heard from most recently. + @Query("select d from CourierState d order by d.online desc, d.lastSeen desc") + List onLineFirst(); + + /// The couriers on line, heard from since `fresh`, inside a box of + /// latitude and longitude, whose application stands approved, who still + /// have a profile, and who have no delivery in hand: one offer, or one delivery, + /// at a time. + /// + /// The approval is read here, in the query, and not trusted to have been + /// put right elsewhere: an approval withdrawn takes a courier out of + /// matching at once, whatever their state still says. A state left behind + /// by an account that is gone is not somebody to send a customer to. + /// + /// What the customer asked of the car and the courier is the caller's to + /// apply to the few this answers. + @Query("select d from CourierState d where d.online = TRUE and d.lastSeen >= :fresh " + + "and d.lat between :south and :north and d.lng between :west and :east " + + "and d.username in (select a.username from CourierApplication a " + + "where a.status = :approved) " + + "and d.username in (select p.username from Profile p) " + + "and d.username not in (select r.courier from Delivery r where r.state in :withDelivery)") + List freeInBox(@Param("fresh") long fresh, @Param("south") double south, + @Param("north") double north, @Param("west") double west, + @Param("east") double east, @Param("approved") String approved, + @Param("withDelivery") DeliveryState[] withDelivery); + + /// Makes the rest of the transaction wait its turn behind any other that + /// holds the same courier, as [ProfileRepository#hold] does for an + /// account. + /// + /// @return 1, or 0 for a courier who has no state + @Modifying + @Query("update CourierState d set d.lastSeen = d.lastSeen where d.username = :username") + int hold(@Param("username") String username); + + /// Counts a rating toward a courier's. Arithmetic done by the database, so + /// two ratings arriving together are both counted. + /// + /// @return 1, or 0 for a courier who has no state to count it in + @Modifying + @Query("update CourierState d set d.ratingSum = d.ratingSum + :stars, " + + "d.ratingCount = d.ratingCount + 1 where d.username = :username") + int addRating(@Param("username") String username, @Param("stars") long stars); + + /// Takes a courier off line. Changes nothing for an account that never + /// drove. + @Modifying + @Query("update CourierState d set d.online = FALSE where d.username = :username") + int takeOffLine(@Param("username") String username); + + /// The couriers on line who reported in at or after `seenSince`. + long countByOnlineTrueAndLastSeenGreaterThanEqual(long seenSince); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryActivityRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryActivityRepository.java new file mode 100644 index 00000000000..d16d3a07531 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryActivityRepository.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.PageRequest; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.DeliveryActivity; + +import java.util.List; + +/// The Live Activities customers follow their deliveries in, by delivery. +@Repository(entity = DeliveryActivity.class, key = String.class) +public interface DeliveryActivityRepository extends CrudRepository { + int deleteByDeliveryId(String deliveryId); + + int deleteByUsername(String username); + + /// Records that a state went out, unless one went out after `before`: + /// 1 for the caller who gets to send, 0 for anyone else at the same moment. + @Modifying + @Query("update DeliveryActivity a set a.sentAt = :now " + + "where a.deliveryId = :deliveryId and a.sentAt < :before") + int claim(@Param("deliveryId") String deliveryId, @Param("now") long now, + @Param("before") long before); + + /// Records that the delivery ended at `now`, unless that is already recorded. + @Modifying + @Query("update DeliveryActivity a set a.endedAt = :now " + + "where a.deliveryId = :deliveryId and a.endedAt = 0") + int markEnded(@Param("deliveryId") String deliveryId, @Param("now") long now); + + /// The activities whose delivery ended before `before` and that are still + /// here, because the phone was not told: oldest first. + @Query("select a from DeliveryActivity a where a.endedAt > 0 and a.endedAt < :before " + + "order by a.endedAt") + List leftBehind(@Param("before") long before, PageRequest page); + + /// Forgets the activities whose delivery ended before `before`. + @Modifying + @Query("delete from DeliveryActivity a where a.endedAt > 0 and a.endedAt < :before") + int dropEndedBefore(@Param("before") long before); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryEventRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryEventRepository.java new file mode 100644 index 00000000000..a71de7cc4ae --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryEventRepository.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.DeliveryEvent; + +import java.util.List; + +/// What happened to each delivery: one row for every state it came into. +@Repository(entity = DeliveryEvent.class, key = String.class) +public interface DeliveryEventRepository extends CrudRepository { + long countByActorAndState(String actor, DeliveryState state); + + /// How many offers each courier has accepted, as `{courier, count}`. + @Query("select e.actor, count(e) from DeliveryEvent e where e.state = :accepted " + + "group by e.actor") + List countByActor(@Param("accepted") DeliveryState accepted); + + /// When each delivery asked for since `since` came into `state`: `{deliveryId, + /// createdAt}`. The entities have no relationship to join along, so the + /// deliveries are named by a nested query. + @Query("select e.deliveryId, e.createdAt from DeliveryEvent e where e.state = :state " + + "and e.deliveryId in (select r.id from Delivery r where r.requestedAt >= :since)") + List reached(@Param("since") long since, @Param("state") DeliveryState state); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryPassedRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryPassedRepository.java new file mode 100644 index 00000000000..5d7805ee546 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryPassedRepository.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codename1.orm.session.Identifier; +import com.codenameone.examples.morsel.domain.DeliveryPassed; + +import java.util.List; + +/// The couriers who passed on a delivery or let its offer lapse, keyed by +/// `Identifier.of(deliveryId, courier)`. +@Repository(entity = DeliveryPassed.class, key = Identifier.class) +public interface DeliveryPassedRepository extends CrudRepository { + List findByDeliveryId(String deliveryId); + + long countByCourier(String courier); + + /// How many offers each courier has passed on or let lapse, as `{courier, + /// count}`. + @Query("select p.courier, count(p.deliveryId) from DeliveryPassed p group by p.courier") + List countByCourier(); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryRepository.java new file mode 100644 index 00000000000..c05313cbaa5 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/DeliveryRepository.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.PageRequest; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.domain.Delivery; + +import java.util.List; + +/// Deliveries. +/// +/// Which state may follow which is `DeliveryStateMachine`'s to say; what is here is +/// the statement that makes a move, told the states it may be made from. Every +/// move is one conditional update, and the count it answers is the whole +/// answer: 1 when this call made the move, 0 when the delivery was somewhere else by +/// then. The database applies the rule and settles a race in the same breath: +/// of two couriers accepting at once, or a customer cancelling as a courier accepts, +/// one statement changes a row and the other changes none. Each is marked +/// `clearAutomatically`, which empties the session after it, so a delivery read +/// before it is read again after it, as the statement left it. +/// +/// A move comes in one of five shapes -- the customer's, the courier's, the +/// courier's taking an offer, the courier's giving one back, and the server's -- +/// each its own statement rather than one assembled at run time. +// tag::morsel-repository[] +@Repository(entity = Delivery.class, key = String.class) +public interface DeliveryRepository extends CrudRepository { + /// The delivery a customer has in one of `states`, or null. + Delivery findFirstByCustomerAndStateIn(String customer, DeliveryState[] states); + + /// The delivery a courier has in one of `states`, or null. + Delivery findFirstByCourierAndStateIn(String courier, DeliveryState[] states); + // end::morsel-repository[] + + /// Whether the account is the customer of a delivery in one of `asCustomer` or the + /// courier of one in one of `asCourier`. + boolean existsByCustomerAndStateInOrCourierAndStateIn(String customer, DeliveryState[] asCustomer, + String courier, DeliveryState[] asCourier); + + boolean existsByCourierAndStateIn(String courier, DeliveryState[] states); + + /// Whether a delivery of the customer's in one of `states` is to be paid with + /// this method. + boolean existsByCustomerAndMethodIdAndStateIn(String customer, String methodId, + DeliveryState[] states); + + /// Whether a completed delivery of the customer's, whose payment is + /// `paymentStatus` and which ended after `since`, is to be charged to + /// this method. + boolean existsByCustomerAndStateAndPaymentStatusAndUpdatedAtGreaterThanAndMethodId( + String customer, DeliveryState state, String paymentStatus, long since, String methodId); + + /// The same, to be charged to any method but `methodId`. + boolean existsByCustomerAndStateAndPaymentStatusAndUpdatedAtGreaterThanAndMethodIdNot( + String customer, DeliveryState state, String paymentStatus, long since, String methodId); + + /// A customer's deliveries whose payment is `paymentStatus` and that carry a tip. + List findByCustomerAndPaymentStatusAndTipCentsGreaterThan(String customer, + String paymentStatus, long tipCents); + + /// A customer's last fifty deliveries, newest first. + List findTop50ByCustomerOrderByRequestedAtDesc(String customer); + + /// The last fifty of a courier's deliveries in one of `states`, newest first. + List findTop50ByCourierAndStateInOrderByRequestedAtDesc(String courier, + DeliveryState[] states); + + /// A customer's deliveries in `state`, newest first. + List findByCustomerAndStateOrderByRequestedAtDesc(String customer, DeliveryState state, + PageRequest page); + + /// Everyone's deliveries in `state`, newest first. + List findByStateOrderByRequestedAtDesc(DeliveryState state, PageRequest page); + + /// The most recent deliveries of all. + @Query("select r from Delivery r order by r.requestedAt desc") + List newest(PageRequest page); + + /// The deliveries in `state` whose offer ran out before `now`. + List findByStateAndOfferExpiresAtLessThan(DeliveryState state, long now); + + /// The deliveries in `state`, the one that has waited longest first. + List findByStateOrderByRequestedAtAsc(DeliveryState state); + + /// A courier's deliveries in `state` that changed at or after a time. + List findByCourierAndStateAndUpdatedAtGreaterThanEqual(String courier, + DeliveryState state, long since); + + long countByCourierAndState(String courier, DeliveryState state); + + long countByRequestedAtGreaterThanEqual(long since); + + long countByStateIn(DeliveryState[] states); + + /// The deliveries an account took part in, on either side. + long countByCustomerOrCourier(String customer, String courier); + + /// The deliveries an account itself called off, as the customer or as the courier. + long countByCustomerAndStateOrCourierAndState(String customer, DeliveryState asCustomer, String courier, + DeliveryState asCourier); + + // ---------------------------------------------------------------- moves + + /// Moves a delivery of `customer`'s from one of `from` to `to`. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.state = :to, r.updatedAt = :now " + + "where r.id = :id and r.state in :from and r.customer = :customer") + int moveAsCustomer(@Param("id") String id, @Param("from") DeliveryState[] from, + @Param("to") DeliveryState to, @Param("now") long now, @Param("customer") String customer); + + /// Moves a delivery of `courier`'s from one of `from` to `to`. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.state = :to, r.updatedAt = :now " + + "where r.id = :id and r.state in :from and r.courier = :courier") + int moveAsCourier(@Param("id") String id, @Param("from") DeliveryState[] from, + @Param("to") DeliveryState to, @Param("now") long now, @Param("courier") String courier); + + // tag::morsel-delivery-move[] + /// Takes an offer made to `courier`: refused once the offer has lapsed, as + /// of `now`, even in the moment before the sweep takes it back. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.state = :to, r.updatedAt = :now " + + "where r.id = :id and r.state in :from and r.courier = :courier " + + "and r.offerExpiresAt >= :now") + int takeOffer(@Param("id") String id, @Param("from") DeliveryState[] from, + @Param("to") DeliveryState to, @Param("now") long now, @Param("courier") String courier); + // end::morsel-delivery-move[] + + /// Gives a delivery of `courier`'s back to be offered again, writing + /// `newCourier` (empty: nobody) as its courier. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.state = :to, r.updatedAt = :now, r.courier = :newCourier " + + "where r.id = :id and r.state in :from and r.courier = :courier") + int giveBack(@Param("id") String id, @Param("from") DeliveryState[] from, + @Param("to") DeliveryState to, @Param("now") long now, @Param("courier") String courier, + @Param("newCourier") String newCourier); + + /// Holds `customer`'s delivery while it is in one of `states`, answering 0 when + /// it is not. The update writes nothing new; it is there for the row lock + /// it takes until the transaction ends, so a move that would take the delivery + /// out of `states` waits for this transaction, or has already happened + /// and makes it answer 0. The state is checked by the database at that + /// moment, never from a copy read earlier. + @Modifying + @Query("update Delivery r set r.customer = :customer " + + "where r.id = :id and r.customer = :customer and r.state in :states") + int holdWhile(@Param("id") String id, @Param("customer") String customer, + @Param("states") DeliveryState[] states); + + /// A move the server makes itself, whoever has the delivery. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.state = :to, r.updatedAt = :now " + + "where r.id = :id and r.state in :from") + int moveByServer(@Param("id") String id, @Param("from") DeliveryState[] from, + @Param("to") DeliveryState to, @Param("now") long now); + + /// Offers a delivery to `courier` until `expiresAt`. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.state = :to, r.updatedAt = :now, r.courier = :courier, " + + "r.offerExpiresAt = :expiresAt where r.id = :id and r.state in :from") + int offer(@Param("id") String id, @Param("from") DeliveryState[] from, + @Param("to") DeliveryState to, @Param("now") long now, @Param("courier") String courier, + @Param("expiresAt") long expiresAt); + + // ----------------------------------------------------------- the rest + + /// Gives a completed delivery its stars, if it has none yet. One statement, + /// for the reason a move is one: a delivery rated twice at the same moment is + /// rated once. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.stars = :stars where r.id = :id and r.customer = :customer " + + "and r.state = 'COMPLETED' and r.stars = 0") + int rate(@Param("id") String id, @Param("customer") String customer, @Param("stars") int stars); + + /// Completed deliveries whose payment has been pending since before `before` + /// and no longer than since `after`, oldest first: the sweep takes one + /// page at a time, and a backlog read newest first would keep the oldest + /// behind the new arrivals until they aged out of the window unsettled. + @Query("select r from Delivery r where r.state = 'COMPLETED' and r.paymentStatus = 'PENDING' " + + "and r.updatedAt < :before and r.updatedAt > :after order by r.updatedAt asc") + List unsettled(@Param("after") long after, @Param("before") long before, + PageRequest page); + + /// Records how the charge for a delivery came out. A statement of its own and + /// not a change to a delivery read earlier, so that it writes this one column + /// whatever else was written to the delivery meanwhile. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.paymentStatus = :status where r.id = :id") + int paymentStatus(@Param("id") String id, @Param("status") String status); + + /// Sets the tip of a completed, paid delivery that has none. + /// + /// @return 1 for the one caller whose tip it now is + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.tipCents = :tip where r.id = :id and r.customer = :customer " + + "and r.state = 'COMPLETED' and r.paymentStatus = 'PAID' and r.tipCents = 0") + int claimTip(@Param("id") String id, @Param("customer") String customer, @Param("tip") long tip); + + /// Takes back a tip that could not be charged. + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.tipCents = 0 where r.id = :id") + int releaseTip(@Param("id") String id); + + /// Marks a paid delivery refunded. + /// + /// @return 1 for the one caller that is to refund it + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.paymentStatus = 'REFUNDED' where r.id = :id " + + "and r.paymentStatus = 'PAID'") + int claimRefund(@Param("id") String id); + + /// Gives a refund's claim back: the delivery is paid again, if it is still + /// marked refunded. + /// + /// @return 1 when the delivery was + @Modifying(clearAutomatically = true) + @Query("update Delivery r set r.paymentStatus = 'PAID' where r.id = :id " + + "and r.paymentStatus = 'REFUNDED'") + int releaseRefund(@Param("id") String id); + + // ---------------------------------------------------------------- sums + // A sum over no rows is null and not zero, on every database, so each + // one is wrapped in `coalesce(..., 0)`. + + /// How many deliveries each courier has in `state`, as `{courier, count}`. + @Query("select r.courier, count(r) from Delivery r where r.state = :state group by r.courier") + List countByCourier(@Param("state") DeliveryState state); + + /// How many of a courier's completed deliveries were paid for, and what the + /// courier's share of their fares came to: `{count, cents}`. + /// + /// The share and not the fare: this is the figure the onboarding screen + /// shows as earned, and the fare includes the service's commission, + /// which the courier never sees and cannot cash out. + @Query("select count(r), coalesce(sum(r.courierCents), 0) from Delivery r " + + "where r.courier = :courier and r.state = 'COMPLETED' and r.paymentStatus = 'PAID'") + Object[] paidTotals(@Param("courier") String courier); + + /// [#paidTotals] for the deliveries that ended since a time: `{count, cents}`. + @Query("select count(r), coalesce(sum(r.courierCents), 0) from Delivery r " + + "where r.courier = :courier and r.state = 'COMPLETED' and r.paymentStatus = 'PAID' " + + "and r.updatedAt >= :since") + Object[] paidTotalsSince(@Param("courier") String courier, @Param("since") long since); + + /// What the courier's share of their completed, paid deliveries came to since + /// a time. + @Query("select coalesce(sum(r.courierCents), 0) from Delivery r where r.courier = :courier " + + "and r.state = 'COMPLETED' and r.paymentStatus = 'PAID' and r.updatedAt >= :since") + Long paidSince(@Param("courier") String courier, @Param("since") long since); + + /// The courier's share of the fares of every delivery they completed and that + /// was paid for -- in cash, or by a card whose charge came back. A charge + /// that failed, was refunded, or has not been answered yet is no money of + /// the service's, so it is not the courier's to cash out either. + @Query("select coalesce(sum(r.courierCents), 0) from Delivery r where r.courier = :courier " + + "and r.state = 'COMPLETED' and r.paymentStatus = 'PAID'") + Long courierShare(@Param("courier") String courier); + + /// The tips handed to a courier in cash. + @Query("select coalesce(sum(r.tipCents), 0) from Delivery r where r.courier = :courier " + + "and r.state = 'COMPLETED' and r.methodId = 'cash'") + Long cashTips(@Param("courier") String courier); + + /// The tips put on a card for a courier whose charge has come back, while + /// the delivery is still paid: a refund returns the tip with the fare. + @Query("select coalesce(sum(r.tipCents), 0) from Delivery r where r.courier = :courier " + + "and r.state = 'COMPLETED' and r.methodId <> 'cash' and r.paymentStatus = 'PAID' " + + "and r.id in (select p.deliveryId from Payment p where p.tipRef <> '')") + Long chargedTips(@Param("courier") String courier); + + /// The ids of a courier's deliveries, completed since a time, whose tip was put + /// on a card and whose charge has come back: [#chargedTips] by delivery. + @Query("select r.id from Delivery r where r.courier = :courier and r.state = 'COMPLETED' " + + "and r.updatedAt >= :since and r.methodId <> 'cash' and r.paymentStatus = 'PAID' " + + "and r.id in (select p.deliveryId from Payment p where p.tipRef <> '')") + List tipChargedSince(@Param("courier") String courier, @Param("since") long since); + + /// What customers paid in fares for deliveries in `state` whose payment is `paid`. + @Query("select coalesce(sum(r.fareCents), 0) from Delivery r " + + "where r.state = :state and r.paymentStatus = :paid") + Long fares(@Param("state") DeliveryState state, @Param("paid") String paid); + + /// The fares of the deliveries in `state` asked for since `since`, paid or not. + @Query("select coalesce(sum(r.fareCents), 0) from Delivery r " + + "where r.requestedAt >= :since and r.state = :state") + Long faresSince(@Param("since") long since, @Param("state") DeliveryState state); + + /// The deliveries asked for since `since`, counted by state: `{state, count}`. + @Query("select r.state, count(r) from Delivery r where r.requestedAt >= :since group by r.state") + List countByState(@Param("since") long since); + + /// The same by product: `{product, count}`. + @Query("select r.product, count(r) from Delivery r where r.requestedAt >= :since " + + "group by r.product") + List countByProduct(@Param("since") long since); + + /// The deliveries in `state` asked for since `since`, counted by courier: + /// `{courier, count}`. + @Query("select r.courier, count(r) from Delivery r where r.requestedAt >= :since " + + "and r.state = :state group by r.courier") + List countByCourier(@Param("since") long since, @Param("state") DeliveryState state); + + /// One row for each delivery asked for since `since`: `{id, requestedAt, + /// state, fareCents, paymentStatus}`. + /// + /// This is what the charts by day and by hour of the day are counted + /// from, in Java. Grouping by a day would mean grouping by `requestedAt` + /// divided by the length of one, and a query cannot say that portably: + /// the number is sent as a bound value, once in the select list and once + /// in the `group by`, and a database that takes a statement's text at its + /// word does not see those as one expression. + @Query("select r.id, r.requestedAt, r.state, r.fareCents, r.paymentStatus from Delivery r " + + "where r.requestedAt >= :since") + List timeline(@Param("since") long since); + + /// What a customer paid for deliveries in `state` whose payment is `paid`: + /// `{fares, fees, tips}`. + @Query("select coalesce(sum(r.fareCents), 0), coalesce(sum(r.feeCents), 0), " + + "coalesce(sum(r.tipCents), 0) from Delivery r " + + "where r.customer = :who and r.state = :state and r.paymentStatus = :paid") + Object[] spent(@Param("who") String username, @Param("state") DeliveryState state, + @Param("paid") String paid); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/LiveTicketRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/LiveTicketRepository.java new file mode 100644 index 00000000000..59bf4f9023a --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/LiveTicketRepository.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.LiveTicket; + +/// The tickets that open a live connection. Only the hash of a ticket is +/// stored, and it is the row's key. +@Repository(entity = LiveTicket.class, key = String.class) +public interface LiveTicketRepository extends CrudRepository { + /// Deletes a ticket and answers how many rows that removed: 1 for the one + /// caller that used it, 0 for anyone presenting it after. A statement and + /// not a read followed by a removal, because that count is the whole + /// point. + int deleteByTicketHash(String ticketHash); + + /// Deletes the tickets that went stale before `now`. + int deleteByExpiresAtLessThan(long now); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ModerationEventRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ModerationEventRepository.java new file mode 100644 index 00000000000..723e7f0239d --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ModerationEventRepository.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.ModerationEvent; + +import java.util.List; + +/// The record of what the admins did to each account. +@Repository(entity = ModerationEvent.class, key = String.class) +public interface ModerationEventRepository extends CrudRepository { + /// When the newest event of an account was written, or null when it has + /// none. + @Query("select max(e.createdAt) from ModerationEvent e where e.username = :username") + Long newest(@Param("username") String username); + + /// An account's last hundred events, newest first. + List findTop100ByUsernameOrderByCreatedAtDescIdAsc(String username); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentCustomerRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentCustomerRepository.java new file mode 100644 index 00000000000..c7e75251580 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentCustomerRepository.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.PaymentCustomer; + +/// The payment processor's names for accounts. +@Repository(entity = PaymentCustomer.class, key = String.class) +public interface PaymentCustomerRepository extends CrudRepository { + /// Writes an account's row without changing it, which holds the row until + /// the transaction ends: whatever else writes it waits its turn. + @Modifying + @Query("update PaymentCustomer c set c.reference = c.reference where c.username = :username") + int hold(@Param("username") String username); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentMethodRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentMethodRepository.java new file mode 100644 index 00000000000..54c6c49607c --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentMethodRepository.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.PaymentMethod; + +import java.util.List; + +/// The cards accounts saved. +@Repository(entity = PaymentMethod.class, key = String.class) +public interface PaymentMethodRepository extends CrudRepository { + /// An account's saved cards, oldest first. + List findByUsernameOrderByCreatedAtAscIdAsc(String username); + + long countByUsername(String username); + + /// The card an account pays with by default, or null when that is cash. + PaymentMethod findFirstByUsernameAndPreferredTrue(String username); + + /// One of an account's cards, or null when the id is not a card of theirs. + PaymentMethod findFirstByIdAndUsername(String id, String username); + + /// When the newest of an account's cards was added, or null with none. + @Query("select max(m.createdAt) from PaymentMethod m where m.username = :username") + Long newestAt(@Param("username") String username); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentRepository.java new file mode 100644 index 00000000000..f5934aceb26 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentRepository.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.PageRequest; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.Payment; + +import java.util.List; + +/// What each delivery was charged, keyed by the delivery. +@Repository(entity = Payment.class, key = String.class) +public interface PaymentRepository extends CrudRepository { + /// The payments of several deliveries, in no order. + List findByDeliveryIdIn(List deliveryIds); + + /// Payments whose refund the processor took and has not finished, the + /// oldest first. + List findByStatusOrderByCreatedAtAsc(String status, PageRequest page); + + /// Records how a pending refund ended. + /// + /// @return 1 for the one caller that recorded it + @Modifying + @Query("update Payment p set p.status = :status, p.refundedAt = :at " + + "where p.deliveryId = :id and p.status = 'REFUND_PENDING'") + int endRefund(@Param("id") String deliveryId, @Param("status") String status, + @Param("at") long at); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentSetupRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentSetupRepository.java new file mode 100644 index 00000000000..8f3d0d5a46f --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PaymentSetupRepository.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.PaymentSetup; + +/// The card setups accounts began with the payment processor. +@Repository(entity = PaymentSetup.class, key = String.class) +public interface PaymentSetupRepository extends CrudRepository { + /// A card setup an account began, or null. + PaymentSetup findFirstByIdAndUsername(String id, String username); + + /// Marks a setup finished, if it is not yet. + /// + /// @return 1 for the one caller that finished it + @Modifying + @Query("update PaymentSetup s set s.completed = TRUE where s.id = :id and s.completed = FALSE") + int claim(@Param("id") String id); + + /// How many setups an account began after `since` and has not finished. + long countByUsernameAndCompletedFalseAndCreatedAtGreaterThan(String username, long since); + + int deleteByUsername(String username); + + /// Deletes the setups that saved their card. + int deleteByCompletedTrue(); + + /// Deletes the setups begun before `before`, finished or not. + int deleteByCreatedAtLessThan(long before); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutAccountRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutAccountRepository.java new file mode 100644 index 00000000000..5701ad7bfa2 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutAccountRepository.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.PayoutAccount; + +/// Where each courier is paid, keyed by the courier. +@Repository(entity = PayoutAccount.class, key = String.class) +public interface PayoutAccountRepository extends CrudRepository { + /// Makes the rest of the transaction wait its turn behind any other that + /// holds the same courier's payout account. + /// + /// @return 1, or 0 for a courier who has not said where to be paid + @Modifying + @Query("update PayoutAccount a set a.last4 = a.last4 where a.username = :courier") + int hold(@Param("courier") String courier); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutRepository.java new file mode 100644 index 00000000000..034950e12b3 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PayoutRepository.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.Payout; + +import java.util.List; + +/// The payouts made to couriers. +@Repository(entity = Payout.class, key = String.class) +public interface PayoutRepository extends CrudRepository { + /// The last hundred payouts made to a courier, newest first. + List findTop100ByCourierOrderByCreatedAtDescIdAsc(String courier); + + /// Everything ever paid out to a courier. A sum over no rows is nothing at + /// all to a database, hence the zero to fall back on. + @Query("select coalesce(sum(p.amountCents), 0) from Payout p where p.courier = :courier") + Long paidOut(@Param("courier") String courier); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PhoneCodeRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PhoneCodeRepository.java new file mode 100644 index 00000000000..529c2477992 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PhoneCodeRepository.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.PhoneCode; + +/// The code an account was texted to prove its phone number. An account has at +/// most one. +@Repository(entity = PhoneCode.class, key = String.class) +public interface PhoneCodeRepository extends CrudRepository { + /// Takes one of the guesses an account's code allows. The database does + /// the sum and applies the limit in the same statement, so of any number + /// of guesses sent at once only as many as are left are let through; + /// nothing is read and written back. + /// + /// The guess is taken of the code whose hash is `codeHash`, and of no + /// other: a new code sent between reading the hash and taking the guess + /// has another hash, and the update changes nothing. + /// + /// The code before it, when one is kept, is guessed at by the same guess, + /// so its count goes up too; its own limit is applied by the caller. + /// + /// Empties the session, so the code is read again after it with the guess + /// counted. + /// + /// @return 1 for a guess taken, 0 when none is left or there is no code + @Modifying(clearAutomatically = true) + @Query("update PhoneCode c set c.attempts = c.attempts + 1, " + + "c.previousAttempts = c.previousAttempts + 1 " + + "where c.username = :username and c.codeHash = :hash and c.attempts < :most") + int takeGuess(@Param("username") String username, @Param("hash") String codeHash, + @Param("most") int most); + + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PreferenceRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PreferenceRepository.java new file mode 100644 index 00000000000..0cc9f5ced64 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PreferenceRepository.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.Preference; + +/// An account's preferences, keyed by its name. An account that never saved any +/// has no row. +@Repository(entity = Preference.class, key = String.class) +public interface PreferenceRepository extends CrudRepository { + int deleteByUsername(String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PricingRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PricingRepository.java new file mode 100644 index 00000000000..2c76d10f427 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PricingRepository.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.Pricing; + +/// The admin's prices: one row, whose key is [#ONLY]. A migration creates it, +/// with an `updatedAt` of 0 while the prices are still the settings'. +@Repository(entity = Pricing.class, key = Integer.class) +public interface PricingRepository extends CrudRepository { + /// The key of the one row there is. + Integer ONLY = Integer.valueOf(1); + + /// Writes new prices over the one row, in one statement that reads + /// nothing first: two admins saving together each write theirs, and the + /// later one is what stays. + /// + /// @return 1, or 0 when the row is missing + @Modifying + @Query("update Pricing p set p.baseCents = :base, p.perKmCents = :perKm, " + + "p.perMinuteCents = :perMinute, p.minimumCents = :minimum, " + + "p.feePercent = :fee, p.commissionPercent = :commission, p.surge = :surge, " + + "p.comfort = :comfort, p.xl = :xl, p.updatedAt = :at, p.updatedBy = :by " + + "where p.id = :id") + int change(@Param("id") int id, @Param("base") long base, @Param("perKm") long perKm, + @Param("perMinute") long perMinute, @Param("minimum") long minimum, + @Param("fee") double fee, @Param("commission") double commission, + @Param("surge") double surge, @Param("comfort") double comfort, + @Param("xl") double xl, @Param("at") long at, @Param("by") String by); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ProfileRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ProfileRepository.java new file mode 100644 index 00000000000..1edd97ae2bd --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/ProfileRepository.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Modifying; +import com.codename1.orm.repository.Param; +import com.codename1.orm.repository.Query; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.Profile; + +import java.util.List; + +/// Profiles, keyed by the e-mail an account signs in with. +/// +/// A repository holds the queries of one kind of entity and nothing else: no +/// rule about who may do what, and no transaction of its own. The build writes +/// its implementation, and every call runs on the session of the transaction +/// its caller opened, so a service method marked `@Transactional` is what +/// makes several of these calls one unit of work. Called outside one, it is +/// refused. +@Repository(entity = Profile.class, key = String.class) +public interface ProfileRepository extends CrudRepository { + /// Makes the rest of the transaction wait its turn behind any other that + /// holds the same account: the row is written, to the value it has, and a + /// written row is kept by its writer until it commits. For a check and an + /// insert that must not be interleaved with the same pair made for the + /// same account. Call it before anything is read: what is read after it is + /// what the transaction before this one left, while a row read before it + /// was read before the wait. + /// + /// @return 1, or 0 when the account has no profile + @Modifying + @Query("update Profile p set p.createdAt = p.createdAt where p.username = :username") + int hold(@Param("username") String username); + + /// Every profile, newest first. + @Query("select p from Profile p order by p.createdAt desc, p.username asc") + List newestFirst(); + + /// The profiles of these accounts; none for a name that has none. + List findByUsernameIn(List usernames); + + /// The profiles of every account that has a courier's state: the fleet, + /// and not every account there is. + @Query("select p from Profile p where p.username in (select d.username from CourierState d)") + List couriers(); + + /// The display names of some accounts, as `{username, displayName}`. The + /// two fields and not the profiles: a list of applications shows nothing + /// else of them. + @Query("select p.username, p.displayName from Profile p where p.username in :names") + List displayNames(@Param("names") List usernames); + + long countByFlaggedTrue(); + + /// When each account made since `since` was made. + @Query("select p.createdAt from Profile p where p.createdAt >= :since") + List signUps(@Param("since") long since); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PushDeviceRepository.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PushDeviceRepository.java new file mode 100644 index 00000000000..b81cc2732bf --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/repository/PushDeviceRepository.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.repository; + +import com.codename1.orm.repository.CrudRepository; +import com.codename1.orm.repository.Repository; +import com.codenameone.examples.morsel.domain.PushDevice; + +import java.util.List; + +/// The devices each account is sent notifications on. +@Repository(entity = PushDevice.class, key = String.class) +public interface PushDeviceRepository extends CrudRepository { + List findByUsername(String username); + + int deleteByUsername(String username); + + int deleteByIdAndUsername(String id, String username); +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/ConsoleSmsSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/ConsoleSmsSender.java new file mode 100644 index 00000000000..cd7fb60ea09 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/ConsoleSmsSender.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +/// Prints the message on the server's console instead of sending it. +/// +/// For development only: [com.codenameone.examples.morsel.Services] hands it +/// out on the `dev`, `test` and `local` profiles alone, and a production server +/// that asks for it is refused with a configuration error. It is not a stand-in +/// for a provider -- nobody receives the text, which is exactly why a +/// development server may then hand the code back to the app. +public class ConsoleSmsSender implements SmsSender { + @Override + public void send(String phone, String text) { + System.out.println("[sms] to " + phone + ": " + text); + } + + @Override + public boolean delivers() { + return false; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MessageBirdSmsSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MessageBirdSmsSender.java new file mode 100644 index 00000000000..d1fb4fb2d40 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MessageBirdSmsSender.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +import com.codename1.backend.Web; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/// Sends through MessageBird's (now Bird's) Messages API: a form POST to +/// `https://rest.messagebird.com/messages` carrying `recipients`, `originator` +/// and `body`, authenticated with an `Authorization: AccessKey ...` header. +/// MessageBird answers 201 when it has accepted the message. +/// +/// Settings: `morsel.sms.messagebird.key` (a live access key) and +/// `morsel.sms.messagebird.originator` (a number, or up to eleven letters and +/// digits). +public class MessageBirdSmsSender implements SmsSender { + private static final String URL = "https://rest.messagebird.com/messages"; + + private final String accessKey; + private final String originator; + + public MessageBirdSmsSender(String accessKey, String originator) { + this.accessKey = accessKey; + this.originator = originator; + } + + @Override + public void send(String phone, String text) throws IOException { + List headers = new ArrayList(); + headers.add("Authorization: AccessKey " + accessKey); + headers.add("Content-Type: application/x-www-form-urlencoded"); + Web.Result sent = Web.request("POST", URL, headers, SmsForm.of("recipients", phone, + "originator", originator, "body", text)); + if (sent.getStatus() != 201) { + // The status only: the body can echo the number back. + throw new IOException("MessageBird answered " + sent.getStatus()); + } + } + + @Override + public boolean delivers() { + return true; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MisconfiguredSmsSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MisconfiguredSmsSender.java new file mode 100644 index 00000000000..c8ea6bd2569 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/MisconfiguredSmsSender.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +import java.io.IOException; + +/// What the server sends with when the SMS settings are incomplete: every +/// message fails, and says why. +/// +/// The problem is logged once when the server starts and again on every send, +/// naming the settings that are missing, so it is in front of whoever reads the +/// log at either moment. The failure is an `IOException` like any provider +/// outage, which the phone verification answers with 503: a server whose SMS +/// settings were forgotten verifies nobody, rather than everybody. +public class MisconfiguredSmsSender implements SmsSender { + private final String problem; + + /// @param problem what is wrong, naming the settings to fix + public MisconfiguredSmsSender(String problem) { + this.problem = problem; + System.err.println("[config] " + problem); + } + + @Override + public void send(String phone, String text) throws IOException { + System.err.println("[config] a text message was not sent: " + problem); + throw new IOException(problem); + } + + /// True, because it stands in for a provider that would: the demo code a + /// development server hands back is only for the console sender. + @Override + public boolean delivers() { + return true; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsForm.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsForm.java new file mode 100644 index 00000000000..0f440b995c3 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsForm.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +import java.io.IOException; +import java.net.URLEncoder; + +/// Builds an `application/x-www-form-urlencoded` body, which is what all three +/// providers' send calls take. +final class SmsForm { + private SmsForm() { + } + + /// Names and values in turn, each escaped as UTF-8. + static byte[] of(String... pairs) throws IOException { + StringBuilder out = new StringBuilder(); + for (int iter = 0; iter + 1 < pairs.length; iter += 2) { + if (out.length() > 0) { + out.append('&'); + } + out.append(URLEncoder.encode(pairs[iter], "UTF-8")).append('=') + .append(URLEncoder.encode(pairs[iter + 1], "UTF-8")); + } + return out.toString().getBytes("UTF-8"); + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsSender.java new file mode 100644 index 00000000000..80811208666 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/SmsSender.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +import java.io.IOException; + +// tag::morsel-sms-sender[] +/// Sends a text message. The one place the server touches an SMS provider, so +/// changing provider is one class. +public interface SmsSender { + /// - `IOException`: when the message could not be handed to the provider + void send(String phone, String text) throws IOException; + + /// Whether a message really reaches a phone. False for the console sender used in + /// development, which is what lets a development server hand the code back to the + /// app in place of the text message nobody is going to receive. + boolean delivers(); +} +// end::morsel-sms-sender[] diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/TwilioSmsSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/TwilioSmsSender.java new file mode 100644 index 00000000000..a44db7d7ce6 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/TwilioSmsSender.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +import com.codename1.backend.Base64; +import com.codename1.backend.Web; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +/// Sends through Twilio's Messages API: a form POST to +/// `/2010-04-01/Accounts/{sid}/Messages.json`, authenticated with the account +/// SID and auth token as HTTP Basic credentials. Twilio answers 201 when it +/// has queued the message. +/// +/// Settings: `morsel.sms.twilio.sid`, `morsel.sms.twilio.token` and +/// `morsel.sms.twilio.from` (a Twilio number in E.164 form, or a messaging +/// service's sender). +/// +/// The account's credentials are settings of the server and never leave it: the +/// app asks the server for a code and has no SMS credentials of its own to leak. +public class TwilioSmsSender implements SmsSender { + private final String accountSid; + private final String authToken; + private final String from; + + public TwilioSmsSender(String accountSid, String authToken, String from) { + this.accountSid = accountSid; + this.authToken = authToken; + this.from = from; + } + + @Override + public void send(String phone, String text) throws IOException { + List headers = new ArrayList(); + headers.add("Authorization: Basic " + + Base64.encode((accountSid + ":" + authToken).getBytes("UTF-8"))); + headers.add("Content-Type: application/x-www-form-urlencoded"); + Web.Result sent = Web.request("POST", "https://api.twilio.com/2010-04-01/Accounts/" + + URLEncoder.encode(accountSid, "UTF-8") + "/Messages.json", headers, + SmsForm.of("To", phone, "From", from, "Body", text)); + if (!sent.isSuccess()) { + // The status only: the body can echo the number back. + throw new IOException("Twilio answered " + sent.getStatus()); + } + } + + @Override + public boolean delivers() { + return true; + } +} diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/VonageSmsSender.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/VonageSmsSender.java new file mode 100644 index 00000000000..b064a695725 --- /dev/null +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/sms/VonageSmsSender.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.sms; + +import com.codename1.backend.Json; +import com.codename1.backend.Web; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +// tag::morsel-sms-provider[] +/// Sends through Vonage's (formerly Nexmo's) SMS API: a form POST to +/// `https://rest.nexmo.com/sms/json` carrying `api_key`, `api_secret`, `to`, +/// `from` and `text`. +/// +/// Vonage answers 200 even for a message it refused; the outcome is the +/// `status` of each entry of `messages`, where `"0"` is success and anything +/// else is an error code. A long text is split into parts and each part has an +/// entry, so every one of them has to be `"0"`. +/// +/// Settings: `morsel.sms.vonage.key`, `morsel.sms.vonage.secret` and +/// `morsel.sms.vonage.from` (a virtual number or an alphanumeric sender id). +/// The number is sent without its leading `+`, as Vonage asks. +public class VonageSmsSender implements SmsSender { + private static final String URL = "https://rest.nexmo.com/sms/json"; + + private final String apiKey; + private final String apiSecret; + private final String from; + + public VonageSmsSender(String apiKey, String apiSecret, String from) { + this.apiKey = apiKey; + this.apiSecret = apiSecret; + this.from = from; + } + + @Override + public void send(String phone, String text) throws IOException { + String to = phone.startsWith("+") ? phone.substring(1) : phone; + List headers = new ArrayList(); + headers.add("Content-Type: application/x-www-form-urlencoded"); + Web.Result sent = Web.request("POST", URL, headers, SmsForm.of("api_key", apiKey, + "api_secret", apiSecret, "to", to, "from", from, "text", text)); + if (sent.getStatus() != 200) { + throw new IOException("Vonage answered " + sent.getStatus()); + } + Object messages = Json.parseObject(sent.getBodyAsString()).get("messages"); + if (!(messages instanceof List) || ((List) messages).isEmpty()) { + throw new IOException("Vonage's answer lists no message"); + } + for (Object entry : (List) messages) { + Object status = entry instanceof Map ? ((Map) entry).get("status") : null; + if (!"0".equals(status)) { + // The code only; `error-text` can echo the number back. + throw new IOException("Vonage refused the message with status " + status); + } + } + } + + @Override + public boolean delivers() { + return true; + } +} +// end::morsel-sms-provider[] diff --git a/scripts/morsel/backend/src/main/resources/db/migration/V1__accounts.sql b/scripts/morsel/backend/src/main/resources/db/migration/V1__accounts.sql new file mode 100644 index 00000000000..57695861307 --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/V1__accounts.sql @@ -0,0 +1,90 @@ +-- What the app knows about an account beyond its sign-in. The sign-in itself -- +-- the password hash, the roles, whether the account is enabled -- lives in the +-- security tables the backend creates (cn1.security.schema.enabled), keyed by +-- the same username. +-- +-- Every script here is written once for SQLite, PostgreSQL and MySQL: no +-- generated keys, no native boolean or timestamp. A yes/no is a SMALLINT 0/1 and +-- a time is milliseconds since the epoch in a BIGINT. +CREATE TABLE profile ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + display_name VARCHAR(120) NOT NULL, + phone VARCHAR(32) NOT NULL, + phone_verified SMALLINT NOT NULL, + vehicle VARCHAR(120) NOT NULL, + plate VARCHAR(32) NOT NULL, + created_at BIGINT NOT NULL, + -- What an account says about itself beyond its name and phone, and what an + -- admin has said about it. Added to the profile one column at a time, each with + -- a default: that is the one form of ALTER TABLE all three engines accept, and + -- the default is what the accounts opened before this script read as. + gender VARCHAR(16) NOT NULL, + language VARCHAR(8) NOT NULL, + flagged SMALLINT NOT NULL, + flag_reason VARCHAR(255) NOT NULL, + emergency_name VARCHAR(120) NOT NULL, + emergency_phone VARCHAR(32) NOT NULL, + home_address VARCHAR(255) NOT NULL, + work_address VARCHAR(255) NOT NULL +); + +-- How one person wants the app and their deliveries. A row exists once they have +-- saved something; until then the defaults are the server's. +CREATE TABLE preference ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + theme VARCHAR(8) NOT NULL, + units VARCHAR(4) NOT NULL, + notify_deliveries SMALLINT NOT NULL, + notify_receipts SMALLINT NOT NULL, + notify_promotions SMALLINT NOT NULL, + courier_gender VARCHAR(8) NOT NULL, + quiet_delivery SMALLINT NOT NULL, + wheelchair SMALLINT NOT NULL, + pet_friendly SMALLINT NOT NULL, + default_tip INTEGER NOT NULL, + share_trips_with VARCHAR(32) NOT NULL +); + +-- The e-mails of accounts that were closed, as hashes. A closed account's deliveries +-- stay under its e-mail, so the address is never registered again; the hash is +-- enough to refuse it without keeping a list of who has left. +CREATE TABLE closed_account ( + username_hash VARCHAR(64) NOT NULL PRIMARY KEY, + closed_at BIGINT NOT NULL +); + +-- The verification code an account is waiting on. Only its hash is kept, and +-- that of the code before it while it is still good: text messages are not +-- delivered in the order they were sent. +CREATE TABLE phone_code ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + code_hash VARCHAR(64) NOT NULL, + expires_at BIGINT NOT NULL, + attempts INTEGER NOT NULL, + sent_at BIGINT NOT NULL, + previous_hash VARCHAR(64) NOT NULL DEFAULT '', + previous_expires_at BIGINT NOT NULL DEFAULT 0, + previous_attempts INTEGER NOT NULL DEFAULT 0 +); + +-- Tickets for opening the live channel. A row is deleted by the connection that +-- presents it, which is what makes a ticket good for one connection only -- and +-- keeping them here and not in memory is what lets a second server instance +-- redeem a ticket the first one issued. +CREATE TABLE live_ticket ( + ticket_hash VARCHAR(64) NOT NULL PRIMARY KEY, + username VARCHAR(190) NOT NULL, + expires_at BIGINT NOT NULL +); + +-- What the admins did to an account and why: flagged it, blocked it, approved +-- or rejected its onboarding application. Never changed once written. +CREATE TABLE moderation_event ( + id VARCHAR(32) NOT NULL PRIMARY KEY, + username VARCHAR(190) NOT NULL, + created_at BIGINT NOT NULL, + actor VARCHAR(190) NOT NULL, + action VARCHAR(16) NOT NULL, + reason VARCHAR(255) NOT NULL +); +CREATE INDEX moderation_event_user ON moderation_event (username, created_at); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/V2__couriers.sql b/scripts/morsel/backend/src/main/resources/db/migration/V2__couriers.sql new file mode 100644 index 00000000000..2b2f33028e4 --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/V2__couriers.sql @@ -0,0 +1,51 @@ +-- A courier's live state: on line or not, and where the car was last seen. +CREATE TABLE courier_state ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + online SMALLINT NOT NULL, + lat DOUBLE PRECISION NOT NULL, + lng DOUBLE PRECISION NOT NULL, + heading DOUBLE PRECISION NOT NULL, + last_seen BIGINT NOT NULL, + rating_sum BIGINT NOT NULL, + rating_count BIGINT NOT NULL +); +-- Matching asks for the couriers on line inside a latitude band. +CREATE INDEX courier_online ON courier_state (online, lat); + +-- An application to drive: one per account, edited as a draft, handed in, and +-- then approved or rejected by an admin. The COURIER role follows `approved`, +-- and matching reads the car's product from here. +CREATE TABLE courier_application ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + status VARCHAR(16) NOT NULL, + legal_name VARCHAR(120) NOT NULL, + date_of_birth VARCHAR(10) NOT NULL, + licence_number VARCHAR(40) NOT NULL, + licence_expiry VARCHAR(10) NOT NULL, + vehicle_make VARCHAR(40) NOT NULL, + vehicle_model VARCHAR(40) NOT NULL, + vehicle_year INTEGER NOT NULL, + vehicle_color VARCHAR(24) NOT NULL, + vehicle_plate VARCHAR(32) NOT NULL, + vehicle_seats INTEGER NOT NULL, + product VARCHAR(16) NOT NULL, + -- Whether the car takes a wheelchair. Not called "accessible", which MySQL + -- reserves. + wheelchair SMALLINT NOT NULL, + rejection_reason VARCHAR(255) NOT NULL, + created_at BIGINT NOT NULL, + submitted_at BIGINT NOT NULL, + reviewed_at BIGINT NOT NULL, + reviewed_by VARCHAR(190) NOT NULL, + pets SMALLINT NOT NULL +); +CREATE INDEX courier_application_status ON courier_application (status, submitted_at); + +-- How long a courier was on line on each day (days since the epoch, UTC), added +-- to by every position report. +CREATE TABLE courier_hours ( + username VARCHAR(190) NOT NULL, + day_number BIGINT NOT NULL, + millis BIGINT NOT NULL, + PRIMARY KEY (username, day_number) +); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/V4__deliveries.sql b/scripts/morsel/backend/src/main/resources/db/migration/V4__deliveries.sql new file mode 100644 index 00000000000..41d0071ed81 --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/V4__deliveries.sql @@ -0,0 +1,65 @@ +CREATE TABLE delivery ( + id VARCHAR(32) NOT NULL PRIMARY KEY, + state VARCHAR(24) NOT NULL, + customer VARCHAR(190) NOT NULL, + -- The courier it is offered to, then the courier who took it. Empty until one + -- is found, and again between one courier passing and the next being asked. + courier VARCHAR(190) NOT NULL, + pickup_lat DOUBLE PRECISION NOT NULL, + pickup_lng DOUBLE PRECISION NOT NULL, + pickup_address VARCHAR(255) NOT NULL, + dropoff_lat DOUBLE PRECISION NOT NULL, + dropoff_lng DOUBLE PRECISION NOT NULL, + dropoff_address VARCHAR(255) NOT NULL, + fare_cents BIGINT NOT NULL, + currency VARCHAR(3) NOT NULL, + distance_meters DOUBLE PRECISION NOT NULL, + duration_seconds DOUBLE PRECISION NOT NULL, + requested_at BIGINT NOT NULL, + updated_at BIGINT NOT NULL, + -- When the offer in hand lapses, or when the search for a courier gives up. + offer_expires_at BIGINT NOT NULL, + search_until BIGINT NOT NULL, + stars SMALLINT NOT NULL, + payment_status VARCHAR(16) NOT NULL, + -- What the customer asked for beyond two points, how the delivery is paid for, and + -- the parts its price is made of. Every amount is cents, fixed when the delivery is + -- asked for; the tip and the courier's share are written when they are known. + product VARCHAR(16) NOT NULL, + method_id VARCHAR(32) NOT NULL, + method_label VARCHAR(64) NOT NULL, + base_cents BIGINT NOT NULL, + distance_cents BIGINT NOT NULL, + time_cents BIGINT NOT NULL, + fee_cents BIGINT NOT NULL, + tip_cents BIGINT NOT NULL, + courier_cents BIGINT NOT NULL, + women_only SMALLINT NOT NULL, + quiet SMALLINT NOT NULL, + wheelchair SMALLINT NOT NULL, + note VARCHAR(255) NOT NULL, + -- A customer may ask for a car their pet is welcome in, and a courier says whether + -- theirs is one. Neither is, until someone says so. + pets SMALLINT NOT NULL +); +CREATE INDEX delivery_customer ON delivery (customer, requested_at); +CREATE INDEX delivery_courier ON delivery (courier, requested_at); +CREATE INDEX delivery_state ON delivery (state); + +-- Every change of a delivery's state, for the admin's view of what happened. +CREATE TABLE delivery_event ( + id VARCHAR(32) NOT NULL PRIMARY KEY, + delivery_id VARCHAR(32) NOT NULL, + state VARCHAR(24) NOT NULL, + actor VARCHAR(190) NOT NULL, + created_at BIGINT NOT NULL +); +CREATE INDEX delivery_event_delivery ON delivery_event (delivery_id, created_at); + +-- The couriers who passed on a delivery, or let its offer lapse: they are not asked +-- again for the same delivery. +CREATE TABLE delivery_passed ( + delivery_id VARCHAR(32) NOT NULL, + courier VARCHAR(190) NOT NULL, + PRIMARY KEY (delivery_id, courier) +); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/V5__payments.sql b/scripts/morsel/backend/src/main/resources/db/migration/V5__payments.sql new file mode 100644 index 00000000000..47935634fb7 --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/V5__payments.sql @@ -0,0 +1,82 @@ +CREATE TABLE payment ( + delivery_id VARCHAR(32) NOT NULL PRIMARY KEY, + amount_cents BIGINT NOT NULL, + currency VARCHAR(3) NOT NULL, + status VARCHAR(16) NOT NULL, + provider_ref VARCHAR(64) NOT NULL, + created_at BIGINT NOT NULL, + -- A delivery's payment grows a tip, which is a second charge, and a refund. + tip_ref VARCHAR(64) NOT NULL, + refund_ref VARCHAR(64) NOT NULL, + -- The tip is refunded on its own, and a refund the processor has not + -- finished is asked about by both references. + tip_refund_ref VARCHAR(64) NOT NULL, + refund_reason VARCHAR(255) NOT NULL, + refunded_at BIGINT NOT NULL +); + +-- The account a customer has with the payment processor, made the first time +-- they add a card. +CREATE TABLE payment_customer ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + reference VARCHAR(64) NOT NULL +); + +-- A saved card: what the processor calls it, and the few things about it that +-- are safe to show. No card number is ever stored here, or anywhere. +CREATE TABLE payment_method ( + id VARCHAR(32) NOT NULL PRIMARY KEY, + username VARCHAR(190) NOT NULL, + brand VARCHAR(24) NOT NULL, + last4 VARCHAR(4) NOT NULL, + exp_month INTEGER NOT NULL, + exp_year INTEGER NOT NULL, + token VARCHAR(64) NOT NULL, + preferred SMALLINT NOT NULL, + created_at BIGINT NOT NULL +); +CREATE INDEX payment_method_user ON payment_method (username, created_at); + +-- An attempt to add a card, between its start and its completion. +CREATE TABLE payment_setup ( + id VARCHAR(32) NOT NULL PRIMARY KEY, + username VARCHAR(190) NOT NULL, + reference VARCHAR(255) NOT NULL, + created_at BIGINT NOT NULL, + completed SMALLINT NOT NULL +); + +-- Where a courier's payouts go, and the payouts made. +CREATE TABLE payout_account ( + username VARCHAR(190) NOT NULL PRIMARY KEY, + holder VARCHAR(120) NOT NULL, + bank_name VARCHAR(80) NOT NULL, + last4 VARCHAR(4) NOT NULL +); + +CREATE TABLE payout ( + id VARCHAR(32) NOT NULL PRIMARY KEY, + courier VARCHAR(190) NOT NULL, + amount_cents BIGINT NOT NULL, + status VARCHAR(16) NOT NULL, + created_at BIGINT NOT NULL, + destination VARCHAR(120) NOT NULL +); +CREATE INDEX payout_courier ON payout (courier, created_at); + +-- What deliveries cost, once an admin has changed it: a single row, id 1. Until +-- there is one the prices are the fare settings of the server. +CREATE TABLE pricing ( + id INTEGER NOT NULL PRIMARY KEY, + base_cents BIGINT NOT NULL, + per_km_cents BIGINT NOT NULL, + per_minute_cents BIGINT NOT NULL, + minimum_cents BIGINT NOT NULL, + fee_percent DOUBLE PRECISION NOT NULL, + commission_percent DOUBLE PRECISION NOT NULL, + surge DOUBLE PRECISION NOT NULL, + comfort DOUBLE PRECISION NOT NULL, + xl DOUBLE PRECISION NOT NULL, + updated_at BIGINT NOT NULL, + updated_by VARCHAR(190) NOT NULL +); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/V6__pricing_row.sql b/scripts/morsel/backend/src/main/resources/db/migration/V6__pricing_row.sql new file mode 100644 index 00000000000..184b86064a2 --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/V6__pricing_row.sql @@ -0,0 +1,8 @@ +-- The one row of prices, there from the start, so that saving prices only +-- ever updates it. Created on the first save instead, two admins saving +-- together before anyone had would both insert it, and one of them fail. +-- An updated_at of 0 says nobody has set prices yet: the settings' are the +-- ones in force until somebody does. +INSERT INTO pricing (id, base_cents, per_km_cents, per_minute_cents, minimum_cents, + fee_percent, commission_percent, surge, comfort, xl, updated_at, updated_by) +VALUES (1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, ''); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/V7__push.sql b/scripts/morsel/backend/src/main/resources/db/migration/V7__push.sql new file mode 100644 index 00000000000..bfdb014600d --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/V7__push.sql @@ -0,0 +1,31 @@ +-- The devices each account is sent push notifications on. Keyed by the hash of +-- the provider and the token, so one phone is on one account at a time: the +-- one that signed in on it last. +CREATE TABLE push_device ( + id VARCHAR(64) NOT NULL PRIMARY KEY, + username VARCHAR(190) NOT NULL, + provider VARCHAR(16) NOT NULL, + token VARCHAR(1024) NOT NULL, + platform VARCHAR(32) NOT NULL, + updated_at BIGINT NOT NULL +); + +CREATE INDEX push_device_username ON push_device (username); + +-- The Live Activity a customer's phone shows a delivery in, while the delivery lasts. +-- sent_at is when the server last sent it a state, which spaces the updates +-- out: the car reports where it is every few seconds, and the lock screen +-- needs to hear about it far less often than that. ended_at is when the delivery +-- ended, 0 while it lasts: the row is kept until the phone has been told, so an +-- end the push provider did not take is sent again. +CREATE TABLE delivery_activity ( + delivery_id VARCHAR(32) NOT NULL PRIMARY KEY, + username VARCHAR(190) NOT NULL, + activity_id VARCHAR(190) NOT NULL, + sent_at BIGINT NOT NULL, + ended_at BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX delivery_activity_ended_at ON delivery_activity (ended_at); + +CREATE INDEX delivery_activity_username ON delivery_activity (username); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/mysql/V3__courier_documents.sql b/scripts/morsel/backend/src/main/resources/db/migration/mysql/V3__courier_documents.sql new file mode 100644 index 00000000000..b529ebf961e --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/mysql/V3__courier_documents.sql @@ -0,0 +1,19 @@ +-- The pictures a onboarding application is backed by -- an identity document, a +-- licence, a selfie, the car's registration and its insurance -- one of each +-- kind for an account, as base64 text. +-- +-- In the database and not on a disk beside the server, so every instance of +-- the server reads the same documents and a backup of the database is a backup +-- of them. The server caps what it accepts well below what this column holds. +-- +-- This is the one script written per engine: a document outgrows the 64 KB of +-- MySQL's TEXT, and LONGTEXT is a type the other two do not have. +CREATE TABLE courier_document ( + username VARCHAR(190) NOT NULL, + kind VARCHAR(24) NOT NULL, + content_type VARCHAR(32) NOT NULL, + status VARCHAR(16) NOT NULL, + uploaded_at BIGINT NOT NULL, + data LONGTEXT NOT NULL, + PRIMARY KEY (username, kind) +); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/postgresql/V3__courier_documents.sql b/scripts/morsel/backend/src/main/resources/db/migration/postgresql/V3__courier_documents.sql new file mode 100644 index 00000000000..3539b395dbf --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/postgresql/V3__courier_documents.sql @@ -0,0 +1,19 @@ +-- The pictures a onboarding application is backed by -- an identity document, a +-- licence, a selfie, the car's registration and its insurance -- one of each +-- kind for an account, as base64 text. +-- +-- In the database and not on a disk beside the server, so every instance of +-- the server reads the same documents and a backup of the database is a backup +-- of them. The server caps what it accepts well below what this column holds. +-- +-- This is the one script written per engine: a document outgrows the 64 KB of +-- MySQL's TEXT, and LONGTEXT is a type the other two do not have. +CREATE TABLE courier_document ( + username VARCHAR(190) NOT NULL, + kind VARCHAR(24) NOT NULL, + content_type VARCHAR(32) NOT NULL, + status VARCHAR(16) NOT NULL, + uploaded_at BIGINT NOT NULL, + data TEXT NOT NULL, + PRIMARY KEY (username, kind) +); diff --git a/scripts/morsel/backend/src/main/resources/db/migration/sqlite/V3__courier_documents.sql b/scripts/morsel/backend/src/main/resources/db/migration/sqlite/V3__courier_documents.sql new file mode 100644 index 00000000000..3539b395dbf --- /dev/null +++ b/scripts/morsel/backend/src/main/resources/db/migration/sqlite/V3__courier_documents.sql @@ -0,0 +1,19 @@ +-- The pictures a onboarding application is backed by -- an identity document, a +-- licence, a selfie, the car's registration and its insurance -- one of each +-- kind for an account, as base64 text. +-- +-- In the database and not on a disk beside the server, so every instance of +-- the server reads the same documents and a backup of the database is a backup +-- of them. The server caps what it accepts well below what this column holds. +-- +-- This is the one script written per engine: a document outgrows the 64 KB of +-- MySQL's TEXT, and LONGTEXT is a type the other two do not have. +CREATE TABLE courier_document ( + username VARCHAR(190) NOT NULL, + kind VARCHAR(24) NOT NULL, + content_type VARCHAR(32) NOT NULL, + status VARCHAR(16) NOT NULL, + uploaded_at BIGINT NOT NULL, + data TEXT NOT NULL, + PRIMARY KEY (username, kind) +); diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessMatrixTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessMatrixTest.java new file mode 100644 index 00000000000..244463692bd --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessMatrixTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.DemoAccounts; +import org.junit.jupiter.api.Test; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcRequestBuilders.put; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// Who may call each of the paths for payments, preferences, onboarding +/// applications, earnings and administration -- every one of them, with tokens +/// earned by signing in. +/// +/// A list and not a sample, because the rule for a path is written once, far +/// from the method it guards, and a path left out of it is open to everybody. +@BackendTest +class AccessMatrixTest { + /// What any signed-in user may call. `GET` first: those are also asked for + /// with a customer's token, and reading changes nothing. + private static final String[] SIGNED_IN = { + "GET /api/account/preferences", + "GET /api/account/profile", + "GET /api/payments/config", + "GET /api/payments/methods", + "GET /api/payments/receipts", + "GET /api/payments/receipts/no-such-delivery", + "GET /api/deliveries/nearby?lat=37.78&lng=-122.41", + "GET /api/onboarding/application", + "GET /api/onboarding/application/documents/licence", + "PUT /api/account/preferences", + "PUT /api/account/profile", + "POST /api/account/password", + "POST /api/account/delete", + "POST /api/account/push", + "POST /api/account/push/remove", + "POST /api/deliveries/no-such-delivery/activity", + "POST /api/payments/methods/setup", + "POST /api/payments/methods/setup/none/complete", + "POST /api/payments/methods/none/default", + "POST /api/payments/methods/none/remove", + "POST /api/deliveries/no-such-delivery/tip", + "PUT /api/onboarding/application", + "POST /api/onboarding/application/documents", + "POST /api/onboarding/application/submit", + }; + private static final String[] COURIERS = { + "GET /api/courier/earnings/summary", + "GET /api/courier/payouts", + "GET /api/courier/payout-account", + "POST /api/courier/payouts", + "PUT /api/courier/payout-account", + }; + private static final String[] ADMINS = { + "GET /api/admin/stats/series?days=7", + "GET /api/admin/users/customer@morsel.example", + "GET /api/admin/applications", + "GET /api/admin/applications/applicant@morsel.example", + "GET /api/admin/applications/applicant@morsel.example/documents/licence", + "GET /api/admin/payments", + "GET /api/admin/pricing", + "POST /api/admin/users/customer@morsel.example/flag", + "POST /api/admin/applications/applicant@morsel.example/approve", + "POST /api/admin/applications/applicant@morsel.example/reject", + "POST /api/admin/deliveries/no-such-delivery/cancel", + "POST /api/admin/deliveries/no-such-delivery/refund", + "PUT /api/admin/pricing", + }; + + @Autowired + private MockMvc mvc; + + private static MockRequestBuilder request(String call) { + int space = call.indexOf(' '); + String method = call.substring(0, space); + String path = call.substring(space + 1); + MockRequestBuilder request = "GET".equals(method) ? get(path) + : "PUT".equals(method) ? put(path) : post(path); + return "GET".equals(method) ? request + : request.contentType("application/json").content("{}"); + } + + private int answer(String call, String token) throws Exception { + MockRequestBuilder request = request(call).header("Host", SignIn.HOST); + if (token != null) { + request = request.header("Authorization", "Bearer " + token); + } + return Trips.statusOf(mvc, request); + } + + private void refused(String[] calls, String token, int status) throws Exception { + for (int iter = 0; iter < calls.length; iter++) { + assertEquals(status, answer(calls[iter], token), calls[iter]); + } + } + + /// The readers among `calls` get past the door: whatever they answer, it + /// is not a refusal to say who may ask. + private void admitted(String[] calls, String token) throws Exception { + for (int iter = 0; iter < calls.length; iter++) { + if (calls[iter].startsWith("GET ")) { + int status = answer(calls[iter], token); + assertTrue(status != 401 && status != 403, calls[iter] + " answered " + status); + } + } + } + + @Test + void nothingIsAnsweredWithoutSigningIn() throws Exception { + refused(SIGNED_IN, null, 401); + refused(COURIERS, null, 401); + refused(ADMINS, null, 401); + refused(SIGNED_IN, "not-a-token", 401); + } + + @Test + void aCustomerIsKeptOutOfOnboardingAndAdministration() throws Exception { + String customer = SignIn.token(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + refused(COURIERS, customer, 403); + refused(ADMINS, customer, 403); + admitted(SIGNED_IN, customer); + // An application is the caller's own: there is no path to another's. + assertEquals(404, answer("GET /api/onboarding/application/documents/licence", customer)); + } + + @Test + void aCourierIsKeptOutOfAdministration() throws Exception { + String courier = SignIn.token(mvc, DemoAccounts.COURIER, DemoAccounts.PASSWORD); + refused(ADMINS, courier, 403); + admitted(COURIERS, courier); + admitted(SIGNED_IN, courier); + } + + @Test + void anAdminReachesAdministrationAndDoesNotDrive() throws Exception { + String admin = SignIn.token(mvc, DemoAccounts.ADMIN, DemoAccounts.PASSWORD); + admitted(ADMINS, admin); + admitted(SIGNED_IN, admin); + refused(COURIERS, admin, 403); + // The applicant's papers are an admin's to read, and nobody else's. + assertEquals(200, answer("GET /api/admin/applications/applicant@morsel.example" + + "/documents/licence", admin)); + String applicant = SignIn.token(mvc, DemoAccounts.APPLICANT, DemoAccounts.PASSWORD); + assertEquals(200, answer("GET /api/onboarding/application/documents/licence", applicant)); + assertEquals(403, answer("GET /api/admin/applications/applicant@morsel.example" + + "/documents/licence", applicant)); + // Not approved, so not a courier. + refused(COURIERS, applicant, 403); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessTest.java new file mode 100644 index 00000000000..b5bd5d687d2 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccessTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import com.codenameone.examples.morsel.live.LiveTickets; +import org.junit.jupiter.api.Test; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/// Who may call what, with tokens earned by signing in: these go through the +/// decoder and the role lookup the app's own requests go through. +@BackendTest +class AccessTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private LiveTickets tickets; + + private MockRequestBuilder as(MockRequestBuilder request, String token) { + return request.header("Host", SignIn.HOST).header("Authorization", "Bearer " + token); + } + + @Test + void eachRoleReachesItsOwnPartOfTheApi() throws Exception { + String customer = SignIn.token(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + String courier = SignIn.token(mvc, DemoAccounts.COURIER, DemoAccounts.PASSWORD); + String admin = SignIn.token(mvc, DemoAccounts.ADMIN, DemoAccounts.PASSWORD); + + mvc.perform(as(get("/api/me"), customer)).andExpect(status().isOk()) + .andExpect(jsonPath("$.courier").value(false)); + mvc.perform(as(get("/api/courier/me"), customer)).andExpect(status().isForbidden()); + mvc.perform(as(get("/api/admin/stats"), customer)).andExpect(status().isForbidden()); + + mvc.perform(as(get("/api/courier/me"), courier)).andExpect(status().isOk()) + .andExpect(jsonPath("$.vehicle").value("Blue Toyota Prius")); + mvc.perform(as(get("/api/admin/users"), courier)).andExpect(status().isForbidden()); + // A courier is a customer too. + mvc.perform(as(get("/api/deliveries/active"), courier)).andExpect(status().isOk()); + + mvc.perform(as(get("/api/admin/stats"), admin)).andExpect(status().isOk()) + .andExpect(jsonPath("$.currency").value("USD")); + mvc.perform(as(get("/api/admin/users"), admin)).andExpect(status().isOk()); + mvc.perform(as(get("/api/admin/couriers"), admin)).andExpect(status().isOk()); + mvc.perform(as(get("/api/admin/deliveries"), admin)).andExpect(status().isOk()); + // An admin who does not drive is not a courier. + mvc.perform(as(get("/api/courier/me"), admin)).andExpect(status().isForbidden()); + } + + @Test + void aRoleGrantedOrAnAccountSuspendedTakesEffectOnTheTokenInHand() throws Exception { + String username = People.customer(accounts, "promoted"); + String token = SignIn.token(mvc, username, People.PASSWORD); + String admin = SignIn.token(mvc, DemoAccounts.ADMIN, DemoAccounts.PASSWORD); + mvc.perform(as(get("/api/courier/me"), token)).andExpect(status().isForbidden()); + + mvc.perform(as(post("/api/admin/users/" + username + "/roles"), admin) + .contentType("application/json").content("{\"courier\":true,\"admin\":false}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.courier").value(true)); + // The same token: the role is read from the account, not from it. + mvc.perform(as(get("/api/courier/me"), token)).andExpect(status().isOk()); + + mvc.perform(as(post("/api/admin/users/" + username + "/suspend"), admin) + .contentType("application/json").content("{\"suspended\":true}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.suspended").value(true)); + mvc.perform(as(get("/api/me"), token)).andExpect(status().isUnauthorized()); + assertNull(SignIn.session(mvc, username, People.PASSWORD)); + + mvc.perform(as(post("/api/admin/users/" + username + "/suspend"), admin) + .contentType("application/json").content("{\"suspended\":false}")) + .andExpect(status().isOk()); + mvc.perform(as(get("/api/me"), token)).andExpect(status().isOk()); + } + + @Test + void anAdminCannotLockThemselvesOut() throws Exception { + String admin = SignIn.token(mvc, DemoAccounts.ADMIN, DemoAccounts.PASSWORD); + mvc.perform(as(post("/api/admin/users/" + DemoAccounts.ADMIN + "/roles"), admin) + .contentType("application/json").content("{\"courier\":false,\"admin\":false}")) + .andExpect(status().isBadRequest()); + mvc.perform(as(post("/api/admin/users/" + DemoAccounts.ADMIN + "/suspend"), admin) + .contentType("application/json").content("{\"suspended\":true}")) + .andExpect(status().isBadRequest()); + mvc.perform(as(post("/api/admin/users/nobody@test.example/suspend"), admin) + .contentType("application/json").content("{\"suspended\":true}")) + .andExpect(status().isNotFound()); + } + + @Test + void aLiveTicketIsGoodOnce() throws Exception { + String customer = SignIn.token(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + mvc.perform(post("/api/live/ticket")).andExpect(status().isUnauthorized()); + String body = mvc.perform(as(post("/api/live/ticket"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.expiresInSeconds").value(30)) + .andReturn().getResponse().getContentAsString(); + String ticket = (String) com.codename1.backend.Json.parseObject(body).get("ticket"); + assertEquals(DemoAccounts.CUSTOMER, tickets.redeem(ticket)); + assertNull(tickets.redeem(ticket)); + assertNull(tickets.redeem("not-a-ticket")); + } + + @Test + void placesAreFoundThroughTheServer() throws Exception { + String customer = SignIn.token(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + mvc.perform(get("/api/geo/search?q=pier&lat=37.8&lng=-122.4")) + .andExpect(status().isUnauthorized()); + mvc.perform(as(get("/api/geo/search?q=PIER&lat=37.8&lng=-122.4"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].name").value("Pier 39")); + // Too little to search for. + mvc.perform(as(get("/api/geo/search?q=pi&lat=37.8&lng=-122.4"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isEmpty()); + mvc.perform(as(get("/api/geo/reverse?lat=37.7956&lng=-122.3936"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("Ferry Building")) + .andExpect(jsonPath("$.lat").value(37.7956)); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccountTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccountTest.java new file mode 100644 index 00000000000..7712023cd23 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/AccountTest.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.Json; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codenameone.examples.morsel.account.DemoAccounts; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/// Opening an account, signing in to it and proving its phone number, through +/// the same requests the app makes. +@BackendTest +class AccountTest { + @Autowired + private MockMvc mvc; + @Autowired + private DataSource db; + + private static String registration(String email, String password, boolean courier) { + return "{\"email\":\"" + email + "\",\"password\":\"" + password + + "\",\"displayName\":\"Test Person\",\"phone\":\"+1 (555) 010-9999\",\"courier\":" + + courier + ",\"vehicle\":\"Grey Honda Civic\",\"plate\":\"TST 1\"}"; + } + + private String bearer(String username, String password) throws Exception { + return "Bearer " + SignIn.token(mvc, username, password); + } + + @Test + void anAccountIsOpenedThenSignedInTo() throws Exception { + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration("New.Customer@Example.com", "a-long-password", false))) + .andExpect(status().isOk()) + // The name is the address in lower case, and the phone number is + // stored in one form however it was typed. + .andExpect(jsonPath("$.username").value("new.customer@example.com")) + .andExpect(jsonPath("$.phone").value("+15550109999")) + .andExpect(jsonPath("$.customer").value(true)) + .andExpect(jsonPath("$.courier").value(false)) + .andExpect(jsonPath("$.admin").value(false)) + .andExpect(jsonPath("$.phoneVerified").value(false)); + + // Signing in is not case sensitive about the address either. + mvc.perform(get("/api/me").header("Host", SignIn.HOST).header("Authorization", + bearer("NEW.CUSTOMER@example.com", "a-long-password"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value("new.customer@example.com")) + .andExpect(jsonPath("$.displayName").value("Test Person")); + } + + @Test + void anAccountThatCannotBeOpenedSaysWhy() throws Exception { + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration("short@example.com", "short", false))) + .andExpect(status().isBadRequest()); + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration("not-an-address", "a-long-password", false))) + .andExpect(status().isBadRequest()); + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration(DemoAccounts.CUSTOMER, "a-long-password", false))) + .andExpect(status().isConflict()); + } + + @Test + void aWrongPasswordSignsNobodyIn() throws Exception { + assertNull(SignIn.session(mvc, DemoAccounts.CUSTOMER, "not-the-password")); + assertNotNull(SignIn.session(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD)); + } + + @Test + void theApiWantsAToken() throws Exception { + mvc.perform(get("/api/me")).andExpect(status().isUnauthorized()); + mvc.perform(get("/api/me").header("Authorization", "Bearer not-a-token")) + .andExpect(status().isUnauthorized()); + // A session is for signing in. It is not a way into the API. + mvc.perform(get("/api/me").cookie("CN1SESSION", + SignIn.session(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD))) + .andExpect(status().isUnauthorized()); + } + + @Test + void aPhoneNumberIsProvedWithTheCodeThatWasSent() throws Exception { + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration("phone@example.com", "a-long-password", true))) + .andExpect(status().isOk()) + // Asking to drive opens an application. It does not make a courier. + .andExpect(jsonPath("$.courier").value(false)) + .andExpect(jsonPath("$.courierStatus").value("draft")); + String bearer = bearer("phone@example.com", "a-long-password"); + + String sent = mvc.perform(post("/api/account/phone/start").header("Host", SignIn.HOST) + .header("Authorization", bearer)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + Map challenge = Json.parseObject(sent); + // A test server sends no text message, so it says what the code was. + String code = (String) challenge.get("demoCode"); + assertNotNull(code); + assertEquals(6, code.length()); + + // A second code straight away is refused: each is a text message. + mvc.perform(post("/api/account/phone/start").header("Host", SignIn.HOST) + .header("Authorization", bearer)) + .andExpect(status().isTooManyRequests()); + + String wrong = "000000".equals(code) ? "000001" : "000000"; + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + wrong + "\"}")) + .andExpect(status().isBadRequest()); + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + code + "\"}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.phoneVerified").value(true)); + // Used up: the same code is not good twice. + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + code + "\"}")) + .andExpect(status().isBadRequest()); + } + + /// A second code is asked for when the first is slow to arrive, and the + /// first may then arrive last. Both are good until the first one would + /// have expired, and using either uses up both. + @Test + void aCodeStaysGoodWhenAnotherIsSentAfterIt() throws Exception { + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration("resend@example.com", "a-long-password", false))) + .andExpect(status().isOk()); + String bearer = bearer("resend@example.com", "a-long-password"); + String first = code(bearer); + // The wait between codes has passed. + db.execute("UPDATE phone_code SET sent_at = sent_at - 60000 WHERE username = ?", + new Object[] {"resend@example.com"}); + String second = code(bearer); + + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + first + "\"}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.phoneVerified").value(true)); + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + second + "\"}")) + .andExpect(status().isBadRequest()); + + // Once the first has expired it is no longer good, though the second is. + String third = code(bearer); + db.execute("UPDATE phone_code SET sent_at = sent_at - 60000 WHERE username = ?", + new Object[] {"resend@example.com"}); + String fourth = code(bearer); + db.execute("UPDATE phone_code SET previous_expires_at = 1 WHERE username = ?", + new Object[] {"resend@example.com"}); + if (!third.equals(fourth)) { + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + third + "\"}")) + .andExpect(status().isBadRequest()); + } + mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + fourth + "\"}")) + .andExpect(status().isOk()); + } + + /// Asking for another code does not give the one before a fresh set of + /// guesses: it keeps what it had left. + @Test + void theCodeBeforeKeepsTheGuessesItHadLeft() throws Exception { + mvc.perform(post("/api/account/register").contentType("application/json") + .content(registration("budget@example.com", "a-long-password", false))) + .andExpect(status().isOk()); + String bearer = bearer("budget@example.com", "a-long-password"); + String first = code(bearer); + // Four of the five guesses (PhoneVerification.MAX_ATTEMPTS) spent. + for (int iter = 0; iter < 4; iter++) { + verify(bearer, other(first, first)).andExpect(status().isBadRequest()); + } + db.execute("UPDATE phone_code SET sent_at = sent_at - 60000 WHERE username = ?", + new Object[] {"budget@example.com"}); + String second = code(bearer); + // The first code's last guess, spent on a wrong one. + verify(bearer, other(first, second)).andExpect(status().isBadRequest()); + // Now the first is out of guesses, though the second is not. + if (!first.equals(second)) { + verify(bearer, first).andExpect(status().isBadRequest()); + } + verify(bearer, second).andExpect(status().isOk()); + } + + /// A six-digit code that is neither of these. + private static String other(String a, String b) { + for (int iter = 0; ; iter++) { + String candidate = String.valueOf(1000000 + iter).substring(1); + if (!candidate.equals(a) && !candidate.equals(b)) { + return candidate; + } + } + } + + private com.codename1.backend.test.ResultActions verify(String bearer, String code) + throws Exception { + return mvc.perform(post("/api/account/phone/verify").header("Host", SignIn.HOST) + .header("Authorization", bearer).contentType("application/json") + .content("{\"code\":\"" + code + "\"}")); + } + + private String code(String bearer) throws Exception { + String sent = mvc.perform(post("/api/account/phone/start").header("Host", SignIn.HOST) + .header("Authorization", bearer)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + return (String) Json.parseObject(sent).get("demoCode"); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DaysTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DaysTest.java new file mode 100644 index 00000000000..6a124d054fb --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DaysTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/// Time on line cut at midnight, which is how a courier's hours are credited. +class DaysTest { + /// 2026-03-01 00:00:00 UTC. + private static final long MIDNIGHT = 20513L * Days.MILLIS; + + @Test + void aStretchAcrossMidnightIsCreditedHalfToEachDay() { + // 23:59:40 to 00:00:20: twenty seconds on each side. Credited whole to + // the day it ended in, the new day had all forty. + long day = Days.number(MIDNIGHT); + assertArrayEquals(new long[] {day - 1, 20000L, day, 20000L}, + Days.split(MIDNIGHT - 20000L, MIDNIGHT + 20000L)); + } + + @Test + void aStretchWithinOneDayIsThatDays() { + long day = Days.number(MIDNIGHT); + assertArrayEquals(new long[] {day, 40000L}, + Days.split(MIDNIGHT + 1000L, MIDNIGHT + 41000L)); + // Ending exactly at midnight is all the old day's. + assertArrayEquals(new long[] {day - 1, 40000L}, + Days.split(MIDNIGHT - 40000L, MIDNIGHT)); + // Starting exactly at it is all the new one's. + assertArrayEquals(new long[] {day, 40000L}, Days.split(MIDNIGHT, MIDNIGHT + 40000L)); + assertArrayEquals(new long[0], Days.split(MIDNIGHT, MIDNIGHT)); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java new file mode 100644 index 00000000000..269ccbcb122 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.Json; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import com.codenameone.examples.morsel.delivery.Deliveries; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// A delivery from request to rating, and the ways it can go otherwise. +@BackendTest +class DeliveryFlowTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private Deliveries deliveries; + @Autowired + private DataSource db; + + private Map json(MockRequestBuilder request) throws Exception { + return Json.parseObject(mvc.perform(request).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString()); + } + + private static MockRequestBuilder body(MockRequestBuilder request, String json) { + return request.contentType("application/json").content(json); + } + + @Test + void aDeliveryFromRequestToRating() throws Exception { + // Lisbon. Each test has a city to itself. + String customer = People.customer(accounts, "lis-customer"); + String courier = People.courier(accounts, "lis-courier"); + String delivery = People.delivery(38.7223, -9.1393, 38.7369, -9.1427); + + // Nobody is on line yet. + mvc.perform(People.asCustomer(body(post("/api/deliveries/quote"), delivery), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.couriersNearby").value(0)) + .andExpect(jsonPath("$.currency").value("USD")); + + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(true, 38.7250, -9.1400)), courier)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.online").value(true)); + Map quote = json(People.asCustomer(body(post("/api/deliveries/quote"), delivery), customer)); + assertEquals(1L, ((Number) quote.get("couriersNearby")).longValue()); + long fare = ((Number) quote.get("amountCents")).longValue(); + assertTrue(fare >= 500, "the minimum fare, at least: " + fare); + + // Asking offers it to the courier at once. The customer is waiting, and is + // not told who was asked. + Map asked = json(People.asCustomer(body(post("/api/deliveries"), delivery), customer)); + String id = (String) asked.get("id"); + assertEquals("OFFERED", asked.get("state")); + assertEquals(null, asked.get("courierUsername")); + assertEquals(fare, ((Number) asked.get("fareCents")).longValue()); + mvc.perform(People.asCustomer(body(post("/api/deliveries"), delivery), customer)) + .andExpect(status().isConflict()); + + // The courier sees the offer, with the clock running, and takes it. + mvc.perform(People.asCourier(get("/api/courier/active"), courier)) + .andExpect(jsonPath("$.id").value(id)) + .andExpect(jsonPath("$.state").value("OFFERED")) + .andExpect(jsonPath("$.customerName").value("lis-customer")); + // Not before it is taken. + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/start"), courier)) + .andExpect(status().isConflict()); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.state").value("ACCEPTED")); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isConflict()); + + // Now the customer has a courier, a car to look for and a position to follow. + mvc.perform(People.asCustomer(get("/api/deliveries/active"), customer)) + .andExpect(jsonPath("$.state").value("ACCEPTED")) + .andExpect(jsonPath("$.courierName").value("lis-courier")) + .andExpect(jsonPath("$.vehicle").value("Green Fiat 500")) + .andExpect(jsonPath("$.courierLat").value(38.725)); + // The two of them can now reach each other, and each is told the + // other's number and not their own. An admin looking at the delivery is + // told neither. + Map customersView = json(People.asCustomer(get("/api/deliveries/" + id), customer)); + assertEquals("+15550111", customersView.get("courierPhone")); + assertEquals(null, customersView.get("customerPhone")); + Map couriersView = json(People.asCourier(get("/api/courier/active"), courier)); + assertEquals("+15550111", couriersView.get("customerPhone")); + assertEquals(null, couriersView.get("courierPhone")); + // A name is the e-mail its account signs in with. Each is given their + // own and not the other's; the admin is given both. + assertEquals(customer, customersView.get("customerUsername")); + assertEquals(null, customersView.get("courierUsername")); + assertEquals(courier, couriersView.get("courierUsername")); + assertEquals(null, couriersView.get("customerUsername")); + Map adminsView = json(People.asAdmin(get("/api/deliveries/" + id), DemoAccounts.ADMIN)); + assertEquals(customer, adminsView.get("customerUsername")); + assertEquals(courier, adminsView.get("courierUsername")); + assertEquals(null, adminsView.get("courierPhone")); + assertEquals(null, adminsView.get("customerPhone")); + // A courier on a delivery stays on line. + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(false, 38.7250, -9.1400)), courier)) + .andExpect(status().isConflict()); + + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(jsonPath("$.state").value("ARRIVED")); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/start"), courier)) + .andExpect(jsonPath("$.state").value("IN_PROGRESS")); + // Too late to cancel. + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)) + .andExpect(status().isConflict()); + // And not yet something to rate. + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/rate"), "{\"stars\":5}"), customer)) + .andExpect(status().isConflict()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/complete"), courier)) + .andExpect(jsonPath("$.state").value("COMPLETED")) + .andExpect(jsonPath("$.paymentStatus").value("PAID")); + + mvc.perform(People.asCustomer(get("/api/deliveries/active"), customer)) + .andExpect(jsonPath("$.state").value("NONE")); + // The delivery is over, and so is the reason to have each other's number. + assertEquals(null, json(People.asCustomer(get("/api/deliveries/" + id), customer)).get("courierPhone")); + assertEquals(null, json(People.asCourier(get("/api/deliveries/" + id), courier)).get("customerPhone")); + // And to know where the courier is, who goes on reporting it for the + // next delivery: not from the delivery, and not from the history it stays in. + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(true, 38.7300, -9.1500)), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.courierName").value("lis-courier")) + .andExpect(jsonPath("$.courierLat").value(0.0)) + .andExpect(jsonPath("$.courierLng").value(0.0)); + mvc.perform(People.asCustomer(get("/api/deliveries/history"), customer)) + .andExpect(jsonPath("$[0].courierLat").value(0.0)) + .andExpect(jsonPath("$[0].courierLng").value(0.0)); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/rate"), "{\"stars\":9}"), customer)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/rate"), "{\"stars\":4}"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.rating").value(4)) + .andExpect(jsonPath("$.courierRating").value(4.0)); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/rate"), "{\"stars\":1}"), customer)) + .andExpect(status().isConflict()); + + mvc.perform(People.asCustomer(get("/api/deliveries/history"), customer)) + .andExpect(jsonPath("$[0].id").value(id)) + .andExpect(jsonPath("$[0].state").value("COMPLETED")); + Map earned = json(People.asCourier(get("/api/courier/earnings"), courier)); + // The courier's share, not the fare: the service keeps a fifth, and + // the onboarding screen must not show it as earned. + long share = fare - Math.round(fare * 20d / 100d); + assertEquals(share, ((Number) earned.get("totalCents")).longValue()); + assertEquals(share, ((Number) earned.get("todayCents")).longValue()); + assertEquals(share, ((Number) earned.get("weekCents")).longValue()); + assertEquals(1L, ((Number) earned.get("deliveries")).longValue()); + } + + @Test + void anOfferPassesFromCourierToCourierThenGivesUp() throws Exception { + // Madrid, with two couriers: one beside the pickup and one a kilometre off. + String customer = People.customer(accounts, "mad-customer"); + String near = People.courier(accounts, "mad-near"); + String far = People.courier(accounts, "mad-far"); + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(true, 40.4169, -3.7036)), near)).andExpect(status().isOk()); + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(true, 40.4260, -3.7036)), far)).andExpect(status().isOk()); + + Map asked = json(People.asCustomer(body(post("/api/deliveries"), + People.delivery(40.4168, -3.7038, 40.4300, -3.6900)), customer)); + String id = (String) asked.get("id"); + // The nearer one is asked first, and only that one. + mvc.perform(People.asCourier(get("/api/courier/active"), near)) + .andExpect(jsonPath("$.id").value(id)); + mvc.perform(People.asCourier(get("/api/courier/active"), far)) + .andExpect(jsonPath("$.state").value("NONE")); + // An offer is one courier's. Another cannot take it, or learn of it. + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), far)) + .andExpect(status().isNotFound()); + + // Passing on it sends it straight to the other. + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/decline"), near)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.state").value("NONE")); + mvc.perform(People.asCourier(get("/api/courier/active"), far)) + .andExpect(jsonPath("$.id").value(id)) + .andExpect(jsonPath("$.state").value("OFFERED")); + + // That one says nothing. Time is moved on by hand: the offer lapsed. + db.execute("UPDATE delivery SET offer_expires_at = 1 WHERE id = ?", new Object[] {id}); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), far)) + .andExpect(status().isConflict()); + deliveries.tick(); + // Both have passed now, so it waits, offered to nobody. + mvc.perform(People.asCustomer(get("/api/deliveries/active"), customer)) + .andExpect(jsonPath("$.state").value("REQUESTED")); + mvc.perform(People.asCourier(get("/api/courier/active"), near)) + .andExpect(jsonPath("$.state").value("NONE")); + + // And when the search runs out of time, the customer is told so. + db.execute("UPDATE delivery SET search_until = 1 WHERE id = ?", new Object[] {id}); + deliveries.tick(); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.state").value("NO_COURIERS")); + mvc.perform(People.asCustomer(get("/api/deliveries/active"), customer)) + .andExpect(jsonPath("$.state").value("NONE")); + } + + @Test + void aCustomerCancelsAndTheCourierIsFree() throws Exception { + // Rome. + String customer = People.customer(accounts, "rom-customer"); + String courier = People.courier(accounts, "rom-courier"); + String stranger = People.customer(accounts, "rom-stranger"); + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(true, 41.9028, 12.4964)), courier)).andExpect(status().isOk()); + String delivery = People.delivery(41.9030, 12.4960, 41.8902, 12.4922); + String id = (String) json(People.asCustomer(body(post("/api/deliveries"), delivery), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + + // Somebody else's delivery does not exist, to read or to cancel. + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), stranger)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), stranger)) + .andExpect(status().isNotFound()); + + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.state").value("CANCELLED_BY_CUSTOMER")); + mvc.perform(People.asCourier(get("/api/courier/active"), courier)) + .andExpect(jsonPath("$.state").value("NONE")); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(status().isConflict()); + // Free again, so the next delivery comes to the same courier. + String next = (String) json(People.asCustomer(body(post("/api/deliveries"), delivery), customer)).get("id"); + mvc.perform(People.asCourier(get("/api/courier/active"), courier)) + .andExpect(jsonPath("$.id").value(next)); + mvc.perform(People.asCustomer(post("/api/deliveries/" + next + "/cancel"), customer)) + .andExpect(status().isOk()); + } + + @Test + void aDeliveryNeedsAProvedPhoneAndSomewhereToGo() throws Exception { + mvc.perform(post("/api/account/register").contentType("application/json") + .content("{\"email\":\"unproved@test.example\",\"password\":\"a-long-password\"," + + "\"displayName\":\"Unproved\",\"phone\":\"+15550112\"}")) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries"), + People.delivery(48.8566, 2.3522, 48.8606, 2.3376)), "unproved@test.example")) + .andExpect(status().isForbidden()); + String customer = People.customer(accounts, "par-customer"); + // The same point twice, and a point that is not on the planet. + mvc.perform(People.asCustomer(body(post("/api/deliveries"), + People.delivery(48.8566, 2.3522, 48.8566, 2.3522)), customer)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/quote"), + People.delivery(148.8566, 2.3522, 48.8606, 2.3376)), customer)) + .andExpect(status().isBadRequest()); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryRaceTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryRaceTest.java new file mode 100644 index 00000000000..f30d14b69ad --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryRaceTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.delivery.Deliveries; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// A delivery answered from several places at the same moment. +/// +/// One statement decides each change of state, and the number of rows it +/// changed is the answer. These tests are the ones that would notice that +/// becoming a read followed by a write: they send the same answer from many +/// threads at once and count how many were told it was theirs. +/// +/// The threads call the service and not the HTTP layer. What is being tested +/// is the transaction each call opens, and the service is where it opens. +@BackendTest +class DeliveryRaceTest { + private static final int THREADS = 8; + /// The outcomes of one call: the delivery was taken, or the status it was + /// refused with. + private static final int TAKEN = 200; + + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private Deliveries deliveries; + @Autowired + private DataSource db; + + /// Runs `calls` at the same moment, each on a thread of its own, and waits + /// for them all. + private static void together(Runnable[] calls) throws Exception { + // A count and a flag, polled: the class library a compiled server + // links against has the atomics and no latch. + final AtomicInteger ready = new AtomicInteger(); + final AtomicBoolean go = new AtomicBoolean(); + Thread[] threads = new Thread[calls.length]; + for (int iter = 0; iter < calls.length; iter++) { + final Runnable call = calls[iter]; + threads[iter] = new Thread(new Runnable() { + @Override + public void run() { + ready.incrementAndGet(); + long giveUp = System.currentTimeMillis() + 30000L; + while (!go.get()) { + if (System.currentTimeMillis() > giveUp) { + return; + } + Thread.yield(); + } + call.run(); + } + }, "race-" + iter); + threads[iter].start(); + } + long giveUp = System.currentTimeMillis() + 30000L; + while (ready.get() < calls.length) { + assertTrue(System.currentTimeMillis() < giveUp, "the threads started"); + Thread.sleep(1L); + } + go.set(true); + for (int iter = 0; iter < threads.length; iter++) { + threads[iter].join(60000L); + assertTrue(!threads[iter].isAlive(), "thread " + iter + " finished"); + } + } + + /// A call of `accept` that writes what came of it into `outcomes[slot]`: + /// [#TAKEN], the status of the refusal, or -1 for anything else. + private Runnable accept(final String courier, final String id, final int[] outcomes, + final Throwable[] unexpected, final int slot) { + return new Runnable() { + @Override + public void run() { + try { + DeliveryDto delivery = deliveries.accept(courier, id); + outcomes[slot] = DeliveryState.ACCEPTED == delivery.state() ? TAKEN : -1; + } catch (ResponseStatusException refused) { + outcomes[slot] = refused.getStatus(); + } catch (Throwable err) { + outcomes[slot] = -1; + unexpected[slot] = err; + } + } + }; + } + + private static int count(int[] outcomes, int wanted) { + int found = 0; + for (int iter = 0; iter < outcomes.length; iter++) { + if (outcomes[iter] == wanted) { + found++; + } + } + return found; + } + + private static void noneUnexpected(Throwable[] unexpected) { + for (int iter = 0; iter < unexpected.length; iter++) { + if (unexpected[iter] != null) { + throw new AssertionError("call " + iter + " failed", unexpected[iter]); + } + } + } + + private long events(String id, String state) throws Exception { + Map row = db.queryOne("SELECT COUNT(*) AS n FROM delivery_event WHERE delivery_id = ? " + + "AND state = ?", new Object[] {id, state}); + return ((Number) row.get("n")).longValue(); + } + + /// A delivery offered to `courier`, who is on line beside the pickup. + private String offered(String customer, String courier, double lat, double lng) throws Exception { + Trips.online(mvc, courier, lat, lng); + Map asked = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + People.delivery(lat + 0.0002, lng + 0.0002, lat + 0.02, lng + 0.02)), customer)); + assertEquals("OFFERED", asked.get("state")); + String id = (String) asked.get("id"); + assertEquals(id, Trips.json(mvc, People.asCourier(get("/api/courier/active"), courier)) + .get("id")); + return id; + } + + @Test + void anOfferAcceptedEightTimesAtOnceIsTakenOnce() throws Exception { + // Warsaw. + String customer = People.customer(accounts, "waw-customer"); + String courier = People.courier(accounts, "waw-courier"); + String id = offered(customer, courier, 52.2297, 21.0122); + + int[] outcomes = new int[THREADS]; + Throwable[] unexpected = new Throwable[THREADS]; + Runnable[] calls = new Runnable[THREADS]; + for (int iter = 0; iter < THREADS; iter++) { + calls[iter] = accept(courier, id, outcomes, unexpected, iter); + } + together(calls); + + noneUnexpected(unexpected); + assertEquals(1, count(outcomes, TAKEN), "one call took the delivery"); + assertEquals(THREADS - 1, count(outcomes, 409), "and every other was told it was taken"); + // What happened is on record once, too. + assertEquals(1L, events(id, "ACCEPTED")); + assertEquals("ACCEPTED", Trips.json(mvc, People.asCustomer(get("/api/deliveries/" + id), customer)) + .get("state")); + } + + @Test + void anOfferThatLapsedIsNotTakenWhileItIsBeingTakenBack() throws Exception { + // Athens. + String customer = People.customer(accounts, "ath-customer"); + final String courier = People.courier(accounts, "ath-courier"); + final String id = offered(customer, courier, 37.9838, 23.7275); + + // Time is moved on by hand: the offer lapsed, and nothing has swept it + // up yet. Taking it is refused already. + db.execute("UPDATE delivery SET offer_expires_at = 1 WHERE id = ?", new Object[] {id}); + int[] alone = new int[1]; + Throwable[] none = new Throwable[1]; + accept(courier, id, alone, none, 0).run(); + noneUnexpected(none); + if (alone[0] != 404) { + // 404 is the sweep having run between the two lines above: the + // offer is gone and so, to this courier, is the delivery. + assertEquals(409, alone[0]); + } + + // Now the sweep and the courier's answers at the same moment. + int[] outcomes = new int[THREADS]; + Throwable[] unexpected = new Throwable[THREADS + 1]; + Runnable[] calls = new Runnable[THREADS + 1]; + for (int iter = 0; iter < THREADS; iter++) { + calls[iter] = accept(courier, id, outcomes, unexpected, iter); + } + calls[THREADS] = new Runnable() { + @Override + public void run() { + try { + deliveries.tick(); + } catch (Throwable err) { + unexpected[THREADS] = err; + } + } + }; + together(calls); + + noneUnexpected(unexpected); + assertEquals(0, count(outcomes, TAKEN), "nobody took a lapsed offer"); + // Refused as lapsed before the sweep, and as no longer theirs after it. + assertEquals(THREADS, count(outcomes, 409) + count(outcomes, 404)); + assertEquals(0L, events(id, "ACCEPTED")); + // The only courier in reach has let it go by, so it waits. + assertEquals("REQUESTED", Trips.json(mvc, People.asCustomer(get("/api/deliveries/" + id), customer)) + .get("state")); + assertEquals("NONE", Trips.json(mvc, People.asCourier(get("/api/courier/active"), courier)) + .get("state")); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)); + } + + @Test + void anOfferTakenBackIsNoLongerTheCouriersToTake() throws Exception { + // Brussels. + String customer = People.customer(accounts, "bru-customer"); + String courier = People.courier(accounts, "bru-courier"); + String id = offered(customer, courier, 50.8503, 4.3517); + + // The offer lapses and the sweep takes it back. The delivery is no longer + // this courier's, and a delivery that is not theirs is answered as no delivery + // at all: 404, as it is for any delivery of somebody else's. + db.execute("UPDATE delivery SET offer_expires_at = 1 WHERE id = ?", new Object[] {id}); + deliveries.tick(); + int[] outcome = new int[1]; + Throwable[] none = new Throwable[1]; + accept(courier, id, outcome, none, 0).run(); + noneUnexpected(none); + assertEquals(404, outcome[0]); + assertEquals(0L, events(id, "ACCEPTED")); + assertEquals("REQUESTED", Trips.json(mvc, People.asCustomer(get("/api/deliveries/" + id), customer)) + .get("state")); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryStateMachineTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryStateMachineTest.java new file mode 100644 index 00000000000..a6abdec5515 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/DeliveryStateMachineTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.delivery.DeliveryStateMachine; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DeliveryStateMachineTest { + private static final DeliveryState[] ALL = {DeliveryState.REQUESTED, DeliveryState.OFFERED, + DeliveryState.ACCEPTED, DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS, DeliveryState.COMPLETED, + DeliveryState.CANCELLED_BY_CUSTOMER, DeliveryState.CANCELLED_BY_COURIER, DeliveryState.NO_COURIERS}; + + @Test + void aDeliveryGoesForwardOneStepAtATime() { + assertTrue(DeliveryStateMachine.allowed(DeliveryState.REQUESTED, DeliveryState.OFFERED)); + assertTrue(DeliveryStateMachine.allowed(DeliveryState.OFFERED, DeliveryState.ACCEPTED)); + assertTrue(DeliveryStateMachine.allowed(DeliveryState.ACCEPTED, DeliveryState.ARRIVED)); + assertTrue(DeliveryStateMachine.allowed(DeliveryState.ARRIVED, DeliveryState.IN_PROGRESS)); + assertTrue(DeliveryStateMachine.allowed(DeliveryState.IN_PROGRESS, DeliveryState.COMPLETED)); + // No skipping: a delivery nobody accepted cannot start, and one that has + // not started cannot be completed. + assertFalse(DeliveryStateMachine.allowed(DeliveryState.REQUESTED, DeliveryState.ACCEPTED)); + assertFalse(DeliveryStateMachine.allowed(DeliveryState.OFFERED, DeliveryState.IN_PROGRESS)); + assertFalse(DeliveryStateMachine.allowed(DeliveryState.ACCEPTED, DeliveryState.COMPLETED)); + } + + @Test + void anOfferNobodyTookGoesBackToWaiting() { + assertTrue(DeliveryStateMachine.allowed(DeliveryState.OFFERED, DeliveryState.REQUESTED)); + assertFalse(DeliveryStateMachine.allowed(DeliveryState.ACCEPTED, DeliveryState.REQUESTED)); + } + + @Test + void cancellingStopsWhenTheTripStarts() { + assertTrue(DeliveryStateMachine.allowed(DeliveryState.REQUESTED, DeliveryState.CANCELLED_BY_CUSTOMER)); + assertTrue(DeliveryStateMachine.allowed(DeliveryState.ARRIVED, DeliveryState.CANCELLED_BY_CUSTOMER)); + assertFalse(DeliveryStateMachine.allowed(DeliveryState.IN_PROGRESS, DeliveryState.CANCELLED_BY_CUSTOMER)); + // A courier can only cancel a delivery they took. + assertFalse(DeliveryStateMachine.allowed(DeliveryState.OFFERED, DeliveryState.CANCELLED_BY_COURIER)); + assertTrue(DeliveryStateMachine.allowed(DeliveryState.ACCEPTED, DeliveryState.CANCELLED_BY_COURIER)); + assertFalse(DeliveryStateMachine.allowed(DeliveryState.IN_PROGRESS, DeliveryState.CANCELLED_BY_COURIER)); + } + + @Test + void nothingLeavesAFinishedState() { + DeliveryState[] finished = {DeliveryState.COMPLETED, DeliveryState.CANCELLED_BY_CUSTOMER, + DeliveryState.CANCELLED_BY_COURIER, DeliveryState.NO_COURIERS}; + for (int from = 0; from < finished.length; from++) { + for (int to = 0; to < ALL.length; to++) { + assertFalse(DeliveryStateMachine.allowed(finished[from], ALL[to]), + finished[from] + " -> " + ALL[to]); + } + } + assertEquals(0, DeliveryStateMachine.sources(DeliveryState.NONE).length); + assertTrue(DeliveryState.ARRIVED.isActive()); + assertFalse(DeliveryState.COMPLETED.isActive()); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/LiveSocketTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/LiveSocketTest.java new file mode 100644 index 00000000000..b377582d712 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/LiveSocketTest.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Crypto; +import com.codename1.backend.Tcp; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.LocalServerPort; +import com.codename1.backend.test.MockMvc; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.live.LiveHub; +import com.codenameone.examples.morsel.live.LiveTickets; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// The live channel over a real socket: a ticket opens it once, and what the +/// server pushes arrives. +@BackendTest(webEnvironment = BackendTest.WebEnvironment.RANDOM_PORT) +class LiveSocketTest { + private static final int TEXT = 0x1; + private static final int CLOSE = 0x8; + + @LocalServerPort + private int port; + @Autowired + private LiveTickets tickets; + @Autowired + private LiveHub hub; + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + + /// The last frame read: its opcode, and its payload as text. + private int opcode; + private byte[] payload; + + @Test + void aTicketOpensTheChannelOnceAndPushesArrive() throws Exception { + String ticket = tickets.issue(DemoAccounts.CUSTOMER).ticket(); + Tcp customer = open("/ws/live?ticket=" + ticket); + try { + readFrame(customer); + assertEquals("{\"type\":\"ready\"}", text()); + + writeText(customer, "{\"type\":\"ping\"}"); + readFrame(customer); + assertEquals("{\"type\":\"pong\"}", text()); + + // Something happens to a delivery of theirs. + hub.deliveryChanged("delivery-1", DeliveryState.ACCEPTED, DemoAccounts.CUSTOMER, DemoAccounts.COURIER, ""); + readFrame(customer); + assertEquals("{\"type\":\"delivery\",\"id\":\"delivery-1\",\"state\":\"ACCEPTED\"}", text()); + hub.courierMoved(DemoAccounts.CUSTOMER, "delivery-1", 37.8, -122.4, 90); + readFrame(customer); + assertTrue(text().startsWith("{\"type\":\"courier\",\"deliveryId\":\"delivery-1\",\"lat\":37.8"), + text()); + // And to somebody else's, which is none of their business. + hub.deliveryChanged("delivery-2", DeliveryState.ACCEPTED, "other@test.example", DemoAccounts.COURIER, ""); + hub.deliveryChanged("delivery-3", DeliveryState.COMPLETED, DemoAccounts.CUSTOMER, "", ""); + readFrame(customer); + assertTrue(text().indexOf("delivery-3") > 0, text()); + + // The same ticket again opens nothing. + Tcp again = open("/ws/live?ticket=" + ticket); + try { + readFrame(again); + assertEquals(CLOSE, opcode); + assertEquals(4401, closeCode()); + } finally { + again.close(); + } + + // Suspending the account closes what it has open. + hub.drop(DemoAccounts.CUSTOMER); + readFrame(customer); + assertEquals(CLOSE, opcode); + assertEquals(4403, closeCode()); + } finally { + customer.close(); + } + } + + @Test + void changingAnAccountsRolesClosesTheChannelItHasOpen() throws Exception { + // The channel keeps the roles it was opened with, so one opened as an + // admin would go on hearing the fleet after the role was taken away. + String username = People.customer(accounts, "live-roles"); + Tcp channel = open("/ws/live?ticket=" + tickets.issue(username).ticket()); + try { + readFrame(channel); + assertEquals("{\"type\":\"ready\"}", text()); + + mvc.perform(People.asAdmin(Trips.body(post("/api/admin/users/" + username + "/roles"), + "{\"courier\":true,\"admin\":false}"), DemoAccounts.ADMIN)) + .andExpect(status().isOk()); + readFrame(channel); + assertEquals(CLOSE, opcode); + } finally { + channel.close(); + } + } + + @Test + void noTicketNoChannel() throws Exception { + String[] paths = {"/ws/live", "/ws/live?ticket=", "/ws/live?ticket=0123456789abcdef"}; + for (int iter = 0; iter < paths.length; iter++) { + Tcp stranger = open(paths[iter]); + try { + readFrame(stranger); + assertEquals(CLOSE, opcode, paths[iter]); + assertEquals(4401, closeCode()); + } finally { + stranger.close(); + } + } + } + + @Test + void anAdminHearsAboutEveryDeliveryAndTheFleet() throws Exception { + Tcp admin = open("/ws/live?ticket=" + tickets.issue(DemoAccounts.ADMIN).ticket()); + try { + readFrame(admin); + assertEquals("{\"type\":\"ready\"}", text()); + hub.deliveryChanged("delivery-9", DeliveryState.REQUESTED, "someone@test.example", "", ""); + readFrame(admin); + assertTrue(text().indexOf("delivery-9") > 0, text()); + hub.fleetChanged(DemoAccounts.COURIER, true, 37.8, -122.4, 0); + readFrame(admin); + assertTrue(text().startsWith("{\"type\":\"fleet\",\"username\":\"" + DemoAccounts.COURIER + + "\",\"online\":true"), text()); + } finally { + admin.close(); + } + } + + private Tcp open(String target) throws IOException { + Tcp connection = Tcp.connect("127.0.0.1", port, 5000); + byte[] request = ascii("GET " + target + " HTTP/1.1\r\nHost: 127.0.0.1\r\n" + + "Upgrade: websocket\r\nConnection: Upgrade\r\n" + + "Sec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n"); + connection.write(request, 0, request.length); + StringBuilder head = new StringBuilder(); + // The head ends at the first empty line: CR LF CR LF. + int ending = 0; + while (head.length() < 8192 && ending < 4) { + int c = readByte(connection); + if (c < 0) { + break; + } + head.append((char) c); + ending = c == (ending % 2 == 0 ? '\r' : '\n') ? ending + 1 : (c == '\r' ? 1 : 0); + } + assertTrue(head.toString().startsWith("HTTP/1.1 101"), head.toString()); + return connection; + } + + private String text() throws IOException { + assertEquals(TEXT, opcode); + return new String(payload, "UTF-8"); + } + + private int closeCode() { + return payload.length < 2 ? -1 : ((payload[0] & 0xff) << 8) | (payload[1] & 0xff); + } + + private void readFrame(Tcp connection) throws IOException { + int first = readByte(connection); + if (first < 0) { + throw new IOException("the connection ended with no frame"); + } + opcode = first & 0xf; + int length = readByte(connection) & 0x7f; + if (length == 126) { + length = (readByte(connection) << 8) | readByte(connection); + } + payload = new byte[length]; + int got = 0; + while (got < length) { + int read = connection.read(payload, got, length - got); + if (read <= 0) { + throw new IOException("the connection ended after " + got + " of " + length); + } + got += read; + } + } + + /// A client's frames are masked; a server refuses one that is not. + private static void writeText(Tcp connection, String text) throws IOException { + byte[] body = ascii(text); + byte[] mask = Crypto.randomBytes(4); + byte[] frame = new byte[6 + body.length]; + frame[0] = (byte) (0x80 | TEXT); + frame[1] = (byte) (0x80 | body.length); + System.arraycopy(mask, 0, frame, 2, 4); + for (int iter = 0; iter < body.length; iter++) { + frame[6 + iter] = (byte) (body[iter] ^ mask[iter & 3]); + } + connection.write(frame, 0, frame.length); + } + + private static int readByte(Tcp connection) throws IOException { + byte[] one = new byte[1]; + int read = connection.read(one, 0, 1); + return read <= 0 ? -1 : one[0] & 0xff; + } + + private static byte[] ascii(String value) { + byte[] out = new byte[value.length()]; + for (int iter = 0; iter < out.length; iter++) { + out[iter] = (byte) value.charAt(iter); + } + return out; + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/MatchingTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/MatchingTest.java new file mode 100644 index 00000000000..3171e4b806c --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/MatchingTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codenameone.examples.morsel.account.Accounts; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// Which courier a delivery is offered to, when the delivery is particular: the kind of +/// car, a woman at the wheel, room for a wheelchair. +@BackendTest +class MatchingTest { + private static final double LAT = 48.2082; + private static final double LNG = 16.3738; + + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private DataSource db; + + private String ask(String customer, String extra) throws Exception { + return Trips.delivery(LAT, LNG, 48.2200, 16.3900, extra); + } + + /// Asks for a delivery, checks the state it is left in, and cancels it so the + /// customer and the courier are free for the next one. + private void expect(String customer, String extra, String state) throws Exception { + Map asked = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + ask(customer, extra)), customer)); + assertEquals(state, asked.get("state"), extra); + mvc.perform(People.asCustomer(post("/api/deliveries/" + asked.get("id") + "/cancel"), customer)) + .andExpect(status().isOk()); + } + + @Test + void aDeliveryGoesToACourierWhoFitsIt() throws Exception { + // Vienna. + String customer = People.customer(accounts, "vie-customer"); + String standard = People.courier(accounts, "vie-standard"); + String comfort = People.courier(accounts, "vie-comfort"); + // What an admin's review of the application would have recorded. + db.execute("UPDATE courier_application SET product = 'comfort' WHERE username = ?", + new Object[] {comfort}); + Trips.online(mvc, standard, LAT, LNG); + Trips.online(mvc, comfort, LAT + 0.001, LNG); + + // The quote says what there is of each kind, and prices each. + Map quote = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries/quote"), + People.delivery(LAT, LNG, 48.2200, 16.3900)), customer)); + List options = (List) quote.get("options"); + assertEquals(3, options.size()); + Map first = (Map) options.get(0); + Map second = (Map) options.get(1); + Map third = (Map) options.get(2); + assertEquals("standard", first.get("product")); + assertEquals("comfort", second.get("product")); + assertEquals("xl", third.get("product")); + assertEquals(1L, Trips.number(first, "couriersNearby")); + assertEquals(1L, Trips.number(second, "couriersNearby")); + assertEquals(0L, Trips.number(third, "couriersNearby")); + assertEquals(4L, Trips.number(first, "seats")); + assertEquals(6L, Trips.number(third, "seats")); + long fare = Trips.number(first, "fare"); + assertEquals(fare, Trips.number(quote, "amountCents")); + assertEquals(fare + Trips.number(first, "serviceFee"), Trips.number(first, "total")); + // Each part of a fare is multiplied and rounded on its own, so the + // dearer kinds are within a cent or two of the multiple. + assertTrue(Math.abs(Trips.number(second, "fare") - fare * 1.35d) <= 2d); + assertTrue(Math.abs(Trips.number(third, "fare") - fare * 1.7d) <= 2d); + // Asked for a kind, the quote is that kind's. + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/quote"), + ask(customer, "\"product\":\"comfort\"")), customer)) + .andExpect(jsonPath("$.amountCents").value((int) Trips.number(second, "fare"))) + .andExpect(jsonPath("$.couriersNearby").value(1)); + mvc.perform(People.asCustomer(get("/api/deliveries/nearby?lat=" + LAT + "&lng=" + LNG), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(2)) + .andExpect(jsonPath("$[0].product").value("standard")) + .andExpect(jsonPath("$[1].product").value("comfort")); + + // A comfort delivery goes past the nearer, standard car. + Map asked = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + ask(customer, "\"product\":\"comfort\"")), customer)); + String id = (String) asked.get("id"); + assertEquals("OFFERED", asked.get("state")); + assertEquals("comfort", asked.get("product")); + assertEquals(Trips.number(second, "fare"), Trips.number(asked, "fareCents")); + mvc.perform(People.asCourier(get("/api/courier/active"), standard)) + .andExpect(jsonPath("$.state").value("NONE")); + mvc.perform(People.asCourier(get("/api/courier/active"), comfort)) + .andExpect(jsonPath("$.id").value(id)); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)) + .andExpect(status().isOk()); + // Nobody drives an XL here, so one waits; and there is no fourth kind. + expect(customer, "\"product\":\"xl\"", "REQUESTED"); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries"), + ask(customer, "\"product\":\"limousine\"")), customer)) + .andExpect(status().isBadRequest()); + + // A woman courier, asked for: neither is one until one says so. + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries"), + ask(customer, "\"courierGender\":\"men\"")), customer)) + .andExpect(status().isBadRequest()); + expect(customer, "\"courierGender\":\"women\"", "REQUESTED"); + db.execute("UPDATE profile SET gender = 'female' WHERE username = ?", + new Object[] {comfort}); + // Still not for a standard delivery: she drives a comfort car. + expect(customer, "\"courierGender\":\"women\"", "REQUESTED"); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/quote"), + ask(customer, "\"courierGender\":\"women\"")), customer)) + .andExpect(jsonPath("$.couriersNearby").value(0)) + .andExpect(jsonPath("$.options[1].couriersNearby").value(1)); + asked = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + ask(customer, "\"courierGender\":\"women\",\"product\":\"comfort\"")), customer)); + id = (String) asked.get("id"); + assertEquals("OFFERED", asked.get("state")); + assertEquals("women", asked.get("courierGender")); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), comfort)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.courierGender").value("women")); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)) + .andExpect(status().isOk()); + + // A car that takes a wheelchair. + expect(customer, "\"accessibleVehicle\":true", "REQUESTED"); + db.execute("UPDATE courier_application SET wheelchair = 1 WHERE username = ?", + new Object[] {standard}); + expect(customer, "\"accessibleVehicle\":true", "OFFERED"); + + // A courier who takes pets: nobody, until one says so. + expect(customer, "\"petFriendly\":true", "REQUESTED"); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/quote"), + ask(customer, "\"petFriendly\":true")), customer)) + .andExpect(jsonPath("$.couriersNearby").value(0)); + db.execute("UPDATE courier_application SET pets = 1 WHERE username = ?", + new Object[] {standard}); + expect(customer, "\"petFriendly\":true", "OFFERED"); + + // An approval withdrawn takes a courier out of matching at once, on + // line or not. + expect(customer, "\"product\":\"standard\"", "OFFERED"); + db.execute("UPDATE courier_application SET status = 'rejected' WHERE username = ?", + new Object[] {standard}); + expect(customer, "\"product\":\"standard\"", "REQUESTED"); + mvc.perform(People.asCustomer(get("/api/deliveries/nearby?lat=" + LAT + "&lng=" + LNG), customer)) + .andExpect(jsonPath("$.length()").value(1)); + db.execute("UPDATE courier_application SET status = 'approved' WHERE username = ?", + new Object[] {standard}); + Trips.offline(mvc, standard); + Trips.offline(mvc, comfort); + } + + @Test + void aCourierWithNoProfileIsOfferedNothing() throws Exception { + // Zurich. + String customer = People.customer(accounts, "zrh-customer"); + String courier = People.courier(accounts, "zrh-courier"); + Trips.online(mvc, courier, 47.3769, 8.5417); + String delivery = People.delivery(47.3771, 8.5419, 47.3900, 8.5600); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/quote"), delivery), customer)) + .andExpect(jsonPath("$.couriersNearby").value(1)); + + // What an account that is half gone leaves behind: on line, approved, + // and nobody. Whatever the delivery asks for, it is not sent there. + db.execute("DELETE FROM profile WHERE username = ?", new Object[] {courier}); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/quote"), delivery), customer)) + .andExpect(jsonPath("$.couriersNearby").value(0)); + Map asked = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), delivery), customer)); + assertEquals("REQUESTED", asked.get("state")); + mvc.perform(People.asCustomer(post("/api/deliveries/" + asked.get("id") + "/cancel"), customer)) + .andExpect(status().isOk()); + } + + @Test + void aCourierWhoGoesOffLineIsOffLine() throws Exception { + // Two reports close together, so the second counts the time between + // them as time on line: adding that time is a bulk update, and the + // courier's row must already be written when it runs. The pause is + // what makes that time more than nothing: two reports in the same + // millisecond add no time and run no update. + String courier = People.courier(accounts, "ldn-courier"); + Trips.online(mvc, courier, 51.5072, -0.1276); + Thread.sleep(50); + mvc.perform(People.asCourier(Trips.body(post("/api/courier/status"), + People.position(false, 0, 0)), courier)) + .andExpect(jsonPath("$.online").value(false)); + mvc.perform(People.asCourier(get("/api/courier/me"), courier)) + .andExpect(jsonPath("$.online").value(false)); + String delivery = People.delivery(51.5074, -0.1278, 51.5155, -0.0922); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/quote"), delivery), + People.customer(accounts, "ldn-customer"))) + .andExpect(jsonPath("$.couriersNearby").value(0)); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ModerationTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ModerationTest.java new file mode 100644 index 00000000000..52c09bd628f --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ModerationTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/// What an admin does about an account or a delivery: flag it, block it, cancel +/// it, and the record that is kept of each. +@BackendTest +class ModerationTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + + private MockRequestBuilder admin(MockRequestBuilder request) { + return People.asAdmin(request, DemoAccounts.ADMIN); + } + + @Test + void anAccountIsFlaggedThenBlockedAndTheRecordSaysWhy() throws Exception { + String who = People.customer(accounts, "troublesome"); + String token = SignIn.token(mvc, who, People.PASSWORD); + Map before = Trips.json(mvc, admin(get("/api/admin/stats/series?days=7"))); + + // A flag says why, and changes nothing the account can do. + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/flag"), + "{\"flagged\":true}"))) + .andExpect(status().isBadRequest()); + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/flag"), + "{\"flagged\":true,\"reason\":\"Rude to two couriers\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.flagged").value(true)) + .andExpect(jsonPath("$.flagReason").value("Rude to two couriers")) + .andExpect(jsonPath("$.suspended").value(false)); + mvc.perform(get("/api/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.flagged").value(true)); + Map after = Trips.json(mvc, admin(get("/api/admin/stats/series?days=7"))); + assertEquals(Trips.number(before, "flaggedUsers") + 1, Trips.number(after, "flaggedUsers")); + + // Not in the middle of a delivery: nobody could end it. Waiting for a + // courier is in the middle of one. + String delivery = (String) Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + People.delivery(41.3874, 2.1686, 41.3974, 2.1786)), who)).get("id"); + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/suspend"), + "{\"suspended\":true}"))) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(post("/api/deliveries/" + delivery + "/cancel"), who)) + .andExpect(status().isOk()); + + // A block is what stops it: the token in hand, and signing in again. + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/suspend"), + "{\"suspended\":true,\"reason\":\"And then a third\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.suspended").value(true)); + mvc.perform(get("/api/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isUnauthorized()); + mvc.perform(get("/api/payments/methods").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isUnauthorized()); + mvc.perform(Trips.body(post("/api/deliveries").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token), + People.delivery(60.1699, 24.9384, 60.1800, 24.9500))) + .andExpect(status().isUnauthorized()); + assertEquals(null, SignIn.session(mvc, who, People.PASSWORD)); + + // The account's record, newest first: who did what, and why. + Map detail = Trips.json(mvc, admin(get("/api/admin/users/" + who))); + assertEquals(who, ((Map) detail.get("user")).get("username")); + List events = (List) detail.get("events"); + assertEquals(2, events.size()); + Map block = (Map) events.get(0); + assertEquals("block", block.get("action")); + assertEquals("And then a third", block.get("reason")); + assertEquals(DemoAccounts.ADMIN, block.get("by")); + assertTrue(Trips.number(block, "at") > 0L); + assertEquals("flag", ((Map) events.get(1)).get("action")); + + // Both are undone, and that is recorded too. + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/suspend"), + "{\"suspended\":false}"))) + .andExpect(status().isOk()); + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/flag"), + "{\"flagged\":false}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.flagged").value(false)) + .andExpect(jsonPath("$.flagReason").value("")); + mvc.perform(get("/api/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()); + mvc.perform(admin(get("/api/admin/users/" + who))) + .andExpect(jsonPath("$.events.length()").value(4)) + .andExpect(jsonPath("$.events[0].action").value("unflag")) + .andExpect(jsonPath("$.events[1].action").value("unblock")); + mvc.perform(admin(get("/api/admin/users/nobody@test.example"))) + .andExpect(status().isNotFound()); + } + + @Test + void anAdminWhoWasJustRemovedChangesNobody() throws Exception { + // Two admins taking each other's role at the same moment were both + // authorized before either committed. The one whose turn comes second + // is looked at again inside its transaction, and by then is no admin. + String first = People.customer(accounts, "admin-one"); + String second = People.customer(accounts, "admin-two"); + accounts.setRoles(DemoAccounts.ADMIN, first, false, true); + accounts.setRoles(DemoAccounts.ADMIN, second, false, true); + accounts.setRoles(first, second, false, false); + try { + accounts.setRoles(second, first, false, false); + fail("an admin who lost the role still withdrew another's"); + } catch (ResponseStatusException refused) { + assertEquals(403, refused.getStatus()); + } + try { + accounts.suspend(second, first, true, ""); + fail("an admin who lost the role still blocked another"); + } catch (ResponseStatusException refused) { + assertEquals(403, refused.getStatus()); + } + assertTrue(accounts.describe(first).admin(), "the first admin is one still"); + assertFalse(accounts.describe(first).suspended(), "and is not blocked"); + } + + @Test + void theDemoDataHasAFlaggedCustomerWithAHistory() throws Exception { + Map detail = Trips.json(mvc, admin(get("/api/admin/users/" + DemoAccounts.FLAGGED_CUSTOMER))); + Map user = (Map) detail.get("user"); + assertEquals(Boolean.TRUE, user.get("flagged")); + assertTrue(Trips.number(detail, "deliveries") > 0L, "the fortnight gave it deliveries"); + assertEquals("flag", ((Map) ((List) detail.get("events")).get(0)).get("action")); + } + + @Test + void anAdminCancelsADeliveryThatIsUnderWay() throws Exception { + // Helsinki, where nobody drives: the delivery waits. + String customer = People.customer(accounts, "hel-customer"); + String id = (String) Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + People.delivery(60.1699, 24.9384, 60.1800, 24.9500)), customer)).get("id"); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/cancel"), "{}"))) + .andExpect(status().isBadRequest()); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/cancel"), + "{\"reason\":\"Reported as a prank\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.state").value("CANCELLED_BY_ADMIN")); + mvc.perform(People.asCustomer(get("/api/deliveries/active"), customer)) + .andExpect(jsonPath("$.state").value("NONE")); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.state").value("CANCELLED_BY_ADMIN")); + // Over is over, and a delivery that never was is not found. + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/cancel"), + "{\"reason\":\"Again\"}"))) + .andExpect(status().isConflict()); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/nosuchdelivery/cancel"), + "{\"reason\":\"Again\"}"))) + .andExpect(status().isNotFound()); + // A delivery that ended this way cost nothing, so it has no receipt. + mvc.perform(People.asCustomer(get("/api/payments/receipts/" + id), customer)) + .andExpect(status().isNotFound()); + mvc.perform(admin(get("/api/admin/users/" + customer))) + .andExpect(jsonPath("$.events[0].action").value("cancel_delivery")) + .andExpect(jsonPath("$.events[0].reason").value("Reported as a prank")); + } + + @Test + void aCourierKeepsTheRoleUntilTheDeliveryTheyAreOnHasEnded() throws Exception { + // Tallinn. + String customer = People.customer(accounts, "tll-customer"); + String courier = People.courier(accounts, "tll-courier"); + Trips.online(mvc, courier, 59.4370, 24.7536); + String id = (String) Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + People.delivery(59.4370, 24.7536, 59.4470, 24.7636)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + + // Withdrawn now, the courier could do nothing more for this customer. + mvc.perform(admin(Trips.body(post("/api/admin/users/" + courier + "/roles"), + "{\"courier\":false,\"admin\":false}"))) + .andExpect(status().isConflict()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(status().isOk()); + + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/cancel"), + "{\"reason\":\"Courier is leaving\"}"))) + .andExpect(status().isOk()); + mvc.perform(admin(Trips.body(post("/api/admin/users/" + courier + "/roles"), + "{\"courier\":false,\"admin\":false}"))) + .andExpect(status().isOk()); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/OnboardingApplicationTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/OnboardingApplicationTest.java new file mode 100644 index 00000000000..4da9bf40940 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/OnboardingApplicationTest.java @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcRequestBuilders.put; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// Applying to drive: a draft, its documents, handing it in, and an admin's +/// answer either way. +@BackendTest +class OnboardingApplicationTest { + /// The smallest things the server takes for a picture: the first bytes of a + /// PNG and of a JPEG, as far as the header that gives their size (one + /// pixel), in base64. + private static final String PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"; + private static final String JPEG = "/9j/4AAQSkZJRgABAQAAAQABAAD/wAARCAABAAEDASIAAhEBAxEB"; + private static final String[] KINDS = {"photo_id", "licence", "selfie", "vehicle_registration", + "insurance"}; + private static final String FORM = "{\"legalName\":\"Ada Applicant\"," + + "\"dateOfBirth\":\"1990-05-17\",\"licenceNumber\":\"L-1234\"," + + "\"licenceExpiry\":\"2999-01-31\",\"vehicleMake\":\"Honda\"," + + "\"vehicleModel\":\"Civic\",\"vehicleYear\":2020,\"vehicleColor\":\"Grey\"," + + "\"vehiclePlate\":\"APP 1\",\"vehicleSeats\":4,\"product\":\"standard\"}"; + + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + + private static String picture(String kind, String data) { + return "{\"kind\":\"" + kind + "\",\"contentType\":\"image/png\",\"dataBase64\":\"" + data + + "\"}"; + } + + private MockRequestBuilder admin(MockRequestBuilder request) { + return People.asAdmin(request, DemoAccounts.ADMIN); + } + + private void upload(String who, String kind) throws Exception { + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture(kind, PNG)), who)).andExpect(status().isOk()); + } + + @Test + void anApplicationIsDraftedHandedInRejectedCorrectedAndApproved() throws Exception { + String who = People.customer(accounts, "applies"); + String token = SignIn.token(mvc, who, People.PASSWORD); + + // Nothing yet, and nothing to hand in. + mvc.perform(People.asCustomer(get("/api/onboarding/application"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("none")) + .andExpect(jsonPath("$.documents.length()").value(5)) + .andExpect(jsonPath("$.documents[0].status").value("missing")); + mvc.perform(People.asCustomer(get("/api/me"), who)) + .andExpect(jsonPath("$.courierStatus").value("none")); + + // Half a form is a draft. It saves, and it cannot be handed in. + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"legalName\":\"Ada Applicant\",\"vehicleMake\":\"Honda\"}"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("draft")) + .andExpect(jsonPath("$.legalName").value("Ada Applicant")); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"dateOfBirth\":\"17/05/1990\"}"), who)) + .andExpect(status().isBadRequest()); + // Written the right way, and no day that ever was. + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"dateOfBirth\":\"1990-02-30\"}"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"dateOfBirth\":\"1991-02-29\"}"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"dateOfBirth\":\"1992-04-31\"}"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"dateOfBirth\":\"1992-02-29\"}"), who)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(post("/api/onboarding/application/submit"), who)) + .andExpect(status().isBadRequest()); + + // A complete form is still not complete without its documents. + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), FORM), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.vehicleYear").value(2020)); + mvc.perform(People.asCustomer(post("/api/onboarding/application/submit"), who)) + .andExpect(status().isBadRequest()); + + // A document has to be a picture, of a kind that exists, of a sane size. + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("photo_id", "bm90IGEgcGljdHVyZQ==")), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("passport", PNG)), who)) + .andExpect(status().isBadRequest()); + StringBuilder huge = new StringBuilder(PNG); + while (huge.length() < 2200000) { + huge.append("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("photo_id", huge.toString())), who)) + .andExpect(status().isPayloadTooLarge()); + // A few bytes that say 100000 pixels wide: refused before an admin's + // app tries to decode them. + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("photo_id", "iVBORw0KGgoAAAANSUhEUgABhqAAAAAB")), who)) + .andExpect(status().isPayloadTooLarge()); + // A JPEG that never says its size is no picture. + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("photo_id", "/9j/4AAQSkZJRgABAQAAAQABAAA=")), who)) + .andExpect(status().isBadRequest()); + + for (int iter = 0; iter < 4; iter++) { + upload(who, KINDS[iter]); + } + // Four of five. + mvc.perform(People.asCustomer(post("/api/onboarding/application/submit"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("insurance", JPEG)), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.documents[4].kind").value("insurance")) + .andExpect(jsonPath("$.documents[4].status").value("uploaded")); + mvc.perform(People.asCustomer(get("/api/onboarding/application/documents/insurance"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.contentType").value("image/jpeg")) + .andExpect(jsonPath("$.dataBase64").value(JPEG)); + + mvc.perform(People.asCustomer(post("/api/onboarding/application/submit"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("pending")); + // In an admin's hands now: not the applicant's to change. + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), FORM), who)) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("selfie", PNG)), who)) + .andExpect(status().isConflict()); + // Still not a courier: by the role, and by the token already in hand. + mvc.perform(People.asCustomer(get("/api/me"), who)) + .andExpect(jsonPath("$.courier").value(false)) + .andExpect(jsonPath("$.courierStatus").value("pending")); + mvc.perform(get("/api/courier/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isForbidden()); + + // The admin finds it among those waiting, and reads a document. + List waiting = Trips.list(mvc, admin(get("/api/admin/applications?status=pending"))); + boolean found = false; + for (int iter = 0; iter < waiting.size(); iter++) { + found |= who.equals(((Map) waiting.get(iter)).get("username")); + } + assertTrue(found, "the application is waiting"); + mvc.perform(admin(get("/api/admin/applications?status=nonsense"))) + .andExpect(status().isBadRequest()); + mvc.perform(admin(get("/api/admin/applications/" + who))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("applies")) + .andExpect(jsonPath("$.licenceNumber").value("L-1234")); + mvc.perform(admin(get("/api/admin/applications/" + who + "/documents/licence"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.dataBase64").value(PNG)); + mvc.perform(admin(get("/api/admin/applications/nobody@test.example"))) + .andExpect(status().isNotFound()); + + // Rejected, which needs a reason, and the applicant is told it. + mvc.perform(admin(Trips.body(post("/api/admin/applications/" + who + "/reject"), "{}"))) + .andExpect(status().isBadRequest()); + mvc.perform(admin(Trips.body(post("/api/admin/applications/" + who + "/reject"), + "{\"reason\":\"The licence photo is unreadable\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("rejected")) + .andExpect(jsonPath("$.reviewedBy").value(DemoAccounts.ADMIN)); + mvc.perform(admin(post("/api/admin/applications/" + who + "/approve"))) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(get("/api/onboarding/application"), who)) + .andExpect(jsonPath("$.status").value("rejected")) + .andExpect(jsonPath("$.rejectionReason").value("The licence photo is unreadable")); + + // Corrected and handed in again, and this time approved. + upload(who, "licence"); + mvc.perform(People.asCustomer(post("/api/onboarding/application/submit"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("pending")) + .andExpect(jsonPath("$.rejectionReason").value("")); + mvc.perform(admin(post("/api/admin/applications/" + who + "/approve"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("approved")) + .andExpect(jsonPath("$.documents[0].status").value("accepted")); + mvc.perform(admin(post("/api/admin/applications/" + who + "/approve"))) + .andExpect(status().isConflict()); + + // Approval is what grants the role: the same token now drives, in the + // car the application named. + mvc.perform(get("/api/courier/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.approved").value(true)) + .andExpect(jsonPath("$.vehicle").value("Grey Honda Civic")) + .andExpect(jsonPath("$.plate").value("APP 1")) + .andExpect(jsonPath("$.product").value("standard")); + mvc.perform(People.asCustomer(get("/api/me"), who)) + .andExpect(jsonPath("$.courier").value(true)) + .andExpect(jsonPath("$.customer").value(true)) + .andExpect(jsonPath("$.courierStatus").value("approved")); + // Both answers are on the account's record. + Map detail = Trips.json(mvc, admin(get("/api/admin/users/" + who))); + List events = (List) detail.get("events"); + assertEquals(2, events.size()); + assertEquals("approve", ((Map) events.get(0)).get("action")); + assertEquals("reject", ((Map) events.get(1)).get("action")); + assertEquals("The licence photo is unreadable", ((Map) events.get(1)).get("reason")); + } + + @Test + void onlyAnApprovedCourierGoesOnLine() throws Exception { + // Holding the role is not enough. This account is let into the + // courier's part of the API and has an application nobody has approved. + String who = People.customer(accounts, "unapproved"); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), FORM), who)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(Trips.body(post("/api/courier/status"), + People.position(true, 59.9139, 10.7522)), who)) + .andExpect(status().isForbidden()); + mvc.perform(People.asCourier(get("/api/courier/me"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.approved").value(false)) + .andExpect(jsonPath("$.online").value(false)); + + // An admin's switch approves, and withdrawing it takes that back. + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/roles"), + "{\"courier\":true,\"admin\":false}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.courierStatus").value("approved")); + Trips.online(mvc, who, 59.9139, 10.7522); + mvc.perform(admin(Trips.body(post("/api/admin/users/" + who + "/roles"), + "{\"courier\":false,\"admin\":false}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.courier").value(false)) + .andExpect(jsonPath("$.courierStatus").value("rejected")); + mvc.perform(People.asCourier(Trips.body(post("/api/courier/status"), + People.position(true, 59.9139, 10.7522)), who)) + .andExpect(status().isForbidden()); + mvc.perform(People.asCourier(get("/api/courier/me"), who)) + .andExpect(jsonPath("$.online").value(false)); + } + + @Test + void anApplicationIsItsOwnersAndTheAdminsOnly() throws Exception { + String owner = People.customer(accounts, "doc-owner"); + String other = People.customer(accounts, "doc-other"); + upload(owner, "photo_id"); + // There is no address by which one applicant reaches another's: each + // sees their own, and the other has none. + mvc.perform(People.asCustomer(get("/api/onboarding/application/documents/photo_id"), other)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(get("/api/onboarding/application"), other)) + .andExpect(jsonPath("$.status").value("none")) + .andExpect(jsonPath("$.username").value(other)); + mvc.perform(People.asCustomer(get("/api/admin/applications/" + owner + + "/documents/photo_id"), other)) + .andExpect(status().isForbidden()); + mvc.perform(People.asCourier(get("/api/admin/applications/" + owner), other)) + .andExpect(status().isForbidden()); + mvc.perform(get("/api/onboarding/application/documents/photo_id")) + .andExpect(status().isUnauthorized()); + mvc.perform(admin(get("/api/admin/applications/" + owner + "/documents/photo_id"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.kind").value("photo_id")); + mvc.perform(admin(get("/api/admin/applications/" + owner + "/documents/selfie"))) + .andExpect(status().isNotFound()); + } + + /// A save or an upload authorized just before the account closed, and + /// run just after, keeps nothing: the closure already forgot the + /// application, and nothing would forget it again. + @Test + void aClosedAccountKeepsNoApplication() throws Exception { + String who = People.customer(accounts, "applies-gone"); + upload(who, "photo_id"); + accounts.close(who, People.PASSWORD); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), FORM), who)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(Trips.body(post("/api/onboarding/application/documents"), + picture("selfie", PNG)), who)).andExpect(status().isNotFound()); + mvc.perform(admin(get("/api/admin/applications/" + who))) + .andExpect(status().isNotFound()); + mvc.perform(admin(get("/api/admin/applications/" + who + "/documents/selfie"))) + .andExpect(status().isNotFound()); + } + + @Test + void theDemoApplicantIsWaitingAndTheDemoCouriersAreApproved() throws Exception { + mvc.perform(admin(get("/api/admin/applications/" + DemoAccounts.APPLICANT))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("pending")) + .andExpect(jsonPath("$.documents[2].status").value("uploaded")); + mvc.perform(admin(get("/api/admin/applications/" + DemoAccounts.COURIER))) + .andExpect(jsonPath("$.status").value("approved")) + .andExpect(jsonPath("$.vehicleMake").value("Toyota")); + mvc.perform(admin(get("/api/admin/applications/" + DemoAccounts.XL_COURIER))) + .andExpect(jsonPath("$.product").value("xl")) + .andExpect(jsonPath("$.accessible").value(true)); + // Registering to drive, the way the app asks, opens a draft. + mvc.perform(post("/api/account/register").contentType("application/json") + .content("{\"email\":\"wants@test.example\",\"password\":\"a-long-password\"," + + "\"displayName\":\"Wants\",\"phone\":\"+15550113\"," + + "\"wantsToDrive\":true,\"gender\":\"female\"}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.courier").value(false)) + .andExpect(jsonPath("$.gender").value("female")) + .andExpect(jsonPath("$.courierStatus").value("draft")); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java new file mode 100644 index 00000000000..0cbe2afd3d0 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import com.codenameone.examples.morsel.pay.Payments; +import com.codenameone.examples.morsel.delivery.Deliveries; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcRequestBuilders.put; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// Paying: the cards an account keeps, the charge when a delivery completes, the +/// tip after it, the refund an admin can make, and what all of it earns the +/// courier. Against the simulated processor, which is what a server with no +/// key configured runs. +@BackendTest +class PaymentsTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private Deliveries deliveries; + @Autowired + private Payments payments; + @Autowired + private DataSource db; + + private MockRequestBuilder admin(MockRequestBuilder request) { + return People.asAdmin(request, DemoAccounts.ADMIN); + } + + private long setupsOf(String username) throws Exception { + Map row = db.queryOne("SELECT COUNT(*) AS n FROM payment_setup WHERE username = ?", + new Object[] {username}); + return ((Number) row.get("n")).longValue(); + } + + @Test + void openSetupsAreCappedAndTheFinishedAndAbandonedAreSwept() throws Exception { + String customer = People.customer(accounts, "setup-looper"); + // Five open at once is as many as an account may have; each is a + // session made at the processor. + String first = null; + for (int iter = 0; iter < 5; iter++) { + Map setup = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), + customer)); + first = first == null ? (String) setup.get("id") : first; + } + mvc.perform(People.asCustomer(post("/api/payments/methods/setup"), customer)) + .andExpect(status().is(429)); + // Finishing one makes room for another. + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + first + + "/complete"), Trips.card(Trips.VISA)), customer)) + .andExpect(status().isOk()); + String last = (String) Trips.json(mvc, People.asCustomer( + post("/api/payments/methods/setup"), customer)).get("id"); + assertEquals(6L, setupsOf(customer)); + // So does an hour going by: abandoned setups do not count. + db.execute("UPDATE payment_setup SET created_at = ? WHERE username = ? AND id <> ?", + new Object[] {Long.valueOf(System.currentTimeMillis() - 90L * 60L * 1000L), + customer, last}); + Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + + // The sweep deletes the finished one, and an abandoned one once it is + // two hours old; younger ones, and the open ones, stay. + payments.sweepSetups(); + assertEquals(6L, setupsOf(customer)); + db.execute("UPDATE payment_setup SET created_at = ? WHERE username = ? AND created_at < ?", + new Object[] {Long.valueOf(System.currentTimeMillis() - 3L * 60L * 60L * 1000L), + customer, Long.valueOf(System.currentTimeMillis() - 60L * 60L * 1000L)}); + payments.sweepSetups(); + assertEquals(2L, setupsOf(customer)); + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + last + + "/complete"), Trips.card(Trips.MASTERCARD)), customer)) + .andExpect(status().isOk()); + } + + @Test + void todaysTripsCountTodaysDeliveriesAlone() throws Exception { + // Tallinn. + String customer = People.customer(accounts, "tll-pay-customer"); + String courier = People.courier(accounts, "tll-pay-courier"); + Trips.online(mvc, courier, 59.4370, 24.7536); + String yesterday = (String) Trips.complete(mvc, customer, courier, People.delivery(59.4370, + 24.7536, 59.4470, 24.7636)).get("id"); + Trips.complete(mvc, customer, courier, People.delivery(59.4370, 24.7536, 59.4470, 24.7636)); + long now = System.currentTimeMillis(); + db.execute("UPDATE delivery SET updated_at = ? WHERE id = ?", new Object[] { + Long.valueOf(now - now % (24L * 60L * 60L * 1000L) - 60L * 60L * 1000L), yesterday}); + Map earned = Trips.json(mvc, People.asCourier(get("/api/courier/earnings"), courier)); + // Both are trips the courier made; one of them was today's, and the + // header's count is of the deliveries its amount is made of. + assertEquals(2L, Trips.number(earned, "deliveries")); + assertEquals(1L, Trips.number(earned, "todayDeliveries")); + Trips.offline(mvc, courier); + } + + @Test + void cardsAreSavedChosenAndRemoved() throws Exception { + String customer = People.customer(accounts, "card-holder"); + String other = People.customer(accounts, "card-other"); + mvc.perform(People.asCustomer(get("/api/payments/config"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.provider").value("simulated")) + .andExpect(jsonPath("$.currency").value("USD")); + // Cash, always, and the default until there is a card. + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(1)) + .andExpect(jsonPath("$[0].id").value("cash")) + .andExpect(jsonPath("$[0].kind").value("cash")) + .andExpect(jsonPath("$[0].isDefault").value(true)); + + // A card that is refused is not saved, and says why; the same setup + // then takes a card that is good. + Map setup = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + assertEquals(Boolean.FALSE, setup.get("hosted")); + String complete = "/api/payments/methods/setup/" + setup.get("id") + "/complete"; + mvc.perform(People.asCustomer(Trips.body(post(complete), Trips.card(Trips.DECLINED)), customer)) + .andExpect(status().is(402)); + mvc.perform(People.asCustomer(Trips.body(post(complete), Trips.card("4242424242424241")), + customer)) + .andExpect(status().is(402)); + // Somebody else cannot finish it for them. + mvc.perform(People.asCustomer(Trips.body(post(complete), Trips.card(Trips.VISA)), other)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(1)); + Map visa = Trips.json(mvc, People.asCustomer(Trips.body(post(complete), + Trips.card(Trips.VISA)), customer)); + assertEquals("card", visa.get("kind")); + assertEquals("Visa", visa.get("brand")); + assertEquals("4242", visa.get("last4")); + assertEquals("Visa 4242", visa.get("label")); + assertEquals(12L, Trips.number(visa, "expMonth")); + // The first card saved is the one to pay with. + assertEquals(Boolean.TRUE, visa.get("isDefault")); + // A setup is used once. + mvc.perform(People.asCustomer(Trips.body(post(complete), Trips.card(Trips.VISA)), customer)) + .andExpect(status().isNotFound()); + + Map second = Trips.addCard(mvc, customer, Trips.MASTERCARD); + assertEquals("Mastercard", second.get("brand")); + assertEquals(Boolean.FALSE, second.get("isDefault")); + String visaId = (String) visa.get("id"); + String secondId = (String) second.get("id"); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + secondId + "/default"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.isDefault").value(true)); + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(3)) + .andExpect(jsonPath("$[0].id").value(visaId)) + .andExpect(jsonPath("$[0].isDefault").value(false)) + .andExpect(jsonPath("$[1].isDefault").value(true)) + .andExpect(jsonPath("$[2].id").value("cash")) + .andExpect(jsonPath("$[2].isDefault").value(false)); + + // A card is its owner's: nobody else sees it, chooses it or removes it. + mvc.perform(People.asCustomer(get("/api/payments/methods"), other)) + .andExpect(jsonPath("$.length()").value(1)); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + visaId + "/default"), other)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + visaId + "/remove"), other)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries"), Trips.delivery(52.52, 13.405, + 52.53, 13.42, "\"paymentMethodId\":\"" + visaId + "\"")), other)) + .andExpect(status().isBadRequest()); + + // Removing the default leaves cash as the default; cash stays. + mvc.perform(People.asCustomer(post("/api/payments/methods/" + secondId + "/remove"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(secondId)); + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(2)) + .andExpect(jsonPath("$[1].id").value("cash")) + .andExpect(jsonPath("$[1].isDefault").value(true)); + mvc.perform(People.asCustomer(post("/api/payments/methods/cash/remove"), customer)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(post("/api/payments/methods/cash/default"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.isDefault").value(true)); + } + + @Test + void aDeliveryIsChargedTippedAndRefundedAndTheCourierIsPaid() throws Exception { + // Berlin. + String customer = People.customer(accounts, "ber-customer"); + String courier = People.courier(accounts, "ber-courier"); + String stranger = People.customer(accounts, "ber-stranger"); + Trips.addCard(mvc, customer, Trips.VISA); + Trips.online(mvc, courier, 52.5200, 13.4050); + + // No method named: the default, which is now the card. + Map done = Trips.complete(mvc, customer, courier, People.delivery(52.5200, 13.4050, 52.5300, + 13.4200)); + String id = (String) done.get("id"); + long fare = Trips.number(done, "fareCents"); + long fee = Trips.number(done, "serviceFee"); + assertEquals("PAID", done.get("paymentStatus")); + assertEquals("Visa 4242", done.get("paymentMethodLabel")); + assertEquals(Boolean.FALSE, done.get("cash")); + assertEquals("standard", done.get("product")); + assertEquals(Math.round(fare * 10d / 100d), fee); + assertEquals(fare + fee, Trips.number(done, "total")); + assertEquals("B", done.get("courierPhotoInitials")); + + // The receipt adds up, part by part. + Map receipt = Trips.receipt(mvc, customer, id); + assertEquals("paid", receipt.get("status")); + assertEquals("Visa 4242", receipt.get("methodLabel")); + assertEquals(fare, Trips.number(receipt, "baseFare") + Trips.number(receipt, + "distanceFare") + Trips.number(receipt, "timeFare")); + assertEquals(fee, Trips.number(receipt, "serviceFee")); + assertEquals(0L, Trips.number(receipt, "tip")); + assertEquals(fare + fee, Trips.number(receipt, "total")); + assertTrue(Trips.number(receipt, "paidAt") > 0L); + assertEquals("Here", receipt.get("pickupName")); + assertEquals("ber-courier", receipt.get("courierName")); + assertEquals("Green Fiat 500", receipt.get("vehicle")); + // It is the customer's, and nobody else's -- the courier's included. + mvc.perform(People.asCustomer(get("/api/payments/receipts/" + id), stranger)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCourier(get("/api/payments/receipts/" + id), courier)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(get("/api/payments/receipts"), stranger)) + .andExpect(jsonPath("$.length()").value(0)); + mvc.perform(People.asCustomer(get("/api/payments/receipts"), customer)) + .andExpect(jsonPath("$.length()").value(1)) + .andExpect(jsonPath("$[0].deliveryId").value(id)); + + // A tip, once, by the customer. + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + id + "/tip"), + "{\"amount\":300}"), stranger)) + .andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + id + "/tip"), + "{\"amount\":0}"), customer)) + .andExpect(status().isBadRequest()); + Map tipped = Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries/" + id + "/tip"), + "{\"amount\":300}"), customer)); + assertEquals(300L, Trips.number(tipped, "tip")); + assertEquals(fare + fee + 300L, Trips.number(tipped, "total")); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + id + "/tip"), + "{\"amount\":300}"), customer)) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.tip").value(300)); + + // The courier earns the fare less the service's fifth, and all the tip. + long earned = fare - Math.round(fare * 20d / 100d) + 300L; + Map summary = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), + courier)); + assertEquals("USD", summary.get("currency")); + assertEquals(earned, Trips.number(summary, "today")); + assertEquals(earned, Trips.number(summary, "week")); + assertEquals(earned, Trips.number(summary, "month")); + assertEquals(earned, Trips.number(summary, "balance")); + assertEquals(1L, Trips.number(summary, "trips")); + assertEquals(300L, Trips.number(summary, "tips")); + assertEquals(1d, ((Number) summary.get("acceptanceRate")).doubleValue()); + List daily = (List) summary.get("daily"); + assertEquals(14, daily.size()); + Map today = (Map) daily.get(13); + assertEquals(Days.iso(System.currentTimeMillis()), today.get("date")); + assertEquals(earned, Trips.number(today, "amount")); + assertEquals(1L, Trips.number(today, "trips")); + assertEquals(0L, Trips.number((Map) daily.get(0), "amount")); + // The older call reports the same share, without the tip. + mvc.perform(People.asCourier(get("/api/courier/earnings"), courier)) + .andExpect(jsonPath("$.totalCents").value((int) (earned - 300L))); + + // Nor can an account that is owed money be closed: nobody could cash + // it out afterwards. + mvc.perform(People.asCourier(Trips.body(post("/api/account/delete"), + "{\"password\":\"" + People.PASSWORD + "\"}"), courier)) + .andExpect(status().isConflict()); + + // Cashing out needs somewhere to send it, and empties the balance. + mvc.perform(People.asCourier(get("/api/courier/payouts"), courier)) + .andExpect(jsonPath("$.length()").value(0)); + mvc.perform(People.asCourier(post("/api/courier/payouts"), courier)) + .andExpect(status().isConflict()); + mvc.perform(People.asCourier(get("/api/courier/payout-account"), courier)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.accountLast4").value("")); + mvc.perform(People.asCourier(Trips.body(put("/api/courier/payout-account"), + "{\"holder\":\"B Courier\",\"bankName\":\"Spree Bank\"," + + "\"accountLast4\":\"12a4\"}"), courier)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCourier(Trips.body(put("/api/courier/payout-account"), + "{\"holder\":\"B Courier\",\"bankName\":\"Spree Bank\"," + + "\"accountLast4\":\"1234\"}"), courier)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.bankName").value("Spree Bank")); + Map payout = Trips.json(mvc, People.asCourier(post("/api/courier/payouts"), courier)); + assertEquals(earned, Trips.number(payout, "amount")); + assertEquals("paid", payout.get("status")); + assertEquals("Spree Bank ****1234", payout.get("destination")); + mvc.perform(People.asCourier(post("/api/courier/payouts"), courier)) + .andExpect(status().isConflict()); + mvc.perform(People.asCourier(get("/api/courier/payouts"), courier)) + .andExpect(jsonPath("$.length()").value(1)) + .andExpect(jsonPath("$[0].id").value((String) payout.get("id"))); + summary = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), courier)); + assertEquals(0L, Trips.number(summary, "balance")); + assertEquals(earned, Trips.number(summary, "today")); + + // An admin sees every payment, and returns this one, once, saying why. + List all = Trips.list(mvc, admin(get("/api/admin/payments"))); + boolean listed = false; + for (int iter = 0; iter < all.size(); iter++) { + Map row = (Map) all.get(iter); + if (id.equals(row.get("deliveryId"))) { + listed = true; + assertEquals(customer, row.get("customerUsername")); + } + } + assertTrue(listed, "the admin's list has the delivery"); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), "{}"))) + .andExpect(status().isBadRequest()); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"The car broke down\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("refunded")); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Twice\"}"))) + .andExpect(status().isConflict()); + assertEquals("refunded", Trips.receipt(mvc, customer, id).get("status")); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("REFUNDED")); + + // The refund took the fare and the tip back, so neither is earned any + // more: not in the day's totals, and not in the balance, which now owes + // what was cashed out. The trip was still driven. + summary = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), courier)); + assertEquals(0L, Trips.number(summary, "today")); + assertEquals(0L, Trips.number(summary, "month")); + assertEquals(0L, Trips.number((Map) ((List) summary.get("daily")).get(13), "amount")); + assertEquals(1L, Trips.number((Map) ((List) summary.get("daily")).get(13), "trips")); + assertEquals(0L, Trips.number(summary, "tips")); + assertEquals(-earned, Trips.number(summary, "balance")); + mvc.perform(People.asCourier(post("/api/courier/payouts"), courier)) + .andExpect(status().isConflict()); + Trips.offline(mvc, courier); + } + + /// A card tip whose charge nobody can vouch for is retried with the + /// delivery's card, so that card stays, and so does its account, until the + /// tip is settled one way or the other. + @Test + void aCardWithATipNobodyCanVouchForStays() throws Exception { + // Bergen. + String customer = People.customer(accounts, "bgo-tip-customer"); + String courier = People.courier(accounts, "bgo-tip-courier"); + String card = (String) Trips.addCard(mvc, customer, Trips.VISA).get("id"); + Trips.online(mvc, courier, 60.3913, 5.3221); + String id = (String) Trips.complete(mvc, customer, courier, People.delivery(60.3913, 5.3221, + 60.4013, 5.3321)).get("id"); + Trips.offline(mvc, courier); + db.execute("UPDATE delivery SET tip_cents = 300 WHERE id = ?", new Object[] {id}); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + card + "/remove"), customer)) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/delete"), + "{\"password\":\"" + People.PASSWORD + "\"}"), customer)) + .andExpect(status().isConflict()); + + // The charge came back: nothing is left to ask about. + db.execute("UPDATE payment SET tip_ref = 'sim_ch_tip' WHERE delivery_id = ?", + new Object[] {id}); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + card + "/remove"), customer)) + .andExpect(status().isOk()); + } + + @Test + void aDeliveryIsNotRefundedWhileItsTipIsBeingCharged() throws Exception { + // Oslo. + String customer = People.customer(accounts, "osl-customer"); + String courier = People.courier(accounts, "osl-courier"); + Trips.addCard(mvc, customer, Trips.VISA); + Trips.online(mvc, courier, 59.9139, 10.7522); + String id = (String) Trips.complete(mvc, customer, courier, People.delivery(59.9139, 10.7522, + 59.9239, 10.7622)).get("id"); + + // A tip as it stands between being claimed and its charge coming back: + // on the delivery, and with no charge recorded for it. + db.execute("UPDATE delivery SET tip_cents = 300 WHERE id = ?", new Object[] {id}); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Too soon\"}"))) + .andExpect(status().isConflict()); + // Refused whole: the delivery is still paid, so the refund can be asked + // for again and the tip's charge has a paid delivery to land on. + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("PAID")); + // Nor is it the courier's to cash out: nobody knows it was charged. + Map before = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), + courier)); + assertEquals(0L, Trips.number(before, "tips")); + long fareOnly = Trips.number(before, "balance"); + // Nor earned today: the day's total is what the balance grew by. + assertEquals(fareOnly, Trips.number(before, "today")); + assertEquals(fareOnly, Trips.number(before, "month")); + + // The charge came back. + db.execute("UPDATE payment SET tip_ref = 'sim_ch_tip' WHERE delivery_id = ?", + new Object[] {id}); + Map after = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), + courier)); + assertEquals(300L, Trips.number(after, "tips")); + assertEquals(fareOnly + 300L, Trips.number(after, "balance")); + assertEquals(fareOnly + 300L, Trips.number(after, "today")); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Now\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("refunded")); + Trips.offline(mvc, courier); + } + + @Test + void theAdminSeesWhatTheCourierEarnedAndNoMore() throws Exception { + // Vilnius. + String customer = People.customer(accounts, "vno-customer"); + String courier = People.courier(accounts, "vno-courier"); + Trips.addCard(mvc, customer, Trips.VISA); + Trips.online(mvc, courier, 54.6872, 25.2797); + String paid = (String) Trips.complete(mvc, customer, courier, People.delivery(54.6872, 25.2797, + 54.6972, 25.2897)).get("id"); + String pending = (String) Trips.complete(mvc, customer, courier, People.delivery(54.6872, + 25.2797, 54.6972, 25.2897)).get("id"); + String refunded = (String) Trips.complete(mvc, customer, courier, People.delivery(54.6872, + 25.2797, 54.6972, 25.2897)).get("id"); + // A charge nobody has answered yet, a delivery refunded, and a tip on the + // paid delivery whose charge has not come back. + db.execute("UPDATE delivery SET payment_status = 'PENDING' WHERE id = ?", + new Object[] {pending}); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + refunded + "/refund"), + "{\"reason\":\"Wrong address\"}"))) + .andExpect(status().isOk()); + db.execute("UPDATE delivery SET tip_cents = 300 WHERE id = ?", new Object[] {paid}); + + long share = ((Number) db.queryOne("SELECT courier_cents FROM delivery WHERE id = ?", + new Object[] {paid}).get("courier_cents")).longValue(); + Map summary = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), + courier)); + Map detail = Trips.json(mvc, admin(get("/api/admin/users/" + courier))); + // The paid delivery's share alone, on both screens. + assertEquals(share, Trips.number(summary, "balance")); + assertEquals(share, Trips.number(summary, "month")); + assertEquals(share, Trips.number(detail, "earned")); + + // Once the tip's charge is on record it is earned on both. + db.execute("UPDATE payment SET tip_ref = 'sim_ch_tip' WHERE delivery_id = ?", + new Object[] {paid}); + summary = Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), courier)); + detail = Trips.json(mvc, admin(get("/api/admin/users/" + courier))); + assertEquals(share + 300L, Trips.number(summary, "balance")); + assertEquals(share + 300L, Trips.number(detail, "earned")); + Trips.offline(mvc, courier); + } + + @Test + void aRefundTheServerNeverRecordedIsAskedForAgain() throws Exception { + // Bergen. + String customer = People.customer(accounts, "bgo-customer"); + String courier = People.courier(accounts, "bgo-courier"); + Trips.addCard(mvc, customer, Trips.VISA); + Trips.online(mvc, courier, 60.3913, 5.3221); + String id = (String) Trips.complete(mvc, customer, courier, People.delivery(60.3913, 5.3221, + 60.4013, 5.3321)).get("id"); + + // A refund as a server that stopped half way leaves it: the delivery + // marked, the processor perhaps asked, and nothing written down. + db.execute("UPDATE delivery SET payment_status = 'REFUNDED' WHERE id = ?", + new Object[] {id}); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Again\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("refunded")); + // And once it is recorded, it is refunded once like any other. + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Twice\"}"))) + .andExpect(status().isConflict()); + Trips.offline(mvc, courier); + } + + @Test + void aRefundTheProcessorHasNotFinishedIsRecordedOnlyOnceItEnds() throws Exception { + // Gothenburg. + String customer = People.customer(accounts, "got-customer"); + String courier = People.courier(accounts, "got-courier"); + String card = (String) Trips.addCard(mvc, customer, Trips.SLOW_REFUND).get("id"); + String method = "\"paymentMethodId\":\"" + card + "\""; + Trips.online(mvc, courier, 57.7089, 11.9746); + String id = (String) Trips.complete(mvc, customer, courier, Trips.delivery(57.7089, 11.9746, + 57.7189, 11.9846, method)).get("id"); + + // Taken, and not yet money back: pending, and not to be asked for twice. + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Slow\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("refund_pending")); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("REFUND_PENDING")); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Again\"}"))) + .andExpect(status().isConflict()); + // The processor has finished it by the time it is asked. + payments.settleRefunds(); + assertEquals("refunded", Trips.receipt(mvc, customer, id).get("status")); + + // And one the processor then fails is recorded as failed, never as + // refunded, and is not tried again from here. + db.execute("UPDATE payment_method SET token = 'sim_pm_lost_card' WHERE id = ?", + new Object[] {card}); + String lost = (String) Trips.complete(mvc, customer, courier, Trips.delivery(57.7089, 11.9746, + 57.7189, 11.9846, method)).get("id"); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + lost + "/refund"), + "{\"reason\":\"Lost\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("refund_pending")); + payments.settleRefunds(); + assertEquals("refund_failed", Trips.receipt(mvc, customer, lost).get("status")); + mvc.perform(People.asCustomer(get("/api/deliveries/" + lost), customer)) + .andExpect(jsonPath("$.paymentStatus").value("REFUND_FAILED")); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + lost + "/refund"), + "{\"reason\":\"Again\"}"))) + .andExpect(status().isConflict()); + Trips.offline(mvc, courier); + } + + @Test + void aDeliveryLeftCompletedAndUnpaidIsSettledLater() throws Exception { + // Lisbon. + String customer = People.customer(accounts, "lis-pay-customer"); + String courier = People.courier(accounts, "lis-pay-courier"); + Trips.addCard(mvc, customer, Trips.VISA); + Trips.online(mvc, courier, 38.7223, -9.1393); + Map done = Trips.complete(mvc, customer, courier, People.delivery(38.7223, -9.1393, 38.7323, + -9.1293)); + String id = (String) done.get("id"); + long total = Trips.number(done, "fareCents") + Trips.number(done, "serviceFee"); + + // The server stopped after the delivery completed and before its payment + // was written. + db.execute("DELETE FROM payment WHERE delivery_id = ?", new Object[] {id}); + db.execute("UPDATE delivery SET payment_status = 'PENDING' WHERE id = ?", + new Object[] {id}); + + // Completed a moment ago, it may still be at the processor: left alone. + deliveries.settleLeftBehind(); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("PENDING")); + + db.execute("UPDATE delivery SET updated_at = ? WHERE id = ?", + new Object[] {Long.valueOf(System.currentTimeMillis() - 5L * 60L * 1000L), id}); + deliveries.settleLeftBehind(); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("PAID")); + Map receipt = Trips.receipt(mvc, customer, id); + assertEquals("paid", receipt.get("status")); + assertEquals(total, Trips.number(receipt, "total")); + + // And once: a second pass finds nothing left to settle. + deliveries.settleLeftBehind(); + assertEquals(total, Trips.number(Trips.receipt(mvc, customer, id), "total")); + Trips.offline(mvc, courier); + } + + @Test + void aChargeNobodyCanVouchForIsLeftPendingAndAskedForAgain() throws Exception { + // Prague. + String customer = People.customer(accounts, "prg-pay-customer"); + String courier = People.courier(accounts, "prg-pay-courier"); + String card = (String) Trips.addCard(mvc, customer, Trips.NO_ANSWER_WHEN_CHARGED).get("id"); + Trips.online(mvc, courier, 50.0755, 14.4378); + Map done = Trips.complete(mvc, customer, courier, Trips.delivery(50.0755, 14.4378, 50.0855, + 14.4478, "\"paymentMethodId\":\"" + card + "\"")); + String id = (String) done.get("id"); + long total = Trips.number(done, "fareCents") + Trips.number(done, "serviceFee"); + + // The processor failed without saying whether it took the money: not + // paid, and not failed either. + assertEquals("COMPLETED", done.get("state")); + assertEquals("PENDING", done.get("paymentStatus")); + // Not money yet, so not the courier's to cash out. + assertEquals(0L, Trips.number(Trips.json(mvc, People.asCourier( + get("/api/courier/earnings/summary"), courier)), "balance")); + // The card is what the delivery will be asked about with, so it stays, + // and so does the account it belongs to. + mvc.perform(People.asCustomer(post("/api/payments/methods/" + card + "/remove"), customer)) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/delete"), + "{\"password\":\"" + People.PASSWORD + "\"}"), customer)) + .andExpect(status().isConflict()); + db.execute("UPDATE delivery SET updated_at = ? WHERE id = ?", + new Object[] {Long.valueOf(System.currentTimeMillis() - 5L * 60L * 1000L), id}); + deliveries.settleLeftBehind(); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("PENDING")); + + // The processor answers again, and the same delivery is asked about. + db.execute("UPDATE payment_method SET token = 'sim_pm_recovered' WHERE id = ?", + new Object[] {card}); + deliveries.settleLeftBehind(); + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("PAID")); + assertEquals(total, Trips.number(Trips.receipt(mvc, customer, id), "total")); + // Paid now, and the courier's share is in the balance. + assertEquals(Trips.number(done, "fareCents") - Math.round( + Trips.number(done, "fareCents") * 20d / 100d), Trips.number(Trips.json(mvc, + People.asCourier(get("/api/courier/earnings/summary"), courier)), "balance")); + // Settled, and the card can go. + mvc.perform(People.asCustomer(post("/api/payments/methods/" + card + "/remove"), customer)) + .andExpect(status().isOk()); + Trips.offline(mvc, courier); + } + + @Test + void aTipNobodyCanVouchForStaysOnTheDeliveryUntilItIsAskedForAgain() throws Exception { + // Vienna. + String customer = People.customer(accounts, "vie-pay-customer"); + String courier = People.courier(accounts, "vie-pay-courier"); + String card = (String) Trips.addCard(mvc, customer, Trips.VISA).get("id"); + Trips.online(mvc, courier, 48.2082, 16.3738); + String id = (String) Trips.complete(mvc, customer, courier, People.delivery(48.2082, 16.3738, + 48.2182, 16.3838)).get("id"); + + // The processor stops answering, and a tip is asked for. + db.execute("UPDATE payment_method SET token = 'sim_pm_fault_now' WHERE id = ?", + new Object[] {card}); + assertEquals(502, Trips.statusOf(mvc, People.asCustomer(Trips.body( + post("/api/deliveries/" + id + "/tip"), "{\"amount\":300}"), customer))); + // It may have been charged, so it stays on the delivery, which cannot be + // refunded around it, and cannot be swapped for another amount. + assertEquals(300L, Trips.number(Trips.receipt(mvc, customer, id), "tip")); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Too soon\"}"))) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + id + "/tip"), + "{\"amount\":500}"), customer)) + .andExpect(status().isConflict()); + + // Asked for again with the same amount once the processor is back. + db.execute("UPDATE payment_method SET token = 'sim_pm_recovered' WHERE id = ?", + new Object[] {card}); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + id + "/tip"), + "{\"amount\":300}"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.tip").value(300)); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + id + "/refund"), + "{\"reason\":\"Now\"}"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("refunded")); + Trips.offline(mvc, courier); + } + + @Test + void theCardLimitHoldsForSetupsStartedWhileThereWasRoom() throws Exception { + String customer = People.customer(accounts, "card-collector"); + for (int iter = 0; iter < 7; iter++) { + Trips.addCard(mvc, customer, Trips.VISA); + } + // Two setups started with room for one card. + Map first = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + Map second = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + + first.get("id") + "/complete"), Trips.card(Trips.MASTERCARD)), customer)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + + second.get("id") + "/complete"), Trips.card(Trips.MASTERCARD)), customer)) + .andExpect(status().isConflict()); + // Cash and eight cards. + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(9)); + } + + @Test + void cashIsRecordedAndACardThatFailsIsReported() throws Exception { + // Dublin. + String customer = People.customer(accounts, "dub-customer"); + String courier = People.courier(accounts, "dub-courier"); + String failing = (String) Trips.addCard(mvc, customer, Trips.FAILS_WHEN_CHARGED).get("id"); + Trips.online(mvc, courier, 53.3498, -6.2603); + + // Cash, asked for by name although a card is the default. + Map cash = Trips.complete(mvc, customer, courier, Trips.delivery(53.3498, -6.2603, 53.3600, + -6.2500, "\"paymentMethodId\":\"cash\",\"note\":\"Blue door\",\"quietDelivery\":true")); + String cashId = (String) cash.get("id"); + assertEquals("PAID", cash.get("paymentStatus")); + assertEquals("Cash", cash.get("paymentMethodLabel")); + assertEquals(Boolean.TRUE, cash.get("cash")); + assertEquals("Blue door", cash.get("note")); + assertEquals(Boolean.TRUE, cash.get("quietDelivery")); + assertEquals("cash", Trips.receipt(mvc, customer, cashId).get("status")); + // Nothing went to a card, so there is nothing to send back to one. + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + cashId + "/refund"), + "{\"reason\":\"Asked nicely\"}"))) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + cashId + "/tip"), + "{\"amount\":150}"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.tip").value(150)) + .andExpect(jsonPath("$.status").value("cash")); + + // The card that is refused when charged: the delivery still ends, and + // says it was not paid for. + Map unpaid = Trips.complete(mvc, customer, courier, Trips.delivery(53.3498, -6.2603, 53.3600, + -6.2500, "\"paymentMethodId\":\"" + failing + "\"")); + String unpaidId = (String) unpaid.get("id"); + assertEquals("COMPLETED", unpaid.get("state")); + assertEquals("FAILED", unpaid.get("paymentStatus")); + assertEquals("failed", Trips.receipt(mvc, customer, unpaidId).get("status")); + mvc.perform(People.asCustomer(Trips.body(post("/api/deliveries/" + unpaidId + "/tip"), + "{\"amount\":150}"), customer)) + .andExpect(status().isConflict()); + mvc.perform(admin(Trips.body(post("/api/admin/deliveries/" + unpaidId + "/refund"), + "{\"reason\":\"Nothing to return\"}"))) + .andExpect(status().isConflict()); + // A card with a delivery riding on it is not removed until the delivery ends. + String waiting = (String) Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + Trips.delivery(53.3498, -6.2603, 53.3600, -6.2500, "\"paymentMethodId\":\"" + failing + + "\"")), customer)).get("id"); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + failing + "/remove"), customer)) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(post("/api/deliveries/" + waiting + "/cancel"), customer)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(post("/api/payments/methods/" + failing + "/remove"), customer)) + .andExpect(status().isOk()); + Trips.offline(mvc, courier); + } + + @Test + void theHostedSetupEndsOnAPageThatSaysSo() throws Exception { + String done = mvc.perform(get("/pay/return?result=done")).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + assertTrue(done.indexOf("Card added") > 0, done); + String cancelled = mvc.perform(get("/pay/return?result=%3Cscript%3E")) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + // Nothing from the address is written into the page. + assertTrue(cancelled.indexOf("No card was added") > 0, cancelled); + assertEquals(-1, cancelled.indexOf("= 300L && base < 300L + admins, "one of them is in force: " + base); + rows = db.queryOne("SELECT COUNT(*) AS n FROM pricing", new Object[0]); + assertEquals(1L, ((Number) rows.get("n")).longValue(), "and there is one row"); + } finally { + // The other tests share these prices. + fares.save(DemoAccounts.ADMIN, before); + } + } + + @Test + void theDashboardsSeriesHasEveryDayAndEveryHour() throws Exception { + Map week = Trips.json(mvc, admin(get("/api/admin/stats/series?days=7"))); + List days = (List) week.get("days"); + assertEquals(7, days.size()); + // Oldest first, ending today, and a day with nothing in it is there. + Map last = (Map) days.get(6); + assertEquals(Days.iso(System.currentTimeMillis()), last.get("date")); + assertEquals(Days.iso(System.currentTimeMillis() - 6L * 86400000L), + ((Map) days.get(0)).get("date")); + for (int iter = 0; iter < days.size(); iter++) { + Map day = (Map) days.get(iter); + assertTrue(Trips.number(day, "deliveries") >= Trips.number(day, "completed") + + Trips.number(day, "cancelled"), "day " + iter); + assertTrue(Trips.number(day, "revenue") >= 0L); + assertNotNull(day.get("newUsers")); + } + assertEquals(24, ((List) week.get("byHour")).size()); + List products = (List) week.get("byProduct"); + assertEquals(3, products.size()); + assertEquals("standard", ((Map) products.get(0)).get("name")); + assertEquals("comfort", ((Map) products.get(1)).get("name")); + assertEquals("xl", ((Map) products.get(2)).get("name")); + assertEquals("USD", week.get("currency")); + assertNotNull(week.get("byState")); + assertTrue(((List) week.get("topCouriers")).size() <= 5); + // The demo data has somebody waiting to be reviewed and somebody + // flagged, and a fortnight of deliveries behind them. + assertTrue(Trips.number(week, "pendingApplications") >= 1L); + assertTrue(Trips.number(week, "flaggedUsers") >= 1L); + assertTrue(Trips.number(week, "averageFare") > 0L); + double rate = ((Number) week.get("completionRate")).doubleValue(); + assertTrue(rate > 0d && rate <= 1d, "completion rate " + rate); + long deliveries = 0L; + for (int iter = 0; iter < days.size(); iter++) { + deliveries += Trips.number((Map) days.get(iter), "deliveries"); + } + assertTrue(deliveries > 0L, "the week has deliveries in it"); + + // A silly number is the fortnight; and there is a most. + mvc.perform(admin(get("/api/admin/stats/series?days=0"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.days.length()").value(14)); + mvc.perform(admin(get("/api/admin/stats/series?days=1000"))) + .andExpect(jsonPath("$.days.length()").value(90)) + .andExpect(jsonPath("$.byHour.length()").value(24)); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ProcessorFaultsTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ProcessorFaultsTest.java new file mode 100644 index 00000000000..dc5ef4d317b --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ProcessorFaultsTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.annotations.Bean; +import com.codename1.backend.annotations.Primary; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.TestConfiguration; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.pay.Payments; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/// Paying, against a processor that fails when a test says so: what is left +/// behind when a request to it fails part way, alone or overlapping another. +/// The service is called directly, on the test's thread, so the request a +/// test lines up inside another runs exactly where it is put. +@BackendTest +class ProcessorFaultsTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private Payments payments; + @Autowired + private ScriptedPayments processor; + @Autowired + private DataSource db; + + @TestConfiguration + static class Processor { + @Bean + @Primary + ScriptedPayments scriptedPayments() { + return new ScriptedPayments(); + } + } + + /// The status `call` was refused with, or 200 when it was not. + private static int refusal(Runnable call) { + try { + call.run(); + return 200; + } catch (ResponseStatusException refused) { + return refused.getStatus(); + } + } + + private Runnable refund(final String deliveryId) { + return new Runnable() { + @Override + public void run() { + try { + payments.refund(deliveryId, "Overlapping"); + } catch (IOException err) { + throw new IllegalStateException(err); + } + } + }; + } + + private Runnable remove(final String customer, final String card) { + return new Runnable() { + @Override + public void run() { + try { + payments.remove(customer, card); + } catch (IOException err) { + throw new IllegalStateException(err); + } + } + }; + } + + private long balance(String courier) throws Exception { + return Trips.number(Trips.json(mvc, People.asCourier(get("/api/courier/earnings/summary"), + courier)), "balance"); + } + + @Test + void aRemovedCardIsDetachedAtTheProcessorFirst() throws Exception { + processor.reset(); + String customer = People.customer(accounts, "card-detacher"); + String card = (String) Trips.addCard(mvc, customer, Trips.VISA).get("id"); + String token = (String) db.queryOne("SELECT token FROM payment_method WHERE id = ?", + new Object[] {card}).get("token"); + + // A processor that cannot be reached keeps the card: deleted here + // alone, it would stay chargeable there with nothing pointing at it. + processor.detachFails = true; + assertEquals(502, refusal(remove(customer, card))); + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(2)) + .andExpect(jsonPath("$[0].id").value(card)); + assertEquals(0, processor.detached.size()); + + processor.detachFails = false; + assertEquals(200, refusal(remove(customer, card))); + assertEquals(1, processor.detached.size()); + assertEquals(token, processor.detached.get(0)); + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(1)); + } + + @Test + void aCardTheLimitRefusesIsDetachedAtTheProcessor() throws Exception { + processor.reset(); + String customer = People.customer(accounts, "card-overflow"); + for (int iter = 0; iter < 7; iter++) { + Trips.addCard(mvc, customer, Trips.VISA); + } + // Two setups started with room for one card, and a third. + Map first = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + Map second = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + Map third = Trips.json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + + first.get("id") + "/complete"), Trips.card(Trips.MASTERCARD)), customer)) + .andExpect(status().isOk()); + assertEquals(0, processor.detached.size()); + + // The processor attached the second card before the limit refused it; + // with no row here, it is detached there, and no card that was kept. + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + + second.get("id") + "/complete"), Trips.card(Trips.MASTERCARD)), customer)) + .andExpect(status().isConflict()); + assertEquals(1, processor.detached.size()); + Map kept = db.queryOne("SELECT COUNT(*) AS n FROM payment_method WHERE token = ?", + new Object[] {processor.detached.get(0)}); + assertEquals(0L, ((Number) kept.get("n")).longValue()); + + // A detach that fails is logged, and the customer still hears the limit. + processor.detachFails = true; + mvc.perform(People.asCustomer(Trips.body(post("/api/payments/methods/setup/" + + third.get("id") + "/complete"), Trips.card(Trips.MASTERCARD)), customer)) + .andExpect(status().isConflict()); + assertEquals(1, processor.detached.size()); + mvc.perform(People.asCustomer(get("/api/payments/methods"), customer)) + .andExpect(jsonPath("$.length()").value(9)); + processor.reset(); + } + + @Test + void aRefundThatFailsDoesNotUndoOneThatOverlappedIt() throws Exception { + processor.reset(); + // Riga. + String customer = People.customer(accounts, "rix-customer"); + String courier = People.courier(accounts, "rix-courier"); + Trips.addCard(mvc, customer, Trips.VISA); + Trips.online(mvc, courier, 56.9496, 24.1052); + String id = (String) Trips.complete(mvc, customer, courier, People.delivery(56.9496, 24.1052, + 56.9596, 24.1152)).get("id"); + + // A refund whose processor fails, and while it is at the processor a + // second one -- let through as one that may have been cut short -- + // that succeeds and is recorded. The second answers first. + processor.duringRefund = refund(id); + processor.refundFailures.add(Boolean.FALSE); + processor.refundFailures.add(Boolean.TRUE); + assertEquals(502, refusal(refund(id))); + // The failure gives back nothing: the payment was refunded, so the + // delivery is, and its fare is not the courier's to cash out. + mvc.perform(People.asCustomer(get("/api/deliveries/" + id), customer)) + .andExpect(jsonPath("$.paymentStatus").value("REFUNDED")); + assertEquals("refunded", Trips.receipt(mvc, customer, id).get("status")); + assertEquals(0L, balance(courier)); + + // The other way round: the overlapping refund fails and gives its + // claim back while the first is still at the processor, and the first + // then succeeds. + final String next = (String) Trips.complete(mvc, customer, courier, People.delivery(56.9496, + 24.1052, 56.9596, 24.1152)).get("id"); + processor.duringRefund = new Runnable() { + @Override + public void run() { + assertEquals(502, refusal(refund(next))); + } + }; + processor.refundFailures.add(Boolean.TRUE); + processor.refundFailures.add(Boolean.FALSE); + assertEquals(200, refusal(refund(next))); + Map delivery = Trips.json(mvc, People.asCustomer(get("/api/deliveries/" + next), customer)); + assertEquals("REFUNDED", delivery.get("paymentStatus")); + assertEquals(0L, balance(courier)); + Trips.offline(mvc, courier); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PushTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PushTest.java new file mode 100644 index 00000000000..648f881ce11 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/PushTest.java @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.DataSource; +import com.codename1.backend.ResponseStatusException; +import com.codename1.backend.Json; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.api.PushDeviceDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.push.PushDevices; +import com.codenameone.examples.morsel.push.PushSender; +import com.codenameone.examples.morsel.push.RecordingPushSender; +import com.codenameone.examples.morsel.push.DeliveryPushes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcRequestBuilders.put; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/// The push notifications a delivery sends, the devices they go to, and the Live +/// Activity a customer follows the courier in. +/// +/// The `test` profile keeps every notification instead of sending it, so each +/// test reads what each phone would have been sent. +@BackendTest +class PushTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + @Autowired + private PushSender sender; + @Autowired + private DataSource db; + @Autowired + private PushDevices pushDevices; + @Autowired + private DeliveryPushes pushes; + + private RecordingPushSender sent; + + @BeforeEach + void recording() { + assertTrue(sender instanceof RecordingPushSender, + "the test profile records notifications: " + sender); + sent = (RecordingPushSender) sender; + } + + private static MockRequestBuilder body(MockRequestBuilder request, String json) { + return request.contentType("application/json").content(json); + } + + private static String device(String provider, String token) { + return "{\"provider\":\"" + provider + "\",\"token\":\"" + token + + "\",\"platform\":\"and\"}"; + } + + private void register(String username, String provider, String token) throws Exception { + mvc.perform(People.asCustomer(body(post("/api/account/push"), device(provider, token)), + username)).andExpect(status().isOk()); + } + + /// The titles `token` was sent, in order; a silent update counts as "". + private String titles(String token) { + StringBuilder out = new StringBuilder(); + for (RecordingPushSender.Sent each : sent.sentTo(token)) { + Object title = each.message().get("title"); + out.append(out.length() > 0 ? "|" : "").append(title == null ? "" : title); + } + return out.toString(); + } + + private static Map data(RecordingPushSender.Sent message) { + return (Map) message.message().get("data"); + } + + private static Map surface(RecordingPushSender.Sent message) { + return (Map) message.message().get("surface"); + } + + private static double progress(Map command) throws Exception { + return ((Number) Json.parseObject((String) command.get("state")).get("progress")) + .doubleValue(); + } + + private static RecordingPushSender.Sent last(List list) { + return list.get(list.size() - 1); + } + + @Test + void aDeviceIsRegisteredInTheProvidersOwnSpelling() throws Exception { + String customer = People.customer(accounts, "push-reg-customer"); + mvc.perform(People.asCustomer(body(post("/api/account/push"), + device("gcm", "reg-token-1")), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.provider").value("fcm")) + .andExpect(jsonPath("$.token").value("reg-token-1")); + // An alias is the same device, not a second one. + mvc.perform(People.asCustomer(body(post("/api/account/push"), + device("fcm", "reg-token-1")), customer)).andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/account/push"), + device("pigeon", "reg-token-2")), customer)).andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(body(post("/api/account/push"), + device("apns", " ")), customer)).andExpect(status().isBadRequest()); + } + + @Test + void customersAndCouriersAreToldOfEveryStepOfADelivery() throws Exception { + // Reykjavik. Each test has a city to itself. + String customer = People.customer(accounts, "push-rkv-customer"); + String courier = People.courier(accounts, "push-rkv-courier"); + register(customer, "apns", "rkv-customer-phone"); + register(courier, "fcm", "rkv-courier-phone"); + Trips.online(mvc, courier, 64.1466, -21.9426); + + Map asked = Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(64.1467, -21.9428, 64.1597, -21.9548)), customer)); + String id = (String) asked.get("id"); + // The offer goes to the courier, who has a few seconds to answer it. + List toCourier = sent.sentTo("rkv-courier-phone"); + assertEquals("New delivery request", titles("rkv-courier-phone")); + assertEquals(id, data(toCourier.get(0)).get("deliveryId")); + assertEquals("OFFERED", data(toCourier.get(0)).get("state")); + assertEquals("courier", data(toCourier.get(0)).get("role")); + assertEquals("fcm", toCourier.get(0).provider()); + assertTrue(((Number) toCourier.get(0).message().get("ttl")).intValue() > 0); + assertEquals("", titles("rkv-customer-phone")); + + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + List toCustomer = sent.sentTo("rkv-customer-phone"); + assertEquals("Your courier is on the way", titles("rkv-customer-phone")); + String body = (String) toCustomer.get(0).message().get("body"); + assertTrue(body.contains("push-rkv-courier") && body.contains("Green Fiat 500"), body); + assertEquals("apns", toCustomer.get(0).provider()); + assertEquals("customer", data(toCustomer.get(0)).get("role")); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/start"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/complete"), courier)) + .andExpect(status().isOk()); + assertEquals("Your courier is on the way|Your courier is here|Your trip has started" + + "|You've arrived", titles("rkv-customer-phone")); + // Without a Live Activity there is nothing to move on. + for (RecordingPushSender.Sent each : sent.sentTo("rkv-customer-phone")) { + assertNull(surface(each)); + assertEquals(id, data(each).get("deliveryId")); + } + assertEquals("New delivery request", titles("rkv-courier-phone")); + } + + @Test + void cancellingTellsTheOtherSide() throws Exception { + // Ljubljana: the courier gives up on the way to the pickup. + String customer = People.customer(accounts, "push-lju-customer"); + String courier = People.courier(accounts, "push-lju-courier"); + register(customer, "fcm", "lju-customer-phone"); + register(courier, "fcm", "lju-courier-phone"); + Trips.online(mvc, courier, 46.0569, 14.5058); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(46.0570, 14.5054, 46.0670, 14.5174)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/cancel"), courier)) + .andExpect(status().isOk()); + assertEquals("Your courier is on the way|Your courier canceled", titles("lju-customer-phone")); + + + // Bratislava: the customer changes their mind while the offer is out. + String bratislavaCustomer = People.customer(accounts, "push-bts-customer"); + String bratislavaCourier = People.courier(accounts, "push-bts-courier"); + register(bratislavaCourier, "fcm", "bts-courier-phone"); + Trips.online(mvc, bratislavaCourier, 48.1486, 17.1077); + String offered = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(48.1487, 17.1076, 48.1601, 17.0909)), bratislavaCustomer)).get("id"); + mvc.perform(People.asCustomer(post("/api/deliveries/" + offered + "/cancel"), bratislavaCustomer)) + .andExpect(status().isOk()); + assertEquals("New delivery request|Delivery canceled", titles("bts-courier-phone")); + } + + @Test + void theLiveActivityFollowsTheCarAndEndsWithTheDelivery() throws Exception { + String customer = People.customer(accounts, "push-cph-customer"); + String courier = People.courier(accounts, "push-cph-courier"); + String other = People.customer(accounts, "push-cph-other"); + register(customer, "apns", "cph-customer-phone"); + Trips.online(mvc, courier, 55.6761, 12.5683); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(55.6762, 12.5680, 55.6900, 12.5900)), customer)).get("id"); + // No activity before there is a courier on the way, and none on + // somebody else's delivery. + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-1\"}"), customer)).andExpect(status().isConflict()); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-1\"}"), other)).andExpect(status().isNotFound()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-1\"}"), customer)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.activityId").value("act-1")); + + // The car moves. The phone has just drawn the activity, so the first + // update waits its turn... + int before = sent.sentTo("cph-customer-phone").size(); + Trips.online(mvc, courier, 55.6765, 12.5682); + assertEquals(before, sent.sentTo("cph-customer-phone").size()); + // ...and once it is due, one report sends one, silently. + db.execute("UPDATE delivery_activity SET sent_at = 0 WHERE delivery_id = ?", new Object[] {id}); + Trips.online(mvc, courier, 55.6764, 12.5681); + Trips.online(mvc, courier, 55.6763, 12.5681); + List toCustomer = sent.sentTo("cph-customer-phone"); + assertEquals(before + 1, toCustomer.size()); + RecordingPushSender.Sent update = last(toCustomer); + assertEquals(Boolean.TRUE, update.message().get("silent")); + Map command = surface(update); + assertEquals("live-update", command.get("operation")); + assertEquals("act-1", command.get("id")); + Map state = Json.parseObject((String) command.get("state")); + assertEquals("On the way", state.get("status")); + assertEquals("push-cph-courier", state.get("courier")); + assertEquals("Green Fiat 500", state.get("car")); + assertEquals("T push-cph-courier", state.get("plate")); + assertEquals(0.25d, ((Number) state.get("progress")).doubleValue(), 0.001d); + assertTrue(((String) state.get("eta")).endsWith(" min"), state.toString()); + + // A change of state moves it on at once, with the notification. + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(status().isOk()); + command = surface(last(sent.sentTo("cph-customer-phone"))); + assertEquals("live-update", command.get("operation")); + assertEquals(0.5d, progress(command), 0.001d); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/start"), courier)) + .andExpect(status().isOk()); + assertEquals(0.75d, progress(surface(last(sent.sentTo("cph-customer-phone")))), 0.001d); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/complete"), courier)) + .andExpect(status().isOk()); + RecordingPushSender.Sent done = last(sent.sentTo("cph-customer-phone")); + assertEquals("You've arrived", done.message().get("title")); + assertEquals("live-end", surface(done).get("operation")); + assertEquals("act-1", surface(done).get("id")); + // And it is forgotten: the car goes on reporting, the customer hears no more. + int after = sent.sentTo("cph-customer-phone").size(); + Trips.online(mvc, courier, 55.6800, 12.5700); + assertEquals(after, sent.sentTo("cph-customer-phone").size()); + } + + @Test + void theSettingsDecideWhatIsSaidButNotWhatIsShown() throws Exception { + // Sofia: a customer who turned delivery updates off hears nothing of the + // courier, while the lock screen still lets go of the cancelled delivery. + String customer = People.customer(accounts, "push-sof-customer"); + String courier = People.courier(accounts, "push-sof-courier"); + String quiet = "{\"language\":\"en\",\"theme\":\"system\",\"units\":\"km\"," + + "\"notifyDeliveryUpdates\":false,\"notifyReceipts\":true," + + "\"notifyPromotions\":false,\"courierGender\":\"\",\"quietDelivery\":false," + + "\"accessibleVehicle\":false,\"petFriendly\":false," + + "\"defaultPaymentMethodId\":\"cash\",\"defaultTipPercent\":0," + + "\"shareTripsWith\":\"\"}"; + mvc.perform(People.asCustomer(body(put("/api/account/preferences"), quiet), customer)) + .andExpect(status().isOk()); + register(customer, "apns", "sof-customer-phone"); + Trips.online(mvc, courier, 42.6977, 23.3219); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(42.6978, 23.3220, 42.7054, 23.3112)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-sof\"}"), customer)).andExpect(status().isOk()); + assertEquals("", titles("sof-customer-phone")); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/cancel"), courier)) + .andExpect(status().isOk()); + List toCustomer = sent.sentTo("sof-customer-phone"); + assertEquals(1, toCustomer.size()); + assertEquals(Boolean.TRUE, toCustomer.get(0).message().get("silent")); + assertEquals("live-end", surface(toCustomer.get(0)).get("operation")); + } + + @Test + void aPhoneBelongsToWhoeverSignedInOnItLast() throws Exception { + String first = People.customer(accounts, "push-ams-first"); + String second = People.customer(accounts, "push-ams-second"); + String courier = People.courier(accounts, "push-ams-courier"); + register(first, "fcm", "ams-shared-phone"); + register(second, "fcm", "ams-shared-phone"); + register(first, "fcm", "ams-first-tablet"); + Trips.online(mvc, courier, 52.3676, 4.9041); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(52.3677, 4.9040, 52.3800, 4.9100)), first)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + assertEquals("", titles("ams-shared-phone")); + assertEquals("Your courier is on the way", titles("ams-first-tablet")); + + // Signing out forgets the device, and only one's own. + mvc.perform(People.asCustomer(body(post("/api/account/push/remove"), + device("fcm", "ams-shared-phone")), first)).andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/account/push/remove"), + device("fcm", "ams-first-tablet")), first)).andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(status().isOk()); + assertEquals("Your courier is on the way", titles("ams-first-tablet")); + + // Closing the account forgets every device it had. + String customer = People.customer(accounts, "push-ams-closing"); + register(customer, "apns", "ams-closing-phone"); + accounts.close(customer, People.PASSWORD); + assertEquals(0L, devices(customer)); + assertEquals(1L, devices(second)); + } + + @Test + void anActivityIsRefusedOnceTheDeliveryHasEnded() throws Exception { + // Helsinki: the delivery is completed. Oslo: the courier cancels it. + String customer = People.customer(accounts, "push-hel-customer"); + String courier = People.courier(accounts, "push-hel-courier"); + Trips.online(mvc, courier, 60.1699, 24.9384); + String done = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(60.1700, 24.9383, 60.1800, 24.9500)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + done + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + done + "/arrived"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + done + "/start"), courier)) + .andExpect(status().isOk()); + // Still on the road: the activity is taken. + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + done + "/activity"), + "{\"activityId\":\"act-hel\"}"), customer)).andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + done + "/complete"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + done + "/activity"), + "{\"activityId\":\"act-hel-2\"}"), customer)).andExpect(status().isConflict()); + + String oslo = People.customer(accounts, "push-osl-customer"); + String oslocourier = People.courier(accounts, "push-osl-courier"); + Trips.online(mvc, oslocourier, 59.9139, 10.7522); + String canceled = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(59.9140, 10.7521, 59.9250, 10.7600)), oslo)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + canceled + "/accept"), + oslocourier)).andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + canceled + "/cancel"), + oslocourier)).andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + canceled + "/activity"), + "{\"activityId\":\"act-osl\"}"), oslo)).andExpect(status().isConflict()); + } + + /// An end the push provider refused is not lost with the activity: the + /// delivery's transition is never retried, so the sweep sends it again. + @Test + void anEndTheProviderRefusedIsSentAgain() throws Exception { + String customer = People.customer(accounts, "push-rey-customer"); + String courier = People.courier(accounts, "push-rey-courier"); + register(customer, "apns", "rey-customer-phone"); + Trips.online(mvc, courier, 64.1466, -21.9426); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(64.1467, -21.9425, 64.1500, -21.9000)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-rey\"}"), customer)).andExpect(status().isOk()); + + sent.refuse(1); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/cancel"), courier)) + .andExpect(status().isOk()); + assertEquals(1L, activities(id), "the refused end keeps its activity"); + int before = sent.sentTo("rey-customer-phone").size(); + + // Not while the call that ended the delivery may still be sending it... + pushes.endLeftBehind(); + assertEquals(before, sent.sentTo("rey-customer-phone").size()); + // ...and once that is past, once. + db.execute("UPDATE delivery_activity SET ended_at = ? WHERE delivery_id = ?", + new Object[] {Long.valueOf(System.currentTimeMillis() - 120000L), id}); + pushes.endLeftBehind(); + List toCustomer = sent.sentTo("rey-customer-phone"); + assertEquals(before + 1, toCustomer.size()); + Map command = surface(last(toCustomer)); + assertEquals("live-end", command.get("operation")); + assertEquals("act-rey", command.get("id")); + assertEquals(0L, activities(id)); + pushes.endLeftBehind(); + assertEquals(before + 1, sent.sentTo("rey-customer-phone").size()); + } + + /// A send that returned is not a send the service took: the real sender + /// hands the message to another thread and learns later, and only then is + /// the activity forgotten. + @Test + void anEndTheServiceNeverConfirmedIsKept() throws Exception { + String customer = People.customer(accounts, "push-tll-customer"); + String courier = People.courier(accounts, "push-tll-courier"); + register(customer, "apns", "tll-customer-phone"); + Trips.online(mvc, courier, 59.4370, 24.7536); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(59.4371, 24.7535, 59.4400, 24.7700)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-tll\"}"), customer)).andExpect(status().isOk()); + + sent.leaveUnconfirmed(1); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)) + .andExpect(status().isOk()); + assertEquals("live-end", surface(last(sent.sentTo("tll-customer-phone"))).get("operation")); + assertEquals(1L, activities(id), "sent, but never confirmed"); + + db.execute("UPDATE delivery_activity SET ended_at = ? WHERE delivery_id = ?", + new Object[] {Long.valueOf(System.currentTimeMillis() - 120000L), id}); + pushes.endLeftBehind(); + assertEquals(0L, activities(id)); + } + + /// An end still undelivered when iOS would have ended the activity itself + /// is dropped, not sent. + @Test + void anEndTooOldToBeOnScreenIsDropped() throws Exception { + db.execute("INSERT INTO delivery_activity (delivery_id, username, activity_id, sent_at, ended_at) " + + "VALUES ('old-delivery', 'push-old-customer', 'act-old', 0, 1)", new Object[0]); + int before = sent.all().size(); + pushes.endLeftBehind(); + assertEquals(0L, activities("old-delivery")); + assertEquals(before, sent.all().size()); + } + + @Test + void aTokenTakenFromAnotherAccountStillRespectsTheCap() throws Exception { + String full = People.customer(accounts, "push-cap-full"); + String other = People.customer(accounts, "push-cap-other"); + for (int i = 0; i < 8; i++) { + register(full, "fcm", "cap-full-" + i); + } + assertEquals(8L, devices(full)); + // Make one device clearly the one heard from longest ago. + db.execute("UPDATE push_device SET updated_at = 1 WHERE token = ?", + new Object[] {"cap-full-3"}); + register(other, "fcm", "cap-moving-phone"); + register(full, "fcm", "cap-moving-phone"); + assertEquals(8L, devices(full)); + assertEquals(0L, devices(other)); + assertEquals(0L, tokens("cap-full-3")); + assertEquals(1L, tokens("cap-moving-phone")); + } + + /// A registration that reaches the database after the account was closed + /// is refused, and leaves no device behind: the profile it holds first is + /// gone. This is the order a registration authorized just before the + /// closure and run just after it would see. + @Test + void aClosedAccountRegistersNoDevice() throws Exception { + String customer = People.customer(accounts, "push-gone-customer"); + accounts.close(customer, People.PASSWORD); + try { + pushDevices.register(customer, new PushDeviceDto("fcm", "gone-customer-phone", "and")); + fail("a device was registered for a closed account"); + } catch (ResponseStatusException refused) { + assertEquals(404, refused.getStatus()); + } + assertEquals(0L, tokens("gone-customer-phone")); + assertEquals(0L, devices(customer)); + } + + /// Registrations one after another for a full account each make their + /// own room, oldest first. Taken together, the registrations wait for one + /// another behind the account's profile row, so this is also what two of + /// them sent at once come to. + @Test + void eachNewDeviceOfAFullAccountReplacesTheOldest() throws Exception { + String full = People.customer(accounts, "push-cap2-full"); + for (int i = 0; i < 8; i++) { + pushDevices.register(full, new PushDeviceDto("fcm", "cap2-full-" + i, "and")); + } + db.execute("UPDATE push_device SET updated_at = 1 WHERE token = ?", + new Object[] {"cap2-full-5"}); + db.execute("UPDATE push_device SET updated_at = 2 WHERE token = ?", + new Object[] {"cap2-full-2"}); + pushDevices.register(full, new PushDeviceDto("fcm", "cap2-new-a", "and")); + pushDevices.register(full, new PushDeviceDto("apns", "cap2-new-b", "ios")); + assertEquals(8L, devices(full)); + assertEquals(0L, tokens("cap2-full-5")); + assertEquals(0L, tokens("cap2-full-2")); + assertEquals(1L, tokens("cap2-new-a")); + assertEquals(1L, tokens("cap2-new-b")); + } + + /// The notification of a change that is no longer the delivery's latest says + /// nothing, in either order: composed after the delivery was cancelled it does + /// not tell the customer a courier is coming, and composed before the + /// cancellation's own it leaves the Live Activity for that one to end. + @Test + void aStaleChangeSaysNothingAndLeavesTheActivityToTheLaterOne() throws Exception { + // Valletta: the acceptance is composed after the cancellation was. + String customer = People.customer(accounts, "push-mla-customer"); + String courier = People.courier(accounts, "push-mla-courier"); + register(customer, "apns", "mla-customer-phone"); + Trips.online(mvc, courier, 35.8989, 14.5146); + String id = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(35.8990, 14.5145, 35.9100, 14.5000)), customer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + id + "/activity"), + "{\"activityId\":\"act-mla\"}"), customer)).andExpect(status().isOk()); + mvc.perform(People.asCustomer(post("/api/deliveries/" + id + "/cancel"), customer)) + .andExpect(status().isOk()); + int told = sent.sentTo("mla-customer-phone").size(); + assertEquals("live-end", surface(last(sent.sentTo("mla-customer-phone"))) + .get("operation")); + pushes.deliveryChanged(id, DeliveryState.ACCEPTED, customer, courier); + assertEquals(told, sent.sentTo("mla-customer-phone").size()); + + // Gdansk: the acceptance is composed after the delivery was cancelled but + // before the cancellation is. It neither speaks nor forgets the + // activity, and the cancellation then ends it. + String gdnCustomer = People.customer(accounts, "push-gdn-customer"); + String gdnCourier = People.courier(accounts, "push-gdn-courier"); + register(gdnCustomer, "apns", "gdn-customer-phone"); + Trips.online(mvc, gdnCourier, 54.3520, 18.6466); + String gdn = (String) Trips.json(mvc, People.asCustomer(body(post("/api/deliveries"), + People.delivery(54.3521, 18.6465, 54.3650, 18.6300)), gdnCustomer)).get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + gdn + "/accept"), gdnCourier)) + .andExpect(status().isOk()); + mvc.perform(People.asCustomer(body(post("/api/deliveries/" + gdn + "/activity"), + "{\"activityId\":\"act-gdn\"}"), gdnCustomer)).andExpect(status().isOk()); + int before = sent.sentTo("gdn-customer-phone").size(); + db.execute("UPDATE delivery SET state = 'CANCELLED_BY_CUSTOMER' WHERE id = ?", + new Object[] {gdn}); + pushes.deliveryChanged(gdn, DeliveryState.ACCEPTED, gdnCustomer, gdnCourier); + assertEquals(before, sent.sentTo("gdn-customer-phone").size()); + assertEquals(1L, activities(gdn)); + pushes.deliveryChanged(gdn, DeliveryState.CANCELLED_BY_CUSTOMER, gdnCustomer, gdnCourier); + RecordingPushSender.Sent ended = last(sent.sentTo("gdn-customer-phone")); + assertEquals("live-end", surface(ended).get("operation")); + assertEquals("act-gdn", surface(ended).get("id")); + assertEquals(0L, activities(gdn)); + } + + private long activities(String deliveryId) throws Exception { + Map row = db.queryOne("SELECT COUNT(*) AS n FROM delivery_activity WHERE delivery_id = ?", + new Object[] {deliveryId}); + return ((Number) row.get("n")).longValue(); + } + + private long tokens(String token) throws Exception { + Map row = db.queryOne("SELECT COUNT(*) AS n FROM push_device WHERE token = ?", + new Object[] {token}); + return ((Number) row.get("n")).longValue(); + } + + private long devices(String username) throws Exception { + Map row = db.queryOne("SELECT COUNT(*) AS n FROM push_device WHERE username = ?", + new Object[] {username}); + return ((Number) row.get("n")).longValue(); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ScriptedPayments.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ScriptedPayments.java new file mode 100644 index 00000000000..fb73a1fbf88 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/ScriptedPayments.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.pay.SimulatedPayments; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/// The simulated processor with a test's hand on it: the test says whether +/// refunds and card detaches fail, and what happens while a refund is at the +/// processor -- which is how two requests that overlap are lined up one inside +/// the other, in the order a test wants, on one thread. +final class ScriptedPayments extends SimulatedPayments { + /// Run inside the next refund, before it answers, and then forgotten. + Runnable duringRefund; + /// How the coming refunds answer, in the order they answer -- a refund + /// run inside another answers before it. True fails one without saying + /// whether it was made; once the list is empty every refund succeeds. + final List refundFailures = new ArrayList(); + /// Whether detaching a card fails the same way. + boolean detachFails; + /// The tokens of the cards detached, in order. + final List detached = new ArrayList(); + + /// Back to a processor that does what the simulated one does. + void reset() { + duringRefund = null; + refundFailures.clear(); + detachFails = false; + detached.clear(); + } + + @Override + public Refund refund(String chargeReference, long amountCents) throws IOException { + Runnable during = duringRefund; + duringRefund = null; + if (during != null) { + during.run(); + } + if (!refundFailures.isEmpty() && refundFailures.remove(0).booleanValue()) { + throw new IOException("The scripted processor did not answer"); + } + return super.refund(chargeReference, amountCents); + } + + @Override + public void detachCard(String customer, String token) throws IOException { + if (detachFails) { + throw new IOException("The scripted processor did not answer"); + } + detached.add(token); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SettingsTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SettingsTest.java new file mode 100644 index 00000000000..c2b81639f91 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SettingsTest.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcRequestBuilders.put; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/// What someone changes about their own account: how the app behaves for them, +/// who they are, their password, and whether they have an account at all. +@BackendTest +class SettingsTest { + @Autowired + private MockMvc mvc; + @Autowired + private Accounts accounts; + + @Test + void preferencesAreSavedAndReadBack() throws Exception { + String who = People.customer(accounts, "prefers"); + mvc.perform(People.asCustomer(get("/api/account/preferences"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.language").value("en")) + .andExpect(jsonPath("$.theme").value("system")) + .andExpect(jsonPath("$.units").value("km")) + .andExpect(jsonPath("$.notifyDeliveryUpdates").value(true)) + .andExpect(jsonPath("$.courierGender").value("any")) + .andExpect(jsonPath("$.defaultPaymentMethodId").value("cash")); + + String card = (String) Trips.addCard(mvc, who, Trips.VISA).get("id"); + String wanted = "{\"language\":\"he\",\"theme\":\"dark\",\"units\":\"mi\"," + + "\"notifyDeliveryUpdates\":false,\"notifyReceipts\":true,\"notifyPromotions\":true," + + "\"courierGender\":\"women\",\"quietDelivery\":true,\"accessibleVehicle\":true," + + "\"petFriendly\":true,\"defaultPaymentMethodId\":\"cash\"," + + "\"defaultTipPercent\":15,\"shareTripsWith\":\"+1 (555) 010-2222\"}"; + mvc.perform(People.asCustomer(Trips.body(put("/api/account/preferences"), wanted), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.language").value("he")); + Map saved = Trips.json(mvc, People.asCustomer(get("/api/account/preferences"), who)); + assertEquals("he", saved.get("language")); + assertEquals("dark", saved.get("theme")); + assertEquals("mi", saved.get("units")); + assertEquals(Boolean.FALSE, saved.get("notifyDeliveryUpdates")); + assertEquals(Boolean.TRUE, saved.get("notifyReceipts")); + assertEquals(Boolean.TRUE, saved.get("notifyPromotions")); + assertEquals("women", saved.get("courierGender")); + assertEquals(Boolean.TRUE, saved.get("quietDelivery")); + assertEquals(Boolean.TRUE, saved.get("accessibleVehicle")); + assertEquals(Boolean.TRUE, saved.get("petFriendly")); + assertEquals(15L, Trips.number(saved, "defaultTipPercent")); + // Stored in the one form a phone number has. + assertEquals("+15550102222", saved.get("shareTripsWith")); + // The default way to pay is the mark on the card itself: saving it + // here and reading the cards agree. + assertEquals("cash", saved.get("defaultPaymentMethodId")); + mvc.perform(People.asCustomer(get("/api/payments/methods"), who)) + .andExpect(jsonPath("$[0].id").value(card)) + .andExpect(jsonPath("$[0].isDefault").value(false)) + .andExpect(jsonPath("$[1].isDefault").value(true)); + mvc.perform(People.asCustomer(get("/api/me"), who)) + .andExpect(jsonPath("$.language").value("he")); + + // Each is one of the values it can take, or it is refused whole. + String[] wrong = {"{\"language\":\"xx\"}", "{\"theme\":\"neon\"}", "{\"units\":\"ft\"}", + "{\"courierGender\":\"men\"}", "{\"defaultTipPercent\":12}", + "{\"shareTripsWith\":\"not a number\"}", + "{\"defaultPaymentMethodId\":\"someoneelsescard\"}"}; + for (int iter = 0; iter < wrong.length; iter++) { + int answered = Trips.statusOf(mvc, People.asCustomer(Trips.body( + put("/api/account/preferences"), wrong[iter]), who)); + assertEquals(iter == wrong.length - 1 ? 404 : 400, answered, wrong[iter]); + } + mvc.perform(People.asCustomer(get("/api/account/preferences"), who)) + .andExpect(jsonPath("$.language").value("he")) + .andExpect(jsonPath("$.theme").value("dark")); + } + + @Test + void aProfileIsSavedAndReadBack() throws Exception { + String who = People.customer(accounts, "profiled"); + mvc.perform(People.asCustomer(get("/api/account/profile"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("profiled")) + .andExpect(jsonPath("$.gender").value("unspecified")); + mvc.perform(People.asCustomer(Trips.body(put("/api/account/profile"), + "{\"name\":\"Pat Profiled\",\"gender\":\"nonbinary\"," + + "\"emergencyContactName\":\"Sam\"," + + "\"emergencyContactPhone\":\"+1 555 010 3333\"," + + "\"homeAddress\":\"1 Home St\",\"workAddress\":\"2 Work Ave\"}"), + who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayName").value("Pat Profiled")) + .andExpect(jsonPath("$.gender").value("nonbinary")); + mvc.perform(People.asCustomer(get("/api/account/profile"), who)) + .andExpect(jsonPath("$.name").value("Pat Profiled")) + .andExpect(jsonPath("$.emergencyContactName").value("Sam")) + .andExpect(jsonPath("$.emergencyContactPhone").value("+15550103333")) + .andExpect(jsonPath("$.homeAddress").value("1 Home St")) + .andExpect(jsonPath("$.workAddress").value("2 Work Ave")); + mvc.perform(People.asCustomer(Trips.body(put("/api/account/profile"), + "{\"name\":\"\",\"gender\":\"female\"}"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(put("/api/account/profile"), + "{\"name\":\"Pat\",\"gender\":\"other\"}"), who)) + .andExpect(status().isBadRequest()); + } + + @Test + void aPasswordIsChangedByWhoeverKnowsTheOldOne() throws Exception { + String who = People.customer(accounts, "rekeyed"); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/password"), + "{\"current\":\"not-the-password\",\"replacement\":\"another-long-one\"}"), + who)) + .andExpect(status().isForbidden()); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/password"), + "{\"current\":\"" + People.PASSWORD + "\",\"replacement\":\"short\"}"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/password"), + "{\"current\":\"" + People.PASSWORD + + "\",\"replacement\":\"another-long-one\"}"), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value(who)); + assertEquals(null, SignIn.session(mvc, who, People.PASSWORD)); + assertEquals(false, SignIn.session(mvc, who, "another-long-one") == null); + } + + @Test + void aChangedPasswordSignsTheOtherDevicesOut() throws Exception { + String who = People.customer(accounts, "two-devices"); + Map phone = SignIn.tokens(mvc, who, People.PASSWORD); + Map tablet = SignIn.tokens(mvc, who, People.PASSWORD); + + mvc.perform(Trips.body(post("/api/account/password"), + "{\"current\":\"" + People.PASSWORD + + "\",\"replacement\":\"another-long-one\"}") + .header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + phone.get("access_token"))) + .andExpect(status().isOk()); + + // The tablet's refresh token asked for no password, and would have + // gone on working for whoever held the tablet. + mvc.perform(SignIn.formPost("/oauth2/token", "grant_type", "refresh_token", + "refresh_token", (String) tablet.get("refresh_token"), + "client_id", SecurityConfig.CLIENT_ID).header("Host", SignIn.HOST)) + .andExpect(status().isBadRequest()); + // The device the password was changed on stays signed in. + mvc.perform(SignIn.formPost("/oauth2/token", "grant_type", "refresh_token", + "refresh_token", (String) phone.get("refresh_token"), + "client_id", SecurityConfig.CLIENT_ID).header("Host", SignIn.HOST)) + .andExpect(status().isOk()); + } + + @Test + void anAccountIsClosedForGood() throws Exception { + // Warsaw, where nobody drives. + String who = People.customer(accounts, "leaving"); + String token = SignIn.token(mvc, who, People.PASSWORD); + Trips.addCard(mvc, who, Trips.VISA); + mvc.perform(People.asCustomer(Trips.body(put("/api/onboarding/application"), + "{\"legalName\":\"Lee Leaving\"}"), who)).andExpect(status().isOk()); + + // Not with a delivery under way: somebody may be onboarding to it. + String delivery = (String) Trips.json(mvc, People.asCustomer(Trips.body(post("/api/deliveries"), + People.delivery(52.2297, 21.0122, 52.2400, 21.0300)), who)).get("id"); + String closure = "{\"password\":\"" + People.PASSWORD + "\"}"; + mvc.perform(People.asCustomer(Trips.body(post("/api/account/delete"), closure), who)) + .andExpect(status().isConflict()); + mvc.perform(People.asCustomer(post("/api/deliveries/" + delivery + "/cancel"), who)) + .andExpect(status().isOk()); + + // Not on the strength of a token: the password is asked for again. + mvc.perform(People.asCustomer(post("/api/account/delete"), who)) + .andExpect(status().isBadRequest()); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/delete"), "{}"), who)) + .andExpect(status().isForbidden()); + mvc.perform(People.asCustomer(Trips.body(post("/api/account/delete"), + "{\"password\":\"not-the-password\"}"), who)) + .andExpect(status().isForbidden()); + mvc.perform(People.asCustomer(get("/api/me"), who)).andExpect(status().isOk()); + + mvc.perform(People.asCustomer(Trips.body(post("/api/account/delete"), closure), who)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value(who)); + // Gone: the token, the password, and the account an admin would look up. + mvc.perform(get("/api/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isUnauthorized()); + assertEquals(null, SignIn.session(mvc, who, People.PASSWORD)); + mvc.perform(People.asAdmin(get("/api/admin/users/" + who), DemoAccounts.ADMIN)) + .andExpect(status().isNotFound()); + mvc.perform(People.asAdmin(get("/api/admin/applications/" + who), DemoAccounts.ADMIN)) + .andExpect(status().isNotFound()); + // And the address is retired with it. The deliveries the account took are + // kept under it, so whoever registered it next would be handed them. + mvc.perform(post("/api/account/register").contentType("application/json") + .content("{\"email\":\"" + who + "\",\"password\":\"" + People.PASSWORD + + "\",\"displayName\":\"Somebody else\"," + + "\"phone\":\"+15550112\"}")) + .andExpect(status().isConflict()); + + // An admin's account is not closed this way. + mvc.perform(People.asAdmin(Trips.body(post("/api/account/delete"), + "{\"password\":\"" + DemoAccounts.PASSWORD + "\"}"), DemoAccounts.ADMIN)) + .andExpect(status().isConflict()); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SignIn.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SignIn.java new file mode 100644 index 00000000000..2c5f5f4389f --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/SignIn.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Json; +import com.codename1.backend.security.oauth2.core.OAuth2Parameters; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; + +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; + +/// Signs a user in the way the app does, for the tests that need a real token: +/// the password for a session, the session for an authorization code, the code +/// for tokens. +final class SignIn { + /// The PKCE pair of RFC 7636's appendix B. + private static final String VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + private static final String CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"; + static final String HOST = "morsel.test:8080"; + + private SignIn() { + } + + static MockRequestBuilder formPost(String path, String... pairs) { + StringBuilder body = new StringBuilder(); + for (int i = 0; i < pairs.length; i += 2) { + if (body.length() > 0) { + body.append('&'); + } + body.append(OAuth2Parameters.encode(pairs[i])).append('=') + .append(OAuth2Parameters.encode(pairs[i + 1])); + } + return post(path).contentType("application/x-www-form-urlencoded").content(body.toString()); + } + + /// The session cookie a correct password earns, or null for a wrong one. + static String session(MockMvc mvc, String username, String password) throws Exception { + return mvc.perform(formPost("/login", "username", username, "password", password) + .header("Host", HOST)) + .andReturn().getResponse().getCookie("CN1SESSION"); + } + + /// The token response for a user: `access_token`, `refresh_token` and the + /// rest. + static Map tokens(MockMvc mvc, String username, String password) throws Exception { + String session = session(mvc, username, password); + if (session == null) { + throw new IllegalStateException("could not sign in as " + username); + } + String location = mvc.perform(get("/oauth2/authorize?response_type=code&client_id=" + + SecurityConfig.CLIENT_ID + "&redirect_uri=" + + OAuth2Parameters.encode(SecurityConfig.LOOPBACK_REDIRECT) + + "&scope=openid%20profile&state=s-1&nonce=n-1&code_challenge=" + CHALLENGE + + "&code_challenge_method=S256") + .header("Host", HOST).cookie("CN1SESSION", session)) + .andReturn().getResponse().getRedirectedUrl(); + if (location == null || location.indexOf("code=") < 0) { + throw new IllegalStateException("no authorization code: " + location); + } + int at = location.indexOf("code=") + 5; + int end = location.indexOf('&', at); + String code = location.substring(at, end < 0 ? location.length() : end); + String body = mvc.perform(formPost("/oauth2/token", + "grant_type", "authorization_code", + "code", code, + "redirect_uri", SecurityConfig.LOOPBACK_REDIRECT, + "code_verifier", VERIFIER, + "client_id", SecurityConfig.CLIENT_ID).header("Host", HOST)) + .andReturn().getResponse().getContentAsString(); + return Json.parseObject(body); + } + + /// A bearer token for a user. + static String token(MockMvc mvc, String username, String password) throws Exception { + Object token = tokens(mvc, username, password).get("access_token"); + if (!(token instanceof String)) { + throw new IllegalStateException("no access token for " + username); + } + return (String) token; + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/StripePaymentsTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/StripePaymentsTest.java new file mode 100644 index 00000000000..225b3d5fb76 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/StripePaymentsTest.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.pay.PaymentDeclinedException; +import com.codenameone.examples.morsel.pay.PaymentProvider; +import com.codenameone.examples.morsel.pay.StripePayments; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/// The Stripe provider, held to what it sends and what it makes of the +/// answers. The network is replaced by a script of canned answers, so this +/// needs no key and reaches nobody: what it proves is the requests' shape and +/// the reading of Stripe's JSON, which is all of the provider there is. +class StripePaymentsTest { + private static final String RETURN = "https://morsel.example/pay/return"; + + /// Answers each request with the next canned reply and keeps what was + /// asked. + private static final class Script implements StripePayments.Transport { + final List asked = new ArrayList(); + final List forms = new ArrayList(); + final List keys = new ArrayList(); + private final List replies = new ArrayList(); + + Script reply(int status, String body) { + replies.add(new StripePayments.Reply(status, body)); + return this; + } + + @Override + public StripePayments.Reply send(String method, String path, String form, + String idempotencyKey) throws IOException { + asked.add(method + " " + path); + forms.add(form); + keys.add(idempotencyKey); + if (replies.isEmpty()) { + throw new IOException("Nothing more was expected to be asked"); + } + return replies.remove(0); + } + } + + private static void has(String form, String pair) { + assertTrue(("&" + form + "&").indexOf("&" + pair + "&") >= 0, pair + " in " + form); + } + + @Test + void aCustomerIsMadeWithTheAccountsName() throws Exception { + Script script = new Script().reply(200, "{\"id\":\"cus_123\",\"object\":\"customer\"}"); + StripePayments stripe = new StripePayments(script, RETURN); + assertEquals("stripe", stripe.name()); + assertEquals("cus_123", stripe.createCustomer("ada@morsel.example", "Ada & Co")); + assertEquals("POST /v1/customers", script.asked.get(0)); + String form = script.forms.get(0); + // Escaped, name and value both: an ampersand in a name must not start + // another field. + has(form, "email=ada%40morsel.example"); + has(form, "name=Ada+%26+Co"); + has(form, "metadata%5Bmorsel_user%5D=ada%40morsel.example"); + // The same request made twice makes one customer: the same key, which + // does not spell the address out. + String key = script.keys.get(0); + assertTrue(key.startsWith("customer-") && key.indexOf("ada") < 0, key); + script.reply(200, "{\"id\":\"cus_123\",\"object\":\"customer\"}"); + stripe.createCustomer("ada@morsel.example", "Ada & Co"); + assertEquals(key, script.keys.get(1)); + script.reply(200, "{\"id\":\"cus_124\",\"object\":\"customer\"}"); + stripe.createCustomer("ada@morsel.example", "Ada"); + assertTrue(!key.equals(script.keys.get(2)), "another name is another request"); + } + + @Test + void aCustomerIsDeletedAndOneAlreadyGoneCountsAsDeleted() throws Exception { + Script script = new Script().reply(200, "{\"id\":\"cus_123\",\"deleted\":true}") + .reply(404, "{\"error\":{\"type\":\"invalid_request_error\"," + + "\"code\":\"resource_missing\"}}") + .reply(500, "{}"); + StripePayments stripe = new StripePayments(script, RETURN); + stripe.forgetCustomer("cus_123"); + assertEquals("DELETE /v1/customers/cus_123", script.asked.get(0)); + assertEquals(null, script.forms.get(0)); + // Asked again after an answer that was lost. + stripe.forgetCustomer("cus_123"); + // Anything else is not a deletion, and the closure it is part of fails. + try { + stripe.forgetCustomer("cus_123"); + throw new AssertionError("A processor that failed must not read as deleted"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("500") >= 0, expected.getMessage()); + } + } + + @Test + void aRemovedCardIsDetachedAndOneAlreadyDetachedCountsAsDetached() throws Exception { + Script script = new Script() + .reply(200, "{\"id\":\"pm_1\",\"object\":\"payment_method\",\"customer\":null}") + // Gone with its customer. + .reply(404, "{\"error\":{\"type\":\"invalid_request_error\"," + + "\"code\":\"resource_missing\"}}") + // Asked again after an answer that was lost: Stripe refuses to + // detach a card attached to nobody, and the card says so. + .reply(400, "{\"error\":{\"type\":\"invalid_request_error\"}}") + .reply(200, "{\"id\":\"pm_1\",\"object\":\"payment_method\",\"customer\":null}") + // Refused while the card is still this customer's: not detached. + .reply(400, "{\"error\":{\"type\":\"invalid_request_error\"}}") + .reply(200, "{\"id\":\"pm_1\",\"object\":\"payment_method\"," + + "\"customer\":\"cus_123\"}") + .reply(500, "{}") + .reply(500, "{}"); + StripePayments stripe = new StripePayments(script, RETURN); + stripe.detachCard("cus_123", "pm_1"); + assertEquals("POST /v1/payment_methods/pm_1/detach", script.asked.get(0)); + stripe.detachCard("cus_123", "pm_1"); + stripe.detachCard("cus_123", "pm_1"); + assertEquals("GET /v1/payment_methods/pm_1", script.asked.get(3)); + for (int iter = 0; iter < 2; iter++) { + try { + stripe.detachCard("cus_123", "pm_1"); + throw new AssertionError("A card still attached must not read as detached"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("detaching") >= 0, + expected.getMessage()); + } + } + } + + @Test + void aCardIsAddedOnAHostedPageAndReadBack() throws Exception { + Script script = new Script() + .reply(200, "{\"id\":\"cs_test_1\",\"object\":\"checkout.session\"," + + "\"url\":\"https://checkout.stripe.com/c/pay/cs_test_1\"}") + .reply(200, "{\"id\":\"cs_test_1\",\"status\":\"complete\"," + + "\"customer\":\"cus_123\",\"setup_intent\":{\"id\":\"seti_1\"," + + "\"status\":\"succeeded\",\"payment_method\":{\"id\":\"pm_1\"," + + "\"type\":\"card\",\"card\":{\"brand\":\"visa\",\"last4\":\"4242\"," + + "\"exp_month\":8,\"exp_year\":2031}}}}"); + StripePayments stripe = new StripePayments(script, RETURN); + PaymentProvider.Setup setup = stripe.startCardSetup("cus_123", "setup9"); + assertTrue(setup.hosted); + assertEquals("cs_test_1", setup.reference); + assertEquals("https://checkout.stripe.com/c/pay/cs_test_1", setup.url); + assertEquals("POST /v1/checkout/sessions", script.asked.get(0)); + String form = script.forms.get(0); + has(form, "mode=setup"); + has(form, "customer=cus_123"); + has(form, "payment_method_types%5B0%5D=card"); + has(form, "client_reference_id=setup9"); + has(form, "success_url=https%3A%2F%2Fmorsel.example%2Fpay%2Freturn%3Fresult%3Ddone"); + has(form, "cancel_url=https%3A%2F%2Fmorsel.example%2Fpay%2Freturn%3Fresult%3Dcancelled"); + + // No card is sent: it was typed on the processor's page. + PaymentProvider.Card card = stripe.finishCardSetup("cus_123", "cs_test_1", null); + assertEquals("GET /v1/checkout/sessions/cs_test_1" + + "?expand%5B%5D=setup_intent.payment_method", script.asked.get(1)); + assertEquals(null, script.forms.get(1)); + assertEquals("pm_1", card.token); + assertEquals("Visa", card.brand); + assertEquals("4242", card.last4); + assertEquals(8, card.expMonth); + assertEquals(2031, card.expYear); + } + + @Test + void aSetupNotFinishedOrSomebodyElsesSavesNoCard() throws Exception { + // The user closed the page: the session is still open, with no card. + Script script = new Script().reply(200, "{\"id\":\"cs_test_2\",\"status\":\"open\"," + + "\"customer\":\"cus_123\",\"setup_intent\":null}"); + try { + new StripePayments(script, RETURN).finishCardSetup("cus_123", "cs_test_2", null); + fail("An unfinished setup saved a card"); + } catch (PaymentDeclinedException expected) { + assertTrue(expected.getMessage().indexOf("not added") > 0); + } + script = new Script().reply(200, "{\"id\":\"cs_test_3\",\"status\":\"complete\"," + + "\"customer\":\"cus_999\",\"setup_intent\":{\"status\":\"succeeded\"," + + "\"payment_method\":{\"id\":\"pm_9\",\"card\":{\"brand\":\"visa\"," + + "\"last4\":\"1111\",\"exp_month\":1,\"exp_year\":2030}}}}"); + try { + new StripePayments(script, RETURN).finishCardSetup("cus_123", "cs_test_3", null); + fail("Another customer's session saved a card"); + } catch (PaymentDeclinedException wrong) { + fail("That is a fault, not a refusal"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("another customer") > 0); + } + // A page to open is the whole point of starting one. + script = new Script().reply(200, "{\"id\":\"cs_test_4\"}"); + try { + new StripePayments(script, RETURN).startCardSetup("cus_123", "setup1"); + fail("A setup with no page was started"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("no page") > 0); + } + } + + @Test + void aDeliveryIsChargedOnceWithNobodyAtTheScreen() throws Exception { + Script script = new Script().reply(200, "{\"id\":\"pi_1\",\"object\":\"payment_intent\"," + + "\"status\":\"succeeded\",\"amount\":1840}"); + StripePayments stripe = new StripePayments(script, RETURN); + assertEquals("pi_1", stripe.charge("delivery-77", "cus_123", "pm_1", 1840L, "USD", + "Morsel delivery")); + assertEquals("POST /v1/payment_intents", script.asked.get(0)); + String form = script.forms.get(0); + has(form, "amount=1840"); + has(form, "currency=usd"); + has(form, "customer=cus_123"); + has(form, "payment_method=pm_1"); + has(form, "off_session=true"); + has(form, "confirm=true"); + has(form, "description=Morsel+delivery"); + has(form, "metadata%5Bmorsel_charge%5D=delivery-77"); + // The key that makes a charge sent twice a charge made once. + assertEquals("delivery-77", script.keys.get(0)); + } + + @Test + void aChargeTheBankWantsApprovedOrRefusesIsADecline() throws Exception { + Script script = new Script().reply(200, "{\"id\":\"pi_2\"," + + "\"status\":\"requires_action\"}"); + try { + new StripePayments(script, RETURN).charge("k1", "cus_123", "pm_1", 500L, "USD", "x"); + fail("A charge awaiting approval was taken as made"); + } catch (PaymentDeclinedException expected) { + assertTrue(expected.getMessage().indexOf("approval") > 0); + } + // What Stripe answers for a card that is refused: 402, and words + // written for the cardholder. + script = new Script().reply(402, "{\"error\":{\"type\":\"card_error\"," + + "\"code\":\"card_declined\",\"decline_code\":\"insufficient_funds\"," + + "\"message\":\"Your card has insufficient funds.\"}}"); + try { + new StripePayments(script, RETURN).charge("k2", "cus_123", "pm_1", 500L, "USD", "x"); + fail("A declined charge was taken as made"); + } catch (PaymentDeclinedException expected) { + assertEquals("Your card has insufficient funds.", expected.getMessage()); + } + // Anything else is a fault, reported by status and code and without + // the processor's message, which can quote the request. + script = new Script().reply(401, "{\"error\":{\"type\":\"invalid_request_error\"," + + "\"code\":\"api_key_invalid\",\"message\":\"Invalid API Key provided: sk_x\"}}"); + try { + new StripePayments(script, RETURN).charge("k3", "cus_123", "pm_1", 500L, "USD", "x"); + fail("A request the processor rejected was taken as made"); + } catch (PaymentDeclinedException wrong) { + fail("That is a fault, not a refusal"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("401") > 0, expected.getMessage()); + assertTrue(expected.getMessage().indexOf("api_key_invalid") > 0); + assertEquals(-1, expected.getMessage().indexOf("sk_x")); + } + script = new Script().reply(502, "Bad gateway"); + try { + new StripePayments(script, RETURN).charge("k4", "cus_123", "pm_1", 500L, "USD", "x"); + fail("An answer that is not JSON was taken as a charge"); + } catch (PaymentDeclinedException wrong) { + fail("That is a fault, not a refusal"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("502") > 0, expected.getMessage()); + } + } + + @Test + void aRefundReturnsTheChargeOnce() throws Exception { + Script script = new Script() + .reply(200, "{\"id\":\"re_1\",\"object\":\"refund\",\"status\":\"succeeded\"}") + .reply(200, "{\"id\":\"re_2\",\"object\":\"refund\",\"status\":\"failed\"}"); + StripePayments stripe = new StripePayments(script, RETURN); + PaymentProvider.Refund made = stripe.refund("pi_1", 1840L); + assertEquals("re_1", made.reference); + assertFalse(made.pending); + assertEquals("POST /v1/refunds", script.asked.get(0)); + has(script.forms.get(0), "payment_intent=pi_1"); + has(script.forms.get(0), "amount=1840"); + assertEquals("refund-pi_1", script.keys.get(0)); + try { + stripe.refund("pi_1", 1840L); + fail("A refund that failed was taken as made"); + } catch (IOException expected) { + assertTrue(expected.getMessage().indexOf("failed") > 0); + } + } + + @Test + void aPendingRefundIsAskedAboutUntilItEnds() throws Exception { + Script script = new Script() + .reply(200, "{\"id\":\"re_3\",\"object\":\"refund\",\"status\":\"pending\"}") + .reply(200, "{\"id\":\"re_4\",\"object\":\"refund\"," + + "\"status\":\"requires_action\"}") + .reply(200, "{\"id\":\"re_3\",\"object\":\"refund\",\"status\":\"pending\"}") + .reply(200, "{\"id\":\"re_3\",\"object\":\"refund\",\"status\":\"succeeded\"}") + .reply(200, "{\"id\":\"re_4\",\"object\":\"refund\",\"status\":\"canceled\"}") + .reply(200, "{\"id\":\"re_5\",\"object\":\"refund\",\"status\":\"failed\"}"); + StripePayments stripe = new StripePayments(script, RETURN); + // Taken, but not yet money back: neither is a refund to record as made. + PaymentProvider.Refund pending = stripe.refund("pi_3", 900L); + assertEquals("re_3", pending.reference); + assertTrue(pending.pending); + assertTrue(stripe.refund("pi_4", 900L).pending); + assertEquals(PaymentProvider.REFUND_PENDING, stripe.refundStatus("re_3")); + assertEquals("GET /v1/refunds/re_3", script.asked.get(2)); + assertEquals(PaymentProvider.REFUND_SUCCEEDED, stripe.refundStatus("re_3")); + // Canceled returned no more money than failed did. + assertEquals(PaymentProvider.REFUND_FAILED, stripe.refundStatus("re_4")); + assertEquals(PaymentProvider.REFUND_FAILED, stripe.refundStatus("re_5")); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/Trips.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/Trips.java new file mode 100644 index 00000000000..32bb1c7deb6 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/Trips.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Json; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockRequestBuilder; + +import java.util.List; +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcRequestBuilders.post; +import static com.codename1.backend.test.MockMvcResultMatchers.status; + +/// The steps several tests take on the way to what they are about: a courier +/// going on line, a card being saved, a delivery being driven to its end. +final class Trips { + static final String VISA = "4242424242424242"; + static final String MASTERCARD = "5555555555554444"; + /// Refused when it is saved. + static final String DECLINED = "4000000000000002"; + /// Saved without complaint, and refused when it is charged. + static final String FAILS_WHEN_CHARGED = "4000000000000341"; + /// Saved without complaint, and charging it fails without an answer. + static final String NO_ANSWER_WHEN_CHARGED = "4000000000000119"; + /// Charged without complaint; a refund of it is pending at first. + static final String SLOW_REFUND = "4000000000007726"; + + private Trips() { + } + + static MockRequestBuilder body(MockRequestBuilder request, String json) { + return request.contentType("application/json").content(json); + } + + /// The object a request that succeeds answers with. + static Map json(MockMvc mvc, MockRequestBuilder request) throws Exception { + return Json.parseObject(mvc.perform(request).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString()); + } + + /// The list a request that succeeds answers with. + static List list(MockMvc mvc, MockRequestBuilder request) throws Exception { + return (List) Json.parse(mvc.perform(request).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString()); + } + + /// The status a request is answered with, whatever it is. + static int statusOf(MockMvc mvc, MockRequestBuilder request) throws Exception { + return mvc.perform(request).andReturn().getResponse().getStatus(); + } + + static long number(Map object, String field) { + return ((Number) object.get(field)).longValue(); + } + + /// A delivery request with more said about it: `extra` is further members of + /// the object, as JSON, without the braces. + static String delivery(double fromLat, double fromLng, double toLat, double toLng, String extra) { + String plain = People.delivery(fromLat, fromLng, toLat, toLng); + return plain.substring(0, plain.length() - 1) + "," + extra + "}"; + } + + static void online(MockMvc mvc, String courier, double lat, double lng) throws Exception { + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(true, lat, lng)), courier)).andExpect(status().isOk()); + } + + static void offline(MockMvc mvc, String courier) throws Exception { + mvc.perform(People.asCourier(body(post("/api/courier/status"), + People.position(false, 0, 0)), courier)).andExpect(status().isOk()); + } + + static String card(String number) { + // A few years off whenever this runs: a card is refused both when it + // has expired and when its date is further away than any card's is. + int year = Integer.parseInt(Days.iso(System.currentTimeMillis()).substring(0, 4)) + 3; + return "{\"number\":\"" + number + "\",\"expMonth\":12,\"expYear\":" + year + + ",\"cvc\":\"123\",\"holder\":\"A Customer\"}"; + } + + /// Saves a card and returns it as the server describes it. + static Map addCard(MockMvc mvc, String customer, String number) throws Exception { + Map setup = json(mvc, People.asCustomer(post("/api/payments/methods/setup"), customer)); + return json(mvc, People.asCustomer(body(post("/api/payments/methods/setup/" + setup.get("id") + + "/complete"), card(number)), customer)); + } + + /// Asks for a delivery and has `courier`, who is on line beside the pickup, take + /// it to its end. Returns the delivery as the courier is shown it on completing. + static Map complete(MockMvc mvc, String customer, String courier, String delivery) throws Exception { + String id = (String) json(mvc, People.asCustomer(body(post("/api/deliveries"), delivery), customer)) + .get("id"); + mvc.perform(People.asCourier(post("/api/courier/offers/" + id + "/accept"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/arrived"), courier)) + .andExpect(status().isOk()); + mvc.perform(People.asCourier(post("/api/courier/deliveries/" + id + "/start"), courier)) + .andExpect(status().isOk()); + return json(mvc, People.asCourier(post("/api/courier/deliveries/" + id + "/complete"), courier)); + } + + static Map receipt(MockMvc mvc, String customer, String deliveryId) throws Exception { + return json(mvc, People.asCustomer(get("/api/payments/receipts/" + deliveryId), customer)); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/WebSignInTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/WebSignInTest.java new file mode 100644 index 00000000000..5ae8a09a0d2 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/WebSignInTest.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.backend.Json; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.security.oauth2.core.OAuth2Parameters; +import com.codename1.backend.test.BackendTest; +import com.codename1.backend.test.MockMvc; +import com.codename1.backend.test.MockResponse; +import com.codenameone.examples.morsel.account.DemoAccounts; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static com.codename1.backend.test.MockMvcRequestBuilders.get; +import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath; +import static com.codename1.backend.test.MockMvcResultMatchers.status; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/// Signing in from the app running in a browser, which sees only how each +/// request ended: no redirect it can stop at, and no cookie it can read. +@BackendTest +class WebSignInTest { + private static final String VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + private static final String CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"; + private static final String HERE = "http://" + SignIn.HOST + SecurityConfig.WEB_REDIRECT_PATH; + + @Autowired + private MockMvc mvc; + + private String authorize(String session, String redirect) throws Exception { + return mvc.perform(get("/oauth2/authorize?response_type=code&client_id=" + + SecurityConfig.CLIENT_ID + "&redirect_uri=" + OAuth2Parameters.encode(redirect) + + "&scope=openid%20profile&state=s-web&code_challenge=" + CHALLENGE + + "&code_challenge_method=S256") + .header("Host", SignIn.HOST).cookie("CN1SESSION", session)) + .andReturn().getResponse().getRedirectedUrl(); + } + + @Test + void thePasswordIsAnsweredWithAStatus() throws Exception { + MockResponse accepted = mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST)) + .andExpect(status().isOk()).andReturn().getResponse(); + // Not a redirect: a browser would follow one and report the page at its end. + assertNull(accepted.getRedirectedUrl()); + assertNotNull(accepted.getCookie("CN1SESSION")); + + MockResponse refused = mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", "not-the-password") + .header("Host", SignIn.HOST)) + .andExpect(status().isUnauthorized()).andReturn().getResponse(); + assertNull(refused.getRedirectedUrl()); + assertNull(refused.getCookie("CN1SESSION")); + } + + @Test + void theCodeComesBackThroughThisServer() throws Exception { + String session = SignIn.session(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + String location = authorize(session, HERE); + assertNotNull(location); + assertTrue(location.startsWith(HERE + "?"), location); + + // What the browser does next, and what the app reads. + String answer = mvc.perform(get(location.substring(("http://" + SignIn.HOST).length())) + .header("Host", SignIn.HOST)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.state").value("s-web")) + .andReturn().getResponse().getContentAsString(); + String code = (String) Json.parseObject(answer).get("code"); + assertNotNull(code); + + Map tokens = Json.parseObject(mvc.perform(SignIn.formPost("/oauth2/token", + "grant_type", "authorization_code", + "code", code, + "redirect_uri", HERE, + "code_verifier", VERIFIER, + "client_id", SecurityConfig.CLIENT_ID).header("Host", SignIn.HOST)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString()); + String token = (String) tokens.get("access_token"); + assertNotNull(token); + mvc.perform(get("/api/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()); + } + + @Test + void aCodeIsNeverSentToAnotherServer() throws Exception { + String session = SignIn.session(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + // Same path, somebody else's server: the address has to be this one. + String elsewhere = authorize(session, "http://attacker.example" + SecurityConfig.WEB_REDIRECT_PATH); + assertTrue(elsewhere == null || elsewhere.indexOf("attacker.example") < 0, String.valueOf(elsewhere)); + String otherPath = authorize(session, "http://" + SignIn.HOST + "/somewhere/else"); + assertTrue(otherPath == null || otherPath.indexOf("code=") < 0, String.valueOf(otherPath)); + } + + @Test + void theInstalledAppStillSignsInItsOwnWay() throws Exception { + assertNotNull(SignIn.token(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD)); + } + + @Test + void theCodePageSaysWhenThereIsNoCode() throws Exception { + mvc.perform(get(SecurityConfig.WEB_REDIRECT_PATH + "?error=access_denied&state=s-web") + .header("Host", SignIn.HOST)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value("access_denied")); + } + + @Test + void aSignInAnotherSiteSubmittedIsRefused() throws Exception { + MockResponse forged = mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST).header("Origin", "http://attacker.example")) + .andExpect(status().isForbidden()).andReturn().getResponse(); + assertNull(forged.getCookie("CN1SESSION")); + // A page in a sandboxed frame, or opened from a file. + mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST).header("Origin", "null")) + .andExpect(status().isForbidden()); + // The same name on another port or scheme is another site. + mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST).header("Origin", "https://" + SignIn.HOST)) + .andExpect(status().isForbidden()); + mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST).header("Origin", "http://morsel.test:9090")) + .andExpect(status().isForbidden()); + // The rest of what changes something on this chain is held to it too. + mvc.perform(SignIn.formPost("/oauth2/token", "grant_type", "authorization_code") + .header("Host", SignIn.HOST).header("Origin", "http://attacker.example")) + .andExpect(status().isForbidden()); + mvc.perform(SignIn.formPost("/logout") + .header("Host", SignIn.HOST).header("Origin", "http://attacker.example")) + .andExpect(status().isForbidden()); + } + + @Test + void aSignInFromThisServersOwnPageIsAccepted() throws Exception { + // A browser spells the host in lower case; the comparison does not care. + MockResponse own = mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST).header("Origin", "HTTP://MORSEL.TEST:8080")) + .andExpect(status().isOk()).andReturn().getResponse(); + assertNotNull(own.getCookie("CN1SESSION")); + // And the installed app, which is not a browser and names no origin. + assertNotNull(SignIn.session(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD)); + } + + @Test + void theOriginIsHeldToTheIssuerWhenOneIsConfigured() { + assertTrue(SecurityConfig.sameOrigin("https://morsel.example", "https://morsel.example")); + assertTrue(SecurityConfig.sameOrigin("https://MORSEL.example", "https://morsel.example/auth")); + assertTrue(!SecurityConfig.sameOrigin("https://morsel.example.attacker.example", + "https://morsel.example")); + assertTrue(!SecurityConfig.sameOrigin("http://morsel.example", "https://morsel.example")); + assertTrue(!SecurityConfig.sameOrigin("https://morsel.example:8443", "https://morsel.example")); + // With an issuer the address a request arrived at is never consulted. + assertEquals("https://morsel.example", SecurityConfig.here("https://morsel.example")); + // With none, and no request in hand, there is no address to accept. + assertNull(SecurityConfig.here("")); + } + + @Test + void theSessionCookieIsNotSentFromOtherSitesOrReadByScripts() throws Exception { + String header = mvc.perform(SignIn.formPost("/login", + "username", DemoAccounts.CUSTOMER, "password", DemoAccounts.PASSWORD) + .header("Host", SignIn.HOST)) + .andReturn().getResponse().getHeader("Set-Cookie"); + assertNotNull(header); + assertTrue(header.indexOf("HttpOnly") > 0, header); + assertTrue(header.indexOf("SameSite=Lax") > 0 || header.indexOf("SameSite=Strict") > 0, header); + } + + @Test + void theSessionEndsWhenTheAppHasItsTokens() throws Exception { + String session = SignIn.session(mvc, DemoAccounts.CUSTOMER, DemoAccounts.PASSWORD); + String location = authorize(session, HERE); + assertTrue(location.indexOf("code=") > 0, location); + String answer = mvc.perform(get(location.substring(("http://" + SignIn.HOST).length())) + .header("Host", SignIn.HOST)) + .andReturn().getResponse().getContentAsString(); + Map tokens = Json.parseObject(mvc.perform(SignIn.formPost("/oauth2/token", + "grant_type", "authorization_code", + "code", (String) Json.parseObject(answer).get("code"), + "redirect_uri", HERE, + "code_verifier", VERIFIER, + "client_id", SecurityConfig.CLIENT_ID).header("Host", SignIn.HOST)) + .andReturn().getResponse().getContentAsString()); + + // Asking nicely does not end it: only a POST does. + mvc.perform(get("/logout").header("Host", SignIn.HOST).cookie("CN1SESSION", session)); + assertTrue(authorize(session, HERE).indexOf("code=") > 0); + + MockResponse ended = mvc.perform(SignIn.formPost("/logout") + .header("Host", SignIn.HOST).header("Origin", "http://" + SignIn.HOST) + .cookie("CN1SESSION", session)) + .andExpect(status().isNoContent()).andReturn().getResponse(); + assertNull(ended.getRedirectedUrl()); + // The cookie earns no more codes... + String after = authorize(session, HERE); + assertTrue(after == null || after.indexOf("code=") < 0, String.valueOf(after)); + // ...and the tokens it earned are untouched. + mvc.perform(get("/api/me").header("Host", SignIn.HOST) + .header("Authorization", "Bearer " + tokens.get("access_token"))) + .andExpect(status().isOk()); + mvc.perform(SignIn.formPost("/oauth2/token", + "grant_type", "refresh_token", + "refresh_token", (String) tokens.get("refresh_token"), + "client_id", SecurityConfig.CLIENT_ID).header("Host", SignIn.HOST)) + .andExpect(status().isOk()); + } + + @Test + void theCodePageIsDataAndNothingElse() throws Exception { + MockResponse page = mvc.perform(get(SecurityConfig.WEB_REDIRECT_PATH + + "?code=%3Cscript%3Ealert(1)%3C/script%3E&state=s").header("Host", SignIn.HOST)) + .andExpect(status().isOk()).andReturn().getResponse(); + assertTrue(String.valueOf(page.getContentType()).startsWith("application/json"), + String.valueOf(page.getContentType())); + assertEquals("nosniff", page.getHeader("X-Content-Type-Options")); + assertEquals("no-store", page.getHeader("Cache-Control")); + } + + @Test + void anIssuerIsTheOnlyWebAddressWhenOneIsConfigured() { + com.codename1.backend.security.oauth2.server.authorization.RegisteredClient app = + com.codename1.backend.security.oauth2.server.authorization.RegisteredClient + .withId("morsel").clientId(SecurityConfig.CLIENT_ID) + .clientAuthenticationMethod(com.codename1.backend.security.oauth2.core + .ClientAuthenticationMethod.NONE) + .authorizationGrantType(com.codename1.backend.security.oauth2.core + .AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUri(SecurityConfig.LOOPBACK_REDIRECT).build(); + java.util.Set uris = SecurityConfig.withWebRedirect(app, "https://morsel.example") + .getRedirectUris(); + assertTrue(uris.contains("https://morsel.example/signin/code"), uris.toString()); + assertTrue(uris.contains(SecurityConfig.LOOPBACK_REDIRECT), uris.toString()); + assertEquals(2, uris.size(), uris.toString()); + // With none configured and no request in hand, nothing is added. + assertEquals(1, SecurityConfig.withWebRedirect(app, "").getRedirectUris().size()); + } +} diff --git a/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/delivery/OfferRecheckTest.java b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/delivery/OfferRecheckTest.java new file mode 100644 index 00000000000..cd7bd34b661 --- /dev/null +++ b/scripts/morsel/backend/src/test/java/com/codenameone/examples/morsel/delivery/OfferRecheckTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.delivery; + +import com.codename1.backend.DataSource; +import com.codename1.backend.annotations.Autowired; +import com.codename1.backend.test.BackendTest; +import com.codenameone.examples.morsel.account.Accounts; +import com.codenameone.examples.morsel.account.DemoAccounts; +import com.codenameone.examples.morsel.api.CourierStatusDto; +import com.codenameone.examples.morsel.api.RegisterDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryRequestDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.courier.Couriers; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/// An offer made to a courier who stopped being fit for the delivery after the +/// search chose them. +/// +/// Choosing and offering are two transactions, and a courier can go off line, +/// drive away, lose their approval or be blocked in between. Each of those is +/// set up here and the offer made to them anyway, which is the second +/// transaction running after the first read them as they were. It sits in +/// this package because the offer is not part of the service's public face. +@BackendTest +class OfferRecheckTest { + // Porto. + private static final double LAT = 41.1579; + private static final double LNG = -8.6291; + + @Autowired + private Accounts accounts; + @Autowired + private Deliveries deliveries; + @Autowired + private Couriers couriers; + @Autowired + private DataSource db; + + private String open(String name, boolean courier) throws Exception { + String username = accounts.register(new RegisterDto(name + "@test.example", + "a-long-password", name, "+15550111", courier, "Green Fiat 500", "T " + name, + false, null)).username(); + accounts.markPhoneVerified(username); + if (courier) { + accounts.setRoles(DemoAccounts.ADMIN, username, true, false); + } + return username; + } + + private void at(String courier, boolean online, double lat, double lng) throws Exception { + couriers.report(courier, new CourierStatusDto(online, lat, lng, 90d)); + } + + private void notOffered(String id, String courier, String why) throws Exception { + assertNull(deliveries.offer(id, courier), why); + assertEquals(DeliveryState.REQUESTED, deliveries.get("", true, id).state(), why); + } + + @Test + void aCourierNoLongerFitForTheDeliveryIsNotOfferedIt() throws Exception { + String customer = open("opo-customer", false); + String courier = open("opo-courier", true); + // Asked for with nobody on line, so it waits. The courier is kept unfit + // from here until the end, so the scheduled search, which runs + // alongside, has nobody to offer it to either. + String id = deliveries.request(customer, new DeliveryRequestDto(LAT, LNG, "Here", LAT + 0.02, + LNG + 0.02, "There", 0d, 0d, null, null, null, false, false, false, null)).id(); + assertEquals(DeliveryState.REQUESTED, deliveries.get("", true, id).state()); + + // On line, a hundred kilometres away. + at(courier, true, LAT + 1d, LNG); + notOffered(id, courier, "a courier out of reach"); + + // In reach, and blocked. + accounts.suspend(DemoAccounts.ADMIN, courier, true, "a test"); + at(courier, true, LAT, LNG); + notOffered(id, courier, "a blocked courier"); + + // No longer approved. + db.execute("UPDATE courier_application SET status = 'rejected' WHERE username = ?", + new Object[] {courier}); + accounts.suspend(DemoAccounts.ADMIN, courier, false, "a test"); + notOffered(id, courier, "a courier whose approval was withdrawn"); + + // Not heard from in a long while, whatever their status says. + db.execute("UPDATE courier_state SET last_seen = 1 WHERE username = ?", + new Object[] {courier}); + db.execute("UPDATE courier_application SET status = 'approved' WHERE username = ?", + new Object[] {courier}); + notOffered(id, courier, "a courier not heard from"); + + // Off line. + at(courier, false, 0d, 0d); + notOffered(id, courier, "a courier off line"); + + // And once fit again, the delivery is theirs: offered here, or by the + // scheduled search if it got there first. + at(courier, true, LAT, LNG); + deliveries.offer(id, courier); + DeliveryDto now = deliveries.get("", true, id); + assertEquals(DeliveryState.OFFERED, now.state()); + assertEquals(courier, now.courierUsername()); + deliveries.cancelByAdmin(DemoAccounts.ADMIN, id, "the test is over"); + at(courier, false, 0d, 0d); + } +} diff --git a/scripts/morsel/check-template.sh b/scripts/morsel/check-template.sh new file mode 100755 index 00000000000..52dfcfcb08f --- /dev/null +++ b/scripts/morsel/check-template.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# +# Builds what the Initializr generates from this project. +# +# check-template.sh [work-directory] +# +# The Initializr offers this application as a template, from resources that +# scripts/sync-initializr-morsel.py derives from it. That the resources match +# is one question; this is the other: whether a project generated from them, +# under another name and in another package, still builds. The real generator +# makes the project, and it is then built against this checkout: +# +# the server's tests pass against the contract module +# the app builds from common, without platform modules and with them, and +# the client half of the contract is generated into it +# +# Needs this checkout's artifacts installed (core, javase, css-compiler, the +# Maven plugin, backend, backend-test), a JDK 17 or newer as JAVA_HOME, python3 +# and unzip. +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +MVN="${MVN:-mvn}" +WORK="${1:-$HERE/target/template-check}" + +log() { echo "[morsel-template] $*" >&2; } + +python3 "$ROOT/scripts/sync-initializr-morsel.py" + +# The version this checkout installs, which the generated poms are pointed at: +# the release the Initializr generates against does not have this project's +# build support until one that follows this change. +VERSION="$(sed -n 's:.*\(.*\).*:\1:p' "$ROOT/maven/pom.xml" | head -n 1)" +[ -n "$VERSION" ] || { log "could not read the version from maven/pom.xml"; exit 1; } + +rm -rf "$WORK" +mkdir -p "$WORK" +log "generating the Initializr's downloads" +python3 "$ROOT/scripts/tests/generate-initializr-fixtures.py" "$WORK/fixtures" > "$WORK/fixtures.log" 2>&1 \ + || { tail -n 60 "$WORK/fixtures.log" >&2; log "the generator failed"; exit 1; } + +# extract -- a generated project, pointed at this checkout's version +extract() { + local dir="$WORK/$1" + mkdir -p "$dir" + unzip -q "$WORK/fixtures/MORSEL-$1-INTELLIJ.zip" -d "$dir" + python3 - "$dir/pom.xml" "$VERSION" <<'PY' +import re, sys +path, version = sys.argv[1], sys.argv[2] +text = open(path, encoding='utf-8').read() +for tag in ('cn1.plugin.version', 'cn1.version'): + text, count = re.subn(r'<%s>[^<]*' % (tag, tag), '<%s>%s' % (tag, version, tag), text) + assert count == 1, (tag, count) +open(path, 'w', encoding='utf-8').write(text) +PY + echo "$dir" +} + +# The fixtures are generated as com.example.probe.LauncherProbe. +PACKAGE=com/example/probe + +minimal="$(extract APP_WITH_BACKEND)" +log "running the generated server's tests" +(cd "$minimal" && "$MVN" -B -q -Dcodename1.platform=backend -pl shared,backend -am test) +ls "$minimal"/backend/target/surefire-reports/TEST-*.xml >/dev/null 2>&1 \ + || { log "the generated server's tests did not run"; exit 1; } +[ -f "$minimal/backend/target/generated-sources/cn1-annotations/$PACKAGE/api/CustomerApiServer.java" ] \ + || { log "the server's half of the contract was not generated"; exit 1; } + +for layout in APP_WITH_BACKEND FULL; do + project="$WORK/$layout" + [ -d "$project" ] || project="$(extract "$layout")" + log "building the generated app ($layout)" + (cd "$project" && "$MVN" -B -q -Dcodename1.platform=javase -Djava.awt.headless=true -DskipTests package) + for built in "classes/theme.res" "classes/$PACKAGE/api/CustomerApiImpl.class" "classes/cn1app/RestClientBootstrap.class"; do + [ -f "$project/common/target/$built" ] \ + || { log "$layout: common/target/$built was not built"; exit 1; } + done +done +log "passed" diff --git a/scripts/morsel/common/codenameone_settings.properties b/scripts/morsel/common/codenameone_settings.properties new file mode 100644 index 00000000000..524d94d9df5 --- /dev/null +++ b/scripts/morsel/common/codenameone_settings.properties @@ -0,0 +1,58 @@ +codename1.android.keystore= +codename1.android.keystoreAlias= +codename1.android.keystorePassword= +codename1.arg.and.themeMode=modern +# A server started on this machine is reached over plain HTTP, which Android +# refuses by default. Remove this once the app talks to a server with a +# certificate. +codename1.arg.android.xapplication_attr=android\:usesCleartextTraffic\="true" +codename1.arg.desktop.interactiveScrollbars=true +codename1.arg.desktop.titleBar=native +codename1.arg.ios.newStorageLocation=true +# What iOS shows when someone applying to drive photographs a document. +codename1.arg.ios.NSCameraUsageDescription=The camera photographs the documents of your application to drive. +# What iOS shows when they pick a picture of a document they already have. +codename1.arg.ios.NSPhotoLibraryUsageDescription=Your photo library is where you pick a picture of a document from. +# What iOS shows before Face ID unlocks the sign-in kept in secure storage. +codename1.arg.ios.NSFaceIDUsageDescription=Authenticate to securely access your account +# What iOS shows when the app first asks where the device is. +codename1.arg.ios.NSLocationWhenInUseUsageDescription=Your location is where a delivery picks you up, and where customers find you while you drive. +# While a courier is online the app keeps reporting where they are from the +# background: iOS needs the location background mode and the Always prompt's +# text, Android a foreground service of the location type. +codename1.arg.ios.background_modes=location +codename1.arg.ios.locationUsageDescription=Your location is where a delivery picks you up, and where customers find you while you drive. +codename1.arg.ios.NSLocationAlwaysAndWhenInUseUsageDescription=While you are online as a courier, Morsel keeps sharing your location with customers and dispatch, even with the app in the background. +codename1.arg.android.foregroundServiceType=location +# Push notifications. With push included the build adds the +# remote-notification background mode and the push entitlement itself. +codename1.arg.ios.includePush=true +# Android push is off until the app has its Firebase file: a build that +# uses PushClient without google-services.json stops with an error. Once +# native/android/google-services.json is in place, delete this line. +codename1.arg.android.messagingService=none +# The delivery on the lock screen updates as the courier moves. +codename1.arg.ios.surfaces.frequentUpdates=true +codename1.arg.ios.themeMode=modern +codename1.arg.java.version=17 +codename1.arg.nativeTheme=native +codename1.cssTheme=true +codename1.displayName=Morsel +codename1.icon=icon.png +codename1.ios.appid=Q5GHSKAL2F.com.codenameone.examples.morsel +codename1.ios.certificate= +codename1.ios.certificatePassword= +codename1.ios.debug.certificate= +codename1.ios.debug.certificatePassword= +codename1.ios.debug.provision= +codename1.ios.provision= +codename1.ios.release.certificate= +codename1.ios.release.certificatePassword= +codename1.ios.release.provision= +codename1.kotlin=false +codename1.languageLevel=5 +codename1.mainName=Morsel +codename1.packageName=com.codenameone.examples.morsel +codename1.secondaryTitle=Hello World +codename1.vendor=CodenameOne +codename1.version=1.0 diff --git a/scripts/morsel/common/icon.png b/scripts/morsel/common/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1f4fa5dd25258fbaa99b15a11d47c83ce4a9a8ac GIT binary patch literal 123269 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

g@MUJI7HpSpMR*={}r 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 + + 4.0.0 + + com.codenameone.examples.morsel + morsel + 1.0-SNAPSHOT + + com.codenameone.examples.morsel + morsel-common + 1.0-SNAPSHOT + jar + + + + + com.codenameone + codenameone-core + provided + + + + ${project.groupId} + morsel-shared + ${project.version} + + + + + com.codenameone + codenameone-javase + test + + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + + + + + + + + + + install-codenameone + ${user.home}/.codenameone/guibuilder.jar + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + validate + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + android-compat + + + ${basedir}/src/main/android + + + + + com.codenameone + codenameone-android-compat + provided + + + + + + kotlin + + + + ${basedir}/src/main/kotlin + + + + 2.2.10 + true + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + + + + org.jetbrains + annotations + 13.0 + + + com.codenameone + java-runtime + provided + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + read-project-properties + + + + ${basedir}/codenameone_settings.properties + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + process-resources + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + ${project.basedir}/src/main/android/kotlin + ${project.basedir}/src/main/android/java + ${project.build.directory}/generated-sources/android + + + -no-reflect + -no-jdk + + + + + test-compile + process-test-sources + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + -no-reflect + -no-jdk + + + + + + + + + + + + + javase + + + codename1.platform + javase + + + + javase + + + + + org.codehaus.mojo + exec-maven-plugin + + java + true + + -Xmx1024M + + -classpath + + ${exec.mainClass} + ${cn1.mainClass} + + + + + + + + + + cn1-javase-natives + + + ${basedir}/../javase/pom.xml + + + + + + com.codenameone + codenameone-maven-plugin + + + compile-javase-natives + process-classes + + compile-javase-natives + + + javase + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.surefire + surefire-junit-platform + 3.0.0-M5 + + + + ${skipTests} + + ${project.build.directory}/cn1-javase/classes + + + + + + + + + cn1-host-javase + + + codename1.platform + javase + + + ${basedir}/../javase/pom.xml + + + + javase + javase + none + + + + + com.codenameone + codenameone-maven-plugin + + + hosted-javase + validate + + hosted-platform + + + javase + + + + build-javase + package + + build + + + javase + + + + + + + + + + cn1-host-android + + + codename1.platform + android + + + ${basedir}/../android/pom.xml + + + + android + android + android-device + + + + + com.codenameone + codenameone-maven-plugin + + + hosted-android + validate + + hosted-platform + + + android + + + + build-android + package + + build + + + android + + + + + + + + + + cn1-host-ios + + + codename1.platform + ios + + + ${basedir}/../ios/pom.xml + + + + ios + ios + ios-device + + + + + com.codenameone + codenameone-maven-plugin + + + hosted-ios + validate + + hosted-platform + + + ios + + + + build-ios + package + + build + + + ios + + + + + + + + + + cn1-host-javascript + + + codename1.platform + javascript + + + ${basedir}/../javascript/pom.xml + + + + javascript + javascript + local-javascript + + + + + com.codenameone + codenameone-maven-plugin + + + hosted-javascript + validate + + hosted-platform + + + javascript + + + + build-javascript + package + + build + + + javascript + + + + + + + + + + cn1-host-win + + + codename1.platform + win + + + ${basedir}/../win/pom.xml + + + + win + win + windows-device + + + + + com.codenameone + codenameone-maven-plugin + + + hosted-win + validate + + hosted-platform + + + win + + + + build-windows + package + + build + + + win + + + + + + + + + + cn1-host-linux + + + codename1.platform + linux + + + ${basedir}/../linux/pom.xml + + + + linux + linux + linux-device + + + + + com.codenameone + codenameone-maven-plugin + + + hosted-linux + validate + + hosted-platform + + + linux + + + + build-linux + package + + build + + + linux + + + + + + + + + + simulator + + javase + com.codename1.impl.javase.Simulator + + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + prepare-simulator-environment + initialize + + prepare-simulator-classpath + + + javase + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-in-simulator + verify + + exec + + + ${cn1.hosted.javase.skip} + ${basedir} + java + + -Xmx1024M + -Dcodename1.css.compiler.args.input=${codename1.css.compiler.args.input} + -Dcodename1.css.compiler.args.output=${codename1.css.compiler.args.output} + -Dcodename1.css.compiler.args.merge=${codename1.css.compiler.args.merge} + ${codename1.exec.args.debug} + ${codename1.exec.args.runjdwp.transport} + + @${cn1.hosted.classpathArgFile} + ${exec.mainClass} + ${codename1.mainClass} + + + + + + + + + + + idea-simulator + + com.codename1.impl.javase.Simulator + true + + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + prepare-simulator-environment + initialize + + prepare-simulator-classpath + + + javase + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-in-simulator-idea + verify + + java + + + ${cn1.hosted.javase.skip} + + ${project.build.directory}/cn1-javase/classes + + + ${codename1.mainClass} + + + + codename1.css.compiler.args.input + ${codename1.css.compiler.args.input} + + + codename1.css.compiler.args.output + ${codename1.css.compiler.args.output} + + + codename1.css.compiler.args.merge + ${codename1.css.compiler.args.merge} + + + + cn1.class.path + ${cn1.class.path} + + + + + + + + + + + + debug-simulator + + com.codename1.impl.javase.Simulator + true + + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + prepare-simulator-environment + initialize + + prepare-simulator-classpath + + + javase + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-in-simulator + verify + + exec + + + ${cn1.hosted.javase.skip} + ${basedir} + java + + -Xdebug + -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} + -Xmx1024M + -Dcodename1.css.compiler.args.input=${codename1.css.compiler.args.input} + -Dcodename1.css.compiler.args.output=${codename1.css.compiler.args.output} + -Dcodename1.css.compiler.args.merge=${codename1.css.compiler.args.merge} + @${cn1.hosted.classpathArgFile} + ${exec.mainClass} + ${codename1.mainClass} + + + + + + + + + + + debug-eclipse + + com.codename1.impl.javase.Simulator + true + + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + prepare-simulator-environment + initialize + + prepare-simulator-classpath + + + javase + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-in-simulator + verify + + exec + + + ${cn1.hosted.javase.skip} + ${basedir} + java + + -Xdebug + -Xrunjdwp:transport=dt_socket,server=y,address=${jpda.address},suspend=y + -Xmx1024M + -Dcodename1.css.compiler.args.input=${codename1.css.compiler.args.input} + -Dcodename1.css.compiler.args.output=${codename1.css.compiler.args.output} + -Dcodename1.css.compiler.args.merge=${codename1.css.compiler.args.merge} + @${cn1.hosted.classpathArgFile} + ${exec.mainClass} + ${codename1.mainClass} + + + + + + + + + + + run-desktop + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + generate-icons + generate-sources + + generate-desktop-app-wrapper + + + javase + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-desktop + verify + + java + + + ${cn1.hosted.javase.skip} + ${codename1.packageName}.${codename1.mainName}Stub + + + ${project.build.directory}/cn1-javase/classes + ${project.build.directory}/cn1-javase/desktop + ${basedir}/src/desktop/resources + ${basedir}/../javase/src/desktop/resources + + + + + + + + + + + executable-jar + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + generate-icons + generate-sources + + generate-desktop-app-wrapper + + + javase + + + + javase-executable-jar + package + + javase-executable-jar + + + javase + + + + + + + + + + test + + + com.codenameone + codenameone-core + compile + + + com.codenameone + codenameone-javase + runtime + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + + + + com.codenameone + codenameone-maven-plugin + + + cn1-tests + test + + test + + + javase + + + + + + + + + + run-android + + + + com.codenameone + codenameone-maven-plugin + + + + run-android-hosted + validate + + hosted-platform + + + android + + + + + + maven-antrun-plugin + + + adb-install + verify + + run + + + ${cn1.hosted.android.skip} + + Running adb install + + + + + + Trying to start app on device using adb + + + + + + + + + + + + + + + + + + + + ios-debug + + + iphone + + + ios + + + + + ios-release + + + iphone + true + + + ios + true + + + + + javascript + + javascript + javascript + + + + + android + + android + android + + + + + win + + windows + win + + + + + windows + + desktop_windows + javase + + + + + mac + + desktop_macosx + javase + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + read-project-properties + + + + ${basedir}/codenameone_settings.properties + + + + + + + + com.codenameone + codenameone-maven-plugin + + + + + transcode-svg + generate-sources + + transcode-svg + + + + + compile-android-res + generate-sources + + compile-android-res + + + + generate-gui-sources + process-sources + + generate-gui-sources + + + + cn1-process-classes + process-classes + + + remap-android + bytecode-compliance + css + + process-annotations + + + + + attach-test-artifact + test + + attach-test-artifact + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + + + + + + + + diff --git a/scripts/morsel/common/src/desktop/resources/NativeTheme.res b/scripts/morsel/common/src/desktop/resources/NativeTheme.res new file mode 100644 index 0000000000000000000000000000000000000000..83e067b6969553d6d2cdd7257e64e7417393864f GIT binary patch literal 300158 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 zü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 BseS+e literal 0 HcmV?d00001 diff --git a/scripts/morsel/common/src/main/css/theme.css b/scripts/morsel/common/src/main/css/theme.css new file mode 100644 index 00000000000..f59da6e0bc5 --- /dev/null +++ b/scripts/morsel/common/src/main/css/theme.css @@ -0,0 +1,1692 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +/* + * The whole look of the app. + * + * COLOURS AND SHAPE -- change these and nothing else to re-skin the app. Every + * rule below takes its colours from here; none names a colour of its own. Each + * colour has a second value for dark mode, used by the block at the end of the + * file. + */ +#Constants { + includeNativeBool: true; + defaultSourceDPIInt: "0"; + /* ONE TYPE SCALE, in millimetres as a phone draws them: 2.1 caption, + 2.5 secondary, 2.75 body, 2.9 control, 3.5 heading, 4.4 title, 7 display. + A desktop draws a millimetre in far fewer pixels and is read from + further away, so every size is a quarter larger there. */ + device-desktop-font-scale: "1.25"; + /* Screens change quickly: nothing waits for an animation. */ + transitionSpeedInt: "200"; + /* The switch, sized from the Switch style's font. */ + switchThumbScaleY: "1.5"; + switchTrackScaleY: "1.8"; + switchTrackScaleX: "3.1"; + switchThumbInsetMM: "0.35"; + switchTrackOffOutlineWidthMM: "0"; + switchTrackOnOutlineWidthMM: "0"; + switchThumbWidthScale: "1.0"; + --brand: #1f5eff; + --brand-pressed: #1747c4; + --brand-contrast: #ffffff; + --surface: #ffffff; + --surface-alt: #f3f4f7; + --surface-press: #e6e9ee; + --text: #12161c; + --text-muted: #667085; + --line: #e6e8ed; + --line-strong: #cfd4dc; + --success: #12805c; + --warning: #b25e09; + --danger: #c5292a; + /* The pale ground a selected card and a badge sit on. */ + --brand-tint: #edf2ff; + --success-tint: #e4f4ed; + --warning-tint: #fcf0df; + --danger-tint: #fcebeb; + + --brand-dark: #6d93ff; + --brand-pressed-dark: #4f78ee; + --brand-contrast-dark: #0b1020; + --surface-dark: #12161c; + --surface-alt-dark: #1c222b; + --surface-press-dark: #2a313c; + --text-dark: #f2f4f7; + --text-muted-dark: #98a2b3; + --line-dark: #2a313c; + --line-strong-dark: #3b4452; + --success-dark: #47c79a; + --warning-dark: #f0a24b; + --danger-dark: #ff7a7a; + --brand-tint-dark: #1a2440; + --success-tint-dark: #15302a; + --warning-tint-dark: #33270f; + --danger-tint-dark: #3a1f22; + + /* ONE RADIUS for anything card- or button-shaped, a smaller one for what + sits inside a card, and the sheet's. */ + --radius: 2.4mm; + --radius-small: 1.5mm; + + /* What floats over the map: the shadow under it, the dimming behind an + open menu, and the corners of the sheet. */ + --shadow: #101828; + /* Under a card with square-ish corners. Black, and without a blur where it + is used: that is the one shadow a rounded rectangle draws for itself. + Any other is rendered to pictures of the card when the theme is + compiled, and a build that cannot render them has no card at all. */ + --shadow-card: rgba(0, 0, 0, 0.14); + --scrim: #000000; + --radius-sheet: 4.4mm; + /* The map's own drawings. The edge is the outline that keeps a pin, the + car and the route readable over any tile. */ + --map-edge: #ffffff; + --map-edge-dark: #0b1020; + --car: #14171f; + --car-dark: #f2f4f7; +} + +/* ---------------------------------------------------------------- surfaces */ + +WlForm { + background-color: var(--surface); + padding: 0; + margin: 0; +} + +WlPage { + background-color: var(--surface); + padding: 3mm 3.6mm 4mm 3.6mm; + margin: 0; +} + +/* The coloured top of the welcome screen. */ +WlHero { + background-color: var(--brand); + padding: 14mm 4.4mm 8mm 4.4mm; + margin: 0; +} + +WlHeroTitle { + color: var(--brand-contrast); + font-family: "native:MainBold"; + font-size: 7mm; + padding: 2.4mm 0 0.8mm 0; + margin: 0; + background-color: transparent; +} + +WlHeroText { + color: var(--brand-contrast); + font-family: "native:MainRegular"; + font-size: 2.9mm; + padding: 0; + margin: 0; + background-color: transparent; +} + +WlHeroIcon { + color: var(--brand-contrast); + padding: 0; + margin: 0; + background-color: transparent; +} + +/* The panel that rises over the map. */ +WlCard { + background-color: var(--surface); + border-radius: var(--radius-sheet) var(--radius-sheet) 0 0; + padding: 3.6mm 3.6mm 4mm 3.6mm; + margin: 0; +} + +/* A panel that floats over the map with a gap around it. */ +WlFloat { + background-color: var(--surface); + border-radius: var(--radius); + padding: 2.4mm 3.2mm 2.4mm 3.2mm; + margin: 2.4mm; +} + +WlPanel { + background-color: var(--surface-alt); + border-radius: var(--radius); + padding: 2.8mm 3mm 2.8mm 3mm; + margin: 1.2mm 0 1.2mm 0; +} + +WlDivider { + background-color: var(--line); + padding: 0; + margin: 2mm 0 2mm 0; +} + +/* -------------------------------------------------------------------- text */ + +WlTitle { + color: var(--text); + font-family: "native:MainBold"; + font-size: 4.4mm; + padding: 0.6mm 0 1mm 0; + margin: 0; + background-color: transparent; +} + +WlHeading { + color: var(--text); + font-family: "native:MainBold"; + font-size: 3.5mm; + padding: 0.4mm 0 0.4mm 0; + margin: 0; + background-color: transparent; +} + +WlText { + color: var(--text); + font-family: "native:MainRegular"; + font-size: 2.75mm; + padding: 0.4mm 0 0.4mm 0; + margin: 0; + background-color: transparent; +} + +WlMuted { + color: var(--text-muted); + font-family: "native:MainRegular"; + font-size: 2.5mm; + padding: 0.4mm 0 0.4mm 0; + margin: 0; + background-color: transparent; +} + +WlLabel { + color: var(--text-muted); + font-family: "native:MainBold"; + font-size: 2.1mm; + padding: 2.6mm 0 1mm 0.3mm; + margin: 0; + background-color: transparent; +} + +WlPrice { + color: var(--text); + font-family: "native:MainBold"; + font-size: 4.4mm; + padding: 0; + margin: 0; + text-align: right; + background-color: transparent; +} + +/* The same, where it is the headline of a sheet: it starts where the + sheet's words start. */ +WlFare { + color: var(--text); + font-family: "native:MainBold"; + font-size: 5.2mm; + padding: 0; + margin: 0; + text-align: left; + background-color: transparent; +} + +WlError { + color: var(--danger); + font-family: "native:MainRegular"; + font-size: 2.5mm; + padding: 1mm 0 1mm 0; + margin: 0; + background-color: transparent; +} + +WlNotice { + color: var(--text); + background-color: var(--surface-alt); + border-radius: var(--radius); + font-family: "native:MainRegular"; + font-size: 2.5mm; + padding: 2.4mm 3mm 2.4mm 3mm; + margin: 2mm 0 1mm 0; +} + +/* ----------------------------------------------------------------- buttons */ + +WlPrimary { + color: var(--brand-contrast); + background-color: var(--brand); + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 2.9mm; + text-align: center; + padding: 2.75mm 4mm 2.75mm 4mm; + margin: 1.6mm 0 0.8mm 0; +} + +WlPrimary.pressed { + background-color: var(--brand-pressed); +} + +WlPrimary.disabled { + color: var(--text-muted); + background-color: var(--surface-press); +} + +WlSecondary { + color: var(--text); + background-color: var(--surface-alt); + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 2.9mm; + text-align: center; + padding: 2.75mm 4mm 2.75mm 4mm; + margin: 1.6mm 0 0.8mm 0; +} + +WlSecondary.pressed { + background-color: var(--surface-press); +} + +WlDanger { + color: var(--danger); + background-color: var(--danger-tint); + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 2.9mm; + text-align: center; + padding: 2.75mm 4mm 2.75mm 4mm; + margin: 1.6mm 0 0.8mm 0; +} + +WlDanger.pressed { + background-color: var(--surface-press); +} + +WlLink { + color: var(--brand); + background-color: transparent; + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 2.75mm; + text-align: center; + padding: 2.4mm 2mm 2.4mm 2mm; + margin: 0; +} + +WlSecondary.disabled, WlDanger.disabled, WlLink.disabled { + color: var(--line-strong); +} + +WlLink.pressed { + color: var(--brand-pressed); + background-color: var(--brand-tint); +} + +/* The round buttons that sit on the map. */ +WlMapButton { + color: var(--text); + background: cn1-round-border; + background-color: var(--surface); + cn1-box-shadow-spread: 0.5mm; + cn1-box-shadow-blur: 1.6mm; + cn1-box-shadow-h: 0.5; + cn1-box-shadow-v: 0.7; + cn1-box-shadow-color: var(--shadow); + padding: 2.4mm; + margin: 1.2mm 2.4mm 1.2mm 2.4mm; +} + +WlMapButton.pressed { + background-color: var(--surface-alt); +} + +/* The same button on a bar: flat, because nothing on a bar floats. */ +WlBarButton { + color: var(--text); + background: cn1-round-border; + background-color: var(--surface); + padding: 2.4mm; + margin: 0.6mm 0.6mm 0.6mm 1.2mm; +} + +WlBarButton.pressed { + background-color: var(--surface-alt); +} + +/* "Where to?" -- looks like a field, behaves like a button. */ +WlSearchBar { + color: var(--text); + background-color: var(--surface-alt); + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 3.1mm; + text-align: left; + padding: 2.9mm 3.2mm 2.9mm 3.2mm; + margin: 0.8mm 0 0.8mm 0; +} + +WlSearchBar.pressed { + background-color: var(--surface-press); +} + +/* ------------------------------------------------------------------ fields */ + +WlField { + color: var(--text); + background-color: var(--surface-alt); + border-radius: var(--radius); + font-family: "native:MainRegular"; + font-size: 2.9mm; + padding: 2.7mm 3mm 2.7mm 3mm; + margin: 0; +} + +/* The field being typed in says so. */ +WlField.selected { + background-color: var(--brand-tint); +} + +WlField.disabled { + color: var(--text-muted); +} + +WlFieldHint { + color: var(--text-muted); + font-family: "native:MainRegular"; + font-size: 2.9mm; + padding: 2.7mm 3mm 2.7mm 3mm; + margin: 0; + background-color: transparent; +} + +/* ------------------------------------------------------------------- lists */ + +WlRow { + background-color: var(--surface); + border-bottom: 1px solid var(--line); + padding: 2.6mm 0 2.6mm 0; + margin: 0; +} + +WlRow.pressed { + background-color: var(--surface-alt); +} + +WlRowIcon { + color: var(--text-muted); + padding: 0 3mm 0 0.4mm; + margin: 0; + background-color: transparent; +} + +WlRowTitle { + color: var(--text); + font-family: "native:MainBold"; + font-size: 2.75mm; + padding: 0; + margin: 0; + background-color: transparent; +} + +WlRowDetail { + color: var(--text-muted); + font-family: "native:MainRegular"; + font-size: 2.4mm; + padding: 0.3mm 0 0 0; + margin: 0; + background-color: transparent; +} + +WlRowValue { + color: var(--text); + font-family: "native:MainBold"; + font-size: 2.75mm; + text-align: right; + padding: 0 0 0 2mm; + margin: 0; + background-color: transparent; +} + +/* ------------------------------------------------------------------ badges */ + +WlBadge { + color: var(--text-muted); + background: cn1-pill-border; + background-color: var(--surface-alt); + font-family: "native:MainBold"; + font-size: 2.05mm; + padding: 0.7mm 2mm 0.7mm 2mm; + margin: 0 0 0 2mm; +} + +WlBadgeGood { + cn1-derive: WlBadge; + color: var(--success); + background-color: var(--success-tint); +} + +WlBadgeWarn { + cn1-derive: WlBadge; + color: var(--warning); + background-color: var(--warning-tint); +} + +WlBadgeBad { + cn1-derive: WlBadge; + color: var(--danger); + background-color: var(--danger-tint); +} + +/* ------------------------------------------------------------------- admin */ + +WlStat { + background-color: var(--surface-alt); + border-radius: var(--radius); + padding: 2.6mm 2.6mm 2.6mm 2.6mm; + margin: 0.8mm; +} + +WlStatValue { + color: var(--text); + font-family: "native:MainBold"; + font-size: 3.9mm; + padding: 0; + margin: 0; + background-color: transparent; +} + +WlStatLabel { + color: var(--text-muted); + font-family: "native:MainRegular"; + font-size: 2.3mm; + padding: 0.3mm 0 0 0; + margin: 0; + background-color: transparent; +} + +/* --------------------------------------------------------------- title bar */ + +/* The strip the clock and the battery are drawn over. */ +StatusBar { + background-color: var(--surface); + padding: 0; + margin: 0; +} + +Toolbar { + background-color: var(--surface); + border-bottom: 1px solid var(--line); + padding: 0.6mm 0 0.6mm 0; + margin: 0; +} + +Title { + color: var(--text); + font-family: "native:MainBold"; + font-size: 3.05mm; + background-color: transparent; +} + +TitleCommand { + color: var(--text); + background-color: transparent; +} + +BackCommand { + color: var(--text); + background-color: transparent; +} + +/* --------------------------------------------------------------------------- + * MAP. A marker is an icon drawn in its style's colour, and the route is a + * line in WlRoute's. + */ +WlMarkerPickup { + color: var(--success); + background-color: var(--map-edge); +} + +WlMarkerDropoff { + color: var(--danger); + background-color: var(--map-edge); +} + +WlMarkerCar { + color: var(--car); + background-color: var(--map-edge); +} + +WlRoute { + color: var(--brand); + background-color: var(--map-edge); +} + +WlStar { + color: var(--warning); + background-color: transparent; + padding: 1.2mm; + margin: 0.6mm 0 0.6mm 0; +} + +WlStar.pressed { + color: var(--text-muted); +} + +/* The switch: the thumb is the colour, the track the background. */ +Switch { + color: #ffffff; + background-color: var(--line-strong); + font-family: "native:MainRegular"; + font-size: 2.5mm; + padding: 0.6mm 0 0.6mm 2mm; + margin: 0; +} + +Switch.selected { + color: #ffffff; + background-color: var(--brand); +} + +Switch.disabled { + color: var(--surface-alt); + background-color: var(--line); +} + +/* --------------------------------------------------------------------------- + * OVER THE MAP. The clear strip the status bar is drawn in, which the map + * runs under, the bar of floating controls, + * the sheet at the bottom and what goes in each. + */ +WlStatusShade { + background-color: transparent; + padding: 0; + margin: 0; +} + +WlMapBar { + background-color: transparent; + padding: 1.2mm 2.4mm 0 0; + margin: 0; +} + +/* "Where to?" -- a card that floats over the map and behaves like a button. */ +WlSearchCard { + color: var(--text); + background-color: var(--surface); + border-radius: var(--radius); + cn1-box-shadow-spread: 0.5mm; + cn1-box-shadow-blur: 0; + cn1-box-shadow-h: 0.5; + cn1-box-shadow-v: 0.7; + cn1-box-shadow-color: var(--shadow-card); + font-family: "native:MainBold"; + font-size: 3.1mm; + text-align: left; + padding: 3mm 3.2mm 3mm 3.2mm; + margin: 0.5mm 0 0.5mm 0; +} + +WlSearchCard.pressed { + background-color: var(--surface-alt); +} + +/* From where to where, once there is somewhere to go. */ +WlTripCard { + cn1-derive: WlSearchCard; + padding: 1.9mm 3.2mm 1.9mm 3.2mm; +} + +WlTripStop { + color: var(--text); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.6mm; + text-align: left; + padding: 0.7mm 0 0.7mm 0; + margin: 0; +} + +/* The courier's card at the top: on line or off, and today's takings. */ +WlStatusCard { + cn1-derive: WlSearchCard; + padding: 1.7mm 3.2mm 1.7mm 2mm; +} + +WlStatusValue { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.3mm; + text-align: right; + padding: 0; + margin: 0; +} + +WlStatusDetail { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.15mm; + text-align: right; + padding: 0; + margin: 0; +} + +WlMapTitle { + cn1-derive: WlSearchCard; + font-size: 3.05mm; + padding: 3mm 3.2mm 3mm 3.2mm; +} + +/* One short fact, in a pill under the bar. */ +WlChip { + color: var(--text); + background: cn1-pill-border; + background-color: var(--surface); + cn1-box-shadow-spread: 0.4mm; + cn1-box-shadow-blur: 1.4mm; + cn1-box-shadow-h: 0.5; + cn1-box-shadow-v: 0.7; + cn1-box-shadow-color: var(--shadow); + font-family: "native:MainBold"; + font-size: 2.4mm; + padding: 1.4mm 3mm 1.4mm 2.6mm; + margin: 1.2mm 2mm 1mm 2mm; +} + +WlSheet { + background-color: var(--surface); + border-radius: var(--radius-sheet) var(--radius-sheet) 0 0; + padding: 0.4mm 3.6mm 3.2mm 3.6mm; + margin: 0; +} + +/* The handle of the sheet: the colour is the bar's. */ +WlGrip { + color: var(--line-strong); + background-color: transparent; + padding: 1.6mm 0 1.8mm 0; + margin: 0; +} + +/* The countdown: the colour is the time left, the background the time gone. */ +WlRingTrack { + color: var(--line); + background-color: transparent; +} + +WlRing { + color: var(--brand); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.3mm; + padding: 0.8mm; + margin: 0 0 0 2.4mm; +} + +WlPulse { + color: var(--brand); + background-color: transparent; + padding: 0.8mm; + margin: 0.8mm 0 0.8mm 0; +} + +/* Home and Work, side by side on the idle sheet. */ +WlShortcut { + color: var(--text); + background-color: var(--surface-alt); + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 2.75mm; + text-align: left; + padding: 2.6mm 3mm 2.6mm 3mm; + margin: 2mm 0.8mm 0.8mm 0.8mm; +} + +WlShortcut.pressed { + background-color: var(--surface-press); +} + +WlCourierCard { + background-color: var(--surface-alt); + border-radius: var(--radius); + padding: 2.8mm 3mm 2.8mm 3mm; + margin: 2mm 0 1mm 0; +} + +WlAvatar { + color: var(--brand-contrast); + background: cn1-round-border; + background-color: var(--brand); + font-family: "native:MainBold"; + font-size: 2.9mm; + text-align: center; + padding: 2.3mm; + margin: 0 3mm 0 0; +} + +/* A number plate reads as one, so it is boxed like one. */ +WlPlate { + color: var(--text); + background-color: var(--surface); + border: 1px solid var(--line-strong); + border-radius: var(--radius-small); + font-family: "native:MainBold"; + font-size: 2.5mm; + text-align: center; + padding: 0.9mm 2mm 0.9mm 2mm; + margin: 1mm 0 0 0; +} + +WlActions { + background-color: transparent; + padding: 0; + margin: 0.8mm 0 0 0; +} + +/* Call, message, share: an icon with a word under it. */ +WlAction { + color: var(--text); + background-color: transparent; + border-radius: var(--radius); + font-family: "native:MainRegular"; + font-size: 2.2mm; + text-align: center; + padding: 1.8mm 0 1.8mm 0; + margin: 0; +} + +WlAction.pressed { + color: var(--brand); + background-color: var(--surface-alt); +} + +WlActionDanger { + cn1-derive: WlAction; + color: var(--danger); +} + +WlActionDanger.pressed { + color: var(--danger); + background-color: var(--danger-tint); +} + +/* The one large round control an off-line courier has. */ +WlGo { + color: var(--brand-contrast); + background: cn1-round-border; + background-color: var(--brand); + cn1-box-shadow-spread: 0.6mm; + cn1-box-shadow-blur: 2mm; + cn1-box-shadow-h: 0.5; + cn1-box-shadow-v: 0.7; + cn1-box-shadow-color: var(--shadow); + font-family: "native:MainBold"; + font-size: 4.2mm; + text-align: center; + padding: 5.6mm; + margin: 1.6mm; +} + +WlGo.pressed { + background-color: var(--brand-pressed); +} + +/* ------------------------------------------------------ records and charts */ + +/* A list that fills a screen: rows from edge to edge, each with the page's + margin inside it. */ +WlList { + background-color: var(--surface); + padding: 0 3.6mm 3.2mm 3.6mm; + margin: 0; +} +/* One line of a receipt: what, and how much. */ +WlFact { + background-color: transparent; + padding: 1.1mm 0 1.1mm 0; + margin: 0; +} +WlFactLabel { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.6mm; + text-align: left; + padding: 0; + margin: 0; +} +WlFactValue { + color: var(--text); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.6mm; + text-align: right; + padding: 0 0 0 3mm; + margin: 0; +} +/* The line the others add up to. */ +WlFactTotal { + cn1-derive: WlFactValue; + font-family: "native:MainBold"; + font-size: 3.3mm; +} +/* The one thing a screen is about, said large at its top. */ +WlBanner { + background-color: transparent; + padding: 5mm 3mm 3mm 3mm; + margin: 0; +} +WlBannerIcon { + color: var(--brand); + background: cn1-round-border; + background-color: var(--brand-tint); + padding: 3.4mm; + margin: 0 0 2.4mm 0; +} +WlBannerIconGood { + cn1-derive: WlBannerIcon; + color: var(--success); + background-color: var(--success-tint); +} +WlBannerIconWarn { + cn1-derive: WlBannerIcon; + color: var(--warning); + background-color: var(--warning-tint); +} +WlBannerIconBad { + cn1-derive: WlBannerIcon; + color: var(--danger); + background-color: var(--danger-tint); +} +WlBannerTitle { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.5mm; + text-align: center; + padding: 0.4mm 0 0.4mm 0; + margin: 0; +} +WlBannerText { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.6mm; + text-align: center; + padding: 0.4mm 2mm 0.4mm 2mm; + margin: 0; +} +/* The amount a receipt is for. */ +WlAmount { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 7mm; + text-align: center; + padding: 1mm 0 0.4mm 0; + margin: 0; +} +/* A group of rows under one heading. */ +WlGroup { + background-color: transparent; + padding: 0; + margin: 0 0 0.8mm 0; +} +/* A chart or a breakdown, boxed with its title. */ +WlTile { + background-color: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 3mm 3mm 3mm 3mm; + margin: 0.8mm 0.8mm 1.6mm 0.8mm; +} +/* A bar chart: the colour is the bars'. */ +WlChart { + color: var(--brand); + background-color: transparent; + padding: 1.6mm 0 0 0; + margin: 0; +} +WlChartPeak { + color: var(--brand-pressed); + background-color: transparent; +} +WlChartGrid { + color: var(--line); + background-color: transparent; +} +WlChartAxis { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2mm; + padding: 0.8mm 0 0 0; + margin: 0; +} +/* The number a chart adds up to, over the chart. */ +WlChartValue { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 4.2mm; + text-align: left; + padding: 0; + margin: 0; +} +/* A share along a track: the colour is the share, the background the track. */ +WlMeter { + color: var(--brand); + background-color: transparent; + padding: 0.8mm 0 1.6mm 0; + margin: 0; +} +/* Something waiting for the admin, at the top of the dashboard. */ +WlAlert { + color: var(--warning); + background-color: var(--warning-tint); + border-radius: var(--radius); + font-family: "native:MainBold"; + font-size: 2.6mm; + text-align: left; + padding: 2.6mm 3mm 2.6mm 3mm; + margin: 0.8mm; +} +WlAlert.pressed { + background-color: var(--surface-press); +} + +/* ----------------------------------------------------------------- choosing */ + +/* One of several things to pick from, of which one is picked: a kind of car, + a way to pay. */ +WlOption { + background-color: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 2.4mm 3mm 2.4mm 3mm; + margin: 0.8mm 0 0.8mm 0; +} +WlOption.pressed { + background-color: var(--surface-alt); +} +WlOptionPicked { + cn1-derive: WlOption; + background-color: var(--brand-tint); + border: 2px solid var(--brand); +} +WlOptionIcon { + color: var(--text); + background-color: transparent; + padding: 0 2.4mm 0 0; + margin: 0; +} +/* A payment card, drawn as one. */ +WlCardFace { + background-color: var(--brand); + border-radius: var(--radius); + padding: 4mm 4mm 3.4mm 4mm; + margin: 1mm 0 2.4mm 0; +} +WlCardBrand { + color: var(--brand-contrast); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 2.9mm; + text-align: right; + padding: 0; + margin: 0; +} +WlCardNumber { + color: var(--brand-contrast); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.7mm; + text-align: left; + padding: 4.8mm 0 2.4mm 0; + margin: 0; +} +WlCardSmall { + color: var(--brand-contrast); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.3mm; + padding: 0; + margin: 0; +} +/* Two fields side by side: the space between them is theirs. */ +WlPairLeft { + background-color: transparent; + padding: 0 1.2mm 0 0; + margin: 0; +} +WlPairRight { + background-color: transparent; + padding: 0 0 0 1.2mm; + margin: 0; +} +/* A picture of a document, small, beside what it is. */ +WlThumb { + color: var(--text-muted); + background-color: var(--surface-alt); + border-radius: var(--radius-small); + padding: 2mm; + margin: 0 3mm 0 0; +} +/* The same picture, filling the screen. */ +WlPicture { + background-color: var(--scrim); + padding: 0; + margin: 0; +} +/* How far through the steps of a form: "Step 2 of 5". */ +WlStepLabel { + color: var(--brand); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 2.2mm; + text-align: left; + padding: 0 0 0.4mm 0; + margin: 0; +} +/* What is fixed to the foot of a form of several steps. */ +WlFooter { + background-color: var(--surface); + border-top: 1px solid var(--line); + padding: 1mm 3.6mm 1.6mm 3.6mm; + margin: 0; +} + +/* ---------------------------------------------------------------- the menu */ + +/* Dimmed by the colour's own alpha. `opacity` would not do it: a style's + opacity fades what a component draws over its background, and the + background itself is filled at the background's transparency -- so an opaque + black with an opacity is still a black screen. */ +WlScrim { + background-color: rgba(0, 0, 0, 0.45); + padding: 0; + margin: 0; +} + +WlMenu { + background-color: var(--surface); + padding: 0; + margin: 0; +} + +WlMenuTop { + background-color: var(--surface); + border-bottom: 1px solid var(--line); + padding: 5mm 4mm 3.6mm 4mm; + margin: 0; +} + +WlMenuName { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.5mm; + text-align: left; + padding: 0; + margin: 0; +} + +WlMenuDetail { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.4mm; + text-align: left; + padding: 0.2mm 0 0 0; + margin: 0; +} + +/* Which of the three apps this is. */ +WlModeBadge { + color: var(--brand); + background: cn1-pill-border; + background-color: var(--brand-tint); + font-family: "native:MainBold"; + font-size: 2.1mm; + text-align: center; + padding: 0.8mm 2.4mm 0.8mm 2.4mm; + margin: 2.4mm 0 0 0; +} + +WlMenuLabel { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 2.1mm; + text-align: left; + padding: 3.6mm 4mm 1mm 4mm; + margin: 0; +} + +WlMenuItem { + color: var(--text); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.9mm; + text-align: left; + padding: 2.6mm 4mm 2.6mm 4mm; + margin: 0; +} + +WlMenuItem.pressed { + background-color: var(--surface-alt); +} + +WlMenuBottom { + background-color: transparent; + border-top: 1px solid var(--line); + padding: 0.8mm 0 1.6mm 0; + margin: 0; +} + +/* The top of a home screen that has no map: the menu, and what this is. */ +WlHomeBar { + background-color: var(--surface); + border-bottom: 1px solid var(--line); + padding: 0.6mm 3.6mm 0.6mm 0; + margin: 0; +} + +/* ---------------------------------------------------------------- settings */ + +WlSection { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.1mm; + text-align: left; + padding: 5mm 0 0.8mm 0; + margin: 0; +} + +/* A few options side by side, one of them chosen. */ +WlSegments { + background-color: var(--surface-alt); + border-radius: var(--radius); + padding: 0.5mm; + margin: 0.8mm 0 1.6mm 0; +} + +WlSegment { + color: var(--text-muted); + background-color: transparent; + border-radius: 1.9mm; + font-family: "native:MainBold"; + font-size: 2.5mm; + text-align: center; + padding: 2mm 0.8mm 2mm 0.8mm; + margin: 0.3mm; +} + +WlSegment.selected { + color: var(--text); + background-color: var(--surface); +} + +/* A toggle that is on wears the pressed style, so the two say the same. */ +WlSegment.pressed { + color: var(--text); + background-color: var(--surface); +} + + +WlSegment.disabled { + color: var(--line-strong); +} + +/* The outline of rows that are on their way: the colour is the bars'. */ +WlSkeleton { + color: var(--surface-alt); + background-color: transparent; + padding: 1mm 0 0 0; + margin: 0; +} + +/* "Pull to refresh", over a list being pulled. */ +PullToRefresh { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.3mm; + text-align: center; + padding: 1.6mm; + margin: 0; +} + +/* What the app says in passing, over the foot of the screen. */ +ToastBar { + background-color: var(--text); + border-radius: var(--radius); + padding: 2.4mm 3mm 2.4mm 3mm; + margin: 2.4mm 3.6mm 3.2mm 3.6mm; +} + +ToastBarMessage { + color: var(--surface); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.5mm; + padding: 0; + margin: 0; +} + +/* ------------------------------------------------- the admin console, wide */ + +/* On a tablet on its side, a desktop or a browser the console is three + columns: where to go, a list, and what was picked from it. */ +WlSide { + background-color: var(--surface-alt); + border-right: 1px solid var(--line); + padding: 2.4mm 1.6mm 2.4mm 1.6mm; + margin: 0; +} + +WlSideName { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.05mm; + padding: 0.8mm 2.4mm 0 2.4mm; + margin: 0; +} + +WlSideDetail { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainRegular"; + font-size: 2.3mm; + padding: 0 2.4mm 2.4mm 2.4mm; + margin: 0; +} + +WlSideLabel { + color: var(--text-muted); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 2.15mm; + padding: 3.2mm 2.4mm 0.8mm 2.4mm; + margin: 0; +} + +WlSideItem { + color: var(--text); + background-color: transparent; + border-radius: var(--radius-small); + font-family: "native:MainRegular"; + font-size: 2.8mm; + text-align: left; + padding: 1.9mm 2.4mm 1.9mm 2.4mm; + margin: 0.2mm 0 0.2mm 0; +} + +WlSideItem.pressed { + background-color: var(--line); +} + +WlSidePicked { + cn1-derive: WlSideItem; + color: var(--brand-contrast); + background-color: var(--brand); + font-family: "native:MainBold"; +} + +WlMaster { + background-color: var(--surface); + border-right: 1px solid var(--line); + padding: 0; + margin: 0; +} + +WlPaneBar { + background-color: var(--surface); + border-bottom: 1px solid var(--line); + padding: 1.2mm 4mm 1.2mm 4mm; + margin: 0; +} + +WlPaneTitle { + color: var(--text); + background-color: transparent; + font-family: "native:MainBold"; + font-size: 3.5mm; + padding: 1.6mm 0 1.6mm 0; + margin: 0; +} + +/* The row of a list whose detail is open beside it. */ +WlRowPicked { + cn1-derive: WlRow; + background-color: var(--surface-alt); +} + +/* + * THE DESKTOP'S OWN LOOK. On macOS, Windows and Linux, and in a desktop + * browser, the platform's theme is under this one (the nativeTheme=native build + * hint), and the admin console wears it: Layouts.dress() gives each component + * the WlDesk twin of its style. A twin derives from the platform's control + * where there is one -- a button, a field, a segmented control, a group box -- + * and takes its colours from the palette every desktop theme declares, so the + * same rules are Aqua on a Mac, Fluent on Windows and Adwaita on Linux. Nothing + * here names the brand; the accent is the platform's. + * + * The values in WlDeskPalette are only what the compiler needs to resolve + * var(): each use is bound to the name, and the installed theme's own value + * replaces it when the app runs. They are declared outside #Constants so that + * they are not exported over the theme's. + */ +WlDeskPalette { + --window-bg-color: #ececec; + --window-bg-color-dark: #1e1e1e; + --view-bg-color: #ffffff; + --view-bg-color-dark: #1e1e1e; + --control-bg-color: #ffffff; + --control-bg-color-dark: #2c2c2c; + --headerbar-bg-color: #e8e8e8; + --headerbar-bg-color-dark: #2a2a2a; + --text-color: #1a1a1a; + --text-color-dark: #ffffff; + --text-secondary-color: #5d5d5d; + --text-secondary-color-dark: #c0c0c0; + --separator-color: #d8d8d8; + --separator-color-dark: #3a3a3a; + --accent-color: #007aff; + --accent-color-dark: #0a84ff; + --accent-fg-color: #ffffff; + --accent-fg-color-dark: #ffffff; + background-color: transparent; + padding: 0; + margin: 0; +} + +WlDeskForm { background-color: var(--window-bg-color); padding: 0; margin: 0; } +WlDeskPage { background-color: var(--window-bg-color); padding: 3mm 5mm 4mm 5mm; margin: 0; } +WlDeskList { background-color: var(--view-bg-color); padding: 0 3mm 2mm 3mm; margin: 0; } +WlDeskMaster { background-color: var(--view-bg-color); border-right: 1px solid #c6c6c8; padding: 0; margin: 0; } +WlDeskHomeBar { background-color: var(--headerbar-bg-color); border-bottom: 1px solid #c6c6c8; padding: 1mm 4mm 1mm 0; margin: 0; } +WlDeskPaneBar { background-color: var(--window-bg-color); border-bottom: 1px solid #c6c6c8; padding: 1mm 5mm 1mm 5mm; margin: 0; } +WlDeskPaneTitle { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 3.2mm; padding: 1.6mm 0 1.6mm 0; margin: 0; } +WlDeskBarButton { cn1-derive: Button; padding: 1.2mm; margin: 0 2mm 0 0; } + +WlDeskSide { background-color: var(--headerbar-bg-color); border-right: 1px solid #c6c6c8; padding: 2.5mm 2mm 2.5mm 2mm; margin: 0; } +WlDeskSideName { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 2.7mm; padding: 1mm 2.6mm 0 2.6mm; margin: 0; } +WlDeskSideDetail { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.25mm; padding: 0 2.6mm 2mm 2.6mm; margin: 0; } +WlDeskSideLabel { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainBold"; font-size: 2.15mm; padding: 3mm 2.6mm 1mm 2.6mm; margin: 0; } +WlDeskSideItem { color: var(--text-color); background-color: transparent; border-radius: 1.4mm; font-family: "native:MainRegular"; font-size: 2.65mm; text-align: left; padding: 1.6mm 2.6mm 1.6mm 2.6mm; margin: 0.2mm 0 0.2mm 0; } +WlDeskSideItem.pressed { background-color: var(--separator-color); } +WlDeskSidePicked { cn1-derive: WlDeskSideItem; color: var(--accent-fg-color); background-color: var(--accent-color); } + +WlDeskPrimary { cn1-derive: RaisedButton; text-align: center; margin: 2mm 0 1mm 0; } +WlDeskSecondary { cn1-derive: Button; text-align: center; margin: 1.5mm 0 1mm 0; } +WlDeskDanger { cn1-derive: Button; color: var(--danger); text-align: center; margin: 1.5mm 0 1mm 0; } +WlDeskLink { cn1-derive: Link; padding: 1.6mm 0 1.6mm 0; } +WlDeskField { cn1-derive: TextField; margin: 0; } +WlDeskFieldHint { cn1-derive: TextHint; } +WlDeskSegments { cn1-derive: TabsContainer; border-radius: 1.6mm; padding: 0.4mm; margin: 1mm 0 2mm 0; } +/* Four of these share the master pane of the console, which is narrow: the + platform's own tab padding leaves "Cancelled" no room and it was drawn cut + off at both ends. */ +WlDeskSegment { cn1-derive: Tab; text-align: center; padding: 1.4mm 0.4mm 1.4mm 0.4mm; margin: 0; } +/* Something waiting is the one thing on the console that is not the + platform's grey: it wears the warning tint the phone's does. */ +WlDeskAlert { color: var(--warning); background-color: var(--warning-tint); border-radius: 1.6mm; font-family: "native:MainBold"; font-size: 2.6mm; text-align: left; padding: 2mm 3mm 2mm 3mm; margin: 1mm 0 1mm 0; } +WlDeskAlert.pressed { color: var(--warning); background-color: var(--warning-tint); border-radius: 1.6mm; font-family: "native:MainBold"; font-size: 2.6mm; text-align: left; padding: 2mm 3mm 2mm 3mm; margin: 1mm 0 1mm 0; } + +WlDeskRow { background-color: transparent; border-bottom: 1px solid #d8d8d8; padding: 2mm 1mm 2mm 1mm; margin: 0; } +WlDeskRow.pressed { background-color: var(--separator-color); } +WlDeskRowPicked { background-color: var(--separator-color); border-bottom: 1px solid #d8d8d8; border-radius: 1.4mm; padding: 2mm 1mm 2mm 1mm; margin: 0; } +WlDeskRowIcon { color: var(--text-secondary-color); background-color: transparent; padding: 0 2.6mm 0 0; margin: 0; } +WlDeskRowTitle { color: var(--text-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.7mm; padding: 0; margin: 0; } +WlDeskRowDetail { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.3mm; padding: 0.4mm 0 0 0; margin: 0; } +WlDeskRowValue { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 2.65mm; text-align: right; padding: 0; margin: 0; } + +WlDeskSection { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 2.8mm; text-align: left; padding: 4mm 0 1.2mm 0; margin: 0; } +WlDeskTitle { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 4.4mm; padding: 0 0 2mm 0; margin: 0; } +WlDeskHeading { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 3.2mm; padding: 0; margin: 0; } +WlDeskText { color: var(--text-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.65mm; padding: 1mm 0 1mm 0; margin: 0; } +WlDeskMuted { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.4mm; padding: 0.5mm 0 0.5mm 0; margin: 0; } +WlDeskLabel { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.4mm; padding: 2.4mm 0 0.8mm 0; margin: 0; } +WlDeskError { color: var(--danger); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.5mm; padding: 1mm 0 1mm 0; margin: 0; } +WlDeskNotice { cn1-derive: GroupBox; color: var(--text-color); font-family: "native:MainRegular"; font-size: 2.55mm; padding: 2.4mm; margin: 2mm 0 1mm 0; } +WlDeskPanel { cn1-derive: GroupBox; padding: 2.6mm 3mm 2.6mm 3mm; margin: 1mm 0 1mm 0; } +WlDeskFact { background-color: transparent; padding: 1.2mm 0 1.2mm 0; margin: 0; } +WlDeskFactLabel { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.55mm; padding: 0; margin: 0; } +WlDeskFactValue { color: var(--text-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.55mm; text-align: right; padding: 0; margin: 0; } +WlDeskFactTotal { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 2.7mm; text-align: right; padding: 0; margin: 0; } +WlDeskAmount { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 6.4mm; text-align: center; padding: 2mm 0 1mm 0; margin: 0; } +WlDeskBannerIcon { color: var(--text-secondary-color); background-color: transparent; padding: 6mm 0 2mm 0; margin: 0; } +WlDeskBannerTitle { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 3.05mm; text-align: center; padding: 0; margin: 0; } +WlDeskBannerText { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.5mm; text-align: center; padding: 1mm 0 0 0; margin: 0; } + +/* The numbers and the charts have no control on any desktop: they keep their + shape, in a group box, in the platform's colours. */ +WlDeskTile { cn1-derive: GroupBox; padding: 3mm 3.4mm 3mm 3.4mm; margin: 1.2mm; } +WlDeskStat { cn1-derive: GroupBox; padding: 2.6mm 3mm 2.6mm 3mm; margin: 1.2mm; } +WlDeskStatValue { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 4.3mm; padding: 0; margin: 0; } +WlDeskStatLabel { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.3mm; padding: 0.6mm 0 0 0; margin: 0; } +WlDeskChartValue { color: var(--text-color); background-color: transparent; font-family: "native:MainBold"; font-size: 4.3mm; padding: 0.6mm 0 0 0; margin: 0; } +WlDeskChartAxis { color: var(--text-secondary-color); background-color: transparent; font-family: "native:MainRegular"; font-size: 2.1mm; padding: 0.6mm 0 0 0; margin: 0; } + + +/* + * DARK MODE. The same rules, with the second value of each colour. A rule added + * above that names a colour needs its line here. + */ +@media (prefers-color-scheme: dark) { + WlForm { background-color: var(--surface-dark); } + WlPage { background-color: var(--surface-dark); } + WlHero { background-color: var(--brand-dark); } + WlHeroTitle { color: var(--brand-contrast-dark); } + WlHeroText { color: var(--brand-contrast-dark); } + WlHeroIcon { color: var(--brand-contrast-dark); } + WlCard { background-color: var(--surface-dark); } + WlFloat { background-color: var(--surface-dark); } + WlPanel { background-color: var(--surface-alt-dark); } + WlDivider { background-color: var(--line-dark); } + WlTitle { color: var(--text-dark); } + WlHeading { color: var(--text-dark); } + WlText { color: var(--text-dark); } + WlMuted { color: var(--text-muted-dark); } + WlLabel { color: var(--text-muted-dark); } + WlPrice { color: var(--text-dark); } + WlFare { color: var(--text-dark); } + WlError { color: var(--danger-dark); } + WlNotice { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlPrimary { color: var(--brand-contrast-dark); background-color: var(--brand-dark); } + WlPrimary.pressed { background-color: var(--brand-pressed-dark); } + WlPrimary.disabled { color: var(--text-muted-dark); background-color: var(--surface-press-dark); } + WlSecondary { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlSecondary.pressed { background-color: var(--surface-press-dark); } + WlDanger { color: var(--danger-dark); background-color: var(--danger-tint-dark); } + WlDanger.pressed { background-color: var(--surface-press-dark); } + WlLink { color: var(--brand-dark); } + WlLink.pressed { color: var(--brand-pressed-dark); background-color: var(--brand-tint-dark); } + WlMapButton { background: cn1-round-border; color: var(--text-dark); background-color: var(--surface-dark); } + WlMapButton.pressed { background: cn1-round-border; background-color: var(--surface-alt-dark); } + WlBarButton { background: cn1-round-border; color: var(--text-dark); background-color: var(--surface-dark); } + WlBarButton.pressed { background: cn1-round-border; background-color: var(--surface-alt-dark); } + WlSearchBar { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlSearchBar.pressed { background-color: var(--surface-press-dark); } + WlField { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlFieldHint { color: var(--text-muted-dark); } + WlRow { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } + WlRow.pressed { background-color: var(--surface-alt-dark); } + WlRowIcon { color: var(--text-muted-dark); } + WlRowTitle { color: var(--text-dark); } + WlRowDetail { color: var(--text-muted-dark); } + WlRowValue { color: var(--text-dark); } + WlBadge { background: cn1-pill-border; color: var(--text-muted-dark); background-color: var(--surface-alt-dark); } + WlBadgeGood { background: cn1-pill-border; color: var(--success-dark); background-color: var(--success-tint-dark); } + WlBadgeWarn { background: cn1-pill-border; color: var(--warning-dark); background-color: var(--warning-tint-dark); } + WlBadgeBad { background: cn1-pill-border; color: var(--danger-dark); background-color: var(--danger-tint-dark); } + WlStat { background-color: var(--surface-alt-dark); } + WlStatValue { color: var(--text-dark); } + WlStatLabel { color: var(--text-muted-dark); } + StatusBar { background-color: var(--surface-dark); } + Toolbar { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } + Title { color: var(--text-dark); } + TitleCommand { color: var(--text-dark); } + BackCommand { color: var(--text-dark); } + WlMarkerPickup { color: var(--success-dark); background-color: var(--map-edge-dark); } + WlMarkerDropoff { color: var(--danger-dark); background-color: var(--map-edge-dark); } + WlMarkerCar { color: var(--car-dark); background-color: var(--map-edge-dark); } + WlRoute { color: var(--brand-dark); background-color: var(--map-edge-dark); } + WlSearchCard { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlSearchCard.pressed { background-color: var(--line-dark); } + WlTripCard { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlTripStop { color: var(--text-dark); } + WlStatusCard { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlStatusValue { color: var(--text-dark); } + WlStatusDetail { color: var(--text-muted-dark); } + WlMapTitle { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlChip { background: cn1-pill-border; color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlSheet { background-color: var(--surface-dark); } + WlGrip { color: var(--line-strong-dark); } + WlRing { color: var(--brand-dark); } + WlRingTrack { color: var(--line-dark); } + WlPulse { color: var(--brand-dark); } + WlShortcut { color: var(--text-dark); background-color: var(--surface-alt-dark); } + WlShortcut.pressed { background-color: var(--surface-press-dark); } + WlCourierCard { background-color: var(--surface-alt-dark); } + WlAvatar { background: cn1-round-border; color: var(--brand-contrast-dark); background-color: var(--brand-dark); } + WlPlate { color: var(--text-dark); background-color: var(--surface-dark); border: 1px solid var(--line-strong-dark); } + WlAction { color: var(--text-dark); } + WlAction.pressed { color: var(--brand-dark); background-color: var(--surface-alt-dark); } + WlActionDanger { color: var(--danger-dark); } + WlActionDanger.pressed { color: var(--danger-dark); background-color: var(--danger-tint-dark); } + WlGo { background: cn1-round-border; color: var(--brand-contrast-dark); background-color: var(--brand-dark); } + WlGo.pressed { background: cn1-round-border; background-color: var(--brand-pressed-dark); } + WlMenu { background-color: var(--surface-dark); } + WlMenuTop { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } + WlMenuName { color: var(--text-dark); } + WlMenuDetail { color: var(--text-muted-dark); } + WlModeBadge { background: cn1-pill-border; color: var(--brand-dark); background-color: var(--brand-tint-dark); } + WlMenuLabel { color: var(--text-muted-dark); } + WlMenuItem { color: var(--text-dark); } + WlMenuItem.pressed { background-color: var(--surface-alt-dark); } + WlMenuBottom { border-top: 1px solid var(--line-dark); } + WlHomeBar { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } + WlSection { color: var(--text-dark); } + WlSegments { background-color: var(--surface-alt-dark); } + WlSegment { color: var(--text-muted-dark); } + WlSegment.selected { color: var(--text-dark); background-color: var(--surface-press-dark); } + WlSegment.pressed { color: var(--text-dark); background-color: var(--surface-press-dark); } + WlList { background-color: var(--surface-dark); } + WlFactLabel { color: var(--text-muted-dark); } + WlFactValue { color: var(--text-dark); } + WlFactTotal { color: var(--text-dark); } + WlBannerIcon { background: cn1-round-border; color: var(--brand-dark); background-color: var(--brand-tint-dark); } + WlBannerIconGood { background: cn1-round-border; color: var(--success-dark); background-color: var(--success-tint-dark); } + WlBannerIconWarn { background: cn1-round-border; color: var(--warning-dark); background-color: var(--warning-tint-dark); } + WlBannerIconBad { background: cn1-round-border; color: var(--danger-dark); background-color: var(--danger-tint-dark); } + WlBannerTitle { color: var(--text-dark); } + WlBannerText { color: var(--text-muted-dark); } + WlAmount { color: var(--text-dark); } + WlTile { background-color: var(--surface-dark); border: 1px solid var(--line-dark); } + WlChart { color: var(--brand-dark); } + WlChartPeak { color: var(--brand-pressed-dark); } + WlChartGrid { color: var(--line-dark); } + WlChartAxis { color: var(--text-muted-dark); } + WlChartValue { color: var(--text-dark); } + WlMeter { color: var(--brand-dark); } + WlAlert { color: var(--warning-dark); background-color: var(--warning-tint-dark); } + WlAlert.pressed { background-color: var(--surface-press-dark); } + WlOption { background-color: var(--surface-dark); border: 1px solid var(--line-dark); } + WlOption.pressed { background-color: var(--surface-alt-dark); } + WlOptionPicked { background-color: var(--brand-tint-dark); border: 2px solid var(--brand-dark); } + WlOptionIcon { color: var(--text-dark); } + WlCardFace { background-color: var(--brand-dark); } + WlCardBrand { color: var(--brand-contrast-dark); } + WlCardNumber { color: var(--brand-contrast-dark); } + WlCardSmall { color: var(--brand-contrast-dark); } + WlThumb { color: var(--text-muted-dark); background-color: var(--surface-alt-dark); } + WlStepLabel { color: var(--brand-dark); } + WlFooter { background-color: var(--surface-dark); border-top: 1px solid var(--line-dark); } + WlStar { color: var(--warning-dark); } + WlSide { background-color: var(--surface-alt-dark); border-right: 1px solid var(--line-dark); } + WlSideName { color: var(--text-dark); } + WlSideDetail { color: var(--text-muted-dark); } + WlSideLabel { color: var(--text-muted-dark); } + WlSideItem { color: var(--text-dark); } + WlSideItem.pressed { background-color: var(--line-dark); } + WlSidePicked { color: var(--brand-contrast-dark); background-color: var(--brand-dark); } + WlMaster { background-color: var(--surface-dark); border-right: 1px solid var(--line-dark); } + WlPaneBar { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } + WlPaneTitle { color: var(--text-dark); } + WlRowPicked { background-color: var(--surface-alt-dark); } + WlSecondary.disabled, WlDanger.disabled, WlLink.disabled { color: var(--line-strong-dark); } + WlField.selected { background-color: var(--brand-tint-dark); } + WlField.disabled { color: var(--text-muted-dark); } + WlStar.pressed { color: var(--text-muted-dark); } + Switch { color: #ffffff; background-color: var(--line-strong-dark); } + Switch.selected { color: #ffffff; background-color: var(--brand-dark); } + Switch.disabled { color: var(--text-muted-dark); background-color: var(--line-dark); } + WlSegment.disabled { color: var(--line-strong-dark); } + WlSkeleton { color: var(--surface-alt-dark); } + PullToRefresh { color: var(--text-muted-dark); } + ToastBar { background-color: var(--text-dark); } + ToastBarMessage { color: var(--surface-dark); } + WlDeskForm { background-color: var(--window-bg-color-dark); } + WlDeskPage { background-color: var(--window-bg-color-dark); } + WlDeskList { background-color: var(--view-bg-color-dark); } + WlDeskMaster { background-color: var(--view-bg-color-dark); border-right: 1px solid #3a3a3a; } + WlDeskHomeBar { background-color: var(--headerbar-bg-color-dark); border-bottom: 1px solid #3a3a3a; } + WlDeskPaneBar { background-color: var(--window-bg-color-dark); border-bottom: 1px solid #3a3a3a; } + WlDeskPaneTitle { color: var(--text-color-dark); } + WlDeskSide { background-color: var(--headerbar-bg-color-dark); border-right: 1px solid #3a3a3a; } + WlDeskSideName { color: var(--text-color-dark); } + WlDeskSideDetail { color: var(--text-secondary-color-dark); } + WlDeskSideLabel { color: var(--text-secondary-color-dark); } + WlDeskSideItem { color: var(--text-color-dark); } + WlDeskSideItem.pressed { background-color: var(--separator-color-dark); } + WlDeskSidePicked { color: var(--accent-fg-color-dark); background-color: var(--accent-color-dark); } + WlDeskDanger { color: var(--danger-dark); } + WlDeskRow { border-bottom: 1px solid #3a3a3a; } + WlDeskRow.pressed { background-color: var(--separator-color-dark); } + WlDeskRowPicked { background-color: var(--separator-color-dark); border-bottom: 1px solid #3a3a3a; } + WlDeskAlert { color: var(--warning-dark); background-color: var(--warning-tint-dark); } + WlDeskAlert.pressed { color: var(--warning-dark); background-color: var(--warning-tint-dark); } + WlDeskRowIcon { color: var(--text-secondary-color-dark); } + WlDeskRowTitle { color: var(--text-color-dark); } + WlDeskRowDetail { color: var(--text-secondary-color-dark); } + WlDeskRowValue { color: var(--text-color-dark); } + WlDeskSection { color: var(--text-color-dark); } + WlDeskTitle { color: var(--text-color-dark); } + WlDeskHeading { color: var(--text-color-dark); } + WlDeskText { color: var(--text-color-dark); } + WlDeskMuted { color: var(--text-secondary-color-dark); } + WlDeskLabel { color: var(--text-secondary-color-dark); } + WlDeskError { color: var(--danger-dark); } + WlDeskNotice { color: var(--text-color-dark); } + WlDeskFactLabel { color: var(--text-secondary-color-dark); } + WlDeskFactValue { color: var(--text-color-dark); } + WlDeskFactTotal { color: var(--text-color-dark); } + WlDeskAmount { color: var(--text-color-dark); } + WlDeskBannerIcon { color: var(--text-secondary-color-dark); } + WlDeskBannerTitle { color: var(--text-color-dark); } + WlDeskBannerText { color: var(--text-secondary-color-dark); } + WlDeskStatValue { color: var(--text-color-dark); } + WlDeskStatLabel { color: var(--text-secondary-color-dark); } + WlDeskChartValue { color: var(--text-color-dark); } + WlDeskChartAxis { color: var(--text-secondary-color-dark); } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java new file mode 100644 index 00000000000..f83bd874336 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.io.Preferences; +import com.codename1.ui.CN; + +/// The few things about the app that are settings and not code. +public final class AppConfig { + /// Where the server is when nothing says otherwise: a server started on + /// this machine with `cn1:backend`. Change this to your deployment's address + /// before you ship; until then the settings screen can point a build at + /// another server, which is how a phone reaches a laptop on the same network. + public static final String DEFAULT_SERVER = "http://localhost:8080"; + /// The id the server knows this app by. The same constant is in the + /// server's `SecurityConfig`. + public static final String CLIENT_ID = "morsel-app"; + /// The address the server hands the authorization code back to. The app + /// reads the code out of the redirect itself and never opens this address, + /// so it only has to be one the server has on its list. + public static final String REDIRECT_URI = "http://127.0.0.1/callback"; + /// The path the server hands the code back on when the app runs in a + /// browser. The same constant is in the server's `SecurityConfig`. + public static final String WEB_REDIRECT_PATH = "/signin/code"; + // tag::morsel-push-key[] + /// The app's push key, from the Push section of the Codename One console. + /// Empty, the app registers for no notifications: everything still works, + /// and a phone in a pocket simply hears nothing until it is opened. + public static final String PUSH_APP_KEY = ""; + // end::morsel-push-key[] + /// Where the map opens when the device will not say where it is. + public static final double DEFAULT_LAT = 37.8080; + public static final double DEFAULT_LNG = -122.4120; + + private static final String SERVER_KEY = "morsel.server"; + private static final String MODE_KEY = "morsel.mode"; + private static final String MODE_OWNER_KEY = "morsel.mode.owner"; + + private AppConfig() { + } + + /// The server's address, with no trailing slash. A `morsel.server.url` + /// system property wins, which is what the tests use; then the address saved + /// from the settings screen; then, in a browser, the address the page came + /// from, since the server that hosts the app is the server it talks to; + /// then [#DEFAULT_SERVER]. + public static String serverUrl() { + String url = System.getProperty("morsel.server.url"); + if (url == null || url.length() == 0) { + String origin = pageOrigin(); + // In a browser it is always the page's own server: see + // serverSwitchable(). An address saved before that is ignored. + url = origin != null ? origin : Preferences.get(SERVER_KEY, DEFAULT_SERVER); + } + return clean(url); + } + + /// Whether the app may be pointed at another server. Not in a browser: + /// there the authorization code is sent back to the page's own server + /// ([#redirectUri]), and another server has not registered that address + /// as one of its own, so every sign-in after a switch would be refused. + /// The hosted app is the server's own, and talks to that server. + public static boolean serverSwitchable() { + return pageOrigin() == null; + } + + /// The address of the page the app is running in -- `https://host`, with + /// no path -- or null when it is not running in a browser. + public static String pageOrigin() { + String origin = CN.getProperty("browser.window.location.origin", null); + if (origin == null || !(origin.regionMatches(true, 0, "http://", 0, 7) + || origin.regionMatches(true, 0, "https://", 0, 8))) { + return null; + } + return clean(origin); + } + + /// Where the server is asked to send the authorization code. + /// + /// The installed app names the loopback address and reads the code off the + /// redirect without following it. A browser follows a redirect itself and + /// will not show the page an answer from another origin, so there the code + /// is sent to the server the page came from, which hands it back. + public static String redirectUri() { + String origin = pageOrigin(); + return origin == null ? REDIRECT_URI : origin + WEB_REDIRECT_PATH; + } + + public static void setServerUrl(String url) { + Preferences.set(SERVER_KEY, clean(url)); + } + + /// The mode the account `owner` (see + /// [com.codenameone.examples.morsel.net.Session#owner(String)]) chose last + /// -- `customer`, `courier` or `admin` -- or + /// empty when they have chosen none, or the choice on record is someone + /// else's. A choice recorded with no name is anyone's: that is how a test + /// puts the app in a mode before it signs in. + public static String mode(String owner) { + String chosenBy = Preferences.get(MODE_OWNER_KEY, ""); + if (chosenBy.length() > 0 && !chosenBy.equals(owner)) { + return ""; + } + return Preferences.get(MODE_KEY, ""); + } + + /// Records `mode` as chosen by whoever signs in next. + public static void setMode(String mode) { + setMode(mode, ""); + } + + public static void setMode(String mode, String username) { + Preferences.set(MODE_KEY, mode == null ? "" : mode); + Preferences.set(MODE_OWNER_KEY, username == null ? "" : username); + } + + /// Whether `url` names the server `current` does, as [#setServerUrl] + /// would store it. + public static boolean sameServer(String url, String current) { + return clean(url).equals(current); + } + + private static String clean(String url) { + String text = url == null ? "" : url.trim(); + while (text.endsWith("/")) { + text = text.substring(0, text.length() - 1); + } + if (text.length() == 0) { + return DEFAULT_SERVER; + } + // Compared without folding the case: a scheme is ASCII, and + // toLowerCase() follows the device's language. + if (!text.regionMatches(true, 0, "http://", 0, 7) + && !text.regionMatches(true, 0, "https://", 0, 8)) { + text = "http://" + text; + } + return text; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java new file mode 100644 index 00000000000..ebc5320bae8 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.system.Lifecycle; +import com.codenameone.examples.morsel.courier.Shift; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.net.Account; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.notify.Notifications; +import com.codenameone.examples.morsel.pay.CardForm; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Look; +import com.codenameone.examples.morsel.ui.Nav; + +/// The app. It opens on whatever the stored session allows: the mode the user +/// was last in when they are still signed in, the welcome screen when not. +/// +/// Where things are: +/// +/// - `net` -- the session, and the typed clients generated from the contracts in +/// the `shared` module. Nothing else in the app builds a URL. +/// - `live` -- the WebSocket the server pushes changes down. +/// - `notify` -- push notifications, for a phone that is not looking. +/// - `ui`, `customer`, `courier`, `admin` -- the screens. +/// - `map` -- the map, its markers and the device's position. +/// - `src/main/css/theme.css` -- every colour, font and margin. +/// - `src/main/l10n` -- every word, in each language the app speaks. +/// - `Prefs` -- what the user chose in Settings. +/// - `Telemetry` -- what is reported about how the app is used, once the user +/// has agreed to it. +public class Morsel extends Lifecycle { + @Override + public void runApp() { + // Before the first screen is built: a screen keeps the look and the + // words it was built with. + Look.apply(); + Lang.apply(); + Telemetry.start(); + Session.start(AppConfig.serverUrl()); + Account.start(); + Nav.launch(); + } + + /// Coming back to the app is how a card added on the payment provider's + /// page is noticed: the browser it opened in tells the app nothing. + /// + /// It is also when the app asks for its push token, which the platform + /// answers at once when nothing has changed, and when the position is + /// looked for again if going to the background put the search down. + @Override + public void start() { + super.start(); + CardForm.resumed(); + Shift.resumed(); + Locator.resumed(); + Notifications.start(); + } + + /// The app is put away, which is the last sure moment to send the usage + /// reports gathered since the last batch. + /// + /// A courier on line goes on reporting where the car is from the + /// background; anyone else's search for the position is put down until + /// the app is back. + @Override + public void stop() { + super.stop(); + Telemetry.flush(); + Shift.paused(); + Locator.paused(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java new file mode 100644 index 00000000000..a959d471782 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.io.Preferences; +import com.codenameone.examples.morsel.api.PlaceDto; +import java.util.ArrayList; +import java.util.List; + +/// What the user has chosen in Settings, and the places they go back to. +/// +/// Every screen reads and writes a preference through this class and through +/// nothing else, so where the values live is decided in one place. Today that +/// is the device: `Preferences`, which survives a restart and is gone with the +/// app. A server that keeps the same values for the account plugs in at +/// [#onChange]: it is told the name of each value as it changes, and puts the +/// account's values in with the setters when it has fetched them. No screen +/// has to change for that. +public final class Prefs { + /// Told when a value changes, with the value's name. + public interface Listener { + void changed(String name); + } + + public static final String LANGUAGE = "language"; + public static final String THEME = "theme"; + public static final String UNITS = "units"; + public static final String NOTIFY_DELIVERY_UPDATES = "notifyDeliveryUpdates"; + public static final String NOTIFY_RECEIPTS = "notifyReceipts"; + public static final String NOTIFY_PROMOTIONS = "notifyPromotions"; + public static final String COURIER_GENDER = "courierGender"; + public static final String QUIET_DELIVERY = "quietDelivery"; + public static final String ACCESSIBLE_VEHICLE = "accessibleVehicle"; + public static final String PET_FRIENDLY = "petFriendly"; + public static final String PAYMENT_METHOD = "defaultPaymentMethodId"; + public static final String TIP_PERCENT = "defaultTipPercent"; + public static final String SHARE_TRIPS = "shareTrips"; + public static final String EMERGENCY_NAME = "emergencyContactName"; + public static final String EMERGENCY_PHONE = "emergencyContactPhone"; + public static final String HOME = "home"; + public static final String WORK = "work"; + public static final String RECENTS = "recents"; + /// Whose the recent destinations are: see [#ownedBy]. + private static final String RECENTS_OWNER = "recentsOwner"; + + public static final String THEME_SYSTEM = "system"; + public static final String THEME_LIGHT = "light"; + public static final String THEME_DARK = "dark"; + public static final String UNITS_KM = "km"; + public static final String UNITS_MILES = "mi"; + public static final String GENDER_ANY = "any"; + public static final String GENDER_WOMEN = "women"; + /// The one way to pay that needs nothing set up. + public static final String PAYMENT_CASH = "cash"; + + private static final String PREFIX = "morsel.pref."; + /// The plain values that are a copy of the account's; see [#ownedBy]. + private static final String[] ACCOUNT_OWNED = {NOTIFY_DELIVERY_UPDATES, NOTIFY_RECEIPTS, + NOTIFY_PROMOTIONS, COURIER_GENDER, QUIET_DELIVERY, ACCESSIBLE_VEHICLE, PET_FRIENDLY, + PAYMENT_METHOD, TIP_PERCENT, SHARE_TRIPS, EMERGENCY_NAME, EMERGENCY_PHONE}; + /// How many destinations are remembered. + private static final int RECENT_LIMIT = 4; + + private static Listener listener; + + private Prefs() { + } + + /// Names the one listener told of every change. A value put in by the + /// listener itself is reported to it like any other, so a listener that + /// stores what it is told must not answer by setting the value again. + public static void onChange(Listener told) { + listener = told; + } + + // -------------------------------------------------------------- appearance + + /// The language chosen, as its two-letter code; empty while the user has + /// chosen none and the device's language decides. + public static String language() { + return text(LANGUAGE, ""); + } + + public static void setLanguage(String code) { + put(LANGUAGE, code); + } + + /// [#THEME_SYSTEM], [#THEME_LIGHT] or [#THEME_DARK]. + public static String theme() { + return text(THEME, THEME_SYSTEM); + } + + public static void setTheme(String theme) { + put(THEME, theme); + } + + /// [#UNITS_KM] or [#UNITS_MILES]. + public static String units() { + return text(UNITS, UNITS_KM); + } + + public static void setUnits(String units) { + put(UNITS, units); + } + + // ----------------------------------------------------------- notifications + + public static boolean notifyDeliveryUpdates() { + return flag(NOTIFY_DELIVERY_UPDATES, true); + } + + public static void setNotifyDeliveryUpdates(boolean on) { + put(NOTIFY_DELIVERY_UPDATES, on); + } + + public static boolean notifyReceipts() { + return flag(NOTIFY_RECEIPTS, true); + } + + public static void setNotifyReceipts(boolean on) { + put(NOTIFY_RECEIPTS, on); + } + + public static boolean notifyPromotions() { + return flag(NOTIFY_PROMOTIONS, false); + } + + public static void setNotifyPromotions(boolean on) { + put(NOTIFY_PROMOTIONS, on); + } + + // -------------------------------------------------------------- the delivery + + /// Who the customer wants at the wheel: [#GENDER_ANY] or [#GENDER_WOMEN]. + public static String courierGender() { + return text(COURIER_GENDER, GENDER_ANY); + } + + public static void setCourierGender(String gender) { + put(COURIER_GENDER, gender); + } + + public static boolean quietDelivery() { + return flag(QUIET_DELIVERY, false); + } + + public static void setQuietDelivery(boolean on) { + put(QUIET_DELIVERY, on); + } + + public static boolean accessibleVehicle() { + return flag(ACCESSIBLE_VEHICLE, false); + } + + public static void setAccessibleVehicle(boolean on) { + put(ACCESSIBLE_VEHICLE, on); + } + + public static boolean petFriendly() { + return flag(PET_FRIENDLY, false); + } + + public static void setPetFriendly(boolean on) { + put(PET_FRIENDLY, on); + } + + // --------------------------------------------------------------- payment + + /// The id of the way the user pays unless they say otherwise for a delivery. + public static String paymentMethod() { + return text(PAYMENT_METHOD, PAYMENT_CASH); + } + + public static void setPaymentMethod(String id) { + put(PAYMENT_METHOD, id); + } + + /// The tip offered first on a receipt, as a percentage of the fare. + public static int tipPercent() { + return Preferences.get(PREFIX + TIP_PERCENT, 0); + } + + public static void setTipPercent(int percent) { + if (percent != tipPercent()) { + Preferences.set(PREFIX + TIP_PERCENT, percent); + changed(TIP_PERCENT); + } + } + + // ---------------------------------------------------- privacy and safety + + /// Whether a delivery under way is shared with the emergency contact. + public static boolean shareTrips() { + return flag(SHARE_TRIPS, false); + } + + public static void setShareTrips(boolean on) { + put(SHARE_TRIPS, on); + } + + public static String emergencyContactName() { + return text(EMERGENCY_NAME, ""); + } + + public static void setEmergencyContactName(String name) { + put(EMERGENCY_NAME, name); + } + + public static String emergencyContactPhone() { + return text(EMERGENCY_PHONE, ""); + } + + public static void setEmergencyContactPhone(String phone) { + put(EMERGENCY_PHONE, phone); + } + + // ---------------------------------------------------------------- places + + /// Home, or null while the user has named none. + public static PlaceDto home() { + return place(HOME); + } + + public static void setHome(PlaceDto place) { + store(HOME, place); + changed(HOME); + } + + /// Work, or null while the user has named none. + public static PlaceDto work() { + return place(WORK); + } + + public static void setWork(PlaceDto place) { + store(WORK, place); + changed(WORK); + } + + /// The last few destinations, the newest first. + public static List recents() { + List all = new ArrayList(); + for (int iter = 0; iter < RECENT_LIMIT; iter++) { + PlaceDto place = place(RECENTS + "." + iter); + if (place != null) { + all.add(place); + } + } + return all; + } + + /// Remembers `place` as the destination chosen last. + public static void addRecent(PlaceDto place) { + if (place == null || place.name() == null || place.name().length() == 0) { + return; + } + List all = recents(); + for (int iter = all.size() - 1; iter >= 0; iter--) { + if (place.name().equals(all.get(iter).name())) { + all.remove(iter); + } + } + all.add(0, place); + for (int iter = 0; iter < RECENT_LIMIT; iter++) { + store(RECENTS + "." + iter, iter < all.size() ? all.get(iter) : null); + } + changed(RECENTS); + } + + /// Says whose device this is now, by the name they signed in with; empty + /// for nobody. When the account changes, what the device holds of the + /// last one is dropped and not shown to the next: the recent + /// destinations, which are kept here only and say where a person goes, + /// and the copy of everything the account keeps on the server -- home, + /// work, the emergency contact, the card paid with, the delivery options. + /// That copy is replaced when the new account's values arrive, but they + /// may not arrive, and then the last person's would be on screen and go + /// out with the next save as the new person's own. How the app looks + /// (language, theme, units) is the device's and stays. The same account + /// signing in again finds its own still there. + /// + /// The listener is told of the recent destinations alone. It sends what + /// it is told of to the server, and values dropped here were not changed + /// by anyone: reported, they would be saved over the new account's. + /// + /// @param account the account, with its server, as + /// [com.codenameone.examples.morsel.net.Session#owner(String)] names + /// it, or empty for none + public static void ownedBy(String account) { + String owner = account == null ? "" : account; + if (owner.equals(text(RECENTS_OWNER, ""))) { + return; + } + for (int iter = 0; iter < RECENT_LIMIT; iter++) { + store(RECENTS + "." + iter, null); + } + store(HOME, null); + store(WORK, null); + for (String name : ACCOUNT_OWNED) { + Preferences.delete(PREFIX + name); + } + Preferences.set(PREFIX + RECENTS_OWNER, owner); + changed(RECENTS); + } + + // ------------------------------------------------------------- the store + + private static String text(String name, String fallback) { + return Preferences.get(PREFIX + name, fallback); + } + + private static boolean flag(String name, boolean fallback) { + return Preferences.get(PREFIX + name, fallback); + } + + private static void put(String name, String value) { + String wanted = value == null ? "" : value.trim(); + if (!wanted.equals(text(name, null))) { + Preferences.set(PREFIX + name, wanted); + changed(name); + } + } + + private static void put(String name, boolean value) { + // Compared with the opposite as the fallback, so a value never stored + // is stored the first time whatever it is. + if (flag(name, !value) != value) { + Preferences.set(PREFIX + name, value); + changed(name); + } + } + + private static PlaceDto place(String name) { + String title = text(name + ".name", ""); + if (title.length() == 0) { + return null; + } + PlaceDto place = new PlaceDto(title, text(name + ".address", ""), + Preferences.get(PREFIX + name + ".lat", 0d), + Preferences.get(PREFIX + name + ".lng", 0d)); + return place; + } + + private static void store(String name, PlaceDto place) { + Preferences.set(PREFIX + name + ".name", place == null ? "" : place.name()); + Preferences.set(PREFIX + name + ".address", + place == null || place.address() == null ? "" : place.address()); + Preferences.set(PREFIX + name + ".lat", place == null ? 0d : place.lat()); + Preferences.set(PREFIX + name + ".lng", place == null ? 0d : place.lng()); + } + + private static void changed(String name) { + if (listener != null) { + listener.changed(name); + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java new file mode 100644 index 00000000000..0249ee1f571 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.analytics.Analytics; +import com.codename1.analytics.AnalyticsConsent; +import com.codename1.analytics.AnalyticsEvent; +import com.codename1.analytics.AnalyticsProvider; +import com.codename1.analytics.CodenameOneAnalyticsProvider; +import com.codename1.analytics.LoggingAnalyticsProvider; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.ui.Lang; + +/// What the app reports about how it is used, and whether it may. +/// +/// This is the only class that talks to `com.codename1.analytics`. A screen +/// says what happened by calling one of the methods here, so the whole +/// vocabulary -- every event name and every parameter -- is in this file, and +/// it is here that the one rule is kept: nothing that says who someone is or +/// where they are ever goes into an event. No name, e-mail address, phone +/// number, address, position, card number or delivery id; an amount of money goes +/// as the bracket it falls in. What is known about the user is two things: the +/// mode they are in and the language they read. +/// +/// Nothing is reported until the user has said yes. The library starts with +/// consent withheld and drops every call made without it, so a method here is +/// always safe to call. The answer is asked for once, after the first sign-in +/// on a device ([#asked()], `ui.ConsentForm`), can be changed in Settings at +/// any time ([#allow(boolean)]), and is kept by the library on the device, in +/// `Preferences`, with the made-up id that tells one installation from the +/// next. Deleting the account forgets both ([#forget()]). +/// +/// On a device the reports go to Codename One's own service, which shows them +/// in the developer console. That service knows which app is reporting from +/// the identity a build stamps into it, so the simulator, which has none, only +/// writes what it would have sent to the log. +public final class Telemetry { + private static final String ACCOUNT = "account"; + private static final String DELIVERY = "delivery"; + private static final String PAYMENT = "payment"; + private static final String COURIER = "courier"; + private static final String ADMIN = "admin"; + private static final String SETTINGS = "settings"; + + /// The screen reported last, which the next one names as where it came from. + private static String screen; + /// The screen that is showing, reported or not. + private static String showing; + private static String role; + /// The role and the language as they were last reported. + private static String described; + private static boolean started; + + private Telemetry() { + } + + /// Says where the reports go. Called once, when the app starts. + public static void start() { + if (started) { + return; + } + started = true; + if (CN.isSimulator()) { + Analytics.addProvider(new LoggingAnalyticsProvider()); + } else { + Analytics.addProvider(new CodenameOneAnalyticsProvider()); + } + } + + /// Sends the reports to `providers` and nowhere else. For a test, which + /// wants to see what was reported and wants none of it to leave. + public static void use(AnalyticsProvider... providers) { + started = true; + Analytics.clearProviders(); + for (AnalyticsProvider provider : providers) { + Analytics.addProvider(provider); + } + screen = null; + described = null; + } + + // --------------------------------------------------------------- consent + + /// Whether the user has answered, either way. + public static boolean asked() { + return Analytics.getConsent() != null; + } + + /// Whether the user said yes. + public static boolean allowed() { + AnalyticsConsent consent = Analytics.getConsent(); + return consent != null && consent.isAnalytics(); + } + + /// Records the user's answer. A yes covers how the app is used and the + /// errors it runs into; nothing here follows a person or serves an + /// advertisement, and those are never granted. + public static void allow(boolean yes) { + boolean was = allowed(); + Analytics.setConsent(AnalyticsConsent.builder() + .analytics(yes) + .crashReporting(yes) + .personalization(false) + .adStorage(false) + .build()); + if (yes && !was) { + // What was dropped while the answer was no: who is using the app, + // in the two words kept about them, and the screen they are on. + screen = null; + described = null; + describe(); + if (showing != null) { + screen(showing); + } + } + } + + /// Takes the answer back, so that it is asked for again. + public static void unask() { + Analytics.setConsent(null); + } + + /// The account is gone: the installation gets a new id, which cuts what is + /// reported from now on from everything reported before, and whoever signs + /// in next is asked for themselves. + public static void forget() { + // What is waiting was gathered under the id that is about to go, and + // is sent under it. + Analytics.flush(); + Analytics.resetClientId(); + Analytics.setConsent(null); + role = null; + screen = null; + described = null; + } + + /// Sends what is waiting. Reports are gathered and sent a batch at a + /// time; this is for when the app is put away. + public static void flush() { + Analytics.flush(); + } + + // ------------------------------------------------------- screens and who + + /// A screen is showing. `name` is the one the screen was built with, which + /// is not translated and does not change with its title. + public static void screen(String name) { + if (name == null || name.length() == 0) { + return; + } + showing = name; + if (name.equals(screen) || !allowed()) { + return; + } + Analytics.screen(name, screen); + screen = name; + } + + /// The user is in `mode`: customer, courier or admin. + public static void role(String mode) { + role = mode; + describe(); + } + + /// Reports the role and the language, when either is news: the home + /// screen is built again for every change of look, and says its mode each + /// time. + private static void describe() { + if (role == null || !allowed()) { + return; + } + String language = Lang.current(); + String now = role + "/" + language; + if (!now.equals(described)) { + described = now; + Analytics.setUserProperty("role", role); + Analytics.setUserProperty("language", language); + } + } + + // --------------------------------------------------------------- account + + public static void signedIn() { + send(ACCOUNT, "login", "method", "password"); + } + + /// @param reason `user` when they chose to, `expired` when the session + /// ended under them, `deleted` when the account was closed + public static void signedOut(String reason) { + send(ACCOUNT, "logout", "reason", reason); + role = null; + } + + /// @param onboarding whether they came to drive + public static void registered(boolean onboarding) { + send(ACCOUNT, "sign_up", "method", "password", "intent", onboarding ? "courier" : "customer"); + } + + public static void phoneVerified() { + send(ACCOUNT, "phone_verified"); + } + + // ------------------------------------------------------------------ deliveries + + /// @param products how many kinds of car were priced + public static void deliveryQuoted(int products, double meters) { + send(DELIVERY, "delivery_quoted", "products", Integer.valueOf(products), "distance", + distance(meters)); + } + + public static void deliveryRequested(String product, boolean cash) { + send(DELIVERY, "delivery_requested", "product", product, "payment", cash ? "cash" : "card"); + } + + /// @param by `customer`, `courier` or `admin` + /// @param state the state the delivery was in + public static void deliveryCancelled(String by, DeliveryState state) { + send(DELIVERY, "delivery_cancelled", "by", by, "state", state == null ? "" : state.name()); + } + + /// @param side whose device saw it end: `customer` or `courier`. A delivery that + /// both of them use the app for is reported by both, so a count of + /// deliveries takes one side. + public static void deliveryCompleted(String side, long fareCents, boolean cash) { + send(DELIVERY, "delivery_completed", "side", side, "fare", fare(fareCents), "payment", + cash ? "cash" : "card"); + } + + public static void rated(int stars) { + send(DELIVERY, "rating_given", "stars", Integer.valueOf(stars)); + } + + // --------------------------------------------------------------- payment + + /// @param where `delivery` at the end of the delivery, `receipt` from the receipt + /// @param percent the share of the fare, or 0 for an amount typed in + public static void tipped(String where, int percent) { + send(PAYMENT, "tip_added", "where", where, "share", + percent > 0 ? percent + "_percent" : "custom"); + } + + public static void cardAdded() { + send(PAYMENT, "card_added"); + } + + public static void cardRemoved() { + send(PAYMENT, "card_removed"); + } + + // --------------------------------------------------------------- onboarding + + /// @param again whether this is an application sent back, being put right + public static void applicationStarted(boolean again) { + send(COURIER, "courier_application_started", "resubmission", Boolean.valueOf(again)); + } + + public static void applicationSubmitted() { + send(COURIER, "courier_application_submitted"); + } + + public static void courierOnline(boolean online) { + send(COURIER, online ? "courier_online" : "courier_offline"); + } + + public static void offerAnswered(boolean accepted) { + send(COURIER, accepted ? "offer_accepted" : "offer_declined"); + } + + public static void payoutRequested(long cents) { + send(COURIER, "payout_requested", "amount", fare(cents)); + } + + // ----------------------------------------------------------------- admin + + public static void applicationReviewed(boolean approved) { + send(ADMIN, approved ? "admin_application_approved" : "admin_application_rejected"); + } + + public static void userFlagged(boolean flagged) { + send(ADMIN, flagged ? "admin_user_flagged" : "admin_user_unflagged"); + } + + public static void userBlocked(boolean blocked) { + send(ADMIN, blocked ? "admin_user_blocked" : "admin_user_unblocked"); + } + + /// @param from the screen it was done from + public static void deliveryRefunded(String from) { + send(ADMIN, "admin_delivery_refunded", "from", from); + } + + public static void pricingChanged() { + send(ADMIN, "admin_pricing_changed"); + } + + // -------------------------------------------------------------- settings + + public static void languageChanged(String code) { + send(SETTINGS, "language_changed", "language", code); + describe(); + } + + public static void themeChanged(String theme) { + send(SETTINGS, "theme_changed", "theme", theme); + } + + // ---------------------------------------------------------------- errors + + /// Something failed that the app caught and carried on from. Only what + /// kind of failure it was is reported: a message can quote what the user + /// typed, or the file it was reading. + public static void error(Throwable failed) { + if (failed != null) { + Analytics.crash(failed, failed.getClass().getName(), false); + } + } + + // ------------------------------------------------------------- the store + + /// The bracket an amount of money falls in, in whole units of the + /// currency. The amount itself, beside a time, can be matched to a delivery. + static String fare(long cents) { + long units = cents / 100; + return units < 5 ? "under_5" : units < 10 ? "5_to_10" : units < 20 ? "10_to_20" + : units < 35 ? "20_to_35" : units < 50 ? "35_to_50" : "50_or_more"; + } + + /// How far, as a bracket in kilometres. + static String distance(double meters) { + return meters < 2000 ? "under_2km" : meters < 5000 ? "2_to_5km" + : meters < 10000 ? "5_to_10km" : meters < 25000 ? "10_to_25km" : "25km_or_more"; + } + + /// Reports `name` with `pairs`: a parameter's name, then its value. + private static void send(String category, String name, Object... pairs) { + AnalyticsEvent.Builder event = AnalyticsEvent.create(name).category(category); + for (int iter = 0; iter + 1 < pairs.length; iter += 2) { + event.param((String) pairs[iter], pairs[iter + 1]); + } + Analytics.event(event.build()); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java new file mode 100644 index 00000000000..2c202977c50 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.ReasonDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// Every delivery, for an admin: which are under way, which finished and which +/// did not, and for each what can still be done about it -- cancelling one that +/// is under way, returning the money for one that is paid. +public final class AdminDeliveriesForm { + private static final String[] FILTERS = {"all", "active", "completed", "cancelled"}; + private static final String[] FILTER_NAMES = {"All", "Under way", "Completed", "Cancelled"}; + + private final Panes panes; + private final Container list = Ui.list("deliveries"); + private List deliveries; + private String filter = FILTERS[0]; + + private AdminDeliveriesForm(Panes panes) { + this.panes = panes; + Container top = new Container(BoxLayout.y()); + top.setUIID("WlPage"); + top.add(Ui.choice("filter", FILTER_NAMES, FILTERS, filter, value -> { + filter = value; + fill(); + })); + Ui.pull(list, this::load); + panes.list("Deliveries", "Deliveries", Panes.listed(top, list), this::load); + } + + static void show(Form previous) { + show(Panes.phone(previous)); + } + + static void show(Panes panes) { + new AdminDeliveriesForm(panes); + } + + private void load() { + Api.admin().deliveries(Net.to(found -> { + deliveries = found; + fill(); + }, Ui.failed(list, this::load))); + } + + private void fill() { + list.removeAll(); + int shown = 0; + for (int iter = 0; deliveries != null && iter < deliveries.size(); iter++) { + final DeliveryDto delivery = deliveries.get(iter); + if (!passes(delivery)) { + continue; + } + String people = delivery.courierName() == null || delivery.courierName().length() == 0 + ? delivery.customerName() + : Lang.tr("{0} with {1}", delivery.customerName(), delivery.courierName()); + Container value = new Container(BoxLayout.y()); + value.add(Ui.plain(Ui.money(delivery.total() > 0 ? delivery.total() : delivery.fareCents(), + delivery.currency()), "WlRowValue")); + value.add(Ui.badge(Ui.state(delivery.state()), Ui.stateBadge(delivery.state()))); + final Container picked = Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), + Ui.when(delivery.requestedAt()) + Ui.DOT + people, value); + list.add(Ui.tap(picked, "delivery-" + shown, e -> { + panes.picked(picked); + show(delivery); + })); + shown++; + } + if (shown == 0 && deliveries != null) { + list.add(Ui.empty(FontImage.MATERIAL_RECEIPT_LONG, "No deliveries here", null)); + } + panes.refresh(); + } + + private boolean passes(DeliveryDto delivery) { + if ("active".equals(filter)) { + return delivery.state().isActive(); + } + if ("completed".equals(filter)) { + return DeliveryState.COMPLETED == delivery.state(); + } + if ("cancelled".equals(filter)) { + return !delivery.state().isActive() && DeliveryState.COMPLETED != delivery.state(); + } + return true; + } + + private void show(final DeliveryDto delivery) { + Container page = Ui.page(); + Label total = Ui.plain(Ui.money(delivery.total() > 0 ? delivery.total() : delivery.fareCents(), + delivery.currency()), "WlAmount"); + page.add(total); + Label state = Ui.badge(Ui.state(delivery.state()), Ui.stateBadge(delivery.state())); + state.setName("deliveryState"); + page.add(FlowLayout.encloseCenter(state)); + page.add(FlowLayout.encloseCenter(Ui.plain(Ui.when(delivery.requestedAt()), "WlMuted"))); + + Ui.section(page, "Trip"); + page.add(Ui.row(FontImage.MATERIAL_TRIP_ORIGIN, delivery.pickupAddress(), null, null)); + page.add(Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), + Ui.distance(delivery.distanceMeters()) + Ui.DOT + Ui.minutes(delivery.durationSeconds()), + null)); + Ui.section(page, "People"); + page.add(Ui.row(FontImage.MATERIAL_HAIL, delivery.customerName(), delivery.customerUsername(), null)); + if (delivery.courierName() != null && delivery.courierName().length() > 0) { + page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, delivery.courierName(), + delivery.vehicle() + (delivery.plate() == null || delivery.plate().length() == 0 ? "" + : Ui.DOT + delivery.plate()), null)); + } + Ui.section(page, "Payment"); + page.add(Ui.fact("Kind of delivery", Lang.tr(Ui.product(delivery.product())))); + page.add(Ui.fact("Fare", Ui.money(delivery.fareCents(), delivery.currency()))); + page.add(Ui.fact("Service fee", Ui.money(delivery.serviceFee(), delivery.currency()))); + page.add(Ui.fact("Tip", Ui.money(delivery.tip(), delivery.currency()))); + page.add(Ui.fact("Paid with", delivery.cash() ? Lang.tr("Cash") : delivery.paymentMethodLabel())); + page.add(Ui.fact("Payment", Lang.tr(payment(delivery.paymentStatus())))); + + if (delivery.state().isActive()) { + page.add(Ui.danger("Cancel this delivery", "cancelDelivery", e -> panes.reason( + "Cancel delivery", Lang.tr("The customer and the courier are both told, and " + + "nobody is charged."), "Cancel delivery", why -> { + ReasonDto reason = new ReasonDto(why); + Api.admin().cancelDelivery(delivery.id(), reason, Net.to(cancelled -> { + Telemetry.deliveryCancelled("admin", delivery.state()); + panes.home(); + }, Ui::fail)); + }))); + } + if ("PAID".equals(delivery.paymentStatus()) && !delivery.cash()) { + page.add(Ui.danger("Refund this delivery", "refund", e -> panes.reason("Refund", + Lang.tr("{0} goes back to the card it came from.", Ui.money( + delivery.total() > 0 ? delivery.total() : delivery.fareCents(), delivery.currency())), + "Refund", why -> { + ReasonDto reason = new ReasonDto(why); + Api.admin().refundDelivery(delivery.id(), reason, Net.to(refunded -> { + Telemetry.deliveryRefunded("deliveries"); + // The processor may not have returned the money + // yet; the receipt says which. + Ui.say("refund_pending".equals(refunded.status()) + ? "The refund is pending" : "The delivery was refunded"); + panes.home(); + }, Ui::fail)); + }))); + } + panes.detail("Delivery", "DeliveryDetail", page); + } + + private static String payment(String status) { + return "PAID".equals(status) ? "Paid" : "REFUNDED".equals(status) ? "Refunded" + : "REFUND_PENDING".equals(status) ? "Refund pending" + : "REFUND_FAILED".equals(status) ? "Refund failed" + : "FAILED".equals(status) ? "Payment failed" : "Payment pending"; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java new file mode 100644 index 00000000000..1e6269a17d2 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.morsel.ui.Layouts; +import com.codenameone.examples.morsel.ui.Menu; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Ui; + +/// Administering: what needs an answer, how the service is doing right now and +/// over the last weeks, and the way to everything an admin looks after. +/// +/// Everything here is behind `/api/admin`, which the server opens to the +/// `ADMIN` role and to nobody else. That is where the protection is. This +/// screen is reached only when `/api/me` says the user is an admin, but that is +/// so the others are not shown something useless -- not what keeps them out. +/// +/// It is laid out for the room there is. This is the phone's: the [Dashboard] +/// with the places under it and in a drawer, each a screen of its own. On a +/// wide screen -- a tablet on its side, a desktop, a browser -- it is the +/// [Console] instead, with the places down the side and a list beside what was +/// picked from it. Which one is decided when the screen is built, and a window +/// resized across the line, or a device turned, builds it again. +public class AdminForm implements Nav.Home { + private static final int POLL_MILLIS = 5000; + + private final Console console; + private final Form form; + private Dashboard dashboard; + + public AdminForm() { + if (Layouts.wide()) { + console = new Console(); + form = console.form(); + return; + } + console = null; + form = Ui.home("Admin"); + final Menu menu = new Menu(form, Nav.ADMIN); + dashboard = new Dashboard(false, new Dashboard.Places() { + @Override + public void applications() { + ApplicationsForm.show(form); + } + + @Override + public void flagged() { + UsersForm.show(form, UsersForm.FLAGGED); + } + }); + menu.item(FontImage.MATERIAL_INSIGHTS, "Dashboard", "home", () -> { }); + menu.item(FontImage.MATERIAL_ASSIGNMENT_IND, "Applications", "applications", + () -> ApplicationsForm.show(form)); + menu.item(FontImage.MATERIAL_GROUP, "People", "users", () -> UsersForm.show(form, "")); + menu.item(FontImage.MATERIAL_RECEIPT_LONG, "Deliveries", "deliveries", + () -> AdminDeliveriesForm.show(form)); + menu.item(FontImage.MATERIAL_PAYMENTS, "Payments", "payments", + () -> PaymentsForm.show(form)); + menu.item(FontImage.MATERIAL_MAP, "Live map", "fleet", this::fleet); + menu.item(FontImage.MATERIAL_SELL, "Pricing", "pricing", () -> PricingForm.show(form)); + + // The bar a title bar would be, with the menu where the map screens + // have it, and the mode said beside the title: an admin who also deliveries + // should never have to wonder which app this is. + Container bar = new Container(new BorderLayout()); + bar.setUIID("WlHomeBar"); + bar.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(menu.flatButton())); + bar.add(BorderLayout.CENTER, FlowLayout.encloseLeftMiddle( + Ui.label("Operations", "WlHeading"))); + // In the menu the badge stands under a name and keeps a gap above it; + // here it is alone on the bar's centre line. + Label admin = Ui.badge("Admin", "WlModeBadge"); + admin.getAllStyles().setMargin(0, 0, 0, 0); + bar.add(BorderLayout.EAST, FlowLayout.encloseCenterMiddle(admin)); + + Container page = dashboard.page(); + Ui.section(page, "Manage"); + Container manage = new Container(BoxLayout.y()); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_ASSIGNMENT_IND, "Applications", + "People who asked to drive, and their papers", null), "applications", + e -> ApplicationsForm.show(form))); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_GROUP, "People", + "Roles, flags, and blocking an account", null), "users", + e -> UsersForm.show(form, ""))); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_RECEIPT_LONG, "Deliveries", + "The most recent, in every state", null), "deliveries", + e -> AdminDeliveriesForm.show(form))); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_PAYMENTS, "Payments", + "What was charged, and refunds", null), "payments", + e -> PaymentsForm.show(form))); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_MAP, "Live map", + "Where every car online is", null), "fleet", e -> fleet())); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_SELL, "Pricing", + "What a delivery costs, and the service's share", null), "pricing", + e -> PricingForm.show(form))); + page.add(manage); + + Container screen = new Container(new BorderLayout()); + // The strip the status bar is drawn in is the bar's colour, not + // whatever is behind the screen. + screen.setUIID("WlForm"); + screen.setSafeArea(true); + screen.add(BorderLayout.NORTH, bar); + screen.add(BorderLayout.CENTER, page); + form.add(screen); + menu.install(); + // A narrow window on a desktop is still a desktop's. + Layouts.dress(form); + form.addShowListener(e -> { + if (Layouts.wide()) { + // Come back to from a screen that was made wide meanwhile. + CN.callSerially(Nav::home); + return; + } + dashboard.shown(); + }); + Layouts.onChange(form, Nav::home); + UITimer.timer(POLL_MILLIS, true, form, dashboard::load); + } + + @Override + public Form form() { + return form; + } + + @Override + public void start() { + if (console != null) { + console.start(); + return; + } + dashboard.start(); + // The window was a console a moment ago, on one of its places: that + // place is where the admin still is. + final String place = Console.resumed(); + if (place != null) { + CN.callSerially(() -> open(place)); + } + } + + public void show() { + start(); + form.show(); + } + + private void open(String place) { + if ("applications".equals(place)) { + ApplicationsForm.show(form); + } else if ("users".equals(place)) { + UsersForm.show(form, ""); + } else if ("deliveries".equals(place)) { + AdminDeliveriesForm.show(form); + } else if ("payments".equals(place)) { + PaymentsForm.show(form); + } else if ("pricing".equals(place)) { + PricingForm.show(form); + } + } + + private void fleet() { + new FleetForm(form).show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java new file mode 100644 index 00000000000..f63503795d9 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.components.ScaleImageLabel; +import com.codename1.io.Log; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Image; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.plaf.Style; +import com.codename1.util.Base64; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.DocumentContentDto; +import com.codenameone.examples.morsel.api.DocumentDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.api.ReasonDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// The people who asked to drive, and the decision about each. +/// +/// An application is approved or sent back with a reason. Approving is what +/// makes the account a courier; nothing else in the app or on the server does. +/// The documents are pictures only an admin and the applicant can fetch, one +/// at a time, when they are opened. +public final class ApplicationsForm { + private static final String[] KINDS = {"photo_id", "licence", "selfie", + "vehicle_registration", "insurance"}; + private static final String[] KIND_NAMES = {"Photo ID", "Onboarding licence", "A photo of you", + "Vehicle registration", "Insurance"}; + + private final Panes panes; + private final Container list = Ui.list("applications"); + private String status = "pending"; + + private ApplicationsForm(Panes panes) { + this.panes = panes; + Container top = new Container(BoxLayout.y()); + top.setUIID("WlPage"); + top.add(Ui.choice("status", new String[] {"Waiting", "Approved", "Sent back"}, + new String[] {"pending", "approved", "rejected"}, status, value -> { + status = value; + load(); + })); + Ui.pull(list, this::load); + panes.list("Applications", "Applications", Panes.listed(top, list), this::load); + } + + static void show(Form previous) { + show(Panes.phone(previous)); + } + + static void show(Panes panes) { + new ApplicationsForm(panes); + } + + private void load() { + final String asked = status; + Api.admin().applications(asked, Net.to(found -> { + if (asked.equals(status)) { + fill(found); + } + }, Ui.failed(list, this::load))); + } + + private void fill(List found) { + list.removeAll(); + if (found == null || found.isEmpty()) { + list.add(Ui.empty(FontImage.MATERIAL_INBOX, "Nothing here", + "pending".equals(status) ? "Every application has had its answer." : null)); + } + for (int iter = 0; found != null && iter < found.size(); iter++) { + final CourierApplicationDto application = found.get(iter); + Container row = Ui.row((char) 0, application.name(), car(application), + Ui.badge(word(application.status()), badge(application.status()))); + row.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle( + Ui.avatar(application.name()))); + final Container picked = row; + list.add(Ui.tap(row, "application-" + application.username(), e -> { + panes.picked(picked); + review(application.username()); + })); + } + panes.refresh(); + } + + /// One application in full, fetched fresh: it may have been answered by + /// another admin since the list was drawn. + private void review(final String username) { + Api.admin().application(username, Net.to(application -> show(application), Ui::fail)); + } + + private void show(final CourierApplicationDto application) { + Container page = Ui.page(); + Label state = Ui.badge(word(application.status()), badge(application.status())); + state.setName("applicationStatus"); + page.add(Ui.person(application.name(), application.username(), state)); + if ("rejected".equals(application.status())) { + page.add(Ui.text(application.rejectionReason(), "WlNotice")); + } + + Ui.section(page, "Applicant"); + page.add(Ui.fact("Legal name", application.legalName())); + page.add(Ui.fact("Date of birth", application.dateOfBirth())); + page.add(Ui.fact("Licence", application.licenceNumber())); + page.add(Ui.fact("Licence expires", application.licenceExpiry())); + if (application.submittedAt() > 0) { + page.add(Ui.fact("Submitted", Ui.when(application.submittedAt()))); + } + if (application.reviewedAt() > 0) { + page.add(Ui.fact("Reviewed", Ui.when(application.reviewedAt()))); + page.add(Ui.fact("Reviewed by", application.reviewedBy())); + } + + Ui.section(page, "Vehicle"); + page.add(Ui.fact("Car", car(application))); + page.add(Ui.fact("Licence plate", application.vehiclePlate())); + page.add(Ui.fact("Seats for customers", String.valueOf(application.vehicleSeats()))); + page.add(Ui.fact("Kind of delivery", Lang.tr(Ui.product(application.product())))); + page.add(Ui.fact("Takes a wheelchair", Lang.tr(application.accessible() ? "Yes" : "No"))); + page.add(Ui.fact("Takes pets", Lang.tr(application.petFriendly() ? "Yes" : "No"))); + + Ui.section(page, "Documents"); + for (int iter = 0; iter < KINDS.length; iter++) { + final String kind = KINDS[iter]; + final String title = KIND_NAMES[iter]; + String had = status(application, kind); + boolean missing = "missing".equals(had); + Container row = Ui.row(FontImage.MATERIAL_IMAGE, title, + missing ? "Not provided" : "Tap to open", Ui.badge( + missing ? "Missing" : "Provided", + missing ? "WlBadgeBad" : "WlBadgeGood")); + if (missing) { + page.add(row); + } else { + page.add(Ui.tap(row, "document-" + kind, + e -> picture(application.username(), kind, title))); + } + } + + if ("pending".equals(application.status())) { + page.add(Ui.primary("Approve", "approve", e -> Api.admin().approve( + application.username(), Net.to(approved -> { + Telemetry.applicationReviewed(true); + Ui.say(Lang.tr("{0} can now drive", application.name())); + panes.home(); + }, Ui::fail)))); + page.add(Ui.danger("Send back", "reject", e -> panes.reason("Send back", + Lang.tr("{0} sees what you write, and can apply again once it is put " + + "right.", application.name()), "Send back", why -> { + ReasonDto reason = new ReasonDto(why); + Api.admin().reject(application.username(), reason, Net.to(rejected -> { + Telemetry.applicationReviewed(false); + panes.home(); + }, Ui::fail)); + }))); + } + panes.detail(application.name(), "ApplicationReview", page); + } + + /// A document, as large as the screen allows. + private void picture(String username, String kind, String title) { + final Container stage = new Container(new BorderLayout()); + stage.setUIID("WlPicture"); + panes.over(title, "Document", stage); + Api.admin().document(username, kind, Net.to(content -> { + Image image = decode(content); + if (image == null) { + stage.add(BorderLayout.CENTER, Ui.empty(FontImage.MATERIAL_BROKEN_IMAGE, + "This picture could not be opened", null)); + } else { + ScaleImageLabel shown = new ScaleImageLabel(image); + shown.setUIID("WlPicture"); + shown.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT); + shown.setName("picture"); + stage.add(BorderLayout.CENTER, shown); + } + panes.refresh(); + }, Ui::fail)); + } + + private static Image decode(DocumentContentDto content) { + if (content == null || content.dataBase64() == null || content.dataBase64().length() == 0) { + return null; + } + try { + byte[] data = Base64.decode(content.dataBase64().getBytes("UTF-8")); + return data == null ? null : Image.createImage(data, 0, data.length); + } catch (java.io.UnsupportedEncodingException impossible) { + Log.e(impossible); + return null; + } catch (RuntimeException unreadable) { + // Not a picture after all; the screen says so. + Log.e(unreadable); + Telemetry.error(unreadable); + return null; + } + } + + private static String status(CourierApplicationDto application, String kind) { + for (int iter = 0; application.documents() != null && iter < application.documents().size(); + iter++) { + DocumentDto document = application.documents().get(iter); + if (kind.equals(document.kind())) { + return document.status(); + } + } + return "missing"; + } + + private static String car(CourierApplicationDto application) { + StringBuilder car = new StringBuilder(); + String[] words = {application.vehicleColor(), application.vehicleMake(), + application.vehicleModel(), + application.vehicleYear() > 0 ? String.valueOf(application.vehicleYear()) : ""}; + for (int iter = 0; iter < words.length; iter++) { + if (words[iter] != null && words[iter].length() > 0) { + car.append(car.length() > 0 ? " " : "").append(words[iter]); + } + } + return car.toString(); + } + + static String word(String status) { + return "pending".equals(status) ? "Waiting" : "approved".equals(status) ? "Approved" + : "rejected".equals(status) ? "Sent back" : "Draft"; + } + + static String badge(String status) { + return "pending".equals(status) ? "WlBadgeWarn" : "approved".equals(status) + ? "WlBadgeGood" : "rejected".equals(status) ? "WlBadgeBad" : "WlBadge"; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java new file mode 100644 index 00000000000..6be76a60c7f --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Command; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Layouts; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.SettingsForm; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.ArrayList; +import java.util.List; + +/// The admin console on a wide screen: a tablet on its side, a desktop window, +/// a browser. +/// +/// One screen, in three parts. The places an admin goes are always in view +/// down the side, where a phone keeps them in a drawer. Beside them is the +/// place that is open: the dashboard, or a list. And beside a list is what was +/// picked from it -- an application, an account, a delivery, a receipt -- so that +/// working through a list does not mean going back to it each time. +/// +/// The parts themselves are the phone's. Each is written against [Panes], and +/// the ones here put a list and its detail side by side where the phone's +/// make each a screen. +/// +/// On a desktop the window has a menu bar, and the places are in it too, each +/// with a key. Where the platform's own theme is installed the console wears +/// it: see [Layouts#dress(Container)]. +final class Console { + private static final int POLL_MILLIS = 5000; + private static final String HOME = "home"; + private static final String FLEET = "fleet"; + private static final float SIDE_MM = 56f; + private static final float MASTER_MM = 100f; + + /// The places, in the order they are listed: an id, and what it is called. + private static final String[][] PLACES = { + {HOME, "Dashboard"}, {"applications", "Applications"}, {"users", "People"}, + {"deliveries", "Deliveries"}, {"payments", "Payments"}, {FLEET, "Live map"}, + {"pricing", "Pricing"}}; + private static final char[] ICONS = {FontImage.MATERIAL_INSIGHTS, + FontImage.MATERIAL_ASSIGNMENT_IND, FontImage.MATERIAL_GROUP, + FontImage.MATERIAL_RECEIPT_LONG, FontImage.MATERIAL_PAYMENTS, FontImage.MATERIAL_MAP, + FontImage.MATERIAL_SELL}; + + /// The place that was open when the window stopped being wide, or the + /// device was turned: where the console opens when it is wide again. + private static String resume = HOME; + + private final Form form = new Form("", new BorderLayout()); + private final Container side; + private final Container body = new Container(new BorderLayout()); + private final Button[] items = new Button[PLACES.length]; + private final Wide panes = new Wide(); + private final Dashboard dashboard; + /// Whether the window, and not the app, has the title bar and the menu. + private final boolean window; + private String place = HOME; + + Console() { + form.setUIID("WlForm"); + form.setName("Admin"); + Ui.watch(form); + form.setScrollable(false); + window = CN.isDesktop() && form.getToolbar().getParent() == null; + if (!window) { + // Everything a title bar would say is down the side. + form.getToolbar().hideToolbar(); + } + dashboard = new Dashboard(true, new Dashboard.Places() { + @Override + public void applications() { + go("applications"); + } + + @Override + public void flagged() { + mark("users"); + UsersForm.show(panes, UsersForm.FLAGGED); + } + }); + side = side(); + Container screen = new Container(new BorderLayout()); + screen.setSafeArea(true); + screen.add(BorderLayout.WEST, side); + screen.add(BorderLayout.CENTER, body); + form.add(BorderLayout.CENTER, screen); + if (window) { + menus(); + } + form.addShowListener(e -> { + if (!Layouts.wide()) { + // It was left wide, for the map or the settings, and the + // window was made narrow while it was away. + narrowed(); + return; + } + if (HOME.equals(place)) { + dashboard.shown(); + } + }); + form.addSizeChangedListener(e -> dashboard.fit()); + Layouts.onChange(form, this::narrowed); + UITimer.timer(POLL_MILLIS, true, form, () -> { + if (HOME.equals(place)) { + dashboard.load(); + } + }); + } + + Form form() { + return form; + } + + /// Fills the console in, at the place it was last on. + void start() { + dashboard.start(); + String to = resume; + resume = HOME; + go(to); + } + + private void narrowed() { + resume = place; + Nav.home(); + } + + /// The place a narrow console should open, having just stopped being a + /// wide one; null when there is none, which is the dashboard. + static String resumed() { + String to = resume; + resume = HOME; + return HOME.equals(to) ? null : to; + } + + /// Opens one of the places. + private void go(String id) { + if (FLEET.equals(id)) { + // A map wants the whole window. + new FleetForm(form).show(); + return; + } + mark(id); + if ("applications".equals(id)) { + ApplicationsForm.show(panes); + } else if ("users".equals(id)) { + UsersForm.show(panes, ""); + } else if ("deliveries".equals(id)) { + AdminDeliveriesForm.show(panes); + } else if ("payments".equals(id)) { + PaymentsForm.show(panes); + } else if ("pricing".equals(id)) { + PricingForm.show(panes); + } else { + panes.clear(); + body.removeAll(); + body.add(BorderLayout.CENTER, dashboard.page()); + title("Dashboard"); + Ui.viewed("Admin"); + panes.refresh(); + if (form.isVisible() && CN.getCurrentForm() == form) { + dashboard.shown(); + } + } + } + + /// Shows `id` as the place that is open. + private void mark(String id) { + place = id; + for (int iter = 0; iter < items.length; iter++) { + items[iter].setUIID(PLACES[iter][0].equals(id) ? "WlSidePicked" : "WlSideItem"); + } + Layouts.dress(side); + // An icon is drawn in the colour of the style it was made for. + for (int iter = 0; iter < items.length; iter++) { + Ui.icon(items[iter], ICONS[iter], 3.4f); + } + } + + private void title(String title) { + if (window) { + form.setTitle(Lang.tr(title)); + } + } + + private Container side() { + Container panel = new Container(new BorderLayout()) { + @Override + protected Dimension calcPreferredSize() { + Dimension size = super.calcPreferredSize(); + size.setWidth(CN.convertToPixels(SIDE_MM)); + return size; + } + }; + panel.setUIID("WlSide"); + panel.setName("side"); + UserDto user = Session.user(); + Container who = new Container(BoxLayout.y()); + who.add(Ui.plain(user.displayName(), "WlSideName")); + who.add(Ui.plain(user.username(), "WlSideDetail")); + + Container list = new Container(BoxLayout.y()); + list.setScrollableY(true); + list.setScrollVisible(false); + for (int iter = 0; iter < PLACES.length; iter++) { + final String id = PLACES[iter][0]; + items[iter] = entry(ICONS[iter], PLACES[iter][1], "nav-" + id, () -> go(id)); + list.add(items[iter]); + } + // The other modes this account's roles open. Everyone may delivery. + list.add(Ui.label("Switch to", "WlSideLabel")); + list.add(entry(FontImage.MATERIAL_HAIL, "Riding", "switch-" + Nav.CUSTOMER, + () -> Nav.switchTo(Nav.CUSTOMER))); + if (user.courier()) { + list.add(entry(FontImage.MATERIAL_DIRECTIONS_CAR, "Onboarding", "switch-" + Nav.COURIER, + () -> Nav.switchTo(Nav.COURIER))); + } + + Container bottom = new Container(BoxLayout.y()); + bottom.add(entry(FontImage.MATERIAL_SETTINGS, "Settings", "menu-settings", + () -> SettingsForm.show(form))); + bottom.add(entry(FontImage.MATERIAL_LOGOUT, "Sign out", "menu-signOut", Nav::signOut)); + + panel.add(BorderLayout.NORTH, who); + panel.add(BorderLayout.CENTER, list); + panel.add(BorderLayout.SOUTH, bottom); + return panel; + } + + private static Button entry(char icon, String text, String name, final Runnable go) { + Button entry = new Button(text, "WlSideItem"); + entry.setName(name); + Ui.icon(entry, icon, 3.4f); + entry.addActionListener(e -> go.run()); + return entry; + } + + /// The window's menu bar: the places under Go, each with a key, and + /// Settings and signing out where each platform keeps them. Called only + /// where the window has a menu bar, which is where commands put in the + /// title bar's side menu are shown. + private void menus() { + for (int iter = 0; iter < PLACES.length; iter++) { + final String id = PLACES[iter][0]; + Command go = Command.create(Lang.tr(PLACES[iter][1]), null, e -> go(id)); + go.setDesktopMenu(Lang.tr("Go")); + go.setDesktopShortcut((char) ('1' + iter)); + form.getToolbar().addCommandToSideMenu(go); + } + Command settings = Command.create(Lang.tr("Settings"), null, + e -> SettingsForm.show(form)); + settings.setDesktopMenu(Command.DESKTOP_MENU_PREFERENCES); + settings.setDesktopShortcut(','); + form.getToolbar().addCommandToSideMenu(settings); + Command signOut = Command.create(Lang.tr("Sign out"), null, e -> Nav.signOut()); + signOut.setDesktopMenu(Command.DESKTOP_MENU_FILE); + form.getToolbar().addCommandToSideMenu(signOut); + } + + /// One thing shown in the detail's place. + private static final class Shown { + final String title; + final String name; + final Component content; + + Shown(String title, String name, Component content) { + this.title = title; + this.name = name; + this.content = content; + } + } + + /// The panes of a wide screen: the list on the left of the room, and what + /// is opened from it on the right. + private final class Wide extends Panes { + private final List open = new ArrayList(); + private final Container detail = new Container(new BorderLayout()); + private Runnable shown; + private Component picked; + + void clear() { + open.clear(); + shown = null; + picked = null; + } + + @Override + void list(String title, String name, Component content, Runnable shown) { + clear(); + this.shown = shown; + Container master = new Container(new BorderLayout()) { + @Override + protected Dimension calcPreferredSize() { + Dimension size = super.calcPreferredSize(); + // A list is as wide as a phone, and never most of the room. + size.setWidth(Math.min(CN.convertToPixels(MASTER_MM), + body.getWidth() > 0 ? body.getWidth() / 2 : Integer.MAX_VALUE)); + return size; + } + }; + master.setUIID("WlMaster"); + master.setName(name); + master.add(BorderLayout.CENTER, content); + body.removeAll(); + body.add(BorderLayout.WEST, master); + body.add(BorderLayout.CENTER, detail); + title(title); + Ui.viewed(name); + show(); + shown.run(); + } + + @Override + void page(String title, String name, Component content) { + clear(); + open.add(new Shown(title, name, content)); + body.removeAll(); + body.add(BorderLayout.CENTER, detail); + title(title); + show(); + } + + @Override + void detail(String title, String name, Component content) { + open.clear(); + over(title, name, content); + } + + @Override + void over(String title, String name, Component content) { + open.add(new Shown(title, name, content)); + show(); + } + + @Override + void back() { + if (!open.isEmpty()) { + open.remove(open.size() - 1); + } + show(); + } + + @Override + void home() { + open.clear(); + picked = null; + show(); + if (shown != null) { + shown.run(); + } + } + + @Override + void refresh() { + Layouts.dress(form); + Ui.refresh(form); + } + + @Override + Form form() { + return form; + } + + @Override + void picked(Component row) { + if (picked != null) { + picked.setUIID("WlRow"); + } + picked = row; + row.setUIID("WlRowPicked"); + } + + /// Puts what is on top in the detail's place, or says that nothing is + /// picked. + private void show() { + detail.removeAll(); + if (open.isEmpty()) { + Container none = Ui.empty(FontImage.MATERIAL_TOUCH_APP, "Nothing selected", + "Pick a row from the list to see it here."); + none.setName("nothing"); + detail.add(BorderLayout.CENTER, none); + } else { + Shown top = open.get(open.size() - 1); + Ui.viewed(top.name); + Container bar = new Container(new BorderLayout()); + bar.setUIID("WlPaneBar"); + // Something shown over the detail is left the way it is on a + // phone: back. The detail itself has nowhere to go back to. + if (open.size() > 1) { + Button back = Ui.flat(FontImage.MATERIAL_ARROW_BACK, "pane-back", + e -> back()); + bar.add(BorderLayout.WEST, back); + } + bar.add(BorderLayout.CENTER, Ui.label(top.title, "WlPaneTitle")); + Container holder = new Container(new BorderLayout()); + holder.setName(top.name); + // It may still be in the holder it was shown in before + // something was put over it. + top.content.remove(); + holder.add(BorderLayout.CENTER, top.content); + detail.add(BorderLayout.NORTH, bar); + detail.add(BorderLayout.CENTER, holder); + } + refresh(); + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java new file mode 100644 index 00000000000..b2f6e6fae88 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codenameone.examples.morsel.api.AdminStatsDto; +import com.codenameone.examples.morsel.api.DayStatsDto; +import com.codenameone.examples.morsel.api.CourierDto; +import com.codenameone.examples.morsel.api.NameCountDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.api.StatsSeriesDto; +import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Chart; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Layouts; +import com.codenameone.examples.morsel.ui.StatRow; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// The numbers of the admin console: what needs an answer, how the service is +/// doing right now, and how it has done over the last weeks. +/// +/// It is the same on a phone and on a wide screen, laid out for the room there +/// is. On a phone everything is one column, with the four numbers two by two. +/// On a wide screen the numbers are one row and the charts are side by side, +/// in as many columns as fit -- see [#fit()], which is what a resized window +/// calls. +final class Dashboard extends LiveChannel.Adapter { + /// A chart is not drawn narrower than this, in millimetres. + private static final float TILE_MM = 88f; + + /// Where the things that want attention lead. + interface Places { + void applications(); + + void flagged(); + } + + private final boolean wide; + private final Places places; + private final Container page; + private final Container alerts = new Container(BoxLayout.y()); + private final StatRow numbers; + private final Container series = new Container(BoxLayout.y()); + private int days = 14; + private int columns; + private StatsSeriesDto last; + + /// @param wide whether to lay out for a wide screen + Dashboard(boolean wide, Places places) { + this.wide = wide; + this.places = places; + numbers = Ui.stats(wide ? 4 : 2); + if (wide) { + // Not Ui.page(): that keeps to a column, and this is the one page + // that is meant to use the width. + page = new Container(BoxLayout.y()); + page.setUIID("WlPage"); + page.setScrollableY(true); + page.setScrollVisible(false); + } else { + page = Ui.page(); + page.setSafeArea(false); + } + page.setName("dashboard"); + alerts.setName("alerts"); + page.add(alerts); + numbers.setName("stats"); + Ui.section(page, "Right now"); + page.add(numbers); + Ui.section(page, "Trends"); + Container range = Ui.choice("range", new String[] {"7 days", "14 days", "30 days"}, + new String[] {"7", "14", "30"}, String.valueOf(days), value -> { + days = Integer.parseInt(value); + loadSeries(); + }); + // On a wide screen the choice is as wide as it needs to be, not the + // whole width of the window. + page.add(wide ? FlowLayout.encloseIn(range) : range); + series.setName("series"); + page.add(series); + } + + /// The page, for the screen to place. + Container page() { + return page; + } + + /// Shows the numbers as nothing, before the first answer. + void start() { + fill(new AdminStatsDto(0, 0, 0, 0, 0, 0, 0L, null)); + } + + /// The page came into view: everything is fetched again. + void shown() { + LiveChannel.listen(this); + load(); + loadSeries(); + } + + @Override + public void deliveryChanged(String id, String state) { + load(); + } + + void load() { + Api.admin().stats(Net.to(this::fill, (status, message) -> { })); + } + + private void loadSeries() { + final int asked = days; + Api.admin().statsSeries(asked, Net.to(found -> { + if (asked == days) { + fill(found); + } + }, (status, message) -> { })); + } + + /// The room changed: the charts are laid out again if a different number + /// of them now fits side by side. + void fit() { + if (wide && last != null && columns != columns()) { + fill(last); + } + } + + private int columns() { + if (!wide) { + return 1; + } + int width = page.getWidth() > 0 ? page.getWidth() : CN.getDisplayWidth() * 3 / 4; + return Layouts.columns(width, TILE_MM, 3); + } + + private void refresh() { + Layouts.dress(page); + Form form = page.getComponentForm(); + if (form != null) { + Ui.refresh(form); + } + } + + private void fill(AdminStatsDto stats) { + numbers.clear(); + numbers.stat(String.valueOf(stats.deliveriesToday()), "Deliveries today", "deliveriesToday"); + numbers.stat(String.valueOf(stats.deliveriesActive()), "Under way", "deliveriesActive"); + numbers.stat(stats.couriersOnline() + " / " + stats.couriers(), "Couriers online", + "couriersOnline"); + numbers.stat(Ui.money(stats.revenueCents(), stats.currency()), "Fares collected", + "revenue"); + refresh(); + } + + private void fill(StatsSeriesDto found) { + last = found; + alerts.removeAll(); + if (found.pendingApplications() > 0) { + alerts.add(alert(FontImage.MATERIAL_ASSIGNMENT_IND, found.pendingApplications() == 1 + ? Lang.tr("1 application to review") + : Lang.tr("{0} applications to review", + String.valueOf(found.pendingApplications())), "alert-applications", + places::applications)); + } + if (found.flaggedUsers() > 0) { + alerts.add(alert(FontImage.MATERIAL_FLAG, found.flaggedUsers() == 1 + ? Lang.tr("1 flagged account") : Lang.tr("{0} flagged accounts", + String.valueOf(found.flaggedUsers())), "alert-flagged", + places::flagged)); + } + series.removeAll(); + int count = found.days() == null ? 0 : found.days().size(); + double[] deliveries = new double[count]; + double[] revenue = new double[count]; + int asked = 0; + int completed = 0; + int cancelled = 0; + long taken = 0; + int joined = 0; + for (int iter = 0; iter < count; iter++) { + DayStatsDto day = found.days().get(iter); + deliveries[iter] = day.deliveries(); + revenue[iter] = day.revenue(); + asked += day.deliveries(); + completed += day.completed(); + cancelled += day.cancelled(); + taken += day.revenue(); + joined += day.newUsers(); + } + + StatRow rates = Ui.stats(3); + rates.stat(Math.round(found.completionRate() * 100d) + "%", "Completed", + "completionRate"); + rates.stat(Ui.money(found.averageFare(), found.currency()), "Average fare", + "averageFare"); + rates.stat(wait(found.averageWaitSeconds()), "Average wait", "averageWait"); + + // One column on a phone, in the order they are added. On a wide screen + // as many columns as fit, dealt out in turn: each tile keeps its own + // height, which a grid of tiles would not let it. + columns = columns(); + Container[] column = new Container[columns]; + if (columns == 1) { + column[0] = series; + } else { + series.add(rates); + Container grid = new Container(new GridLayout(1, columns)); + for (int iter = 0; iter < columns; iter++) { + column[iter] = new Container(BoxLayout.y()); + grid.add(column[iter]); + } + series.add(grid); + } + int next = 0; + String from = Lang.tr("{0} days ago", String.valueOf(Math.max(1, count - 1))); + String to = Lang.tr("Today"); + column[next++ % columns].add(tile("Deliveries", String.valueOf(asked), "seriesDeliveries", + Lang.tr("{0} completed, {1} cancelled", String.valueOf(completed), + String.valueOf(cancelled)), + Chart.of("chartDeliveries", deliveries, from, to))); + column[next++ % columns].add(tile("Fares", Ui.money(taken, found.currency()), + "seriesRevenue", Lang.tr("{0} new accounts", String.valueOf(joined)), + Chart.of("chartRevenue", revenue, from, to))); + if (columns == 1) { + series.add(rates); + } + + int hours = found.byHour() == null ? 0 : found.byHour().size(); + double[] hourly = new double[hours]; + for (int iter = 0; iter < hours; iter++) { + Integer value = found.byHour().get(iter); + hourly[iter] = value == null ? 0 : value.intValue(); + } + column[next++ % columns].add(tile("Requests by hour", null, null, + "The busy hours, in UTC", Chart.of("chartHours", hourly, "00:00", "23:00"))); + column[next++ % columns].add(shares("By outcome", found.byState(), true)); + column[next++ % columns].add(shares("By kind of delivery", found.byProduct(), false)); + + if (found.topCouriers() != null && !found.topCouriers().isEmpty()) { + Container top = box("Top couriers"); + for (int iter = 0; iter < found.topCouriers().size(); iter++) { + CourierDto courier = found.topCouriers().get(iter); + Container row = Ui.row((char) 0, courier.displayName(), courier.vehicle(), + Ui.plain(Lang.tr("{0} deliveries", String.valueOf(courier.deliveries())), + "WlRowValue")); + row.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle( + Ui.avatar(courier.displayName()))); + top.add(row); + } + column[next++ % columns].add(top); + } + refresh(); + } + + /// Something that wants the admin's attention, as a button that goes to it. + private static Button alert(char icon, String text, String name, final Runnable go) { + Button alert = new Button("", "WlAlert"); + alert.setShouldLocalize(false); + alert.setText(text); + alert.setName(name); + Ui.icon(alert, icon, 3.4f); + alert.addActionListener(e -> go.run()); + return alert; + } + + private static Container box(String title) { + Container box = new Container(BoxLayout.y()); + box.setUIID("WlTile"); + box.add(Ui.label(title, "WlStatLabel")); + return box; + } + + /// A chart with what it adds up to written over it. + private static Container tile(String title, String value, String name, String detail, + Container chart) { + Container tile = box(title); + if (value != null) { + Label total = Ui.plain(value, "WlChartValue"); + total.setName(name); + tile.add(total); + } + tile.add(Ui.plain(Lang.tr(detail), "WlMuted")); + tile.add(chart); + return tile; + } + + /// How a total divides: each part with its count and its share as a bar. + private static Container shares(String title, List parts, boolean states) { + Container tile = box(title); + long total = 0; + for (int iter = 0; parts != null && iter < parts.size(); iter++) { + total += parts.get(iter).count(); + } + for (int iter = 0; parts != null && iter < parts.size(); iter++) { + NameCountDto part = parts.get(iter); + String name = states ? Ui.state(DeliveryState.named(part.name())) : Ui.product(part.name()); + tile.add(Ui.fact(name.length() == 0 ? part.name() : name, + String.valueOf(part.count()))); + // A total of nothing has no shares; the bars are then empty. + tile.add(new Chart.Meter(total == 0 ? 0 : part.count() / (double) total)); + } + return tile; + } + + private static String wait(double seconds) { + if (seconds < 90) { + return Lang.tr("{0} s", String.valueOf(Math.round(seconds))); + } + return Ui.minutes(seconds); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java new file mode 100644 index 00000000000..063979aebbc --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.maps.LatLng; +import com.codename1.ui.Command; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codenameone.examples.morsel.api.CourierDto; +import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.map.CarMarker; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.map.MapStage; +import com.codenameone.examples.morsel.map.Maps; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; +import com.codename1.ui.util.UITimer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/// Every car online, on one map, moving as the couriers report in. +public class FleetForm extends LiveChannel.Adapter { + private static final int POLL_MILLIS = 10000; + + private final Form form = Ui.home("Fleet"); + private final MapStage stage; + private final Label count = Ui.label("", "WlHeading"); + private final Map cars = new HashMap(); + + FleetForm(final Form previous) { + // No title bar to carry the back arrow, so the round button over the + // map is it -- and the screen's back command too, which is what the + // device's own back button and gesture run. + Command back = Command.create("", null, e -> previous.showBack()); + form.setBackCommand(back); + stage = new MapStage(Locator.here(), Maps.CITY_ZOOM, + Ui.round(FontImage.MATERIAL_ARROW_BACK, "back", e -> previous.showBack())); + stage.header(Ui.label("Live map", "WlMapTitle")); + stage.addRound(FontImage.MATERIAL_ZOOM_OUT_MAP, "recentre", this::frame); + count.setName("fleetCount"); + stage.sheet().peek().add(count); + stage.sheet().peek().add(Ui.label("Cars move as their couriers report in.", "WlMuted")); + stage.sheet().ready(false); + form.add(stage.layers()); + form.addShowListener(e -> { + LiveChannel.listen(this); + load(); + }); + // The channel moves the cars; this catches the one whose courier went + // quiet without saying so. + UITimer.timer(POLL_MILLIS, true, form, this::load); + } + + void show() { + counted(); + form.show(); + } + + private void load() { + Api.admin().couriers(Net.to(this::fill, Ui::fail)); + } + + private void fill(List couriers) { + Map gone = new HashMap(cars); + for (int iter = 0; couriers != null && iter < couriers.size(); iter++) { + CourierDto courier = couriers.get(iter); + if (courier.online()) { + gone.remove(courier.username()); + place(courier.username(), courier.lat(), courier.lng(), courier.heading()); + } + } + for (Map.Entry left : gone.entrySet()) { + left.getValue().remove(); + cars.remove(left.getKey()); + } + counted(); + } + + @Override + public void fleetChanged(String username, boolean online, double lat, double lng, + double heading) { + if (online) { + place(username, lat, lng, heading); + } else { + CarMarker car = cars.remove(username); + if (car != null) { + car.remove(); + } + } + counted(); + } + + private void place(String username, double lat, double lng, double heading) { + LatLng at = new LatLng(lat, lng); + CarMarker car = cars.get(username); + if (car == null) { + cars.put(username, new CarMarker(stage.map, form, at, heading)); + } else { + car.moveTo(at, heading); + } + } + + /// Moves the camera to take in every car there is. + private void frame() { + List all = new ArrayList(); + for (CarMarker car : cars.values()) { + all.add(car.position()); + } + if (all.isEmpty()) { + stage.map.moveCamera(Locator.here(), Maps.CITY_ZOOM); + } else { + stage.frame(all.toArray(new LatLng[all.size()])); + } + } + + private void counted() { + int size = cars.size(); + count.setText(size == 0 ? Lang.tr("No cars online") : size == 1 + ? Lang.tr("1 car online") : Lang.tr("{0} cars online", String.valueOf(size))); + stage.map.repaint(); + Ui.refresh(form); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java new file mode 100644 index 00000000000..32ac1c0a024 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.ui.Layouts; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.ArrayList; +import java.util.List; + +/// Where a part of the admin console shows what it has. +/// +/// Every part is a list and what is opened from it: an application, an +/// account, a delivery. On a phone each of those is a screen of its own, reached +/// from the one before and left with the back arrow. On a wide screen the list +/// stays where it is and what is opened from it is shown beside it. The parts +/// are written once, against this, and do not know which they are in: +/// +/// - [#list] shows the list. +/// - [#detail] shows what was picked from it, in place of whatever was open. +/// - [#over] shows something on top of that: a document, the question "why?". +/// - [#back] leaves the last of those; [#home] leaves all of them. +/// +/// [#phone(Form)] is the phone's. The wide one is [Console]'s. +abstract class Panes { + /// Shows a part's list. + /// + /// @param name what a test waits for + /// @param shown run each time the list comes into view, to fetch what is in it + abstract void list(String title, String name, Component content, Runnable shown); + + /// Shows a part that is one page and has nothing to open: the prices. + abstract void page(String title, String name, Component content); + + /// Shows what was picked from the list, in place of whatever was open. + abstract void detail(String title, String name, Component content); + + /// Shows `content` on top of what is open. + abstract void over(String title, String name, Component content); + + /// Leaves what is on top. + abstract void back(); + + /// Back to the list alone, which is fetched again. + abstract void home(); + + /// Lays out again what is showing, after it changed. + abstract void refresh(); + + /// The screen all of this is on, for something that needs one to return to. + abstract Form form(); + + /// Marks `row` as the one that is open, where the list stays in view. + void picked(Component row) { + } + + /// Asks why, on top of what is open. See [Ui#reason]. + void reason(String title, String text, String action, Ui.Reason reason) { + over(title, "Reason", Ui.reasonPage(text, action, reason)); + } + + /// The panes of a phone: a screen each. + /// + /// @param previous where the back arrow of the list leads + static Panes phone(final Form previous) { + return new Panes() { + private final List

screens = new ArrayList(); + + @Override + void list(String title, String name, Component content, final Runnable shown) { + Form form = Ui.form(title, name); + Ui.back(form, previous); + form.add(BorderLayout.CENTER, content); + form.addShowListener(e -> shown.run()); + watch(form); + screens.clear(); + screens.add(form); + form.show(); + } + + @Override + void page(String title, String name, Component content) { + list(title, name, content, () -> { }); + } + + @Override + void detail(String title, String name, Component content) { + while (screens.size() > 1) { + screens.remove(screens.size() - 1); + } + over(title, name, content); + } + + @Override + void over(String title, String name, Component content) { + Form form = Ui.form(title, name); + Ui.back(form, this::back); + form.add(BorderLayout.CENTER, content); + watch(form); + screens.add(form); + form.show(); + } + + /// A screen made for a narrow window is of no use to a wide one: + /// the console is built again, as the wide one. And on a desktop + /// a narrow window still wears the desktop's look. + private void watch(Form form) { + Layouts.dress(form); + Layouts.onChange(form, Nav::home); + } + + @Override + void back() { + if (screens.size() > 1) { + screens.remove(screens.size() - 1); + } + form().showBack(); + } + + @Override + void home() { + while (screens.size() > 1) { + screens.remove(screens.size() - 1); + } + form().showBack(); + } + + @Override + void refresh() { + Layouts.dress(form()); + Ui.refresh(form()); + } + + @Override + Form form() { + return screens.get(screens.size() - 1); + } + }; + } + + /// A list with what filters it above it, as the content of [#list]. + static Container listed(Component filters, Component list) { + Container content = new Container(new BorderLayout()); + if (filters != null) { + content.add(BorderLayout.NORTH, filters); + } + content.add(BorderLayout.CENTER, list); + return content; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java new file mode 100644 index 00000000000..0527b514a43 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.ReasonDto; +import com.codenameone.examples.morsel.api.ReceiptDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.pay.ReceiptForm; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// What was charged, newest first, and for each whether it went through. A +/// payment opens as its receipt, which is where an admin returns the money. +public final class PaymentsForm { + private PaymentsForm() { + } + + static void show(Form previous) { + show(Panes.phone(previous)); + } + + static void show(final Panes panes) { + final Container list = Ui.list("payments"); + final Runnable[] load = new Runnable[1]; + load[0] = () -> Api.admin().payments(Net.to(found -> fill(panes, list, found), + Ui.failed(list, () -> load[0].run()))); + panes.list("Payments", "Payments", Panes.listed(null, list), load[0]); + } + + /// A payment, opened: its receipt, and the way to return the money. + private static void open(final Panes panes, final ReceiptDto payment) { + Container page = ReceiptForm.page(payment, true); + if ("paid".equals(payment.status())) { + page.add(Ui.danger("Refund this delivery", "refund", e -> panes.reason("Refund", + Lang.tr("{0} goes back to the card it came from.", + Ui.money(payment.total(), payment.currency())), "Refund", why -> { + ReasonDto reason = new ReasonDto(why); + Api.admin().refundDelivery(payment.deliveryId(), reason, Net.to(refunded -> { + Telemetry.deliveryRefunded("payments"); + open(panes, refunded); + }, Ui::fail)); + }))); + } + panes.detail("Receipt", "Receipt", page); + } + + private static void fill(final Panes panes, Container list, List payments) { + list.removeAll(); + if (payments == null || payments.isEmpty()) { + list.add(Ui.empty(FontImage.MATERIAL_PAYMENTS, "No payments yet", + "A delivery shows up here when it is paid for.")); + } + for (int iter = 0; payments != null && iter < payments.size(); iter++) { + final ReceiptDto payment = payments.get(iter); + Container value = new Container(BoxLayout.y()); + value.add(Ui.plain(Ui.money(payment.total(), payment.currency()), "WlRowValue")); + value.add(Ui.badge(ReceiptForm.status(payment.status()), + ReceiptForm.badge(payment.status()))); + String who = payment.customerUsername() == null ? "" : payment.customerUsername(); + String how = "cash".equals(payment.status()) ? Lang.tr("Cash") : payment.methodLabel(); + final Container picked = Ui.row("cash".equals(payment.status()) + ? FontImage.MATERIAL_PAYMENTS : FontImage.MATERIAL_CREDIT_CARD, + payment.dropoffName(), + who + (who.length() > 0 && how != null && how.length() > 0 ? Ui.DOT : "") + + (how == null ? "" : how), value); + list.add(Ui.tap(picked, "payment-" + iter, e -> { + panes.picked(picked); + open(panes, payment); + })); + } + panes.refresh(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java new file mode 100644 index 00000000000..60f982809c3 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.PricingDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; + +/// What a delivery costs: the parts of a fare, the service's share, and what the +/// roomier cars and the busy hours multiply it by. +/// +/// A change is for the deliveries asked for from then on; a delivery already quoted +/// keeps its price. The server refuses numbers that could only be a mistake, +/// and says which. +public final class PricingForm { + private PricingForm() { + } + + static void show(final Form previous) { + show(Panes.phone(previous)); + } + + static void show(final Panes panes) { + final Container page = Ui.page(); + panes.page("Pricing", "Pricing", page); + Api.admin().pricing(Net.to(pricing -> fill(panes, page, pricing), Ui::fail)); + } + + private static void fill(final Panes panes, final Container page, final PricingDto pricing) { + page.removeAll(); + Ui.section(page, Lang.tr("The fare, in {0}", pricing.currency())); + final TextField base = money(page, "Base fare", "baseFare", pricing.baseFare(), null); + Container pair = new Container(new GridLayout(1, 2)); + Container left = side("WlPairLeft"); + Container right = side("WlPairRight"); + final TextField perKm = money(left, "Per kilometre", "perKm", pricing.perKm(), null); + final TextField perMinute = money(right, "Per minute", "perMinute", pricing.perMinute(), + null); + pair.add(left).add(right); + page.add(pair); + final TextField minimum = money(page, "Minimum fare", "minimumFare", pricing.minimumFare(), + null); + + Ui.section(page, "The service's share"); + pair = new Container(new GridLayout(1, 2)); + left = side("WlPairLeft"); + right = side("WlPairRight"); + final TextField fee = number(left, "Service fee, %", "serviceFeePercent", + pricing.serviceFeePercent()); + final TextField commission = number(right, "Commission, %", "commissionPercent", + pricing.commissionPercent()); + pair.add(left).add(right); + page.add(pair); + page.add(Ui.text("The fee is added to the fare and paid by the customer. The commission " + + "is kept from the fare; the rest is the courier's.", "WlMuted")); + + Ui.section(page, "Multipliers"); + final TextField surge = number(page, "Surge, while demand is high", "surgeMultiplier", + pricing.surgeMultiplier()); + pair = new Container(new GridLayout(1, 2)); + left = side("WlPairLeft"); + right = side("WlPairRight"); + final TextField comfort = number(left, "Comfort", "comfortMultiplier", + pricing.comfortMultiplier()); + final TextField xl = number(right, "XL", "xlMultiplier", pricing.xlMultiplier()); + pair.add(left).add(right); + page.add(pair); + + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + final Button save = Ui.primary("Save prices", "submit", null); + save.addActionListener(e -> { + PricingDto wanted = new PricingDto(pricing.currency(), cents(base), cents(perKm), + cents(perMinute), cents(minimum), value(fee), value(commission), value(surge), + value(comfort), value(xl)); + save.setEnabled(false); + error.setText(""); + Api.admin().savePricing(wanted, Net.to(saved -> { + Telemetry.pricingChanged(); + Ui.say("The new prices are in force"); + fill(panes, page, saved); + }, (status, message) -> { + save.setEnabled(true); + error.setText(message); + panes.refresh(); + })); + }); + page.add(error).add(save); + panes.refresh(); + } + + private static Container side(String uiid) { + Container side = new Container(BoxLayout.y()); + side.setUIID(uiid); + return side; + } + + private static TextField money(Container to, String caption, String name, long cents, + String hint) { + TextField field = Ui.field(to, caption, name, hint == null ? "0.00" : hint, + TextArea.DECIMAL); + long part = cents % 100; + field.setText((cents / 100) + "." + (part < 10 ? "0" : "") + part); + return field; + } + + private static TextField number(Container to, String caption, String name, double value) { + TextField field = Ui.field(to, caption, name, "1.0", TextArea.DECIMAL); + // Two decimal places are all a percentage or a multiplier needs. + long hundredths = Math.round(value * 100d); + long part = hundredths % 100; + field.setText((hundredths / 100) + "." + (part < 10 ? "0" : "") + part); + return field; + } + + private static long cents(TextField field) { + return Math.round(value(field) * 100d); + } + + /// The number typed, or -1 for anything that is not one, which the server + /// then refuses by name. + private static double value(TextField field) { + String text = field.getText().trim().replace(',', '.'); + boolean point = false; + if (text.length() == 0 || text.length() > 12) { + return -1; + } + for (int iter = 0; iter < text.length(); iter++) { + char letter = text.charAt(iter); + if (letter == '.' && !point) { + point = true; + } else if (letter < '0' || letter > '9') { + return -1; + } + } + return ".".equals(text) ? -1 : Double.parseDouble(text); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java new file mode 100644 index 00000000000..446a2fcf827 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.admin; + +import com.codename1.components.SpanLabel; +import com.codename1.components.Switch; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.FlagDto; +import com.codenameone.examples.morsel.api.ModerationEventDto; +import com.codenameone.examples.morsel.api.RoleChangeDto; +import com.codenameone.examples.morsel.api.SuspendDto; +import com.codenameone.examples.morsel.api.UserDetailDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.StatRow; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// The accounts, and what an admin decides about each: its roles, whether it +/// is flagged for attention, and whether it may be used at all. +/// +/// All of it takes effect at once. The server reads an account's roles and its +/// block on every request, not from the token the app is holding, so a courier +/// made an admin has the admin screens the next time their app asks who they +/// are, and a blocked account's next call is refused. +/// +/// A flag and a block are both kept with a reason and with who gave it; the +/// account's screen shows that history, newest first. +public final class UsersForm { + static final String ALL = "all"; + static final String FLAGGED = "flagged"; + private static final String[] FILTERS = {ALL, "customers", "couriers", "admins", FLAGGED, + "blocked"}; + private static final String[] FILTER_NAMES = {"Everyone", "Customers", "Couriers", "Admins", + "Flagged", "Blocked"}; + + private final Panes panes; + private final Container list = Ui.list("users"); + private final TextField search; + private List users; + private String filter; + + private UsersForm(Panes panes, String filter) { + this.panes = panes; + this.filter = filter == null || filter.length() == 0 ? ALL : filter; + Container top = new Container(BoxLayout.y()); + top.setUIID("WlPage"); + search = new TextField("", Lang.tr("Search by name or e-mail"), 24, TextField.ANY); + search.setUIID("WlField"); + search.getHintLabel().setUIID("WlFieldHint"); + search.setName("search"); + search.addDataChangedListener((type, index) -> fill()); + top.add(search); + top.add(Ui.choice("filter", FILTER_NAMES, FILTERS, this.filter, value -> { + this.filter = value; + fill(); + }, 3)); + panes.list("People", "Users", Panes.listed(top, list), + this::load); + } + + private void load() { + Api.admin().users(Net.to(found -> { + users = found; + fill(); + }, Ui.failed(list, this::load))); + } + + /// @param filter one of the filters to open on, or empty for everyone + static void show(Form previous, String filter) { + show(Panes.phone(previous), filter); + } + + static void show(Panes panes, String filter) { + new UsersForm(panes, filter); + } + + private void fill() { + list.removeAll(); + String wanted = search.getText().trim(); + int shown = 0; + for (int iter = 0; users != null && iter < users.size(); iter++) { + final UserDto user = users.get(iter); + if (!passes(user) || !matches(user, wanted)) { + continue; + } + shown++; + Container marks = new Container(new FlowLayout(Label.RIGHT, Label.CENTER)); + if (user.flagged()) { + marks.add(Ui.badge("Flagged", "WlBadgeWarn")); + } + marks.add(Ui.badge(roles(user), user.suspended() ? "WlBadgeBad" : "WlBadge")); + Container row = Ui.row((char) 0, user.displayName(), user.username(), marks); + row.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle( + Ui.avatar(user.displayName()))); + final Container picked = row; + list.add(Ui.tap(row, "user-" + user.username(), e -> { + panes.picked(picked); + open(user.username()); + })); + } + if (shown == 0 && users != null) { + list.add(Ui.empty(FontImage.MATERIAL_PERSON_SEARCH, "Nobody matches", null)); + } + panes.refresh(); + } + + private boolean passes(UserDto user) { + if (FLAGGED.equals(filter)) { + return user.flagged(); + } + if ("blocked".equals(filter)) { + return user.suspended(); + } + if ("admins".equals(filter)) { + return user.admin(); + } + if ("couriers".equals(filter)) { + return user.courier(); + } + if ("customers".equals(filter)) { + return !user.courier() && !user.admin(); + } + return true; + } + + /// Whether `wanted` is in the name or the address, whatever the case. Both + /// are compared a letter at a time: a name is not a protocol token, but + /// folding it by the device's language would still miss an `I` in Turkish. + private static boolean matches(UserDto user, String wanted) { + return wanted.length() == 0 || contains(user.displayName(), wanted) + || contains(user.username(), wanted); + } + + private static boolean contains(String text, String wanted) { + if (text == null) { + return false; + } + for (int at = 0; at + wanted.length() <= text.length(); at++) { + if (text.regionMatches(true, at, wanted, 0, wanted.length())) { + return true; + } + } + return false; + } + + static String roles(UserDto user) { + if (user.suspended()) { + return "Blocked"; + } + return user.admin() ? "Admin" : user.courier() ? "Courier" : "Customer"; + } + + /// One account in full, fetched each time it is shown. + private void open(final String username) { + Api.admin().user(username, Net.to(detail -> show(detail), Ui::fail)); + } + + private void show(final UserDetailDto detail) { + final UserDto user = detail.user(); + Container page = Ui.page(); + + Label standing = Ui.badge(user.suspended() ? "Blocked" : user.flagged() ? "Flagged" + : "In good standing", user.suspended() ? "WlBadgeBad" : user.flagged() + ? "WlBadgeWarn" : "WlBadgeGood"); + standing.setName("standing"); + page.add(Ui.person(user.displayName(), user.username(), standing)); + if (user.flagged() && user.flagReason() != null && user.flagReason().length() > 0) { + SpanLabel why = Ui.text(user.flagReason(), "WlNotice"); + why.setName("flagReason"); + page.add(why); + } + + StatRow numbers = Ui.stats(3); + numbers.stat(String.valueOf(detail.deliveries()), "Deliveries", "userDeliveries"); + numbers.stat(String.valueOf(detail.cancellations()), "Cancelled", + "userCancellations"); + numbers.stat(detail.rating() > 0 ? Ui.tenths(detail.rating()) : "-", "Rating", + "userRating"); + page.add(numbers); + StatRow money = Ui.stats(2); + money.stat(Ui.money(detail.spent(), null), "Spent riding", "userSpent"); + money.stat(Ui.money(detail.earned(), null), "Earned onboarding", "userEarned"); + page.add(money); + + Ui.section(page, "Contact"); + page.add(Ui.row(FontImage.MATERIAL_PHONE, user.phone(), + user.phoneVerified() ? "Verified" : "Not verified", null)); + if (user.courier()) { + page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, user.vehicle(), user.plate(), null)); + } + + Ui.section(page, "Access"); + final Switch courier = toggle("courier", user.courier()); + final Switch admin = toggle("admin", user.admin()); + page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, "Courier", + "May go online and take deliveries", courier)); + page.add(Ui.row(FontImage.MATERIAL_INSIGHTS, "Admin", + "May see and change everything here", admin)); + // A change the server refuses -- an admin removing their own role, say + // -- is put back, so the switches always show what is true. Putting + // one back is itself a change, which `settling` keeps from being sent. + final boolean[] settling = new boolean[1]; + // The roles as the server last confirmed them: courier, admin. + final boolean[] held = {user.courier(), user.admin()}; + final Runnable roles = () -> { + if (settling[0]) { + return; + } + RoleChangeDto change = new RoleChangeDto(courier.isValue(), admin.isValue()); + Api.admin().setRoles(user.username(), change, Net.to(saved -> { + held[0] = saved.courier(); + held[1] = saved.admin(); + }, (status, message) -> { + Ui.fail(status, message); + settling[0] = true; + courier.setValue(held[0]); + admin.setValue(held[1]); + settling[0] = false; + })); + }; + courier.addChangeListener(e -> roles.run()); + admin.addChangeListener(e -> roles.run()); + + Ui.section(page, "Moderation"); + page.add(Ui.text("A flag marks the account for the admins and changes nothing for " + + "its owner. A block signs it out and refuses it until lifted.", "WlMuted")); + Container actions = new Container(new GridLayout(1, 2)); + actions.add(pair("WlPairLeft", user.flagged() + ? Ui.secondary("Remove flag", "unflag", e -> flag(user, false, "")) + : Ui.secondary("Flag", "flag", e -> panes.reason("Flag account", + Lang.tr("Why does {0} need the admins' attention?", user.displayName()), + "Flag", why -> flag(user, true, why))))); + actions.add(pair("WlPairRight", user.suspended() + ? Ui.secondary("Unblock", "unblock", e -> block(user, false, "")) + : Ui.danger("Block", "block", e -> panes.reason("Block account", + Lang.tr("{0} is signed out everywhere and cannot sign in again " + + "until the block is lifted.", user.displayName()), "Block", + why -> block(user, true, why))))); + page.add(actions); + + Ui.section(page, "History"); + List events = detail.events(); + if (events == null || events.isEmpty()) { + page.add(Ui.label("Nothing has been done to this account.", "WlMuted")); + } + for (int iter = 0; events != null && iter < events.size(); iter++) { + ModerationEventDto event = events.get(iter); + Container row = Ui.row(icon(event.action()), action(event.action()), + event.reason() == null || event.reason().length() == 0 ? null : event.reason(), + null); + row.setName("event-" + iter); + Container when = new Container(BoxLayout.y()); + when.add(Ui.plain(Ui.when(event.at()), "WlRowDetail")); + when.add(Ui.plain(event.by(), "WlRowDetail")); + row.add(BorderLayout.EAST, when); + page.add(row); + } + panes.detail(user.displayName(), "User", page); + } + + private void flag(UserDto user, boolean flagged, String why) { + FlagDto change = new FlagDto(flagged, why); + Api.admin().flag(user.username(), change, Net.to(saved -> { + Telemetry.userFlagged(flagged); + open(user.username()); + }, Ui::fail)); + } + + private void block(UserDto user, boolean blocked, String why) { + SuspendDto change = new SuspendDto(blocked, why); + Api.admin().suspend(user.username(), change, Net.to(saved -> { + Telemetry.userBlocked(blocked); + open(user.username()); + }, Ui::fail)); + } + + private static Container pair(String uiid, Button button) { + Container side = new Container(new BorderLayout()); + side.setUIID(uiid); + side.add(BorderLayout.CENTER, button); + return side; + } + + private static char icon(String action) { + return "block".equals(action) ? FontImage.MATERIAL_BLOCK + : "unblock".equals(action) ? FontImage.MATERIAL_LOCK_OPEN + : "flag".equals(action) ? FontImage.MATERIAL_FLAG + : "unflag".equals(action) ? FontImage.MATERIAL_OUTLINED_FLAG + : "approve".equals(action) ? FontImage.MATERIAL_CHECK_CIRCLE + : "reject".equals(action) ? FontImage.MATERIAL_CANCEL + : FontImage.MATERIAL_MANAGE_ACCOUNTS; + } + + private static String action(String action) { + return "block".equals(action) ? "Blocked" : "unblock".equals(action) ? "Unblocked" + : "flag".equals(action) ? "Flagged" : "unflag".equals(action) ? "Flag removed" + : "approve".equals(action) ? "Approved to drive" : "reject".equals(action) + ? "Application sent back" : "Roles changed"; + } + + private static Switch toggle(String name, boolean value) { + Switch toggle = new Switch(); + toggle.setName(name); + toggle.setValue(value); + return toggle; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java new file mode 100644 index 00000000000..867bce089ec --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.components.SpanLabel; +import com.codename1.l10n.ParseException; +import com.codename1.l10n.SimpleDateFormat; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Image; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.spinner.Picker; +import com.codename1.util.Base64; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.DocumentDto; +import com.codenameone.examples.morsel.api.DocumentUploadDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.api.ProfileDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.net.Account; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Chart; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Photos; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/// The application to drive, a step at a time: you, your licence, your car, +/// your documents, and a last look before it is handed in. +/// +/// Each step is saved as it is left, so an application can be put down half +/// way and picked up on another day or another phone. The server keeps the +/// draft and decides what "complete" means: handing in an application that is +/// missing something comes back with what, in words, and that is shown as it +/// is rather than worked out again here. +public final class ApplicationForm { + /// The documents asked for, in the order the server lists them. + public static final String[] KINDS = {"photo_id", "licence", "selfie", + "vehicle_registration", "insurance"}; + private static final String[] KIND_NAMES = {"Photo ID", "Onboarding licence", "A photo of you", + "Vehicle registration", "Insurance"}; + private static final String[] KIND_HINTS = {"A passport or an identity card", + "The side with your photo on it", "Your face, in good light, no sunglasses", + "The paper that says the car is yours", "The page with the policy's dates on it"}; + private static final char[] KIND_ICONS = {FontImage.MATERIAL_BADGE, + FontImage.MATERIAL_CREDIT_CARD, FontImage.MATERIAL_FACE, + FontImage.MATERIAL_DESCRIPTION, FontImage.MATERIAL_VERIFIED_USER}; + private static final String[] STEPS = {"About you", "Your licence", "Your car", + "Your documents", "Review and submit"}; + private static final String[] GENDERS = {"unspecified", "female", "male", "nonbinary"}; + private static final String[] GENDER_NAMES = {"Rather not say", "Woman", "Man", + "Non-binary"}; + private static final String ISO = "yyyy-MM-dd"; + + private final Form form = Ui.form("Drive with Morsel", "Application"); + private final Runnable done; + private final Container body = Ui.page(); + private final Label stepLabel = Ui.plain("", "WlStepLabel"); + private final Label stepTitle = Ui.label("", "WlTitle"); + private final Container progress = new Container(new BorderLayout()); + private final SpanLabel error = Ui.text("", "WlError"); + private final Button next = Ui.primary("Continue", "next", e -> forward()); + private final Button back = Ui.link("Back", "backStep", e -> backward()); + /// The pictures taken on this visit, to show beside their documents. + private final Map thumbs = new HashMap(); + private CourierApplicationDto application; + private String gender; + private int step; + /// Reads what the step on screen has in its fields into [#application]. + private Runnable collect = () -> { }; + + private ApplicationForm(CourierApplicationDto application, Runnable done) { + this.application = application; + this.done = done; + Ui.back(form, () -> done.run()); + UserDto user = Session.user(); + ProfileDto profile = Account.profile(); + gender = profile != null && profile.gender() != null && profile.gender().length() > 0 + ? profile.gender() : user.gender() == null || user.gender().length() == 0 + ? GENDERS[0] : user.gender(); + if (application.legalName() == null || application.legalName().length() == 0) { + this.application = filled(user.displayName(), application.dateOfBirth(), + application.licenceNumber(), application.licenceExpiry()); + } + stepLabel.setName("step"); + error.setName("error"); + Container head = new Container(BoxLayout.y()); + head.setUIID("WlPage"); + head.add(stepLabel).add(stepTitle).add(progress); + Container foot = new Container(BoxLayout.y()); + foot.setUIID("WlFooter"); + foot.setSafeArea(true); + foot.add(error).add(next).add(back); + form.add(BorderLayout.NORTH, head); + form.add(BorderLayout.CENTER, body); + form.add(BorderLayout.SOUTH, foot); + } + + /// @param done run when the user leaves, with the application handed in or + /// not; it shows the screen to go back to + public static void show(Form previous, CourierApplicationDto application, Runnable done) { + ApplicationForm wizard = new ApplicationForm(application, done); + wizard.display(); + wizard.form.show(); + } + + // -------------------------------------------------------------- the steps + + private void display() { + stepLabel.setText(Lang.tr("Step {0} of {1}", String.valueOf(step + 1), + String.valueOf(STEPS.length))); + stepTitle.setText(STEPS[step]); + progress.removeAll(); + progress.add(BorderLayout.CENTER, new Chart.Meter((step + 1) / (double) STEPS.length)); + body.removeAll(); + // A new step starts at its top, wherever the last one was left. + body.scrollRectToVisible(0, 0, 1, 1, body); + error.setText(""); + collect = () -> { }; + if (step == 0) { + you(); + } else if (step == 1) { + licence(); + } else if (step == 2) { + car(); + } else if (step == 3) { + documents(); + } else { + review(); + } + next.setText(step == STEPS.length - 1 ? "Submit application" : "Continue"); + next.setName(step == STEPS.length - 1 ? "submitApplication" : "next"); + back.setHidden(step == 0); + Ui.refresh(form); + } + + private void you() { + body.add(Ui.text("As it is written on your licence. Customers see only your first name.", + "WlMuted")); + final TextField name = Ui.field(body, "Legal name", "legalName", ""); + name.setText(application.legalName()); + final Picker born = date(body, "Date of birth", "dateOfBirth", application.dateOfBirth()); + body.add(Ui.label("Gender", "WlLabel")); + body.add(Ui.choice("gender", GENDER_NAMES, GENDERS, gender, value -> gender = value, 2)); + body.add(Ui.text("Customers who ask for a woman at the wheel are offered to couriers " + + "who say they are one.", "WlMuted")); + collect = () -> application = filled(name.getText().trim(), iso(born), + application.licenceNumber(), application.licenceExpiry()); + } + + private void licence() { + body.add(Ui.text("The number is on the front of the card.", "WlMuted")); + final TextField number = Ui.field(body, "Licence number", "licenceNumber", ""); + number.setText(application.licenceNumber()); + final Picker expiry = date(body, "Expires", "licenceExpiry", application.licenceExpiry()); + collect = () -> application = filled(application.legalName(), + application.dateOfBirth(), number.getText().trim(), iso(expiry)); + } + + private void car() { + Container pair = new Container(new GridLayout(1, 2)); + Container left = side("WlPairLeft"); + Container right = side("WlPairRight"); + final TextField make = Ui.field(left, "Make", "vehicleMake", "Toyota"); + final TextField model = Ui.field(right, "Model", "vehicleModel", "Prius"); + pair.add(left).add(right); + body.add(pair); + Container second = new Container(new GridLayout(1, 2)); + left = side("WlPairLeft"); + right = side("WlPairRight"); + final TextField year = Ui.field(left, "Year", "vehicleYear", "2021", TextArea.NUMERIC); + final TextField colour = Ui.field(right, "Colour", "vehicleColor", "Blue"); + second.add(left).add(right); + body.add(second); + Container third = new Container(new GridLayout(1, 2)); + left = side("WlPairLeft"); + right = side("WlPairRight"); + final TextField plate = Ui.field(left, "Licence plate", "vehiclePlate", "7ABC123"); + final TextField seats = Ui.field(right, "Seats for customers", "vehicleSeats", "4", + TextArea.NUMERIC); + third.add(left).add(right); + body.add(third); + make.setText(application.vehicleMake()); + model.setText(application.vehicleModel()); + year.setText(application.vehicleYear() > 0 + ? String.valueOf(application.vehicleYear()) : ""); + colour.setText(application.vehicleColor()); + plate.setText(application.vehiclePlate()); + seats.setText(String.valueOf(application.vehicleSeats() > 0 + ? application.vehicleSeats() : 4)); + final String[] product = {application.product() == null + || application.product().length() == 0 ? "standard" : application.product()}; + body.add(Ui.label("Kind of delivery", "WlLabel")); + body.add(Ui.choice("product", new String[] {"Standard", "Comfort", "XL"}, + new String[] {"standard", "comfort", "xl"}, product[0], + value -> product[0] = value)); + body.add(Ui.text("Comfort is a newer, roomier car. XL seats six or more.", "WlMuted")); + final boolean[] accessible = {application.accessible()}; + body.add(Ui.row(FontImage.MATERIAL_ACCESSIBLE, "Takes a wheelchair", + "A ramp or a lift, and room to secure the chair", + Ui.toggle("accessible", accessible[0], on -> accessible[0] = on))); + final boolean[] pets = {application.petFriendly()}; + body.add(Ui.row(FontImage.MATERIAL_PETS, "Takes pets", + "Customers travelling with a pet are offered to you", + Ui.toggle("petFriendly", pets[0], on -> pets[0] = on))); + collect = () -> { + CourierApplicationDto a = application; + application = new CourierApplicationDto(a.username(), a.name(), a.status(), + a.legalName(), a.dateOfBirth(), a.licenceNumber(), a.licenceExpiry(), + make.getText().trim(), model.getText().trim(), number(year.getText()), + colour.getText().trim(), plate.getText().trim(), number(seats.getText()), + product[0], accessible[0], pets[0], a.documents(), a.rejectionReason(), + a.submittedAt(), a.reviewedAt(), a.reviewedBy()); + }; + } + + /// The application as it stands, with the applicant's and the licence's + /// details as given. + private CourierApplicationDto filled(String legalName, String dateOfBirth, + String licenceNumber, String licenceExpiry) { + CourierApplicationDto a = application; + return new CourierApplicationDto(a.username(), a.name(), a.status(), legalName, + dateOfBirth, licenceNumber, licenceExpiry, a.vehicleMake(), a.vehicleModel(), + a.vehicleYear(), a.vehicleColor(), a.vehiclePlate(), a.vehicleSeats(), + a.product(), a.accessible(), a.petFriendly(), a.documents(), + a.rejectionReason(), a.submittedAt(), a.reviewedAt(), a.reviewedBy()); + } + + private void documents() { + body.add(Ui.text("Photograph each one flat, with all four corners in the picture.", + "WlMuted")); + for (int iter = 0; iter < KINDS.length; iter++) { + body.add(document(iter)); + } + } + + /// One document: what it is, whether we have it, and the two ways to give + /// it to us. + private Container document(final int index) { + final String kind = KINDS[index]; + String status = status(kind); + boolean have = !"missing".equals(status); + Container box = new Container(BoxLayout.y()); + box.setUIID("WlOption"); + Label picture = new Label("", "WlThumb"); + Image thumb = thumbs.get(kind); + if (thumb != null) { + int side = CN.convertToPixels(9f); + picture.setIcon(thumb.fill(side, side)); + } else { + Ui.icon(picture, KIND_ICONS[index], 4f); + } + Label state = Ui.badge(have ? "rejected".equals(status) ? "Rejected" + : "accepted".equals(status) ? "Accepted" : "Added" : "Needed", + have ? "rejected".equals(status) ? "WlBadgeBad" : "WlBadgeGood" : "WlBadgeWarn"); + state.setName("status-" + kind); + // The state stands over the name and not beside it: the name is long + // in some languages and has to keep the whole line. + state.getAllStyles().setMargin(0, 0, 0, 0); + Container lines = new Container(BoxLayout.y()); + lines.add(FlowLayout.encloseIn(state)); + lines.add(Ui.label(KIND_NAMES[index], "WlRowTitle")); + lines.add(Ui.text(KIND_HINTS[index], "WlRowDetail")); + Container top = new Container(new BorderLayout()); + top.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(picture)); + top.add(BorderLayout.CENTER, lines); + box.add(top); + Container ways = new Container(new GridLayout(1, 2)); + Button camera = Ui.link(have ? "Retake" : "Camera", "camera-" + kind, + e -> Photos.camera((jpeg, taken) -> upload(kind, jpeg, taken))); + Ui.icon(camera, FontImage.MATERIAL_PHOTO_CAMERA, 3f); + Button gallery = Ui.link("Gallery", "gallery-" + kind, + e -> Photos.gallery((jpeg, taken) -> upload(kind, jpeg, taken))); + Ui.icon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 3f); + ways.add(camera).add(gallery); + box.add(ways); + return box; + } + + private void upload(final String kind, byte[] jpeg, final Image picture) { + DocumentUploadDto document = new DocumentUploadDto(kind, "image/jpeg", + Base64.encodeNoNewline(jpeg)); + Api.onboarding().upload(document, Net.to(saved -> { + application = saved; + thumbs.put(kind, picture); + if (step == 3) { + display(); + } + }, this::failed)); + } + + private void review() { + body.add(Ui.text("Have a last look. You can change anything until it is handed in.", + "WlMuted")); + Ui.section(body, "You"); + body.add(Ui.fact("Legal name", application.legalName())); + body.add(Ui.fact("Date of birth", application.dateOfBirth())); + body.add(Ui.fact("Licence", application.licenceNumber())); + body.add(Ui.fact("Licence expires", application.licenceExpiry())); + Ui.section(body, "Your car"); + body.add(Ui.fact("Car", ApplyForm.car(application))); + body.add(Ui.fact("Licence plate", application.vehiclePlate())); + body.add(Ui.fact("Seats for customers", String.valueOf(application.vehicleSeats()))); + body.add(Ui.fact("Kind of delivery", Lang.tr(Ui.product(application.product())))); + body.add(Ui.fact("Takes a wheelchair", Lang.tr(application.accessible() ? "Yes" : "No"))); + body.add(Ui.fact("Takes pets", Lang.tr(application.petFriendly() ? "Yes" : "No"))); + Ui.section(body, "Documents"); + int have = KINDS.length - ApplyForm.missing(application); + Container documents = Ui.fact("Photos added", have + " / " + KINDS.length); + documents.getComponentAt(1).setName("documentsAdded"); + body.add(documents); + body.add(Ui.text("By submitting you confirm that all of this is true and that the " + + "documents are your own.", "WlMuted")); + } + + // ----------------------------------------------------------- moving along + + private void forward() { + collect.run(); + // What the step asks for, asked for here: the server would take a + // half-empty draft, and say what is missing only at the very end. + if (incomplete()) { + error.setText(Lang.tr("Fill in everything on this step to continue.")); + Ui.refresh(form); + return; + } + next.setEnabled(false); + error.setText(""); + if (step == STEPS.length - 1) { + Api.onboarding().submit(Net.to(submitted -> { + Telemetry.applicationSubmitted(); + next.setEnabled(true); + Ui.say("Application sent. We will be in touch."); + done.run(); + }, this::failed)); + return; + } + if (step == 3) { + // Nothing typed on this step: each picture was sent as it was taken. + next.setEnabled(true); + step++; + display(); + return; + } + final boolean named = step == 0; + Api.onboarding().save(application, Net.to(saved -> { + application = saved; + next.setEnabled(true); + if (named) { + // The gender is the account's, not the application's. + ProfileDto profile = Account.profile(); + Account.saveProfile(profile == null ? Session.user().displayName() : profile.name(), + gender, () -> { }, (status, message) -> { }); + } + step++; + display(); + }, this::failed)); + } + + private boolean incomplete() { + if (step == 0) { + return empty(application.legalName()) || empty(application.dateOfBirth()); + } + if (step == 1) { + return empty(application.licenceNumber()) || empty(application.licenceExpiry()); + } + if (step == 2) { + return empty(application.vehicleMake()) || empty(application.vehicleModel()) + || empty(application.vehicleColor()) || empty(application.vehiclePlate()) + || application.vehicleYear() <= 0 || application.vehicleSeats() <= 0; + } + return false; + } + + private static boolean empty(String text) { + return text == null || text.length() == 0; + } + + private void backward() { + collect.run(); + if (step > 0) { + step--; + display(); + } + } + + private void failed(int status, String message) { + next.setEnabled(true); + // The server's own sentence: what is missing, or what is wrong with it. + error.setText(Lang.tr(message)); + Ui.refresh(form); + } + + private String status(String kind) { + for (int iter = 0; application.documents() != null && iter < application.documents().size(); + iter++) { + DocumentDto document = application.documents().get(iter); + if (kind.equals(document.kind())) { + return document.status(); + } + } + return "missing"; + } + + private static Container side(String uiid) { + Container side = new Container(BoxLayout.y()); + side.setUIID(uiid); + return side; + } + + /// A field for a date, which opens the platform's own way of choosing one. + private static Picker date(Container to, String caption, String name, String value) { + Picker picker = new Picker(); + picker.setType(Display.PICKER_TYPE_DATE); + picker.setUIID("WlField"); + picker.setName(name); + picker.setFormatter(new SimpleDateFormat("d MMM yyyy")); + if (value != null && value.length() > 0) { + try { + picker.setDate(new SimpleDateFormat(ISO).parse(value)); + } catch (ParseException unreadable) { + // Left on today: the server would not have stored such a date. + picker.setDate(new Date()); + } + } + to.add(Ui.label(caption, "WlLabel")).add(picker); + return picker; + } + + private static String iso(Picker picker) { + Date date = picker.getDate(); + return date == null ? "" : new SimpleDateFormat(ISO).format(date); + } + + private static int number(String text) { + String trimmed = text == null ? "" : text.trim(); + if (trimmed.length() == 0 || trimmed.length() > 6) { + return 0; + } + for (int iter = 0; iter < trimmed.length(); iter++) { + if (trimmed.charAt(iter) < '0' || trimmed.charAt(iter) > '9') { + return 0; + } + } + return Integer.parseInt(trimmed); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java new file mode 100644 index 00000000000..b59eb7e3703 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.DocumentDto; +import com.codenameone.examples.morsel.api.CourierApplicationDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Menu; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Ui; + +/// The courier's side of the app for someone who is not a courier yet: where +/// their application stands, and the way on from there. +/// +/// Whether anyone drives is the server's decision and an admin's: an +/// application is filled in, handed in, and approved or sent back with a +/// reason. This screen shows which of those it is. There is no way from here +/// to going online -- the screen that has that button is not built for an +/// account the server does not call a courier, and the server refuses the call +/// from one anyway. +public class ApplyForm implements Nav.Home { + private static final int POLL_MILLIS = 5000; + + private final Form form = Ui.home("Apply"); + private final Menu menu = new Menu(form, Nav.COURIER); + private final Container page = Ui.page(); + private String showing = ""; + + public ApplyForm() { + menu.item(FontImage.MATERIAL_ASSIGNMENT, "Your application", "home", () -> { }); + Container bar = new Container(new BorderLayout()); + bar.setUIID("WlHomeBar"); + bar.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(menu.flatButton())); + bar.add(BorderLayout.CENTER, FlowLayout.encloseLeftMiddle( + Ui.label("Drive with Morsel", "WlHeading"))); + page.setSafeArea(false); + Container screen = new Container(new BorderLayout()); + // The strip the status bar is drawn in is the bar's colour, not + // whatever is behind the screen. + screen.setUIID("WlForm"); + screen.setSafeArea(true); + screen.add(BorderLayout.NORTH, bar); + screen.add(BorderLayout.CENTER, page); + form.add(screen); + menu.install(); + form.addShowListener(e -> load()); + // An answer can come at any moment, and is worth showing when it does. + UITimer.timer(POLL_MILLIS, true, form, this::load); + } + + @Override + public Form form() { + return form; + } + + @Override + public void start() { + load(); + } + + private void load() { + Api.onboarding().application(Net.to(this::fill, (status, message) -> { })); + } + + private void fill(final CourierApplicationDto application) { + String status = application.status() == null ? "none" : application.status(); + if ("approved".equals(status)) { + // The account is a courier's now: say so to the app, and let it + // build the screen a courier gets. + if (!"approved".equals(showing)) { + showing = "approved"; + Session.refreshUser(Nav::home, Ui::fail); + } + return; + } + String now = status + ":" + application.submittedAt() + ":" + missing(application); + if (now.equals(showing)) { + return; + } + showing = now; + page.removeAll(); + if ("pending".equals(status)) { + pending(application); + } else if ("rejected".equals(status)) { + rejected(application); + } else { + invite(application, "draft".equals(status) && started(application)); + } + Ui.refresh(form); + } + + /// Not applied yet, or part of the way through. + private void invite(final CourierApplicationDto application, boolean started) { + Container banner = Ui.banner(FontImage.MATERIAL_DIRECTIONS_CAR, + started ? "Finish your application" : "Earn on your own schedule", + started ? Lang.tr("{0} of 5 documents added. Pick up where you left off.", + String.valueOf(5 - missing(application))) + : "Tell us about you and your car, add five photos, and we take it " + + "from there.", "WlBannerIcon"); + page.add(banner); + Label state = Ui.label(started ? "Draft" : "Not started", "WlBadge"); + state.setName("applicationStatus"); + page.add(FlowLayout.encloseCenter(state)); + Ui.section(page, "What you will need"); + page.add(Ui.row(FontImage.MATERIAL_BADGE, "A onboarding licence", + "Its number, its expiry date and a photo of it", null)); + page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, "A car with four doors", + "Registered and insured, with the papers to show it", null)); + page.add(Ui.row(FontImage.MATERIAL_PHOTO_CAMERA, "About ten minutes", + "Most of it is taking five photos", null)); + final boolean begun = started; + page.add(Ui.primary(started ? "Continue application" : "Start application", "apply", + e -> { + if (!begun) { + Telemetry.applicationStarted(false); + } + ApplicationForm.show(form, application, this::refreshed); + })); + } + + private void pending(CourierApplicationDto application) { + page.add(Ui.banner(FontImage.MATERIAL_HOURGLASS_TOP, "Application under review", + "We have everything we asked for. Someone on our team is reading it.", + "WlBannerIconWarn")); + Label state = Ui.label("Under review", "WlBadgeWarn"); + state.setName("applicationStatus"); + page.add(FlowLayout.encloseCenter(state)); + Ui.section(page, "What happens next"); + page.add(Ui.row(FontImage.MATERIAL_CHECK_CIRCLE, "Application received", + application.submittedAt() > 0 ? Ui.when(application.submittedAt()) : null, null)); + page.add(Ui.row(FontImage.MATERIAL_FACT_CHECK, "Documents checked", + "Usually within two working days", null)); + page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, "You go online", + "This screen becomes your courier's home the moment you are approved", null)); + Ui.section(page, "Your car"); + page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, car(application), + application.vehiclePlate(), null)); + } + + private void rejected(final CourierApplicationDto application) { + page.add(Ui.banner(FontImage.MATERIAL_ERROR_OUTLINE, "We could not approve this yet", + "Read what was missing, put it right, and send the application again.", + "WlBannerIconBad")); + Label state = Ui.label("Changes needed", "WlBadgeBad"); + state.setName("applicationStatus"); + page.add(FlowLayout.encloseCenter(state)); + Ui.section(page, "What the reviewer said"); + Container said = new Container(BoxLayout.y()); + said.setUIID("WlPanel"); + com.codename1.components.SpanLabel reason = Ui.text("", "WlText"); + // The reviewer's own words, in whatever language they wrote them. + reason.setShouldLocalize(false); + reason.setText(application.rejectionReason()); + reason.setName("rejectionReason"); + said.add(reason); + page.add(said); + page.add(Ui.primary("Fix and resubmit", "apply", e -> { + Telemetry.applicationStarted(true); + ApplicationForm.show(form, application, this::refreshed); + })); + } + + /// Back from the form: whatever was done there, show how things stand now. + private void refreshed() { + showing = ""; + form.showBack(); + } + + private static boolean started(CourierApplicationDto application) { + return application.legalName() != null && application.legalName().length() > 0 + || missing(application) < 5; + } + + static int missing(CourierApplicationDto application) { + int missing = 0; + for (int iter = 0; application.documents() != null && iter < application.documents().size(); + iter++) { + DocumentDto document = application.documents().get(iter); + if ("missing".equals(document.status())) { + missing++; + } + } + return application.documents() == null ? 5 : missing; + } + + static String car(CourierApplicationDto application) { + StringBuilder car = new StringBuilder(); + append(car, application.vehicleColor()); + append(car, application.vehicleMake()); + append(car, application.vehicleModel()); + if (application.vehicleYear() > 0) { + append(car, String.valueOf(application.vehicleYear())); + } + return car.toString(); + } + + private static void append(StringBuilder to, String word) { + if (word != null && word.length() > 0) { + to.append(to.length() > 0 ? " " : "").append(word); + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java new file mode 100644 index 00000000000..7074a9a0a56 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.maps.LatLng; +import com.codename1.maps.Marker; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.CourierStatusDto; +import com.codenameone.examples.morsel.api.EarningsDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.map.CarMarker; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.map.MapStage; +import com.codenameone.examples.morsel.map.Maps; +import com.codenameone.examples.morsel.map.Routes; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.BottomSheet; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Menu; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Pulse; +import com.codenameone.examples.morsel.ui.DeliveryListForm; +import com.codenameone.examples.morsel.ui.Ring; +import com.codenameone.examples.morsel.ui.StatRow; +import com.codenameone.examples.morsel.ui.Ui; +import java.io.IOException; + +/// Onboarding: go online, answer the offer, drive the delivery. +/// +/// While online the app reports the car's position every few seconds. That +/// report is three things at once to the server: where to draw the car on the +/// customer's map, how far this courier is from the next request, and the sign the +/// courier is still there -- a courier who stops reporting stops being offered +/// deliveries. +/// +/// An offer is the server's to make and to withdraw. It arrives as a delivery in +/// the `OFFERED` state with a number of seconds to answer in; when they run out +/// the server offers the delivery to the next courier, and this screen finds out +/// the way it finds out everything, by asking what the courier's delivery now is. +/// +/// Each step of a delivery has one thing to do next, and the sheet shows that one +/// thing as its only large button. +public class CourierForm extends LiveChannel.Adapter implements Nav.Home { + private static final int TICK_MILLIS = 3000; + /// How fast the made-up car drives where there is no real position: + /// brisk, so a demonstration does not take the time a real delivery would. + private static final double SIMULATED_SPEED = 25; + /// Meters a second in town, for a time of arrival worked out from a + /// distance: 30 km/h. + private static final double CITY_SPEED = 8.3; + private static final Net.Failed QUIET = (status, message) -> { }; + + private final Form form = Ui.home("Courier"); + private final Menu menu = new Menu(form, Nav.COURIER); + private final MapStage stage; + private final BottomSheet sheet; + private final CarMarker car; + + private LatLng here = Locator.here(); + private double heading; + private boolean online; + private DeliveryDto delivery; + private EarningsDto earnings; + /// What the last finished trip paid, to say so until the next one starts. + private String lastFare; + private String showing = ""; + /// Where the car is being driven to, as `deliveryId:state`, and the way there. + private String target = ""; + private Routes.Way way; + private double travelled; + private Marker pickupMarker; + private Marker dropoffMarker; + private Maps.Line line; + private Ring countdown; + private int ticks; + + public CourierForm() { + menu.item(FontImage.MATERIAL_DIRECTIONS_CAR, "Drive", "home", () -> { }); + menu.item(FontImage.MATERIAL_HISTORY, "Your trips", "history", + () -> DeliveryListForm.show(form, "Your trips", cb -> Api.courier().history(cb), false)); + menu.item(FontImage.MATERIAL_PAYMENTS, "Earnings", "earnings", + () -> EarningsForm.show(form)); + stage = new MapStage(here, Maps.STREET_ZOOM, menu.button()); + sheet = stage.sheet(); + car = new CarMarker(stage.map, form, here, heading); + stage.addRound(FontImage.MATERIAL_MY_LOCATION, "recentre", this::recentre); + form.add(stage.layers()); + menu.install(); + form.addShowListener(e -> { + LiveChannel.listen(this); + refresh(); + }); + UITimer.timer(TICK_MILLIS, true, form, this::tick); + UITimer.timer(1000, true, form, this::second); + } + + @Override + public Form form() { + return form; + } + + @Override + public void start() { + display(); + // Picks up where the courier left off: still online, perhaps mid-delivery. + Api.courier().me(Net.to(me -> { + // Too late: the user has left for another mode or signed out, and + // Shift was ended then. Starting it again here would report a + // courier with no courier's screen. + if (!Nav.isHome(form)) { + return; + } + online = me.online(); + shift(); + showing = ""; + display(); + refresh(); + }, QUIET)); + loadEarnings(); + } + + public void show() { + start(); + form.show(); + } + + // ------------------------------------------------------------ the server + + private void tick() { + ticks++; + if (!online) { + return; + } + move(); + report(true, QUIET); + if (!LiveChannel.connected() || ticks % 3 == 0) { + refresh(); + } + } + + /// Tells the server where the car is, and whether it is taking deliveries. + private void report(final boolean wanted, Net.Failed failed) { + final CourierStatusDto status = new CourierStatusDto(wanted, here.getLatitude(), + here.getLongitude(), heading); + Api.courier().status(status, Net.to(me -> { + if (online != me.online()) { + online = me.online(); + shift(); + Telemetry.courierOnline(online); + showing = ""; + display(); + } + }, failed)); + } + + private void refresh() { + Api.courier().active(Net.to(this::update, QUIET)); + } + + /// Keeps the phone reporting in the background exactly while the server + /// has this courier on line. + private void shift() { + if (online) { + Shift.begin(); + } else { + Shift.end(); + } + } + + @Override + public void deliveryChanged(String id, String state) { + refresh(); + } + + private void update(DeliveryDto latest) { + boolean none = latest == null || DeliveryState.NONE == latest.state(); + if (none && delivery != null && DeliveryState.OFFERED == delivery.state()) { + Ui.say("The request went to another courier"); + } else if (none && delivery != null) { + Ui.say("The customer cancelled"); + } + delivery = none ? null : latest; + if (delivery != null) { + lastFare = null; + } + aim(); + display(); + } + + private void act(Net.Call call) { + act(call, () -> { }); + } + + /// The same, with something to do first when the server agrees. + private void act(Net.Call call, final Runnable agreed) { + Net.send(call, changed -> { + agreed.run(); + update(changed); + }, (status, message) -> { + Ui.fail(status, message); + refresh(); + }); + } + + private void loadEarnings() { + Api.courier().earnings(Net.to(earned -> { + earnings = earned; + showing = ""; + display(); + }, QUIET)); + } + + // --------------------------------------------------------------- the map + + /// Points the car at wherever the delivery needs it next: the pickup once the + /// delivery is accepted, the destination once the customer is aboard. + private void aim() { + String wanted = delivery == null ? "" : delivery.id() + ":" + delivery.state(); + if (wanted.equals(target)) { + return; + } + target = wanted; + way = null; + travelled = 0; + if (pickupMarker != null) { + stage.map.removeMarker(pickupMarker); + pickupMarker = null; + } + if (dropoffMarker != null) { + stage.map.removeMarker(dropoffMarker); + dropoffMarker = null; + } + if (line != null) { + line.remove(stage.map); + line = null; + } + if (delivery == null) { + stage.center(here, Maps.STREET_ZOOM); + return; + } + final LatLng pickup = new LatLng(delivery.pickupLat(), delivery.pickupLng()); + final LatLng dropoff = new LatLng(delivery.dropoffLat(), delivery.dropoffLng()); + pickupMarker = Maps.pickup(stage.map, pickup); + dropoffMarker = Maps.dropoff(stage.map, dropoff); + stage.frame(here, pickup, dropoff); + if (DeliveryState.ARRIVED == delivery.state()) { + return; + } + final LatLng to = DeliveryState.IN_PROGRESS == delivery.state() ? dropoff : pickup; + final String aimed = target; + Routes.find(here, to, route -> { + if (!aimed.equals(target) || delivery == null) { + return; + } + line = Maps.route(stage.map, route.points); + // An offer shows the way; only an accepted delivery is driven. + if (DeliveryState.OFFERED != delivery.state()) { + way = route; + } + }); + } + + /// Moves the car. On a phone the phone has moved it already; where there + /// is no real position, this drives it along the route. + private void move() { + LatLng now; + if (!Locator.simulated()) { + now = Locator.here(); + } else if (way != null) { + travelled += SIMULATED_SPEED * TICK_MILLIS / 1000d; + now = Routes.along(way.points, travelled); + } else { + return; + } + if (Maps.meters(here, now) > 2) { + heading = Maps.heading(here, now); + } + here = now; + car.moveTo(here, heading); + chip(); + } + + private void recentre() { + if (delivery == null) { + stage.center(here, Maps.STREET_ZOOM); + } else { + stage.frame(here, new LatLng(delivery.pickupLat(), delivery.pickupLng()), + new LatLng(delivery.dropoffLat(), delivery.dropoffLng())); + } + } + + /// How far, and about how long, to where the delivery needs the car next. + private void chip() { + if (delivery == null || DeliveryState.OFFERED == delivery.state() + || DeliveryState.ARRIVED == delivery.state()) { + stage.chip(null, (char) 0); + return; + } + double meters = Maps.meters(here, next()); + stage.chip(Ui.minutes(meters / CITY_SPEED) + Ui.DOT + Ui.distance(meters), + FontImage.MATERIAL_NAVIGATION); + } + + private LatLng next() { + return DeliveryState.IN_PROGRESS == delivery.state() + ? new LatLng(delivery.dropoffLat(), delivery.dropoffLng()) + : new LatLng(delivery.pickupLat(), delivery.pickupLng()); + } + + // ------------------------------------------------------------- the sheet + + private void second() { + // The offer has run out: the server has moved on, so ask what is left. + if (countdown != null && countdown.secondsLeft() == 0) { + countdown = null; + refresh(); + } + } + + private void display() { + String now = (online ? "on" : "off") + ":" + (delivery == null ? "" : delivery.id() + delivery.state()); + if (now.equals(showing)) { + return; + } + showing = now; + sheet.reset(); + countdown = null; + header(); + chip(); + if (delivery == null) { + waiting(); + } else if (DeliveryState.OFFERED == delivery.state()) { + offer(); + } else { + trip(); + } + sheet.ready(false); + } + + /// The card at the top: online or off, and what today has made. + private void header() { + Container card = new Container(new BorderLayout()); + card.setUIID("WlStatusCard"); + card.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle( + Ui.badge(online ? "Online" : "Offline", online ? "WlBadgeGood" : "WlBadge"))); + if (earnings != null) { + Container today = new Container(BoxLayout.y()); + today.add(Ui.label(Ui.money(earnings.todayCents(), earnings.currency()), + "WlStatusValue")); + today.add(Ui.label(Lang.tr("Today, {0} trips", String.valueOf(earnings.todayDeliveries())), + "WlStatusDetail")); + card.add(BorderLayout.EAST, today); + } + stage.header(card); + } + + private void waiting() { + Container peek = sheet.peek(); + Label title = Ui.label(online ? "Looking for deliveries" : "You are offline", "WlHeading"); + title.setName("courierState"); + peek.add(title); + if (lastFare != null) { + peek.add(Ui.text(Lang.tr("Trip complete: {0}", lastFare), "WlNotice")); + } + if (online) { + peek.add(Ui.label("Stay close to busy streets; requests come to the nearest car.", + "WlMuted")); + peek.add(FlowLayout.encloseCenter(new Pulse())); + } else { + peek.add(Ui.label("Go online to start taking deliveries.", "WlMuted")); + Button go = new Button("GO", "WlGo"); + go.setName("goOnline"); + go.addActionListener(e -> report(true, Ui::fail)); + peek.add(FlowLayout.encloseCenter(go)); + } + if (earnings != null) { + StatRow numbers = Ui.stats(3); + numbers.stat(Ui.money(earnings.todayCents(), earnings.currency()), "Today", + "earnedToday"); + numbers.stat(Ui.money(earnings.weekCents(), earnings.currency()), "This week", + "earnedWeek"); + numbers.stat(String.valueOf(earnings.deliveries()), "Trips", "trips"); + peek.add(numbers); + } + if (online) { + peek.add(Ui.secondary("Go offline", "goOffline", e -> report(false, Ui::fail))); + } + } + + private void offer() { + final String id = delivery.id(); + Container peek = sheet.peek(); + Label title = Ui.label("Delivery request", "WlHeading"); + title.setName("courierState"); + countdown = new Ring(); + countdown.setName("countdown"); + countdown.start(delivery.offerExpiresInSeconds(), delivery.offerExpiresInSeconds()); + Label fare = Ui.label(Ui.money(delivery.fareCents(), delivery.currency()), "WlFare"); + fare.setName("fare"); + Container words = new Container(BoxLayout.y()); + words.add(title); + words.add(fare); + Container top = new Container(new BorderLayout()); + top.add(BorderLayout.CENTER, words); + top.add(BorderLayout.EAST, FlowLayout.encloseCenterMiddle(countdown)); + peek.add(top); + double away = Maps.meters(here, new LatLng(delivery.pickupLat(), delivery.pickupLng())); + peek.add(Ui.row(FontImage.MATERIAL_TRIP_ORIGIN, delivery.pickupAddress(), + Lang.tr("{0} away, {1}", Ui.distance(away), Ui.minutes(away / CITY_SPEED)), null)); + peek.add(Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), + Ui.distance(delivery.distanceMeters()) + Ui.DOT + Ui.minutes(delivery.durationSeconds()), + null)); + peek.add(Ui.primary("Accept", "accept", e -> act(cb -> Api.courier().accept(id, cb), + () -> Telemetry.offerAnswered(true)))); + peek.add(Ui.link("Decline", "decline", e -> act(cb -> Api.courier().decline(id, cb), + () -> Telemetry.offerAnswered(false)))); + sheet.more().add(Ui.row(FontImage.MATERIAL_PERSON, delivery.customerName(), asked(), null)); + sheet.more().add(Ui.row(delivery.cash() ? FontImage.MATERIAL_PAYMENTS + : FontImage.MATERIAL_CREDIT_CARD, delivery.cash() ? "Cash" : "Card", + Lang.tr(Ui.product(delivery.product())), null)); + } + + private void trip() { + final String id = delivery.id(); + DeliveryState state = delivery.state(); + Container peek = sheet.peek(); + Label title = Ui.label(DeliveryState.ACCEPTED == state + ? Lang.tr("Pick up {0}", delivery.customerName()) + : DeliveryState.ARRIVED == state ? Lang.tr("Waiting for {0}", delivery.customerName()) + : Lang.tr("On the trip"), "WlHeading"); + title.setName("courierState"); + peek.add(title); + boolean aboard = DeliveryState.IN_PROGRESS == state; + peek.add(Ui.row(aboard ? FontImage.MATERIAL_PLACE : FontImage.MATERIAL_TRIP_ORIGIN, + aboard ? delivery.dropoffAddress() : delivery.pickupAddress(), + aboard ? "Drop-off" : "Pickup", null)); + if (delivery.cash()) { + // Said before the trip ends, so that it is not a surprise at the door. + Label cash = Ui.label(Lang.tr("Collect {0} in cash", Ui.money( + delivery.total() > 0 ? delivery.total() : delivery.fareCents(), delivery.currency())), + "WlNotice"); + cash.setName("collectCash"); + Ui.icon(cash, FontImage.MATERIAL_PAYMENTS, 3f); + peek.add(cash); + } + if (!aboard) { + Container reach = new Container(new GridLayout(1, 2)); + reach.setUIID("WlActions"); + reach.add(Ui.action(FontImage.MATERIAL_CALL, "Call", "call", e -> call())); + reach.add(Ui.action(FontImage.MATERIAL_SMS, "Message", "message", e -> message())); + peek.add(reach); + } + if (DeliveryState.ACCEPTED == state) { + peek.add(Ui.primary("I have arrived", "arrived", + e -> act(cb -> Api.courier().arrived(id, cb)))); + } else if (DeliveryState.ARRIVED == state) { + peek.add(Ui.primary("Start trip", "start", + e -> act(cb -> Api.courier().start(id, cb)))); + } else { + peek.add(Ui.primary("Complete trip", "complete", + e -> Api.courier().complete(id, Net.to(finished -> { + Telemetry.deliveryCompleted("courier", finished.fareCents(), finished.cash()); + lastFare = Ui.money(finished.fareCents(), finished.currency()); + Ui.say(finished.cash() ? Lang.tr("Collect {0} in cash", Ui.money( + finished.total() > 0 ? finished.total() : finished.fareCents(), + finished.currency())) : Lang.tr("Trip complete: {0}", lastFare)); + delivery = null; + aim(); + display(); + loadEarnings(); + }, Ui::fail)))); + } + Container more = sheet.more(); + more.add(Ui.row(FontImage.MATERIAL_PERSON, delivery.customerName(), asked(), null)); + more.add(Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), + Ui.distance(delivery.distanceMeters()) + Ui.DOT + Ui.minutes(delivery.durationSeconds()), + Ui.label(Ui.money(delivery.fareCents(), delivery.currency()), "WlRowValue"))); + // A customer who is aboard is taken where they are going: there is no + // cancelling from here, only finishing. + if (!aboard) { + more.add(Ui.link("Cancel delivery", "cancel", + e -> Api.courier().cancel(id, Net.to(cancelled -> { + Telemetry.deliveryCancelled("courier", state); + delivery = null; + aim(); + display(); + }, Ui::fail)))); + } + } + + private void call() { + String number = delivery == null ? null : delivery.customerPhone(); + if (number == null || number.length() == 0 || !CN.canDial()) { + Ui.say("Calling is not available on this device"); + return; + } + CN.dial(number); + } + + private void message() { + String number = delivery == null ? null : delivery.customerPhone(); + if (number == null || number.length() == 0) { + Ui.say("Your customer cannot be messaged right now"); + return; + } + try { + CN.sendSMS(number, Lang.tr("Hi, this is your Morsel courier.")); + } catch (IOException failed) { + Ui.say("Messages are not available on this device"); + } + } + + /// What the customer asked for with the delivery, in a line. + private String asked() { + StringBuilder asked = new StringBuilder(); + if (delivery.quietDelivery()) { + asked.append(Lang.tr("Quiet delivery")); + } + if (delivery.accessibleVehicle()) { + asked.append(asked.length() > 0 ? ", " : "").append(Lang.tr("Accessible vehicle")); + } + if (delivery.petFriendly()) { + asked.append(asked.length() > 0 ? ", " : "").append(Lang.tr("Pet friendly")); + } + if (delivery.note() != null && delivery.note().length() > 0) { + asked.append(asked.length() > 0 ? ", " : "").append(delivery.note()); + } + return asked.toString(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java new file mode 100644 index 00000000000..58d951469ae --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.EarningsSummaryDto; +import com.codenameone.examples.morsel.api.PayoutAccountDto; +import com.codenameone.examples.morsel.api.PayoutDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.pay.Cards; +import com.codenameone.examples.morsel.ui.Chart; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.StatRow; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// What onboarding has paid: what is waiting to be cashed out, the last +/// fortnight a day at a time, and the payouts already sent. +/// +/// The sums are the server's. It works them out from the deliveries it recorded, +/// less its commission, so this screen can show them and cannot change them. +public final class EarningsForm { + private final Form form = Ui.form("Earnings", "Earnings"); + private final Container page = Ui.page(); + private PayoutAccountDto account; + + private EarningsForm(Form previous) { + Ui.back(form, previous); + form.add(BorderLayout.CENTER, page); + } + + public static void show(Form previous) { + EarningsForm earnings = new EarningsForm(previous); + earnings.form.show(); + earnings.load(); + } + + private void load() { + Api.courier().payoutAccount(Net.to(found -> { + account = found; + Api.courier().earningsSummary(Net.to(summary -> Api.courier().payouts(Net.to( + payouts -> fill(summary, payouts), Ui::fail)), Ui::fail)); + }, Ui::fail)); + } + + private void fill(final EarningsSummaryDto summary, List payouts) { + page.removeAll(); + final String currency = summary.currency(); + Container balance = new Container(BoxLayout.y()); + balance.setUIID("WlPanel"); + balance.add(Ui.label("Ready to cash out", "WlStatLabel")); + Label amount = Ui.plain(Ui.money(summary.balance(), currency), "WlChartValue"); + amount.setName("balance"); + balance.add(amount); + final boolean banked = account != null && account.accountLast4() != null + && account.accountLast4().length() > 0; + Button cash = Ui.primary("Cash out", "cashOut", e -> { + if (!banked) { + account(); + return; + } + Api.courier().cashOut(Net.to(sent -> { + Telemetry.payoutRequested(sent.amount()); + Ui.say(Lang.tr("{0} is on its way to your bank", Ui.money(sent.amount(), + currency))); + load(); + }, Ui::fail)); + }); + cash.setEnabled(summary.balance() > 0); + balance.add(cash); + page.add(balance); + + StatRow periods = Ui.stats(3); + periods.stat(Ui.money(summary.today(), currency), "Today", "earnedToday"); + periods.stat(Ui.money(summary.week(), currency), "This week", "earnedWeek"); + periods.stat(Ui.money(summary.month(), currency), "This month", "earnedMonth"); + page.add(periods); + + Ui.section(page, "The last two weeks"); + int days = summary.daily() == null ? 0 : summary.daily().size(); + double[] amounts = new double[days]; + for (int iter = 0; iter < days; iter++) { + amounts[iter] = summary.daily().get(iter).amount(); + } + page.add(Chart.of("daily", amounts, Lang.tr("{0} days ago", String.valueOf( + Math.max(1, days - 1))), Lang.tr("Today"))); + + StatRow numbers = Ui.stats(2); + numbers.stat(String.valueOf(summary.trips()), "Trips", "trips"); + numbers.stat(Ui.money(summary.tips(), currency), "Tips", "tips"); + numbers.stat(summary.rating() > 0 ? Ui.tenths(summary.rating()) : "-", "Rating", + "rating"); + numbers.stat(Math.round(summary.acceptanceRate() * 100d) + "%", "Requests accepted", + "acceptance"); + page.add(numbers); + + Ui.section(page, "Payouts"); + page.add(Ui.tap(Ui.row(FontImage.MATERIAL_ACCOUNT_BALANCE, + banked ? account.bankName() : Lang.tr("Add a bank account"), + banked ? Lang.tr("Account ending {0}", account.accountLast4()) + : Lang.tr("Where your earnings are sent"), null), "payoutAccount", + e -> account())); + for (int iter = 0; payouts != null && iter < payouts.size(); iter++) { + PayoutDto payout = payouts.get(iter); + Container row = Ui.row(FontImage.MATERIAL_NORTH_EAST, Ui.when(payout.createdAt()), + payout.destination(), Ui.plain(Ui.money(payout.amount(), currency), + "WlRowValue")); + row.setName("payout-" + iter); + page.add(row); + } + if (payouts == null || payouts.isEmpty()) { + page.add(Ui.label("Nothing has been paid out yet.", "WlMuted")); + } + Ui.refresh(form); + } + + /// Where payouts go. The account number is typed in full and only its + /// last four digits leave the phone: the rest is the bank's business, and + /// a sample server has no bank. + private void account() { + final Form edit = Ui.form("Bank account", "PayoutAccount"); + Ui.back(edit, form); + Container fields = Ui.page(); + final TextField holder = Ui.field(fields, "Account holder", "holder", ""); + final TextField bank = Ui.field(fields, "Bank", "bankName", ""); + final TextField number = Ui.field(fields, "Account number", "accountNumber", "", + TextArea.NUMERIC); + if (account != null) { + holder.setText(account.holder()); + bank.setText(account.bankName()); + } + final SpanLabel error = Ui.text("", "WlError"); + fields.add(error); + fields.add(Ui.primary("Save", "submit", e -> { + String digits = Cards.digits(number.getText()); + if (digits.length() < 4) { + error.setText(Lang.tr("Type the account number.")); + edit.revalidate(); + return; + } + PayoutAccountDto wanted = new PayoutAccountDto(holder.getText().trim(), + bank.getText().trim(), digits.substring(digits.length() - 4)); + Api.courier().savePayoutAccount(wanted, Net.to(saved -> { + form.showBack(); + load(); + }, (status, message) -> { + error.setText(message); + edit.revalidate(); + })); + })); + edit.add(BorderLayout.CENTER, fields); + edit.show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java new file mode 100644 index 00000000000..0200310adf8 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.courier; + +import com.codename1.background.ForegroundService; +import com.codename1.maps.LatLng; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.api.CourierStatusDto; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.map.Maps; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; + +import java.util.concurrent.atomic.AtomicBoolean; + +// tag::morsel-shift[] +/// A courier's time on line, as far as the phone is concerned: the position +/// keeps going to the server while the app is in the background. +/// +/// In the foreground the courier screen reports every few seconds itself. Put +/// away -- the courier switched to a navigation app, or locked the phone -- that +/// screen stops, and this takes over: it follows the position in the +/// background and reports it when the car has moved [#MIN_METERS], and at +/// least every [#KEEP_ALIVE_MILLIS] even when it has not, because a courier who +/// goes quiet for a minute stops being offered deliveries. +/// +/// - On iOS the `location` background mode keeps the app running while it +/// tracks, and each new position is the moment to report. +/// - On Android a foreground service keeps it running, with the notification +/// the system requires, and wakes it for the reports a parked car would not +/// send. +/// - The simulator and the browser stand still and report nothing here. +public final class Shift { + /// How far the car moves before the server hears of it again. + static final double MIN_METERS = 25d; + /// The longest the server goes without hearing, well inside the minute + /// after which it stops offering this courier deliveries. + static final long KEEP_ALIVE_MILLIS = 20000L; + private static final String CHANNEL = "morsel-onboarding"; + private static final Net.Failed QUIET = (status, message) -> { }; + + private static boolean on; + private static boolean background; + private static long sentAt; + private static LatLng sentFrom; + private static ForegroundService service; + /// Read by the service's own thread, so not a plain field. + private static final AtomicBoolean RUNNING = new AtomicBoolean(); + private static final Locator.Watcher WATCHER = (state, where) -> { + if (state == Locator.FOUND && background) { + report(where); + } + }; + + private Shift() { + } + + /// The courier went on line. + public static void begin() { + if (on) { + return; + } + on = true; + sentFrom = null; + Locator.track(true); + Locator.watch(WATCHER); + if (ForegroundService.isSupported() && "and".equals(CN.getPlatformName()) + && !CN.isSimulator()) { + RUNNING.set(true); + service = ForegroundService.start(CHANNEL, "You're online", + "Morsel shares your location with customers while you drive.", null, + Shift::keepAlive); + } + } + + /// The courier went off line, signed out or left the courier's side of the + /// app. Nothing goes on following where the phone is. + public static void end() { + if (!on) { + return; + } + on = false; + Locator.unwatch(WATCHER); + Locator.track(false); + RUNNING.set(false); + if (service != null) { + service.stop(); + service = null; + } + } + + /// Leaving the courier's side of the app, with the session still there: + /// the server is told this courier is off line before the phone stops + /// reporting. Otherwise it would go on offering deliveries to a phone that is + /// no longer listening until the reports went stale, a minute, and each + /// customer would wait out an offer nobody sees. One request, not waited + /// for; if it is lost the minute is the fallback. + public static void leave() { + if (on) { + LatLng where = Locator.here(); + Api.courier().status(new CourierStatusDto(false, where.getLatitude(), + where.getLongitude(), Locator.heading()), Net.to(me -> { }, QUIET)); + } + end(); + } + + public static boolean isOn() { + return on; + } + + /// The app went to the background, and the reports are this class's. + public static void paused() { + background = true; + } + + /// The app is back, and the courier screen reports again. + public static void resumed() { + background = false; + } + // end::morsel-shift[] + + /// The foreground service's task, on a thread of its own: wakes now and + /// then for the car that is not moving, which brings no new positions. + private static void keepAlive(ForegroundService running) { + while (RUNNING.get() && running.isRunning()) { + try { + Thread.sleep(KEEP_ALIVE_MILLIS); + } catch (InterruptedException stop) { + return; + } + if (RUNNING.get()) { + CN.callSerially(() -> { + if (on && background) { + report(Locator.here()); + } + }); + } + } + } + + /// Reports `where` if the car has moved far enough or long enough has + /// passed. The answer says whether the server still has this courier on + /// line; one that does not ends the shift here too. + private static void report(LatLng where) { + long now = System.currentTimeMillis(); + boolean moved = sentFrom == null || Maps.meters(sentFrom, where) >= MIN_METERS; + if (!moved && now - sentAt < KEEP_ALIVE_MILLIS) { + return; + } + sentAt = now; + sentFrom = where; + Api.courier().status(new CourierStatusDto(true, where.getLatitude(), where.getLongitude(), + Locator.heading()), Net.to(me -> { + if (!me.online()) { + end(); + } + }, QUIET)); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java new file mode 100644 index 00000000000..50f2108f066 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java @@ -0,0 +1,906 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.customer; + +import com.codename1.maps.LatLng; +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.FareOptionDto; +import com.codenameone.examples.morsel.api.FareQuoteDto; +import com.codenameone.examples.morsel.api.NearbyCourierDto; +import com.codenameone.examples.morsel.api.PaymentMethodDto; +import com.codenameone.examples.morsel.api.PlaceDto; +import com.codenameone.examples.morsel.api.RatingDto; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryRequestDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.api.TipDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.map.CarMarker; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.map.MapStage; +import com.codenameone.examples.morsel.map.Maps; +import com.codenameone.examples.morsel.map.Routes; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.pay.ReceiptForm; +import com.codenameone.examples.morsel.pay.WalletForm; +import com.codenameone.examples.morsel.ui.BottomSheet; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Menu; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Pulse; +import com.codenameone.examples.morsel.ui.DeliveryListForm; +import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.morsel.ui.VerifyPhoneForm; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/// Riding: the map, a search card over the top of it, and a sheet at the +/// bottom that is whatever the delivery needs next -- where to, what it costs, who +/// is coming, how it was. +/// +/// The sheet follows the delivery's state, and the state is the server's. Nothing +/// here moves a delivery along; it asks (request, cancel, rate) and then shows what +/// the server says the delivery now is, whether that came back from the call, from +/// the next poll, or because the live channel said to look. +public class CustomerForm extends LiveChannel.Adapter implements Nav.Home { + private static final int POLL_MILLIS = 3000; + /// Meters a second in town, for a time of arrival worked out from a + /// distance: 30 km/h. + private static final double CITY_SPEED = 8.3; + private static final Net.Failed QUIET = (status, message) -> { }; + + private final Form form = Ui.home("Customer"); + private final Menu menu = new Menu(form, Nav.CUSTOMER); + private final MapStage stage; + private final BottomSheet sheet; + + private LatLng here = Locator.here(); + private String hereAddress = "Current location"; + private PlaceDto destination; + private Routes.Way way; + private DeliveryRequestDto request; + private FareQuoteDto fare; + /// The delivery on screen: under way, or just finished and not yet dismissed. + private DeliveryDto delivery; + /// Where the delivery's car is: as the delivery last said, or as the live channel + /// said since, which is more recent than any poll. + private double carLat; + private double carLng; + private double carHeading; + /// What the sheet is showing, so that a poll that changes nothing leaves + /// it alone. + private String showing = ""; + private com.codename1.maps.Marker pickupMarker; + private com.codename1.maps.Marker dropoffMarker; + private CarMarker car; + private Maps.Line line; + private int polls; + /// The cars online near here, drawn while there is no delivery to look at. + private final List nearby = new ArrayList(); + /// What the delivery being offered is asked for with: the kind of car and the + /// way to pay, each of them changed on the offer itself. + private String product = "standard"; + private PaymentMethodDto paying; + private List methods; + + public CustomerForm() { + menu.item(FontImage.MATERIAL_HAIL, "Delivery", "home", () -> { }); + menu.item(FontImage.MATERIAL_HISTORY, "Your deliveries", "history", + () -> DeliveryListForm.show(form, "Your deliveries", cb -> Api.customer().history(cb), false, + (list, picked) -> { + if (DeliveryState.COMPLETED == picked.state()) { + ReceiptForm.open(list, picked.id()); + } + })); + menu.item(FontImage.MATERIAL_ACCOUNT_BALANCE_WALLET, "Wallet", "wallet", + () -> WalletForm.show(form)); + UserDto user = Session.user(); + if (!user.courier()) { + menu.item(FontImage.MATERIAL_DIRECTIONS_CAR, "Drive with Morsel", "drive", + () -> Nav.switchTo(Nav.COURIER)); + } + stage = new MapStage(here, Maps.STREET_ZOOM, menu.button()); + sheet = stage.sheet(); + stage.addRound(FontImage.MATERIAL_MY_LOCATION, "recentre", this::recentre); + // The position comes when the platform has it, which is after this + // screen is up: until then the screen stands on the demo city. + stage.whenLocated((where, first) -> { + here = where; + if (first && delivery == null && destination == null && "idle".equals(showing)) { + start(); + loadNearby(); + } + }); + form.add(stage.layers()); + menu.install(); + form.addShowListener(e -> { + LiveChannel.listen(this); + refresh(); + loadMethods(); + }); + UITimer.timer(POLL_MILLIS, true, form, this::poll); + } + + @Override + public Form form() { + return form; + } + + @Override + public void start() { + idle(); + Api.geo().reverse(here.getLatitude(), here.getLongitude(), Net.to(place -> { + if (place != null && place.name() != null && place.name().length() > 0) { + hereAddress = place.name(); + } + }, QUIET)); + } + + public void show() { + start(); + form.show(); + } + + // ------------------------------------------------------------ the server + + private void poll() { + polls++; + if (delivery == null || !delivery.state().isActive()) { + // Nothing to follow, so the map shows who is about: often enough + // to see them move, seldom enough to cost nothing. + if (delivery == null && polls % 2 == 0) { + loadNearby(); + } + return; + } + // The live channel says when to look; the poll is for when it cannot. + if (!LiveChannel.connected() || polls % 4 == 0) { + refresh(); + } + } + + private void refresh() { + if (delivery != null) { + final String id = delivery.id(); + Api.customer().delivery(id, Net.to(this::update, QUIET)); + } else { + Api.customer().active(Net.to(this::update, QUIET)); + } + } + + @Override + public void deliveryChanged(String id, String state) { + refresh(); + } + + @Override + public void courierMoved(String deliveryId, double lat, double lng, double heading) { + if (delivery != null && delivery.id().equals(deliveryId)) { + carLat = lat; + carLng = lng; + carHeading = heading; + car(); + } + } + + private void update(DeliveryDto latest) { + if (latest == null || DeliveryState.NONE == latest.state()) { + return; + } + boolean first = delivery == null || !delivery.id().equals(latest.id()); + // Seen to end here, and not found already over when the app started. + if (!first && DeliveryState.COMPLETED != delivery.state() + && DeliveryState.COMPLETED == latest.state()) { + Telemetry.deliveryCompleted("customer", latest.fareCents(), latest.cash()); + } + delivery = latest; + carLat = latest.courierLat(); + carLng = latest.courierLng(); + carHeading = latest.courierHeading(); + if (first) { + clearNearby(); + draw(); + } + car(); + DeliveryCard.follow(latest); + String now = latest.id() + ":" + latest.state(); + if (!now.equals(showing)) { + showing = now; + deliveryDisplay(); + } + } + + /// The customer's ways to pay, for the offer to name the one in use. + private void loadMethods() { + Api.payments().methods(Net.to(found -> { + methods = found; + paying = null; + if ("offer".equals(showing) && fare != null) { + offer(); + } + }, QUIET)); + } + + /// The method this delivery is paid with: the one picked for it, else the + /// customer's default. + private PaymentMethodDto method() { + if (paying != null) { + return paying; + } + for (int iter = 0; methods != null && iter < methods.size(); iter++) { + if (methods.get(iter).isDefault()) { + return methods.get(iter); + } + } + return null; + } + + private void loadNearby() { + Api.customer().nearby(here.getLatitude(), here.getLongitude(), Net.to(cars -> { + if (delivery == null) { + showNearby(cars); + } + }, QUIET)); + } + + /// Moves the cars already drawn to where the cars are now. The server does + /// not say which car is which -- a customer has no business knowing -- so each + /// position goes to the nearest marker not yet used, which is the same car + /// unless two have crossed, and then nobody can tell. + private void showNearby(List cars) { + List free = new ArrayList(nearby); + nearby.clear(); + for (int iter = 0; cars != null && iter < cars.size(); iter++) { + NearbyCourierDto found = cars.get(iter); + LatLng at = new LatLng(found.lat(), found.lng()); + int closest = -1; + double least = 0; + for (int other = 0; other < free.size(); other++) { + double meters = Maps.meters(free.get(other).position(), at); + if (closest < 0 || meters < least) { + closest = other; + least = meters; + } + } + if (closest >= 0) { + CarMarker moved = free.remove(closest); + moved.moveTo(at, found.heading()); + nearby.add(moved); + } else { + nearby.add(new CarMarker(stage.map, form, at, found.heading())); + } + } + for (int iter = 0; iter < free.size(); iter++) { + free.get(iter).remove(); + } + stage.map.repaint(); + } + + private void clearNearby() { + for (int iter = 0; iter < nearby.size(); iter++) { + nearby.get(iter).remove(); + } + nearby.clear(); + } + + // --------------------------------------------------------------- the map + + private void clearMap() { + if (pickupMarker != null) { + stage.map.removeMarker(pickupMarker); + pickupMarker = null; + } + if (dropoffMarker != null) { + stage.map.removeMarker(dropoffMarker); + dropoffMarker = null; + } + if (car != null) { + car.remove(); + car = null; + } + if (line != null) { + line.remove(stage.map); + line = null; + } + } + + private void draw(LatLng pickup, LatLng dropoff, Routes.Way route) { + clearMap(); + if (route != null) { + line = Maps.route(stage.map, route.points); + } + pickupMarker = Maps.pickup(stage.map, pickup); + dropoffMarker = Maps.dropoff(stage.map, dropoff); + stage.frame(pickup, dropoff); + } + + /// Draws the delivery's two ends, and its route once that is known. + private void draw() { + final String id = delivery.id(); + final LatLng pickup = new LatLng(delivery.pickupLat(), delivery.pickupLng()); + final LatLng dropoff = new LatLng(delivery.dropoffLat(), delivery.dropoffLng()); + draw(pickup, dropoff, null); + Routes.find(pickup, dropoff, route -> { + if (delivery != null && delivery.id().equals(id)) { + draw(pickup, dropoff, route); + car(); + } + }); + } + + /// Puts the courier's car where the delivery says it is, and says in the chip + /// how long until it gets where it is going. + private void car() { + // By the courier's name: the server keeps the courier's sign-in address + // to the courier. + boolean assigned = delivery != null && delivery.courierName() != null + && delivery.courierName().length() > 0 && delivery.state().isActive() + && DeliveryState.OFFERED != delivery.state() + && (carLat != 0 || carLng != 0); + if (!assigned) { + if (car != null) { + car.remove(); + car = null; + } + if (delivery != null) { + stage.chip(null, (char) 0); + } + return; + } + LatLng at = new LatLng(carLat, carLng); + boolean first = false; + if (car == null) { + car = new CarMarker(stage.map, form, at, carHeading); + first = true; + } else { + car.moveTo(at, carHeading); + } + stage.map.repaint(); + if (DeliveryState.ARRIVED == delivery.state()) { + stage.chip(Lang.tr("Your courier is here"), FontImage.MATERIAL_PLACE); + return; + } + boolean aboard = DeliveryState.IN_PROGRESS == delivery.state(); + double meters = Maps.meters(at, aboard ? new LatLng(delivery.dropoffLat(), delivery.dropoffLng()) + : new LatLng(delivery.pickupLat(), delivery.pickupLng())); + stage.chip(Lang.tr(aboard ? "{0} to destination" : "Pickup in {0}", + Ui.minutes(meters / CITY_SPEED)) + Ui.DOT + Ui.distance(meters), + FontImage.MATERIAL_SCHEDULE); + if (first) { + // The car has just appeared: the map is fitted again with it in. + recentre(); + } + } + + private void recentre() { + if (delivery != null && delivery.state().isActive()) { + stage.frame(new LatLng(delivery.pickupLat(), delivery.pickupLng()), + new LatLng(delivery.dropoffLat(), delivery.dropoffLng()), car == null ? null + : car.position()); + } else if (destination != null) { + stage.frame(here, new LatLng(destination.lat(), destination.lng())); + } else { + stage.center(here, Maps.STREET_ZOOM); + } + } + + // ------------------------------------------------------------- the sheet + + private void display(String key) { + showing = key; + sheet.reset(); + } + + /// The card at the top: the search when there is nowhere to go yet, and + /// from where to where once there is. + private void header(String to) { + if (to == null) { + Button where = new Button("Where to?", "WlSearchCard"); + where.setName("whereTo"); + Ui.icon(where, FontImage.MATERIAL_SEARCH, 3.8f); + where.addActionListener(e -> PlaceSearchForm.show(form, here, this::go)); + stage.header(where); + return; + } + Container trip = new Container(BoxLayout.y()); + trip.setUIID("WlTripCard"); + trip.add(stop(FontImage.MATERIAL_TRIP_ORIGIN, hereAddress)); + trip.add(stop(FontImage.MATERIAL_PLACE, to)); + stage.header(trip); + } + + private static Label stop(char icon, String text) { + Label stop = Ui.label(text, "WlTripStop"); + Ui.icon(stop, icon, 2.8f); + return stop; + } + + /// Nothing under way: where to? + private void idle() { + display("idle"); + delivery = null; + destination = null; + way = null; + fare = null; + request = null; + paying = null; + product = "standard"; + clearMap(); + stage.chip(null, (char) 0); + stage.center(here, Maps.STREET_ZOOM); + header(null); + UserDto user = Session.user(); + Container peek = sheet.peek(); + peek.add(Ui.label(Lang.tr("Hello, {0}", firstName(user.displayName())), "WlHeading")); + peek.add(Ui.label("Where are you going?", "WlMuted")); + if (!user.phoneVerified()) { + peek.add(Ui.text("Verify your phone number to request a delivery.", "WlNotice")); + peek.add(Ui.link("Verify now", "verify", + e -> VerifyPhoneForm.show(() -> new CustomerForm().show()))); + } + // Home and work, always offered: with a place, they go there; without + // one, they ask for it. + Container saved = new Container(new GridLayout(1, 2)); + saved.add(shortcut(FontImage.MATERIAL_HOME, "Home", "home", Prefs.home())); + saved.add(shortcut(FontImage.MATERIAL_WORK, "Work", "work", Prefs.work())); + peek.add(saved); + List recents = Prefs.recents(); + for (int iter = 0; iter < recents.size(); iter++) { + final PlaceDto place = recents.get(iter); + sheet.more().add(Ui.tap(Ui.row(FontImage.MATERIAL_SCHEDULE, place.name(), + place.address(), null), "recent-" + iter, e -> go(place))); + } + sheet.ready(false); + loadNearby(); + } + + private Button shortcut(char icon, String title, final String which, final PlaceDto place) { + Button shortcut = new Button(title, "WlShortcut"); + shortcut.setName("shortcut-" + which); + Ui.icon(shortcut, icon, 3.4f); + shortcut.addActionListener(e -> { + if (place != null) { + go(place); + return; + } + PlaceSearchForm.show(form, here, picked -> { + if ("home".equals(which)) { + Prefs.setHome(picked); + } else { + Prefs.setWork(picked); + } + idle(); + }); + }); + return shortcut; + } + + /// A destination is chosen. + private void go(final PlaceDto place) { + if (place.lat() == 0 && place.lng() == 0) { + // A place that came with the account from another device: the + // server keeps what it is called, and where that is is looked up. + double lat = here.getLatitude(); + double lng = here.getLongitude(); + Api.geo().search(place.name(), lat, lng, Net.to(found -> { + if (found == null || found.isEmpty()) { + Ui.say("That place could not be found. Choose it again."); + return; + } + go(new PlaceDto(place.name(), place.address(), found.get(0).lat(), + found.get(0).lng())); + }, Ui::fail)); + return; + } + destination = place; + Prefs.addRecent(place); + quote(); + } + + /// The route to the destination, and what it would cost. + private void quote() { + display("quote"); + header(destination.name()); + sheet.peek().add(Ui.label(destination.name(), "WlHeading")); + sheet.peek().add(Ui.label("Working out the fare", "WlMuted")); + sheet.peek().add(FlowLayout.encloseCenter(new Pulse())); + sheet.ready(false); + final PlaceDto chosen = destination; + final LatLng to = new LatLng(chosen.lat(), chosen.lng()); + Routes.find(here, to, route -> { + if (destination != chosen) { //NOPMD CompareObjectsWithEquals + return; + } + way = route; + draw(here, to, route); + request = request(); + Api.customer().quote(request, Net.to(quoted -> { + if (destination == chosen) { //NOPMD CompareObjectsWithEquals + fare = quoted; + Telemetry.deliveryQuoted(quoted.options() == null ? 0 : quoted.options().size(), + quoted.distanceMeters()); + offer(); + } + }, (status, message) -> { + Ui.fail(status, message); + idle(); + })); + }); + } + + /// The request, with what the customer asked for in Settings. + private DeliveryRequestDto request() { + return new DeliveryRequestDto(here.getLatitude(), here.getLongitude(), hereAddress, + destination.lat(), destination.lng(), destination.name(), way.meters, way.seconds, + Prefs.paymentMethod(), null, Prefs.courierGender(), Prefs.quietDelivery(), + Prefs.accessibleVehicle(), Prefs.petFriendly(), null); + } + + /// The offer: the kinds of car there are and what each costs, how it is + /// paid, and the button that asks for it. + private void offer() { + display("offer"); + clearNearby(); + // How far and how long is said in the sheet here, not in the chip: + // the sheet leaves a strip of map just tall enough for the route, and + // a chip in it would sit on the pins. + stage.chip(null, (char) 0); + Container peek = sheet.peek(); + Label length = Ui.plain(Ui.distance(fare.distanceMeters()) + Ui.DOT + + Ui.minutes(fare.durationSeconds()), "WlMuted"); + length.setName("tripLength"); + Container headed = new Container(new BorderLayout()); + headed.add(BorderLayout.CENTER, Ui.label("Choose a delivery", "WlHeading")); + headed.add(BorderLayout.EAST, FlowLayout.encloseCenterMiddle(length)); + peek.add(headed); + FareOptionDto picked = null; + List options = fare.options(); + for (int iter = 0; options != null && iter < options.size(); iter++) { + final FareOptionDto option = options.get(iter); + boolean mine = option.product().equals(product); + if (mine) { + picked = option; + } + peek.add(option(option, mine)); + } + long price = picked == null ? fare.amountCents() : picked.total(); + Label total = Ui.plain(Ui.money(price, fare.currency()), "WlRowValue"); + total.setName("fare"); + final PaymentMethodDto method = method(); + Container pay = Ui.row(method == null || "cash".equals(method.kind()) + ? FontImage.MATERIAL_PAYMENTS : FontImage.MATERIAL_CREDIT_CARD, + WalletForm.label(method), asked(), total); + ((Label) ((Container) pay.getComponentAt(1)).getComponentAt(0)).setName("payWith"); + peek.add(Ui.tap(pay, "payment", e -> WalletForm.pick(form, + method == null ? Prefs.PAYMENT_CASH : method.id(), chosen -> { + paying = chosen; + offer(); + }))); + final Button go = Ui.primary(picked == null ? Lang.tr("Request delivery") + : Lang.tr("Request {0}", Lang.tr(picked.name())), "request", null); + go.addActionListener(e -> { + go.setEnabled(false); + PaymentMethodDto with = method(); + // The trip that was quoted, as the kind of car and the payment + // chosen on this sheet. + DeliveryRequestDto q = request; + DeliveryRequestDto asking = new DeliveryRequestDto(q.pickupLat(), q.pickupLng(), + q.pickupAddress(), q.dropoffLat(), q.dropoffLng(), q.dropoffAddress(), + q.distanceMeters(), q.durationSeconds(), + with == null ? Prefs.PAYMENT_CASH : with.id(), product, q.courierGender(), + q.quietDelivery(), q.accessibleVehicle(), q.petFriendly(), q.note()); + request = asking; + final boolean cash = with == null || "cash".equals(with.kind()); + Api.customer().request(asking, Net.to(asked -> { + Telemetry.deliveryRequested(product, cash); + update(asked); + }, (status, message) -> { + go.setEnabled(true); + Ui.fail(status, message); + })); + }); + peek.add(go); + peek.add(Ui.link("Cancel", "back", e -> idle())); + sheet.ready(false); + stage.frame(here, new LatLng(destination.lat(), destination.lng())); + } + + /// One kind of car: what it is, how long until one comes, what it costs. + private Container option(final FareOptionDto option, boolean picked) { + Container row = new Container(new BorderLayout()); + row.setUIID(picked ? "WlOptionPicked" : "WlOption"); + Label icon = new Label("", "WlOptionIcon"); + Ui.icon(icon, "xl".equals(option.product()) + ? FontImage.MATERIAL_AIRPORT_SHUTTLE : "comfort".equals(option.product()) + ? FontImage.MATERIAL_LOCAL_TAXI : FontImage.MATERIAL_DIRECTIONS_CAR, 4.4f); + row.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(icon)); + Container lines = new Container(BoxLayout.y()); + // How many it seats sits beside the name as a figure, which leaves + // the line under it for how far off a car is, whole in any language. + Label seats = Ui.plain(String.valueOf(option.seats()), "WlRowDetail"); + Ui.icon(seats, FontImage.MATERIAL_PERSON, 2.5f); + Container named = new Container(new BorderLayout()); + named.add(BorderLayout.WEST, Ui.label(option.name(), "WlRowTitle")); + named.add(BorderLayout.CENTER, seats); + lines.add(named); + lines.add(Ui.plain(option.couriersNearby() == 0 ? Lang.tr("None nearby") + : Lang.tr("{0} away", Ui.minutes(option.etaMinutes() * 60d)), "WlRowDetail")); + row.add(BorderLayout.CENTER, lines); + Label price = Ui.plain(Ui.money(option.total(), fare.currency()), "WlRowValue"); + price.setName("price-" + option.product()); + row.add(BorderLayout.EAST, FlowLayout.encloseCenterMiddle(price)); + return Ui.tap(row, "product-" + option.product(), e -> { + product = option.product(); + offer(); + }); + } + + /// What the customer's settings ask of every delivery, in a line. + private static String asked() { + StringBuilder asked = new StringBuilder(); + if (Prefs.GENDER_WOMEN.equals(Prefs.courierGender())) { + asked.append(Lang.tr("Women couriers only")); + } + if (Prefs.quietDelivery()) { + asked.append(asked.length() > 0 ? ", " : "").append(Lang.tr("Quiet delivery")); + } + if (Prefs.accessibleVehicle()) { + asked.append(asked.length() > 0 ? ", " : "").append(Lang.tr("Accessible vehicle")); + } + if (Prefs.petFriendly()) { + asked.append(asked.length() > 0 ? ", " : "").append(Lang.tr("Pet friendly")); + } + return asked.toString(); + } + + /// A delivery exists: the sheet is its state. + private void deliveryDisplay() { + sheet.reset(); + DeliveryState state = delivery.state(); + Label title = Ui.label(Ui.state(state), "WlHeading"); + title.setName("deliveryState"); + Container peek = sheet.peek(); + header(delivery.dropoffAddress()); + if (DeliveryState.REQUESTED == state || DeliveryState.OFFERED == state) { + stage.chip(null, (char) 0); + peek.add(title); + peek.add(Ui.label(Lang.tr("To {0}", delivery.dropoffAddress()), "WlMuted")); + peek.add(FlowLayout.encloseCenter(new Pulse())); + peek.add(cancelButton()); + } else if (state.isActive()) { + peek.add(title); + peek.add(courierCard()); + peek.add(actions(DeliveryState.IN_PROGRESS != state)); + Container more = sheet.more(); + more.add(Ui.row(FontImage.MATERIAL_TRIP_ORIGIN, delivery.pickupAddress(), null, null)); + more.add(Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), null, + Ui.label(Ui.money(delivery.fareCents(), delivery.currency()), "WlRowValue"))); + more.add(Ui.row(FontImage.MATERIAL_PAYMENTS, "Payment", paid(), null)); + } else if (DeliveryState.COMPLETED == state) { + stage.chip(null, (char) 0); + receipt(); + } else { + stage.chip(null, (char) 0); + peek.add(title); + peek.add(Ui.text(DeliveryState.NO_COURIERS == state + ? "Nobody could take the delivery just now. You have not been charged." + : DeliveryState.CANCELLED_BY_COURIER == state + ? "Your courier had to cancel. You have not been charged." + : "The delivery was cancelled.", "WlText")); + peek.add(Ui.primary("OK", "dismiss", e -> idle())); + } + sheet.ready(false); + } + + /// Who is coming, and in what. + private Container courierCard() { + Container card = new Container(new BorderLayout()); + card.setUIID("WlCourierCard"); + String initials = delivery.courierPhotoInitials(); + Label face = Ui.avatar(delivery.courierName()); + if (initials != null && initials.length() > 0) { + face.setText(initials); + } + Container lines = new Container(BoxLayout.y()); + Label name = Ui.plain(delivery.courierName(), "WlRowTitle"); + Ui.shrink(name, 2.8f); + lines.add(name); + // The rating leads the line about the car, with its star. + Label car = Ui.plain(delivery.courierRating() > 0 + ? Ui.tenths(delivery.courierRating()) + Ui.DOT + delivery.vehicle() : delivery.vehicle(), + "WlRowDetail"); + if (delivery.courierRating() > 0) { + Ui.icon(car, FontImage.MATERIAL_STAR, 2.5f); + } + Ui.shrink(car, 2.2f); + lines.add(car); + if (delivery.plate() != null && delivery.plate().length() > 0) { + // The plate has a line of its own: beside the name it cut the + // name short on a narrow phone. + Label plate = new Label("", "WlPlate"); + // A plate is a plate in any language. + plate.setShouldLocalize(false); + plate.setText(delivery.plate()); + lines.add(FlowLayout.encloseIn(plate)); + } + card.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(face)); + card.add(BorderLayout.CENTER, lines); + return card; + } + + /// What can be done about a courier who is on the way. + private Container actions(boolean cancellable) { + Container actions = new Container(new GridLayout(1, cancellable ? 4 : 3)); + actions.setUIID("WlActions"); + // The courier's number comes with the delivery while the courier has it, + // and is gone from it when the delivery is over. + actions.add(Ui.action(FontImage.MATERIAL_CALL, "Call", "call", e -> call())); + actions.add(Ui.action(FontImage.MATERIAL_SMS, "Message", "message", e -> message())); + actions.add(Ui.action(FontImage.MATERIAL_SHARE, "Share", "share", e -> CN.share( + Lang.tr("I am on my way to {0} with {1} in a {2} ({3}).", delivery.dropoffAddress(), + delivery.courierName(), delivery.vehicle(), delivery.plate()), null, null))); + if (cancellable) { + final String id = delivery.id(); + final DeliveryState state = delivery.state(); + Button cancel = Ui.action(FontImage.MATERIAL_CLOSE, "Cancel", "cancel", + e -> Api.customer().cancel(id, Net.to(cancelled -> { + Telemetry.deliveryCancelled("customer", state); + idle(); + }, Ui::fail))); + cancel.setUIID("WlActionDanger"); + actions.add(cancel); + } + return actions; + } + + private void call() { + String number = delivery == null ? null : delivery.courierPhone(); + if (number == null || number.length() == 0 || !CN.canDial()) { + Ui.say("Calling is not available on this device"); + return; + } + CN.dial(number); + } + + private void message() { + String number = delivery == null ? null : delivery.courierPhone(); + if (number == null || number.length() == 0) { + Ui.say("Your courier cannot be messaged right now"); + return; + } + try { + CN.sendSMS(number, Lang.tr("Hi, this is your Morsel customer.")); + } catch (IOException failed) { + Ui.say("Messages are not available on this device"); + } + } + + private String paid() { + String label = delivery.paymentMethodLabel(); + return delivery.cash() || label == null || label.length() == 0 ? Lang.tr("Cash") : label; + } + + private Button cancelButton() { + final String id = delivery.id(); + final DeliveryState state = delivery.state(); + return Ui.danger("Cancel delivery", "cancel", + e -> Api.customer().cancel(id, Net.to(cancelled -> { + Telemetry.deliveryCancelled("customer", state); + idle(); + }, Ui::fail))); + } + + /// The delivery is over: what it cost, how it was, and a tip if the customer + /// wants to leave one. The stars are sent as they are tapped; the tip goes + /// with Done, so that a slip of the thumb is not a charge. + private void receipt() { + Container peek = sheet.peek(); + Label title = Ui.label("You have arrived", "WlHeading"); + title.setName("deliveryState"); + Label price = Ui.plain(Ui.money(delivery.total() > 0 ? delivery.total() : delivery.fareCents(), + delivery.currency()), "WlPrice"); + price.setName("fare"); + Container top = new Container(new BorderLayout()); + top.add(BorderLayout.CENTER, title); + top.add(BorderLayout.EAST, price); + peek.add(top); + Label how = Ui.plain(Ui.distance(delivery.distanceMeters()) + Ui.DOT + + Ui.minutes(delivery.durationSeconds()) + Ui.DOT + paid(), "WlMuted"); + how.setName("paidWith"); + peek.add(how); + if ("FAILED".equals(delivery.paymentStatus())) { + peek.add(Ui.text("The card was declined. Choose another way to pay in your " + + "wallet.", "WlNotice")); + } + SpanLabel asked = Ui.text("", "WlText"); + asked.setShouldLocalize(false); + asked.setText(Lang.tr("How was the delivery with {0}?", delivery.courierName())); + peek.add(asked); + final Container stars = new Container(new FlowLayout(CN.CENTER)); + final String id = delivery.id(); + for (int star = 1; star <= 5; star++) { + final int given = star; + Button button = new Button("", "WlStar"); + button.setName("star-" + star); + Ui.icon(button, FontImage.MATERIAL_STAR_BORDER, 5.6f); + button.addActionListener(e -> { + for (int iter = 0; iter < 5; iter++) { + Ui.icon((Button) stars.getComponentAt(iter), + iter < given ? FontImage.MATERIAL_STAR + : FontImage.MATERIAL_STAR_BORDER, 5.6f); + } + stars.revalidate(); + RatingDto rating = new RatingDto(given); + Api.customer().rate(id, rating, Net.to(rated -> Telemetry.rated(given), + Ui::fail)); + }); + stars.add(button); + } + peek.add(stars); + final int[] percent = {Prefs.tipPercent()}; + boolean tippable = "PAID".equals(delivery.paymentStatus()) && delivery.tip() == 0; + if (tippable) { + peek.add(Ui.label("Tip", "WlLabel")); + peek.add(Ui.choice("tip", new String[] {"None", "10%", "15%", "20%"}, + new String[] {"0", "10", "15", "20"}, String.valueOf(percent[0]), + value -> percent[0] = Integer.parseInt(value))); + } + final long fareCents = delivery.fareCents(); + final boolean tipping = tippable; + peek.add(Ui.primary("Done", "done", e -> { + if (tipping && percent[0] > 0) { + TipDto tip = new TipDto(Math.max(100L, Math.round(fareCents * percent[0] / 100d))); + final int share = percent[0]; + Api.payments().tip(id, tip, Net.to(tipped -> { + Telemetry.tipped("delivery", share); + Ui.say("Thank you. Your tip was sent."); + }, Ui::fail)); + } + idle(); + })); + peek.add(Ui.link("View receipt", "receipt", e -> ReceiptForm.open(form, id))); + Container more = sheet.more(); + more.add(Ui.row(FontImage.MATERIAL_TRIP_ORIGIN, delivery.pickupAddress(), null, null)); + more.add(Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), null, null)); + more.add(Ui.row(FontImage.MATERIAL_PERSON, delivery.courierName(), delivery.vehicle(), null)); + } + + private static String firstName(String name) { + String text = name == null ? "" : name.trim(); + int space = text.indexOf(' '); + return space < 0 ? text : text.substring(0, space); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java new file mode 100644 index 00000000000..a374e5343b5 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.customer; + +import com.codename1.surfaces.LiveActivity; +import com.codename1.surfaces.LiveActivityDescriptor; +import com.codename1.surfaces.SurfaceColor; +import com.codename1.surfaces.SurfaceColumn; +import com.codename1.surfaces.SurfaceFontWeight; +import com.codename1.surfaces.SurfaceNode; +import com.codename1.surfaces.SurfaceProgress; +import com.codename1.surfaces.SurfaceRow; +import com.codename1.surfaces.SurfaceText; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.api.DeliveryActivityDto; +import com.codenameone.examples.morsel.api.DeliveryActivityKeys; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; + +import java.util.HashMap; +import java.util.Map; + +/// The delivery on the lock screen: a Live Activity that says the courier is on +/// the way, who they are, the car to look for and how long until it is there. +/// +/// It starts when a courier takes the delivery and ends with the delivery. While the +/// app is open the customer screen moves it on with every change it sees; once +/// the app is put away the server does, with push notifications addressed to +/// the activity's id, which is why the id is sent to the server here. +/// +/// Where it shows: the lock screen and the Dynamic Island on an iPhone, an +/// ongoing notification on Android, and a small window on the desktop and in +/// the simulator. Elsewhere [LiveActivity#start] answers a handle that does +/// nothing, and this class has nothing to check. +final class DeliveryCard { + private static final Net.Failed QUIET = (status, message) -> { }; + private static final int ACCENT = 0xff1f6feb; + + private static LiveActivity activity; + private static String deliveryId; + /// What the activity last showed, so that a poll that changes nothing + /// sends nothing. + private static String shown = ""; + /// A start or an update is under way on another thread. + private static boolean busy; + /// The delivery a start was last tried for. A platform with nothing to show + /// answers an inactive handle, and asking again on every poll would not + /// change that. + private static String tried; + + private DeliveryCard() { + } + + // tag::morsel-delivery-card-follow[] + /// The customer screen has `delivery` as it now is. Called on the event thread. + static void follow(final DeliveryDto delivery) { + if (delivery == null || busy) { + return; + } + boolean meeting = DeliveryState.ACCEPTED == delivery.state() || DeliveryState.ARRIVED == delivery.state() + || DeliveryState.IN_PROGRESS == delivery.state(); + if (activity != null && !delivery.id().equals(deliveryId)) { + // A delivery this screen no longer shows: its activity goes. + final LiveActivity stale = activity; + activity = null; + deliveryId = null; + shown = ""; + CN.scheduleBackgroundTask(() -> stale.end(null, true)); + } + final boolean ours = activity != null; + if (!meeting) { + if (ours) { + final LiveActivity ending = activity; + final boolean done = DeliveryState.COMPLETED == delivery.state(); + activity = null; + deliveryId = null; + shown = ""; + // A finished delivery stays on the lock screen a little while, as + // the platform decides; a cancelled one goes at once. + CN.scheduleBackgroundTask(() -> ending.end(done ? state(delivery) : null, !done)); + } + return; + } + final Map now = state(delivery); + String key = delivery.id() + ":" + now; + if (ours ? key.equals(shown) : delivery.id().equals(tried)) { + return; + } + if (!ours) { + tried = delivery.id(); + } + shown = key; + busy = true; + final LiveActivity current = ours ? activity : null; + // Off the event thread: on a phone starting and updating an activity + // is a round trip to the system. + CN.scheduleBackgroundTask(() -> { + if (current != null) { + current.update(now); + CN.callSerially(() -> busy = false); + return; + } + final LiveActivity started = LiveActivity.start(descriptor(), now); + CN.callSerially(() -> { + busy = false; + if (!started.isActive()) { + return; + } + activity = started; + deliveryId = delivery.id(); + Api.customer().activity(delivery.id(), new DeliveryActivityDto(started.getId()), + Net.to(saved -> { }, QUIET)); + }); + }); + } + // end::morsel-delivery-card-follow[] + + /// The activity's state, in the keys the server sends too. + static Map state(DeliveryDto delivery) { + Map out = new HashMap(); + String status; + double progress; + if (DeliveryState.ARRIVED == delivery.state()) { + status = "Your courier is here"; + progress = DeliveryActivityKeys.ARRIVED; + } else if (DeliveryState.IN_PROGRESS == delivery.state()) { + status = "On the trip"; + progress = DeliveryActivityKeys.IN_TRIP; + } else if (DeliveryState.COMPLETED == delivery.state()) { + status = "You've arrived"; + progress = DeliveryActivityKeys.DONE; + } else { + status = "On the way"; + progress = DeliveryActivityKeys.ON_THE_WAY; + } + out.put(DeliveryActivityKeys.STATUS, status); + out.put(DeliveryActivityKeys.PROGRESS, Double.valueOf(progress)); + out.put(DeliveryActivityKeys.COURIER, text(delivery.courierName())); + out.put(DeliveryActivityKeys.CAR, text(delivery.vehicle())); + out.put(DeliveryActivityKeys.PLATE, text(delivery.plate())); + out.put(DeliveryActivityKeys.ETA, delivery.etaMinutes() > 0 + && DeliveryState.ARRIVED != delivery.state() ? delivery.etaMinutes() + " min" : ""); + return out; + } + + // tag::morsel-delivery-card-layout[] + /// The layout, filled in from the state on the device: an update sends the + /// state alone. + private static LiveActivityDescriptor descriptor() { + return new LiveActivityDescriptor(DeliveryActivityKeys.KIND) + .setContent(content()) + .setCompactLeading(new SurfaceText("${courier}").setFontSize(13) + .setMaxLines(1)) + .setCompactTrailing(new SurfaceText("${eta}").setFontSize(13) + .setColor(SurfaceColor.ACCENT)) + .setMinimal(new SurfaceText("${eta}").setFontSize(11)) + .setExpandedCenter(new SurfaceText("${status}").setFontSize(16) + .setFontWeight(SurfaceFontWeight.SEMIBOLD)) + .setExpandedBottom(content()) + .setTint(SurfaceColor.rgb(ACCENT)); + } + + private static SurfaceNode content() { + return new SurfaceColumn().setSpacing(6).setPadding(12) + .add(new SurfaceRow().setSpacing(10) + .add(new SurfaceColumn().setSpacing(2).setWeight(1) + .add(new SurfaceText("${status}").setFontSize(15) + .setFontWeight(SurfaceFontWeight.SEMIBOLD) + .setMaxLines(1)) + .add(new SurfaceText("${courier} - ${car}").setFontSize(13) + .setColor(SurfaceColor.SECONDARY_LABEL) + .setMaxLines(1)) + .add(new SurfaceText("${plate}").setFontSize(13) + .setFontWeight(SurfaceFontWeight.BOLD) + .setMaxLines(1))) + .add(new SurfaceText("${eta}").setFontSize(22) + .setFontWeight(SurfaceFontWeight.BOLD) + .setColor(SurfaceColor.ACCENT))) + .add(new SurfaceProgress(SurfaceProgress.STYLE_LINEAR) + .setValueState(DeliveryActivityKeys.PROGRESS) + .setColor(SurfaceColor.rgb(ACCENT))); + } + // end::morsel-delivery-card-layout[] + + private static String text(String value) { + return value == null ? "" : value; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java new file mode 100644 index 00000000000..c66b6ef0ff8 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.customer; + +import com.codename1.maps.LatLng; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.api.PlaceDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Ui; + +import java.util.List; + +/// Finds a place by name. +/// +/// The search goes to the app's own server, which asks a geocoder on the app's +/// behalf. So the app holds no geocoder key, the server can swap one provider +/// for another without a release, and the provider sees the server and never a +/// user's device. +public final class PlaceSearchForm { + public interface Picked { + void picked(PlaceDto place); + } + + private static final int PAUSE_MILLIS = 350; + + private final Form form = Ui.form("Where to?", "PlaceSearch"); + private final Container results = new Container(BoxLayout.y()); + private final TextField query; + private final LatLng near; + private final Form previous; + private final Picked picked; + private int typed; + + private PlaceSearchForm(Form previous, LatLng near, Picked picked) { + this.previous = previous; + this.near = near; + this.picked = picked; + Ui.back(form, previous); + Container top = Ui.page(); + top.setScrollableY(false); + query = Ui.field(top, "Destination", "query", "An address or a place"); + // Searches when the typing pauses, not at every letter. + query.addDataChangedListener((type, index) -> { + final int mine = ++typed; + UITimer.timer(PAUSE_MILLIS, false, form, () -> { + if (mine == typed) { + search(); + } + }); + }); + query.setDoneListener(e -> search()); + results.setName("results"); + results.setScrollableY(true); + form.add(BorderLayout.NORTH, top); + form.add(BorderLayout.CENTER, results); + suggest(); + } + + /// Before anything is typed: the places already known, which are most of + /// where anybody goes. + private void suggest() { + results.removeAll(); + int count = 0; + count = known(FontImage.MATERIAL_HOME, Prefs.home(), count); + count = known(FontImage.MATERIAL_WORK, Prefs.work(), count); + List recents = Prefs.recents(); + for (int iter = 0; iter < recents.size(); iter++) { + count = known(FontImage.MATERIAL_SCHEDULE, recents.get(iter), count); + } + Ui.refresh(form); + } + + private int known(char icon, final PlaceDto place, int count) { + if (place == null) { + return count; + } + results.add(Ui.tap(Ui.row(icon, place.name(), place.address(), null), "known-" + count, + e -> { + previous.showBack(); + picked.picked(place); + })); + return count + 1; + } + + public static void show(Form previous, LatLng near, Picked picked) { + PlaceSearchForm search = new PlaceSearchForm(previous, near, picked); + search.form.show(); + search.query.startEditingAsync(); + } + + private void search() { + final String text = query.getText().trim(); + if (text.length() < 2) { + suggest(); + return; + } + Api.geo().search(text, near.getLatitude(), near.getLongitude(), Net.to(places -> { + // An answer to what is no longer in the field is dropped. + if (text.equals(query.getText().trim())) { + fill(places); + } + }, Ui::fail)); + } + + private void fill(List places) { + results.removeAll(); + if (places == null || places.isEmpty()) { + Container none = Ui.page(); + none.add(Ui.label("Nothing found", "WlMuted")); + results.add(none); + } + for (int iter = 0; places != null && iter < places.size(); iter++) { + final PlaceDto place = places.get(iter); + results.add(Ui.tap(Ui.row(FontImage.MATERIAL_PLACE, place.name(), place.address(), + null), "place-" + iter, e -> { + previous.showBack(); + picked.picked(place); + })); + } + Ui.refresh(form); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java new file mode 100644 index 00000000000..e66a8b9a4a0 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.live; + +import com.codename1.io.CharArrayReader; +import com.codename1.io.JSONParser; +import com.codename1.io.WebSocket; +import com.codename1.io.WebSocketState; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; + +import java.io.IOException; +import java.util.Map; +import java.util.Timer; + +/// The WebSocket the server pushes changes down: a delivery changed state, the +/// courier moved, a car went on or offline. +/// +/// It only ever says *that* something changed. What the delivery now is, the app +/// asks for over the REST API, which stays the one source of truth -- so a +/// frame that is lost, or a socket that is down, costs a few seconds and +/// nothing else. Every screen that listens here also polls, slowly while the +/// socket is up and faster while it is not ([#connected] says which). +/// +/// A WebSocket handshake cannot carry the bearer token on every platform, so +/// the connection is let in by a ticket: the app asks for one over the +/// authenticated API, and the server accepts it once, within half a minute. +public final class LiveChannel { + /// What a screen wants to hear about. All of it arrives on the EDT. + public interface Listener { + void deliveryChanged(String id, String state); + + void courierMoved(String deliveryId, double lat, double lng, double heading); + + void fleetChanged(String username, boolean online, double lat, double lng, + double heading); + } + + /// A listener that ignores everything, to override the part a screen wants. + public static class Adapter implements Listener { + @Override + public void deliveryChanged(String id, String state) { + } + + @Override + public void courierMoved(String deliveryId, double lat, double lng, double heading) { + } + + @Override + public void fleetChanged(String username, boolean online, double lat, double lng, + double heading) { + } + } + + private static final int PING_MILLIS = 25000; + + private static WebSocket socket; + private static Listener listener; + private static boolean wanted; + private static boolean connected; + private static int attempts; + /// Counts the connections made, so that what a closed or replaced one + /// still has to say -- its `onClose`, a frame already on its way -- is + /// recognised as stale and dropped. + private static int generation; + private static Timer pinger; + + private LiveChannel() { + } + + /// Connects, and keeps reconnecting until [#stop]. + public static void start() { + if (wanted) { + return; + } + wanted = true; + attempts = 0; + connect(); + } + + public static void stop() { + wanted = false; + connected = false; + listener = null; + generation++; + if (pinger != null) { + pinger.cancel(); + pinger = null; + } + if (socket != null) { + socket.close(); + socket = null; + } + } + + /// The one screen that is listening: the one on display. + public static void listen(Listener current) { + listener = current; + } + + public static boolean connected() { + return connected; + } + + private static void connect() { + if (!wanted || !WebSocket.isSupported()) { + return; + } + final int mine = ++generation; + Api.account().liveTicket(Net.to(ticket -> { + if (mine == generation && wanted) { + open(ticket.ticket(), mine); + } + }, (status, message) -> lost(mine))); + } + + private static void open(String ticket, final int mine) { + String base = Session.serverUrl(); + // http -> ws and https -> wss: the scheme keeps its "s". + String url = "ws" + base.substring(4) + "/ws/live?ticket=" + ticket; + // The handlers run on the socket's thread, so each hands its news to + // the EDT, where the rest of the app lives. + socket = WebSocket.build(url) + .onConnect(ws -> CN.callSerially(() -> opened(mine))) + .onTextMessage((ws, text) -> CN.callSerially(() -> frame(text, mine))) + .onClose((ws, code, reason) -> CN.callSerially(() -> lost(mine))) + .onError((ws, err) -> CN.callSerially(() -> lost(mine))) + .connect(10000); + } + + private static void opened(int mine) { + if (mine != generation) { + return; + } + connected = true; + attempts = 0; + if (pinger != null) { + pinger.cancel(); + } + // Something has to cross an idle connection now and then, or a proxy + // on the way decides it is dead. + pinger = CN.setInterval(PING_MILLIS, () -> { + if (mine == generation && socket != null + && socket.getReadyState() == WebSocketState.OPEN) { + socket.send("{\"type\":\"ping\"}"); + } + }); + } + + private static void lost(int mine) { + if (mine != generation || !wanted) { + return; + } + connected = false; + final int waiting = ++generation; + if (socket != null) { + socket.close(); + socket = null; + } + // 1, 2, 4 ... 32 seconds, and then every 32. + int delay = 1000 << Math.min(attempts++, 5); + // Only if nothing has happened to the channel in the meantime. Stopped + // and started again during the wait, it has a connection of its own + // by now, and connecting here as well would take that one's place: + // its callbacks would go unheard and its socket would be left open. + CN.setTimeout(delay, () -> CN.callSerially(() -> { + if (waiting == generation) { + connect(); + } + })); + } + + private static void frame(String text, int mine) { + if (mine != generation || listener == null) { + return; + } + Map json; + try { + json = new JSONParser().parseJSON(new CharArrayReader(text.toCharArray())); + } catch (IOException malformed) { + return; + } + Object type = json.get("type"); + if ("delivery".equals(type)) { + listener.deliveryChanged(text(json, "id"), text(json, "state")); + } else if ("courier".equals(type)) { + listener.courierMoved(text(json, "deliveryId"), number(json, "lat"), number(json, "lng"), + number(json, "heading")); + } else if ("fleet".equals(type)) { + Object online = json.get("online"); + listener.fleetChanged(text(json, "username"), + Boolean.TRUE.equals(online) || "true".equals(online), + number(json, "lat"), number(json, "lng"), number(json, "heading")); + } + } + + // Tested, never cast and caught: a failed cast does not throw on iOS. + private static String text(Map json, String key) { + Object value = json.get(key); + return value instanceof String ? (String) value : ""; + } + + private static double number(Map json, String key) { + Object value = json.get(key); + return value instanceof Number ? ((Number) value).doubleValue() : 0; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java new file mode 100644 index 00000000000..e44729f6426 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.map; + +import com.codename1.maps.LatLng; +import com.codename1.maps.MapView; +import com.codename1.maps.Marker; +import com.codename1.maps.MarkerOptions; +import com.codename1.ui.EncodedImage; +import com.codename1.ui.Form; +import com.codename1.ui.util.UITimer; + +/// A car on a map, that drives from one reported position to the next. +/// +/// Positions arrive every few seconds. Put where each one says, the car would +/// jump; so it is moved there a little at a time over about the time until +/// the next report, and turned to face the way it is going. +public final class CarMarker { + /// How long a move takes: the interval couriers report at. + private static final int GLIDE_MILLIS = 3000; + private static final int FRAME_MILLIS = 50; + /// The turn is done in this much of the move, as a car turns at a corner + /// and not all along the street. + private static final double TURN_PART = 0.3; + + private final MapView map; + private final Form form; + private Marker marker; + private EncodedImage icon; + private LatLng at; + private double heading; + private LatLng from; + private LatLng to; + private double fromHeading; + private double toHeading; + private long startedAt; + private UITimer timer; + + /// @param form the screen the map is on; the car moves while it is showing + /// @param heading degrees clockwise from north + public CarMarker(MapView map, Form form, LatLng at, double heading) { + this.map = map; + this.form = form; + place(at, heading); + } + + public LatLng position() { + return at; + } + + public double heading() { + return heading; + } + + /// Drives the car to `target`. + /// + /// @param facing the heading reported with the position, or a negative + /// number when none was, and the way the car moved says which way it + /// faces + public void moveTo(LatLng target, double facing) { + double moved = Maps.meters(at, target); + double wanted = facing >= 0 ? facing : moved > 2 ? Maps.heading(at, target) : heading; + // Too far to have been driven in one interval: the car was somewhere + // else, and is shown where it is. + if (moved > 1500) { + stop(); + place(target, wanted); + map.repaint(); + return; + } + from = at; + to = target; + fromHeading = heading; + toHeading = wanted; + startedAt = System.currentTimeMillis(); + if (timer == null) { + timer = UITimer.timer(FRAME_MILLIS, true, form, this::step); + } + } + + public void remove() { + stop(); + if (marker != null) { + map.removeMarker(marker); + marker = null; + } + } + + private void stop() { + if (timer != null) { + timer.cancel(); + timer = null; + } + } + + private void step() { + double part = Math.min(1d, (System.currentTimeMillis() - startedAt) + / (double) GLIDE_MILLIS); + // The shorter way round from one heading to the other. + double turn = toHeading - fromHeading; + while (turn > 180) { + turn -= 360; + } + while (turn < -180) { + turn += 360; + } + place(new LatLng(from.getLatitude() + (to.getLatitude() - from.getLatitude()) * part, + from.getLongitude() + (to.getLongitude() - from.getLongitude()) * part), + fromHeading + turn * Math.min(1d, part / TURN_PART)); + map.repaint(); + if (part >= 1d) { + stop(); + } + } + + private void place(LatLng position, double facing) { + at = position; + heading = facing; + EncodedImage wanted = Maps.car(facing); + if (marker != null && wanted == icon) { //NOPMD CompareObjectsWithEquals + marker.setPosition(position); + return; + } + // A marker's picture cannot be changed, so a car that has turned is a + // new marker. + if (marker != null) { + map.removeMarker(marker); + } + icon = wanted; + marker = map.addMarker(new MarkerOptions(position).icon(wanted).anchor(0.5f, 0.5f)); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java new file mode 100644 index 00000000000..0751c8f03da --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.map; + +import com.codename1.location.Location; +import com.codename1.location.LocationListener; +import com.codename1.location.LocationManager; +import com.codename1.location.LocationRequest; +import com.codename1.maps.LatLng; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.AppConfig; + +import java.util.ArrayList; +import java.util.List; + +/// Where the device is. +/// +/// On a phone, and in a browser, that is the position the platform reports. In +/// the simulator there is no position worth the name, so the app stands at a +/// fixed point instead -- and since a simulated courier would then never move, +/// the courier screen drives the car along the route itself when [#simulated] +/// says so. +/// +/// Nothing here waits. The platform answers when it answers -- after the user +/// has dealt with a permission prompt, which may be never -- so [#here] is +/// always the best answer there is at that moment, and a screen that wants to +/// follow the search [#watch]es it: the position when it comes, or that it was +/// refused, or that there is none to be had, or that nothing has been heard +/// yet. The screen stays usable through all of them, on the middle of the demo +/// city until something better is known. +public final class Locator { + /// Positions are made up; nothing is being looked for. + public static final int SIMULATED = 0; + /// Asked, and not answered yet. + public static final int SEARCHING = 1; + /// [#here] is where the device is. + public static final int FOUND = 2; + /// The user, or a setting, refused. + public static final int DENIED = 3; + /// The platform has no position to give. + public static final int UNAVAILABLE = 4; + /// Still not answered after [#PATIENCE]: a prompt nobody has dealt with, + /// or a fix that is slow to come. An answer is still taken when it does. + public static final int SLOW = 5; + + /// How long a search may go unanswered before a screen says so. + private static final int PATIENCE = 8000; + /// How often a tracking search asks for a position. + private static final long TRACK_MILLIS = 5000L; + + /// Told every time the search gets somewhere, on the event thread. + public interface Watcher { + /// @param state one of the constants of [Locator] + /// @param where the position for [#FOUND]; otherwise where the app + /// stands in the meantime + void located(int state, LatLng where); + } + + private static LatLng pinned; + private static LatLng found; + private static int state = SIMULATED; + private static boolean started; + /// Whether the position is wanted precisely and in the background: a + /// courier on line. See [#track]. + private static boolean tracking; + /// Whether the search was put down because the app went to the + /// background, to be taken up again when it returns. + private static boolean paused; + private static double heading; + /// Counts the searches, so that the patience of one that has been + /// restarted does not run out on the next. + private static int search; + private static final List WATCHERS = new ArrayList(); + + private Locator() { + } + + /// Stands the device at `point`, whatever it reports. For the tests. + public static void pin(LatLng point) { + pinned = point; + } + + /// Whether positions are made up, because there is no real one to read. + public static boolean simulated() { + return pinned != null || CN.isSimulator(); + } + + /// The device's position as far as it is known: the last one reported, + /// and until there is one the middle of the demo city. Never waits. + public static LatLng here() { + if (pinned != null) { + return pinned; + } + if (found != null && !CN.isSimulator()) { + return found; + } + return new LatLng(AppConfig.DEFAULT_LAT, AppConfig.DEFAULT_LNG); + } + + /// The direction the device last moved in, in degrees from north. + public static double heading() { + return heading; + } + + // tag::morsel-locator-track[] + /// Asks for the position precisely, often, and on in the background, or + /// goes back to the ordinary search. A courier on line tracks: customers follow + /// the car on a map, and the server offers deliveries by distance. + /// + /// The platform decides what "in the background" allows. With the + /// `location` background mode an iPhone goes on reporting after the app is + /// put away; Android does while the app runs a foreground service, which is + /// what [com.codenameone.examples.morsel.courier.Shift] starts. + public static void track(boolean on) { + if (tracking == on) { + return; + } + tracking = on; + if (started && !simulated()) { + LocationManager.getLocationManager().setLocationListener(null); + start(true); + } + } + + /// The app went to the background. Unless a courier is tracking, the + /// platform is told to stop looking: an app with a background location + /// mode is otherwise kept running, and draining the battery, for a map + /// nobody is looking at. + public static void paused() { + if (!started || tracking || simulated()) { + return; + } + paused = true; + search++; + LocationManager.getLocationManager().setLocationListener(null); + } + + /// The app is back: whatever [#paused] put down is taken up again. + public static void resumed() { + if (!paused) { + return; + } + paused = false; + if (started && !simulated()) { + start(true); + } + } + // end::morsel-locator-track[] + + /// How the search stands: one of the constants above. + public static int state() { + return simulated() ? SIMULATED : state; + } + + /// Follows the search, starting it if nothing has yet. Asking is what + /// makes the platform ask the user for permission. `watcher` is told + /// where things stand now unless that is nothing worth telling. + public static void watch(Watcher watcher) { + if (!WATCHERS.contains(watcher)) { + WATCHERS.add(watcher); + } + if (simulated()) { + return; + } + if (!started) { + start(true); + } else if (state != SEARCHING) { + watcher.located(state, here()); + } + } + + /// Stops following. When nobody is left the platform is told to stop + /// too: a location listener left in place keeps the receiver running, and + /// the battery paying for it, on every screen without a map and after + /// signing out. The position it found is kept for [#here], and the next + /// [#watch] starts the search again. + public static void unwatch(Watcher watcher) { + WATCHERS.remove(watcher); + if (started && WATCHERS.isEmpty()) { + started = false; + paused = false; + // Whatever the listener or the patience of this search still has + // to say is about a search nobody is following. + search++; + LocationManager.getLocationManager().setLocationListener(null); + } + } + + /// Asks again, after a refusal or a failure. + public static void retry() { + if (simulated()) { + return; + } + LocationManager.getLocationManager().setLocationListener(null); + start(false); + } + + private static void start(final boolean resumed) { + started = true; + final int mine = ++search; + // A search taken up again -- one screen with a map left, the next + // arrived -- already knows where the device is, and says so, where + // a first search has only that it is looking. + set(resumed && found != null ? FOUND : SEARCHING); + LocationListener listener = new LocationListener() { + @Override + public void locationUpdated(Location location) { + if (mine != search || location == null) { + return; + } + found = new LatLng(location.getLatitude(), location.getLongitude()); + if (location.getDirection() >= 0f) { + heading = location.getDirection(); + } + set(FOUND); + } + + @Override + public void providerStateChanged(int newState) { + if (mine != search || newState == LocationManager.AVAILABLE) { + return; + } + if (newState == LocationManager.OUT_OF_SERVICE) { + set(DENIED); + } else if (found == null) { + // With a position in hand, losing the signal for a while + // is not worth a word. + set(UNAVAILABLE); + } + } + }; + if (tracking) { + // Every few seconds, as precisely as the device can: what a car on + // a customer's map needs. The ordinary search takes the platform's + // default, which on an iPhone waits for 100 meters of movement. + LocationManager.getLocationManager().setLocationListener(listener, + new LocationRequest(LocationRequest.PRIORITY_HIGH_ACCUARCY, TRACK_MILLIS)); + } else { + LocationManager.getLocationManager().setLocationListener(listener); + } + CN.setTimeout(PATIENCE, new Runnable() { + @Override + public void run() { + if (mine == search && state == SEARCHING) { + set(SLOW); + } + } + }); + } + + private static void set(int next) { + state = next; + LatLng where = here(); + // A copy: a watcher may stop watching when it hears. + List all = new ArrayList(WATCHERS); + for (Watcher watcher : all) { + watcher.located(next, where); + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java new file mode 100644 index 00000000000..92c4566dcbc --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.map; + +import com.codename1.maps.LatLng; +import com.codename1.maps.MapView; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.LayeredLayout; +import com.codenameone.examples.morsel.ui.BottomSheet; +import com.codenameone.examples.morsel.ui.Ui; + +/// A map that fills the screen, and what floats over it. +/// +/// From the top: a shade under the status bar, so the clock stays readable +/// over any map; a bar with a round button at its start and whatever the +/// screen puts beside it -- a search card, a status; a chip for one short +/// fact. At the bottom: round buttons at the end of the screen, and under +/// them the [BottomSheet]. +/// +/// Under the chip, when there is something to say about where the device is: +/// that its position was refused, cannot be had, or is slow to come. It is a +/// button, and pressing it asks again; the map and everything else stay as +/// usable as they were, standing on the demo city in the meantime. See +/// [Locator]. +/// +/// The map is one layer and all of that is another. The parts of the upper +/// layer with nothing in them take no touches, so the map under them is +/// dragged and pinched as if nothing were there. +public final class MapStage { + public final MapView map; + private final Container layers; + private final Container north = new Container(BoxLayout.y()); + private final Container south = new Container(BoxLayout.y()); + private final Container header = new Container(new BorderLayout()); + private final Container chips = new Container(new FlowLayout(Component.CENTER)); + private final Container side = new Container(new FlowLayout(Component.RIGHT)); + private final Label chip = Ui.chip("", (char) 0); + private final Button notice = new Button("", "WlChip"); + private final Container notices = new Container(new FlowLayout(Component.CENTER)); + private final Locator.Watcher watcher = new Locator.Watcher() { + @Override + public void located(int state, LatLng where) { + locationChanged(state, where); + } + }; + private Located located; + private boolean everLocated; + /// Whether the search under way was asked for with the notice, and so + /// deserves to be seen to be under way. + private boolean asked; + private final BottomSheet sheet = new BottomSheet(); + /// Counts the times the camera was sent somewhere, so that a framing put + /// off until after a layout can tell it has been overtaken. + private int framed; + + /// Told where the device is, when that becomes known and when it changes. + public interface Located { + /// @param first whether this is the first position this map has had: + /// the moment to move the camera, which later ones must not, since + /// by then the user may be looking at somewhere else + void at(LatLng where, boolean first); + } + + /// @param lead the round button at the start of the bar: the menu on a + /// home screen, the way back on any other + public MapStage(LatLng center, int zoom, Button lead) { + map = Maps.create(center, zoom); + + Container shade = new Container(BoxLayout.y()); + shade.setUIID("WlStatusShade"); + // All of its height is the inset the device asks to be kept clear, so + // it is exactly as tall as the status bar and absent where there is + // none to cover. + shade.setSafeArea(true); + Container bar = new Container(new BorderLayout()); + bar.setUIID("WlMapBar"); + bar.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(lead)); + bar.add(BorderLayout.CENTER, header); + chip.setName("chip"); + chip.setHidden(true); + chips.add(chip); + notice.setName("locationNotice"); + FontImage.setMaterialIcon(notice, FontImage.MATERIAL_LOCATION_OFF, 3.2f); + notice.addActionListener(e -> { + asked = true; + Locator.retry(); + }); + notices.setHidden(true); + notices.add(notice); + north.add(shade).add(bar).add(chips).add(notices); + south.add(side).add(sheet); + + Container floating = new Container(new BorderLayout()); + floating.add(BorderLayout.NORTH, north); + floating.add(BorderLayout.SOUTH, south); + layers = new Container(new LayeredLayout()) { + @Override + protected void initComponent() { + super.initComponent(); + // After this screen is up: an answer that is already known is + // told at once, and telling it lays the screen out. + CN.callSerially(new Runnable() { + @Override + public void run() { + if (isInitialized()) { + Locator.watch(watcher); + } + } + }); + } + + @Override + protected void deinitialize() { + Locator.unwatch(watcher); + super.deinitialize(); + } + }; + layers.add(map).add(floating); + } + + /// Has `listener` told where the device is. Until it is, the screen has + /// [Locator#here], which is the demo city when nothing better is known. + public void whenLocated(Located listener) { + located = listener; + } + + private void locationChanged(int state, LatLng where) { + String text = null; + if (state == Locator.FOUND) { + boolean first = !everLocated; + everLocated = true; + if (located != null) { + located.at(where, first); + } + } else if (state == Locator.DENIED) { + text = "Location is turned off. Try again"; + } else if (state == Locator.UNAVAILABLE) { + text = "Can't find your location. Try again"; + } else if (state == Locator.SLOW) { + text = "Still looking for your location. Try again"; + } else if (state == Locator.SEARCHING && asked) { + text = "Looking for your location..."; + } + if (state != Locator.SEARCHING) { + asked = false; + } + boolean none = text == null; + if (!none) { + notice.setText(text); + } + if (notices.isHidden() != none || !none) { + notices.setHidden(none); + north.revalidate(); + } + } + + /// The map with everything over it, to add to a screen. + public Container layers() { + return layers; + } + + public BottomSheet sheet() { + return sheet; + } + + /// Puts `content` beside the round button at the top, in place of what + /// was there; null leaves the place empty. + public void header(Component content) { + header.removeAll(); + if (content != null) { + header.add(BorderLayout.CENTER, content); + } + north.revalidate(); + } + + /// Adds a round button above the sheet: recentre, and the like. + public Button addRound(char icon, String name, final Runnable onClick) { + Button button = Ui.round(icon, name, e -> onClick.run()); + side.add(button); + return button; + } + + /// Shows `text` in the chip under the bar; null or empty takes the chip + /// away. + public void chip(String text, char icon) { + boolean none = text == null || text.length() == 0; + if (!none) { + chip.setText(text); + if (icon != 0) { + FontImage.setMaterialIcon(chip, icon, 3.2f); + } + } + if (chip.isHidden() != none || !none) { + chip.setHidden(none); + north.revalidate(); + } + } + + /// Moves the camera so that `points` are all in the part of the map + /// nothing covers. + /// + /// Twice, the second time after the screen has been laid out again: this + /// is asked for while the sheet is being filled, when the sheet is still + /// as tall as what it showed before. + public void frame(final LatLng... points) { + final int asked = ++framed; + fit(points); + CN.callSerially(new Runnable() { + @Override + public void run() { + // Not if the camera has been sent somewhere else since. + if (asked == framed) { + layers.revalidate(); + fit(points); + } + } + }); + } + + /// Puts `center` in the middle of the map, in place of whatever was + /// being framed. + public void center(LatLng center, int zoom) { + framed++; + map.moveCamera(center, zoom); + } + + private void fit(LatLng... points) { + // The chip counts whether it is showing or not: it comes and goes + // with the courier's car, and the pins must not end up under it. + int top = north.getHeight() + (chip.isHidden() ? CN.convertToPixels(9f) : 0); + int bottom = Math.max(south.getHeight(), south.getPreferredH()); + if (map.getHeight() > 0 && bottom == 0) { + // Asked before the first layout: the sheet will take about this. + bottom = map.getHeight() * 2 / 5; + } + // A little over, so a pin at the edge is not half under the sheet. + int margin = CN.convertToPixels(4f); + Maps.frame(map, top + margin, bottom + margin, points); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java new file mode 100644 index 00000000000..5897184c23d --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.map; + +import com.codename1.maps.LatLng; +import com.codename1.maps.MapBounds; +import com.codename1.maps.MapView; +import com.codename1.maps.Marker; +import com.codename1.maps.MarkerOptions; +import com.codename1.maps.Polyline; +import com.codename1.maps.vector.MapStyle; +import com.codename1.maps.vector.MvtTileSource; +import com.codename1.maps.vector.TileSource; +import com.codename1.ui.CN; +import com.codename1.ui.EncodedImage; +import com.codename1.ui.Graphics; +import com.codename1.ui.Image; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; +import com.codename1.util.MathUtil; +import com.codenameone.examples.morsel.ui.Look; + +import java.util.ArrayList; +import java.util.List; + +/// The map, as the screens use it. +/// +/// `MapView` draws OpenStreetMap vector tiles with no key and no account, in a +/// light or a dark style to match the app. Everything put on it is drawn here, +/// in code, in colours from `theme.css`: the pickup and drop-off pins +/// (`WlMarkerPickup`, `WlMarkerDropoff`), the car (`WlMarkerCar`) and the route +/// (`WlRoute`). In each of those styles the text colour is the shape and the +/// background colour is its edge. +public final class Maps { + public static final int CITY_ZOOM = 13; + public static final int STREET_ZOOM = 15; + /// The car is drawn once for each of this many directions, and the + /// drawings kept. + private static final int HEADINGS = 36; + + private static TileSource tiles; + private static final EncodedImage[] CARS = new EncodedImage[HEADINGS]; + /// Whether [#CARS] were drawn for the dark look. + private static boolean carsDark; + + private Maps() { + } + + /// Draws every map from these tiles from now on. The tests use the handful + /// bundled with them, so a screenshot does not depend on a tile server. + public static void useTiles(TileSource source) { + tiles = source; + } + + public static MapView create(LatLng center, int zoom) { + MapView map = new MapView(tiles == null ? MvtTileSource.openFreeMap() : tiles, + Look.dark() ? MapStyle.dark() : MapStyle.light()); + map.setName("map"); + // The map is the screen here, not an item on a page that scrolls, so + // the wheel is for zooming it. + map.setWheelZoom(true); + map.moveCamera(center, zoom); + return map; + } + + /// Where the delivery starts: a disc with a dot in it. + public static Marker pickup(MapView map, LatLng at) { + return marker(map, at, pin("WlMarkerPickup", true)); + } + + /// Where the delivery ends: the same, square. + public static Marker dropoff(MapView map, LatLng at) { + return marker(map, at, pin("WlMarkerDropoff", false)); + } + + private static Marker marker(MapView map, LatLng at, EncodedImage icon) { + return map.addMarker(new MarkerOptions(at).icon(icon).anchor(0.5f, 0.5f)); + } + + private static EncodedImage pin(String uiid, boolean round) { + Style style = UIManager.getInstance().getComponentStyle(uiid); + int side = CN.convertToPixels(5.5f); + int edge = Math.max(2, side / 7); + int dot = Math.max(2, side / 4); + Image image = Image.createImage(side, side, 0); + Graphics g = image.getGraphics(); + g.setAntiAliased(true); + g.setColor(style.getBgColor()); + fill(g, round, 0, side); + g.setColor(style.getFgColor()); + fill(g, round, edge, side - 2 * edge); + g.setColor(style.getBgColor()); + fill(g, round, (side - dot) / 2, dot); + return EncodedImage.createFromImage(image, false); + } + + private static void fill(Graphics g, boolean round, int at, int side) { + if (round) { + g.fillArc(at, at, side, side, 0, 360); + } else { + g.fillRoundRect(at, at, side, side, side / 4, side / 4); + } + } + + /// A car seen from above, pointing along `heading` -- degrees clockwise + /// from north. A map marker cannot be turned, so the car is drawn turned. + static EncodedImage car(double heading) { + boolean dark = Look.dark(); + if (dark != carsDark) { + carsDark = dark; + for (int iter = 0; iter < HEADINGS; iter++) { + CARS[iter] = null; + } + } + int step = (int) Math.round(heading * HEADINGS / 360d) % HEADINGS; + if (step < 0) { + step += HEADINGS; + } + if (CARS[step] == null) { + CARS[step] = drawCar(step * 360d / HEADINGS); + } + return CARS[step]; + } + + private static EncodedImage drawCar(double heading) { + Style style = UIManager.getInstance().getComponentStyle("WlMarkerCar"); + // Square, and wide enough for the car's length at any angle. + int side = CN.convertToPixels(9f); + double length = side * 0.78; + double width = side * 0.40; + Image image = Image.createImage(side, side, 0); + Graphics g = image.getGraphics(); + g.setAntiAliased(true); + double angle = Math.toRadians(heading); + // The body with its corners cut, as across (x) and along (y), in + // halves of the width and the length; first the edge, then the body + // inside it, then the two windows. + double[] bodyX = {-0.62, 0.62, 1, 1, 0.7, -0.7, -1, -1}; + double[] bodyY = {1, 1, 0.72, -0.8, -1, -1, -0.8, 0.72}; + g.setColor(style.getBgColor()); + shape(g, bodyX, bodyY, width / 2 + side * 0.035, length / 2 + side * 0.035, angle, side); + g.setColor(style.getFgColor()); + shape(g, bodyX, bodyY, width / 2, length / 2, angle, side); + g.setColor(style.getBgColor()); + shape(g, new double[] {-0.62, 0.62, 0.74, -0.74}, new double[] {0.5, 0.5, 0.14, 0.14}, + width / 2, length / 2, angle, side); + shape(g, new double[] {-0.7, 0.7, 0.6, -0.6}, new double[] {-0.42, -0.42, -0.7, -0.7}, + width / 2, length / 2, angle, side); + return EncodedImage.createFromImage(image, false); + } + + /// Fills the outline `(x, y)`, scaled and turned about the middle of a + /// square image `side` across. + private static void shape(Graphics g, double[] x, double[] y, double scaleX, double scaleY, + double angle, int side) { + int[] screenX = new int[x.length]; + int[] screenY = new int[x.length]; + double sin = Math.sin(angle); + double cos = Math.cos(angle); + for (int iter = 0; iter < x.length; iter++) { + double across = x[iter] * scaleX; + double along = y[iter] * scaleY; + // Along is up the screen when the heading is north. + screenX[iter] = (int) Math.round(side / 2d + across * cos + along * sin); + screenY[iter] = (int) Math.round(side / 2d + across * sin - along * cos); + } + g.fillPolygon(screenX, screenY, x.length); + } + + /// A route on the map: the line and the edge drawn under it. + public static final class Line { + private final Polyline edge; + private final Polyline line; + + Line(Polyline edge, Polyline line) { + this.edge = edge; + this.line = line; + } + + public void remove(MapView map) { + map.removePolyline(edge); + map.removePolyline(line); + } + } + + /// Draws `points` as the route and returns it, to remove it by. + public static Line route(MapView map, List points) { + Style style = UIManager.getInstance().getComponentStyle("WlRoute"); + int width = Math.max(4, CN.convertToPixels(1.1f)); + // The edge first, so that the line is drawn over it. + Polyline edge = stroke(points, style.getBgColor(), width + Math.max(2, width / 2)); + Polyline line = stroke(points, style.getFgColor(), width); + map.addPolyline(edge); + map.addPolyline(line); + return new Line(edge, line); + } + + private static Polyline stroke(List points, int color, int width) { + LatLng[] line = new LatLng[points.size()]; + points.toArray(line); + return new Polyline(line).setStrokeColor(color).setStrokeWidth(width); + } + + /// Moves the camera so that all of `points` are in view. + public static void show(MapView map, LatLng... points) { + frame(map, 0, 0, points); + } + + /// Moves the camera so that all of `points` are in the part of the map + /// that is not covered: `top` pixels of it are under something at the top, + /// and `bottom` pixels under the sheet. + public static void frame(MapView map, int top, int bottom, LatLng... points) { + List all = new ArrayList(); + for (LatLng point : points) { + if (point != null) { + all.add(point); + } + } + if (all.isEmpty()) { + return; + } + int height = map.getHeight(); + if (height > 0 && top + bottom > 0) { + // The camera fits what it is given to the whole map. So it is given + // more: the points, and a point far enough above and below them + // that the points themselves land in the clear part. + double north = all.get(0).getLatitude(); + double south = north; + for (LatLng point : all) { + north = Math.max(north, point.getLatitude()); + south = Math.min(south, point.getLatitude()); + } + double clear = Math.max(0.3, 1d - (top + bottom) / (double) height); + double span = Math.max(north - south, 0.002) / clear; + double middle = all.get(0).getLongitude(); + all.add(new LatLng(north + span * top / height, middle)); + all.add(new LatLng(south - span * bottom / height, middle)); + } + if (all.size() == 1) { + map.moveCamera(all.get(0), STREET_ZOOM); + } else { + // Room for a marker's own size at the edge, on a screen of any density. + map.fitBounds(MapBounds.fromCoordinates(all), CN.convertToPixels(10, true)); + } + } + + /// The distance between two points in meters, along the surface. + public static double meters(LatLng a, LatLng b) { + double lat1 = Math.toRadians(a.getLatitude()); + double lat2 = Math.toRadians(b.getLatitude()); + double dLat = lat2 - lat1; + double dLng = Math.toRadians(b.getLongitude() - a.getLongitude()); + double h = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) * Math.sin(dLng / 2); + return 2 * 6371000d * MathUtil.asin(Math.min(1d, Math.sqrt(h))); + } + + /// The compass heading from `a` to `b`, in degrees. + public static double heading(LatLng a, LatLng b) { + double lat1 = Math.toRadians(a.getLatitude()); + double lat2 = Math.toRadians(b.getLatitude()); + double dLng = Math.toRadians(b.getLongitude() - a.getLongitude()); + double y = Math.sin(dLng) * Math.cos(lat2); + double x = Math.cos(lat1) * Math.sin(lat2) + - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng); + double degrees = Math.toDegrees(MathUtil.atan2(y, x)); + return degrees < 0 ? degrees + 360 : degrees; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java new file mode 100644 index 00000000000..db2e632ece6 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.map; + +import com.codename1.maps.LatLng; +import com.codename1.maps.routing.Route; +import com.codename1.maps.routing.RouteCallback; +import com.codename1.maps.routing.RouteRequest; +import com.codename1.maps.routing.Routing; +import com.codename1.maps.routing.TravelMode; + +import java.util.ArrayList; +import java.util.List; + +/// The way from one point to another by road. +/// +/// The roads come from `Routing`, whose default service is the public OSRM +/// demo server: free, keyless, and with no promise of being there. So a route +/// that cannot be found is not an error here. The answer is then the straight +/// line, with the time it would take at a city's pace -- the map still shows +/// where the car is going, and the fare is still quoted, from a length that is +/// a little short. For an app you ship, give `Routing.setService` a routing +/// server of your own. +public final class Routes { + /// A route, reduced to what the screens use. + public static final class Way { + public final List points; + public final double meters; + public final double seconds; + + Way(List points, double meters, double seconds) { + this.points = points; + this.meters = meters; + this.seconds = seconds; + } + } + + public interface Found { + void found(Way way); + } + + /// Meters a second in town: 30 km/h. + private static final double CITY_SPEED = 8.3; + + private static boolean straight; + + private Routes() { + } + + /// Answers every request with the straight line and asks no server. The + /// tests turn this on, so that what they see does not depend on one. + public static void useStraightLines(boolean on) { + straight = on; + } + + public static void find(final LatLng from, final LatLng to, final Found found) { + if (straight) { + found.found(line(from, to)); + return; + } + Routing.findRoute(new RouteRequest(from, to).setTravelMode(TravelMode.DRIVING), + new RouteCallback() { + @Override + public void routesFound(List routes) { + Object first = routes == null || routes.isEmpty() ? null : routes.get(0); + if (!(first instanceof Route)) { + found.found(line(from, to)); + return; + } + Route route = (Route) first; + List points = new ArrayList(); + for (Object point : route.getPoints()) { + if (point instanceof LatLng) { + points.add((LatLng) point); + } + } + if (points.size() < 2) { + found.found(line(from, to)); + return; + } + found.found(new Way(points, route.getDistanceMeters(), + route.getDurationSeconds())); + } + + @Override + public void routeFailed(String message, Throwable err) { + found.found(line(from, to)); + } + }); + } + + private static Way line(LatLng from, LatLng to) { + List points = new ArrayList(); + points.add(from); + points.add(to); + double meters = Maps.meters(from, to); + return new Way(points, meters, meters / CITY_SPEED); + } + + /// The point `meters` along `points` from its start; the end when the line + /// is shorter than that. + public static LatLng along(List points, double meters) { + double left = meters; + for (int iter = 1; iter < points.size(); iter++) { + LatLng a = points.get(iter - 1); + LatLng b = points.get(iter); + double leg = Maps.meters(a, b); + if (left <= leg && leg > 0) { + double part = left / leg; + return new LatLng(a.getLatitude() + (b.getLatitude() - a.getLatitude()) * part, + a.getLongitude() + (b.getLongitude() - a.getLongitude()) * part); + } + left -= leg; + } + return points.get(points.size() - 1); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java new file mode 100644 index 00000000000..d599d284ec8 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.net; + +import com.codename1.io.Preferences; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.api.PlaceDto; +import com.codenameone.examples.morsel.api.PreferencesDto; +import com.codenameone.examples.morsel.api.ProfileDto; + +/// Keeps what the user chose in Settings with their account. +/// +/// [Prefs] is still what every screen reads and writes, and still on the +/// device: that copy is what the app starts from before the server has +/// answered, and all there is for someone who is signed out. This class is the +/// other half. When someone signs in it fetches the account's values and puts +/// them into `Prefs`, so a phone they have never used looks like the one they +/// left; and from then on it is told of every change and sends it back. +/// +/// A change is not sent the moment it is made. A name typed into a field +/// changes with every letter, so changes are gathered for a moment and sent as +/// one request with everything in it. +/// +/// A change is not forgotten when its request fails, either. The device's +/// values stay marked as unsent, on the device, until the server has taken +/// them: they are sent again a little later, and an account load that finds +/// them unsent -- the app restarted before the network came back -- sends them +/// rather than overwriting them with what the server still holds. +public final class Account { + /// How long after the last change the values are sent. + private static final int SETTLE_MILLIS = 700; + /// How long after a request failed it is sent again. + private static final int RETRY_MILLIS = 30000; + /// The keys under which each half is marked unsent; the value is the + /// account it was not sent for. + private static final String UNSENT_PREFERENCES = "morsel.account.unsent.preferences"; + private static final String UNSENT_PROFILE = "morsel.account.unsent.profile"; + + /// True while the account's values are being put into `Prefs`, which + /// reports each of them as a change like any other. + private static boolean applying; + private static boolean preferencesChanged; + private static boolean profileChanged; + private static boolean waiting; + /// Whether each half of the account's values has arrived since it signed + /// in. Until one has, the device's copy of it is not the account's -- it + /// is the defaults, or what an earlier session left -- and is not sent. + private static boolean preferencesLoaded; + private static boolean profileLoaded; + /// The profile as the server last gave it: the name and the gender are + /// part of it, and are sent back as they were with the rest. + private static ProfileDto profile; + /// What a request carries beyond the settings, for a test to look at. + private static PreferencesDto sent; + /// How many requests of each half were sent. An answer clears the unsent + /// mark only if no later request of its half went out after it. + private static int preferencesSends; + private static int profileSends; + + private Account() { + } + + /// Starts listening to `Prefs`. Called once, when the app starts. + public static void start() { + Prefs.onChange(Account::changed); + } + + /// Fetches the account's settings and profile into `Prefs`, then runs + /// `done` -- also when the server did not answer, with what the device + /// had. Each half is asked for whether or not the other came, and one that + /// did not come is asked for again before anything of it is saved. + public static void load(final Runnable done) { + if (Session.user() == null) { + done.run(); + return; + } + profile = null; + preferencesLoaded = false; + profileLoaded = false; + // What another account left unsent is not this one's to send. + String owner = Session.owner(); + if (!isUnsent(UNSENT_PREFERENCES, owner)) { + Preferences.delete(UNSENT_PREFERENCES); + } + if (!isUnsent(UNSENT_PROFILE, owner)) { + Preferences.delete(UNSENT_PROFILE); + } + fetch(done); + } + + private static void fetch(final Runnable done) { + final String owner = Session.owner(); + // An answer from a session that has since ended still runs `done`. + // Signing out and in while the account loads is rare, and the worst + // of it is the home screen shown a moment before its settings, which + // the new session's own load applies when it ends. The data itself is + // only taken for the account signed in now, which is checked below. + final Runnable thenProfile = () -> { + if (profileLoaded || !signedInAs(owner)) { + done.run(); + return; + } + Api.account().profile(Net.to(described -> { + if (signedInAs(owner)) { + if (described != null && isUnsent(UNSENT_PROFILE, owner)) { + // The device has a change the server never took: + // the server's name and gender are kept, the rest is + // the device's, and is sent. + profile = described; + profileLoaded = true; + profileChanged = true; + schedule(SETTLE_MILLIS); + } else { + apply(described); + profileLoaded = described != null; + } + } + done.run(); + }, (status, message) -> done.run())); + }; + if (preferencesLoaded) { + thenProfile.run(); + return; + } + Api.account().preferences(Net.to(preferences -> { + if (signedInAs(owner)) { + if (preferences != null && isUnsent(UNSENT_PREFERENCES, owner)) { + // As for the profile: the device's are newer. + preferencesLoaded = true; + preferencesChanged = true; + schedule(SETTLE_MILLIS); + } else { + apply(preferences); + preferencesLoaded = preferences != null; + } + } + thenProfile.run(); + }, (status, message) -> thenProfile.run())); + } + + /// The profile last fetched or saved, or null before there is one. + public static ProfileDto profile() { + return profile; + } + + /// Saves the name and the gender, which only the profile screen changes. + public static void saveProfile(final String name, final String gender, final Runnable done, + final Net.Failed failed) { + if (!profileLoaded) { + // The rest of the profile goes out with the name, and it has not + // been seen yet; see send(). + fetch(() -> { + if (profileLoaded) { + saveProfile(name, gender, done, failed); + } else { + failed.failed(0, "Could not reach the server. Check your connection."); + } + }); + return; + } + ProfileDto wanted = described(name, gender); + final String owner = Session.owner(); + Api.account().saveProfile(wanted, Net.to(user -> { + // The answer is about the account that asked; see send(). + if (signedInAs(owner)) { + profile = wanted; + Session.setUser(user); + } + done.run(); + }, failed)); + } + + /// The preferences sent last, or null when none were. + public static PreferencesDto lastSent() { + return sent; + } + + /// Sends what is waiting now, for a screen that is about to end the session. + public static void flush() { + waiting = false; + send(); + } + + private static void changed(String name) { + if (applying || Session.user() == null || Prefs.RECENTS.equals(name)) { + return; + } + if (Prefs.EMERGENCY_NAME.equals(name) || Prefs.EMERGENCY_PHONE.equals(name) + || Prefs.HOME.equals(name) || Prefs.WORK.equals(name)) { + profileChanged = true; + } + // The contact's number is in both: it is who a trip is shared with. + if (!Prefs.EMERGENCY_NAME.equals(name) && !Prefs.HOME.equals(name) + && !Prefs.WORK.equals(name)) { + preferencesChanged = true; + } + schedule(SETTLE_MILLIS); + } + + /// Sends what has changed `millis` from now, unless a send is already + /// waiting. + private static void schedule(int millis) { + if (!waiting) { + waiting = true; + CN.setTimeout(millis, () -> CN.callSerially(() -> { + if (waiting) { + waiting = false; + send(); + } + })); + } + } + + private static void send() { + if (Session.user() == null) { + preferencesChanged = false; + profileChanged = false; + return; + } + if (preferencesChanged && !preferencesLoaded || profileChanged && !profileLoaded) { + // The account's values never arrived, so what the device holds in + // their place is not known to be this account's: sent, it would + // replace on the server what was never seen here. They are asked + // for again instead, and the change that brought this about gives + // way to them. A later change is saved as usual once they are in. + preferencesChanged = preferencesChanged && preferencesLoaded; + profileChanged = profileChanged && profileLoaded; + fetch(() -> { }); + } + final String owner = Session.owner(); + if (preferencesChanged) { + preferencesChanged = false; + sent = preferences(); + // Marked before it goes, and cleared once the server has it: until + // then the device's values are the account's newest. + Preferences.set(UNSENT_PREFERENCES, owner); + final int send = ++preferencesSends; + Api.account().savePreferences(sent, Net.to(saved -> { + if (send == preferencesSends) { + sentFor(UNSENT_PREFERENCES, owner); + } + }, (status, message) -> { + if (refused(status)) { + // Refused: sent again, it would be refused again. + if (send == preferencesSends) { + sentFor(UNSENT_PREFERENCES, owner); + } + } else if (signedInAs(owner)) { + preferencesChanged = true; + schedule(RETRY_MILLIS); + } + })); + } + if (profileChanged) { + profileChanged = false; + final ProfileDto wanted = described(); + Preferences.set(UNSENT_PROFILE, owner); + final int send = ++profileSends; + Api.account().saveProfile(wanted, Net.to(user -> { + if (send == profileSends) { + sentFor(UNSENT_PROFILE, owner); + } + // Signing out sends what is waiting and does not wait for the + // answer. If somebody else has signed in by the time it comes, + // this profile is not theirs: kept, its name and gender would + // go out with their next change and overwrite their own. + if (signedInAs(owner)) { + profile = wanted; + } + }, (status, message) -> { + if (refused(status)) { + if (send == profileSends) { + sentFor(UNSENT_PROFILE, owner); + } + } else if (signedInAs(owner)) { + profileChanged = true; + schedule(RETRY_MILLIS); + } + })); + } + } + + /// Whether the server refused a request for what it carried, so that sent + /// again it would be refused again. Anything else -- the server not + /// reached (0), busy, failing, or the session needing a new token -- may + /// succeed later, and the change is kept for then. + private static boolean refused(int status) { + return status >= 400 && status < 500 && status != 401 && status != 408 + && status != 429; + } + + /// Whether `key` marks a change that `owner`'s account has not taken. + private static boolean isUnsent(String key, String owner) { + return owner != null && owner.equals(Preferences.get(key, "")); + } + + /// The server has what `owner` left unsent under `key`. The mark is one + /// key for every account, so an answer that comes after somebody else + /// signed in leaves theirs alone: it may be a change of their own that + /// never went through, and cleared it would be lost to the server's copy. + private static void sentFor(String key, String owner) { + if (isUnsent(key, owner)) { + Preferences.delete(key); + } + } + + /// Whether the account signed in now is `owner` ([Session#owner()]): an + /// answer that comes back after the session it was asked in has ended is + /// not acted on. + private static boolean signedInAs(String owner) { + return owner != null && owner.equals(Session.owner()); + } + + private static PreferencesDto preferences() { + String language = Prefs.language(); + return new PreferencesDto( + language.length() == 0 ? com.codenameone.examples.morsel.ui.Lang.current() + : language, + Prefs.theme(), + Prefs.units(), + Prefs.notifyDeliveryUpdates(), + Prefs.notifyReceipts(), + Prefs.notifyPromotions(), + Prefs.courierGender(), + Prefs.quietDelivery(), + Prefs.accessibleVehicle(), + Prefs.petFriendly(), + Prefs.paymentMethod(), + Prefs.tipPercent(), + Prefs.shareTrips() ? Prefs.emergencyContactPhone() : ""); + } + + /// The profile to send: the one the server gave, with the device's values + /// for what `Prefs` holds. + private static ProfileDto described() { + return described(profile == null ? Session.user().displayName() : profile.name(), + profile == null ? Session.user().gender() : profile.gender()); + } + + /// The same, with `name` and `gender` in place of the ones held. + private static ProfileDto described(String name, String gender) { + PlaceDto home = Prefs.home(); + PlaceDto work = Prefs.work(); + return new ProfileDto(name, gender, Prefs.emergencyContactName(), + Prefs.emergencyContactPhone(), home == null ? "" : home.name(), + work == null ? "" : work.name()); + } + + private static void apply(PreferencesDto from) { + if (from == null) { + return; + } + applying = true; + try { + // An account that has never chosen a language leaves the device's. + if (from.language() != null && from.language().length() > 0) { + Prefs.setLanguage(from.language()); + } + Prefs.setTheme(from.theme()); + Prefs.setUnits(from.units()); + Prefs.setNotifyDeliveryUpdates(from.notifyDeliveryUpdates()); + Prefs.setNotifyReceipts(from.notifyReceipts()); + Prefs.setNotifyPromotions(from.notifyPromotions()); + Prefs.setCourierGender(from.courierGender()); + Prefs.setQuietDelivery(from.quietDelivery()); + Prefs.setAccessibleVehicle(from.accessibleVehicle()); + Prefs.setPetFriendly(from.petFriendly()); + String method = from.defaultPaymentMethodId(); + if (method != null && method.length() > 0) { + Prefs.setPaymentMethod(method); + } + Prefs.setTipPercent(from.defaultTipPercent()); + String sharedWith = from.shareTripsWith(); + Prefs.setShareTrips(sharedWith != null && sharedWith.length() > 0); + } finally { + applying = false; + } + } + + private static void apply(ProfileDto from) { + if (from == null) { + return; + } + profile = from; + applying = true; + try { + Prefs.setEmergencyContactName(from.emergencyContactName()); + Prefs.setEmergencyContactPhone(from.emergencyContactPhone()); + Prefs.setHome(place(Prefs.home(), from.homeAddress())); + Prefs.setWork(place(Prefs.work(), from.workAddress())); + } finally { + applying = false; + } + } + + /// The place for an address the account has. The server keeps the words + /// and not the position, so the place this device already has is kept when + /// it is the same one; another is left without a position, for the screen + /// that goes there to look up. + private static PlaceDto place(PlaceDto known, String address) { + if (address == null || address.length() == 0) { + return null; + } + if (known != null && address.equals(known.name())) { + return known; + } + return new PlaceDto(address, "", 0d, 0d); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java new file mode 100644 index 00000000000..9baf3a3e4ae --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.net; + +import com.codename1.io.rest.RestClients; +import com.codenameone.examples.morsel.api.AccountApi; +import com.codenameone.examples.morsel.api.AdminApi; +import com.codenameone.examples.morsel.api.CourierApi; +import com.codenameone.examples.morsel.api.OnboardingApi; +import com.codenameone.examples.morsel.api.GeoApi; +import com.codenameone.examples.morsel.api.PaymentApi; +import com.codenameone.examples.morsel.api.CustomerApi; + +/// The server, as the app calls it. +/// +/// Each of these is an implementation the build generated from an interface in +/// the `shared` module. The server implements the other half of the same +/// interface, so a path or a field that changes on one side stops the other +/// from compiling. +public final class Api { + private static AccountApi account; + private static CustomerApi customer; + private static CourierApi courier; + private static GeoApi geo; + private static AdminApi admin; + private static PaymentApi payments; + private static OnboardingApi onboarding; + + private Api() { + } + + static void connect(String baseUrl) { + account = RestClients.create(AccountApi.class, baseUrl); + customer = RestClients.create(CustomerApi.class, baseUrl); + courier = RestClients.create(CourierApi.class, baseUrl); + geo = RestClients.create(GeoApi.class, baseUrl); + admin = RestClients.create(AdminApi.class, baseUrl); + payments = RestClients.create(PaymentApi.class, baseUrl); + onboarding = RestClients.create(OnboardingApi.class, baseUrl); + } + + public static AccountApi account() { + return account; + } + + public static CustomerApi customer() { + return customer; + } + + public static CourierApi courier() { + return courier; + } + + public static GeoApi geo() { + return geo; + } + + public static AdminApi admin() { + return admin; + } + + public static PaymentApi payments() { + return payments; + } + + /// Applying to drive, which is open to everyone; [#courier] is for those + /// whose application was approved. + public static OnboardingApi onboarding() { + return onboarding; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java new file mode 100644 index 00000000000..075ddbf5848 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.net; + +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; +import com.codenameone.examples.morsel.ui.Lang; + +/// Makes a call through a generated client and sorts out what came back. +/// +/// A generated method answers with a `Response` whatever happened: null when +/// the server could not be reached, and otherwise a status with either the +/// typed result or, for a refusal, the server's reason as text. This turns that +/// into one of two callbacks, so a screen writes what to do with the result and +/// what to do with a failure, and nothing else. +/// +/// ```java +/// Api.customer().active(Net.to(delivery -> show(delivery), Ui::fail)); +/// ``` +public final class Net { + /// A call to a generated client method, waiting for its callback. + public interface Call { + void run(OnComplete> callback); + } + + public interface Ok { + void got(T value); + } + + public interface Failed { + /// @param status the HTTP status, or 0 when the server was not reached + /// @param message something to show the user + void failed(int status, String message); + } + + private Net() { + } + + /// The callback to hand a generated method: `ok` gets the result, `failed` + /// everything else. + public static OnComplete> to(final Ok ok, final Failed failed) { + return new OnComplete>() { + @Override + public void completed(Response response) { + if (response == null) { + failed.failed(0, "Could not reach the server. Check your connection."); + return; + } + int status = response.getResponseCode(); + if (status >= 200 && status < 300) { + ok.got(response.getResponseData()); + return; + } + failed.failed(status, reason(response, status)); + } + }; + } + + /// Makes a call that was handed over as a value, which is how one screen + /// serves several calls of the same shape. + public static void send(Call call, Ok ok, Failed failed) { + call.run(to(ok, failed)); + } + + /// What the server said when it refused. Read as an `Object` and tested, + /// never cast to the type the call was declared with: on a refusal the body + /// is text whatever that type is. + private static String reason(Response response, int status) { + Object body = response.getResponseData(); + if (body instanceof String && ((String) body).length() > 0 + && ((String) body).length() < 200 && ((String) body).indexOf('<') < 0 + && ((String) body).indexOf('{') < 0) { + return (String) body; + } + if (status == 401) { + return "Please sign in again."; + } + if (status == 403) { + return "Your account is not allowed to do that."; + } + if (status == 429) { + return "Too many attempts. Wait a moment and try again."; + } + return Lang.tr("Something went wrong ({0}). Try again.", String.valueOf(status)); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java new file mode 100644 index 00000000000..8a69e53026c --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.net; + +import com.codename1.io.ConnectionRequest; +import com.codename1.io.JSONParser; +import com.codename1.io.NetworkManager; +import com.codename1.io.RequestAuthorizer; +import com.codename1.io.Util; +import com.codename1.io.oidc.OidcClient; +import com.codename1.io.oidc.OidcConfiguration; +import com.codename1.io.oidc.OidcRequestAuthorizer; +import com.codename1.io.oidc.OidcTokens; +import com.codename1.io.oidc.PkceChallenge; +import com.codename1.io.oidc.SecureStorageTokenStore; +import com.codename1.io.oidc.TokenStore; +import com.codename1.ui.CN; +import com.codename1.util.SuccessCallback; +import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.api.UserDto; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Map; + +/// Who is signed in, and the tokens that say so. +/// +/// Signing in is OAuth's authorization-code flow with PKCE against the app's +/// own server, made without a browser: the app shows its own sign-in screen, +/// posts the password to the server for a short-lived session, exchanges the +/// session for an authorization code and the code for tokens. The password is +/// sent once, to the server that owns it, and is never stored; what is stored +/// is the token pair, in the device's secure storage. +/// +/// From then on [OidcRequestAuthorizer] does the work: it puts the access token +/// on every request under `/api`, renews it shortly before it expires, and +/// renews and retries when the server refuses one. When renewing fails -- the +/// refresh token ran out, or the account was suspended -- it says so, and the +/// app goes back to the welcome screen. +public final class Session { + private static final String STORE_PREFIX = "morsel"; + private static final String SESSION_COOKIE = "CN1SESSION"; + + private static String base; + private static OidcClient client; + private static OidcRequestAuthorizer authorizer; + private static TokenStore store; + private static UserDto user; + private static Runnable onSignedOut; + /// Counts the sessions: moved on whenever the server changes, an account + /// signs in, or the session ends. An answer to a request made in an + /// earlier session describes somebody who is no longer signed in here -- + /// another server's account, or the account before a sign-out on this + /// one -- and is dropped rather than installed for whoever is now. + private static int session; + + private Session() { + } + + /// Points the app at a server. Called at start-up, and again when the + /// server address is changed. + public static void start(String baseUrl) { + if (store == null) { + // Falls back to ordinary storage where the platform has no keystore + // to offer, which is the simulator. + store = new SecureStorageTokenStore().allowPlainStorageFallback(true); + } + if (base != null) { + NetworkManager.getInstance().setAuthorizer(base + "/api", null); + if (!base.equals(baseUrl)) { + // Changing the server ends the session with the old one, and + // its tokens go with it. + store.clear(storeKey(base)); + } + } + base = baseUrl; + user = null; + session++; + client = OidcClient.create(OidcConfiguration.newBuilder() + .issuer(base) + .authorizationEndpoint(base + "/oauth2/authorize") + .tokenEndpoint(base + "/oauth2/token") + .revocationEndpoint(base + "/oauth2/revoke") + .jwksUri(base + "/oauth2/jwks") + .build()) + .setClientId(AppConfig.CLIENT_ID) + .setRedirectUri(AppConfig.redirectUri()) + .setScopes("openid", "profile") + .setTokenStore(store) + .setStoreKey(storeKey(base)); + authorizer = new OidcRequestAuthorizer(client); + authorizer.install(base + "/api"); + // Not tied to this start(). A request to an earlier server still in + // flight when the app switched, answered 401 with its refresh refused, + // signs the new server out too: one more sign-in, after a server + // switch that already asked for one, and nothing of either account is + // exposed by it. + authorizer.addSignInRequiredListener((source, reason) -> { + user = null; + session++; + if (onSignedOut != null) { + onSignedOut.run(); + } + }); + Api.connect(base); + } + + /// Where the tokens issued by one server are kept: under a name made from + /// that server's address. + /// + /// One name for every server would hand the tokens of the last one to + /// whichever address is typed next -- the app would restore them and send + /// them, refresh token included, to a server that never issued them. + /// Every character that is not a letter or a digit is written as its code, + /// so two addresses never share a name and the name is one any platform's + /// storage accepts. + static String storeKey(String server) { + StringBuilder key = new StringBuilder(STORE_PREFIX).append('-'); + for (int iter = 0; iter < server.length(); iter++) { + char c = server.charAt(iter); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { + key.append(c); + } else { + key.append('_').append(Integer.toHexString(c)).append('_'); + } + } + return key.toString(); + } + + /// Keeps the tokens somewhere else. The tests keep them in memory, so one + /// run leaves nothing behind for the next. + public static void useStore(TokenStore tokens) { + store = tokens; + } + + /// What to do when the session ends without the user asking: go back to + /// the welcome screen. + public static void onSignedOut(Runnable handler) { + onSignedOut = handler; + } + + public static String serverUrl() { + return base; + } + + /// The signed-in user as the server last described them, or null. + public static UserDto user() { + return user; + } + + public static void setUser(UserDto described) { + user = described; + if (described != null && described.username() != null) { + // What the device keeps about one person is not the next one's. + Prefs.ownedBy(owner(described.username())); + } + } + + /// Who what the device keeps belongs to: the signed-in account, or null. + /// See [#owner(String)]. + public static String owner() { + return user == null || user.username() == null ? null : owner(user.username()); + } + + /// The device's name for the account `username` on the server in use. A + /// name is unique on one server only: the same e-mail address signed in + /// to two servers is two accounts, and what one left -- recent places, + /// a change it has not sent yet -- is not the other's to see or send. + public static String owner(String username) { + return storeKey(base == null ? "" : base) + "/" + username; + } + + /// Picks up the session a previous run left, if it is still good. + /// + /// @param signedIn there is a session, and [#user] is set + /// @param signedOut there is none + /// @param unreachable there is one, but the server did not answer + public static void restore(final Runnable signedIn, final Runnable signedOut, + final Runnable unreachable) { + // Loading the tokens can outlast the session it was asked in -- the + // server is changed while it reads -- and then neither answer is this + // session's to act on: see [#session]. + final int asked = session; + authorizer.load().ready(new SuccessCallback() { + @Override + public void onSucess(OidcTokens tokens) { + if (asked != session) { + return; + } + if (tokens == null || tokens.getAccessToken() == null) { + signedOut.run(); + return; + } + refreshUser(signedIn, (status, message) -> { + if (status == 0) { + unreachable.run(); + } else { + signedOut.run(); + } + }); + } + }).except(new SuccessCallback() { + @Override + public void onSucess(Throwable err) { + if (asked == session) { + signedOut.run(); + } + } + }); + } + + /// Asks the server who this is. The roles in the answer decide which modes + /// the app offers, so it is asked again whenever that could have changed. + public static void refreshUser(final Runnable done, Net.Failed failed) { + // An answer from a session that has since ended -- the server was + // changed while an approval poll waited, or the account signed out + // and another signed in -- describes that account, not this one; + // it is dropped. See [#session]. + final int asked = session; + Api.account().me(Net.to(described -> { + if (asked != session) { + return; + } + setUser(described); + done.run(); + }, (status, message) -> { + if (asked == session) { + failed.failed(status, message); + } + })); + } + + /// Signs in with an e-mail address and password. + public static void signIn(String email, String password, final Runnable done, + final Net.Failed failed) { + // The server can be changed while the sign-in is under way, and + // start() then puts a new authorizer in place. Tokens the old server + // issued are dropped rather than installed in the new one, which would + // send them -- the refresh token too -- to a server that never issued + // them. See [#session]. + final int asked = session; + authenticate(email, password, tokens -> { + if (asked != session) { + return; + } + session++; + authorizer.setTokens(tokens); + store.save(storeKey(base), tokens); + refreshUser(done, failed); + }, failed); + } + + /// Exchanges an e-mail address and password for tokens, and does nothing + /// with them: [#signIn] is this, and then keeping what comes back. + public static void authenticate(final String email, final String password, + final Net.Ok got, final Net.Failed failed) { + final PkceChallenge pkce = PkceChallenge.generate(); + // Any unguessable value does for the state; this is one. + final String state = PkceChallenge.generate().getVerifier(); + // In a browser the app sees neither the session cookie nor a redirect: + // the browser keeps the first and follows the second. So there the + // password's answer is its status, and the code is read from the page + // the redirect ends on -- see AppConfig.redirectUri(). + final boolean web = AppConfig.pageOrigin() != null; + final String redirect = AppConfig.redirectUri(); + + // The steps read `base` as they go rather than pinning it. Changing + // the server in the middle of a sign-in hands the next step to the new + // server, which can do nothing with what it gets: a session cookie and + // a code that only the first server honours, single use and good for + // minutes. A server the user chose is trusted with more than that -- + // the password in step 1 goes to it -- and the last check in + // signIn drops the tokens of a sign-in that outlived its server. + // 1. The password, for a session. + final Step login = new Step("POST", base + "/login"); + login.addArgument("username", email); + login.addArgument("password", password); + login.send(() -> { + if (login.status == 0) { + failed.failed(0, "Could not reach the server. Check your connection."); + return; + } + if (login.status == 429) { + failed.failed(429, "Too many attempts. Wait a minute and try again."); + return; + } + if (web ? login.status != 200 : login.session == null) { + failed.failed(401, "That e-mail and password do not match an account."); + return; + } + // 2. The session, for an authorization code. + final Step authorize = new Step("GET", base + "/oauth2/authorize"); + if (web) { + // Or a session that did not take is answered with a page. + authorize.addRequestHeader("Accept", "application/json"); + } else { + authorize.addRequestHeader("Cookie", login.session); + } + authorize.addArgument("response_type", "code"); + authorize.addArgument("client_id", AppConfig.CLIENT_ID); + authorize.addArgument("redirect_uri", redirect); + authorize.addArgument("scope", "openid profile"); + authorize.addArgument("state", state); + authorize.addArgument("code_challenge", pkce.getChallenge()); + authorize.addArgument("code_challenge_method", pkce.getMethod()); + authorize.send(() -> { + String code = web ? authorize.answer("code") + : parameter(authorize.location, "code"); + String returned = web ? authorize.answer("state") + : parameter(authorize.location, "state"); + // The state has to come back as it went: a code that arrives + // with another was not issued for this sign-in. + if (code == null || !state.equals(returned)) { + failed.failed(authorize.status, "Signing in failed. Try again."); + return; + } + // 3. The code, for tokens. + final Step token = new Step("POST", base + "/oauth2/token"); + token.addArgument("grant_type", "authorization_code"); + token.addArgument("code", code); + token.addArgument("redirect_uri", redirect); + token.addArgument("code_verifier", pkce.getVerifier()); + token.addArgument("client_id", AppConfig.CLIENT_ID); + token.send(() -> { + OidcTokens tokens = token.status == 200 ? token.tokens() : null; + if (tokens == null || tokens.getAccessToken() == null) { + failed.failed(token.status, "Signing in failed. Try again."); + return; + } + // 4. The session has done its work, and is ended rather + // than left to run out: in a browser it is a cookie that + // would go on signing this user in to whoever sat down + // next. Nothing waits on the answer; the tokens stand + // without it. + Step end = new Step("POST", base + "/logout"); + if (!web) { + end.addRequestHeader("Cookie", login.session); + } + end.send(() -> { }); + got.got(tokens); + }); + }); + }); + } + + /// Signs out: the refresh token is revoked at the server, so the session + /// is over there too and not only forgotten here. + public static void signOut(final Runnable done) { + OidcTokens tokens = authorizer.getTokens(); + if (tokens != null && tokens.getRefreshToken() != null) { + // Sent and not waited for. Signing out is something the user does + // to this phone, and it is done here whether or not the server + // answers: the tokens are gone from the device either way. A + // revocation that is lost leaves a refresh token only someone who + // already copied it off the phone could use, and it expires on + // its own; holding the sign-out until the network allows would + // trap the user signed in on a phone with no connection. + client.revoke(tokens.getRefreshToken()); + } + user = null; + session++; + authorizer.signOut().ready(new SuccessCallback() { + @Override + public void onSucess(Boolean cleared) { + done.run(); + } + }).except(new SuccessCallback() { + @Override + public void onSucess(Throwable err) { + done.run(); + } + }); + } + + /// A parameter of the query string of `url`, or null. + static String parameter(String url, String name) { + int query = url == null ? -1 : url.indexOf('?'); + if (query < 0) { + return null; + } + int at = query + 1; + while (at < url.length()) { + int end = url.indexOf('&', at); + if (end < 0) { + end = url.length(); + } + if (url.startsWith(name + "=", at)) { + return Util.decode(url.substring(at + name.length() + 1, end), "UTF-8", false); + } + at = end + 1; + } + return null; + } + + /// One request of the sign-in. It follows no redirect and keeps no cookie + /// jar, because the redirect and the cookie are the answers it is after; + /// and it carries no bearer token, because there is not one yet. + private static final class Step extends ConnectionRequest { + int status; + String location; + String session; + private byte[] body; + private Runnable then; + private boolean reported; + + Step(String method, String url) { + setUrl(url); + setHttpMethod(method); + setPost(!"GET".equals(method)); + setFollowRedirects(false); + setCookiesEnabled(false); + setReadResponseForErrors(true); + setDuplicateSupported(true); + setFailSilently(true); + setAuthorizer(RequestAuthorizer.NONE); + } + + void send(Runnable next) { + then = next; + NetworkManager.getInstance().addToQueue(this); + } + + /// A text member of the JSON this request was answered with, or null. + String answer(String name) { + if (status != 200 || body == null) { + return null; + } + try { + Map json = new JSONParser().parseJSON(new InputStreamReader( + new ByteArrayInputStream(body), "UTF-8")); + Object value = json == null ? null : json.get(name); + return value instanceof String ? (String) value : null; + } catch (IOException malformed) { + return null; + } catch (RuntimeException malformed) { + return null; + } + } + + OidcTokens tokens() { + try { + Map json = new JSONParser().parseJSON(new InputStreamReader( + new ByteArrayInputStream(body == null ? new byte[0] : body), "UTF-8")); + return OidcTokens.fromTokenResponse(json, null); + } catch (IOException malformed) { + return null; + } catch (RuntimeException malformed) { + return null; + } + } + + @Override + protected void readHeaders(Object connection) throws IOException { + location = getHeader(connection, "Location"); + String[] cookies = getHeaders(connection, "Set-Cookie"); + for (int iter = 0; cookies != null && iter < cookies.length; iter++) { + if (cookies[iter] != null && cookies[iter].startsWith(SESSION_COOKIE + "=")) { + int end = cookies[iter].indexOf(';'); + session = end < 0 ? cookies[iter] : cookies[iter].substring(0, end); + } + } + } + + @Override + protected void handleErrorResponseCode(int code, String message) { + status = code; + } + + @Override + protected void readResponse(InputStream input) throws IOException { + if (status == 0) { + status = getResponseCode(); + } + body = Util.readInputStream(input); + } + + @Override + protected void postResponse() { + if (status == 0) { + status = getResponseCode(); + } + report(); + } + + @Override + protected void handleException(Exception err) { + status = 0; + report(); + } + + private void report() { + if (!reported) { + reported = true; + CN.callSerially(then); + } + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java new file mode 100644 index 00000000000..a08d9e7cf45 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.notify; + +import com.codename1.push.PushClient; +import com.codename1.push.PushError; +import com.codename1.push.PushListener; +import com.codename1.push.PushMessage; +import com.codename1.push.PushSubscription; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.morsel.api.PushDeviceDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Nav; + +/// Push notifications: the delivery news that reaches a phone in a pocket. +/// +/// The phone registers with the platform's push service once, and the token +/// it gets is handed to the Morsel server for whoever is signed in. Signing +/// out takes it back, so the next person to use the phone is not told about +/// the last one's deliveries. The server decides what to send and when; see the +/// server's `DeliveryPushes`. +/// +/// Only on a phone (Android and iOS) and only with [AppConfig#PUSH_APP_KEY] +/// set. The simulator has no push service to register with, and a browser +/// build is told everything over the live channel while its page is open. +public final class Notifications { + private static final Net.Failed QUIET = (status, message) -> { }; + private static PushClient client; + private static PushSubscription subscription; + + private Notifications() { + } + + // tag::morsel-push-register[] + /// Asks the platform for a push token. Called every time the app comes to + /// the foreground; registering is idempotent, and the platform answers + /// with the token it already gave when nothing has changed. + public static void start() { + if (!available()) { + return; + } + if (client == null) { + client = PushClient.builder(AppConfig.PUSH_APP_KEY) + .listener(new Listener()) + .build(); + } + client.register(); + } + + /// Someone has signed in: the server learns where to reach them. + public static void signedIn() { + PushDeviceDto device = device(); + if (device != null && Session.user() != null) { + Api.account().registerPush(device, Net.to(saved -> { }, QUIET)); + } + } + + /// Someone is signing out: the server forgets this phone for them, and + /// then `then` runs. It runs whether or not the server could be reached; + /// signing out never waits on a notification. + public static void signingOut(Runnable then) { + PushDeviceDto device = device(); + if (device == null || Session.user() == null) { + then.run(); + return; + } + // A failure is not kept for retrying. The row it would remove names + // this phone's token, and the next sign-in on the phone takes that row + // over for whoever signs in, as the server keeps a phone on the + // account that signed in on it last. What is lost to an outage is + // the window until then, on the phone of the account that signed out + // -- noted in the implementor's guide among the hardening a + // production deployment adds, not worth a durable queue in a sample. + Api.account().unregisterPush(device, Net.to(gone -> then.run(), + (status, message) -> then.run())); + } + // end::morsel-push-register[] + + /// Whether this build can be sent notifications at all. + public static boolean available() { + if (AppConfig.PUSH_APP_KEY.length() == 0 || CN.isSimulator()) { + return false; + } + String platform = CN.getPlatformName(); + return "and".equals(platform) || "ios".equals(platform); + } + + private static PushDeviceDto device() { + if (subscription == null) { + return null; + } + return new PushDeviceDto(subscription.getTransportId(), subscription.getToken(), + CN.getPlatformName()); + } + + // tag::morsel-push-listener[] + /// Called on the event thread. A message is about a delivery: the app opens + /// the screen that shows it, which reads the delivery from the server. The + /// message's own words are for the notification shade, not for the app. + private static final class Listener implements PushListener { + @Override + public void onRegistration(PushSubscription registered) { + subscription = registered; + signedIn(); + } + + @Override + public void onMessage(PushMessage message) { + Object delivery = message.getData().get("deliveryId"); + Object role = message.getData().get("role"); + // A message with no title is a silent one: the Live Activity + // moving on, which the push client has already applied. + String title = message.getTitle(); + if (delivery != null && title != null && title.length() > 0 + && Session.user() != null) { + Nav.showDelivery(role == null ? Nav.CUSTOMER : String.valueOf(role)); + } + } + + @Override + public void onError(PushError error) { + // Nothing to tell the user: the app works without notifications, + // and asks again the next time it comes to the foreground. + System.out.println("Push registration failed: " + error.getCode() + " " + + error.getMessage()); + } + } + // end::morsel-push-listener[] +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java new file mode 100644 index 00000000000..554b4656f82 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.CardDto; +import com.codenameone.examples.morsel.api.PaymentSetupDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; + +/// Adds a card. +/// +/// How depends on who takes the payments, and the server says which when the +/// setup starts. A real provider has a page of its own for typing a card into: +/// the app opens it in the browser, never sees the number, and asks the server +/// what was saved when the user comes back. The simulated provider a +/// development server uses has no such page, so the card is typed here -- which +/// is also what lets the whole flow be tried, and tested, with no account +/// anywhere. +public final class CardForm { + /// The setup the browser was opened for, to finish when the app is back. + private static CardForm waiting; + /// What a hosted setup is finished with: the card went to the processor's + /// own page, and nothing of it is here to send. + private static final CardDto NO_CARD = new CardDto(null, 0, 0, null, null); + + private final Form form = Ui.form("Add a card", "Card"); + private final Form previous; + private final Runnable added; + private final Container page = Ui.page(); + private final SpanLabel error = Ui.text("", "WlError"); + private String setupId; + + private CardForm(Form previous, Runnable added) { + this.previous = previous; + this.added = added; + Ui.back(form, previous); + error.setName("error"); + form.add(BorderLayout.CENTER, page); + } + + /// @param added run once the card is in the wallet; it is what shows the + /// next screen + public static void show(Form previous, Runnable added) { + final CardForm card = new CardForm(previous, added); + card.form.show(); + Api.payments().startSetup(Net.to(card::begin, (status, message) -> { + Ui.fail(status, message); + previous.showBack(); + })); + } + + /// The app is in front again. If it left to add a card, see what came of it. + public static void resumed() { + if (waiting != null) { + waiting.finish(NO_CARD); + } + } + + private void begin(PaymentSetupDto setup) { + setupId = setup.id(); + if (setup.hosted()) { + hosted(setup.url()); + } else { + typed(); + } + Ui.refresh(form); + } + + private void hosted(final String url) { + page.add(Ui.banner(FontImage.MATERIAL_LOCK, "Your card goes straight to the bank", + "The payment provider's own page opens in your browser. Morsel never sees " + + "the number. Come back here when you have finished.", "WlBannerIcon")); + page.add(error); + page.add(Ui.primary("Open the secure page", "openHosted", e -> { + waiting = this; + CN.execute(url); + })); + page.add(Ui.secondary("I have finished", "finishHosted", e -> finish(NO_CARD))); + } + + private void typed() { + final Label number = Ui.plain(mask(""), "WlCardNumber"); + final Label holder = Ui.plain("", "WlCardSmall"); + final Label expires = Ui.plain("MM/YY", "WlCardSmall"); + final Label brand = Ui.plain("", "WlCardBrand"); + Container face = new Container(new BorderLayout()); + face.setUIID("WlCardFace"); + face.add(BorderLayout.NORTH, brand); + face.add(BorderLayout.CENTER, number); + Container foot = new Container(new BorderLayout()); + foot.add(BorderLayout.CENTER, holder); + foot.add(BorderLayout.EAST, expires); + face.add(BorderLayout.SOUTH, foot); + page.add(face); + + final TextField digits = Ui.field(page, "Card number", "cardNumber", + "4242 4242 4242 4242", TextArea.NUMERIC); + Container pair = new Container(new GridLayout(1, 2)); + Container left = new Container(BoxLayout.y()); + Container right = new Container(BoxLayout.y()); + left.setUIID("WlPairLeft"); + right.setUIID("WlPairRight"); + final TextField expiry = Ui.field(left, "Expires", "cardExpiry", "MM/YY", + TextArea.NUMERIC); + final TextField cvc = Ui.field(right, "Security code", "cardCvc", "123", + TextArea.NUMERIC); + pair.add(left).add(right); + page.add(pair); + final TextField name = Ui.field(page, "Name on the card", "cardHolder", ""); + // Each field is written back in the shape a card has it in. Writing it + // back is itself a change, so the listener stops when there is nothing + // left to correct. + digits.addDataChangedListener((type, index) -> { + String shaped = Cards.group(digits.getText()); + if (!shaped.equals(digits.getText())) { + digits.setText(shaped); + } + number.setText(mask(Cards.digits(shaped))); + brand.setText(Cards.brand(Cards.digits(shaped))); + face.revalidate(); + }); + expiry.addDataChangedListener((type, index) -> { + String shaped = Cards.expiry(expiry.getText()); + if (!shaped.equals(expiry.getText())) { + expiry.setText(shaped); + } + expires.setText(shaped.length() == 0 ? "MM/YY" : shaped); + face.revalidate(); + }); + name.addDataChangedListener((type, index) -> { + holder.setText(name.getText()); + face.revalidate(); + }); + page.add(Ui.text("This server takes no real payments. Use the test card " + + "4242 4242 4242 4242 with any date ahead and any three digits.", "WlNotice")); + page.add(error); + page.add(Ui.primary("Save card", "saveCard", e -> { + String wrong = Cards.check(digits.getText(), expiry.getText(), cvc.getText()); + if (wrong != null) { + error.setText(Lang.tr(wrong)); + Ui.refresh(form); + return; + } + CardDto card = new CardDto(Cards.digits(digits.getText()), + Cards.month(expiry.getText()), Cards.year(expiry.getText()), + cvc.getText().trim(), name.getText().trim()); + finish(card); + })); + } + + private void finish(CardDto card) { + waiting = null; + error.setText(""); + Api.payments().completeSetup(setupId, card, Net.to(method -> { + Telemetry.cardAdded(); + added.run(); + }, + (status, message) -> { + error.setText(message); + Ui.refresh(form); + })); + } + + /// The number as the card shows it: what is typed, and dots for the rest. + private static String mask(String digits) { + StringBuilder shown = new StringBuilder(); + for (int iter = 0; iter < 16; iter++) { + if (iter > 0 && iter % 4 == 0) { + shown.append(" "); + } + shown.append(iter < digits.length() ? digits.charAt(iter) : '*'); + } + return shown.toString(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java new file mode 100644 index 00000000000..e25886424c3 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +/// What a card number, an expiry date and a security code look like. +/// +/// Checking them here is a courtesy: it says "that is not a card number" before +/// a request is made. The decision is the provider's, which checks again and +/// is the only one who knows whether the card is real. +public final class Cards { + private Cards() { + } + + /// Only the digits of `text`. + public static String digits(String text) { + StringBuilder digits = new StringBuilder(); + for (int iter = 0; text != null && iter < text.length(); iter++) { + char letter = text.charAt(iter); + if (letter >= '0' && letter <= '9') { + digits.append(letter); + } + } + return digits.toString(); + } + + /// The digits of `text` in fours, as a card has them: `4242 4242`. + public static String group(String text) { + String digits = digits(text); + if (digits.length() > 19) { + digits = digits.substring(0, 19); + } + StringBuilder grouped = new StringBuilder(); + for (int iter = 0; iter < digits.length(); iter++) { + if (iter > 0 && iter % 4 == 0) { + grouped.append(' '); + } + grouped.append(digits.charAt(iter)); + } + return grouped.toString(); + } + + /// The digits of `text` as a month and a year: `0428` is `04/28`. + public static String expiry(String text) { + String digits = digits(text); + if (digits.length() > 4) { + digits = digits.substring(0, 4); + } + return digits.length() <= 2 ? digits : digits.substring(0, 2) + "/" + digits.substring(2); + } + + public static int month(String expiry) { + String digits = digits(expiry); + return digits.length() < 2 ? 0 : Integer.parseInt(digits.substring(0, 2)); + } + + /// The year in full: a card says `28` and means 2028. + public static int year(String expiry) { + String digits = digits(expiry); + return digits.length() < 4 ? 0 : 2000 + Integer.parseInt(digits.substring(2, 4)); + } + + /// The network a number belongs to, from how it starts; empty while that + /// cannot be told. + public static String brand(String digits) { + if (digits.startsWith("4")) { + return "Visa"; + } + if (digits.startsWith("34") || digits.startsWith("37")) { + return "Amex"; + } + if (digits.startsWith("6011") || digits.startsWith("65")) { + return "Discover"; + } + if (digits.length() >= 2) { + int two = Integer.parseInt(digits.substring(0, 2)); + if (two >= 51 && two <= 55 || two >= 22 && two <= 27) { + return "Mastercard"; + } + } + return ""; + } + + /// What is wrong with what was typed, in English, or null when nothing is. + public static String check(String number, String expiry, String cvc) { + String digits = digits(number); + if (digits.length() < 12 || digits.length() > 19 || !luhn(digits)) { + return "That is not a card number. Check the digits."; + } + int month = month(expiry); + if (month < 1 || month > 12 || year(expiry) == 0) { + return "Write the expiry date as month and year: 04/28."; + } + String code = cvc == null ? "" : cvc.trim(); + if (code.length() < 3 || code.length() > 4 || digits(code).length() != code.length()) { + return "The security code is the three or four digits on the card."; + } + return null; + } + + /// The check digit every card number ends in, which catches a mistyped one. + public static boolean luhn(String digits) { + int sum = 0; + boolean twice = false; + for (int iter = digits.length() - 1; iter >= 0; iter--) { + int digit = digits.charAt(iter) - '0'; + if (twice) { + digit *= 2; + if (digit > 9) { + digit -= 9; + } + } + sum += digit; + twice = !twice; + } + return digits.length() > 0 && sum % 10 == 0; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java new file mode 100644 index 00000000000..c9287f98681 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.ReasonDto; +import com.codenameone.examples.morsel.api.ReceiptDto; +import com.codenameone.examples.morsel.api.TipDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; + +/// What a delivery cost, how it was paid, and -- for the customer -- the chance to +/// leave a tip. +/// +/// Every number here is the server's, in cents, and is shown as it came. The +/// one sum the app does is the tip a percentage stands for, which it then sends +/// as an amount: what "15%" means is decided where the customer can see it. +public final class ReceiptForm { + private static final int[] PERCENTS = {10, 15, 20}; + + private ReceiptForm() { + } + + /// Fetches the customer's receipt for `deliveryId` and shows it. + public static void open(final Form previous, String deliveryId) { + Api.payments().receipt(deliveryId, Net.to(receipt -> show(previous, receipt, false), + Ui::fail)); + } + + /// @param admin true for an admin, who may return the money and may not tip + public static void show(final Form previous, final ReceiptDto receipt, final boolean admin) { + final Form form = Ui.form("Receipt", "Receipt"); + Ui.back(form, previous); + Container page = page(receipt, admin); + actions(form, previous, page, receipt, admin); + form.add(BorderLayout.CENTER, page); + form.show(); + } + + /// What a receipt says, without what can be done about it: for a caller + /// with its own place to show one, which the admin console is. + /// + /// @param admin true to name the customer, which an admin needs and a customer does not + public static Container page(final ReceiptDto receipt, final boolean admin) { + Container page = Ui.page(); + final String currency = receipt.currency(); + + Label total = Ui.plain(Ui.money(receipt.total(), currency), "WlAmount"); + total.setName("total"); + page.add(total); + Label status = Ui.badge(status(receipt.status()), badge(receipt.status())); + status.setName("paymentStatus"); + page.add(FlowLayout.encloseCenter(status)); + page.add(FlowLayout.encloseCenter(Ui.plain( + receipt.paidAt() > 0 ? Ui.when(receipt.paidAt()) : "", "WlMuted"))); + + Ui.section(page, "Trip"); + Container trip = new Container(BoxLayout.y()); + trip.add(Ui.row(FontImage.MATERIAL_TRIP_ORIGIN, receipt.pickupName(), null, null)); + trip.add(Ui.row(FontImage.MATERIAL_PLACE, receipt.dropoffName(), + Ui.distance(receipt.distanceMeters()) + Ui.DOT + + Ui.minutes(receipt.durationSeconds()), null)); + if (receipt.courierName() != null && receipt.courierName().length() > 0) { + trip.add(Ui.row(FontImage.MATERIAL_PERSON, receipt.courierName(), receipt.vehicle(), + null)); + } + if (admin && receipt.customerUsername() != null && receipt.customerUsername().length() > 0) { + trip.add(Ui.row(FontImage.MATERIAL_HAIL, receipt.customerUsername(), "Customer", null)); + } + page.add(trip); + + Ui.section(page, "Fare"); + Container fare = new Container(BoxLayout.y()); + fare.setUIID("WlPanel"); + fare.add(Ui.fact("Base fare", Ui.money(receipt.baseFare(), currency))); + fare.add(Ui.fact("Distance", Ui.money(receipt.distanceFare(), currency))); + fare.add(Ui.fact("Time", Ui.money(receipt.timeFare(), currency))); + fare.add(Ui.fact("Service fee", Ui.money(receipt.serviceFee(), currency))); + if (receipt.discount() > 0) { + fare.add(Ui.fact("Discount", "-" + Ui.money(receipt.discount(), currency))); + } + if (receipt.tip() > 0) { + Container tipped = Ui.fact("Tip", Ui.money(receipt.tip(), currency)); + tipped.getComponentAt(1).setName("tipPaid"); + fare.add(tipped); + } + fare.add(Ui.fact("Total", Ui.money(receipt.total(), currency), "WlFactTotal")); + fare.add(Ui.fact("Paid with", "cash".equals(receipt.status()) ? Lang.tr("Cash") + : receipt.methodLabel())); + page.add(fare); + return page; + } + + private static void actions(final Form form, final Form previous, Container page, + final ReceiptDto receipt, final boolean admin) { + final String currency = receipt.currency(); + boolean settled = "paid".equals(receipt.status()) || "cash".equals(receipt.status()); + if (!admin && settled && receipt.tip() == 0) { + tip(form, previous, page, receipt); + } + if (admin && "paid".equals(receipt.status())) { + page.add(Ui.danger("Refund this delivery", "refund", e -> Ui.reason(form, + "Refund", Lang.tr("{0} goes back to the card it came from.", + Ui.money(receipt.total(), currency)), "Refund", + why -> { + ReasonDto reason = new ReasonDto(why); + Api.admin().refundDelivery(receipt.deliveryId(), reason, Net.to(refunded -> { + Telemetry.deliveryRefunded("receipt"); + show(previous, refunded, true); + }, Ui::fail)); + }))); + } + } + + /// The tip: three shares of the fare to tap, or an amount to type. + private static void tip(final Form form, final Form previous, Container page, + final ReceiptDto receipt) { + Ui.section(page, "Add a tip"); + page.add(Ui.text(receipt.courierName() == null || receipt.courierName().length() == 0 + ? Lang.tr("All of it goes to your courier.") + : Lang.tr("All of it goes to {0}.", receipt.courierName()), "WlMuted")); + final long fare = receipt.baseFare() + receipt.distanceFare() + receipt.timeFare(); + String[] labels = new String[PERCENTS.length]; + String[] values = new String[PERCENTS.length]; + for (int iter = 0; iter < PERCENTS.length; iter++) { + long cents = Math.max(100L, Math.round(fare * PERCENTS[iter] / 100d)); + values[iter] = String.valueOf(cents); + labels[iter] = PERCENTS[iter] + "% " + Ui.money(cents, receipt.currency()); + } + final TextField custom = new TextField("", Lang.tr("Another amount"), 8, TextArea.DECIMAL); + custom.setUIID("WlField"); + custom.getHintLabel().setUIID("WlFieldHint"); + custom.setName("tipCustom"); + final long[] chosen = {0}; + Container presets = Ui.choice("tip", labels, values, "", value -> { + chosen[0] = Long.parseLong(value); + custom.setText(""); + }); + final String[] amounts = values; + // The amounts are numbers; nothing in them is a word to look up. + for (int iter = 0; iter < presets.getComponentCount(); iter++) { + ((Button) presets.getComponentAt(iter)).setShouldLocalize(false); + } + page.add(presets); + page.add(custom); + page.add(Ui.primary("Send tip", "sendTip", e -> { + long cents = custom.getText().trim().length() > 0 ? cents(custom.getText()) + : chosen[0]; + if (cents <= 0) { + Ui.say("Choose an amount for the tip"); + return; + } + TipDto tip = new TipDto(cents); + // Which of the shares it was, when it was one of them. + int share = 0; + for (int iter = 0; iter < amounts.length; iter++) { + if (custom.getText().trim().length() == 0 + && amounts[iter].equals(String.valueOf(cents))) { + share = PERCENTS[iter]; + } + } + final int reported = share; + Api.payments().tip(receipt.deliveryId(), tip, Net.to(tipped -> { + Telemetry.tipped("receipt", reported); + Ui.say("Thank you. Your tip was sent."); + show(previous, tipped, false); + }, Ui::fail)); + })); + } + + /// An amount typed with or without a decimal point, in cents; 0 for + /// anything that is not an amount. + static long cents(String typed) { + String text = typed.trim().replace(',', '.'); + int point = text.indexOf('.'); + String whole = point < 0 ? text : text.substring(0, point); + String part = point < 0 ? "" : text.substring(point + 1); + if (part.length() > 2) { + part = part.substring(0, 2); + } + while (part.length() < 2) { + part += "0"; + } + if (Cards.digits(whole).length() != whole.length() + || Cards.digits(part).length() != part.length() || whole.length() > 6) { + return 0; + } + return (whole.length() == 0 ? 0 : Long.parseLong(whole)) * 100 + Long.parseLong(part); + } + + /// A payment's state, in words. + public static String status(String status) { + return "paid".equals(status) ? "Paid" : "cash".equals(status) ? "Paid in cash" + : "refunded".equals(status) ? "Refunded" + : "refund_pending".equals(status) ? "Refund pending" + : "refund_failed".equals(status) ? "Refund failed" + : "failed".equals(status) ? "Payment failed" : "Payment pending"; + } + + public static String badge(String status) { + return "paid".equals(status) || "cash".equals(status) ? "WlBadgeGood" + : "failed".equals(status) || "refund_failed".equals(status) ? "WlBadgeBad" + : "WlBadgeWarn"; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java new file mode 100644 index 00000000000..5c921b52997 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.pay; + +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.PaymentMethodDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Ui; +import java.util.List; + +/// The ways the customer can pay: their cards, and cash, which is always there. +/// +/// The list is the server's. A card is added by the payment provider and what +/// comes back is a brand and four digits; the number itself is never kept by +/// this app, and by the server only long enough to hand it over. +/// +/// The same screen answers "which of these, for this delivery?": opened with +/// [#pick], a tap on a method chooses it and goes back. +public final class WalletForm { + public interface Picked { + void picked(PaymentMethodDto method); + } + + private final Form form; + private final Form previous; + private final Picked picked; + private final String chosen; + private final Container list = new Container(BoxLayout.y()); + + private WalletForm(Form previous, String chosen, Picked picked) { + this.previous = previous; + this.picked = picked; + this.chosen = chosen; + form = Ui.form(picked == null ? "Wallet" : "Pay with", "Wallet"); + Ui.back(form, previous); + Container page = Ui.page(); + page.add(Ui.text(picked == null + ? "Tap a card to make it the one that pays unless you say otherwise." + : "Choose how to pay for this delivery.", "WlMuted")); + list.setName("methods"); + page.add(list); + page.add(Ui.secondary("Add a card", "addCard", + e -> CardForm.show(form, () -> { + form.showBack(); + load(); + }))); + form.add(BorderLayout.CENTER, page); + } + + /// The wallet, to look after. + public static void show(Form previous) { + WalletForm wallet = new WalletForm(previous, null, null); + wallet.form.show(); + wallet.load(); + } + + /// The wallet, to choose from for one delivery. + /// + /// @param chosen the id of the method the delivery has now + public static void pick(Form previous, String chosen, Picked picked) { + WalletForm wallet = new WalletForm(previous, chosen, picked); + wallet.form.show(); + wallet.load(); + } + + /// The name of a method, as a customer would say it. + public static String label(PaymentMethodDto method) { + if (method == null || "cash".equals(method.kind())) { + return Lang.tr("Cash"); + } + return method.label() == null || method.label().length() == 0 + ? method.brand() + " " + method.last4() : method.label(); + } + + private void load() { + Api.payments().methods(Net.to(this::fill, Ui::fail)); + } + + private void fill(List methods) { + list.removeAll(); + for (int iter = 0; methods != null && iter < methods.size(); iter++) { + final PaymentMethodDto method = methods.get(iter); + boolean cash = "cash".equals(method.kind()); + boolean marked = picked == null ? method.isDefault() : method.id().equals(chosen); + Container row = new Container(new BorderLayout()); + row.setUIID(marked ? "WlOptionPicked" : "WlOption"); + Label icon = new Label("", "WlOptionIcon"); + Ui.icon(icon, cash ? FontImage.MATERIAL_PAYMENTS + : FontImage.MATERIAL_CREDIT_CARD, 4f); + row.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(icon)); + Container lines = new Container(BoxLayout.y()); + lines.add(Ui.plain(label(method), "WlRowTitle")); + lines.add(Ui.plain(cash ? Lang.tr("Pay the courier directly") + : Lang.tr("Expires {0}", expiry(method)), "WlRowDetail")); + row.add(BorderLayout.CENTER, lines); + Container end = new Container(new FlowLayout(Label.RIGHT, Label.CENTER)); + if (method.isDefault()) { + end.add(Ui.badge("Default", "WlBadgeGood")); + } + row.add(BorderLayout.EAST, end); + Ui.tap(row, cash ? "method-cash" : "method-" + iter, e -> choose(method)); + list.add(row); + if (!cash && picked == null) { + // Its own line under the card, so that a tap meant for the card + // is never a tap on Remove. + Button remove = Ui.link("Remove this card", "remove-" + iter, e -> + Api.payments().remove(method.id(), Net.to(gone -> { + Telemetry.cardRemoved(); + if (method.id().equals(Prefs.paymentMethod())) { + Prefs.setPaymentMethod(Prefs.PAYMENT_CASH); + } + load(); + }, Ui::fail))); + list.add(FlowLayout.encloseRight(remove)); + } + } + Ui.refresh(form); + } + + private void choose(final PaymentMethodDto method) { + if (picked != null) { + previous.showBack(); + picked.picked(method); + return; + } + Api.payments().makeDefault(method.id(), Net.to(made -> { + Prefs.setPaymentMethod(method.id()); + load(); + }, Ui::fail)); + } + + private static String expiry(PaymentMethodDto method) { + int year = method.expYear() % 100; + return (method.expMonth() < 10 ? "0" : "") + method.expMonth() + "/" + + (year < 10 ? "0" : "") + year; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java new file mode 100644 index 00000000000..58f27795723 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Graphics; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.geom.Rectangle; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.plaf.Style; + +/// The sheet that rises from the bottom of a map. +/// +/// It has two parts. [#peek()] is what is always there: the headline and the +/// one thing to do next. [#more()] is the rest -- the details, the lesser +/// actions -- and is shown when the sheet is pulled up and put away when it is +/// pushed down, so the map keeps most of the screen until the user asks for +/// the sheet. The handle at the top is what is pulled; a tap on it does the +/// same. A sheet with nothing in `more` has no handle. +/// +/// A screen fills it the same way each time: [#reset()], add to the two parts, +/// [#ready()]. +public final class BottomSheet extends Container { + private final Grip grip = new Grip(); + private final Container peek = new Container(BoxLayout.y()); + private final Container more = new Container(BoxLayout.y()); + private boolean expanded; + + public BottomSheet() { + super(BoxLayout.y()); + setUIID("WlSheet"); + setName("panel"); + setSafeArea(true); + // A touch on the sheet is the sheet's, and does not reach through to + // drag the map under it. + setGrabsPointerEvents(true); + add(grip).add(peek).add(more); + more.setHidden(true); + } + + /// The padding under the sheet's content as the theme has it, and as it + /// was last made here. + private int styledUnder = -1; + private int madeUnder = -1; + + /// Keeps the content clear of what the device draws over the bottom of + /// the screen, by making the sheet's own padding at least that deep. + /// + /// Marking the sheet a safe area is not enough. A safe area is padded + /// while it is laid out, from where it then is; asked how tall it wants + /// to be before that, it answers without the padding, is given that + /// height, and then spends some of it on the padding after all -- which + /// came out of the last thing in the sheet. Padding that is there both + /// times cannot disagree with itself. The mark stays for the sides, which + /// do not change how tall the sheet is. + private void clearOfTheDevice() { + Style style = getUnselectedStyle(); + int now = style.getPaddingBottom(); + if (now != madeUnder) { + // Styled again since: a change of theme, or of name. + styledUnder = now; + } + Rectangle safe = Display.getInstance().getDisplaySafeArea(new Rectangle()); + int covered = safe.getHeight() <= 0 ? 0 + : Math.max(0, CN.getDisplayHeight() - safe.getY() - safe.getHeight()); + int wanted = Math.max(styledUnder, covered); + if (wanted != now) { + getAllStyles().setPaddingUnitBottom(Style.UNIT_TYPE_PIXELS); + getAllStyles().setPaddingBottom(wanted); + } + madeUnder = wanted; + } + + @Override + protected Dimension calcPreferredSize() { + clearOfTheDevice(); + return super.calcPreferredSize(); + } + + @Override + public void layoutContainer() { + clearOfTheDevice(); + super.layoutContainer(); + } + + public Container peek() { + return peek; + } + + public Container more() { + return more; + } + + /// Empties the sheet, for the next thing it will show. + public void reset() { + peek.removeAll(); + more.removeAll(); + } + + /// The sheet has been filled: lays it out. + /// + /// @param open whether to show `more` straight away + public void ready(boolean open) { + boolean extra = more.getComponentCount() > 0; + grip.handle(extra); + expanded = extra && open; + more.setHidden(!expanded); + Container parent = getParent(); + if (parent != null) { + parent.revalidate(); + } + } + + public boolean isExpanded() { + return expanded; + } + + public void setExpanded(boolean wanted) { + if (wanted == expanded || more.getComponentCount() == 0) { + return; + } + expanded = wanted; + more.setHidden(!wanted); + Container parent = getParent(); + if (parent != null) { + parent.animateLayout(180); + } + } + + /// The handle: a short bar, pulled up or down, or tapped. + private final class Grip extends Component { + private int pressedY; + private boolean pulled; + /// Whether there is anything to pull the sheet up for. With nothing, + /// the handle is not drawn and still takes its room: without it the + /// first line of the sheet sat against the sheet's top edge. + private boolean handle; + + void handle(boolean wanted) { + if (handle != wanted) { + handle = wanted; + setShouldCalcPreferredSize(true); + } + } + + Grip() { + setUIID("WlGrip"); + setGrabsPointerEvents(true); + } + + @Override + protected Dimension calcPreferredSize() { + Style style = getStyle(); + return new Dimension(CN.convertToPixels(12f), handle + ? CN.convertToPixels(1.2f) + style.getVerticalPadding() + : CN.convertToPixels(2.6f)); + } + + @Override + public void paint(Graphics g) { + if (!handle) { + return; + } + Style style = getStyle(); + int width = CN.convertToPixels(10f); + int height = CN.convertToPixels(1.2f); + g.setColor(style.getFgColor()); + g.setAntiAliased(true); + g.fillRoundRect(getX() + (getWidth() - width) / 2, + getY() + (getHeight() - height) / 2, width, height, height, height); + } + + @Override + public void pointerPressed(int x, int y) { + pressedY = y; + pulled = false; + } + + @Override + public void pointerDragged(int x, int y) { + // Far enough to be meant, and once for each pull. + if (!pulled && Math.abs(y - pressedY) > CN.convertToPixels(2f)) { + pulled = true; + setExpanded(y < pressedY); + } + } + + @Override + public void pointerReleased(int x, int y) { + if (!pulled) { + setExpanded(!expanded); + } + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java new file mode 100644 index 00000000000..e4aea2cb7f3 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Graphics; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; + +/// A row of bars: deliveries a day, takings a day, requests by the hour. +/// +/// Drawn here, in a few lines, because that is all a bar chart is and because +/// it then takes its colours from `theme.css` like everything else: the bars +/// are the `WlChart` style's colour, the highest of them `WlChartPeak`'s, and +/// the lines behind them `WlChartGrid`'s. Nothing here names a colour. +public final class Chart extends Component { + private static final float HEIGHT_MM = 24f; + + private final double[] values; + + private Chart(double[] values) { + this.values = values; + setUIID("WlChart"); + setFocusable(false); + } + + /// The chart, with what its two ends are written under it. + /// + /// @param name what a test finds the chart by + /// @param from the caption under the first bar, already translated + /// @param to the caption under the last + public static Container of(String name, double[] values, String from, String to) { + Chart chart = new Chart(values); + chart.setName(name); + Container box = new Container(new BorderLayout()); + box.add(BorderLayout.CENTER, chart); + Container axis = new Container(new BorderLayout()); + axis.add(BorderLayout.WEST, Ui.plain(from, "WlChartAxis")); + axis.add(BorderLayout.EAST, Ui.plain(to, "WlChartAxis")); + box.add(BorderLayout.SOUTH, axis); + return box; + } + + /// How many bars there are, for a test. + public int bars() { + return values.length; + } + + @Override + protected Dimension calcPreferredSize() { + Style style = getStyle(); + return new Dimension(CN.convertToPixels(40f), + CN.convertToPixels(HEIGHT_MM) + style.getVerticalPadding()); + } + + @Override + public void paint(Graphics g) { + Style style = getStyle(); + int left = getX() + style.getPaddingLeftNoRTL(); + int top = getY() + style.getPaddingTop(); + int width = getWidth() - style.getHorizontalPadding(); + int height = getHeight() - style.getVerticalPadding(); + if (width <= 0 || height <= 0) { + return; + } + UIManager styles = getUIManager(); + g.setColor(styles.getComponentStyle("WlChartGrid").getFgColor()); + // Three lines to read the heights against: the foot, half, the top. + for (int line = 0; line <= 2; line++) { + int y = top + (height - 1) * line / 2; + g.drawLine(left, y, left + width, y); + } + int count = values.length; + if (count == 0) { + return; + } + double most = 0; + int peak = -1; + for (int iter = 0; iter < count; iter++) { + if (values[iter] > most) { + most = values[iter]; + peak = iter; + } + } + if (most <= 0) { + return; + } + // The space is shared out in whole pixels, so every bar is as wide as + // every other and what is left over goes to the two ends. + int slot = Math.max(1, width / count); + int gap = Math.max(1, slot / 4); + int bar = Math.max(1, slot - gap); + int start = left + (width - slot * count + gap) / 2; + int round = Math.min(bar, CN.convertToPixels(1.2f)); + int normal = style.getFgColor(); + int highest = styles.getComponentStyle("WlChartPeak").getFgColor(); + boolean rtl = isRTL(); + g.setAntiAliased(true); + for (int iter = 0; iter < count; iter++) { + int tall = (int) Math.round(values[iter] / most * (height - 2)); + if (values[iter] > 0 && tall < round) { + tall = round; + } + if (tall <= 0) { + continue; + } + // Time runs the way the language is read. + int at = rtl ? count - 1 - iter : iter; + g.setColor(iter == peak ? highest : normal); + g.fillRoundRect(start + at * slot, top + height - tall, bar, tall, round, round); + } + } + + /// A share of something, as a bar part of the way along a track: the bar + /// is the `WlMeter` style's colour and the track a faint wash of it. + public static final class Meter extends Component { + private static final int TRACK_ALPHA = 36; + private final double share; + + /// @param share from 0 to 1 + public Meter(double share) { + this.share = share < 0 ? 0 : share > 1 ? 1 : share; + setUIID("WlMeter"); + setFocusable(false); + } + + @Override + protected Dimension calcPreferredSize() { + Style style = getStyle(); + return new Dimension(CN.convertToPixels(20f), + CN.convertToPixels(1.6f) + style.getVerticalPadding()); + } + + @Override + public void paint(Graphics g) { + Style style = getStyle(); + int left = getX() + style.getPaddingLeftNoRTL(); + int top = getY() + style.getPaddingTop(); + int width = getWidth() - style.getHorizontalPadding(); + int height = getHeight() - style.getVerticalPadding(); + if (width <= 0 || height <= 0) { + return; + } + g.setAntiAliased(true); + // The track is the bar's own colour, faint: a background colour + // would be painted square behind the rounded ends. + int alpha = g.getAlpha(); + g.setColor(style.getFgColor()); + g.setAlpha(TRACK_ALPHA); + g.fillRoundRect(left, top, width, height, height, height); + g.setAlpha(alpha); + int filled = (int) Math.round(width * share); + if (filled > 0) { + filled = Math.max(filled, height); + g.setColor(style.getFgColor()); + g.fillRoundRect(isRTL() ? left + width - filled : left, top, filled, height, + height, height); + } + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java new file mode 100644 index 00000000000..4423e46a911 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.morsel.Telemetry; + +/// Asks, once on a device, whether the app may report how it is used. +/// +/// It is shown after the first sign-in and before anything is counted, and it +/// has no way out but an answer: nothing is reported while there is none, so +/// leaving the question open would be a no that is asked again at every +/// start. Either answer can be changed in Settings, which is said here. +public final class ConsentForm { + private ConsentForm() { + } + + /// @param then where to go once there is an answer + public static void show(final Runnable then) { + Form form = Ui.form("Usage statistics", "Consent"); + Container page = Ui.page(); + Container banner = Ui.banner(FontImage.MATERIAL_INSIGHTS, "Help improve Morsel", + "Morsel can count which screens are opened and which features are used, " + + "so that we can see what works and fix what does not.", "WlBannerIcon"); + // The page has its own top padding, and the banner's on top of it + // pushed the last line of the promise below under the answers. + banner.getAllStyles().setPaddingTop(0); + page.add(banner); + page.add(Ui.row(FontImage.MATERIAL_VISIBILITY_OFF, "No personal details", + "Never your name, e-mail, phone number, places or card details", null)); + // The two answers stay in view however much there is to read. + Container answers = new Container(BoxLayout.y()); + answers.setUIID("WlPage"); + answers.setSafeArea(true); + answers.add(Ui.primary("Share usage statistics", "consentYes", e -> answer(true, then))); + answers.add(Ui.secondary("Not now", "consentNo", e -> answer(false, then))); + answers.add(Ui.text("Turn it on or off at any time in Settings", "WlMuted")); + form.add(BorderLayout.SOUTH, answers); + form.add(BorderLayout.CENTER, page); + form.show(); + } + + private static void answer(boolean yes, Runnable then) { + Telemetry.allow(yes); + then.run(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java new file mode 100644 index 00000000000..6f186db2044 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.morsel.api.DeliveryDto; +import com.codenameone.examples.morsel.net.Net; + +import java.util.List; + +/// A list of deliveries: a customer's, a courier's, or for an admin everyone's. Which +/// it is depends only on the call it is given -- and on the server, which +/// answers each of the three with the deliveries that caller may see. +public final class DeliveryListForm { + private DeliveryListForm() { + } + + /// @param people whether to name the customer and courier on each row, which + /// is for the admin's list + public static void show(Form previous, String title, Net.Call> call, + final boolean people) { + show(previous, title, call, people, null); + } + + /// What a tap on a delivery does, given the list to come back to. + public interface Opened { + void opened(Form list, DeliveryDto delivery); + } + + public static void show(Form previous, String title, Net.Call> call, + final boolean people, final Opened opened) { + final Form form = Ui.form(title, "Deliveries"); + Ui.back(form, previous); + final Container list = Ui.list("deliveries"); + form.add(BorderLayout.CENTER, list); + form.show(); + final Runnable[] load = new Runnable[1]; + load[0] = () -> Net.send(call, deliveries -> { + list.removeAll(); + if (deliveries == null || deliveries.isEmpty()) { + list.add(Ui.empty(FontImage.MATERIAL_HISTORY, "No deliveries yet", + "A delivery shows up here as soon as it is asked for.")); + } + for (int iter = 0; deliveries != null && iter < deliveries.size(); iter++) { + final DeliveryDto delivery = deliveries.get(iter); + String detail = Ui.when(delivery.requestedAt()); + if (people) { + detail += " - " + (delivery.courierName() == null + || delivery.courierName().length() == 0 ? delivery.customerName() + : Lang.tr("{0} with {1}", delivery.customerName(), delivery.courierName())); + } + Container value = new Container(BoxLayout.y()); + value.add(Ui.plain(Ui.money(delivery.total() > 0 ? delivery.total() : delivery.fareCents(), + delivery.currency()), "WlRowValue")); + value.add(Ui.badge(Ui.state(delivery.state()), Ui.stateBadge(delivery.state()))); + Container row = Ui.row(FontImage.MATERIAL_PLACE, delivery.dropoffAddress(), detail, + value); + if (opened == null) { + row.setName("delivery-" + iter); + } else { + Ui.tap(row, "delivery-" + iter, e -> opened.opened(form, delivery)); + } + list.add(row); + } + Ui.refresh(form); + }, Ui.failed(list, () -> load[0].run())); + Ui.pull(list, load[0]); + load[0].run(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java new file mode 100644 index 00000000000..5798c454611 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.l10n.L10NManager; +import com.codename1.ui.plaf.UIManager; +import com.codename1.ui.util.Resources; +import com.codenameone.examples.morsel.Prefs; +import java.util.HashMap; +import java.util.Map; + +/// The language the app speaks. +/// +/// The translations are the `Bundle_.properties` files in +/// `src/main/l10n`, which the build packs into the theme. One of them at a +/// time is installed as the `UIManager`'s bundle, and from then on every +/// label, button, title and hint looks its text up there as it is set: the +/// English text in the source is the key, and a text with no entry is shown as +/// it is. So a screen written in English is translated without saying so. +/// +/// What a component cannot do by itself is a sentence with something put into +/// it -- "Pick up Riley". Those go through [#tr(String, Object...)], with the +/// whole sentence as the key and `{0}` where the name goes, because where it +/// goes is the translator's decision. +/// +/// To add a string: write it in English in the code, and add a line for it to +/// each of the four translated files. To add a language: add its file, and its +/// code to [#CODES]. +public final class Lang { + /// The languages there is a translation for. English is the source. + public static final String[] CODES = {"en", "es", "fr", "de", "he"}; + + private static final String BUNDLE = "Bundle"; + /// The entry each file names its own language in, in that language. + private static final String NAME_KEY = "language.name"; + + private Lang() { + } + + /// The language in use: the one chosen in Settings, else the device's when + /// there is a translation for it, else English. + public static String current() { + String chosen = known(Prefs.language()); + if (chosen != null) { + return chosen; + } + String device = known(L10NManager.getInstance().getLanguage()); + return device == null ? CODES[0] : device; + } + + /// Installs the translation for [#current()]. A screen built after this is + /// in the new language, and laid out right to left when the language is + /// written that way; one already built keeps the words it was built with. + public static void apply() { + Map bundle = new HashMap(); + // Always said, one way or the other: a bundle that is silent about the + // direction leaves the one before it in force. + bundle.put("@rtl", "false"); + Map translated = table(current()); + if (translated != null) { + bundle.putAll(translated); + } + UIManager.getInstance().setBundle(bundle); + } + + /// What `code`'s language calls itself. + public static String name(String code) { + Map translated = table(code); + String name = translated == null ? null : translated.get(NAME_KEY); + return name == null || name.length() == 0 ? code : name; + } + + public static String tr(String text) { + return UIManager.getInstance().localize(text, text); + } + + /// Translates `pattern` and puts `values` where it says `{0}`, `{1}`... + public static String tr(String pattern, Object... values) { + String text = tr(pattern); + for (int iter = 0; iter < values.length; iter++) { + String mark = "{" + iter + "}"; + String value = String.valueOf(values[iter]); + int at = text.indexOf(mark); + while (at >= 0) { + text = text.substring(0, at) + value + text.substring(at + mark.length()); + at = text.indexOf(mark, at + value.length()); + } + } + return text; + } + + /// The code among [#CODES] that `language` is, or null. A device reports + /// its language with or without a country (`es`, `es_MX`) and Hebrew under + /// either of two codes; this is compared as the ASCII it is, since folding + /// the case would follow the very language being worked out. + private static String known(String language) { + if (language == null || language.length() < 2) { + return null; + } + if (language.regionMatches(true, 0, "iw", 0, 2)) { + return "he"; + } + for (String code : CODES) { + if (language.regionMatches(true, 0, code, 0, 2)) { + return code; + } + } + return null; + } + + private static Map table(String code) { + Resources theme = Resources.getGlobalResources(); + return theme == null ? null : theme.getL10N(BUNDLE, code); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java new file mode 100644 index 00000000000..6305eccc554 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; +import java.util.HashMap; +import java.util.Map; + +/// How much room there is, and whose look the app wears. +/// +/// The app is laid out for a phone. Two things change that, and both are +/// asked here so that no screen works them out for itself: +/// +/// - [#wide()]: the window is as wide as a tablet on its side, a desktop +/// window or a browser. The admin console then shows its navigation and a +/// list with its detail side by side; every other screen keeps its column +/// and stops it growing past [#column()]. +/// - [#desktopLook()]: the platform's own desktop theme is installed -- macOS +/// Aqua, Windows Fluent or GNOME Adwaita, chosen by the `nativeTheme=native` +/// build hint. The admin console then dresses its controls in that theme's +/// buttons, fields and lists with [#dress(Container)], and keeps the app's +/// own styles only for what a desktop has no control for: the charts and +/// the numbers. +/// +/// Both are read when a screen is built. A window that is resized across the +/// line, or a device that is turned, rebuilds the screen: see +/// [#onChange(Form, Runnable)]. +public final class Layouts { + /// A window at least this wide, in millimetres, is laid out side by side. + /// A phone on its side is narrower; a tablet on its side is not. + private static final float WIDE_MM = 165f; + /// The widest a column of text and controls is left to grow. + private static final float COLUMN_MM = 150f; + + /// The app's own style names, and the name each goes by under a desktop + /// theme. Those are declared at the end of `theme.css`, each deriving from + /// the native control it stands for. + private static final Map DESKTOP = new HashMap(); + + static { + String[] dressed = {"WlForm", "WlPage", "WlList", "WlPrimary", "WlSecondary", "WlDanger", + "WlLink", "WlField", "WlFieldHint", "WlRow", "WlRowPicked", "WlRowIcon", "WlRowTitle", + "WlRowDetail", "WlRowValue", "WlSection", "WlHeading", "WlText", "WlMuted", "WlLabel", + "WlTitle", "WlError", "WlNotice", "WlPanel", "WlFact", "WlFactLabel", "WlFactValue", + "WlFactTotal", "WlSegments", "WlSegment", "WlTile", "WlStat", "WlStatValue", + "WlStatLabel", "WlChartValue", "WlChartAxis", "WlAmount", "WlBannerIcon", + "WlBannerTitle", "WlBannerText", "WlAlert", "WlSide", "WlSideItem", "WlSidePicked", + "WlSideLabel", "WlSideName", "WlSideDetail", "WlPaneBar", "WlPaneTitle", "WlMaster", + "WlBarButton", "WlHomeBar"}; + for (int iter = 0; iter < dressed.length; iter++) { + DESKTOP.put(dressed[iter], "WlDesk" + dressed[iter].substring(2)); + } + } + + private Layouts() { + } + + /// Whether there is room to lay a screen out side by side. + public static boolean wide() { + return CN.getDisplayWidth() >= CN.convertToPixels(WIDE_MM); + } + + /// The widest a single column of content is drawn, in pixels. + public static int column() { + return CN.convertToPixels(COLUMN_MM); + } + + /// How many columns of `millimetres` each fit in `width` pixels: at least + /// one, and no more than `most`. + public static int columns(int width, float millimetres, int most) { + int fit = width / Math.max(1, CN.convertToPixels(millimetres)); + return Math.max(1, Math.min(most, fit)); + } + + /// Whether the platform's desktop theme is the one under the app's own. + /// + /// The three desktop themes are the only ones installed on a desktop that + /// mark themselves as a platform's defaults; a desktop build that kept the + /// legacy theme, a phone and a tablet all answer false and keep the app's + /// look everywhere. + public static boolean desktopLook() { + return CN.isDesktop() + && UIManager.getInstance().isThemeConstant("nativeThemeDefaultsBool", false); + } + + /// Dresses everything under `root` in the desktop theme, when there is one: + /// each component styled by the app takes the twin of its style that + /// derives from the platform's control, and whatever scrolls shows the + /// platform's scrollbar. Does nothing anywhere else. + /// + /// It is done to a finished tree rather than where each piece is made, so + /// that the screens are written once, for a phone, and the console alone + /// decides what a desktop makes of them. + public static void dress(Container root) { + if (!desktopLook()) { + return; + } + dressed(root); + } + + private static void dressed(Component component) { + String twin = DESKTOP.get(component.getUIID()); + if (twin != null) { + component.setUIID(twin); + if (component instanceof Label) { + redraw((Label) component); + } + } + if (component instanceof SpanLabel) { + SpanLabel span = (SpanLabel) component; + String text = DESKTOP.get(span.getTextUIID()); + if (text != null) { + span.setTextUIID(text); + } + return; + } + if (component instanceof Container) { + Container holder = (Container) component; + if (holder.isScrollableY()) { + holder.setScrollVisible(true); + } + // A row that is one button is still a container of labels. + for (int iter = 0; iter < holder.getComponentCount(); iter++) { + dressed(holder.getComponentAt(iter)); + } + } + } + + /// Draws the icon of `label` again in the colours of the style it now has: + /// an icon is an image, made once from the style the label had then. + private static void redraw(Label label) { + if (!(label.getIcon() instanceof FontImage)) { + return; + } + FontImage icon = (FontImage) label.getIcon(); + String glyph = icon.getText(); + float pixels = icon.getFont() == null ? 0 : icon.getFont().getPixelSize(); + if (glyph == null || glyph.length() != 1 || pixels <= 0) { + return; + } + FontImage.setMaterialIcon(label, glyph.charAt(0), + pixels * 100f / CN.convertToPixels(100f)); + } + + /// Keeps the content of `page` in a column no wider than [#column()], in + /// the middle of however much room there is: called by the page as it is + /// laid out, with the padding its style gave it. + /// + /// @param margin the side padding of the page's own style, in pixels + static void centre(Container page, int margin) { + int side = Math.max(margin, (page.getWidth() - column()) / 2); + Style style = page.getAllStyles(); + Style now = page.getUnselectedStyle(); + if (now.getPaddingLeftNoRTL() != side || now.getPaddingRightNoRTL() != side) { + style.setPaddingUnitLeft(Style.UNIT_TYPE_PIXELS); + style.setPaddingUnitRight(Style.UNIT_TYPE_PIXELS); + style.setPaddingLeft(side); + style.setPaddingRight(side); + } + } + + /// Calls `rebuild` when `form` stops being wide or starts to be, which is + /// a window dragged across the line or a tablet turned. The screen was + /// built for the room there was; it is built again for the room there is. + public static void onChange(final Form form, final Runnable rebuild) { + final boolean[] was = {wide()}; + form.addSizeChangedListener(e -> { + boolean now = wide(); + if (now != was[0]) { + was[0] = now; + // After the event: the form is in the middle of being resized. + CN.callSerially(rebuild); + } + }); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java new file mode 100644 index 00000000000..8bd00995ebb --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.plaf.UIManager; +import com.codenameone.examples.morsel.Prefs; + +/// Light or dark. +/// +/// `theme.css` has both: every rule has its dark twin in the block at the end +/// of the file. Which of the two a component gets is decided when its style is +/// resolved, so a screen built after [#apply()] is in the new look and one +/// already built is not -- the caller rebuilds what is on screen. +public final class Look { + private Look() { + } + + /// Makes the look the one chosen in Settings: light, dark, or whichever + /// the device is in. + public static void apply() { + String theme = Prefs.theme(); + CN.setDarkMode(Prefs.THEME_DARK.equals(theme) ? Boolean.TRUE + : Prefs.THEME_LIGHT.equals(theme) ? Boolean.FALSE : null); + if (CN.isDesktop()) { + // On a desktop the platform's theme takes the colours and the font + // the user chose for the whole machine -- the accent among them. + // It reaches only what that theme styles, which is what the admin + // console wears; see Layouts. + UIManager.getInstance().setUseNativeColors(true); + UIManager.getInstance().setUseNativeFonts(true); + } + UIManager.getInstance().refreshTheme(); + } + + /// Whether the app is drawn dark right now. A platform that cannot say is + /// taken to be light. + public static boolean dark() { + Boolean dark = CN.isDarkMode(); + return dark != null && dark.booleanValue(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java new file mode 100644 index 00000000000..94e3f26c427 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.net.Session; + +/// The menu of a home screen: who is signed in, which of the app's three modes +/// this is, the places that mode has, and the way to another mode. +/// +/// Each mode fills in its own places with [#item]; nothing here knows what a +/// customer's or a courier's are. What is the same for all of them is the top -- +/// the person and the mode -- and the bottom: the other modes the account's +/// roles open, Settings, and signing out. An account with one role is offered +/// no other mode, because it has none. +/// +/// It slides in over the screen as a layer of the screen's own, not as the +/// title bar's side menu: the home screens have no title bar, and on a desktop +/// the title bar's menu is handed to the window's. +public final class Menu { + private final Form form; + private final String mode; + private final Container layer = new Container(new BorderLayout()); + private final Container places = new Container(BoxLayout.y()); + private final Container panel; + + /// @param mode the mode `form` is the home of: `Nav.CUSTOMER` and the others + public Menu(Form form, String mode) { + this.form = form; + this.mode = mode; + panel = new Container(new BorderLayout()) { + @Override + protected Dimension calcPreferredSize() { + // Most of a phone's width, and no wider than a menu wants to + // be on a tablet or a desktop. + Dimension size = super.calcPreferredSize(); + size.setWidth(Math.min(CN.getDisplayWidth() * 5 / 6, CN.convertToPixels(78f))); + return size; + } + }; + panel.setUIID("WlMenu"); + panel.setSafeArea(true); + panel.setGrabsPointerEvents(true); + panel.setName("menu"); + build(); + // What is left of the screen beside the menu: dimmed, and a tap on it + // puts the menu away. + Button dim = new Button("", "WlScrim"); + dim.setName("menu-dim"); + dim.addActionListener(e -> close()); + layer.add(BorderLayout.WEST, panel); + layer.add(BorderLayout.CENTER, dim); + layer.setHidden(true); + layer.setVisible(false); + } + + /// Adds the menu to the screen, closed, as its top layer. Call it after + /// the screen's own content has been added. + public void install() { + form.add(layer); + } + + /// The round button that opens the menu, for the screen to place. + public Button button() { + return Ui.round(FontImage.MATERIAL_MENU, "menu-open", e -> open()); + } + + /// The button that opens the menu, for a screen with a bar and no map. + public Button flatButton() { + return Ui.flat(FontImage.MATERIAL_MENU, "menu-open", e -> open()); + } + + /// Adds one of the mode's own places. + /// + /// @param name the button's name is `menu-` and this + public Menu item(char icon, String text, String name, final Runnable go) { + places.add(entry(icon, text, "menu-" + name, go)); + return this; + } + + public void open() { + if (!layer.isHidden()) { + return; + } + layer.setHidden(false); + layer.setVisible(true); + form.getContentPane().revalidate(); + // Laid out where it belongs; started from off the edge it comes from, + // which is the right-hand one in a right-to-left language. + panel.setX(panel.isRTL() ? layer.getWidth() : -panel.getWidth()); + layer.animateLayout(200); + } + + public void close() { + if (layer.isHidden()) { + return; + } + layer.setHidden(true); + layer.setVisible(false); + form.getContentPane().revalidate(); + } + + private void build() { + UserDto user = Session.user(); + Container top = new Container(BoxLayout.y()); + top.setUIID("WlMenuTop"); + Container who = new Container(new BorderLayout()); + Container names = new Container(BoxLayout.y()); + names.add(Ui.label(user.displayName(), "WlMenuName")); + names.add(Ui.label(user.username(), "WlMenuDetail")); + who.add(BorderLayout.WEST, Ui.avatar(user.displayName())); + who.add(BorderLayout.CENTER, names); + top.add(who); + // Which of the three apps this is, said in words at the top of each. + top.add(FlowLayout.encloseIn(Ui.badge(Nav.ADMIN.equals(mode) ? "Admin mode" + : Nav.COURIER.equals(mode) ? "Courier mode" : "Customer mode", "WlModeBadge"))); + + Container list = new Container(BoxLayout.y()); + list.setScrollableY(true); + list.setScrollVisible(false); + list.add(places); + // The other modes, for an account whose roles open more than one. + // Everyone may delivery; onboarding and administering are granted. + if (user.courier() || user.admin() || !Nav.CUSTOMER.equals(mode)) { + list.add(Ui.label("Switch to", "WlMenuLabel")); + if (!Nav.CUSTOMER.equals(mode)) { + list.add(other(FontImage.MATERIAL_HAIL, "Riding", Nav.CUSTOMER)); + } + if (user.courier() && !Nav.COURIER.equals(mode)) { + list.add(other(FontImage.MATERIAL_DIRECTIONS_CAR, "Onboarding", Nav.COURIER)); + } + if (user.admin() && !Nav.ADMIN.equals(mode)) { + list.add(other(FontImage.MATERIAL_INSIGHTS, "Administration", Nav.ADMIN)); + } + } + + Container bottom = new Container(BoxLayout.y()); + bottom.setUIID("WlMenuBottom"); + bottom.add(entry(FontImage.MATERIAL_SETTINGS, "Settings", "menu-settings", + () -> SettingsForm.show(form))); + bottom.add(entry(FontImage.MATERIAL_LOGOUT, "Sign out", "menu-signOut", Nav::signOut)); + + panel.add(BorderLayout.NORTH, top); + panel.add(BorderLayout.CENTER, list); + panel.add(BorderLayout.SOUTH, bottom); + } + + private Button other(char icon, String text, final String to) { + return entry(icon, text, "switch-" + to, () -> Nav.switchTo(to)); + } + + private Button entry(char icon, String text, String name, final Runnable go) { + Button entry = new Button(text, "WlMenuItem"); + entry.setName(name); + Ui.icon(entry, icon, 3.8f); + entry.addActionListener(e -> { + close(); + go.run(); + }); + return entry; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java new file mode 100644 index 00000000000..62336b158af --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.admin.AdminForm; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.courier.ApplyForm; +import com.codenameone.examples.morsel.courier.CourierForm; +import com.codenameone.examples.morsel.courier.Shift; +import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.net.Account; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.notify.Notifications; +import com.codenameone.examples.morsel.customer.CustomerForm; + +/// Decides which screen the app is on. +/// +/// The app is three apps in one -- for riding, for onboarding and for +/// administering -- and which of them a user may enter is the server's +/// decision: it is the roles on their account, as `/api/me` reports them. Each +/// has a home screen of its own, with its own menu, and the user is in one of +/// them at a time. Hiding a mode here is a courtesy and not the protection; the +/// server refuses a courier's or an admin's call from anyone without the role, +/// whatever the app shows. +public final class Nav { + public static final String CUSTOMER = "customer"; + public static final String COURIER = "courier"; + public static final String ADMIN = "admin"; + + /// The home screen of a mode. + public interface Home { + /// Fills the screen in and starts it talking to the server. Called + /// once, before the screen is first shown. + void start(); + + Form form(); + } + + /// The home screen last built, and the mode it is the home of. + private static Form homeForm; + private static String homeMode = ""; + + private Nav() { + } + + /// Whether `form` is still the home screen, for an answer that arrives + /// after the user has gone to another mode or signed out and should then + /// change nothing. + public static boolean isHome(Form form) { + return form != null && form == homeForm; + } + + /// The first screen: wherever the stored session leads. + public static void launch() { + Session.onSignedOut(() -> { + Shift.end(); + LiveChannel.stop(); + Telemetry.signedOut("expired"); + WelcomeForm.show(); + }); + Form starting = Ui.form("", "Starting"); + starting.getToolbar().hideToolbar(); + starting.show(); + Session.restore(Nav::enter, WelcomeForm::show, Nav::unreachable); + } + + /// Someone has just signed in, or been found still signed in: their + /// account's settings are fetched, applied, and then their home is shown in + /// the language and the colours they chose. + public static void enter() { + // Where this phone is to be sent notifications, now that there is + // someone to send them to. + Notifications.signedIn(); + Account.load(() -> { + Look.apply(); + Lang.apply(); + if (Telemetry.asked()) { + home(); + } else { + // Once on a device, before the first screen that would be + // counted: whether it may be. + ConsentForm.show(Nav::home); + } + }); + } + + /// Shows the home screen of the mode the user is in. + public static void home() { + Form home = build(); + if (home != null) { + home.show(); + } + } + + /// Builds the home screen of the mode the user is in, without showing it: + /// for a caller that is about to show something on top of it. Null, and + /// the welcome screen showing, when nobody is signed in. + public static Form build() { + UserDto user = Session.user(); + if (user == null) { + WelcomeForm.show(); + return null; + } + LiveChannel.start(); + String mode = mode(user); + Telemetry.role(mode); + // Onboarding is for the approved. Anyone else who chooses it gets the + // way there: the application, and what has become of it. + if (!COURIER.equals(mode)) { + // Out of the courier's side of the app is off line as far as the + // phone is concerned: nothing goes on reporting where it is. + Shift.leave(); + } + Home home = ADMIN.equals(mode) ? new AdminForm() + : !COURIER.equals(mode) ? new CustomerForm() + : user.courier() ? new CourierForm() : new ApplyForm(); + home.start(); + homeForm = home.form(); + homeMode = mode; + return homeForm; + } + + /// The mode to open for `user`. + /// + /// The one they chose last, while their account still allows it. With no + /// choice made, the one their account is for: an admin administers, a + /// courier drives, and everyone else deliveries. A choice is remembered for the + /// account that made it, so the next person to sign in on the same device + /// starts where their own account does. + public static String mode(UserDto user) { + String saved = AppConfig.mode(Session.owner(user.username())); + if (ADMIN.equals(saved) && user.admin()) { + return ADMIN; + } + // Not only for a courier: someone who is applying to be one is in the + // courier's side of the app too, on the screen that says how it stands. + if (COURIER.equals(saved)) { + return COURIER; + } + if (CUSTOMER.equals(saved)) { + return CUSTOMER; + } + return user.admin() ? ADMIN : user.courier() ? COURIER : CUSTOMER; + } + + /// The user chose `mode` from the menu. + public static void switchTo(String mode) { + UserDto user = Session.user(); + AppConfig.setMode(mode, user == null ? "" : Session.owner(user.username())); + home(); + } + + /// A notification about a delivery was opened: the home that shows that delivery, + /// as the customer or as the courier the notification was for. Nothing + /// changes when that home is already on screen; it hears of the change + /// over the live channel too. + public static void showDelivery(String role) { + UserDto user = Session.user(); + if (user == null) { + return; + } + String wanted = COURIER.equals(role) && user.courier() ? COURIER : CUSTOMER; + if (wanted.equals(homeMode) && homeForm != null && CN.getCurrentForm() == homeForm) { + return; + } + AppConfig.setMode(wanted, Session.owner(user.username())); + home(); + } + + public static void signOut() { + leave("user", true); + } + + /// The account was closed: signed out, and what the device kept for the + /// usage reports forgotten with it. + public static void deleted() { + // Closing the account already made the server forget its devices. + leave("deleted", false); + Telemetry.forget(); + // Nobody is coming back for where that account went. + Prefs.ownedBy(""); + } + + private static void leave(final String reason, boolean forgetDevice) { + Account.flush(); + Shift.leave(); + LiveChannel.stop(); + homeForm = null; + homeMode = ""; + // The server forgets this phone for the account first, while there is + // still a session to ask with. + Runnable out = () -> { + Telemetry.signedOut(reason); + Session.signOut(WelcomeForm::show); + }; + // The delivery's Live Activity is left to the system. Signing out does not + // end the delivery, which goes on for the customer who signed out, and the + // system removes an activity on its own within hours. Ending it here + // would hide a delivery that is still happening. + if (forgetDevice) { + Notifications.signingOut(out); + } else { + out.run(); + } + } + + /// Points the app at another server, which ends the session with this one. + public static void changeServer(final String url) { + Runnable switchOver = () -> { + AppConfig.setServerUrl(url); + Session.start(AppConfig.serverUrl()); + launch(); + }; + if (AppConfig.sameServer(url, Session.serverUrl()) || Session.user() == null) { + LiveChannel.stop(); + switchOver.run(); + return; + } + // Signed in: sign out the way the Sign out button does, against the + // server still in use, before pointing anywhere else. Session.start + // only forgets the tokens on this phone, so the old server would keep + // a live refresh token and this phone's push registration, and go on + // notifying it about that account's deliveries. + Account.flush(); + Shift.leave(); + LiveChannel.stop(); + homeForm = null; + homeMode = ""; + Notifications.signingOut(() -> { + Telemetry.signedOut("server"); + Session.signOut(switchOver); + }); + } + + /// There is a session, and no server to use it with. + private static void unreachable() { + Form form = Ui.form("Morsel", "Unreachable"); + Container page = Ui.page(); + page.add(Ui.label("No connection", "WlTitle")); + page.add(Ui.text(Lang.tr("The server at {0} did not answer.", Session.serverUrl()), + "WlText")); + page.add(Ui.primary("Try again", "retry", e -> launch())); + if (AppConfig.serverSwitchable()) { + page.add(Ui.secondary("Server address", "server", e -> ServerForm.show(form))); + } + form.add(BorderLayout.CENTER, page); + form.show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java new file mode 100644 index 00000000000..0c6c13eae24 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.capture.Capture; +import com.codename1.io.FileSystemStorage; +import com.codename1.io.Log; +import com.codename1.io.Util; +import com.codename1.ui.CN; +import com.codename1.ui.Image; +import com.codename1.ui.events.ActionEvent; +import com.codename1.ui.util.ImageIO; +import com.codenameone.examples.morsel.Telemetry; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/// A picture from the camera or from the gallery, made small enough to send. +/// +/// A phone's camera makes pictures of several megabytes, and the server takes +/// documents of up to one and a half. So whatever comes back is scaled to fit +/// in [#LONGEST_SIDE] and written as a JPEG, which leaves a page of a licence +/// perfectly readable in a fraction of that. +public final class Photos { + /// Where the pictures come from, for a test that has no camera. + public interface Source { + /// @param camera true when the camera was asked for, false the gallery + /// @return the picture, or null as when the user backs out + Image take(boolean camera); + } + + public interface Taken { + /// @param jpeg the picture, encoded + /// @param picture the same picture, to show + void taken(byte[] jpeg, Image picture); + } + + private static final int LONGEST_SIDE = 1280; + private static final float QUALITY = 0.8f; + + private static Source source; + + private Photos() { + } + + /// Takes every picture from `from` from now on, and opens neither the + /// camera nor the gallery. The tests use it to hand in a picture of their + /// own. + public static void useSource(Source from) { + source = from; + } + + public static void camera(Taken taken) { + if (source != null) { + deliver(source.take(true), taken); + return; + } + Capture.capturePhoto(e -> picked(e, taken)); + } + + public static void gallery(Taken taken) { + if (source != null) { + deliver(source.take(false), taken); + return; + } + CN.openGallery(e -> picked(e, taken), CN.GALLERY_IMAGE); + } + + /// The camera and the gallery both answer with the path of a file, or + /// with nothing when the user backed out. + private static void picked(ActionEvent event, Taken taken) { + Object path = event == null ? null : event.getSource(); + if (!(path instanceof String)) { + return; + } + InputStream in = null; + try { + in = FileSystemStorage.getInstance().openInputStream((String) path); + deliver(Image.createImage(in), taken); + } catch (IOException failed) { + Log.e(failed); + Telemetry.error(failed); + Ui.say("That picture could not be read"); + } finally { + Util.cleanup(in); + } + } + + private static void deliver(Image picture, Taken taken) { + if (picture == null) { + return; + } + Image small = picture; + if (picture.getWidth() > LONGEST_SIDE || picture.getHeight() > LONGEST_SIDE) { + small = picture.scaledSmallerRatio(LONGEST_SIDE, LONGEST_SIDE); + } + ImageIO encoder = ImageIO.getImageIO(); + if (encoder == null) { + Ui.say("This device cannot prepare a picture to send"); + return; + } + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + encoder.save(small, out, ImageIO.FORMAT_JPEG, QUALITY); + taken.taken(out.toByteArray(), small); + } catch (IOException failed) { + Log.e(failed); + Telemetry.error(failed); + Ui.say("That picture could not be read"); + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java new file mode 100644 index 00000000000..946ffeb9290 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.api.PlaceDto; +import com.codenameone.examples.morsel.api.ProfileDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.net.Account; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.customer.PlaceSearchForm; + +/// Who the user is to the service: their name, who to tell if something goes +/// wrong, and the two places they go most. +public final class ProfileForm { + private static final String[] GENDERS = {"unspecified", "female", "male", "nonbinary"}; + private static final String[] GENDER_NAMES = {"Rather not say", "Woman", "Man", + "Non-binary"}; + + private ProfileForm() { + } + + /// @param saved where to go once the profile is saved: a screen that shows + /// the name is built again there + public static void show(final Form previous, final Runnable saved) { + final Form form = Ui.form("Profile", "Profile"); + Ui.back(form, previous); + UserDto user = Session.user(); + ProfileDto profile = Account.profile(); + Container page = Ui.page(); + + Ui.section(page, "About you"); + final TextField name = Ui.field(page, "Name", "name", "What couriers will call you"); + name.setText(profile == null ? user.displayName() : profile.name()); + final String[] gender = {profile == null || profile.gender() == null + || profile.gender().length() == 0 ? GENDERS[0] : profile.gender()}; + page.add(Ui.label("Gender", "WlLabel")); + // Two rows of two: four of these do not fit side by side in German. + page.add(Ui.choice("gender", GENDER_NAMES, GENDERS, gender[0], + value -> gender[0] = value, 2)); + page.add(Ui.text("Customers who ask for a woman at the wheel are offered to couriers " + + "who say they are one.", "WlMuted")); + + Ui.section(page, "Emergency contact"); + final TextField contact = Ui.field(page, "Their name", "emergencyName", ""); + contact.setText(Prefs.emergencyContactName()); + final TextField number = Ui.field(page, "Their phone number", "emergencyPhone", + "+1 555 0100", TextArea.PHONENUMBER); + number.setText(Prefs.emergencyContactPhone()); + + Ui.section(page, "Saved places"); + Container places = SettingsForm.group(); + places.add(place(form, previous, saved, FontImage.MATERIAL_HOME, "Home", "home", + Prefs.home())); + places.add(place(form, previous, saved, FontImage.MATERIAL_WORK, "Work", "work", + Prefs.work())); + page.add(places); + + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + final Button save = Ui.primary("Save", "submit", null); + save.addActionListener(e -> { + save.setEnabled(false); + error.setText(""); + Prefs.setEmergencyContactName(contact.getText()); + Prefs.setEmergencyContactPhone(number.getText()); + Account.saveProfile(name.getText().trim(), gender[0], saved, (status, message) -> { + save.setEnabled(true); + error.setText(message); + Ui.refresh(form); + }); + }); + page.add(error).add(save); + form.add(BorderLayout.CENTER, page); + form.show(); + } + + private static Container place(final Form form, final Form previous, final Runnable saved, + char icon, String title, final String which, PlaceDto place) { + return Ui.tap(Ui.row(icon, title, place == null ? Lang.tr("Not set") : place.name(), + SettingsForm.chevron()), "place-" + which, + e -> PlaceSearchForm.show(form, Locator.here(), picked -> { + if ("home".equals(which)) { + Prefs.setHome(picked); + } else { + Prefs.setWork(picked); + } + show(previous, saved); + })); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java new file mode 100644 index 00000000000..cfeac069004 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.plaf.Style; + +/// "Still looking": a dot that sends out rings, the way a search does on a +/// map. It runs for as long as it is on screen. +/// +/// The colour is the `WlPulse` style's text colour. +public final class Pulse extends Component { + /// How long one ring takes from the dot to the edge. + private static final int PERIOD_MILLIS = 1800; + private static final int RINGS = 3; + + public Pulse() { + setUIID("WlPulse"); + } + + @Override + protected void initComponent() { + super.initComponent(); + Form form = getComponentForm(); + if (form != null) { + form.registerAnimated(this); + } + } + + @Override + protected void deinitialize() { + Form form = getComponentForm(); + if (form != null) { + form.deregisterAnimated(this); + } + super.deinitialize(); + } + + @Override + public boolean animate() { + return true; + } + + @Override + protected Dimension calcPreferredSize() { + Style style = getStyle(); + int side = CN.convertToPixels(13f); + return new Dimension(side + style.getHorizontalPadding(), + side + style.getVerticalPadding()); + } + + @Override + public void paint(Graphics g) { + Style style = getStyle(); + int side = Math.min(getWidth() - style.getHorizontalPadding(), + getHeight() - style.getVerticalPadding()); + if (side <= 0) { + return; + } + int centerX = getX() + getWidth() / 2; + int centerY = getY() + getHeight() / 2; + int dot = Math.max(4, CN.convertToPixels(2f)); + int alpha = g.getAlpha(); + g.setAntiAliased(true); + g.setColor(style.getFgColor()); + long now = System.currentTimeMillis() % PERIOD_MILLIS; + for (int ring = 0; ring < RINGS; ring++) { + // Each ring is a third of the way behind the one before it, and + // fades as it grows. + double part = ((now + (long) ring * PERIOD_MILLIS / RINGS) % PERIOD_MILLIS) + / (double) PERIOD_MILLIS; + int size = dot + (int) ((side - dot) * part); + g.setAlpha((int) (90 * (1d - part))); + g.fillArc(centerX - size / 2, centerY - size / 2, size, size, 0, 360); + } + g.setAlpha(alpha); + g.fillArc(centerX - dot / 2, centerY - dot / 2, dot, dot, 0, 360); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java new file mode 100644 index 00000000000..9e5b0fffccf --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.RegisterDto; +import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; + +/// Creates an account, signs in with it and goes on to verify the phone. +/// +/// Everyone who registers can delivery. Someone who wants to drive says so here +/// and is taken on to the application; the courier role comes when an admin +/// approves it. Nobody registers as an admin either: that role is granted by +/// another admin, from the admin screens. +public final class RegisterForm { + private RegisterForm() { + } + + public static void show(Form previous) { + final Form form = Ui.form("Create an account", "Register"); + Ui.back(form, previous); + Container page = Ui.page(); + final TextField name = Ui.field(page, "Name", "name", "What couriers will call you"); + final TextField email = Ui.field(page, "E-mail", "email", "you@example.com", + TextArea.EMAILADDR); + final TextField phone = Ui.field(page, "Mobile number", "phone", "+1 555 010 0000", + TextArea.PHONENUMBER); + final TextField password = Ui.field(page, "Password", "password", + "At least 8 characters", TextArea.PASSWORD); + + // Riding is open to everyone the moment they have an account. Onboarding + // is asked for here and granted later, by an admin who has read the + // application this choice leads to. + final String[] wants = {Nav.CUSTOMER}; + page.add(Ui.label("I want to", "WlLabel")); + page.add(Ui.choice("want", new String[] {"Delivery", "Drive"}, + new String[] {Nav.CUSTOMER, Nav.COURIER}, wants[0], value -> wants[0] = value)); + page.add(Ui.text("Couriers apply after signing up: a few details about you and your " + + "car, and five photos. You can delivery in the meantime.", "WlMuted")); + + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + final Button create = Ui.primary("Create account", "submit", null); + create.addActionListener(e -> { + final boolean onboarding = Nav.COURIER.equals(wants[0]); + final RegisterDto request = new RegisterDto(email.getText().trim(), password.getText(), + name.getText().trim(), phone.getText().trim(), false, null, null, onboarding, + null); + final Net.Failed failed = (status, message) -> { + create.setEnabled(true); + error.setText(message); + Ui.refresh(form); + }; + create.setEnabled(false); + error.setText(""); + // The server checks every field and says what is wrong with the + // first that fails, so nothing is validated twice. + Api.account().register(request, Net.to(user -> Session.signIn(request.email(), + request.password(), () -> { + // Someone who came to drive starts on the courier's side, + // which for them is the application. + AppConfig.setMode(onboarding ? Nav.COURIER : Nav.CUSTOMER, Session.owner(request.email())); + Telemetry.registered(onboarding); + VerifyPhoneForm.show(Nav::enter); + }, failed), failed)); + }); + page.add(error).add(create); + form.add(BorderLayout.CENTER, page); + form.show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java new file mode 100644 index 00000000000..f24476b45d7 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Font; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; + +/// A countdown drawn as a ring that empties, with the seconds left inside it. +/// +/// The colours are two styles' text colours: `WlRing` for the part of the +/// ring still left and `WlRingTrack` for the part used up. Neither has a +/// background, so whatever the ring sits on shows through its middle. +public final class Ring extends Component { + private long endsAt; + private int total = 1; + private int shown = -1; + + public Ring() { + setUIID("WlRing"); + } + + /// Starts counting down from `seconds`, of a count that began at `of`. + public void start(int seconds, int of) { + total = Math.max(1, Math.max(seconds, of)); + endsAt = System.currentTimeMillis() + seconds * 1000L; + repaint(); + } + + /// The whole seconds left, never less than none. + public int secondsLeft() { + long left = endsAt - System.currentTimeMillis(); + return left <= 0 ? 0 : (int) ((left + 999) / 1000); + } + + @Override + protected void initComponent() { + super.initComponent(); + Form form = getComponentForm(); + if (form != null) { + form.registerAnimated(this); + } + } + + @Override + protected void deinitialize() { + Form form = getComponentForm(); + if (form != null) { + form.deregisterAnimated(this); + } + super.deinitialize(); + } + + /// Repaints while the count is running, which is what makes the ring + /// shrink smoothly and not a second at a time. + @Override + public boolean animate() { + boolean running = endsAt > System.currentTimeMillis() || shown != 0; + shown = secondsLeft(); + return running; + } + + @Override + protected Dimension calcPreferredSize() { + Style style = getStyle(); + int side = CN.convertToPixels(10.5f); + return new Dimension(side + style.getHorizontalPadding(), + side + style.getVerticalPadding()); + } + + @Override + public void paint(Graphics g) { + Style style = getStyle(); + int track = UIManager.getInstance().getComponentStyle("WlRingTrack").getFgColor(); + int side = Math.min(getWidth() - style.getHorizontalPadding(), + getHeight() - style.getVerticalPadding()); + if (side <= 0) { + return; + } + int centerX = getX() + getWidth() / 2; + int centerY = getY() + getHeight() / 2; + int stroke = Math.max(2, CN.convertToPixels(0.9f)); + int radius = (side - stroke) / 2; + long left = Math.max(0L, endsAt - System.currentTimeMillis()); + double part = Math.min(1d, left / (total * 1000d)); + + // The ring as dots around the circle, the first at the top and going + // clockwise: enough of them that they join up. + g.setAntiAliased(true); + int dots = Math.max(60, (int) (2 * Math.PI * radius / Math.max(1, stroke / 2))); + int lit = (int) Math.round(dots * part); + for (int dot = 0; dot < dots; dot++) { + double angle = 2 * Math.PI * dot / dots; + int x = centerX + (int) Math.round(Math.sin(angle) * radius); + int y = centerY - (int) Math.round(Math.cos(angle) * radius); + g.setColor(dot < lit ? style.getFgColor() : track); + g.fillArc(x - stroke / 2, y - stroke / 2, stroke, stroke, 0, 360); + } + + String text = String.valueOf(secondsLeft()); + Font font = style.getFont(); + g.setFont(font); + g.setColor(style.getFgColor()); + g.drawString(text, centerX - font.stringWidth(text) / 2, centerY - font.getHeight() / 2); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java new file mode 100644 index 00000000000..1ae24cafc32 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.net.Session; + +/// Where the server is. A released app has one answer and no use for this +/// screen; while you are developing, it is how a phone is pointed at the server +/// running on your computer -- `http://` and the computer's address on the +/// network, since `localhost` on a phone is the phone. +public final class ServerForm { + private ServerForm() { + } + + public static void show(Form previous) { + Form form = Ui.form("Server address", "Server"); + Ui.back(form, previous); + Container page = Ui.page(); + page.add(Ui.text("The address of the Morsel server this app talks to. " + + "Changing it signs you out.", "WlText")); + final TextField url = Ui.field(page, "Address", "url", "http://192.168.1.20:8080", + TextArea.URL); + url.setText(Session.serverUrl()); + page.add(Ui.primary("Save", "submit", e -> Nav.changeServer(url.getText()))); + form.add(BorderLayout.CENTER, page); + form.show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java new file mode 100644 index 00000000000..30bc951919f --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.AccountClosureDto; +import com.codenameone.examples.morsel.api.PasswordChangeDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.pay.WalletForm; + +/// Settings: the account, and everything about the app a person may want +/// their own way. +/// +/// Every control here reads and writes [Prefs] and nothing else, so this +/// screen does not know where a setting is kept: `Prefs` keeps it on the +/// device, and `net.Account` hears of the change and keeps it with the account. +/// The three under Appearance change what is already on screen -- the +/// language, light or dark, miles or kilometres -- and take effect at once: the +/// look and the words are applied, and the screens are built again in them, +/// this one included. +public final class SettingsForm { + private SettingsForm() { + } + + public static void show(final Form previous) { + final Form form = Ui.form("Settings", "Settings"); + Ui.back(form, previous); + final UserDto user = Session.user(); + Container page = Ui.page(); + + page.add(Ui.person(user.displayName(), user.username(), null)); + + Ui.section(page, "Account"); + Container account = group(); + account.add(Ui.tap(Ui.row(FontImage.MATERIAL_PERSON, "Profile", + "Name, emergency contact, home and work", chevron()), "profile", + e -> ProfileForm.show(form, () -> show(previous)))); + account.add(Ui.row(FontImage.MATERIAL_PHONE, user.phone(), + user.phoneVerified() ? null : "Needed before your first delivery", + user.phoneVerified() ? Ui.badge("Verified", "WlBadgeGood") + : Ui.link("Verify", "verify", + e -> VerifyPhoneForm.show(() -> show(previous))))); + if (user.courier()) { + account.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, user.vehicle(), user.plate(), + null)); + } + account.add(Ui.tap(Ui.row(FontImage.MATERIAL_LOCK, "Password", + "Change the password you sign in with", chevron()), "password", + e -> password(form))); + page.add(account); + + Ui.section(page, "Delivery preferences"); + Container delivery = group(); + delivery.add(Ui.label("Courier", "WlLabel")); + delivery.add(Ui.choice("courierGender", new String[] {"Any courier", "Women only"}, + new String[] {Prefs.GENDER_ANY, Prefs.GENDER_WOMEN}, Prefs.courierGender(), + Prefs::setCourierGender)); + delivery.add(Ui.row(FontImage.MATERIAL_VOLUME_OFF, "Quiet delivery", + "Ask the courier to keep conversation short", + Ui.toggle("quietDelivery", Prefs.quietDelivery(), Prefs::setQuietDelivery))); + delivery.add(Ui.row(FontImage.MATERIAL_ACCESSIBLE, "Accessible vehicle", + "Only cars that take a wheelchair", + Ui.toggle("accessibleVehicle", Prefs.accessibleVehicle(), + Prefs::setAccessibleVehicle))); + delivery.add(Ui.row(FontImage.MATERIAL_PETS, "Pet friendly", + "Tell the courier an animal is coming", + Ui.toggle("petFriendly", Prefs.petFriendly(), Prefs::setPetFriendly))); + page.add(delivery); + + Ui.section(page, "Payment"); + Container pay = group(); + pay.add(Ui.tap(Ui.row(FontImage.MATERIAL_ACCOUNT_BALANCE_WALLET, "Wallet", + "Your cards, and which of them pays by default", chevron()), "wallet", + e -> WalletForm.show(form))); + pay.add(Ui.label("Tip offered first", "WlLabel")); + pay.add(Ui.choice("tip", new String[] {"None", "10%", "15%", "20%"}, + new String[] {"0", "10", "15", "20"}, String.valueOf(Prefs.tipPercent()), + value -> Prefs.setTipPercent(Integer.parseInt(value)))); + page.add(pay); + + Ui.section(page, "Notifications"); + Container notify = group(); + notify.add(Ui.row(FontImage.MATERIAL_NOTIFICATIONS, "Delivery updates", + "Courier found, arriving, at the door", + Ui.toggle("notifyDeliveryUpdates", Prefs.notifyDeliveryUpdates(), + Prefs::setNotifyDeliveryUpdates))); + notify.add(Ui.row(FontImage.MATERIAL_RECEIPT, "Receipts", "After every delivery", + Ui.toggle("notifyReceipts", Prefs.notifyReceipts(), Prefs::setNotifyReceipts))); + notify.add(Ui.row(FontImage.MATERIAL_LOCAL_OFFER, "Offers", "Discounts and news", + Ui.toggle("notifyPromotions", Prefs.notifyPromotions(), + Prefs::setNotifyPromotions))); + page.add(notify); + + Ui.section(page, "Appearance"); + Container look = group(); + look.add(Ui.label("Language", "WlLabel")); + String speaking = Lang.current(); + for (int iter = 0; iter < Lang.CODES.length; iter++) { + final String code = Lang.CODES[iter]; + Label tick = new Label("", "WlRowIcon"); + if (code.equals(speaking)) { + Ui.icon(tick, FontImage.MATERIAL_CHECK, 3.4f); + } + Container row = new Container(new BorderLayout()); + row.setUIID("WlRow"); + // A language is called what its speakers call it, whatever + // language the rest of the screen is in. + row.add(BorderLayout.CENTER, Ui.plain(Lang.name(code), "WlRowTitle")); + row.add(BorderLayout.EAST, tick); + look.add(Ui.tap(row, "language-" + code, e -> { + boolean other = !code.equals(Lang.current()); + Prefs.setLanguage(code); + if (other) { + Telemetry.languageChanged(code); + } + again(previous); + })); + } + look.add(Ui.label("Theme", "WlLabel")); + look.add(Ui.choice("theme", new String[] {"System", "Light", "Dark"}, + new String[] {Prefs.THEME_SYSTEM, Prefs.THEME_LIGHT, Prefs.THEME_DARK}, + Prefs.theme(), value -> { + if (!value.equals(Prefs.theme())) { + Prefs.setTheme(value); + Telemetry.themeChanged(value); + } + again(previous); + })); + look.add(Ui.label("Distances", "WlLabel")); + look.add(Ui.choice("units", new String[] {"Kilometres", "Miles"}, + new String[] {Prefs.UNITS_KM, Prefs.UNITS_MILES}, Prefs.units(), value -> { + Prefs.setUnits(value); + again(previous); + })); + page.add(look); + + Ui.section(page, "Privacy and safety"); + Container safe = group(); + String contact = Prefs.emergencyContactName(); + safe.add(Ui.row(FontImage.MATERIAL_SHARE_LOCATION, "Share my trips", + contact.length() == 0 + ? Lang.tr("Name an emergency contact in your profile first") + : Lang.tr("Saves {0}'s number with your account as your trip contact. " + + "No message is sent.", contact), + Ui.toggle("shareTrips", Prefs.shareTrips(), Prefs::setShareTrips))); + // Kept on this device and not with the account: it is this + // installation that reports or does not. + safe.add(Ui.row(FontImage.MATERIAL_INSIGHTS, "Usage statistics", + "Share which screens and features are used. Never your name, places or " + + "payment details.", + Ui.toggle("usageStatistics", Telemetry.allowed(), Telemetry::allow))); + page.add(safe); + + Ui.section(page, "About"); + Container about = group(); + about.add(Ui.row(FontImage.MATERIAL_INFO, "Morsel", + "A sample delivery service built with Codename One", null)); + about.add(Ui.tap(Ui.row(FontImage.MATERIAL_DESCRIPTION, "Terms of service", null, + chevron()), "terms", e -> legal(form, "Terms of service", + "Morsel is a sample. No real deliveries are booked, no real couriers are " + + "sent and no money changes hands.", null))); + about.add(Ui.tap(Ui.row(FontImage.MATERIAL_POLICY, "Privacy policy", null, chevron()), + "privacy", e -> legal(form, "Privacy policy", + "Your position is sent to the server you signed in to while you " + + "delivery or drive, and to nobody else. Your settings are kept with " + + "your account on that server.", + "With usage statistics turned on, the app also counts which screens " + + "are opened and which features are used, and reports that to " + + "Codename One's analytics service under a made-up id for this " + + "device. Your name, e-mail, phone number, places and card details " + + "are never part of it, and turning it off in Settings stops it."))); + if (AppConfig.serverSwitchable()) { + about.add(Ui.tap(Ui.row(FontImage.MATERIAL_DNS, "Server address", null, chevron()), + "server", e -> ServerForm.show(form))); + } + page.add(about); + + page.add(Ui.secondary("Sign out", "signOut", e -> Nav.signOut())); + page.add(Ui.link("Delete my account", "deleteAccount", e -> delete(form))); + form.add(BorderLayout.CENTER, page); + form.show(); + } + + /// Applies the look and the language, and shows everything again in them. + /// + /// A screen that is already built keeps the words and colours it was built + /// with, so the mode's first screen is built anew to go back to, and this + /// one over it. + private static void again(Form previous) { + Look.apply(); + Lang.apply(); + Form home = Nav.build(); + show(home == null ? previous : home); + } + + static Container group() { + Container group = new Container(BoxLayout.y()); + group.setUIID("WlGroup"); + return group; + } + + /// The mark at the end of a row that leads somewhere. + static Label chevron() { + Label chevron = new Label("", "WlRowIcon"); + Ui.icon(chevron, FontImage.MATERIAL_CHEVRON_RIGHT, 3.4f); + return chevron; + } + + /// Changing the password. The server wants the current one as well as the + /// new: being signed in is what a borrowed phone is, too. + private static void password(final Form previous) { + final Form form = Ui.form("Password", "Password"); + Ui.back(form, previous); + Container page = Ui.page(); + final TextField current = Ui.field(page, "Current password", "current", "", + TextArea.PASSWORD); + final TextField replacement = Ui.field(page, "New password", "replacement", + "At least 8 characters", TextArea.PASSWORD); + final TextField again = Ui.field(page, "New password, again", "again", "", + TextArea.PASSWORD); + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + final Button save = Ui.primary("Change password", "submit", null); + save.addActionListener(e -> { + if (!replacement.getText().equals(again.getText())) { + error.setText(Lang.tr("The two new passwords are not the same.")); + Ui.refresh(form); + return; + } + PasswordChangeDto change = new PasswordChangeDto(current.getText(), + replacement.getText()); + save.setEnabled(false); + error.setText(""); + Api.account().changePassword(change, Net.to(user -> { + Ui.say("Your password was changed"); + previous.showBack(); + }, (status, message) -> { + save.setEnabled(true); + error.setText(message); + Ui.refresh(form); + })); + }); + page.add(error).add(save); + form.add(BorderLayout.CENTER, page); + form.show(); + } + + /// Closing the account, which cannot be taken back, so it is its own + /// screen that says what goes and has one red button. + private static void delete(final Form previous) { + final Form form = Ui.form("Delete account", "DeleteAccount"); + Ui.back(form, previous); + Container page = Ui.page(); + page.add(Ui.banner(FontImage.MATERIAL_DELETE_FOREVER, "Delete your account?", + "Your sign-in, your profile, your saved cards and your settings are removed " + + "for good. Deliveries you took stay in the service's records without your name.", + "WlBannerIconBad")); + // Asked for again, as changing the password asks: being signed in is + // what a borrowed phone is, too, and the server refuses without it. + final TextField password = Ui.field(page, "Current password", "password", "", + TextArea.PASSWORD); + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + page.add(error); + final Button go = Ui.danger("Delete my account", "confirmDelete", null); + go.addActionListener(e -> { + go.setEnabled(false); + AccountClosureDto closure = new AccountClosureDto(password.getText()); + Api.account().deleteAccount(closure, Net.to(gone -> Nav.deleted(), (status, message) -> { + go.setEnabled(true); + error.setText(message); + Ui.refresh(form); + })); + }); + page.add(go); + page.add(Ui.link("Keep my account", "keep", e -> previous.showBack())); + form.add(BorderLayout.CENTER, page); + form.show(); + } + + private static void legal(Form previous, String title, String text, String more) { + Form form = Ui.form(title, "Legal"); + Ui.back(form, previous); + Container page = Ui.page(); + page.add(Ui.text(text, "WlText")); + if (more != null) { + page.add(Ui.text(more, "WlText")); + } + form.add(BorderLayout.CENTER, page); + form.show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java new file mode 100644 index 00000000000..de11e9b8f65 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.net.Session; + +/// E-mail and password. The form is the app's own; what happens when it is +/// submitted is in [Session#signIn]. +public final class SignInForm { + private SignInForm() { + } + + public static void show(Form previous) { + final Form form = Ui.form("Sign in", "SignIn"); + Ui.back(form, previous); + Container page = Ui.page(); + final TextField email = Ui.field(page, "E-mail", "email", "you@example.com", + TextArea.EMAILADDR); + final TextField password = Ui.field(page, "Password", "password", "", + TextArea.PASSWORD); + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + final Button signIn = Ui.primary("Sign in", "submit", null); + signIn.addActionListener(e -> { + if (email.getText().trim().length() == 0 || password.getText().length() == 0) { + error.setText("Enter your e-mail and password."); + Ui.refresh(form); + return; + } + signIn.setEnabled(false); + error.setText(""); + Session.signIn(email.getText().trim(), password.getText(), () -> { + Telemetry.signedIn(); + Nav.enter(); + }, (status, message) -> { + signIn.setEnabled(true); + error.setText(message); + Ui.refresh(form); + }); + }); + page.add(error).add(signIn); + form.add(BorderLayout.CENTER, page); + form.show(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java new file mode 100644 index 00000000000..41d063a0f83 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Graphics; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.plaf.Style; + +/// What a list shows while what is in it is on its way: the outline of a few +/// rows, drawn as bars where the icon, the two lines and the value will be. +/// +/// It says "this is loading" without a word and without moving, and it keeps +/// the screen from jumping when the rows arrive, because they land where the +/// outline was. The colour is the `WlSkeleton` style's. +final class Skeleton extends Component { + /// How tall one row is drawn, in millimetres: a row of two lines. + private static final float ROW_MM = 9.6f; + + private final int rows; + + Skeleton(int rows) { + this.rows = rows; + setUIID("WlSkeleton"); + setFocusable(false); + } + + @Override + protected Dimension calcPreferredSize() { + Style style = getStyle(); + return new Dimension(CN.convertToPixels(40f) + style.getHorizontalPadding(), + rows * CN.convertToPixels(ROW_MM) + style.getVerticalPadding()); + } + + @Override + public void paint(Graphics g) { + Style style = getStyle(); + int left = getX() + style.getPaddingLeftNoRTL(); + int width = getWidth() - style.getHorizontalPadding(); + int row = CN.convertToPixels(ROW_MM); + int dot = CN.convertToPixels(4.4f); + int tall = CN.convertToPixels(1.9f); + int thin = CN.convertToPixels(1.5f); + int gap = CN.convertToPixels(2.6f); + if (width <= dot + gap) { + return; + } + boolean rtl = isRTL(); + g.setAntiAliased(true); + g.setColor(style.getFgColor()); + int lines = width - dot - gap; + for (int iter = 0; iter < rows; iter++) { + int top = getY() + style.getPaddingTop() + iter * row; + int middle = top + row / 2; + // Each row a little shorter than the one before: a list of + // identical bars reads as a pattern, not as a list. + int first = lines * (48 - iter % 3 * 7) / 100; + int second = lines * (30 + iter % 2 * 9) / 100; + int value = lines * 14 / 100; + bar(g, rtl, left, width, 0, middle - dot / 2, dot, dot); + bar(g, rtl, left, width, dot + gap, middle - tall - thin / 3, first, tall); + bar(g, rtl, left, width, dot + gap, middle + thin * 2 / 3, second, thin); + bar(g, rtl, left, width, width - value, middle - tall / 2, value, tall); + } + } + + /// One bar, `from` pixels in from the side a line of text starts at. + private static void bar(Graphics g, boolean rtl, int left, int width, int from, int y, + int w, int h) { + int x = rtl ? left + width - from - w : left + from; + g.fillRoundRect(x, y, w, h, h, h); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java new file mode 100644 index 00000000000..3829ac22256 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.CN; +import com.codename1.ui.Container; +import com.codename1.ui.Font; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.GridLayout; + +import java.util.ArrayList; +import java.util.List; + +/// A row of dashboard numbers, each over its caption, sized as one thing. +/// +/// A label asked to fit its own text decides alone and after the fact: it +/// changes its font once it has been given a width, by which time the tile +/// around it has its height from the font it had before, and its neighbour +/// has decided something else. The result was a row whose numbers were three +/// sizes and whose captions were cut off, or pushed out of the tile. +/// +/// So the row decides, before it lays its tiles out and from the width it +/// really has: one size for every number, the largest at which the longest +/// fits, and one for every caption, which goes onto a second line before it +/// is made any smaller. +public final class StatRow extends Container { + /// The smallest a number and a caption are drawn, in millimetres. + private static final float SMALLEST_VALUE = 2.6f; + private static final float SMALLEST_CAPTION = 1.9f; + + private final int columns; + private final List tiles = new ArrayList(); + private int laidOutAt; + private String fitted = ""; + private String planned = ""; + private boolean correcting; + + StatRow(int columns) { + super(new GridLayout(1, columns)); + this.columns = columns; + } + + /// Adds a number: `value` as it is, over `caption` in the user's language. + /// The number's label is called `name`. + public StatRow stat(String value, String caption, String name) { + Tile tile = new Tile(value, Lang.tr(caption), name); + tiles.add(tile); + add(tile); + return this; + } + + /// Takes every number out, for a row that is filled again. + public void clear() { + tiles.clear(); + removeAll(); + } + + @Override + protected Dimension calcPreferredSize() { + // Asked how tall it is before it knows how wide: answered for the + // width it last had, or the room its nearest sized ancestor offers. + fit(laidOutAt > 0 ? laidOutAt : roomOffered()); + planned = fitted; + return super.calcPreferredSize(); + } + + @Override + public void layoutContainer() { + int width = getWidth(); + if (width > 0) { + laidOutAt = width; + fit(width); + if (!fitted.equals(planned) && !correcting) { + // The height was answered for another width, and the tiles + // are a different height at this one. + correcting = true; + CN.callSerially(() -> { + correcting = false; + setShouldCalcPreferredSize(true); + Form form = getComponentForm(); + if (form != null) { + form.revalidate(); + } + }); + } + } + super.layoutContainer(); + } + + private int roomOffered() { + Container parent = getParent(); + while (parent != null) { + if (parent.getWidth() > 0) { + return parent.getWidth() - parent.getStyle().getHorizontalPadding(); + } + parent = parent.getParent(); + } + return CN.getDisplayWidth(); + } + + private void fit(int width) { + if (tiles.isEmpty() || width <= 0) { + return; + } + int across = Math.min(columns, tiles.size()); + Tile first = tiles.get(0); + int room = (width - getStyle().getHorizontalPadding()) / across + - first.getStyle().getHorizontalPadding() + - first.getStyle().getHorizontalMargins(); + if (room <= 0) { + return; + } + float value = 1f; + float caption = 1f; + for (Tile tile : tiles) { + value = Math.min(value, share(room - tile.value.sides(), tile.value.width(tile.number))); + tile.split(room - tile.upper.sides()); + caption = Math.min(caption, share(room - tile.upper.sides(), tile.widest())); + } + StringBuilder made = new StringBuilder(); + for (Tile tile : tiles) { + made.append(tile.value.scale(value, SMALLEST_VALUE)).append('/'); + made.append(tile.upper.scale(caption, SMALLEST_CAPTION)).append('/'); + tile.lower.scale(caption, SMALLEST_CAPTION); + made.append(tile.show()).append(' '); + } + fitted = made.toString(); + } + + /// The part of its full size at which text `needed` pixels wide fits + /// `room`. + private static float share(int room, int needed) { + return needed <= room || needed <= 0 ? 1f : Math.max(0f, room) / (float) needed; + } + + /// One number over its caption. + private static final class Tile extends Container { + private final String number; + private final String caption; + private final Sized value; + private final Sized upper; + private final Sized lower; + private String top; + private String bottom; + + Tile(String number, String caption, String name) { + super(BoxLayout.y()); + setUIID("WlStat"); + this.number = number == null ? "" : number; + this.caption = caption == null ? "" : caption; + value = new Sized(Ui.plain(this.number, "WlStatValue")); + value.label.setName(name); + upper = new Sized(Ui.plain(this.caption, "WlStatLabel")); + lower = new Sized(Ui.plain("", "WlStatLabel")); + top = this.caption; + bottom = ""; + add(value.label).add(upper.label).add(lower.label); + show(); + } + + /// Puts the caption on one line if `room` holds it, and otherwise on + /// two, broken at the space that leaves the longer line shortest. + void split(int room) { + top = caption; + bottom = ""; + int whole = upper.width(caption); + if (whole <= room) { + return; + } + int best = whole; + for (int at = caption.indexOf(' '); at > 0; at = caption.indexOf(' ', at + 1)) { + String before = caption.substring(0, at); + String after = caption.substring(at + 1); + int longer = Math.max(upper.width(before), upper.width(after)); + if (longer < best) { + best = longer; + top = before; + bottom = after; + } + } + } + + int widest() { + return Math.max(upper.width(top), upper.width(bottom)); + } + + /// Writes the lines decided by [#split]; answers how many there are. + int show() { + if (!top.equals(upper.label.getText())) { + upper.label.setText(top); + } + if (!bottom.equals(lower.label.getText())) { + lower.label.setText(bottom); + } + boolean second = bottom.length() > 0; + if (lower.label.isHidden() == second) { + lower.label.setHidden(!second); + lower.label.setVisible(second); + } + return second ? 2 : 1; + } + } + + /// A label and the font its style gave it, which is the size it is drawn + /// at when there is room. The style is asked again every time, because a + /// label is restyled under us: the desktop console gives it another name, + /// and a change of theme gives it another font. + private static final class Sized { + private final Label label; + private Font styled; + private Font applied; + private int appliedPixels; + + Sized(Label label) { + this.label = label; + } + + private Font styled() { + Font now = label.getUnselectedStyle().getFont(); + if (now != applied || styled == null) { + styled = now; + applied = now; + appliedPixels = Math.round(now.getPixelSize()); + } + return styled; + } + + int sides() { + return label.getUnselectedStyle().getHorizontalPadding() + + label.getUnselectedStyle().getHorizontalMargins(); + } + + /// How wide `text` is at the full size. + int width(String text) { + return text.length() == 0 ? 0 : styled().stringWidth(text); + } + + /// Draws the label at `share` of its full size, and never smaller + /// than `smallest` millimetres; answers the size in pixels. + int scale(float share, float smallest) { + Font full = styled(); + int most = Math.round(full.getPixelSize()); + if (most <= 0) { + // Not a font that can be drawn at another size. + return 0; + } + int least = Math.min(most, CN.convertToPixels(smallest)); + int pixels = share >= 1f ? most : Math.max(least, (int) Math.floor(most * share)); + if (pixels != appliedPixels) { + applied = pixels == most ? full : full.derive(pixels, full.getStyle()); + appliedPixels = pixels; + label.getAllStyles().setFont(applied); + } + return pixels; + } + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java new file mode 100644 index 00000000000..a7130718710 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.components.Switch; +import com.codename1.components.ToastBar; +import com.codename1.l10n.SimpleDateFormat; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Command; +import com.codename1.ui.ButtonGroup; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.RadioButton; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.events.ActionEvent; +import com.codename1.ui.events.ActionListener; +import com.codename1.ui.geom.Rectangle; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.DeliveryState; + +import java.util.Date; + +/// The pieces every screen is made of. Each one is a component with a style +/// name from `theme.css`; nothing here, or in any screen, sets a colour or a +/// font in code. +public final class Ui { + /// What goes between two short facts on one line: a raised dot. + public static final String DOT = " \u00b7 "; + + private Ui() { + } + + /// A screen with a title bar. + /// + /// @param name what a test waits for; the title is for people and changes + public static Form form(String title, String name) { + Form form = new Form(title, new BorderLayout()); + form.setUIID("WlForm"); + form.setName(name); + watch(form); + clearOfTheStatusBar(form); + form.addSizeChangedListener(e -> clearOfTheStatusBar(form)); + return form; + } + + /// Keeps the title bar of `form` under what the device draws over the top + /// of the screen -- the clock, a notch -- the same way the map screens + /// keep their floating bar under it: by the inset the device reports. The + /// strip a form reserves for the status bar is as tall as the theme makes + /// it, which says nothing of a notch; the title bar is padded by whatever + /// that strip leaves uncovered. + private static void clearOfTheStatusBar(Form form) { + Container bar = form.getToolbar(); + if (bar == null) { + return; + } + Rectangle safe = Display.getInstance().getDisplaySafeArea(new Rectangle()); + int covered = Math.max(0, safe.getY()); + Container area = bar.getParent(); + int kept = 0; + if (area != null && area.getLayout() instanceof BorderLayout) { + Component strip = ((BorderLayout) area.getLayout()).getNorth(); + if (strip != null && strip != bar) { + kept = strip.getPreferredH(); + } + } + int styled = UIManager.getInstance().getComponentStyle("Toolbar").getPaddingTop(); + int wanted = styled + Math.max(0, covered - kept); + if (bar.getUnselectedStyle().getPaddingTop() != wanted) { + bar.getAllStyles().setPaddingUnitTop(Style.UNIT_TYPE_PIXELS); + bar.getAllStyles().setPaddingTop(wanted); + } + } + + /// Gives `label` a material icon `size` millimetres tall, and the space + /// between it and the label's words: without one the two touch. + public static void icon(Label label, char icon, float size) { + FontImage.setMaterialIcon(label, icon, size); + label.setGap(CN.convertToPixels(ICON_GAP)); + } + + /// The space between an icon and the words beside it, in millimetres. + private static final float ICON_GAP = 1.4f; + + /// Has `form` counted as a screen each time it is shown, under its name. + /// Every screen of the app is made by this class or handed to this method, + /// so this is the one place a screen view is reported from. + public static void watch(final Form form) { + form.addShowListener(e -> viewed(form.getName())); + } + + /// Counts something that takes the place of a screen without being a + /// form: a pane of the admin's console on a wide window. + public static void viewed(String name) { + Telemetry.screen(name); + } + + /// The first screen of a mode: no title bar, and layers, so that a map can + /// fill the screen with the controls floating over it and the menu sliding + /// in over both. + public static Form home(String name) { + Form form = new Form("", new LayeredLayout()); + form.setUIID("WlForm"); + form.setName(name); + watch(form); + form.getToolbar().hideToolbar(); + // What is on the screen scrolls, where it needs to. The screen itself + // does not, or it would offer to scroll the map away. + form.setScrollable(false); + return form; + } + + /// Gives `form` a back arrow that returns to `previous`. + public static void back(Form form, final Form previous) { + back(form, () -> previous.showBack()); + } + + /// Gives `form` a back arrow that runs `leave`. + /// + /// On a desktop whose window owns the title bar there is no title bar in + /// the app to carry an arrow. The command is then named, so that it is in + /// the window's menu, and the screen gets a line of its own to go back + /// from. A screen that has put something of its own at its top keeps it, + /// under that line: a screen with no way back is one nobody can leave + /// without a device's back button, and a desktop has none. + public static void back(final Form form, final Runnable leave) { + boolean window = CN.isDesktop() && form.getToolbar().getParent() == null; + Command back = form.getToolbar().setBackCommand(window ? Lang.tr("Back") : "", + e -> leave.run()); + if (!window) { + return; + } + back.setDesktopMenu(Command.DESKTOP_MENU_VIEW); + back.setDesktopShortcut('['); + final boolean[] added = new boolean[1]; + form.addShowListener(e -> { + if (added[0] || form.getToolbar().getParent() != null + || !(form.getContentPane().getLayout() instanceof BorderLayout)) { + return; + } + added[0] = true; + Container bar = new Container(new BorderLayout()); + bar.setUIID("WlPaneBar"); + Button arrow = button("Back", "back-bar", "WlLink", e2 -> leave.run()); + icon(arrow, FontImage.MATERIAL_ARROW_BACK, 3.4f); + bar.add(BorderLayout.WEST, arrow); + Component top = ((BorderLayout) form.getContentPane().getLayout()).getNorth(); + if (top == null) { + form.add(BorderLayout.NORTH, bar); + } else { + top.remove(); + Container both = new Container(BoxLayout.y()); + both.add(bar).add(top); + form.add(BorderLayout.NORTH, both); + } + Layouts.dress(bar); + form.revalidate(); + }); + } + + /// A column of content with the page's margins, that scrolls when it has to. + public static Container page() { + Container page = new Column(); + page.setUIID("WlPage"); + page.setScrollableY(true); + page.setScrollVisible(false); + page.setSafeArea(true); + return page; + } + + public static Container card() { + Container card = new Container(BoxLayout.y()); + card.setUIID("WlCard"); + return card; + } + + public static Label label(String text, String uiid) { + return new Label(text == null ? "" : text, uiid); + } + + /// Text that wraps. + public static SpanLabel text(String text, String uiid) { + SpanLabel label = new SpanLabel(text == null ? "" : text); + label.setUIID("Container"); + label.setTextUIID(uiid); + return label; + } + + public static Button primary(String text, String name, ActionListener onClick) { + return button(text, name, "WlPrimary", onClick); + } + + public static Button secondary(String text, String name, ActionListener onClick) { + return button(text, name, "WlSecondary", onClick); + } + + public static Button danger(String text, String name, ActionListener onClick) { + return button(text, name, "WlDanger", onClick); + } + + public static Button link(String text, String name, ActionListener onClick) { + return button(text, name, "WlLink", onClick); + } + + /// A round button that floats over the map. + public static Button round(char icon, String name, ActionListener onClick) { + Button button = button("", name, "WlMapButton", onClick); + icon(button, icon, 4f); + return button; + } + + /// The same button for a bar that is a surface of its own: flat, since a + /// shadow says "this floats", and on a bar nothing does. + public static Button flat(char icon, String name, ActionListener onClick) { + Button button = button("", name, "WlBarButton", onClick); + icon(button, icon, 4f); + return button; + } + + /// One of a row of things to do: an icon with a word under it. + public static Button action(char icon, String text, String name, + ActionListener onClick) { + Button button = button(text, name, "WlAction", onClick); + icon(button, icon, 4f); + button.setTextPosition(Component.BOTTOM); + button.setGap(CN.convertToPixels(0.6f)); + return button; + } + + /// A short fact in a pill: how far, how long. The icon may be 0. + public static Label chip(String text, char icon) { + Label chip = new Label(text == null ? "" : text, "WlChip"); + if (icon != 0) { + icon(chip, icon, 2.7f); + } + return chip; + } + + /// A person, as the initials of their name in a circle. + public static Label avatar(String name) { + Label avatar = new Label("", "WlAvatar"); + // Initials are not words: looking them up would translate "OK". + avatar.setShouldLocalize(false); + avatar.setText(initials(name)); + return avatar; + } + + /// The first letters of the first and last words of `name`. + public static String initials(String name) { + String text = name == null ? "" : name.trim(); + if (text.length() == 0) { + return "?"; + } + int space = text.lastIndexOf(' '); + String first = text.substring(0, 1); + return space < 0 || space + 1 >= text.length() ? first + : first + text.substring(space + 1, space + 2); + } + + public interface Flipped { + void flipped(boolean on); + } + + /// A switch that reports each change. + public static Switch toggle(String name, boolean value, final Flipped flipped) { + final Switch toggle = new Switch(); + toggle.setName(name); + toggle.setValue(value); + toggle.addChangeListener(e -> flipped.flipped(toggle.isValue())); + return toggle; + } + + public interface Chosen { + void chosen(String value); + } + + /// A few options side by side, one of them selected. Each button is named + /// `name-value`. + /// + /// @param labels what each option is called, for people + /// @param values what each option is, for the code + public static Container choice(String name, String[] labels, final String[] values, + String current, final Chosen chosen) { + return choice(name, labels, values, current, chosen, values.length); + } + + /// The same, in rows of `columns`, for options that do not fit in one. + public static Container choice(String name, String[] labels, final String[] values, + String current, final Chosen chosen, int columns) { + Container row = new Container(new GridLayout((values.length + columns - 1) / columns, + columns)); + row.setUIID("WlSegments"); + ButtonGroup group = new ButtonGroup(); + for (int iter = 0; iter < values.length; iter++) { + final String value = values[iter]; + RadioButton option = RadioButton.createToggle(labels[iter], group); + option.setUIID("WlSegment"); + option.setName(name + "-" + value); + option.setSelected(value.equals(current)); + option.addActionListener(e -> chosen.chosen(value)); + row.add(option); + } + return row; + } + + /// The longest address that is still shown on one line. + private static final int LONG_ADDRESS = 26; + + /// Who a screen is about: their initials, their name, the address they sign + /// in with, and under those `value` when there is something to say about + /// them. The address wraps rather than being cut off; it is what tells + /// two people with the same name apart. + public static Container person(String name, String username, Component value) { + Container who = new Container(new BorderLayout()); + who.setUIID("WlPanel"); + Container names = new Container(BoxLayout.y()); + Label called = plain(name, "WlHeading"); + shrink(called, 3f); + String signsIn = username == null ? "" : username; + Component address; + if (signsIn.length() > LONG_ADDRESS) { + // An address has no spaces to break at: a long one is broken + // where the line ends, which keeps all of it readable. + SpanLabel wrapped = text("", "WlMuted"); + wrapped.setShouldLocalize(false); + wrapped.setText(signsIn); + address = wrapped; + } else { + Label line = plain(signsIn, "WlMuted"); + shrink(line, 2f); + address = line; + } + names.add(called).add(address); + if (value != null) { + names.add(FlowLayout.encloseIn(value)); + } + who.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(avatar(name))); + who.add(BorderLayout.CENTER, names); + return who; + } + + /// Lets `label` draw its text smaller, down to `smallest` millimetres, + /// when it would otherwise be cut off -- and never larger than its style + /// says, which is what fitting text to its space does when left alone. + public static void shrink(Label label, float smallest) { + float styled = label.getUnselectedStyle().getFont().getPixelSize() + / (float) CN.convertToPixels(1f); + label.setAutoSizeMode(true); + label.setMaxAutoSize(styled); + label.setMinAutoSize(Math.min(smallest, styled)); + } + + /// Lays `form` out again after its content changed while it was showing. + /// + /// Twice, the second time after the first has been drawn: text that wraps + /// learns its width from the first pass, and only then knows how many + /// lines it is. + public static void refresh(final Form form) { + form.revalidate(); + CN.callSerially(() -> form.revalidate()); + } + + /// The heading over a group of settings or facts. + public static void section(Container page, String title) { + page.add(label(title, "WlSection")); + } + + /// A label for text that is a name or a number, not a word to translate. + public static Label plain(String text, String uiid) { + Label label = new Label("", uiid); + label.setShouldLocalize(false); + label.setText(text == null ? "" : text); + return label; + } + + /// One line of a receipt or a record: what it is on one side, and its + /// value, which is never translated, on the other. + public static Container fact(String caption, String value) { + return fact(caption, value, "WlFactValue"); + } + + public static Container fact(String caption, String value, String valueUiid) { + Container fact = new Container(new BorderLayout()); + fact.setUIID("WlFact"); + fact.add(BorderLayout.CENTER, label(caption, "WlFactLabel")); + fact.add(BorderLayout.EAST, plain(value, valueUiid)); + return fact; + } + + /// What a screen shows instead of a list with nothing in it, and at the top + /// of a screen that is about one thing: a large icon, a line, and a few + /// words under it. + public static Container banner(char icon, String title, String text, String iconUiid) { + Container banner = new Container(BoxLayout.y()); + banner.setUIID("WlBanner"); + Label image = new Label("", iconUiid); + icon(image, icon, 6f); + banner.add(FlowLayout.encloseCenter(image)); + SpanLabel heading = text(title, "WlBannerTitle"); + heading.getTextComponent().setAlignment(Component.CENTER); + banner.add(heading); + if (text != null && text.length() > 0) { + SpanLabel words = text(text, "WlBannerText"); + words.getTextComponent().setAlignment(Component.CENTER); + banner.add(words); + } + return banner; + } + + public static Container empty(char icon, String title, String text) { + return banner(icon, title, text, "WlBannerIcon"); + } + + /// A list that fills a screen: rows from edge to edge, scrolling. + public static Container list(String name) { + Container list = new Column(); + list.setUIID("WlList"); + list.setScrollableY(true); + list.setScrollVisible(false); + list.setName(name); + // Until it is filled: every list is emptied by what fills it. + list.add(new Skeleton(5)); + return list; + } + + /// Has a pull down on `list` fetch what is in it again, with `load`. A + /// window on a desktop is not pulled, and gets nothing. + public static void pull(Container list, final Runnable load) { + if (!CN.isDesktop()) { + list.addPullToRefresh(load); + } + } + + /// A column that stays a column: on a window wider than a column of text + /// should be, it keeps its content in the middle rather than stretching + /// every row and every button from one edge to the other. + private static final class Column extends Container { + private String styled; + private int margin; + + Column() { + super(BoxLayout.y()); + } + + @Override + public void layoutContainer() { + // The margin is the style's own, read once for each style the + // column is given: what is set below replaces it. + if (!getUIID().equals(styled)) { + styled = getUIID(); + margin = getUnselectedStyle().getPaddingLeftNoRTL(); + } + Layouts.centre(this, margin); + super.layoutContainer(); + } + } + + public interface Reason { + void given(String reason); + } + + /// Asks why, before something an admin does to someone else's account or + /// delivery. The reason is kept with what was done, so it is not optional. + /// + /// @param action what the button says, which is what will be done + public static void reason(final Form previous, String title, String text, String action, + final Reason reason) { + final Form form = form(title, "Reason"); + back(form, previous); + form.add(BorderLayout.CENTER, reasonPage(text, action, reason)); + form.show(); + } + + /// What [#reason] asks with, for a caller that has its own place to put + /// it: the admin console shows it beside the list on a wide screen. + public static Container reasonPage(String text, String action, final Reason reason) { + final Container page = page(); + page.add(text(text, "WlText")); + final TextField why = field(page, "Reason", "reason", "A sentence the record will keep"); + final SpanLabel error = text("", "WlError"); + error.setName("error"); + page.add(error); + page.add(danger(action, "confirm", e -> { + String given = why.getText().trim(); + if (given.length() == 0) { + error.setText(Lang.tr("Say why, in a few words.")); + page.getComponentForm().revalidate(); + return; + } + reason.given(given); + })); + return page; + } + + private static Button button(String text, String name, String uiid, + ActionListener onClick) { + Button button = new Button(text, uiid); + // The name is what a test finds the button by. + button.setName(name); + if (onClick != null) { + button.addActionListener(onClick); + } + return button; + } + + /// Adds a caption and a text field under it to `to`, and returns the field. + public static TextField field(Container to, String caption, String name, String hint, + int constraint) { + TextField field = new TextField("", hint, 24, constraint); + field.setUIID("WlField"); + field.getHintLabel().setUIID("WlFieldHint"); + field.setName(name); + to.add(label(caption, "WlLabel")).add(field); + return field; + } + + public static TextField field(Container to, String caption, String name, String hint) { + return field(to, caption, name, hint, TextArea.ANY); + } + + /// A row of a list: an icon, two lines of text, and something at the end. + /// The icon may be 0, and the last two null. + public static Container row(char icon, String title, String detail, Component value) { + Container row = new Container(new BorderLayout()); + row.setUIID("WlRow"); + if (icon != 0) { + Label image = new Label("", "WlRowIcon"); + icon(image, icon, 4f); + row.add(BorderLayout.WEST, image); + } + Container lines = new Container(BoxLayout.y()); + lines.add(label(title, "WlRowTitle")); + if (detail != null && detail.length() > 0) { + lines.add(text(detail, "WlRowDetail")); + } + row.add(BorderLayout.CENTER, lines); + if (value != null) { + // Its own height, in the middle of the row: straight into EAST it + // would be stretched to the height of the row. + row.add(BorderLayout.EAST, FlowLayout.encloseCenterMiddle(value)); + } + return row; + } + + /// Makes the whole of `row` one button. The name is the button's, for a + /// test to click. + public static Container tap(Container row, String name, ActionListener onClick) { + Button lead = new Button(); + lead.setName(name); + lead.setHidden(true); + lead.addActionListener(onClick); + row.add(BorderLayout.SOUTH, lead); + row.setLeadComponent(lead); + return row; + } + + /// A day, without the time. + public static String day(long millis) { + return new SimpleDateFormat("MMM d, yyyy").format(new Date(millis)); + } + + /// The product a delivery was asked for as, in words. + public static String product(String product) { + return "comfort".equals(product) ? "Comfort" : "xl".equals(product) ? "XL" : "Standard"; + } + + /// A moment, as a short date and time. + public static String when(long millis) { + return new SimpleDateFormat("MMM d, HH:mm").format(new Date(millis)); + } + + public static Label badge(String text, String uiid) { + return new Label(text, uiid); + } + + /// A row of numbers, each with a caption under it, `columns` across: + /// add them with [StatRow#stat]. More than `columns` of them run onto a + /// further row. + public static StatRow stats(int columns) { + return new StatRow(columns); + } + + /// Tells the user something failed. The signature is `Net.Failed`'s, so a + /// call can end `Ui::fail`. + public static void fail(int status, String message) { + // What the server says is the server's wording; what the app says of + // its own is translated. Looking both up costs nothing. + ToastBar.showErrorMessage(Lang.tr(message)); + } + + /// What a list's load ends with when it fails: the user is told, as with + /// [#fail], and a list still showing the outline of rows that are not + /// coming shows what went wrong instead, with a way to ask again. A list + /// that already has rows keeps them. + /// + /// @param load fetches the list again, or null when there is no asking again + public static Net.Failed failed(final Container list, final Runnable load) { + return (status, message) -> { + fail(status, message); + if (list.getComponentCount() != 1 || !(list.getComponentAt(0) instanceof Skeleton)) { + return; + } + list.removeAll(); + list.add(empty(FontImage.MATERIAL_CLOUD_OFF, message, null)); + if (load != null) { + list.add(FlowLayout.encloseCenter(link("Try again", "retry", e -> { + list.removeAll(); + list.add(new Skeleton(5)); + list.revalidate(); + load.run(); + }))); + } + list.revalidate(); + }; + } + + public static void say(String message) { + ToastBar.showInfoMessage(Lang.tr(message)); + } + + /// An amount of money. Whole cents in and text out: `$12.40` for dollars, + /// and the amount followed by the currency's code for anything else. + public static String money(long cents, String currency) { + long whole = cents / 100; + long part = Math.abs(cents % 100); + String amount = whole + "." + (part < 10 ? "0" : "") + part; + if (currency == null || currency.length() == 0 || "USD".equals(currency)) { + return "$" + amount; + } + return amount + " " + currency; + } + + /// A distance, in the units chosen in Settings. + public static String distance(double meters) { + if (Prefs.UNITS_MILES.equals(Prefs.units())) { + double feet = meters * 3.28084; + if (feet < 900) { + return Lang.tr("{0} ft", String.valueOf(Math.round(feet / 50d) * 50)); + } + return Lang.tr("{0} mi", tenths(meters / 1609.344)); + } + if (meters < 950) { + return Lang.tr("{0} m", String.valueOf(Math.round(meters / 50d) * 50)); + } + return Lang.tr("{0} km", tenths(meters / 1000d)); + } + + /// `value` with one digit after the point. + public static String tenths(double value) { + long tenths = Math.round(value * 10d); + return (tenths / 10) + "." + (tenths % 10); + } + + public static String minutes(double seconds) { + return Lang.tr("{0} min", String.valueOf(Math.max(1L, Math.round(seconds / 60d)))); + } + + /// A delivery's state in words. + public static String state(DeliveryState state) { + if (DeliveryState.REQUESTED == state || DeliveryState.OFFERED == state) { + return "Finding a courier"; + } + if (DeliveryState.ACCEPTED == state) { + return "Courier on the way"; + } + if (DeliveryState.ARRIVED == state) { + return "Courier at pickup"; + } + if (DeliveryState.IN_PROGRESS == state) { + return "On the trip"; + } + if (DeliveryState.COMPLETED == state) { + return "Completed"; + } + if (DeliveryState.CANCELLED_BY_CUSTOMER == state) { + return "Cancelled by customer"; + } + if (DeliveryState.CANCELLED_BY_COURIER == state) { + return "Cancelled by courier"; + } + if (DeliveryState.CANCELLED_BY_ADMIN == state) { + return "Cancelled by Morsel"; + } + if (DeliveryState.NO_COURIERS == state) { + return "No courier found"; + } + return ""; + } + + /// The style of the badge a state is shown in. + public static String stateBadge(DeliveryState state) { + if (DeliveryState.COMPLETED == state) { + return "WlBadgeGood"; + } + if (state != null && state.isActive()) { + return "WlBadgeWarn"; + } + return "WlBadgeBad"; + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java new file mode 100644 index 00000000000..e119b131f6e --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BorderLayout; +import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.morsel.api.PhoneCodeDto; +import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.morsel.net.Api; +import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.morsel.net.Session; + +/// Proves the phone number is the user's, which the server asks for before the +/// first delivery. +/// +/// The code is the server's: it makes it up, texts it, and is the only one who +/// checks it. The app never sees the code unless the server is a development +/// one with no SMS provider configured, which sends it back so the flow can be +/// tried without a phone. +public final class VerifyPhoneForm { + private VerifyPhoneForm() { + } + + /// @param after where to go once the number is verified, or the user + /// decides to leave it for later + public static void show(final Runnable after) { + final Form form = Ui.form("Verify your number", "VerifyPhone"); + UserDto user = Session.user(); + Container page = Ui.page(); + page.add(Ui.text(user == null || user.phone() == null + ? Lang.tr("We sent a six-digit code to your phone.") + : Lang.tr("We sent a six-digit code to {0}.", user.phone()), "WlText")); + final SpanLabel demo = Ui.text("", "WlNotice"); + demo.setName("demoCode"); + demo.setHidden(true); + page.add(demo); + final TextField code = Ui.field(page, "Code", "code", "000000", TextArea.NUMERIC); + final SpanLabel error = Ui.text("", "WlError"); + error.setName("error"); + final Button verify = Ui.primary("Verify", "submit", null); + verify.addActionListener(e -> { + PhoneCodeDto entered = new PhoneCodeDto(code.getText().trim()); + verify.setEnabled(false); + error.setText(""); + Api.account().verifyPhone(entered, Net.to(verified -> { + Session.setUser(verified); + Telemetry.phoneVerified(); + after.run(); + }, (status, message) -> { + verify.setEnabled(true); + error.setText(message); + Ui.refresh(form); + })); + }); + final Runnable send = () -> Api.account().startPhoneVerification(Net.to(challenge -> { + if (challenge.demoCode() != null && challenge.demoCode().length() > 0) { + demo.setText(Lang.tr("This is a development server, so no text was " + + "sent. Your code is {0}.", challenge.demoCode())); + demo.setHidden(false); + Ui.refresh(form); + } + }, (status, message) -> { + error.setText(message); + Ui.refresh(form); + })); + page.add(error).add(verify); + page.add(Ui.link("Send a new code", "resend", e -> send.run())); + page.add(Ui.link("Not now", "skip", e -> after.run())); + form.add(BorderLayout.CENTER, page); + form.show(); + send.run(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java new file mode 100644 index 00000000000..61d0af7a3b3 --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.ui; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.morsel.AppConfig; + +/// What a signed-out user sees. +public final class WelcomeForm { + private WelcomeForm() { + } + + public static void show() { + final Form form = Ui.form("", "Welcome"); + form.getToolbar().hideToolbar(); + + Container hero = new Container(BoxLayout.y()); + hero.setUIID("WlHero"); + hero.setSafeArea(true); + Label icon = new Label("", "WlHeroIcon"); + Ui.icon(icon, FontImage.MATERIAL_NEAR_ME, 10f); + hero.add(icon); + hero.add(Ui.label("Morsel", "WlHeroTitle")); + hero.add(Ui.text("A delivery when you need one, and a fair way to drive.", "WlHeroText")); + + Container actions = Ui.page(); + actions.setScrollableY(false); + actions.add(Ui.primary("Sign in", "signIn", e -> SignInForm.show(form))); + actions.add(Ui.secondary("Create an account", "register", e -> RegisterForm.show(form))); + if (AppConfig.serverSwitchable()) { + actions.add(Ui.link("Server address", "server", e -> ServerForm.show(form))); + } + + form.add(BorderLayout.CENTER, hero); + form.add(BorderLayout.SOUTH, actions); + form.show(); + } +} diff --git a/scripts/morsel/common/src/main/l10n/Bundle.properties b/scripts/morsel/common/src/main/l10n/Bundle.properties new file mode 100644 index 00000000000..c07a695976e --- /dev/null +++ b/scripts/morsel/common/src/main/l10n/Bundle.properties @@ -0,0 +1,3 @@ +# Workaround for the simulator AutoLocalizationBundle @im fabrication crash +# in Codename One <= 7.0.236. Once the framework fix ships, this file can be removed. +@im= diff --git a/scripts/morsel/common/src/main/l10n/Bundle_de.properties b/scripts/morsel/common/src/main/l10n/Bundle_de.properties new file mode 100644 index 00000000000..84bb4e612cb --- /dev/null +++ b/scripts/morsel/common/src/main/l10n/Bundle_de.properties @@ -0,0 +1,532 @@ +# Morsel in German. The key of every line is the English text the code +# names; see ui/Lang.java for how a line is found and how to add one. +@rtl=false +language.name=Deutsch + +Admin=Admin +Dashboard=\u00dcbersicht +Live\ map=Live-Karte +Deliveries=Fahrten +People=Personen +Operations=Betrieb +Morsel\ administration=Morsel-Verwaltung +Today=Heute +Manage=Verwalten +Where\ every\ car\ online\ is=Wo jedes Auto gerade ist +The\ most\ recent,\ in\ every\ state=Die neuesten, in jedem Status +Roles,\ and\ suspending\ an\ account=Rollen und Sperren von Konten +Deliveries\ today=Fahrten heute +Under\ way=Unterwegs +Online=Online +Offline=Offline +Fares=Einnahmen +Cars\ move\ as\ their\ couriers\ report\ in.=Die Autos bewegen sich, sobald die Fahrer sich melden. +No\ cars\ online=Keine Autos online +1\ car\ online=1 Auto online +{0}\ cars\ online={0} Autos online +Suspended=Gesperrt +Courier=Fahrer +Customer=Fahrgast +Verified=Best\u00e4tigt +Not\ verified=Nicht best\u00e4tigt +May\ go\ online\ and\ take\ deliveries=Darf online gehen und Fahrten annehmen +May\ see\ and\ change\ everything\ here=Darf hier alles sehen und \u00e4ndern +Signed\ out,\ and\ refused\ until\ lifted=Abgemeldet und abgewiesen, bis die Sperre aufgehoben wird +Access=Zugriff +Drive=Fahren +Your\ trips=Deine Fahrten +Earnings=Verdienst +The\ request\ went\ to\ another\ courier=Die Anfrage ging an einen anderen Fahrer +The\ customer\ cancelled=Der Fahrgast hat storniert +Today,\ {0}\ trips=Heute, {0} Fahrten +Looking\ for\ deliveries=Suche nach Fahrten +You\ are\ offline=Du bist offline +Trip\ complete\:\ {0}=Fahrt abgeschlossen: {0} +Stay\ close\ to\ busy\ streets;\ requests\ come\ to\ the\ nearest\ car.=Bleib in der N\u00e4he belebter Stra\u00dfen; Anfragen gehen an das n\u00e4chste Auto. +Go\ online\ to\ start\ taking\ deliveries.=Geh online, um Fahrten anzunehmen. +GO=LOS +This\ week=Diese Woche +Trips=Fahrten +All\ time=Insgesamt +Go\ offline=Offline gehen +Go\ online=Online gehen +Delivery\ request=Fahrtanfrage +{0}\ away,\ {1}={0} entfernt, {1} +Accept=Annehmen +Decline=Ablehnen +Pick\ up\ {0}={0} abholen +Waiting\ for\ {0}=Warten auf {0} +On\ the\ trip=Auf der Fahrt +Drop-off=Ziel +Pickup=Abholung +I\ have\ arrived=Ich bin da +Start\ trip=Fahrt starten +Complete\ trip=Fahrt abschlie\u00dfen +Cancel\ delivery=Fahrt stornieren +Quiet\ delivery=Ruhige Fahrt +Accessible\ vehicle=Barrierefreies Fahrzeug +Could\ not\ reach\ the\ server.\ Check\ your\ connection.=Der Server ist nicht erreichbar. Pr\u00fcfe deine Verbindung. +Please\ sign\ in\ again.=Bitte melde dich erneut an. +Your\ account\ is\ not\ allowed\ to\ do\ that.=Dein Konto darf das nicht. +Too\ many\ attempts.\ Wait\ a\ moment\ and\ try\ again.=Zu viele Versuche. Warte kurz und versuche es erneut. +Too\ many\ attempts.\ Wait\ a\ minute\ and\ try\ again.=Zu viele Versuche. Warte eine Minute und versuche es erneut. +Something\ went\ wrong\ ({0}).\ Try\ again.=Etwas ist schiefgelaufen ({0}). Versuche es erneut. +That\ e-mail\ and\ password\ do\ not\ match\ an\ account.=Diese E-Mail und dieses Passwort passen zu keinem Konto. +Signing\ in\ failed.\ Try\ again.=Anmeldung fehlgeschlagen. Versuche es erneut. +Where\ to?=Wohin? +Destination=Ziel +An\ address\ or\ a\ place=Eine Adresse oder ein Ort +Nothing\ found=Nichts gefunden +Current\ location=Aktueller Standort +Delivery=Fahren lassen +Your\ deliveries=Deine Fahrten +Your\ courier\ is\ here=Dein Fahrer ist da +{0}\ to\ destination={0} bis zum Ziel +Pickup\ in\ {0}=Abholung in {0} +Hello,\ {0}=Hallo, {0} +Where\ are\ you\ going?=Wohin soll es gehen? +Verify\ your\ phone\ number\ to\ request\ a\ delivery.=Best\u00e4tige deine Telefonnummer, um eine Fahrt anzufragen. +Verify\ now=Jetzt best\u00e4tigen +Home=Zuhause +Work=Arbeit +Working\ out\ the\ fare=Fahrpreis wird berechnet +No\ couriers\ nearby\ right\ now=Gerade keine Fahrer in der N\u00e4he +1\ courier\ nearby,\ {0}\ away=1 Fahrer in der N\u00e4he, {0} entfernt +{0}\ couriers\ nearby,\ the\ closest\ {1}\ away={0} Fahrer in der N\u00e4he, der n\u00e4chste {1} entfernt +Request\ delivery=Fahrt anfragen +Cancel=Abbrechen +Payment=Zahlung +Delivery\ preferences=Fahrtw\u00fcnsche +Cash=Bar +Card=Karte +Women\ couriers\ only=Nur Fahrerinnen +Pet\ friendly=Haustiere willkommen +To\ {0}=Nach {0} +Nobody\ could\ take\ the\ delivery\ just\ now.\ You\ have\ not\ been\ charged.=Gerade konnte niemand die Fahrt \u00fcbernehmen. Dir wurde nichts berechnet. +Your\ courier\ had\ to\ cancel.\ You\ have\ not\ been\ charged.=Dein Fahrer musste stornieren. Dir wurde nichts berechnet. +The\ delivery\ was\ cancelled.=Die Fahrt wurde storniert. +OK=OK +Call=Anrufen +Calls\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Anrufe beim Fahrer sind auf diesem Server nicht verf\u00fcgbar +Message=Nachricht +Messages\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Nachrichten an den Fahrer sind auf diesem Server nicht verf\u00fcgbar +Share=Teilen +I\ am\ on\ my\ way\ to\ {0}\ with\ {1}\ in\ a\ {2}\ ({3}).=Ich bin unterwegs nach {0} mit {1} in einem {2} ({3}). +You\ have\ arrived=Du bist angekommen +How\ was\ the\ delivery\ with\ {0}?=Wie war die Fahrt mit {0}? +Thanks\ for\ the\ rating=Danke f\u00fcr die Bewertung +Not\ now=Nicht jetzt +Admin\ mode=Admin-Modus +Courier\ mode=Fahrermodus +Customer\ mode=Fahrgastmodus +Switch\ to=Wechseln zu +Riding=Mitfahren +Onboarding=Fahren +Administration=Verwaltung +Settings=Einstellungen +Sign\ out=Abmelden +No\ connection=Keine Verbindung +The\ server\ at\ {0}\ did\ not\ answer.=Der Server unter {0} hat nicht geantwortet. +Try\ again=Erneut versuchen +Server\ address=Serveradresse +Create\ an\ account=Konto erstellen +Name=Name +What\ couriers\ will\ call\ you=Wie Fahrer dich nennen +E-mail=E-Mail +Mobile\ number=Handynummer +Password=Passwort +At\ least\ 8\ characters=Mindestens 8 Zeichen +I\ want\ to\ drive=Ich m\u00f6chte fahren +Take\ delivery\ requests\ and\ earn=Fahrten annehmen und verdienen +Car=Auto +Blue\ Toyota\ Prius=Blauer Toyota Prius +Licence\ plate=Kennzeichen +Create\ account=Konto erstellen +No\ deliveries\ yet=Noch keine Fahrten +{0}\ with\ {1}={0} mit {1} +Server=Server +The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=Die Adresse des Morsel-Servers, mit dem diese App spricht. Eine \u00c4nderung meldet dich ab. +Address=Adresse +Save=Speichern +Account=Konto +Needed\ before\ your\ first\ delivery=Vor deiner ersten Fahrt erforderlich +Verify=Best\u00e4tigen +Any\ courier=Beliebiger Fahrer +Women\ only=Nur Frauen +Ask\ the\ courier\ to\ keep\ conversation\ short=Den Fahrer bitten, wenig zu reden +Only\ cars\ that\ take\ a\ wheelchair=Nur Autos, die einen Rollstuhl mitnehmen +Tell\ the\ courier\ an\ animal\ is\ coming=Dem Fahrer sagen, dass ein Tier mitkommt +Pay\ with=Bezahlen mit +Tip=Trinkgeld +None=Keins +Notifications=Benachrichtigungen +Delivery\ updates=Fahrt-Updates +Courier\ found,\ arriving,\ at\ the\ door=Fahrer gefunden, unterwegs, vor der T\u00fcr +Receipts=Belege +After\ every\ delivery=Nach jeder Fahrt +Offers=Angebote +Discounts\ and\ news=Rabatte und Neuigkeiten +Appearance=Darstellung +Language=Sprache +Theme=Design +System=System +Light=Hell +Dark=Dunkel +Distances=Entfernungen +Kilometres=Kilometer +Miles=Meilen +Privacy\ and\ safety=Datenschutz und Sicherheit +Share\ my\ trips=Meine Fahrten teilen +Emergency\ contact=Notfallkontakt +Their\ name=Name +Their\ phone\ number=Telefonnummer +About=Info +A\ sample\ delivery\ service\ built\ with\ Codename\ One=Ein Beispiel-Fahrdienst, gebaut mit Codename One +Terms\ of\ service=Nutzungsbedingungen +Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel ist ein Beispiel. Es werden keine echten Fahrten gebucht, keine echten Fahrer geschickt und es flie\u00dft kein Geld. +Privacy\ policy=Datenschutzerkl\u00e4rung +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Dein Standort wird w\u00e4hrend der Fahrt an den Server gesendet, bei dem du angemeldet bist, und an niemanden sonst. Einstellungen bleiben auf diesem Ger\u00e4t. +Sign\ in=Anmelden +Enter\ your\ e-mail\ and\ password.=Gib deine E-Mail und dein Passwort ein. +{0}\ ft={0} ft +{0}\ mi={0} mi +{0}\ m={0} m +{0}\ km={0} km +{0}\ min={0} Min. +Finding\ a\ courier=Fahrer wird gesucht +Courier\ on\ the\ way=Fahrer unterwegs +Courier\ at\ pickup=Fahrer am Abholort +Completed=Abgeschlossen +Cancelled\ by\ customer=Vom Fahrgast storniert +Cancelled\ by\ courier=Vom Fahrer storniert +No\ courier\ found=Kein Fahrer gefunden +Verify\ your\ number=Nummer best\u00e4tigen +We\ sent\ a\ six-digit\ code\ to\ your\ phone.=Wir haben einen sechsstelligen Code an dein Telefon gesendet. +We\ sent\ a\ six-digit\ code\ to\ {0}.=Wir haben einen sechsstelligen Code an {0} gesendet. +Code=Code +This\ is\ a\ development\ server,\ so\ no\ text\ was\ sent.\ Your\ code\ is\ {0}.=Dies ist ein Entwicklungsserver, daher wurde keine SMS gesendet. Dein Code ist {0}. +Send\ a\ new\ code=Neuen Code senden +A\ delivery\ when\ you\ need\ one,\ and\ a\ fair\ way\ to\ drive.=Eine Fahrt, wenn du sie brauchst, und eine faire Art zu fahren. + +# Onboarding applications, payments, the admin console and the account. +1\ application\ to\ review=1 Bewerbung zu pr\u00fcfen +{0}\ applications\ to\ review={0} Bewerbungen zu pr\u00fcfen +1\ flagged\ account=1 markiertes Konto +{0}\ flagged\ accounts={0} markierte Konten +7\ days=7 Tage +14\ days=14 Tage +30\ days=30 Tage +A\ car\ with\ four\ doors=Ein Auto mit vier T\u00fcren +A\ onboarding\ licence=Ein F\u00fchrerschein +A\ flag\ marks\ the\ account\ for\ the\ admins\ and\ changes\ nothing\ for\ its\ owner.\ A\ block\ signs\ it\ out\ and\ refuses\ it\ until\ lifted.=Eine Markierung macht die Admins auf das Konto aufmerksam und \u00e4ndert f\u00fcr den Inhaber nichts. Eine Sperre meldet es ab und weist es ab, bis sie aufgehoben wird. +A\ passport\ or\ an\ identity\ card=Ein Reisepass oder Personalausweis +A\ photo\ of\ you=Ein Foto von dir +A\ ramp\ or\ a\ lift,\ and\ room\ to\ secure\ the\ chair=Rampe oder Lift und Platz, um den Stuhl zu sichern +A\ delivery\ shows\ up\ here\ as\ soon\ as\ it\ is\ asked\ for.=Eine Fahrt erscheint hier, sobald sie angefragt wird. +A\ delivery\ shows\ up\ here\ when\ it\ is\ paid\ for.=Eine Fahrt erscheint hier, sobald sie bezahlt ist. +A\ sentence\ the\ record\ will\ keep=Ein Satz, der im Protokoll bleibt +About\ ten\ minutes=Etwa zehn Minuten +About\ you=\u00dcber dich +Accepted=Akzeptiert +Account\ ending\ {0}=Konto endet auf {0} +Account\ holder=Kontoinhaber +Account\ number=Kontonummer +Add\ a\ bank\ account=Bankkonto hinzuf\u00fcgen +Add\ a\ card=Karte hinzuf\u00fcgen +Add\ a\ tip=Trinkgeld geben +Added=Hinzugef\u00fcgt +Admins=Admins +All=Alle +All\ of\ it\ goes\ to\ your\ courier.=Alles geht an deinen Fahrer. +All\ of\ it\ goes\ to\ {0}.=Alles geht an {0}. +Another\ amount=Anderer Betrag +Applicant=Bewerber +Application=Bewerbung +Application\ received=Bewerbung eingegangen +Application\ sent\ back=Bewerbung zur\u00fcckgegeben +Application\ sent.\ We\ will\ be\ in\ touch.=Bewerbung gesendet. Wir melden uns. +Application\ under\ review=Bewerbung wird gepr\u00fcft +Applications=Bewerbungen +Approve=Genehmigen +Approved=Genehmigt +Approved\ to\ drive=Zum Fahren zugelassen +As\ it\ is\ written\ on\ your\ licence.\ Customers\ see\ only\ your\ first\ name.=Wie auf deinem F\u00fchrerschein. Fahrg\u00e4ste sehen nur deinen Vornamen. +Average\ fare=Durchschnittspreis +Average\ wait=Durchschnittliche Wartezeit +Back=Zur\u00fcck +Bank=Bank +Bank\ account=Bankkonto +Base\ fare=Grundpreis +Block=Sperren +Block\ account=Konto sperren +Blocked=Gesperrt +Blue=Blau +By\ kind\ of\ delivery=Nach Fahrtart +By\ outcome=Nach Ergebnis +By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ documents\ are\ your\ own.=Mit dem Absenden best\u00e4tigst du, dass alles stimmt und die Dokumente deine eigenen sind. +Calling\ is\ not\ available\ on\ this\ device=Anrufe sind auf diesem Ger\u00e4t nicht m\u00f6glich +Cancel\ this\ delivery=Diese Fahrt stornieren +Cancelled=Storniert +Cancelled\ by\ Morsel=Von Morsel storniert +Card\ number=Kartennummer +Cash\ out=Auszahlen +Change\ password=Passwort \u00e4ndern +Change\ the\ password\ you\ sign\ in\ with=\u00c4ndere das Passwort f\u00fcr die Anmeldung +Changes\ needed=\u00c4nderungen n\u00f6tig +Choose\ a\ delivery=Fahrt ausw\u00e4hlen +Choose\ an\ amount\ for\ the\ tip=W\u00e4hle einen Betrag f\u00fcr das Trinkgeld +Choose\ how\ to\ pay\ for\ this\ delivery.=W\u00e4hle, wie du diese Fahrt bezahlst. +Collect\ {0}\ in\ cash={0} in bar kassieren +Colour=Farbe +Comfort=Comfort +Comfort\ is\ a\ newer,\ roomier\ car.\ XL\ seats\ six\ or\ more.=Comfort ist ein neueres, ger\u00e4umigeres Auto. XL hat sechs oder mehr Pl\u00e4tze. +Commission,\ %=Provision, % +Contact=Kontakt +Continue=Weiter +Continue\ application=Bewerbung fortsetzen +Current\ password=Aktuelles Passwort +Date\ of\ birth=Geburtsdatum +Default=Standard +Delete\ account=Konto l\u00f6schen +Delete\ my\ account=Mein Konto l\u00f6schen +Delete\ your\ account?=Dein Konto l\u00f6schen? +Discount=Rabatt +Distance=Strecke +Documents=Dokumente +Documents\ checked=Dokumente gepr\u00fcft +Done=Fertig +Draft=Entwurf +Drive\ with\ Morsel=Mit Morsel fahren +Couriers=Fahrer +Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Fahrer bewerben sich nach der Registrierung: ein paar Angaben zu dir und deinem Auto und f\u00fcnf Fotos. Bis dahin kannst du mitfahren. +Couriers\ online=Fahrer online +Onboarding\ licence=F\u00fchrerschein +Earn\ on\ your\ own\ schedule=Verdiene nach deinem Zeitplan +Earned\ onboarding=Als Fahrer verdient +Every\ application\ has\ had\ its\ answer.=Jede Bewerbung wurde beantwortet. +Everyone=Alle +Expires=L\u00e4uft ab +Expires\ {0}=L\u00e4uft ab {0} +Fare=Fahrpreis +Finish\ your\ application=Schlie\u00dfe deine Bewerbung ab +Fix\ and\ resubmit=Korrigieren und erneut senden +Flag=Markieren +Flag\ account=Konto markieren +Flag\ removed=Markierung entfernt +Flagged=Markiert +Gender=Geschlecht +Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Ein letzter Blick. Bis zum Absenden kannst du alles \u00e4ndern. +Hi,\ this\ is\ your\ Morsel\ courier.=Hallo, hier ist dein Morsel-Fahrer. +Hi,\ this\ is\ your\ Morsel\ customer.=Hallo, hier ist dein Morsel-Fahrgast. +History=Verlauf +I\ have\ finished=Ich bin fertig +I\ want\ to=Ich m\u00f6chte +In\ good\ standing=In Ordnung +Insurance=Versicherung +Its\ number,\ its\ expiry\ date\ and\ a\ photo\ of\ it=Nummer, Ablaufdatum und ein Foto davon +Keep\ my\ account=Mein Konto behalten +Kind\ of\ delivery=Fahrtart +Legal\ name=Rechtlicher Name +Licence=F\u00fchrerschein +Licence\ expires=F\u00fchrerschein g\u00fcltig bis +Licence\ number=F\u00fchrerscheinnummer +Make=Marke +Man=Mann +Messages\ are\ not\ available\ on\ this\ device=Nachrichten sind auf diesem Ger\u00e4t nicht m\u00f6glich +Minimum\ fare=Mindestpreis +Missing=Fehlt +Model=Modell +Moderation=Moderation +Most\ of\ it\ is\ taking\ five\ photos=Das meiste sind f\u00fcnf Fotos +Multipliers=Multiplikatoren +Name\ an\ emergency\ contact\ in\ your\ profile\ first=Gib zuerst einen Notfallkontakt im Profil an +Name\ on\ the\ card=Name auf der Karte +Name,\ emergency\ contact,\ home\ and\ work=Name, Notfallkontakt, Zuhause und Arbeit +Needed=Fehlt noch +New\ password=Neues Passwort +New\ password,\ again=Neues Passwort, noch einmal +No=Nein +No\ payments\ yet=Noch keine Zahlungen +No\ deliveries\ here=Keine Fahrten hier +Nobody\ matches=Keine Treffer +Non-binary=Nicht-bin\u00e4r +Not\ provided=Nicht eingereicht +Not\ set=Nicht festgelegt +Not\ started=Nicht begonnen +Nothing\ has\ been\ done\ to\ this\ account.=Mit diesem Konto ist nichts geschehen. +Nothing\ has\ been\ paid\ out\ yet.=Es wurde noch nichts ausgezahlt. +Nothing\ here=Nichts hier +Open\ the\ secure\ page=Sichere Seite \u00f6ffnen +Paid=Bezahlt +Paid\ in\ cash=Bar bezahlt +Paid\ with=Bezahlt mit +Payment\ failed=Zahlung fehlgeschlagen +Payment\ pending=Zahlung ausstehend +Payments=Zahlungen +Payouts=Auszahlungen +People\ who\ asked\ to\ drive,\ and\ their\ papers=Wer fahren m\u00f6chte, mit Unterlagen +Per\ kilometre=Pro Kilometer +Per\ minute=Pro Minute +Photo\ ID=Lichtbildausweis +Photograph\ each\ one\ flat,\ with\ all\ four\ corners\ in\ the\ picture.=Fotografiere jedes flach, mit allen vier Ecken im Bild. +Photos\ added=Fotos hinzugef\u00fcgt +Pricing=Preise +Profile=Profil +Provided=Eingereicht +Rather\ not\ say=Keine Angabe +Rating=Bewertung +Read\ what\ was\ missing,\ put\ it\ right,\ and\ send\ the\ application\ again.=Lies, was fehlte, korrigiere es und sende die Bewerbung erneut. +Ready\ to\ cash\ out=Bereit zur Auszahlung +Reason=Grund +Receipt=Beleg +Refund=Erstatten +Refund\ this\ delivery=Diese Fahrt erstatten +Refunded=Erstattet +Registered\ and\ insured,\ with\ the\ papers\ to\ show\ it=Zugelassen und versichert, mit Nachweisen +Rejected=Abgelehnt +Remove\ flag=Markierung entfernen +Remove\ this\ card=Diese Karte entfernen +Request\ {0}={0} anfragen +Requests\ accepted=Angenommene Anfragen +Requests\ by\ hour=Anfragen pro Stunde +Retake=Neu aufnehmen +Review\ and\ submit=Pr\u00fcfen und absenden +Reviewed=Gepr\u00fcft +Reviewed\ by=Gepr\u00fcft von +Customers=Fahrg\u00e4ste +Customers\ who\ ask\ for\ a\ woman\ at\ the\ wheel\ are\ offered\ to\ couriers\ who\ say\ they\ are\ one.=Fahrg\u00e4ste, die eine Frau am Steuer w\u00fcnschen, werden Fahrerinnen angeboten, die das angeben. +Right\ now=Gerade jetzt +Roles\ changed=Rollen ge\u00e4ndert +Roles,\ flags,\ and\ blocking\ an\ account=Rollen, Markierungen und Kontosperren +Save\ card=Karte speichern +Save\ prices=Preise speichern +Saved\ places=Gespeicherte Orte +Say\ why,\ in\ a\ few\ words.=Sag in wenigen Worten, warum. +Search\ by\ name\ or\ e-mail=Nach Name oder E-Mail suchen +Seats\ for\ customers=Pl\u00e4tze f\u00fcr Fahrg\u00e4ste +Security\ code=Sicherheitscode +Send\ back=Zur\u00fcckgeben +Send\ tip=Trinkgeld senden +Saves\ {0}'s\ number\ with\ your\ account\ as\ your\ trip\ contact.\ No\ message\ is\ sent.=Speichert die Nummer von {0} in deinem Konto als Fahrtkontakt. Es wird keine Nachricht gesendet. +Sent\ back=Zur\u00fcckgegeben +Service\ fee=Servicegeb\u00fchr +Service\ fee,\ %=Servicegeb\u00fchr, % +Spent\ riding=F\u00fcr Fahrten ausgegeben +Standard=Standard +Start\ application=Bewerbung starten +Step\ {0}\ of\ {1}=Schritt {0} von {1} +Submit\ application=Bewerbung absenden +Submitted=Eingereicht +Surge,\ while\ demand\ is\ high=Aufschlag bei hoher Nachfrage +Takes\ a\ wheelchair=Rollstuhlgerecht +Tap\ a\ card\ to\ make\ it\ the\ one\ that\ pays\ unless\ you\ say\ otherwise.=Tippe auf eine Karte, damit sie zahlt, sofern du nichts anderes w\u00e4hlst. +Tap\ to\ open=Zum \u00d6ffnen tippen +Tell\ us\ about\ you\ and\ your\ car,\ add\ five\ photos,\ and\ we\ take\ it\ from\ there.=Erz\u00e4hl uns von dir und deinem Auto, f\u00fcge f\u00fcnf Fotos hinzu, den Rest machen wir. +Thank\ you.\ Your\ tip\ was\ sent.=Danke. Dein Trinkgeld wurde gesendet. +That\ is\ not\ a\ card\ number.\ Check\ the\ digits.=Das ist keine Kartennummer. Pr\u00fcfe die Ziffern. +That\ picture\ could\ not\ be\ read=Das Bild konnte nicht gelesen werden +That\ place\ could\ not\ be\ found.\ Choose\ it\ again.=Der Ort wurde nicht gefunden. W\u00e4hle ihn erneut. +The\ busy\ hours,\ in\ UTC=Die Sto\u00dfzeiten, in UTC +The\ card\ was\ declined.\ Choose\ another\ way\ to\ pay\ in\ your\ wallet.=Die Karte wurde abgelehnt. W\u00e4hle im Wallet eine andere Zahlungsart. +The\ fare,\ in\ {0}=Der Fahrpreis, in {0} +The\ fee\ is\ added\ to\ the\ fare\ and\ paid\ by\ the\ customer.\ The\ commission\ is\ kept\ from\ the\ fare;\ the\ rest\ is\ the\ courier's.=Die Geb\u00fchr kommt zum Fahrpreis hinzu und wird vom Fahrgast bezahlt. Die Provision wird vom Fahrpreis einbehalten; der Rest geh\u00f6rt dem Fahrer. +The\ last\ two\ weeks=Die letzten zwei Wochen +The\ new\ prices\ are\ in\ force=Die neuen Preise gelten +The\ number\ is\ on\ the\ front\ of\ the\ card.=Die Nummer steht auf der Vorderseite der Karte. +The\ page\ with\ the\ policy's\ dates\ on\ it=Die Seite mit den Daten der Police +The\ paper\ that\ says\ the\ car\ is\ yours=Das Papier, das dich als Halter ausweist +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=Die Seite des Zahlungsanbieters \u00f6ffnet sich im Browser. Morsel sieht die Nummer nie. Komm hierher zur\u00fcck, wenn du fertig bist. +The\ delivery\ was\ refunded=Die Fahrt wurde erstattet +Refund\ pending=Erstattung ausstehend +Refund\ failed=Erstattung fehlgeschlagen +The\ refund\ is\ pending=Die Erstattung ist ausstehend +The\ customer\ and\ the\ courier\ are\ both\ told,\ and\ nobody\ is\ charged.=Fahrgast und Fahrer werden informiert, niemand wird belastet. +The\ security\ code\ is\ the\ three\ or\ four\ digits\ on\ the\ card.=Der Sicherheitscode sind die drei oder vier Ziffern auf der Karte. +The\ service's\ share=Der Anteil des Dienstes +The\ side\ with\ your\ photo\ on\ it=Die Seite mit deinem Foto +The\ two\ new\ passwords\ are\ not\ the\ same.=Die beiden neuen Passw\u00f6rter stimmen nicht \u00fcberein. +This\ device\ cannot\ prepare\ a\ picture\ to\ send=Dieses Ger\u00e4t kann kein Bild zum Senden vorbereiten +This\ month=Diesen Monat +This\ picture\ could\ not\ be\ opened=Dieses Bild konnte nicht ge\u00f6ffnet werden +This\ screen\ becomes\ your\ courier's\ home\ the\ moment\ you\ are\ approved=Dieser Bildschirm wird dein Fahrer-Start, sobald du zugelassen bist +This\ server\ takes\ no\ real\ payments.\ Use\ the\ test\ card\ 4242\ 4242\ 4242\ 4242\ with\ any\ date\ ahead\ and\ any\ three\ digits.=Dieser Server nimmt keine echten Zahlungen an. Nutze die Testkarte 4242 4242 4242 4242 mit einem beliebigen k\u00fcnftigen Datum und drei beliebigen Ziffern. +Time=Zeit +Tip\ offered\ first=Zuerst angebotenes Trinkgeld +Tips=Trinkgelder +Top\ couriers=Top-Fahrer +Total=Gesamt +Trends=Trends +Trip=Fahrt +Type\ the\ account\ number.=Gib die Kontonummer ein. +Unblock=Entsperren +Unblocked=Entsperrt +Under\ review=In Pr\u00fcfung +Usually\ within\ two\ working\ days=Meist innerhalb von zwei Werktagen +Vehicle=Fahrzeug +Vehicle\ registration=Fahrzeugschein +View\ receipt=Beleg ansehen +Waiting=Wartend +Wallet=Wallet +We\ could\ not\ approve\ this\ yet=Wir konnten das noch nicht genehmigen +We\ have\ everything\ we\ asked\ for.\ Someone\ on\ our\ team\ is\ reading\ it.=Wir haben alles, was wir brauchen. Jemand aus dem Team liest es gerade. +What\ a\ delivery\ costs,\ and\ the\ service's\ share=Was eine Fahrt kostet, und der Anteil des Dienstes +What\ happens\ next=Wie es weitergeht +What\ the\ reviewer\ said=Was der Pr\u00fcfer sagte +What\ was\ charged,\ and\ refunds=Was berechnet wurde, und Erstattungen +What\ you\ will\ need=Was du brauchst +Where\ your\ earnings\ are\ sent=Wohin deine Einnahmen gehen +Why\ does\ {0}\ need\ the\ admins'\ attention?=Warum braucht {0} die Aufmerksamkeit der Admins? +Woman=Frau +Write\ the\ expiry\ date\ as\ month\ and\ year\:\ 04/28.=Gib das Ablaufdatum als Monat und Jahr ein: 04/28. +Year=Baujahr +Yes=Ja +You=Du +You\ go\ online=Du gehst online +Your\ application=Deine Bewerbung +Your\ car=Dein Auto +Your\ card\ goes\ straight\ to\ the\ bank=Deine Karte geht direkt an die Bank +Your\ cards,\ and\ which\ of\ them\ pays\ by\ default=Deine Karten und welche standardm\u00e4\u00dfig zahlt +Your\ documents=Deine Dokumente +Your\ courier\ cannot\ be\ messaged\ right\ now=Dein Fahrer ist gerade nicht per Nachricht erreichbar +Your\ face,\ in\ good\ light,\ no\ sunglasses=Dein Gesicht, bei gutem Licht, ohne Sonnenbrille +Your\ licence=Dein F\u00fchrerschein +Your\ password\ was\ changed=Dein Passwort wurde ge\u00e4ndert +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Your\ settings\ are\ kept\ with\ your\ account\ on\ that\ server.=Dein Standort wird w\u00e4hrend der Fahrt an den Server gesendet, bei dem du angemeldet bist, und an niemanden sonst. Deine Einstellungen liegen bei deinem Konto auf diesem Server. +Your\ customer\ cannot\ be\ messaged\ right\ now=Dein Fahrgast ist gerade nicht per Nachricht erreichbar +Your\ sign-in,\ your\ profile,\ your\ saved\ cards\ and\ your\ settings\ are\ removed\ for\ good.\ Deliveries\ you\ took\ stay\ in\ the\ service's\ records\ without\ your\ name.=Deine Anmeldung, dein Profil, deine gespeicherten Karten und deine Einstellungen werden endg\u00fcltig entfernt. Deine Fahrten bleiben ohne deinen Namen in den Aufzeichnungen des Dienstes. +{0}\ away={0} entfernt +{0}\ can\ now\ drive={0} darf jetzt fahren +{0}\ completed,\ {1}\ cancelled={0} abgeschlossen, {1} storniert +{0}\ days\ ago=vor {0} Tagen +{0}\ goes\ back\ to\ the\ card\ it\ came\ from.={0} geht zur\u00fcck auf die Karte, von der es kam. +{0}\ is\ on\ its\ way\ to\ your\ bank={0} ist auf dem Weg zu deiner Bank +{0}\ is\ signed\ out\ everywhere\ and\ cannot\ sign\ in\ again\ until\ the\ block\ is\ lifted.={0} wird \u00fcberall abgemeldet und kann sich erst wieder anmelden, wenn die Sperre aufgehoben ist. +{0}\ new\ accounts={0} neue Konten +{0}\ of\ 5\ documents\ added.\ Pick\ up\ where\ you\ left\ off.={0} von 5 Dokumenten hinzugef\u00fcgt. Mach dort weiter, wo du aufgeh\u00f6rt hast. +{0}\ deliveries={0} Fahrten +{0}\ s={0} s +{0}\ sees\ what\ you\ write,\ and\ can\ apply\ again\ once\ it\ is\ put\ right.={0} sieht, was du schreibst, und kann sich erneut bewerben, sobald es korrigiert ist. +Fill\ in\ everything\ on\ this\ step\ to\ continue.=F\u00fclle in diesem Schritt alles aus, um fortzufahren. +None\ nearby=Keiner in der N\u00e4he +Pay\ the\ courier\ directly=Zahle direkt beim Fahrer +Camera=Kamera +Gallery=Galerie +Fares\ collected=Eingenommene Fahrpreise +Takes\ pets=Nimmt Haustiere mit +Customers\ travelling\ with\ a\ pet\ are\ offered\ to\ you=Fahrg\u00e4ste mit Haustier werden dir angeboten +Go=Gehe zu +Nothing\ selected=Nichts ausgew\u00e4hlt +Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=W\u00e4hlen Sie eine Zeile aus der Liste, um sie hier zu sehen. +Usage\ statistics=Nutzungsstatistiken +Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Teile, welche Bildschirme und Funktionen genutzt werden. Niemals deinen Namen, deine Orte oder deine Zahlungsdaten. +Help\ improve\ Morsel=Hilf mit, Morsel zu verbessern +Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel kann z\u00e4hlen, welche Bildschirme ge\u00f6ffnet und welche Funktionen genutzt werden, damit wir sehen, was funktioniert, und beheben k\u00f6nnen, was nicht funktioniert. +No\ personal\ details=Keine pers\u00f6nlichen Daten +Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Niemals dein Name, deine E-Mail-Adresse, deine Telefonnummer, deine Orte oder deine Kartendaten +Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Jederzeit in den Einstellungen ein- oder ausschalten +Share\ usage\ statistics=Nutzungsstatistiken teilen +With\ usage\ statistics\ turned\ on,\ the\ app\ also\ counts\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ and\ reports\ that\ to\ Codename\ One's\ analytics\ service\ under\ a\ made-up\ id\ for\ this\ device.\ Your\ name,\ e-mail,\ phone\ number,\ places\ and\ card\ details\ are\ never\ part\ of\ it,\ and\ turning\ it\ off\ in\ Settings\ stops\ it.=Sind die Nutzungsstatistiken eingeschaltet, z\u00e4hlt die App au\u00dferdem, welche Bildschirme ge\u00f6ffnet und welche Funktionen genutzt werden, und meldet das unter einer f\u00fcr dieses Ger\u00e4t erfundenen Kennung an den Analysedienst von Codename One. Dein Name, deine E-Mail-Adresse, deine Telefonnummer, deine Orte und deine Kartendaten geh\u00f6ren nie dazu, und wenn du sie in den Einstellungen ausschaltest, h\u00f6rt es auf. +Location\ is\ turned\ off.\ Try\ again=Standort ist deaktiviert. Erneut versuchen +Can't\ find\ your\ location.\ Try\ again=Standort nicht gefunden. Erneut versuchen +Still\ looking\ for\ your\ location.\ Try\ again=Standort wird noch gesucht. Erneut versuchen +Looking\ for\ your\ location...=Standort wird gesucht... diff --git a/scripts/morsel/common/src/main/l10n/Bundle_en.properties b/scripts/morsel/common/src/main/l10n/Bundle_en.properties new file mode 100644 index 00000000000..c9adac326b1 --- /dev/null +++ b/scripts/morsel/common/src/main/l10n/Bundle_en.properties @@ -0,0 +1,532 @@ +# Morsel in English. The key of every line is the English text the code +# names; see ui/Lang.java for how a line is found and how to add one. +@rtl=false +language.name=English + +Admin=Admin +Dashboard=Dashboard +Live\ map=Live map +Deliveries=Deliveries +People=People +Operations=Operations +Morsel\ administration=Morsel administration +Today=Today +Manage=Manage +Where\ every\ car\ online\ is=Where every car online is +The\ most\ recent,\ in\ every\ state=The most recent, in every state +Roles,\ and\ suspending\ an\ account=Roles, and suspending an account +Deliveries\ today=Deliveries today +Under\ way=Under way +Online=Online +Offline=Offline +Fares=Fares +Cars\ move\ as\ their\ couriers\ report\ in.=Cars move as their couriers report in. +No\ cars\ online=No cars online +1\ car\ online=1 car online +{0}\ cars\ online={0} cars online +Suspended=Suspended +Courier=Courier +Customer=Customer +Verified=Verified +Not\ verified=Not verified +May\ go\ online\ and\ take\ deliveries=May go online and take deliveries +May\ see\ and\ change\ everything\ here=May see and change everything here +Signed\ out,\ and\ refused\ until\ lifted=Signed out, and refused until lifted +Access=Access +Drive=Drive +Your\ trips=Your trips +Earnings=Earnings +The\ request\ went\ to\ another\ courier=The request went to another courier +The\ customer\ cancelled=The customer cancelled +Today,\ {0}\ trips=Today, {0} trips +Looking\ for\ deliveries=Looking for deliveries +You\ are\ offline=You are offline +Trip\ complete\:\ {0}=Trip complete: {0} +Stay\ close\ to\ busy\ streets;\ requests\ come\ to\ the\ nearest\ car.=Stay close to busy streets; requests come to the nearest car. +Go\ online\ to\ start\ taking\ deliveries.=Go online to start taking deliveries. +GO=GO +This\ week=This week +Trips=Trips +All\ time=All time +Go\ offline=Go offline +Go\ online=Go online +Delivery\ request=Delivery request +{0}\ away,\ {1}={0} away, {1} +Accept=Accept +Decline=Decline +Pick\ up\ {0}=Pick up {0} +Waiting\ for\ {0}=Waiting for {0} +On\ the\ trip=On the trip +Drop-off=Drop-off +Pickup=Pickup +I\ have\ arrived=I have arrived +Start\ trip=Start trip +Complete\ trip=Complete trip +Cancel\ delivery=Cancel delivery +Quiet\ delivery=Quiet delivery +Accessible\ vehicle=Accessible vehicle +Could\ not\ reach\ the\ server.\ Check\ your\ connection.=Could not reach the server. Check your connection. +Please\ sign\ in\ again.=Please sign in again. +Your\ account\ is\ not\ allowed\ to\ do\ that.=Your account is not allowed to do that. +Too\ many\ attempts.\ Wait\ a\ moment\ and\ try\ again.=Too many attempts. Wait a moment and try again. +Too\ many\ attempts.\ Wait\ a\ minute\ and\ try\ again.=Too many attempts. Wait a minute and try again. +Something\ went\ wrong\ ({0}).\ Try\ again.=Something went wrong ({0}). Try again. +That\ e-mail\ and\ password\ do\ not\ match\ an\ account.=That e-mail and password do not match an account. +Signing\ in\ failed.\ Try\ again.=Signing in failed. Try again. +Where\ to?=Where to? +Destination=Destination +An\ address\ or\ a\ place=An address or a place +Nothing\ found=Nothing found +Current\ location=Current location +Delivery=Delivery +Your\ deliveries=Your deliveries +Your\ courier\ is\ here=Your courier is here +{0}\ to\ destination={0} to destination +Pickup\ in\ {0}=Pickup in {0} +Hello,\ {0}=Hello, {0} +Where\ are\ you\ going?=Where are you going? +Verify\ your\ phone\ number\ to\ request\ a\ delivery.=Verify your phone number to request a delivery. +Verify\ now=Verify now +Home=Home +Work=Work +Working\ out\ the\ fare=Working out the fare +No\ couriers\ nearby\ right\ now=No couriers nearby right now +1\ courier\ nearby,\ {0}\ away=1 courier nearby, {0} away +{0}\ couriers\ nearby,\ the\ closest\ {1}\ away={0} couriers nearby, the closest {1} away +Request\ delivery=Request delivery +Cancel=Cancel +Payment=Payment +Delivery\ preferences=Delivery preferences +Cash=Cash +Card=Card +Women\ couriers\ only=Women couriers only +Pet\ friendly=Pet friendly +To\ {0}=To {0} +Nobody\ could\ take\ the\ delivery\ just\ now.\ You\ have\ not\ been\ charged.=Nobody could take the delivery just now. You have not been charged. +Your\ courier\ had\ to\ cancel.\ You\ have\ not\ been\ charged.=Your courier had to cancel. You have not been charged. +The\ delivery\ was\ cancelled.=The delivery was cancelled. +OK=OK +Call=Call +Calls\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Calls to your courier are not available on this server +Message=Message +Messages\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Messages to your courier are not available on this server +Share=Share +I\ am\ on\ my\ way\ to\ {0}\ with\ {1}\ in\ a\ {2}\ ({3}).=I am on my way to {0} with {1} in a {2} ({3}). +You\ have\ arrived=You have arrived +How\ was\ the\ delivery\ with\ {0}?=How was the delivery with {0}? +Thanks\ for\ the\ rating=Thanks for the rating +Not\ now=Not now +Admin\ mode=Admin mode +Courier\ mode=Courier mode +Customer\ mode=Customer mode +Switch\ to=Switch to +Riding=Riding +Onboarding=Onboarding +Administration=Administration +Settings=Settings +Sign\ out=Sign out +No\ connection=No connection +The\ server\ at\ {0}\ did\ not\ answer.=The server at {0} did not answer. +Try\ again=Try again +Server\ address=Server address +Create\ an\ account=Create an account +Name=Name +What\ couriers\ will\ call\ you=What couriers will call you +E-mail=E-mail +Mobile\ number=Mobile number +Password=Password +At\ least\ 8\ characters=At least 8 characters +I\ want\ to\ drive=I want to drive +Take\ delivery\ requests\ and\ earn=Take delivery requests and earn +Car=Car +Blue\ Toyota\ Prius=Blue Toyota Prius +Licence\ plate=Licence plate +Create\ account=Create account +No\ deliveries\ yet=No deliveries yet +{0}\ with\ {1}={0} with {1} +Server=Server +The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=The address of the Morsel server this app talks to. Changing it signs you out. +Address=Address +Save=Save +Account=Account +Needed\ before\ your\ first\ delivery=Needed before your first delivery +Verify=Verify +Any\ courier=Any courier +Women\ only=Women only +Ask\ the\ courier\ to\ keep\ conversation\ short=Ask the courier to keep conversation short +Only\ cars\ that\ take\ a\ wheelchair=Only cars that take a wheelchair +Tell\ the\ courier\ an\ animal\ is\ coming=Tell the courier an animal is coming +Pay\ with=Pay with +Tip=Tip +None=None +Notifications=Notifications +Delivery\ updates=Delivery updates +Courier\ found,\ arriving,\ at\ the\ door=Courier found, arriving, at the door +Receipts=Receipts +After\ every\ delivery=After every delivery +Offers=Offers +Discounts\ and\ news=Discounts and news +Appearance=Appearance +Language=Language +Theme=Theme +System=System +Light=Light +Dark=Dark +Distances=Distances +Kilometres=Kilometres +Miles=Miles +Privacy\ and\ safety=Privacy and safety +Share\ my\ trips=Share my trips +Emergency\ contact=Emergency contact +Their\ name=Their name +Their\ phone\ number=Their phone number +About=About +A\ sample\ delivery\ service\ built\ with\ Codename\ One=A sample delivery service built with Codename One +Terms\ of\ service=Terms of service +Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel is a sample. No real deliveries are booked, no real couriers are sent and no money changes hands. +Privacy\ policy=Privacy policy +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Your position is sent to the server you signed in to while you delivery or drive, and to nobody else. Settings are kept on this device. +Sign\ in=Sign in +Enter\ your\ e-mail\ and\ password.=Enter your e-mail and password. +{0}\ ft={0} ft +{0}\ mi={0} mi +{0}\ m={0} m +{0}\ km={0} km +{0}\ min={0} min +Finding\ a\ courier=Finding a courier +Courier\ on\ the\ way=Courier on the way +Courier\ at\ pickup=Courier at pickup +Completed=Completed +Cancelled\ by\ customer=Cancelled by customer +Cancelled\ by\ courier=Cancelled by courier +No\ courier\ found=No courier found +Verify\ your\ number=Verify your number +We\ sent\ a\ six-digit\ code\ to\ your\ phone.=We sent a six-digit code to your phone. +We\ sent\ a\ six-digit\ code\ to\ {0}.=We sent a six-digit code to {0}. +Code=Code +This\ is\ a\ development\ server,\ so\ no\ text\ was\ sent.\ Your\ code\ is\ {0}.=This is a development server, so no text was sent. Your code is {0}. +Send\ a\ new\ code=Send a new code +A\ delivery\ when\ you\ need\ one,\ and\ a\ fair\ way\ to\ drive.=A delivery when you need one, and a fair way to drive. + +# Onboarding applications, payments, the admin console and the account. +1\ application\ to\ review=1 application to review +{0}\ applications\ to\ review={0} applications to review +1\ flagged\ account=1 flagged account +{0}\ flagged\ accounts={0} flagged accounts +7\ days=7 days +14\ days=14 days +30\ days=30 days +A\ car\ with\ four\ doors=A car with four doors +A\ onboarding\ licence=A onboarding licence +A\ flag\ marks\ the\ account\ for\ the\ admins\ and\ changes\ nothing\ for\ its\ owner.\ A\ block\ signs\ it\ out\ and\ refuses\ it\ until\ lifted.=A flag marks the account for the admins and changes nothing for its owner. A block signs it out and refuses it until lifted. +A\ passport\ or\ an\ identity\ card=A passport or an identity card +A\ photo\ of\ you=A photo of you +A\ ramp\ or\ a\ lift,\ and\ room\ to\ secure\ the\ chair=A ramp or a lift, and room to secure the chair +A\ delivery\ shows\ up\ here\ as\ soon\ as\ it\ is\ asked\ for.=A delivery shows up here as soon as it is asked for. +A\ delivery\ shows\ up\ here\ when\ it\ is\ paid\ for.=A delivery shows up here when it is paid for. +A\ sentence\ the\ record\ will\ keep=A sentence the record will keep +About\ ten\ minutes=About ten minutes +About\ you=About you +Accepted=Accepted +Account\ ending\ {0}=Account ending {0} +Account\ holder=Account holder +Account\ number=Account number +Add\ a\ bank\ account=Add a bank account +Add\ a\ card=Add a card +Add\ a\ tip=Add a tip +Added=Added +Admins=Admins +All=All +All\ of\ it\ goes\ to\ your\ courier.=All of it goes to your courier. +All\ of\ it\ goes\ to\ {0}.=All of it goes to {0}. +Another\ amount=Another amount +Applicant=Applicant +Application=Application +Application\ received=Application received +Application\ sent\ back=Application sent back +Application\ sent.\ We\ will\ be\ in\ touch.=Application sent. We will be in touch. +Application\ under\ review=Application under review +Applications=Applications +Approve=Approve +Approved=Approved +Approved\ to\ drive=Approved to drive +As\ it\ is\ written\ on\ your\ licence.\ Customers\ see\ only\ your\ first\ name.=As it is written on your licence. Customers see only your first name. +Average\ fare=Average fare +Average\ wait=Average wait +Back=Back +Bank=Bank +Bank\ account=Bank account +Base\ fare=Base fare +Block=Block +Block\ account=Block account +Blocked=Blocked +Blue=Blue +By\ kind\ of\ delivery=By kind of delivery +By\ outcome=By outcome +By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ documents\ are\ your\ own.=By submitting you confirm that all of this is true and that the documents are your own. +Calling\ is\ not\ available\ on\ this\ device=Calling is not available on this device +Cancel\ this\ delivery=Cancel this delivery +Cancelled=Cancelled +Cancelled\ by\ Morsel=Cancelled by Morsel +Card\ number=Card number +Cash\ out=Cash out +Change\ password=Change password +Change\ the\ password\ you\ sign\ in\ with=Change the password you sign in with +Changes\ needed=Changes needed +Choose\ a\ delivery=Choose a delivery +Choose\ an\ amount\ for\ the\ tip=Choose an amount for the tip +Choose\ how\ to\ pay\ for\ this\ delivery.=Choose how to pay for this delivery. +Collect\ {0}\ in\ cash=Collect {0} in cash +Colour=Colour +Comfort=Comfort +Comfort\ is\ a\ newer,\ roomier\ car.\ XL\ seats\ six\ or\ more.=Comfort is a newer, roomier car. XL seats six or more. +Commission,\ %=Commission, % +Contact=Contact +Continue=Continue +Continue\ application=Continue application +Current\ password=Current password +Date\ of\ birth=Date of birth +Default=Default +Delete\ account=Delete account +Delete\ my\ account=Delete my account +Delete\ your\ account?=Delete your account? +Discount=Discount +Distance=Distance +Documents=Documents +Documents\ checked=Documents checked +Done=Done +Draft=Draft +Drive\ with\ Morsel=Drive with Morsel +Couriers=Couriers +Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Couriers apply after signing up: a few details about you and your car, and five photos. You can delivery in the meantime. +Couriers\ online=Couriers online +Onboarding\ licence=Onboarding licence +Earn\ on\ your\ own\ schedule=Earn on your own schedule +Earned\ onboarding=Earned onboarding +Every\ application\ has\ had\ its\ answer.=Every application has had its answer. +Everyone=Everyone +Expires=Expires +Expires\ {0}=Expires {0} +Fare=Fare +Finish\ your\ application=Finish your application +Fix\ and\ resubmit=Fix and resubmit +Flag=Flag +Flag\ account=Flag account +Flag\ removed=Flag removed +Flagged=Flagged +Gender=Gender +Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Have a last look. You can change anything until it is handed in. +Hi,\ this\ is\ your\ Morsel\ courier.=Hi, this is your Morsel courier. +Hi,\ this\ is\ your\ Morsel\ customer.=Hi, this is your Morsel customer. +History=History +I\ have\ finished=I have finished +I\ want\ to=I want to +In\ good\ standing=In good standing +Insurance=Insurance +Its\ number,\ its\ expiry\ date\ and\ a\ photo\ of\ it=Its number, its expiry date and a photo of it +Keep\ my\ account=Keep my account +Kind\ of\ delivery=Kind of delivery +Legal\ name=Legal name +Licence=Licence +Licence\ expires=Licence expires +Licence\ number=Licence number +Make=Make +Man=Man +Messages\ are\ not\ available\ on\ this\ device=Messages are not available on this device +Minimum\ fare=Minimum fare +Missing=Missing +Model=Model +Moderation=Moderation +Most\ of\ it\ is\ taking\ five\ photos=Most of it is taking five photos +Multipliers=Multipliers +Name\ an\ emergency\ contact\ in\ your\ profile\ first=Name an emergency contact in your profile first +Name\ on\ the\ card=Name on the card +Name,\ emergency\ contact,\ home\ and\ work=Name, emergency contact, home and work +Needed=Needed +New\ password=New password +New\ password,\ again=New password, again +No=No +No\ payments\ yet=No payments yet +No\ deliveries\ here=No deliveries here +Nobody\ matches=Nobody matches +Non-binary=Non-binary +Not\ provided=Not provided +Not\ set=Not set +Not\ started=Not started +Nothing\ has\ been\ done\ to\ this\ account.=Nothing has been done to this account. +Nothing\ has\ been\ paid\ out\ yet.=Nothing has been paid out yet. +Nothing\ here=Nothing here +Open\ the\ secure\ page=Open the secure page +Paid=Paid +Paid\ in\ cash=Paid in cash +Paid\ with=Paid with +Payment\ failed=Payment failed +Payment\ pending=Payment pending +Payments=Payments +Payouts=Payouts +People\ who\ asked\ to\ drive,\ and\ their\ papers=People who asked to drive, and their papers +Per\ kilometre=Per kilometre +Per\ minute=Per minute +Photo\ ID=Photo ID +Photograph\ each\ one\ flat,\ with\ all\ four\ corners\ in\ the\ picture.=Photograph each one flat, with all four corners in the picture. +Photos\ added=Photos added +Pricing=Pricing +Profile=Profile +Provided=Provided +Rather\ not\ say=Rather not say +Rating=Rating +Read\ what\ was\ missing,\ put\ it\ right,\ and\ send\ the\ application\ again.=Read what was missing, put it right, and send the application again. +Ready\ to\ cash\ out=Ready to cash out +Reason=Reason +Receipt=Receipt +Refund=Refund +Refund\ this\ delivery=Refund this delivery +Refunded=Refunded +Registered\ and\ insured,\ with\ the\ papers\ to\ show\ it=Registered and insured, with the papers to show it +Rejected=Rejected +Remove\ flag=Remove flag +Remove\ this\ card=Remove this card +Request\ {0}=Request {0} +Requests\ accepted=Requests accepted +Requests\ by\ hour=Requests by hour +Retake=Retake +Review\ and\ submit=Review and submit +Reviewed=Reviewed +Reviewed\ by=Reviewed by +Customers=Customers +Customers\ who\ ask\ for\ a\ woman\ at\ the\ wheel\ are\ offered\ to\ couriers\ who\ say\ they\ are\ one.=Customers who ask for a woman at the wheel are offered to couriers who say they are one. +Right\ now=Right now +Roles\ changed=Roles changed +Roles,\ flags,\ and\ blocking\ an\ account=Roles, flags, and blocking an account +Save\ card=Save card +Save\ prices=Save prices +Saved\ places=Saved places +Say\ why,\ in\ a\ few\ words.=Say why, in a few words. +Search\ by\ name\ or\ e-mail=Search by name or e-mail +Seats\ for\ customers=Seats for customers +Security\ code=Security code +Send\ back=Send back +Send\ tip=Send tip +Saves\ {0}'s\ number\ with\ your\ account\ as\ your\ trip\ contact.\ No\ message\ is\ sent.=Saves {0}'s number with your account as your trip contact. No message is sent. +Sent\ back=Sent back +Service\ fee=Service fee +Service\ fee,\ %=Service fee, % +Spent\ riding=Spent riding +Standard=Standard +Start\ application=Start application +Step\ {0}\ of\ {1}=Step {0} of {1} +Submit\ application=Submit application +Submitted=Submitted +Surge,\ while\ demand\ is\ high=Surge, while demand is high +Takes\ a\ wheelchair=Takes a wheelchair +Tap\ a\ card\ to\ make\ it\ the\ one\ that\ pays\ unless\ you\ say\ otherwise.=Tap a card to make it the one that pays unless you say otherwise. +Tap\ to\ open=Tap to open +Tell\ us\ about\ you\ and\ your\ car,\ add\ five\ photos,\ and\ we\ take\ it\ from\ there.=Tell us about you and your car, add five photos, and we take it from there. +Thank\ you.\ Your\ tip\ was\ sent.=Thank you. Your tip was sent. +That\ is\ not\ a\ card\ number.\ Check\ the\ digits.=That is not a card number. Check the digits. +That\ picture\ could\ not\ be\ read=That picture could not be read +That\ place\ could\ not\ be\ found.\ Choose\ it\ again.=That place could not be found. Choose it again. +The\ busy\ hours,\ in\ UTC=The busy hours, in UTC +The\ card\ was\ declined.\ Choose\ another\ way\ to\ pay\ in\ your\ wallet.=The card was declined. Choose another way to pay in your wallet. +The\ fare,\ in\ {0}=The fare, in {0} +The\ fee\ is\ added\ to\ the\ fare\ and\ paid\ by\ the\ customer.\ The\ commission\ is\ kept\ from\ the\ fare;\ the\ rest\ is\ the\ courier's.=The fee is added to the fare and paid by the customer. The commission is kept from the fare; the rest is the courier's. +The\ last\ two\ weeks=The last two weeks +The\ new\ prices\ are\ in\ force=The new prices are in force +The\ number\ is\ on\ the\ front\ of\ the\ card.=The number is on the front of the card. +The\ page\ with\ the\ policy's\ dates\ on\ it=The page with the policy's dates on it +The\ paper\ that\ says\ the\ car\ is\ yours=The paper that says the car is yours +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=The payment provider's own page opens in your browser. Morsel never sees the number. Come back here when you have finished. +The\ delivery\ was\ refunded=The delivery was refunded +Refund\ pending=Refund pending +Refund\ failed=Refund failed +The\ refund\ is\ pending=The refund is pending +The\ customer\ and\ the\ courier\ are\ both\ told,\ and\ nobody\ is\ charged.=The customer and the courier are both told, and nobody is charged. +The\ security\ code\ is\ the\ three\ or\ four\ digits\ on\ the\ card.=The security code is the three or four digits on the card. +The\ service's\ share=The service's share +The\ side\ with\ your\ photo\ on\ it=The side with your photo on it +The\ two\ new\ passwords\ are\ not\ the\ same.=The two new passwords are not the same. +This\ device\ cannot\ prepare\ a\ picture\ to\ send=This device cannot prepare a picture to send +This\ month=This month +This\ picture\ could\ not\ be\ opened=This picture could not be opened +This\ screen\ becomes\ your\ courier's\ home\ the\ moment\ you\ are\ approved=This screen becomes your courier's home the moment you are approved +This\ server\ takes\ no\ real\ payments.\ Use\ the\ test\ card\ 4242\ 4242\ 4242\ 4242\ with\ any\ date\ ahead\ and\ any\ three\ digits.=This server takes no real payments. Use the test card 4242 4242 4242 4242 with any date ahead and any three digits. +Time=Time +Tip\ offered\ first=Tip offered first +Tips=Tips +Top\ couriers=Top couriers +Total=Total +Trends=Trends +Trip=Trip +Type\ the\ account\ number.=Type the account number. +Unblock=Unblock +Unblocked=Unblocked +Under\ review=Under review +Usually\ within\ two\ working\ days=Usually within two working days +Vehicle=Vehicle +Vehicle\ registration=Vehicle registration +View\ receipt=View receipt +Waiting=Waiting +Wallet=Wallet +We\ could\ not\ approve\ this\ yet=We could not approve this yet +We\ have\ everything\ we\ asked\ for.\ Someone\ on\ our\ team\ is\ reading\ it.=We have everything we asked for. Someone on our team is reading it. +What\ a\ delivery\ costs,\ and\ the\ service's\ share=What a delivery costs, and the service's share +What\ happens\ next=What happens next +What\ the\ reviewer\ said=What the reviewer said +What\ was\ charged,\ and\ refunds=What was charged, and refunds +What\ you\ will\ need=What you will need +Where\ your\ earnings\ are\ sent=Where your earnings are sent +Why\ does\ {0}\ need\ the\ admins'\ attention?=Why does {0} need the admins' attention? +Woman=Woman +Write\ the\ expiry\ date\ as\ month\ and\ year\:\ 04/28.=Write the expiry date as month and year: 04/28. +Year=Year +Yes=Yes +You=You +You\ go\ online=You go online +Your\ application=Your application +Your\ car=Your car +Your\ card\ goes\ straight\ to\ the\ bank=Your card goes straight to the bank +Your\ cards,\ and\ which\ of\ them\ pays\ by\ default=Your cards, and which of them pays by default +Your\ documents=Your documents +Your\ courier\ cannot\ be\ messaged\ right\ now=Your courier cannot be messaged right now +Your\ face,\ in\ good\ light,\ no\ sunglasses=Your face, in good light, no sunglasses +Your\ licence=Your licence +Your\ password\ was\ changed=Your password was changed +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Your\ settings\ are\ kept\ with\ your\ account\ on\ that\ server.=Your position is sent to the server you signed in to while you delivery or drive, and to nobody else. Your settings are kept with your account on that server. +Your\ customer\ cannot\ be\ messaged\ right\ now=Your customer cannot be messaged right now +Your\ sign-in,\ your\ profile,\ your\ saved\ cards\ and\ your\ settings\ are\ removed\ for\ good.\ Deliveries\ you\ took\ stay\ in\ the\ service's\ records\ without\ your\ name.=Your sign-in, your profile, your saved cards and your settings are removed for good. Deliveries you took stay in the service's records without your name. +{0}\ away={0} away +{0}\ can\ now\ drive={0} can now drive +{0}\ completed,\ {1}\ cancelled={0} completed, {1} cancelled +{0}\ days\ ago={0} days ago +{0}\ goes\ back\ to\ the\ card\ it\ came\ from.={0} goes back to the card it came from. +{0}\ is\ on\ its\ way\ to\ your\ bank={0} is on its way to your bank +{0}\ is\ signed\ out\ everywhere\ and\ cannot\ sign\ in\ again\ until\ the\ block\ is\ lifted.={0} is signed out everywhere and cannot sign in again until the block is lifted. +{0}\ new\ accounts={0} new accounts +{0}\ of\ 5\ documents\ added.\ Pick\ up\ where\ you\ left\ off.={0} of 5 documents added. Pick up where you left off. +{0}\ deliveries={0} deliveries +{0}\ s={0} s +{0}\ sees\ what\ you\ write,\ and\ can\ apply\ again\ once\ it\ is\ put\ right.={0} sees what you write, and can apply again once it is put right. +Fill\ in\ everything\ on\ this\ step\ to\ continue.=Fill in everything on this step to continue. +None\ nearby=None nearby +Pay\ the\ courier\ directly=Pay the courier directly +Camera=Camera +Gallery=Gallery +Fares\ collected=Fares collected +Takes\ pets=Takes pets +Customers\ travelling\ with\ a\ pet\ are\ offered\ to\ you=Customers travelling with a pet are offered to you +Go=Go +Nothing\ selected=Nothing selected +Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=Pick a row from the list to see it here. +Usage\ statistics=Usage statistics +Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Share which screens and features are used. Never your name, places or payment details. +Help\ improve\ Morsel=Help improve Morsel +Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel can count which screens are opened and which features are used, so that we can see what works and fix what does not. +No\ personal\ details=No personal details +Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Never your name, e-mail, phone number, places or card details +Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Turn it on or off at any time in Settings +Share\ usage\ statistics=Share usage statistics +With\ usage\ statistics\ turned\ on,\ the\ app\ also\ counts\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ and\ reports\ that\ to\ Codename\ One's\ analytics\ service\ under\ a\ made-up\ id\ for\ this\ device.\ Your\ name,\ e-mail,\ phone\ number,\ places\ and\ card\ details\ are\ never\ part\ of\ it,\ and\ turning\ it\ off\ in\ Settings\ stops\ it.=With usage statistics turned on, the app also counts which screens are opened and which features are used, and reports that to Codename One's analytics service under a made-up id for this device. Your name, e-mail, phone number, places and card details are never part of it, and turning it off in Settings stops it. +Location\ is\ turned\ off.\ Try\ again=Location is turned off. Try again +Can't\ find\ your\ location.\ Try\ again=Can't find your location. Try again +Still\ looking\ for\ your\ location.\ Try\ again=Still looking for your location. Try again +Looking\ for\ your\ location...=Looking for your location... diff --git a/scripts/morsel/common/src/main/l10n/Bundle_es.properties b/scripts/morsel/common/src/main/l10n/Bundle_es.properties new file mode 100644 index 00000000000..8c9c88365d5 --- /dev/null +++ b/scripts/morsel/common/src/main/l10n/Bundle_es.properties @@ -0,0 +1,532 @@ +# Morsel in Spanish. The key of every line is the English text the code +# names; see ui/Lang.java for how a line is found and how to add one. +@rtl=false +language.name=Espa\u00f1ol + +Admin=Administrador +Dashboard=Panel +Live\ map=Mapa en vivo +Deliveries=Viajes +People=Personas +Operations=Operaciones +Morsel\ administration=Administraci\u00f3n de Morsel +Today=Hoy +Manage=Gestionar +Where\ every\ car\ online\ is=D\u00f3nde est\u00e1 cada coche conectado +The\ most\ recent,\ in\ every\ state=Los m\u00e1s recientes, en cualquier estado +Roles,\ and\ suspending\ an\ account=Roles y suspensi\u00f3n de cuentas +Deliveries\ today=Viajes hoy +Under\ way=En curso +Online=En l\u00ednea +Offline=Desconectado +Fares=Tarifas +Cars\ move\ as\ their\ couriers\ report\ in.=Los coches se mueven seg\u00fan informan sus conductores. +No\ cars\ online=Ning\u00fan coche conectado +1\ car\ online=1 coche conectado +{0}\ cars\ online={0} coches conectados +Suspended=Suspendido +Courier=Conductor +Customer=Pasajero +Verified=Verificado +Not\ verified=Sin verificar +May\ go\ online\ and\ take\ deliveries=Puede conectarse y aceptar viajes +May\ see\ and\ change\ everything\ here=Puede ver y cambiar todo aqu\u00ed +Signed\ out,\ and\ refused\ until\ lifted=Sesi\u00f3n cerrada y acceso denegado hasta que se levante +Access=Acceso +Drive=Conducir +Your\ trips=Tus trayectos +Earnings=Ganancias +The\ request\ went\ to\ another\ courier=La solicitud pas\u00f3 a otro conductor +The\ customer\ cancelled=El pasajero cancel\u00f3 +Today,\ {0}\ trips=Hoy, {0} trayectos +Looking\ for\ deliveries=Buscando viajes +You\ are\ offline=Est\u00e1s desconectado +Trip\ complete\:\ {0}=Trayecto completado: {0} +Stay\ close\ to\ busy\ streets;\ requests\ come\ to\ the\ nearest\ car.=Qu\u00e9date cerca de calles concurridas; las solicitudes llegan al coche m\u00e1s cercano. +Go\ online\ to\ start\ taking\ deliveries.=Con\u00e9ctate para empezar a aceptar viajes. +GO=IR +This\ week=Esta semana +Trips=Trayectos +All\ time=Total +Go\ offline=Desconectarse +Go\ online=Conectarse +Delivery\ request=Solicitud de viaje +{0}\ away,\ {1}=A {0}, {1} +Accept=Aceptar +Decline=Rechazar +Pick\ up\ {0}=Recoger a {0} +Waiting\ for\ {0}=Esperando a {0} +On\ the\ trip=En viaje +Drop-off=Destino +Pickup=Recogida +I\ have\ arrived=He llegado +Start\ trip=Iniciar trayecto +Complete\ trip=Finalizar trayecto +Cancel\ delivery=Cancelar viaje +Quiet\ delivery=Viaje tranquilo +Accessible\ vehicle=Veh\u00edculo accesible +Could\ not\ reach\ the\ server.\ Check\ your\ connection.=No se pudo contactar con el servidor. Comprueba tu conexi\u00f3n. +Please\ sign\ in\ again.=Vuelve a iniciar sesi\u00f3n. +Your\ account\ is\ not\ allowed\ to\ do\ that.=Tu cuenta no tiene permiso para hacer eso. +Too\ many\ attempts.\ Wait\ a\ moment\ and\ try\ again.=Demasiados intentos. Espera un momento y vuelve a intentarlo. +Too\ many\ attempts.\ Wait\ a\ minute\ and\ try\ again.=Demasiados intentos. Espera un minuto y vuelve a intentarlo. +Something\ went\ wrong\ ({0}).\ Try\ again.=Algo sali\u00f3 mal ({0}). Int\u00e9ntalo de nuevo. +That\ e-mail\ and\ password\ do\ not\ match\ an\ account.=Ese correo y contrase\u00f1a no corresponden a ninguna cuenta. +Signing\ in\ failed.\ Try\ again.=No se pudo iniciar sesi\u00f3n. Int\u00e9ntalo de nuevo. +Where\ to?=\u00bfAd\u00f3nde vas? +Destination=Destino +An\ address\ or\ a\ place=Una direcci\u00f3n o un lugar +Nothing\ found=No se encontr\u00f3 nada +Current\ location=Ubicaci\u00f3n actual +Delivery=Viajar +Your\ deliveries=Tus viajes +Your\ courier\ is\ here=Tu conductor ha llegado +{0}\ to\ destination={0} hasta el destino +Pickup\ in\ {0}=Recogida en {0} +Hello,\ {0}=Hola, {0} +Where\ are\ you\ going?=\u00bfAd\u00f3nde vas? +Verify\ your\ phone\ number\ to\ request\ a\ delivery.=Verifica tu n\u00famero de tel\u00e9fono para pedir un viaje. +Verify\ now=Verificar ahora +Home=Casa +Work=Trabajo +Working\ out\ the\ fare=Calculando la tarifa +No\ couriers\ nearby\ right\ now=No hay conductores cerca ahora +1\ courier\ nearby,\ {0}\ away=1 conductor cerca, a {0} +{0}\ couriers\ nearby,\ the\ closest\ {1}\ away={0} conductores cerca, el m\u00e1s cercano a {1} +Request\ delivery=Pedir viaje +Cancel=Cancelar +Payment=Pago +Delivery\ preferences=Preferencias de viaje +Cash=Efectivo +Card=Tarjeta +Women\ couriers\ only=Solo conductoras +Pet\ friendly=Admite mascotas +To\ {0}=A {0} +Nobody\ could\ take\ the\ delivery\ just\ now.\ You\ have\ not\ been\ charged.=Nadie pudo aceptar el viaje ahora. No se te ha cobrado. +Your\ courier\ had\ to\ cancel.\ You\ have\ not\ been\ charged.=Tu conductor tuvo que cancelar. No se te ha cobrado. +The\ delivery\ was\ cancelled.=El viaje fue cancelado. +OK=Aceptar +Call=Llamar +Calls\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Las llamadas al conductor no est\u00e1n disponibles en este servidor +Message=Mensaje +Messages\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Los mensajes al conductor no est\u00e1n disponibles en este servidor +Share=Compartir +I\ am\ on\ my\ way\ to\ {0}\ with\ {1}\ in\ a\ {2}\ ({3}).=Voy de camino a {0} con {1} en un {2} ({3}). +You\ have\ arrived=Has llegado +How\ was\ the\ delivery\ with\ {0}?=\u00bfQu\u00e9 tal el viaje con {0}? +Thanks\ for\ the\ rating=Gracias por la valoraci\u00f3n +Not\ now=Ahora no +Admin\ mode=Modo administrador +Courier\ mode=Modo conductor +Customer\ mode=Modo pasajero +Switch\ to=Cambiar a +Riding=Viajar +Onboarding=Conducir +Administration=Administraci\u00f3n +Settings=Ajustes +Sign\ out=Cerrar sesi\u00f3n +No\ connection=Sin conexi\u00f3n +The\ server\ at\ {0}\ did\ not\ answer.=El servidor en {0} no respondi\u00f3. +Try\ again=Reintentar +Server\ address=Direcci\u00f3n del servidor +Create\ an\ account=Crear una cuenta +Name=Nombre +What\ couriers\ will\ call\ you=C\u00f3mo te llamar\u00e1n los conductores +E-mail=Correo electr\u00f3nico +Mobile\ number=N\u00famero de m\u00f3vil +Password=Contrase\u00f1a +At\ least\ 8\ characters=Al menos 8 caracteres +I\ want\ to\ drive=Quiero conducir +Take\ delivery\ requests\ and\ earn=Acepta solicitudes y gana dinero +Car=Coche +Blue\ Toyota\ Prius=Toyota Prius azul +Licence\ plate=Matr\u00edcula +Create\ account=Crear cuenta +No\ deliveries\ yet=A\u00fan no hay viajes +{0}\ with\ {1}={0} con {1} +Server=Servidor +The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=La direcci\u00f3n del servidor Morsel que usa esta app. Cambiarla cierra tu sesi\u00f3n. +Address=Direcci\u00f3n +Save=Guardar +Account=Cuenta +Needed\ before\ your\ first\ delivery=Necesario antes de tu primer viaje +Verify=Verificar +Any\ courier=Cualquier conductor +Women\ only=Solo mujeres +Ask\ the\ courier\ to\ keep\ conversation\ short=Pide al conductor que hable poco +Only\ cars\ that\ take\ a\ wheelchair=Solo coches aptos para silla de ruedas +Tell\ the\ courier\ an\ animal\ is\ coming=Avisa al conductor de que viaja un animal +Pay\ with=Pagar con +Tip=Propina +None=Ninguna +Notifications=Notificaciones +Delivery\ updates=Novedades del viaje +Courier\ found,\ arriving,\ at\ the\ door=Conductor encontrado, llegando, en la puerta +Receipts=Recibos +After\ every\ delivery=Despu\u00e9s de cada viaje +Offers=Ofertas +Discounts\ and\ news=Descuentos y novedades +Appearance=Apariencia +Language=Idioma +Theme=Tema +System=Sistema +Light=Claro +Dark=Oscuro +Distances=Distancias +Kilometres=Kil\u00f3metros +Miles=Millas +Privacy\ and\ safety=Privacidad y seguridad +Share\ my\ trips=Compartir mis viajes +Emergency\ contact=Contacto de emergencia +Their\ name=Su nombre +Their\ phone\ number=Su n\u00famero de tel\u00e9fono +About=Acerca de +A\ sample\ delivery\ service\ built\ with\ Codename\ One=Un servicio de viajes de ejemplo creado con Codename One +Terms\ of\ service=Condiciones del servicio +Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel es un ejemplo. No se reservan viajes reales, no se env\u00edan conductores reales y no se mueve dinero. +Privacy\ policy=Pol\u00edtica de privacidad +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Tu posici\u00f3n se env\u00eda al servidor en el que iniciaste sesi\u00f3n mientras viajas o conduces, y a nadie m\u00e1s. Los ajustes se guardan en este dispositivo. +Sign\ in=Iniciar sesi\u00f3n +Enter\ your\ e-mail\ and\ password.=Introduce tu correo y contrase\u00f1a. +{0}\ ft={0} pies +{0}\ mi={0} mi +{0}\ m={0} m +{0}\ km={0} km +{0}\ min={0} min +Finding\ a\ courier=Buscando conductor +Courier\ on\ the\ way=Conductor en camino +Courier\ at\ pickup=Conductor en el punto de recogida +Completed=Completado +Cancelled\ by\ customer=Cancelado por el pasajero +Cancelled\ by\ courier=Cancelado por el conductor +No\ courier\ found=No se encontr\u00f3 conductor +Verify\ your\ number=Verifica tu n\u00famero +We\ sent\ a\ six-digit\ code\ to\ your\ phone.=Enviamos un c\u00f3digo de seis d\u00edgitos a tu tel\u00e9fono. +We\ sent\ a\ six-digit\ code\ to\ {0}.=Enviamos un c\u00f3digo de seis d\u00edgitos a {0}. +Code=C\u00f3digo +This\ is\ a\ development\ server,\ so\ no\ text\ was\ sent.\ Your\ code\ is\ {0}.=Este es un servidor de desarrollo, as\u00ed que no se envi\u00f3 ning\u00fan SMS. Tu c\u00f3digo es {0}. +Send\ a\ new\ code=Enviar un c\u00f3digo nuevo +A\ delivery\ when\ you\ need\ one,\ and\ a\ fair\ way\ to\ drive.=Un viaje cuando lo necesitas, y una forma justa de conducir. + +# Onboarding applications, payments, the admin console and the account. +1\ application\ to\ review=1 solicitud por revisar +{0}\ applications\ to\ review={0} solicitudes por revisar +1\ flagged\ account=1 cuenta marcada +{0}\ flagged\ accounts={0} cuentas marcadas +7\ days=7 d\u00edas +14\ days=14 d\u00edas +30\ days=30 d\u00edas +A\ car\ with\ four\ doors=Un coche de cuatro puertas +A\ onboarding\ licence=Un permiso de conducir +A\ flag\ marks\ the\ account\ for\ the\ admins\ and\ changes\ nothing\ for\ its\ owner.\ A\ block\ signs\ it\ out\ and\ refuses\ it\ until\ lifted.=Una marca se\u00f1ala la cuenta a los administradores y no cambia nada para su due\u00f1o. Un bloqueo cierra su sesi\u00f3n y la rechaza hasta que se levante. +A\ passport\ or\ an\ identity\ card=Un pasaporte o un documento de identidad +A\ photo\ of\ you=Una foto tuya +A\ ramp\ or\ a\ lift,\ and\ room\ to\ secure\ the\ chair=Rampa o elevador, y espacio para asegurar la silla +A\ delivery\ shows\ up\ here\ as\ soon\ as\ it\ is\ asked\ for.=Un viaje aparece aqu\u00ed en cuanto se pide. +A\ delivery\ shows\ up\ here\ when\ it\ is\ paid\ for.=Un viaje aparece aqu\u00ed cuando se paga. +A\ sentence\ the\ record\ will\ keep=Una frase que quedar\u00e1 registrada +About\ ten\ minutes=Unos diez minutos +About\ you=Sobre ti +Accepted=Aceptado +Account\ ending\ {0}=Cuenta terminada en {0} +Account\ holder=Titular de la cuenta +Account\ number=N\u00famero de cuenta +Add\ a\ bank\ account=A\u00f1adir una cuenta bancaria +Add\ a\ card=A\u00f1adir una tarjeta +Add\ a\ tip=A\u00f1adir propina +Added=A\u00f1adido +Admins=Administradores +All=Todos +All\ of\ it\ goes\ to\ your\ courier.=Todo va para tu conductor. +All\ of\ it\ goes\ to\ {0}.=Todo va para {0}. +Another\ amount=Otro importe +Applicant=Solicitante +Application=Solicitud +Application\ received=Solicitud recibida +Application\ sent\ back=Solicitud devuelta +Application\ sent.\ We\ will\ be\ in\ touch.=Solicitud enviada. Te avisaremos. +Application\ under\ review=Solicitud en revisi\u00f3n +Applications=Solicitudes +Approve=Aprobar +Approved=Aprobada +Approved\ to\ drive=Aprobado para conducir +As\ it\ is\ written\ on\ your\ licence.\ Customers\ see\ only\ your\ first\ name.=Como figura en tu permiso. Los pasajeros solo ven tu nombre de pila. +Average\ fare=Tarifa media +Average\ wait=Espera media +Back=Atr\u00e1s +Bank=Banco +Bank\ account=Cuenta bancaria +Base\ fare=Tarifa base +Block=Bloquear +Block\ account=Bloquear cuenta +Blocked=Bloqueada +Blue=Azul +By\ kind\ of\ delivery=Por tipo de viaje +By\ outcome=Por resultado +By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ documents\ are\ your\ own.=Al enviar confirmas que todo esto es cierto y que los documentos son tuyos. +Calling\ is\ not\ available\ on\ this\ device=Este dispositivo no permite llamar +Cancel\ this\ delivery=Cancelar este viaje +Cancelled=Cancelados +Cancelled\ by\ Morsel=Cancelado por Morsel +Card\ number=N\u00famero de tarjeta +Cash\ out=Retirar +Change\ password=Cambiar contrase\u00f1a +Change\ the\ password\ you\ sign\ in\ with=Cambia la contrase\u00f1a con la que entras +Changes\ needed=Requiere cambios +Choose\ a\ delivery=Elige un viaje +Choose\ an\ amount\ for\ the\ tip=Elige el importe de la propina +Choose\ how\ to\ pay\ for\ this\ delivery.=Elige c\u00f3mo pagar este viaje. +Collect\ {0}\ in\ cash=Cobra {0} en efectivo +Colour=Color +Comfort=Comfort +Comfort\ is\ a\ newer,\ roomier\ car.\ XL\ seats\ six\ or\ more.=Comfort es un coche m\u00e1s nuevo y amplio. XL lleva a seis o m\u00e1s. +Commission,\ %=Comisi\u00f3n, % +Contact=Contacto +Continue=Continuar +Continue\ application=Continuar la solicitud +Current\ password=Contrase\u00f1a actual +Date\ of\ birth=Fecha de nacimiento +Default=Predeterminada +Delete\ account=Eliminar cuenta +Delete\ my\ account=Eliminar mi cuenta +Delete\ your\ account?=\u00bfEliminar tu cuenta? +Discount=Descuento +Distance=Distancia +Documents=Documentos +Documents\ checked=Documentos comprobados +Done=Listo +Draft=Borrador +Drive\ with\ Morsel=Conduce con Morsel +Couriers=Conductores +Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Los conductores presentan su solicitud tras registrarse: unos datos sobre ti y tu coche, y cinco fotos. Mientras tanto puedes viajar. +Couriers\ online=Conductores en l\u00ednea +Onboarding\ licence=Permiso de conducir +Earn\ on\ your\ own\ schedule=Gana a tu ritmo +Earned\ onboarding=Ganado conduciendo +Every\ application\ has\ had\ its\ answer.=Todas las solicitudes tienen respuesta. +Everyone=Todos +Expires=Caduca +Expires\ {0}=Caduca {0} +Fare=Tarifa +Finish\ your\ application=Termina tu solicitud +Fix\ and\ resubmit=Corregir y reenviar +Flag=Marcar +Flag\ account=Marcar cuenta +Flag\ removed=Marca retirada +Flagged=Marcada +Gender=G\u00e9nero +Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Un \u00faltimo vistazo. Puedes cambiar lo que quieras hasta entregarla. +Hi,\ this\ is\ your\ Morsel\ courier.=Hola, soy tu conductor de Morsel. +Hi,\ this\ is\ your\ Morsel\ customer.=Hola, soy tu pasajero de Morsel. +History=Historial +I\ have\ finished=He terminado +I\ want\ to=Quiero +In\ good\ standing=En regla +Insurance=Seguro +Its\ number,\ its\ expiry\ date\ and\ a\ photo\ of\ it=Su n\u00famero, su fecha de caducidad y una foto +Keep\ my\ account=Conservar mi cuenta +Kind\ of\ delivery=Tipo de viaje +Legal\ name=Nombre legal +Licence=Permiso +Licence\ expires=El permiso caduca +Licence\ number=N\u00famero de permiso +Make=Marca +Man=Hombre +Messages\ are\ not\ available\ on\ this\ device=Este dispositivo no permite enviar mensajes +Minimum\ fare=Tarifa m\u00ednima +Missing=Falta +Model=Modelo +Moderation=Moderaci\u00f3n +Most\ of\ it\ is\ taking\ five\ photos=Casi todo es hacer cinco fotos +Multipliers=Multiplicadores +Name\ an\ emergency\ contact\ in\ your\ profile\ first=Primero indica un contacto de emergencia en tu perfil +Name\ on\ the\ card=Nombre en la tarjeta +Name,\ emergency\ contact,\ home\ and\ work=Nombre, contacto de emergencia, casa y trabajo +Needed=Pendiente +New\ password=Nueva contrase\u00f1a +New\ password,\ again=Nueva contrase\u00f1a, otra vez +No=No +No\ payments\ yet=A\u00fan no hay pagos +No\ deliveries\ here=No hay viajes aqu\u00ed +Nobody\ matches=Nadie coincide +Non-binary=No binario +Not\ provided=No aportado +Not\ set=Sin definir +Not\ started=Sin empezar +Nothing\ has\ been\ done\ to\ this\ account.=No se ha hecho nada con esta cuenta. +Nothing\ has\ been\ paid\ out\ yet.=A\u00fan no se ha pagado nada. +Nothing\ here=Nada por aqu\u00ed +Open\ the\ secure\ page=Abrir la p\u00e1gina segura +Paid=Pagado +Paid\ in\ cash=Pagado en efectivo +Paid\ with=Pagado con +Payment\ failed=Pago fallido +Payment\ pending=Pago pendiente +Payments=Pagos +Payouts=Pagos recibidos +People\ who\ asked\ to\ drive,\ and\ their\ papers=Quienes pidieron conducir, y sus papeles +Per\ kilometre=Por kil\u00f3metro +Per\ minute=Por minuto +Photo\ ID=Documento con foto +Photograph\ each\ one\ flat,\ with\ all\ four\ corners\ in\ the\ picture.=Fotograf\u00eda cada uno en plano, con las cuatro esquinas visibles. +Photos\ added=Fotos a\u00f1adidas +Pricing=Precios +Profile=Perfil +Provided=Aportado +Rather\ not\ say=Prefiero no decirlo +Rating=Valoraci\u00f3n +Read\ what\ was\ missing,\ put\ it\ right,\ and\ send\ the\ application\ again.=Lee lo que faltaba, corr\u00edgelo y vuelve a enviar la solicitud. +Ready\ to\ cash\ out=Listo para retirar +Reason=Motivo +Receipt=Recibo +Refund=Reembolsar +Refund\ this\ delivery=Reembolsar este viaje +Refunded=Reembolsado +Registered\ and\ insured,\ with\ the\ papers\ to\ show\ it=Matriculado y asegurado, con los papeles que lo prueban +Rejected=Rechazado +Remove\ flag=Quitar marca +Remove\ this\ card=Quitar esta tarjeta +Request\ {0}=Pedir {0} +Requests\ accepted=Solicitudes aceptadas +Requests\ by\ hour=Solicitudes por hora +Retake=Repetir +Review\ and\ submit=Revisar y enviar +Reviewed=Revisada +Reviewed\ by=Revisada por +Customers=Pasajeros +Customers\ who\ ask\ for\ a\ woman\ at\ the\ wheel\ are\ offered\ to\ couriers\ who\ say\ they\ are\ one.=Los pasajeros que piden una mujer al volante se ofrecen a las conductoras que indican serlo. +Right\ now=Ahora mismo +Roles\ changed=Roles cambiados +Roles,\ flags,\ and\ blocking\ an\ account=Roles, marcas y bloqueo de cuentas +Save\ card=Guardar tarjeta +Save\ prices=Guardar precios +Saved\ places=Lugares guardados +Say\ why,\ in\ a\ few\ words.=Di por qu\u00e9, en pocas palabras. +Search\ by\ name\ or\ e-mail=Buscar por nombre o correo +Seats\ for\ customers=Plazas para pasajeros +Security\ code=C\u00f3digo de seguridad +Send\ back=Devolver +Send\ tip=Enviar propina +Saves\ {0}'s\ number\ with\ your\ account\ as\ your\ trip\ contact.\ No\ message\ is\ sent.=Guarda el n\u00famero de {0} en tu cuenta como contacto de viaje. No se env\u00eda ning\u00fan mensaje. +Sent\ back=Devuelta +Service\ fee=Tarifa de servicio +Service\ fee,\ %=Tarifa de servicio, % +Spent\ riding=Gastado en viajes +Standard=Est\u00e1ndar +Start\ application=Empezar la solicitud +Step\ {0}\ of\ {1}=Paso {0} de {1} +Submit\ application=Enviar solicitud +Submitted=Enviada +Surge,\ while\ demand\ is\ high=Recargo, con demanda alta +Takes\ a\ wheelchair=Admite silla de ruedas +Tap\ a\ card\ to\ make\ it\ the\ one\ that\ pays\ unless\ you\ say\ otherwise.=Toca una tarjeta para que sea la que paga salvo que digas otra cosa. +Tap\ to\ open=Toca para abrir +Tell\ us\ about\ you\ and\ your\ car,\ add\ five\ photos,\ and\ we\ take\ it\ from\ there.=Cu\u00e9ntanos sobre ti y tu coche, a\u00f1ade cinco fotos y nosotros seguimos. +Thank\ you.\ Your\ tip\ was\ sent.=Gracias. Tu propina se envi\u00f3. +That\ is\ not\ a\ card\ number.\ Check\ the\ digits.=Eso no es un n\u00famero de tarjeta. Revisa los d\u00edgitos. +That\ picture\ could\ not\ be\ read=No se pudo leer esa imagen +That\ place\ could\ not\ be\ found.\ Choose\ it\ again.=No se encontr\u00f3 ese lugar. El\u00edgelo de nuevo. +The\ busy\ hours,\ in\ UTC=Las horas punta, en UTC +The\ card\ was\ declined.\ Choose\ another\ way\ to\ pay\ in\ your\ wallet.=La tarjeta fue rechazada. Elige otra forma de pago en tu cartera. +The\ fare,\ in\ {0}=La tarifa, en {0} +The\ fee\ is\ added\ to\ the\ fare\ and\ paid\ by\ the\ customer.\ The\ commission\ is\ kept\ from\ the\ fare;\ the\ rest\ is\ the\ courier's.=La tarifa de servicio se suma al precio y la paga el pasajero. La comisi\u00f3n se retiene del precio; el resto es del conductor. +The\ last\ two\ weeks=Las dos \u00faltimas semanas +The\ new\ prices\ are\ in\ force=Los nuevos precios est\u00e1n en vigor +The\ number\ is\ on\ the\ front\ of\ the\ card.=El n\u00famero est\u00e1 en el anverso de la tarjeta. +The\ page\ with\ the\ policy's\ dates\ on\ it=La p\u00e1gina con las fechas de la p\u00f3liza +The\ paper\ that\ says\ the\ car\ is\ yours=El papel que dice que el coche es tuyo +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=La p\u00e1gina del proveedor de pagos se abre en tu navegador. Morsel nunca ve el n\u00famero. Vuelve aqu\u00ed cuando termines. +The\ delivery\ was\ refunded=El viaje fue reembolsado +Refund\ pending=Reembolso pendiente +Refund\ failed=Reembolso fallido +The\ refund\ is\ pending=El reembolso est\u00e1 pendiente +The\ customer\ and\ the\ courier\ are\ both\ told,\ and\ nobody\ is\ charged.=Se avisa al pasajero y al conductor, y no se cobra a nadie. +The\ security\ code\ is\ the\ three\ or\ four\ digits\ on\ the\ card.=El c\u00f3digo de seguridad son los tres o cuatro d\u00edgitos de la tarjeta. +The\ service's\ share=La parte del servicio +The\ side\ with\ your\ photo\ on\ it=La cara con tu foto +The\ two\ new\ passwords\ are\ not\ the\ same.=Las dos contrase\u00f1as nuevas no coinciden. +This\ device\ cannot\ prepare\ a\ picture\ to\ send=Este dispositivo no puede preparar una imagen para enviar +This\ month=Este mes +This\ picture\ could\ not\ be\ opened=No se pudo abrir esta imagen +This\ screen\ becomes\ your\ courier's\ home\ the\ moment\ you\ are\ approved=Esta pantalla ser\u00e1 tu inicio de conductor en cuanto te aprueben +This\ server\ takes\ no\ real\ payments.\ Use\ the\ test\ card\ 4242\ 4242\ 4242\ 4242\ with\ any\ date\ ahead\ and\ any\ three\ digits.=Este servidor no acepta pagos reales. Usa la tarjeta de prueba 4242 4242 4242 4242 con cualquier fecha futura y tres d\u00edgitos cualesquiera. +Time=Tiempo +Tip\ offered\ first=Propina sugerida +Tips=Propinas +Top\ couriers=Mejores conductores +Total=Total +Trends=Tendencias +Trip=Trayecto +Type\ the\ account\ number.=Escribe el n\u00famero de cuenta. +Unblock=Desbloquear +Unblocked=Desbloqueada +Under\ review=En revisi\u00f3n +Usually\ within\ two\ working\ days=Normalmente en dos d\u00edas laborables +Vehicle=Veh\u00edculo +Vehicle\ registration=Permiso de circulaci\u00f3n +View\ receipt=Ver recibo +Waiting=En espera +Wallet=Cartera +We\ could\ not\ approve\ this\ yet=A\u00fan no pudimos aprobarla +We\ have\ everything\ we\ asked\ for.\ Someone\ on\ our\ team\ is\ reading\ it.=Tenemos todo lo que pedimos. Alguien del equipo la est\u00e1 leyendo. +What\ a\ delivery\ costs,\ and\ the\ service's\ share=Cu\u00e1nto cuesta un viaje, y la parte del servicio +What\ happens\ next=Qu\u00e9 pasa ahora +What\ the\ reviewer\ said=Lo que dijo el revisor +What\ was\ charged,\ and\ refunds=Lo cobrado, y los reembolsos +What\ you\ will\ need=Lo que necesitar\u00e1s +Where\ your\ earnings\ are\ sent=Ad\u00f3nde se env\u00edan tus ganancias +Why\ does\ {0}\ need\ the\ admins'\ attention?=\u00bfPor qu\u00e9 {0} necesita la atenci\u00f3n de los administradores? +Woman=Mujer +Write\ the\ expiry\ date\ as\ month\ and\ year\:\ 04/28.=Escribe la caducidad como mes y a\u00f1o: 04/28. +Year=A\u00f1o +Yes=S\u00ed +You=T\u00fa +You\ go\ online=Te pones en l\u00ednea +Your\ application=Tu solicitud +Your\ car=Tu coche +Your\ card\ goes\ straight\ to\ the\ bank=Tu tarjeta va directa al banco +Your\ cards,\ and\ which\ of\ them\ pays\ by\ default=Tus tarjetas, y cu\u00e1l paga por defecto +Your\ documents=Tus documentos +Your\ courier\ cannot\ be\ messaged\ right\ now=Ahora no se puede escribir a tu conductor +Your\ face,\ in\ good\ light,\ no\ sunglasses=Tu cara, con buena luz y sin gafas de sol +Your\ licence=Tu permiso +Your\ password\ was\ changed=Tu contrase\u00f1a se cambi\u00f3 +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Your\ settings\ are\ kept\ with\ your\ account\ on\ that\ server.=Tu posici\u00f3n se env\u00eda al servidor en el que iniciaste sesi\u00f3n mientras viajas o conduces, y a nadie m\u00e1s. Tus ajustes se guardan con tu cuenta en ese servidor. +Your\ customer\ cannot\ be\ messaged\ right\ now=Ahora no se puede escribir a tu pasajero +Your\ sign-in,\ your\ profile,\ your\ saved\ cards\ and\ your\ settings\ are\ removed\ for\ good.\ Deliveries\ you\ took\ stay\ in\ the\ service's\ records\ without\ your\ name.=Tu acceso, tu perfil, tus tarjetas guardadas y tus ajustes se eliminan para siempre. Tus viajes quedan en los registros del servicio sin tu nombre. +{0}\ away=a {0} +{0}\ can\ now\ drive={0} ya puede conducir +{0}\ completed,\ {1}\ cancelled={0} completados, {1} cancelados +{0}\ days\ ago=hace {0} d\u00edas +{0}\ goes\ back\ to\ the\ card\ it\ came\ from.={0} vuelve a la tarjeta de la que sali\u00f3. +{0}\ is\ on\ its\ way\ to\ your\ bank={0} va camino de tu banco +{0}\ is\ signed\ out\ everywhere\ and\ cannot\ sign\ in\ again\ until\ the\ block\ is\ lifted.={0} queda sin sesi\u00f3n en todas partes y no puede volver a entrar hasta que se levante el bloqueo. +{0}\ new\ accounts={0} cuentas nuevas +{0}\ of\ 5\ documents\ added.\ Pick\ up\ where\ you\ left\ off.={0} de 5 documentos a\u00f1adidos. Sigue donde lo dejaste. +{0}\ deliveries={0} viajes +{0}\ s={0} s +{0}\ sees\ what\ you\ write,\ and\ can\ apply\ again\ once\ it\ is\ put\ right.={0} ve lo que escribes y puede volver a solicitarlo cuando lo corrija. +Fill\ in\ everything\ on\ this\ step\ to\ continue.=Completa todo en este paso para continuar. +None\ nearby=Ninguno cerca +Pay\ the\ courier\ directly=Paga directamente al conductor +Camera=C\u00e1mara +Gallery=Galer\u00eda +Fares\ collected=Tarifas cobradas +Takes\ pets=Acepta mascotas +Customers\ travelling\ with\ a\ pet\ are\ offered\ to\ you=Se te ofrecen los pasajeros que viajan con mascota +Go=Ir +Nothing\ selected=Nada seleccionado +Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=Elige una fila de la lista para verla aqu\u00ed. +Usage\ statistics=Estad\u00edsticas de uso +Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Comparte qu\u00e9 pantallas y funciones se usan. Nunca tu nombre, tus lugares ni tus datos de pago. +Help\ improve\ Morsel=Ayuda a mejorar Morsel +Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel puede contar qu\u00e9 pantallas se abren y qu\u00e9 funciones se usan, para que podamos ver qu\u00e9 funciona y arreglar lo que no. +No\ personal\ details=Sin datos personales +Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Nunca tu nombre, correo electr\u00f3nico, n\u00famero de tel\u00e9fono, lugares ni datos de tarjeta +Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Act\u00edvalo o desact\u00edvalo cuando quieras en Ajustes +Share\ usage\ statistics=Compartir estad\u00edsticas de uso +With\ usage\ statistics\ turned\ on,\ the\ app\ also\ counts\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ and\ reports\ that\ to\ Codename\ One's\ analytics\ service\ under\ a\ made-up\ id\ for\ this\ device.\ Your\ name,\ e-mail,\ phone\ number,\ places\ and\ card\ details\ are\ never\ part\ of\ it,\ and\ turning\ it\ off\ in\ Settings\ stops\ it.=Con las estad\u00edsticas de uso activadas, la app tambi\u00e9n cuenta qu\u00e9 pantallas se abren y qu\u00e9 funciones se usan, y lo env\u00eda al servicio de anal\u00edtica de Codename One con un identificador inventado para este dispositivo. Tu nombre, correo electr\u00f3nico, n\u00famero de tel\u00e9fono, lugares y datos de tarjeta nunca forman parte de ello, y al desactivarlas en Ajustes deja de hacerlo. +Location\ is\ turned\ off.\ Try\ again=La ubicaci\u00f3n est\u00e1 desactivada. Reintentar +Can't\ find\ your\ location.\ Try\ again=No encontramos tu ubicaci\u00f3n. Reintentar +Still\ looking\ for\ your\ location.\ Try\ again=Seguimos buscando tu ubicaci\u00f3n. Reintentar +Looking\ for\ your\ location...=Buscando tu ubicaci\u00f3n... diff --git a/scripts/morsel/common/src/main/l10n/Bundle_fr.properties b/scripts/morsel/common/src/main/l10n/Bundle_fr.properties new file mode 100644 index 00000000000..cbd23551b6c --- /dev/null +++ b/scripts/morsel/common/src/main/l10n/Bundle_fr.properties @@ -0,0 +1,532 @@ +# Morsel in French. The key of every line is the English text the code +# names; see ui/Lang.java for how a line is found and how to add one. +@rtl=false +language.name=Fran\u00e7ais + +Admin=Admin +Dashboard=Tableau de bord +Live\ map=Carte en direct +Deliveries=Courses +People=Personnes +Operations=Op\u00e9rations +Morsel\ administration=Administration de Morsel +Today=Aujourd'hui +Manage=G\u00e9rer +Where\ every\ car\ online\ is=O\u00f9 se trouve chaque voiture en ligne +The\ most\ recent,\ in\ every\ state=Les plus r\u00e9centes, dans tous les \u00e9tats +Roles,\ and\ suspending\ an\ account=R\u00f4les et suspension de comptes +Deliveries\ today=Courses aujourd'hui +Under\ way=En cours +Online=En ligne +Offline=Hors ligne +Fares=Recettes +Cars\ move\ as\ their\ couriers\ report\ in.=Les voitures bougent \u00e0 mesure que les chauffeurs se signalent. +No\ cars\ online=Aucune voiture en ligne +1\ car\ online=1 voiture en ligne +{0}\ cars\ online={0} voitures en ligne +Suspended=Suspendu +Courier=Chauffeur +Customer=Passager +Verified=V\u00e9rifi\u00e9 +Not\ verified=Non v\u00e9rifi\u00e9 +May\ go\ online\ and\ take\ deliveries=Peut se connecter et prendre des courses +May\ see\ and\ change\ everything\ here=Peut tout voir et tout modifier ici +Signed\ out,\ and\ refused\ until\ lifted=D\u00e9connect\u00e9, et refus\u00e9 jusqu'\u00e0 la lev\u00e9e +Access=Acc\u00e8s +Drive=Conduire +Your\ trips=Vos trajets +Earnings=Revenus +The\ request\ went\ to\ another\ courier=La demande est pass\u00e9e \u00e0 un autre chauffeur +The\ customer\ cancelled=Le passager a annul\u00e9 +Today,\ {0}\ trips=Aujourd'hui, {0} trajets +Looking\ for\ deliveries=Recherche de courses +You\ are\ offline=Vous \u00eates hors ligne +Trip\ complete\:\ {0}=Trajet termin\u00e9 : {0} +Stay\ close\ to\ busy\ streets;\ requests\ come\ to\ the\ nearest\ car.=Restez pr\u00e8s des rues anim\u00e9es ; les demandes vont \u00e0 la voiture la plus proche. +Go\ online\ to\ start\ taking\ deliveries.=Passez en ligne pour commencer \u00e0 prendre des courses. +GO=GO +This\ week=Cette semaine +Trips=Trajets +All\ time=Depuis le d\u00e9but +Go\ offline=Passer hors ligne +Go\ online=Passer en ligne +Delivery\ request=Demande de course +{0}\ away,\ {1}=\u00c0 {0}, {1} +Accept=Accepter +Decline=Refuser +Pick\ up\ {0}=Prendre {0} +Waiting\ for\ {0}=En attente de {0} +On\ the\ trip=En course +Drop-off=D\u00e9pose +Pickup=Prise en charge +I\ have\ arrived=Je suis arriv\u00e9 +Start\ trip=D\u00e9marrer la course +Complete\ trip=Terminer la course +Cancel\ delivery=Annuler la course +Quiet\ delivery=Course silencieuse +Accessible\ vehicle=V\u00e9hicule accessible +Could\ not\ reach\ the\ server.\ Check\ your\ connection.=Impossible de joindre le serveur. V\u00e9rifiez votre connexion. +Please\ sign\ in\ again.=Veuillez vous reconnecter. +Your\ account\ is\ not\ allowed\ to\ do\ that.=Votre compte n'est pas autoris\u00e9 \u00e0 faire cela. +Too\ many\ attempts.\ Wait\ a\ moment\ and\ try\ again.=Trop de tentatives. Patientez un instant et r\u00e9essayez. +Too\ many\ attempts.\ Wait\ a\ minute\ and\ try\ again.=Trop de tentatives. Patientez une minute et r\u00e9essayez. +Something\ went\ wrong\ ({0}).\ Try\ again.=Une erreur s'est produite ({0}). R\u00e9essayez. +That\ e-mail\ and\ password\ do\ not\ match\ an\ account.=Cet e-mail et ce mot de passe ne correspondent \u00e0 aucun compte. +Signing\ in\ failed.\ Try\ again.=La connexion a \u00e9chou\u00e9. R\u00e9essayez. +Where\ to?=O\u00f9 allez-vous ? +Destination=Destination +An\ address\ or\ a\ place=Une adresse ou un lieu +Nothing\ found=Aucun r\u00e9sultat +Current\ location=Position actuelle +Delivery=Course +Your\ deliveries=Vos courses +Your\ courier\ is\ here=Votre chauffeur est l\u00e0 +{0}\ to\ destination={0} jusqu'\u00e0 destination +Pickup\ in\ {0}=Prise en charge dans {0} +Hello,\ {0}=Bonjour, {0} +Where\ are\ you\ going?=O\u00f9 allez-vous ? +Verify\ your\ phone\ number\ to\ request\ a\ delivery.=V\u00e9rifiez votre num\u00e9ro de t\u00e9l\u00e9phone pour commander une course. +Verify\ now=V\u00e9rifier maintenant +Home=Domicile +Work=Travail +Working\ out\ the\ fare=Calcul du tarif +No\ couriers\ nearby\ right\ now=Aucun chauffeur \u00e0 proximit\u00e9 pour le moment +1\ courier\ nearby,\ {0}\ away=1 chauffeur \u00e0 proximit\u00e9, \u00e0 {0} +{0}\ couriers\ nearby,\ the\ closest\ {1}\ away={0} chauffeurs \u00e0 proximit\u00e9, le plus proche \u00e0 {1} +Request\ delivery=Commander la course +Cancel=Annuler +Payment=Paiement +Delivery\ preferences=Pr\u00e9f\u00e9rences de course +Cash=Esp\u00e8ces +Card=Carte +Women\ couriers\ only=Conductrices uniquement +Pet\ friendly=Animaux accept\u00e9s +To\ {0}=Vers {0} +Nobody\ could\ take\ the\ delivery\ just\ now.\ You\ have\ not\ been\ charged.=Personne n'a pu prendre la course. Vous n'avez pas \u00e9t\u00e9 d\u00e9bit\u00e9. +Your\ courier\ had\ to\ cancel.\ You\ have\ not\ been\ charged.=Votre chauffeur a d\u00fb annuler. Vous n'avez pas \u00e9t\u00e9 d\u00e9bit\u00e9. +The\ delivery\ was\ cancelled.=La course a \u00e9t\u00e9 annul\u00e9e. +OK=OK +Call=Appeler +Calls\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Les appels au chauffeur ne sont pas disponibles sur ce serveur +Message=Message +Messages\ to\ your\ courier\ are\ not\ available\ on\ this\ server=Les messages au chauffeur ne sont pas disponibles sur ce serveur +Share=Partager +I\ am\ on\ my\ way\ to\ {0}\ with\ {1}\ in\ a\ {2}\ ({3}).=Je suis en route vers {0} avec {1} dans une {2} ({3}). +You\ have\ arrived=Vous \u00eates arriv\u00e9 +How\ was\ the\ delivery\ with\ {0}?=Comment s'est pass\u00e9e la course avec {0} ? +Thanks\ for\ the\ rating=Merci pour votre note +Not\ now=Pas maintenant +Admin\ mode=Mode admin +Courier\ mode=Mode chauffeur +Customer\ mode=Mode passager +Switch\ to=Passer \u00e0 +Riding=Passager +Onboarding=Conduite +Administration=Administration +Settings=R\u00e9glages +Sign\ out=Se d\u00e9connecter +No\ connection=Pas de connexion +The\ server\ at\ {0}\ did\ not\ answer.=Le serveur \u00e0 {0} n'a pas r\u00e9pondu. +Try\ again=R\u00e9essayer +Server\ address=Adresse du serveur +Create\ an\ account=Cr\u00e9er un compte +Name=Nom +What\ couriers\ will\ call\ you=Comment les chauffeurs vous appelleront +E-mail=E-mail +Mobile\ number=Num\u00e9ro de mobile +Password=Mot de passe +At\ least\ 8\ characters=Au moins 8 caract\u00e8res +I\ want\ to\ drive=Je veux conduire +Take\ delivery\ requests\ and\ earn=Prenez des courses et gagnez de l'argent +Car=Voiture +Blue\ Toyota\ Prius=Toyota Prius bleue +Licence\ plate=Plaque d'immatriculation +Create\ account=Cr\u00e9er le compte +No\ deliveries\ yet=Pas encore de courses +{0}\ with\ {1}={0} avec {1} +Server=Serveur +The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=L'adresse du serveur Morsel utilis\u00e9 par cette application. La modifier vous d\u00e9connecte. +Address=Adresse +Save=Enregistrer +Account=Compte +Needed\ before\ your\ first\ delivery=N\u00e9cessaire avant votre premi\u00e8re course +Verify=V\u00e9rifier +Any\ courier=Tout chauffeur +Women\ only=Femmes uniquement +Ask\ the\ courier\ to\ keep\ conversation\ short=Demander au chauffeur de limiter la conversation +Only\ cars\ that\ take\ a\ wheelchair=Uniquement des voitures acceptant un fauteuil roulant +Tell\ the\ courier\ an\ animal\ is\ coming=Pr\u00e9venir le chauffeur qu'un animal vous accompagne +Pay\ with=Payer avec +Tip=Pourboire +None=Aucun +Notifications=Notifications +Delivery\ updates=Suivi de la course +Courier\ found,\ arriving,\ at\ the\ door=Chauffeur trouv\u00e9, en approche, devant la porte +Receipts=Re\u00e7us +After\ every\ delivery=Apr\u00e8s chaque course +Offers=Offres +Discounts\ and\ news=R\u00e9ductions et nouveaut\u00e9s +Appearance=Apparence +Language=Langue +Theme=Th\u00e8me +System=Syst\u00e8me +Light=Clair +Dark=Sombre +Distances=Distances +Kilometres=Kilom\u00e8tres +Miles=Miles +Privacy\ and\ safety=Confidentialit\u00e9 et s\u00e9curit\u00e9 +Share\ my\ trips=Partager mes trajets +Emergency\ contact=Contact d'urgence +Their\ name=Son nom +Their\ phone\ number=Son num\u00e9ro de t\u00e9l\u00e9phone +About=\u00c0 propos +A\ sample\ delivery\ service\ built\ with\ Codename\ One=Un service de courses d'exemple cr\u00e9\u00e9 avec Codename One +Terms\ of\ service=Conditions d'utilisation +Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel est un exemple. Aucune course r\u00e9elle n'est r\u00e9serv\u00e9e, aucun chauffeur r\u00e9el n'est envoy\u00e9 et aucun argent n'est \u00e9chang\u00e9. +Privacy\ policy=Politique de confidentialit\u00e9 +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Votre position est envoy\u00e9e au serveur auquel vous \u00eates connect\u00e9 pendant vos courses, et \u00e0 personne d'autre. Les r\u00e9glages restent sur cet appareil. +Sign\ in=Se connecter +Enter\ your\ e-mail\ and\ password.=Saisissez votre e-mail et votre mot de passe. +{0}\ ft={0} pi +{0}\ mi={0} mi +{0}\ m={0} m +{0}\ km={0} km +{0}\ min={0} min +Finding\ a\ courier=Recherche d'un chauffeur +Courier\ on\ the\ way=Chauffeur en route +Courier\ at\ pickup=Chauffeur sur place +Completed=Termin\u00e9e +Cancelled\ by\ customer=Annul\u00e9e par le passager +Cancelled\ by\ courier=Annul\u00e9e par le chauffeur +No\ courier\ found=Aucun chauffeur trouv\u00e9 +Verify\ your\ number=V\u00e9rifiez votre num\u00e9ro +We\ sent\ a\ six-digit\ code\ to\ your\ phone.=Nous avons envoy\u00e9 un code \u00e0 six chiffres sur votre t\u00e9l\u00e9phone. +We\ sent\ a\ six-digit\ code\ to\ {0}.=Nous avons envoy\u00e9 un code \u00e0 six chiffres au {0}. +Code=Code +This\ is\ a\ development\ server,\ so\ no\ text\ was\ sent.\ Your\ code\ is\ {0}.=Ceci est un serveur de d\u00e9veloppement, aucun SMS n'a donc \u00e9t\u00e9 envoy\u00e9. Votre code est {0}. +Send\ a\ new\ code=Envoyer un nouveau code +A\ delivery\ when\ you\ need\ one,\ and\ a\ fair\ way\ to\ drive.=Une course quand il vous en faut une, et une fa\u00e7on \u00e9quitable de conduire. + +# Onboarding applications, payments, the admin console and the account. +1\ application\ to\ review=1 candidature \u00e0 examiner +{0}\ applications\ to\ review={0} candidatures \u00e0 examiner +1\ flagged\ account=1 compte signal\u00e9 +{0}\ flagged\ accounts={0} comptes signal\u00e9s +7\ days=7 jours +14\ days=14 jours +30\ days=30 jours +A\ car\ with\ four\ doors=Une voiture \u00e0 quatre portes +A\ onboarding\ licence=Un permis de conduire +A\ flag\ marks\ the\ account\ for\ the\ admins\ and\ changes\ nothing\ for\ its\ owner.\ A\ block\ signs\ it\ out\ and\ refuses\ it\ until\ lifted.=Un signalement d\u00e9signe le compte aux administrateurs et ne change rien pour son titulaire. Un blocage le d\u00e9connecte et le refuse jusqu'\u00e0 sa lev\u00e9e. +A\ passport\ or\ an\ identity\ card=Un passeport ou une carte d'identit\u00e9 +A\ photo\ of\ you=Une photo de vous +A\ ramp\ or\ a\ lift,\ and\ room\ to\ secure\ the\ chair=Une rampe ou un \u00e9l\u00e9vateur, et la place pour arrimer le fauteuil +A\ delivery\ shows\ up\ here\ as\ soon\ as\ it\ is\ asked\ for.=Une course appara\u00eet ici d\u00e8s qu'elle est demand\u00e9e. +A\ delivery\ shows\ up\ here\ when\ it\ is\ paid\ for.=Une course appara\u00eet ici une fois pay\u00e9e. +A\ sentence\ the\ record\ will\ keep=Une phrase qui restera au dossier +About\ ten\ minutes=Une dizaine de minutes +About\ you=\u00c0 propos de vous +Accepted=Accept\u00e9 +Account\ ending\ {0}=Compte se terminant par {0} +Account\ holder=Titulaire du compte +Account\ number=Num\u00e9ro de compte +Add\ a\ bank\ account=Ajouter un compte bancaire +Add\ a\ card=Ajouter une carte +Add\ a\ tip=Ajouter un pourboire +Added=Ajout\u00e9 +Admins=Administrateurs +All=Toutes +All\ of\ it\ goes\ to\ your\ courier.=Il revient int\u00e9gralement \u00e0 votre chauffeur. +All\ of\ it\ goes\ to\ {0}.=Il revient int\u00e9gralement \u00e0 {0}. +Another\ amount=Autre montant +Applicant=Candidat +Application=Candidature +Application\ received=Candidature re\u00e7ue +Application\ sent\ back=Candidature renvoy\u00e9e +Application\ sent.\ We\ will\ be\ in\ touch.=Candidature envoy\u00e9e. Nous vous recontacterons. +Application\ under\ review=Candidature en cours d'examen +Applications=Candidatures +Approve=Approuver +Approved=Approuv\u00e9e +Approved\ to\ drive=Autoris\u00e9 \u00e0 conduire +As\ it\ is\ written\ on\ your\ licence.\ Customers\ see\ only\ your\ first\ name.=Tel qu'il figure sur votre permis. Les passagers ne voient que votre pr\u00e9nom. +Average\ fare=Tarif moyen +Average\ wait=Attente moyenne +Back=Retour +Bank=Banque +Bank\ account=Compte bancaire +Base\ fare=Prise en charge +Block=Bloquer +Block\ account=Bloquer le compte +Blocked=Bloqu\u00e9 +Blue=Bleu +By\ kind\ of\ delivery=Par type de course +By\ outcome=Par issue +By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ documents\ are\ your\ own.=En envoyant, vous confirmez que tout ceci est exact et que les documents sont les v\u00f4tres. +Calling\ is\ not\ available\ on\ this\ device=Les appels ne sont pas disponibles sur cet appareil +Cancel\ this\ delivery=Annuler cette course +Cancelled=Annul\u00e9es +Cancelled\ by\ Morsel=Annul\u00e9e par Morsel +Card\ number=Num\u00e9ro de carte +Cash\ out=Encaisser +Change\ password=Changer le mot de passe +Change\ the\ password\ you\ sign\ in\ with=Changez le mot de passe de connexion +Changes\ needed=Modifications requises +Choose\ a\ delivery=Choisissez une course +Choose\ an\ amount\ for\ the\ tip=Choisissez le montant du pourboire +Choose\ how\ to\ pay\ for\ this\ delivery.=Choisissez comment payer cette course. +Collect\ {0}\ in\ cash=Encaissez {0} en esp\u00e8ces +Colour=Couleur +Comfort=Confort +Comfort\ is\ a\ newer,\ roomier\ car.\ XL\ seats\ six\ or\ more.=Confort : une voiture plus r\u00e9cente et plus spacieuse. XL : six places ou plus. +Commission,\ %=Commission, % +Contact=Contact +Continue=Continuer +Continue\ application=Reprendre la candidature +Current\ password=Mot de passe actuel +Date\ of\ birth=Date de naissance +Default=Par d\u00e9faut +Delete\ account=Supprimer le compte +Delete\ my\ account=Supprimer mon compte +Delete\ your\ account?=Supprimer votre compte ? +Discount=Remise +Distance=Distance +Documents=Documents +Documents\ checked=Documents v\u00e9rifi\u00e9s +Done=Termin\u00e9 +Draft=Brouillon +Drive\ with\ Morsel=Conduire avec Morsel +Couriers=Chauffeurs +Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Les chauffeurs postulent apr\u00e8s l'inscription : quelques informations sur vous et votre voiture, et cinq photos. En attendant, vous pouvez voyager. +Couriers\ online=Chauffeurs en ligne +Onboarding\ licence=Permis de conduire +Earn\ on\ your\ own\ schedule=Gagnez \u00e0 votre rythme +Earned\ onboarding=Gagn\u00e9 en conduisant +Every\ application\ has\ had\ its\ answer.=Toutes les candidatures ont re\u00e7u leur r\u00e9ponse. +Everyone=Tous +Expires=Expire +Expires\ {0}=Expire {0} +Fare=Tarif +Finish\ your\ application=Terminez votre candidature +Fix\ and\ resubmit=Corriger et renvoyer +Flag=Signaler +Flag\ account=Signaler le compte +Flag\ removed=Signalement retir\u00e9 +Flagged=Signal\u00e9 +Gender=Genre +Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Un dernier coup d'\u0153il. Vous pouvez tout modifier jusqu'\u00e0 l'envoi. +Hi,\ this\ is\ your\ Morsel\ courier.=Bonjour, c'est votre chauffeur Morsel. +Hi,\ this\ is\ your\ Morsel\ customer.=Bonjour, c'est votre passager Morsel. +History=Historique +I\ have\ finished=J'ai termin\u00e9 +I\ want\ to=Je veux +In\ good\ standing=En r\u00e8gle +Insurance=Assurance +Its\ number,\ its\ expiry\ date\ and\ a\ photo\ of\ it=Son num\u00e9ro, sa date d'expiration et une photo +Keep\ my\ account=Garder mon compte +Kind\ of\ delivery=Type de course +Legal\ name=Nom l\u00e9gal +Licence=Permis +Licence\ expires=Expiration du permis +Licence\ number=Num\u00e9ro de permis +Make=Marque +Man=Homme +Messages\ are\ not\ available\ on\ this\ device=Les messages ne sont pas disponibles sur cet appareil +Minimum\ fare=Tarif minimum +Missing=Manquant +Model=Mod\u00e8le +Moderation=Mod\u00e9ration +Most\ of\ it\ is\ taking\ five\ photos=L'essentiel, c'est cinq photos \u00e0 prendre +Multipliers=Multiplicateurs +Name\ an\ emergency\ contact\ in\ your\ profile\ first=Indiquez d'abord un contact d'urgence dans votre profil +Name\ on\ the\ card=Nom sur la carte +Name,\ emergency\ contact,\ home\ and\ work=Nom, contact d'urgence, domicile et travail +Needed=Requis +New\ password=Nouveau mot de passe +New\ password,\ again=Nouveau mot de passe, encore +No=Non +No\ payments\ yet=Aucun paiement pour l'instant +No\ deliveries\ here=Aucune course ici +Nobody\ matches=Aucun r\u00e9sultat +Non-binary=Non binaire +Not\ provided=Non fourni +Not\ set=Non d\u00e9fini +Not\ started=Non commenc\u00e9e +Nothing\ has\ been\ done\ to\ this\ account.=Rien n'a \u00e9t\u00e9 fait sur ce compte. +Nothing\ has\ been\ paid\ out\ yet.=Rien n'a encore \u00e9t\u00e9 vers\u00e9. +Nothing\ here=Rien ici +Open\ the\ secure\ page=Ouvrir la page s\u00e9curis\u00e9e +Paid=Pay\u00e9 +Paid\ in\ cash=Pay\u00e9 en esp\u00e8ces +Paid\ with=Pay\u00e9 avec +Payment\ failed=Paiement \u00e9chou\u00e9 +Payment\ pending=Paiement en attente +Payments=Paiements +Payouts=Versements +People\ who\ asked\ to\ drive,\ and\ their\ papers=Ceux qui ont demand\u00e9 \u00e0 conduire, et leurs papiers +Per\ kilometre=Par kilom\u00e8tre +Per\ minute=Par minute +Photo\ ID=Pi\u00e8ce d'identit\u00e9 +Photograph\ each\ one\ flat,\ with\ all\ four\ corners\ in\ the\ picture.=Photographiez chacun \u00e0 plat, les quatre coins visibles. +Photos\ added=Photos ajout\u00e9es +Pricing=Tarification +Profile=Profil +Provided=Fourni +Rather\ not\ say=Je pr\u00e9f\u00e8re ne pas le dire +Rating=Note +Read\ what\ was\ missing,\ put\ it\ right,\ and\ send\ the\ application\ again.=Lisez ce qui manquait, corrigez-le et renvoyez la candidature. +Ready\ to\ cash\ out=Pr\u00eat \u00e0 encaisser +Reason=Motif +Receipt=Re\u00e7u +Refund=Rembourser +Refund\ this\ delivery=Rembourser cette course +Refunded=Rembours\u00e9 +Registered\ and\ insured,\ with\ the\ papers\ to\ show\ it=Immatricul\u00e9e et assur\u00e9e, papiers \u00e0 l'appui +Rejected=Refus\u00e9 +Remove\ flag=Retirer le signalement +Remove\ this\ card=Retirer cette carte +Request\ {0}=Commander {0} +Requests\ accepted=Demandes accept\u00e9es +Requests\ by\ hour=Demandes par heure +Retake=Reprendre +Review\ and\ submit=V\u00e9rifier et envoyer +Reviewed=Examin\u00e9e +Reviewed\ by=Examin\u00e9e par +Customers=Passagers +Customers\ who\ ask\ for\ a\ woman\ at\ the\ wheel\ are\ offered\ to\ couriers\ who\ say\ they\ are\ one.=Les passagers qui demandent une femme au volant sont propos\u00e9s aux conductrices qui se d\u00e9clarent telles. +Right\ now=En ce moment +Roles\ changed=R\u00f4les modifi\u00e9s +Roles,\ flags,\ and\ blocking\ an\ account=R\u00f4les, signalements et blocage de comptes +Save\ card=Enregistrer la carte +Save\ prices=Enregistrer les tarifs +Saved\ places=Lieux enregistr\u00e9s +Say\ why,\ in\ a\ few\ words.=Dites pourquoi, en quelques mots. +Search\ by\ name\ or\ e-mail=Rechercher par nom ou e-mail +Seats\ for\ customers=Places passagers +Security\ code=Code de s\u00e9curit\u00e9 +Send\ back=Renvoyer +Send\ tip=Envoyer le pourboire +Saves\ {0}'s\ number\ with\ your\ account\ as\ your\ trip\ contact.\ No\ message\ is\ sent.=Enregistre le num\u00e9ro de {0} dans votre compte comme contact de trajet. Aucun message n'est envoy\u00e9. +Sent\ back=Renvoy\u00e9e +Service\ fee=Frais de service +Service\ fee,\ %=Frais de service, % +Spent\ riding=D\u00e9pens\u00e9 en courses +Standard=Standard +Start\ application=Commencer la candidature +Step\ {0}\ of\ {1}=\u00c9tape {0} sur {1} +Submit\ application=Envoyer la candidature +Submitted=Envoy\u00e9e +Surge,\ while\ demand\ is\ high=Majoration, quand la demande est forte +Takes\ a\ wheelchair=Accepte un fauteuil roulant +Tap\ a\ card\ to\ make\ it\ the\ one\ that\ pays\ unless\ you\ say\ otherwise.=Touchez une carte pour qu'elle paie par d\u00e9faut. +Tap\ to\ open=Touchez pour ouvrir +Tell\ us\ about\ you\ and\ your\ car,\ add\ five\ photos,\ and\ we\ take\ it\ from\ there.=Parlez-nous de vous et de votre voiture, ajoutez cinq photos, et nous faisons le reste. +Thank\ you.\ Your\ tip\ was\ sent.=Merci. Votre pourboire a \u00e9t\u00e9 envoy\u00e9. +That\ is\ not\ a\ card\ number.\ Check\ the\ digits.=Ce n'est pas un num\u00e9ro de carte. V\u00e9rifiez les chiffres. +That\ picture\ could\ not\ be\ read=Cette image n'a pas pu \u00eatre lue +That\ place\ could\ not\ be\ found.\ Choose\ it\ again.=Ce lieu est introuvable. Choisissez-le \u00e0 nouveau. +The\ busy\ hours,\ in\ UTC=Les heures de pointe, en UTC +The\ card\ was\ declined.\ Choose\ another\ way\ to\ pay\ in\ your\ wallet.=La carte a \u00e9t\u00e9 refus\u00e9e. Choisissez un autre moyen de paiement dans votre portefeuille. +The\ fare,\ in\ {0}=Le tarif, en {0} +The\ fee\ is\ added\ to\ the\ fare\ and\ paid\ by\ the\ customer.\ The\ commission\ is\ kept\ from\ the\ fare;\ the\ rest\ is\ the\ courier's.=Les frais s'ajoutent au tarif et sont pay\u00e9s par le passager. La commission est pr\u00e9lev\u00e9e sur le tarif ; le reste revient au chauffeur. +The\ last\ two\ weeks=Les deux derni\u00e8res semaines +The\ new\ prices\ are\ in\ force=Les nouveaux tarifs sont en vigueur +The\ number\ is\ on\ the\ front\ of\ the\ card.=Le num\u00e9ro figure au recto de la carte. +The\ page\ with\ the\ policy's\ dates\ on\ it=La page indiquant les dates de la police +The\ paper\ that\ says\ the\ car\ is\ yours=Le document attestant que la voiture est \u00e0 vous +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=La page du prestataire de paiement s'ouvre dans votre navigateur. Morsel ne voit jamais le num\u00e9ro. Revenez ici une fois termin\u00e9. +The\ delivery\ was\ refunded=La course a \u00e9t\u00e9 rembours\u00e9e +Refund\ pending=Remboursement en attente +Refund\ failed=Remboursement \u00e9chou\u00e9 +The\ refund\ is\ pending=Le remboursement est en attente +The\ customer\ and\ the\ courier\ are\ both\ told,\ and\ nobody\ is\ charged.=Le passager et le chauffeur sont pr\u00e9venus, et personne n'est d\u00e9bit\u00e9. +The\ security\ code\ is\ the\ three\ or\ four\ digits\ on\ the\ card.=Le code de s\u00e9curit\u00e9 correspond aux trois ou quatre chiffres de la carte. +The\ service's\ share=La part du service +The\ side\ with\ your\ photo\ on\ it=Le c\u00f4t\u00e9 portant votre photo +The\ two\ new\ passwords\ are\ not\ the\ same.=Les deux nouveaux mots de passe sont diff\u00e9rents. +This\ device\ cannot\ prepare\ a\ picture\ to\ send=Cet appareil ne peut pas pr\u00e9parer une image \u00e0 envoyer +This\ month=Ce mois-ci +This\ picture\ could\ not\ be\ opened=Cette image n'a pas pu \u00eatre ouverte +This\ screen\ becomes\ your\ courier's\ home\ the\ moment\ you\ are\ approved=Cet \u00e9cran devient votre accueil chauffeur d\u00e8s votre approbation +This\ server\ takes\ no\ real\ payments.\ Use\ the\ test\ card\ 4242\ 4242\ 4242\ 4242\ with\ any\ date\ ahead\ and\ any\ three\ digits.=Ce serveur n'accepte aucun paiement r\u00e9el. Utilisez la carte de test 4242 4242 4242 4242 avec une date future et trois chiffres quelconques. +Time=Temps +Tip\ offered\ first=Pourboire propos\u00e9 d'abord +Tips=Pourboires +Top\ couriers=Meilleurs chauffeurs +Total=Total +Trends=Tendances +Trip=Trajet +Type\ the\ account\ number.=Saisissez le num\u00e9ro de compte. +Unblock=D\u00e9bloquer +Unblocked=D\u00e9bloqu\u00e9 +Under\ review=En cours d'examen +Usually\ within\ two\ working\ days=G\u00e9n\u00e9ralement sous deux jours ouvr\u00e9s +Vehicle=V\u00e9hicule +Vehicle\ registration=Carte grise +View\ receipt=Voir le re\u00e7u +Waiting=En attente +Wallet=Portefeuille +We\ could\ not\ approve\ this\ yet=Nous n'avons pas encore pu l'approuver +We\ have\ everything\ we\ asked\ for.\ Someone\ on\ our\ team\ is\ reading\ it.=Nous avons tout ce que nous avons demand\u00e9. Un membre de l'\u00e9quipe la lit. +What\ a\ delivery\ costs,\ and\ the\ service's\ share=Le prix d'une course, et la part du service +What\ happens\ next=La suite +What\ the\ reviewer\ said=Ce qu'a dit l'examinateur +What\ was\ charged,\ and\ refunds=Les montants d\u00e9bit\u00e9s, et les remboursements +What\ you\ will\ need=Ce qu'il vous faut +Where\ your\ earnings\ are\ sent=O\u00f9 vos gains sont envoy\u00e9s +Why\ does\ {0}\ need\ the\ admins'\ attention?=Pourquoi {0} requiert-il l'attention des administrateurs ? +Woman=Femme +Write\ the\ expiry\ date\ as\ month\ and\ year\:\ 04/28.=Saisissez la date d'expiration sous la forme mois et ann\u00e9e : 04/28. +Year=Ann\u00e9e +Yes=Oui +You=Vous +You\ go\ online=Vous passez en ligne +Your\ application=Votre candidature +Your\ car=Votre voiture +Your\ card\ goes\ straight\ to\ the\ bank=Votre carte va directement \u00e0 la banque +Your\ cards,\ and\ which\ of\ them\ pays\ by\ default=Vos cartes, et celle qui paie par d\u00e9faut +Your\ documents=Vos documents +Your\ courier\ cannot\ be\ messaged\ right\ now=Impossible d'\u00e9crire \u00e0 votre chauffeur pour l'instant +Your\ face,\ in\ good\ light,\ no\ sunglasses=Votre visage, bien \u00e9clair\u00e9, sans lunettes de soleil +Your\ licence=Votre permis +Your\ password\ was\ changed=Votre mot de passe a \u00e9t\u00e9 modifi\u00e9 +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Your\ settings\ are\ kept\ with\ your\ account\ on\ that\ server.=Votre position est envoy\u00e9e au serveur auquel vous \u00eates connect\u00e9 pendant vos courses, et \u00e0 personne d'autre. Vos r\u00e9glages sont conserv\u00e9s avec votre compte sur ce serveur. +Your\ customer\ cannot\ be\ messaged\ right\ now=Impossible d'\u00e9crire \u00e0 votre passager pour l'instant +Your\ sign-in,\ your\ profile,\ your\ saved\ cards\ and\ your\ settings\ are\ removed\ for\ good.\ Deliveries\ you\ took\ stay\ in\ the\ service's\ records\ without\ your\ name.=Votre connexion, votre profil, vos cartes enregistr\u00e9es et vos r\u00e9glages sont supprim\u00e9s d\u00e9finitivement. Vos courses restent dans les registres du service sans votre nom. +{0}\ away=\u00e0 {0} +{0}\ can\ now\ drive={0} peut d\u00e9sormais conduire +{0}\ completed,\ {1}\ cancelled={0} termin\u00e9es, {1} annul\u00e9es +{0}\ days\ ago=il y a {0} jours +{0}\ goes\ back\ to\ the\ card\ it\ came\ from.={0} retourne sur la carte d'origine. +{0}\ is\ on\ its\ way\ to\ your\ bank={0} est en route vers votre banque +{0}\ is\ signed\ out\ everywhere\ and\ cannot\ sign\ in\ again\ until\ the\ block\ is\ lifted.={0} est d\u00e9connect\u00e9 partout et ne peut plus se connecter tant que le blocage n'est pas lev\u00e9. +{0}\ new\ accounts={0} nouveaux comptes +{0}\ of\ 5\ documents\ added.\ Pick\ up\ where\ you\ left\ off.={0} documents sur 5 ajout\u00e9s. Reprenez o\u00f9 vous en \u00e9tiez. +{0}\ deliveries={0} courses +{0}\ s={0} s +{0}\ sees\ what\ you\ write,\ and\ can\ apply\ again\ once\ it\ is\ put\ right.={0} voit ce que vous \u00e9crivez et peut postuler de nouveau une fois corrig\u00e9. +Fill\ in\ everything\ on\ this\ step\ to\ continue.=Remplissez tout sur cette \u00e9tape pour continuer. +None\ nearby=Aucun \u00e0 proximit\u00e9 +Pay\ the\ courier\ directly=Payez directement le chauffeur +Camera=Appareil photo +Gallery=Galerie +Fares\ collected=Recettes encaiss\u00e9es +Takes\ pets=Accepte les animaux +Customers\ travelling\ with\ a\ pet\ are\ offered\ to\ you=Les passagers voyageant avec un animal vous sont propos\u00e9s +Go=Aller +Nothing\ selected=Aucune s\u00e9lection +Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=Choisissez une ligne de la liste pour l'afficher ici. +Usage\ statistics=Statistiques d'utilisation +Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Partagez quels \u00e9crans et quelles fonctions sont utilis\u00e9s. Jamais votre nom, vos lieux ni vos donn\u00e9es de paiement. +Help\ improve\ Morsel=Aidez \u00e0 am\u00e9liorer Morsel +Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel peut compter quels \u00e9crans sont ouverts et quelles fonctions sont utilis\u00e9es, pour que nous puissions voir ce qui marche et corriger ce qui ne marche pas. +No\ personal\ details=Aucune donn\u00e9e personnelle +Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Jamais votre nom, votre e-mail, votre num\u00e9ro de t\u00e9l\u00e9phone, vos lieux ni les donn\u00e9es de votre carte +Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Activez ou d\u00e9sactivez-les \u00e0 tout moment dans R\u00e9glages +Share\ usage\ statistics=Partager les statistiques d'utilisation +With\ usage\ statistics\ turned\ on,\ the\ app\ also\ counts\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ and\ reports\ that\ to\ Codename\ One's\ analytics\ service\ under\ a\ made-up\ id\ for\ this\ device.\ Your\ name,\ e-mail,\ phone\ number,\ places\ and\ card\ details\ are\ never\ part\ of\ it,\ and\ turning\ it\ off\ in\ Settings\ stops\ it.=Lorsque les statistiques d'utilisation sont activ\u00e9es, l'application compte aussi quels \u00e9crans sont ouverts et quelles fonctions sont utilis\u00e9es, et le transmet au service d'analyse de Codename One sous un identifiant invent\u00e9 pour cet appareil. Votre nom, votre e-mail, votre num\u00e9ro de t\u00e9l\u00e9phone, vos lieux et les donn\u00e9es de votre carte n'en font jamais partie, et les d\u00e9sactiver dans R\u00e9glages y met fin. +Location\ is\ turned\ off.\ Try\ again=La localisation est d\u00e9sactiv\u00e9e. R\u00e9essayer +Can't\ find\ your\ location.\ Try\ again=Position introuvable. R\u00e9essayer +Still\ looking\ for\ your\ location.\ Try\ again=Recherche de votre position en cours. R\u00e9essayer +Looking\ for\ your\ location...=Recherche de votre position... diff --git a/scripts/morsel/common/src/main/l10n/Bundle_he.properties b/scripts/morsel/common/src/main/l10n/Bundle_he.properties new file mode 100644 index 00000000000..d5e8a0530de --- /dev/null +++ b/scripts/morsel/common/src/main/l10n/Bundle_he.properties @@ -0,0 +1,532 @@ +# Morsel in Hebrew. The key of every line is the English text the code +# names; see ui/Lang.java for how a line is found and how to add one. +@rtl=true +language.name=\u05e2\u05d1\u05e8\u05d9\u05ea + +Admin=\u05de\u05e0\u05d4\u05dc +Dashboard=\u05dc\u05d5\u05d7 \u05d1\u05e7\u05e8\u05d4 +Live\ map=\u05de\u05e4\u05d4 \u05d7\u05d9\u05d4 +Deliveries=\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +People=\u05d0\u05e0\u05e9\u05d9\u05dd +Operations=\u05ea\u05e4\u05e2\u05d5\u05dc +Morsel\ administration=\u05e0\u05d9\u05d4\u05d5\u05dc Morsel +Today=\u05d4\u05d9\u05d5\u05dd +Manage=\u05e0\u05d9\u05d4\u05d5\u05dc +Where\ every\ car\ online\ is=\u05d4\u05d9\u05db\u05df \u05e0\u05de\u05e6\u05d0 \u05db\u05dc \u05e8\u05db\u05d1 \u05de\u05d7\u05d5\u05d1\u05e8 +The\ most\ recent,\ in\ every\ state=\u05d4\u05d0\u05d7\u05e8\u05d5\u05e0\u05d5\u05ea, \u05d1\u05db\u05dc \u05de\u05e6\u05d1 +Roles,\ and\ suspending\ an\ account=\u05ea\u05e4\u05e7\u05d9\u05d3\u05d9\u05dd \u05d5\u05d4\u05e9\u05e2\u05d9\u05d9\u05ea \u05d7\u05e9\u05d1\u05d5\u05e0\u05d5\u05ea +Deliveries\ today=\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05d4\u05d9\u05d5\u05dd +Under\ way=\u05d1\u05d3\u05e8\u05da +Online=\u05de\u05d7\u05d5\u05d1\u05e8 +Offline=\u05de\u05e0\u05d5\u05ea\u05e7 +Fares=\u05d4\u05db\u05e0\u05e1\u05d5\u05ea +Cars\ move\ as\ their\ couriers\ report\ in.=\u05d4\u05e8\u05db\u05d1\u05d9\u05dd \u05d6\u05d6\u05d9\u05dd \u05dc\u05e4\u05d9 \u05d3\u05d9\u05d5\u05d5\u05d7\u05d9 \u05d4\u05e0\u05d4\u05d2\u05d9\u05dd. +No\ cars\ online=\u05d0\u05d9\u05df \u05e8\u05db\u05d1\u05d9\u05dd \u05de\u05d7\u05d5\u05d1\u05e8\u05d9\u05dd +1\ car\ online=\u05e8\u05db\u05d1 \u05d0\u05d7\u05d3 \u05de\u05d7\u05d5\u05d1\u05e8 +{0}\ cars\ online={0} \u05e8\u05db\u05d1\u05d9\u05dd \u05de\u05d7\u05d5\u05d1\u05e8\u05d9\u05dd +Suspended=\u05de\u05d5\u05e9\u05e2\u05d4 +Courier=\u05e0\u05d4\u05d2 +Customer=\u05e0\u05d5\u05e1\u05e2 +Verified=\u05de\u05d0\u05d5\u05de\u05ea +Not\ verified=\u05dc\u05d0 \u05de\u05d0\u05d5\u05de\u05ea +May\ go\ online\ and\ take\ deliveries=\u05e8\u05e9\u05d0\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05d5\u05dc\u05e7\u05d1\u05dc \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +May\ see\ and\ change\ everything\ here=\u05e8\u05e9\u05d0\u05d9 \u05dc\u05e8\u05d0\u05d5\u05ea \u05d5\u05dc\u05e9\u05e0\u05d5\u05ea \u05db\u05d0\u05df \u05d4\u05db\u05d5\u05dc +Signed\ out,\ and\ refused\ until\ lifted=\u05de\u05e0\u05d5\u05ea\u05e7, \u05d5\u05e0\u05d3\u05d7\u05d4 \u05e2\u05d3 \u05dc\u05d4\u05e1\u05e8\u05ea \u05d4\u05d4\u05e9\u05e2\u05d9\u05d4 +Access=\u05d2\u05d9\u05e9\u05d4 +Drive=\u05e0\u05d4\u05d9\u05d2\u05d4 +Your\ trips=\u05d4\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05e9\u05dc\u05da +Earnings=\u05d4\u05db\u05e0\u05e1\u05d5\u05ea +The\ request\ went\ to\ another\ courier=\u05d4\u05d1\u05e7\u05e9\u05d4 \u05e2\u05d1\u05e8\u05d4 \u05dc\u05e0\u05d4\u05d2 \u05d0\u05d7\u05e8 +The\ customer\ cancelled=\u05d4\u05e0\u05d5\u05e1\u05e2 \u05d1\u05d9\u05d8\u05dc +Today,\ {0}\ trips=\u05d4\u05d9\u05d5\u05dd, {0} \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +Looking\ for\ deliveries=\u05de\u05d7\u05e4\u05e9 \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +You\ are\ offline=\u05d0\u05d9\u05e0\u05da \u05de\u05d7\u05d5\u05d1\u05e8 +Trip\ complete\:\ {0}=\u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 \u05d4\u05d5\u05e9\u05dc\u05de\u05d4: {0} +Stay\ close\ to\ busy\ streets;\ requests\ come\ to\ the\ nearest\ car.=\u05d4\u05d9\u05e9\u05d0\u05e8 \u05dc\u05d9\u05d3 \u05e8\u05d7\u05d5\u05d1\u05d5\u05ea \u05e1\u05d5\u05d0\u05e0\u05d9\u05dd; \u05d4\u05d1\u05e7\u05e9\u05d5\u05ea \u05de\u05d2\u05d9\u05e2\u05d5\u05ea \u05dc\u05e8\u05db\u05d1 \u05d4\u05e7\u05e8\u05d5\u05d1 \u05d1\u05d9\u05d5\u05ea\u05e8. +Go\ online\ to\ start\ taking\ deliveries.=\u05d4\u05ea\u05d7\u05d1\u05e8 \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05dc\u05e7\u05d1\u05dc \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea. +GO=\u05e6\u05d0 +This\ week=\u05d4\u05e9\u05d1\u05d5\u05e2 +Trips=\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +All\ time=\u05e1\u05da \u05d4\u05db\u05d5\u05dc +Go\ offline=\u05d4\u05ea\u05e0\u05ea\u05e7 +Go\ online=\u05d4\u05ea\u05d7\u05d1\u05e8 +Delivery\ request=\u05d1\u05e7\u05e9\u05ea \u05e0\u05e1\u05d9\u05e2\u05d4 +{0}\ away,\ {1}=\u05d1\u05de\u05e8\u05d7\u05e7 {0}, {1} +Accept=\u05e7\u05d1\u05dc +Decline=\u05d3\u05d7\u05d4 +Pick\ up\ {0}=\u05dc\u05d0\u05e1\u05d5\u05e3 \u05d0\u05ea {0} +Waiting\ for\ {0}=\u05de\u05de\u05ea\u05d9\u05df \u05dc-{0} +On\ the\ trip=\u05d1\u05e0\u05e1\u05d9\u05e2\u05d4 +Drop-off=\u05d9\u05e2\u05d3 +Pickup=\u05d0\u05d9\u05e1\u05d5\u05e3 +I\ have\ arrived=\u05d4\u05d2\u05e2\u05ea\u05d9 +Start\ trip=\u05d4\u05ea\u05d7\u05dc \u05e0\u05e1\u05d9\u05e2\u05d4 +Complete\ trip=\u05e1\u05d9\u05d9\u05dd \u05e0\u05e1\u05d9\u05e2\u05d4 +Cancel\ delivery=\u05d1\u05d8\u05dc \u05e0\u05e1\u05d9\u05e2\u05d4 +Quiet\ delivery=\u05e0\u05e1\u05d9\u05e2\u05d4 \u05e9\u05e7\u05d8\u05d4 +Accessible\ vehicle=\u05e8\u05db\u05d1 \u05e0\u05d2\u05d9\u05e9 +Could\ not\ reach\ the\ server.\ Check\ your\ connection.=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05d2\u05d9\u05e2 \u05dc\u05e9\u05e8\u05ea. \u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8. +Please\ sign\ in\ again.=\u05e0\u05d0 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05de\u05d7\u05d3\u05e9. +Your\ account\ is\ not\ allowed\ to\ do\ that.=\u05dc\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da \u05d0\u05d9\u05df \u05d4\u05e8\u05e9\u05d0\u05d4 \u05dc\u05db\u05da. +Too\ many\ attempts.\ Wait\ a\ moment\ and\ try\ again.=\u05d9\u05d5\u05ea\u05e8 \u05de\u05d3\u05d9 \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d5\u05ea. \u05d4\u05de\u05ea\u05df \u05e8\u05d2\u05e2 \u05d5\u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1. +Too\ many\ attempts.\ Wait\ a\ minute\ and\ try\ again.=\u05d9\u05d5\u05ea\u05e8 \u05de\u05d3\u05d9 \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d5\u05ea. \u05d4\u05de\u05ea\u05df \u05d3\u05e7\u05d4 \u05d5\u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1. +Something\ went\ wrong\ ({0}).\ Try\ again.=\u05de\u05e9\u05d4\u05d5 \u05d4\u05e9\u05ea\u05d1\u05e9 ({0}). \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1. +That\ e-mail\ and\ password\ do\ not\ match\ an\ account.=\u05d4\u05d3\u05d5\u05d0"\u05dc \u05d5\u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05d0\u05d9\u05e0\u05dd \u05ea\u05d5\u05d0\u05de\u05d9\u05dd \u05dc\u05d7\u05e9\u05d1\u05d5\u05df. +Signing\ in\ failed.\ Try\ again.=\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4. \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1. +Where\ to?=\u05dc\u05d0\u05df? +Destination=\u05d9\u05e2\u05d3 +An\ address\ or\ a\ place=\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05d5 \u05de\u05e7\u05d5\u05dd +Nothing\ found=\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d3\u05d1\u05e8 +Current\ location=\u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05d4\u05e0\u05d5\u05db\u05d7\u05d9 +Delivery=\u05e0\u05e1\u05d9\u05e2\u05d4 +Your\ deliveries=\u05d4\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05e9\u05dc\u05da +Your\ courier\ is\ here=\u05d4\u05e0\u05d4\u05d2 \u05e9\u05dc\u05da \u05db\u05d0\u05df +{0}\ to\ destination={0} \u05e2\u05d3 \u05d4\u05d9\u05e2\u05d3 +Pickup\ in\ {0}=\u05d0\u05d9\u05e1\u05d5\u05e3 \u05d1\u05e2\u05d5\u05d3 {0} +Hello,\ {0}=\u05e9\u05dc\u05d5\u05dd, {0} +Where\ are\ you\ going?=\u05dc\u05d0\u05df \u05e0\u05d5\u05e1\u05e2\u05d9\u05dd? +Verify\ your\ phone\ number\ to\ request\ a\ delivery.=\u05d0\u05de\u05ea \u05d0\u05ea \u05de\u05e1\u05e4\u05e8 \u05d4\u05d8\u05dc\u05e4\u05d5\u05df \u05db\u05d3\u05d9 \u05dc\u05d4\u05d6\u05de\u05d9\u05df \u05e0\u05e1\u05d9\u05e2\u05d4. +Verify\ now=\u05d0\u05de\u05ea \u05e2\u05db\u05e9\u05d9\u05d5 +Home=\u05d1\u05d9\u05ea +Work=\u05e2\u05d1\u05d5\u05d3\u05d4 +Working\ out\ the\ fare=\u05de\u05d7\u05e9\u05d1 \u05d0\u05ea \u05d4\u05de\u05d7\u05d9\u05e8 +No\ couriers\ nearby\ right\ now=\u05d0\u05d9\u05df \u05db\u05e8\u05d2\u05e2 \u05e0\u05d4\u05d2\u05d9\u05dd \u05d1\u05e7\u05e8\u05d1\u05ea \u05de\u05e7\u05d5\u05dd +1\ courier\ nearby,\ {0}\ away=\u05e0\u05d4\u05d2 \u05d0\u05d7\u05d3 \u05d1\u05e7\u05e8\u05d1\u05ea \u05de\u05e7\u05d5\u05dd, \u05d1\u05de\u05e8\u05d7\u05e7 {0} +{0}\ couriers\ nearby,\ the\ closest\ {1}\ away={0} \u05e0\u05d4\u05d2\u05d9\u05dd \u05d1\u05e7\u05e8\u05d1\u05ea \u05de\u05e7\u05d5\u05dd, \u05d4\u05e7\u05e8\u05d5\u05d1 \u05d1\u05d9\u05d5\u05ea\u05e8 \u05d1\u05de\u05e8\u05d7\u05e7 {1} +Request\ delivery=\u05d4\u05d6\u05de\u05df \u05e0\u05e1\u05d9\u05e2\u05d4 +Cancel=\u05d1\u05d9\u05d8\u05d5\u05dc +Payment=\u05ea\u05e9\u05dc\u05d5\u05dd +Delivery\ preferences=\u05d4\u05e2\u05d3\u05e4\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d4 +Cash=\u05de\u05d6\u05d5\u05de\u05df +Card=\u05db\u05e8\u05d8\u05d9\u05e1 +Women\ couriers\ only=\u05e0\u05d4\u05d2\u05d5\u05ea \u05d1\u05dc\u05d1\u05d3 +Pet\ friendly=\u05d9\u05d3\u05d9\u05d3\u05d5\u05ea\u05d9 \u05dc\u05d7\u05d9\u05d5\u05ea \u05de\u05d7\u05de\u05d3 +To\ {0}=\u05d0\u05dc {0} +Nobody\ could\ take\ the\ delivery\ just\ now.\ You\ have\ not\ been\ charged.=\u05d0\u05e3 \u05d0\u05d7\u05d3 \u05dc\u05d0 \u05d9\u05db\u05d5\u05dc \u05d4\u05d9\u05d4 \u05dc\u05e7\u05d7\u05ea \u05d0\u05ea \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 \u05db\u05e2\u05ea. \u05dc\u05d0 \u05d7\u05d5\u05d9\u05d1\u05ea. +Your\ courier\ had\ to\ cancel.\ You\ have\ not\ been\ charged.=\u05d4\u05e0\u05d4\u05d2 \u05e0\u05d0\u05dc\u05e5 \u05dc\u05d1\u05d8\u05dc. \u05dc\u05d0 \u05d7\u05d5\u05d9\u05d1\u05ea. +The\ delivery\ was\ cancelled.=\u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 \u05d1\u05d5\u05d8\u05dc\u05d4. +OK=\u05d0\u05d9\u05e9\u05d5\u05e8 +Call=\u05d4\u05ea\u05e7\u05e9\u05e8 +Calls\ to\ your\ courier\ are\ not\ available\ on\ this\ server=\u05e9\u05d9\u05d7\u05d5\u05ea \u05dc\u05e0\u05d4\u05d2 \u05d0\u05d9\u05e0\u05df \u05d6\u05de\u05d9\u05e0\u05d5\u05ea \u05d1\u05e9\u05e8\u05ea \u05d6\u05d4 +Message=\u05d4\u05d5\u05d3\u05e2\u05d4 +Messages\ to\ your\ courier\ are\ not\ available\ on\ this\ server=\u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05dc\u05e0\u05d4\u05d2 \u05d0\u05d9\u05e0\u05df \u05d6\u05de\u05d9\u05e0\u05d5\u05ea \u05d1\u05e9\u05e8\u05ea \u05d6\u05d4 +Share=\u05e9\u05ea\u05e3 +I\ am\ on\ my\ way\ to\ {0}\ with\ {1}\ in\ a\ {2}\ ({3}).=\u05d0\u05e0\u05d9 \u05d1\u05d3\u05e8\u05da \u05d0\u05dc {0} \u05e2\u05dd {1} \u05d1\u05e8\u05db\u05d1 {2} ({3}). +You\ have\ arrived=\u05d4\u05d2\u05e2\u05ea +How\ was\ the\ delivery\ with\ {0}?=\u05d0\u05d9\u05da \u05d4\u05d9\u05d9\u05ea\u05d4 \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 \u05e2\u05dd {0}? +Thanks\ for\ the\ rating=\u05ea\u05d5\u05d3\u05d4 \u05e2\u05dc \u05d4\u05d3\u05d9\u05e8\u05d5\u05d2 +Not\ now=\u05dc\u05d0 \u05e2\u05db\u05e9\u05d9\u05d5 +Admin\ mode=\u05de\u05e6\u05d1 \u05de\u05e0\u05d4\u05dc +Courier\ mode=\u05de\u05e6\u05d1 \u05e0\u05d4\u05d2 +Customer\ mode=\u05de\u05e6\u05d1 \u05e0\u05d5\u05e1\u05e2 +Switch\ to=\u05e2\u05d1\u05d5\u05e8 \u05d0\u05dc +Riding=\u05e0\u05e1\u05d9\u05e2\u05d4 +Onboarding=\u05e0\u05d4\u05d9\u05d2\u05d4 +Administration=\u05e0\u05d9\u05d4\u05d5\u05dc +Settings=\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea +Sign\ out=\u05d4\u05ea\u05e0\u05ea\u05e7 +No\ connection=\u05d0\u05d9\u05df \u05d7\u05d9\u05d1\u05d5\u05e8 +The\ server\ at\ {0}\ did\ not\ answer.=\u05d4\u05e9\u05e8\u05ea \u05d1\u05db\u05ea\u05d5\u05d1\u05ea {0} \u05dc\u05d0 \u05e2\u05e0\u05d4. +Try\ again=\u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1 +Server\ address=\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05e9\u05e8\u05ea +Create\ an\ account=\u05e6\u05d5\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df +Name=\u05e9\u05dd +What\ couriers\ will\ call\ you=\u05db\u05da \u05d9\u05e7\u05e8\u05d0\u05d5 \u05dc\u05da \u05d4\u05e0\u05d4\u05d2\u05d9\u05dd +E-mail=\u05d3\u05d5\u05d0"\u05dc +Mobile\ number=\u05de\u05e1\u05e4\u05e8 \u05e0\u05d9\u05d9\u05d3 +Password=\u05e1\u05d9\u05e1\u05de\u05d4 +At\ least\ 8\ characters=\u05dc\u05e4\u05d7\u05d5\u05ea 8 \u05ea\u05d5\u05d5\u05d9\u05dd +I\ want\ to\ drive=\u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6\u05d4 \u05dc\u05e0\u05d4\u05d5\u05d2 +Take\ delivery\ requests\ and\ earn=\u05e7\u05d1\u05dc \u05d1\u05e7\u05e9\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d4 \u05d5\u05d4\u05e8\u05d5\u05d5\u05d7 +Car=\u05e8\u05db\u05d1 +Blue\ Toyota\ Prius=\u05d8\u05d5\u05d9\u05d5\u05d8\u05d4 \u05e4\u05e8\u05d9\u05d5\u05e1 \u05db\u05d7\u05d5\u05dc\u05d4 +Licence\ plate=\u05dc\u05d5\u05d7\u05d9\u05ea \u05e8\u05d9\u05e9\u05d5\u05d9 +Create\ account=\u05e6\u05d5\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df +No\ deliveries\ yet=\u05d0\u05d9\u05df \u05e2\u05d3\u05d9\u05d9\u05df \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +{0}\ with\ {1}={0} \u05e2\u05dd {1} +Server=\u05e9\u05e8\u05ea +The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=\u05db\u05ea\u05d5\u05d1\u05ea \u05e9\u05e8\u05ea Morsel \u05e9\u05d4\u05d0\u05e4\u05dc\u05d9\u05e7\u05e6\u05d9\u05d4 \u05e4\u05d5\u05e0\u05d4 \u05d0\u05dc\u05d9\u05d5. \u05e9\u05d9\u05e0\u05d5\u05d9 \u05e9\u05dc\u05d4 \u05d9\u05e0\u05ea\u05e7 \u05d0\u05d5\u05ea\u05da. +Address=\u05db\u05ea\u05d5\u05d1\u05ea +Save=\u05e9\u05de\u05d5\u05e8 +Account=\u05d7\u05e9\u05d1\u05d5\u05df +Needed\ before\ your\ first\ delivery=\u05e0\u05d3\u05e8\u05e9 \u05dc\u05e4\u05e0\u05d9 \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 \u05d4\u05e8\u05d0\u05e9\u05d5\u05e0\u05d4 +Verify=\u05d0\u05de\u05ea +Any\ courier=\u05db\u05dc \u05e0\u05d4\u05d2 +Women\ only=\u05e0\u05e9\u05d9\u05dd \u05d1\u05dc\u05d1\u05d3 +Ask\ the\ courier\ to\ keep\ conversation\ short=\u05d1\u05e7\u05e9 \u05de\u05d4\u05e0\u05d4\u05d2 \u05dc\u05de\u05e2\u05d8 \u05d1\u05e9\u05d9\u05d7\u05d4 +Only\ cars\ that\ take\ a\ wheelchair=\u05e8\u05e7 \u05e8\u05db\u05d1\u05d9\u05dd \u05d4\u05de\u05ea\u05d0\u05d9\u05de\u05d9\u05dd \u05dc\u05db\u05d9\u05e1\u05d0 \u05d2\u05dc\u05d2\u05dc\u05d9\u05dd +Tell\ the\ courier\ an\ animal\ is\ coming=\u05d4\u05d5\u05d3\u05e2 \u05dc\u05e0\u05d4\u05d2 \u05e9\u05de\u05e6\u05d8\u05e8\u05e4\u05ea \u05d7\u05d9\u05d4 +Pay\ with=\u05ea\u05e9\u05dc\u05d5\u05dd \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea +Tip=\u05d8\u05d9\u05e4 +None=\u05dc\u05dc\u05d0 +Notifications=\u05d4\u05ea\u05e8\u05d0\u05d5\u05ea +Delivery\ updates=\u05e2\u05d3\u05db\u05d5\u05e0\u05d9 \u05e0\u05e1\u05d9\u05e2\u05d4 +Courier\ found,\ arriving,\ at\ the\ door=\u05e0\u05de\u05e6\u05d0 \u05e0\u05d4\u05d2, \u05de\u05ea\u05e7\u05e8\u05d1, \u05d1\u05e4\u05ea\u05d7 +Receipts=\u05e7\u05d1\u05dc\u05d5\u05ea +After\ every\ delivery=\u05d0\u05d7\u05e8\u05d9 \u05db\u05dc \u05e0\u05e1\u05d9\u05e2\u05d4 +Offers=\u05de\u05d1\u05e6\u05e2\u05d9\u05dd +Discounts\ and\ news=\u05d4\u05e0\u05d7\u05d5\u05ea \u05d5\u05d7\u05d3\u05e9\u05d5\u05ea +Appearance=\u05de\u05e8\u05d0\u05d4 +Language=\u05e9\u05e4\u05d4 +Theme=\u05e2\u05e8\u05db\u05ea \u05e0\u05d5\u05e9\u05d0 +System=\u05de\u05e2\u05e8\u05db\u05ea +Light=\u05d1\u05d4\u05d9\u05e8 +Dark=\u05db\u05d4\u05d4 +Distances=\u05de\u05e8\u05d7\u05e7\u05d9\u05dd +Kilometres=\u05e7\u05d9\u05dc\u05d5\u05de\u05d8\u05e8\u05d9\u05dd +Miles=\u05de\u05d9\u05d9\u05dc\u05d9\u05dd +Privacy\ and\ safety=\u05e4\u05e8\u05d8\u05d9\u05d5\u05ea \u05d5\u05d1\u05d8\u05d9\u05d7\u05d5\u05ea +Share\ my\ trips=\u05e9\u05ea\u05e3 \u05d0\u05ea \u05d4\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05e9\u05dc\u05d9 +Emergency\ contact=\u05d0\u05d9\u05e9 \u05e7\u05e9\u05e8 \u05dc\u05d7\u05d9\u05e8\u05d5\u05dd +Their\ name=\u05d4\u05e9\u05dd +Their\ phone\ number=\u05de\u05e1\u05e4\u05e8 \u05d4\u05d8\u05dc\u05e4\u05d5\u05df +About=\u05d0\u05d5\u05d3\u05d5\u05ea +A\ sample\ delivery\ service\ built\ with\ Codename\ One=\u05e9\u05d9\u05e8\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05dc\u05d3\u05d5\u05d2\u05de\u05d4 \u05e9\u05e0\u05d1\u05e0\u05d4 \u05e2\u05dd Codename One +Terms\ of\ service=\u05ea\u05e0\u05d0\u05d9 \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea +Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel \u05d4\u05d9\u05d0 \u05d3\u05d5\u05d2\u05de\u05d4. \u05dc\u05d0 \u05de\u05d5\u05d6\u05de\u05e0\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05d0\u05de\u05d9\u05ea\u05d9\u05d5\u05ea, \u05dc\u05d0 \u05e0\u05e9\u05dc\u05d7\u05d9\u05dd \u05e0\u05d4\u05d2\u05d9\u05dd \u05d0\u05de\u05d9\u05ea\u05d9\u05d9\u05dd \u05d5\u05db\u05e1\u05e3 \u05d0\u05d9\u05e0\u05d5 \u05de\u05d7\u05dc\u05d9\u05e3 \u05d9\u05d3\u05d9\u05d9\u05dd. +Privacy\ policy=\u05de\u05d3\u05d9\u05e0\u05d9\u05d5\u05ea \u05e4\u05e8\u05d8\u05d9\u05d5\u05ea +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=\u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05dc\u05da \u05e0\u05e9\u05dc\u05d7 \u05dc\u05e9\u05e8\u05ea \u05e9\u05d0\u05dc\u05d9\u05d5 \u05d4\u05ea\u05d7\u05d1\u05e8\u05ea \u05d1\u05d6\u05de\u05df \u05e0\u05e1\u05d9\u05e2\u05d4 \u05d0\u05d5 \u05e0\u05d4\u05d9\u05d2\u05d4, \u05d5\u05dc\u05d0\u05e3 \u05d0\u05d7\u05d3 \u05d0\u05d7\u05e8. \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e0\u05e9\u05de\u05e8\u05d5\u05ea \u05d1\u05de\u05db\u05e9\u05d9\u05e8 \u05d6\u05d4. +Sign\ in=\u05d4\u05ea\u05d7\u05d1\u05e8 +Enter\ your\ e-mail\ and\ password.=\u05d4\u05d6\u05df \u05d3\u05d5\u05d0"\u05dc \u05d5\u05e1\u05d9\u05e1\u05de\u05d4. +{0}\ ft={0} \u05e8\u05d2\u05dc +{0}\ mi={0} \u05de\u05d9\u05d9\u05dc +{0}\ m={0} \u05de' +{0}\ km={0} \u05e7"\u05de +{0}\ min={0} \u05d3\u05e7' +Finding\ a\ courier=\u05de\u05d7\u05e4\u05e9 \u05e0\u05d4\u05d2 +Courier\ on\ the\ way=\u05d4\u05e0\u05d4\u05d2 \u05d1\u05d3\u05e8\u05da +Courier\ at\ pickup=\u05d4\u05e0\u05d4\u05d2 \u05d1\u05e0\u05e7\u05d5\u05d3\u05ea \u05d4\u05d0\u05d9\u05e1\u05d5\u05e3 +Completed=\u05d4\u05d5\u05e9\u05dc\u05de\u05d4 +Cancelled\ by\ customer=\u05d1\u05d5\u05d8\u05dc\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 \u05d4\u05e0\u05d5\u05e1\u05e2 +Cancelled\ by\ courier=\u05d1\u05d5\u05d8\u05dc\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 \u05d4\u05e0\u05d4\u05d2 +No\ courier\ found=\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05e0\u05d4\u05d2 +Verify\ your\ number=\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05de\u05e1\u05e4\u05e8 +We\ sent\ a\ six-digit\ code\ to\ your\ phone.=\u05e9\u05dc\u05d7\u05e0\u05d5 \u05e7\u05d5\u05d3 \u05d1\u05df \u05e9\u05e9 \u05e1\u05e4\u05e8\u05d5\u05ea \u05dc\u05d8\u05dc\u05e4\u05d5\u05df \u05e9\u05dc\u05da. +We\ sent\ a\ six-digit\ code\ to\ {0}.=\u05e9\u05dc\u05d7\u05e0\u05d5 \u05e7\u05d5\u05d3 \u05d1\u05df \u05e9\u05e9 \u05e1\u05e4\u05e8\u05d5\u05ea \u05d0\u05dc {0}. +Code=\u05e7\u05d5\u05d3 +This\ is\ a\ development\ server,\ so\ no\ text\ was\ sent.\ Your\ code\ is\ {0}.=\u05d6\u05d4\u05d5 \u05e9\u05e8\u05ea \u05e4\u05d9\u05ea\u05d5\u05d7, \u05d5\u05dc\u05db\u05df \u05dc\u05d0 \u05e0\u05e9\u05dc\u05d7\u05d4 \u05d4\u05d5\u05d3\u05e2\u05d4. \u05d4\u05e7\u05d5\u05d3 \u05e9\u05dc\u05da \u05d4\u05d5\u05d0 {0}. +Send\ a\ new\ code=\u05e9\u05dc\u05d7 \u05e7\u05d5\u05d3 \u05d7\u05d3\u05e9 +A\ delivery\ when\ you\ need\ one,\ and\ a\ fair\ way\ to\ drive.=\u05e0\u05e1\u05d9\u05e2\u05d4 \u05db\u05e9\u05e6\u05e8\u05d9\u05da, \u05d5\u05d3\u05e8\u05da \u05d4\u05d5\u05d2\u05e0\u05ea \u05dc\u05e0\u05d4\u05d5\u05d2. + +# Onboarding applications, payments, the admin console and the account. +1\ application\ to\ review=\u05d1\u05e7\u05e9\u05d4 \u05d0\u05d7\u05ea \u05dc\u05d1\u05d3\u05d9\u05e7\u05d4 +{0}\ applications\ to\ review={0} \u05d1\u05e7\u05e9\u05d5\u05ea \u05dc\u05d1\u05d3\u05d9\u05e7\u05d4 +1\ flagged\ account=\u05d7\u05e9\u05d1\u05d5\u05df \u05de\u05e1\u05d5\u05de\u05df \u05d0\u05d7\u05d3 +{0}\ flagged\ accounts={0} \u05d7\u05e9\u05d1\u05d5\u05e0\u05d5\u05ea \u05de\u05e1\u05d5\u05de\u05e0\u05d9\u05dd +7\ days=7 \u05d9\u05de\u05d9\u05dd +14\ days=14 \u05d9\u05de\u05d9\u05dd +30\ days=30 \u05d9\u05de\u05d9\u05dd +A\ car\ with\ four\ doors=\u05e8\u05db\u05d1 \u05e2\u05dd \u05d0\u05e8\u05d1\u05e2 \u05d3\u05dc\u05ea\u05d5\u05ea +A\ onboarding\ licence=\u05e8\u05d9\u05e9\u05d9\u05d5\u05df \u05e0\u05d4\u05d9\u05d2\u05d4 +A\ flag\ marks\ the\ account\ for\ the\ admins\ and\ changes\ nothing\ for\ its\ owner.\ A\ block\ signs\ it\ out\ and\ refuses\ it\ until\ lifted.=\u05e1\u05d9\u05de\u05d5\u05df \u05de\u05e4\u05e0\u05d4 \u05d0\u05ea \u05ea\u05e9\u05d5\u05de\u05ea \u05dc\u05d1 \u05d4\u05de\u05e0\u05d4\u05dc\u05d9\u05dd \u05dc\u05d7\u05e9\u05d1\u05d5\u05df \u05d5\u05d0\u05d9\u05e0\u05d5 \u05de\u05e9\u05e0\u05d4 \u05d3\u05d1\u05e8 \u05dc\u05d1\u05e2\u05dc\u05d9\u05d5. \u05d7\u05e1\u05d9\u05de\u05d4 \u05de\u05e0\u05ea\u05e7\u05ea \u05d0\u05d5\u05ea\u05d5 \u05d5\u05d3\u05d5\u05d7\u05d4 \u05d0\u05d5\u05ea\u05d5 \u05e2\u05d3 \u05e9\u05ea\u05d5\u05e1\u05e8. +A\ passport\ or\ an\ identity\ card=\u05d3\u05e8\u05db\u05d5\u05df \u05d0\u05d5 \u05ea\u05e2\u05d5\u05d3\u05ea \u05d6\u05d4\u05d5\u05ea +A\ photo\ of\ you=\u05ea\u05de\u05d5\u05e0\u05d4 \u05e9\u05dc\u05da +A\ ramp\ or\ a\ lift,\ and\ room\ to\ secure\ the\ chair=\u05e8\u05de\u05e4\u05d4 \u05d0\u05d5 \u05de\u05e2\u05dc\u05d5\u05df, \u05d5\u05de\u05e7\u05d5\u05dd \u05dc\u05e2\u05d2\u05df \u05d0\u05ea \u05d4\u05db\u05d9\u05e1\u05d0 +A\ delivery\ shows\ up\ here\ as\ soon\ as\ it\ is\ asked\ for.=\u05e0\u05e1\u05d9\u05e2\u05d4 \u05de\u05d5\u05e4\u05d9\u05e2\u05d4 \u05db\u05d0\u05df \u05d1\u05e8\u05d2\u05e2 \u05e9\u05de\u05d6\u05de\u05d9\u05e0\u05d9\u05dd \u05d0\u05d5\u05ea\u05d4. +A\ delivery\ shows\ up\ here\ when\ it\ is\ paid\ for.=\u05e0\u05e1\u05d9\u05e2\u05d4 \u05de\u05d5\u05e4\u05d9\u05e2\u05d4 \u05db\u05d0\u05df \u05dc\u05d0\u05d7\u05e8 \u05e9\u05e9\u05d5\u05dc\u05de\u05d4. +A\ sentence\ the\ record\ will\ keep=\u05de\u05e9\u05e4\u05d8 \u05e9\u05d9\u05d9\u05e9\u05de\u05e8 \u05d1\u05e8\u05e9\u05d5\u05de\u05d4 +About\ ten\ minutes=\u05db\u05e2\u05e9\u05e8 \u05d3\u05e7\u05d5\u05ea +About\ you=\u05e2\u05dc\u05d9\u05da +Accepted=\u05d4\u05ea\u05e7\u05d1\u05dc +Account\ ending\ {0}=\u05d7\u05e9\u05d1\u05d5\u05df \u05d4\u05de\u05e1\u05ea\u05d9\u05d9\u05dd \u05d1-{0} +Account\ holder=\u05d1\u05e2\u05dc \u05d4\u05d7\u05e9\u05d1\u05d5\u05df +Account\ number=\u05de\u05e1\u05e4\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df +Add\ a\ bank\ account=\u05d4\u05d5\u05e1\u05e4\u05ea \u05d7\u05e9\u05d1\u05d5\u05df \u05d1\u05e0\u05e7 +Add\ a\ card=\u05d4\u05d5\u05e1\u05e4\u05ea \u05db\u05e8\u05d8\u05d9\u05e1 +Add\ a\ tip=\u05d4\u05d5\u05e1\u05e4\u05ea \u05d8\u05d9\u05e4 +Added=\u05e0\u05d5\u05e1\u05e3 +Admins=\u05de\u05e0\u05d4\u05dc\u05d9\u05dd +All=\u05d4\u05db\u05d5\u05dc +All\ of\ it\ goes\ to\ your\ courier.=\u05db\u05d5\u05dc\u05d5 \u05e2\u05d5\u05d1\u05e8 \u05dc\u05e0\u05d4\u05d2 \u05e9\u05dc\u05da. +All\ of\ it\ goes\ to\ {0}.=\u05db\u05d5\u05dc\u05d5 \u05e2\u05d5\u05d1\u05e8 \u05d0\u05dc {0}. +Another\ amount=\u05e1\u05db\u05d5\u05dd \u05d0\u05d7\u05e8 +Applicant=\u05d4\u05de\u05d1\u05e7\u05e9 +Application=\u05d1\u05e7\u05e9\u05d4 +Application\ received=\u05d4\u05d1\u05e7\u05e9\u05d4 \u05d4\u05ea\u05e7\u05d1\u05dc\u05d4 +Application\ sent\ back=\u05d4\u05d1\u05e7\u05e9\u05d4 \u05d4\u05d5\u05d7\u05d6\u05e8\u05d4 +Application\ sent.\ We\ will\ be\ in\ touch.=\u05d4\u05d1\u05e7\u05e9\u05d4 \u05e0\u05e9\u05dc\u05d7\u05d4. \u05e0\u05d9\u05e6\u05d5\u05e8 \u05e7\u05e9\u05e8. +Application\ under\ review=\u05d4\u05d1\u05e7\u05e9\u05d4 \u05d1\u05d1\u05d3\u05d9\u05e7\u05d4 +Applications=\u05d1\u05e7\u05e9\u05d5\u05ea +Approve=\u05d0\u05d9\u05e9\u05d5\u05e8 +Approved=\u05d0\u05d5\u05e9\u05e8\u05d4 +Approved\ to\ drive=\u05d0\u05d5\u05e9\u05e8 \u05dc\u05e0\u05d4\u05d5\u05d2 +As\ it\ is\ written\ on\ your\ licence.\ Customers\ see\ only\ your\ first\ name.=\u05db\u05e4\u05d9 \u05e9\u05db\u05ea\u05d5\u05d1 \u05d1\u05e8\u05d9\u05e9\u05d9\u05d5\u05df. \u05d4\u05e0\u05d5\u05e1\u05e2\u05d9\u05dd \u05e8\u05d5\u05d0\u05d9\u05dd \u05e8\u05e7 \u05d0\u05ea \u05e9\u05de\u05da \u05d4\u05e4\u05e8\u05d8\u05d9. +Average\ fare=\u05de\u05d7\u05d9\u05e8 \u05de\u05de\u05d5\u05e6\u05e2 +Average\ wait=\u05d4\u05de\u05ea\u05e0\u05d4 \u05de\u05de\u05d5\u05e6\u05e2\u05ea +Back=\u05d7\u05d6\u05e8\u05d4 +Bank=\u05d1\u05e0\u05e7 +Bank\ account=\u05d7\u05e9\u05d1\u05d5\u05df \u05d1\u05e0\u05e7 +Base\ fare=\u05de\u05d7\u05d9\u05e8 \u05d1\u05e1\u05d9\u05e1 +Block=\u05d7\u05e1\u05d9\u05de\u05d4 +Block\ account=\u05d7\u05e1\u05d9\u05de\u05ea \u05d7\u05e9\u05d1\u05d5\u05df +Blocked=\u05d7\u05e1\u05d5\u05dd +Blue=\u05db\u05d7\u05d5\u05dc +By\ kind\ of\ delivery=\u05dc\u05e4\u05d9 \u05e1\u05d5\u05d2 \u05e0\u05e1\u05d9\u05e2\u05d4 +By\ outcome=\u05dc\u05e4\u05d9 \u05ea\u05d5\u05e6\u05d0\u05d4 +By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ documents\ are\ your\ own.=\u05d1\u05e9\u05dc\u05d9\u05d7\u05d4 \u05d0\u05ea\u05d4 \u05de\u05d0\u05e9\u05e8 \u05e9\u05db\u05dc \u05d4\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05db\u05d5\u05e0\u05d9\u05dd \u05d5\u05e9\u05d4\u05de\u05e1\u05de\u05db\u05d9\u05dd \u05e9\u05dc\u05da. +Calling\ is\ not\ available\ on\ this\ device=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05ea\u05e7\u05e9\u05e8 \u05de\u05de\u05db\u05e9\u05d9\u05e8 \u05d6\u05d4 +Cancel\ this\ delivery=\u05d1\u05d9\u05d8\u05d5\u05dc \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 +Cancelled=\u05d1\u05d5\u05d8\u05dc\u05d5 +Cancelled\ by\ Morsel=\u05d1\u05d5\u05d8\u05dc\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 Morsel +Card\ number=\u05de\u05e1\u05e4\u05e8 \u05db\u05e8\u05d8\u05d9\u05e1 +Cash\ out=\u05de\u05e9\u05d9\u05db\u05d4 +Change\ password=\u05e9\u05d9\u05e0\u05d5\u05d9 \u05e1\u05d9\u05e1\u05de\u05d4 +Change\ the\ password\ you\ sign\ in\ with=\u05e9\u05d9\u05e0\u05d5\u05d9 \u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05d0\u05d9\u05ea\u05d4 \u05e0\u05db\u05e0\u05e1\u05d9\u05dd +Changes\ needed=\u05e0\u05d3\u05e8\u05e9\u05d9\u05dd \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd +Choose\ a\ delivery=\u05d1\u05d7\u05d9\u05e8\u05ea \u05e0\u05e1\u05d9\u05e2\u05d4 +Choose\ an\ amount\ for\ the\ tip=\u05d9\u05e9 \u05dc\u05d1\u05d7\u05d5\u05e8 \u05e1\u05db\u05d5\u05dd \u05dc\u05d8\u05d9\u05e4 +Choose\ how\ to\ pay\ for\ this\ delivery.=\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05de\u05e6\u05e2\u05d9 \u05ea\u05e9\u05dc\u05d5\u05dd \u05dc\u05e0\u05e1\u05d9\u05e2\u05d4 \u05d6\u05d5. +Collect\ {0}\ in\ cash=\u05dc\u05d2\u05d1\u05d5\u05ea {0} \u05d1\u05de\u05d6\u05d5\u05de\u05df +Colour=\u05e6\u05d1\u05e2 +Comfort=\u05e7\u05d5\u05de\u05e4\u05d5\u05e8\u05d8 +Comfort\ is\ a\ newer,\ roomier\ car.\ XL\ seats\ six\ or\ more.=\u05e7\u05d5\u05de\u05e4\u05d5\u05e8\u05d8 \u05d4\u05d5\u05d0 \u05e8\u05db\u05d1 \u05d7\u05d3\u05e9 \u05d5\u05de\u05e8\u05d5\u05d5\u05d7 \u05d9\u05d5\u05ea\u05e8. XL \u05de\u05d5\u05e9\u05d9\u05d1 \u05e9\u05d9\u05e9\u05d4 \u05d0\u05d5 \u05d9\u05d5\u05ea\u05e8. +Commission,\ %=\u05e2\u05de\u05dc\u05d4, % +Contact=\u05e4\u05e8\u05d8\u05d9 \u05e7\u05e9\u05e8 +Continue=\u05d4\u05de\u05e9\u05da +Continue\ application=\u05d4\u05de\u05e9\u05da \u05d4\u05d1\u05e7\u05e9\u05d4 +Current\ password=\u05e1\u05d9\u05e1\u05de\u05d4 \u05e0\u05d5\u05db\u05d7\u05d9\u05ea +Date\ of\ birth=\u05ea\u05d0\u05e8\u05d9\u05da \u05dc\u05d9\u05d3\u05d4 +Default=\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc +Delete\ account=\u05de\u05d7\u05d9\u05e7\u05ea \u05d7\u05e9\u05d1\u05d5\u05df +Delete\ my\ account=\u05de\u05d7\u05d9\u05e7\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05d9 +Delete\ your\ account?=\u05dc\u05de\u05d7\u05d5\u05e7 \u05d0\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df? +Discount=\u05d4\u05e0\u05d7\u05d4 +Distance=\u05de\u05e8\u05d7\u05e7 +Documents=\u05de\u05e1\u05de\u05db\u05d9\u05dd +Documents\ checked=\u05d4\u05de\u05e1\u05de\u05db\u05d9\u05dd \u05e0\u05d1\u05d3\u05e7\u05d9\u05dd +Done=\u05e1\u05d9\u05d5\u05dd +Draft=\u05d8\u05d9\u05d5\u05d8\u05d4 +Drive\ with\ Morsel=\u05dc\u05e0\u05d4\u05d5\u05d2 \u05e2\u05dd Morsel +Couriers=\u05e0\u05d4\u05d2\u05d9\u05dd +Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=\u05e0\u05d4\u05d2\u05d9\u05dd \u05de\u05d2\u05d9\u05e9\u05d9\u05dd \u05d1\u05e7\u05e9\u05d4 \u05dc\u05d0\u05d7\u05e8 \u05d4\u05d4\u05e8\u05e9\u05de\u05d4: \u05db\u05de\u05d4 \u05e4\u05e8\u05d8\u05d9\u05dd \u05e2\u05dc\u05d9\u05da \u05d5\u05e2\u05dc \u05d4\u05e8\u05db\u05d1, \u05d5\u05d7\u05de\u05e9 \u05ea\u05de\u05d5\u05e0\u05d5\u05ea. \u05d1\u05d9\u05e0\u05ea\u05d9\u05d9\u05dd \u05d0\u05e4\u05e9\u05e8 \u05dc\u05e0\u05e1\u05d5\u05e2. +Couriers\ online=\u05e0\u05d4\u05d2\u05d9\u05dd \u05de\u05d7\u05d5\u05d1\u05e8\u05d9\u05dd +Onboarding\ licence=\u05e8\u05d9\u05e9\u05d9\u05d5\u05df \u05e0\u05d4\u05d9\u05d2\u05d4 +Earn\ on\ your\ own\ schedule=\u05dc\u05d4\u05e8\u05d5\u05d5\u05d9\u05d7 \u05d1\u05d6\u05de\u05df \u05e9\u05dc\u05da +Earned\ onboarding=\u05d4\u05e8\u05d5\u05d5\u05d9\u05d7 \u05d1\u05e0\u05d4\u05d9\u05d2\u05d4 +Every\ application\ has\ had\ its\ answer.=\u05db\u05dc \u05d4\u05d1\u05e7\u05e9\u05d5\u05ea \u05e0\u05e2\u05e0\u05d5. +Everyone=\u05db\u05d5\u05dc\u05dd +Expires=\u05ea\u05d5\u05e7\u05e3 +Expires\ {0}=\u05d1\u05ea\u05d5\u05e7\u05e3 \u05e2\u05d3 {0} +Fare=\u05de\u05d7\u05d9\u05e8 \u05e0\u05e1\u05d9\u05e2\u05d4 +Finish\ your\ application=\u05d4\u05e9\u05dc\u05de\u05ea \u05d4\u05d1\u05e7\u05e9\u05d4 +Fix\ and\ resubmit=\u05ea\u05d9\u05e7\u05d5\u05df \u05d5\u05e9\u05dc\u05d9\u05d7\u05d4 \u05de\u05d7\u05d3\u05e9 +Flag=\u05e1\u05d9\u05de\u05d5\u05df +Flag\ account=\u05e1\u05d9\u05de\u05d5\u05df \u05d7\u05e9\u05d1\u05d5\u05df +Flag\ removed=\u05d4\u05e1\u05d9\u05de\u05d5\u05df \u05d4\u05d5\u05e1\u05e8 +Flagged=\u05de\u05e1\u05d5\u05de\u05df +Gender=\u05de\u05d2\u05d3\u05e8 +Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=\u05de\u05d1\u05d8 \u05d0\u05d7\u05e8\u05d5\u05df. \u05d0\u05e4\u05e9\u05e8 \u05dc\u05e9\u05e0\u05d5\u05ea \u05d4\u05db\u05d5\u05dc \u05e2\u05d3 \u05dc\u05d4\u05d2\u05e9\u05d4. +Hi,\ this\ is\ your\ Morsel\ courier.=\u05e9\u05dc\u05d5\u05dd, \u05de\u05d3\u05d1\u05e8 \u05d4\u05e0\u05d4\u05d2 \u05e9\u05dc\u05da \u05de-Morsel. +Hi,\ this\ is\ your\ Morsel\ customer.=\u05e9\u05dc\u05d5\u05dd, \u05de\u05d3\u05d1\u05e8 \u05d4\u05e0\u05d5\u05e1\u05e2 \u05e9\u05dc\u05da \u05de-Morsel. +History=\u05d4\u05d9\u05e1\u05d8\u05d5\u05e8\u05d9\u05d4 +I\ have\ finished=\u05e1\u05d9\u05d9\u05de\u05ea\u05d9 +I\ want\ to=\u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6\u05d4 +In\ good\ standing=\u05ea\u05e7\u05d9\u05df +Insurance=\u05d1\u05d9\u05d8\u05d5\u05d7 +Its\ number,\ its\ expiry\ date\ and\ a\ photo\ of\ it=\u05d4\u05de\u05e1\u05e4\u05e8, \u05ea\u05d0\u05e8\u05d9\u05da \u05d4\u05ea\u05e4\u05d5\u05d2\u05d4 \u05d5\u05ea\u05de\u05d5\u05e0\u05d4 \u05e9\u05dc\u05d5 +Keep\ my\ account=\u05dc\u05d4\u05e9\u05d0\u05d9\u05e8 \u05d0\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df +Kind\ of\ delivery=\u05e1\u05d5\u05d2 \u05e0\u05e1\u05d9\u05e2\u05d4 +Legal\ name=\u05e9\u05dd \u05e8\u05e9\u05de\u05d9 +Licence=\u05e8\u05d9\u05e9\u05d9\u05d5\u05df +Licence\ expires=\u05ea\u05d5\u05e7\u05e3 \u05d4\u05e8\u05d9\u05e9\u05d9\u05d5\u05df +Licence\ number=\u05de\u05e1\u05e4\u05e8 \u05e8\u05d9\u05e9\u05d9\u05d5\u05df +Make=\u05d9\u05e6\u05e8\u05df +Man=\u05d2\u05d1\u05e8 +Messages\ are\ not\ available\ on\ this\ device=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05e9\u05dc\u05d5\u05d7 \u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05de\u05de\u05db\u05e9\u05d9\u05e8 \u05d6\u05d4 +Minimum\ fare=\u05de\u05d7\u05d9\u05e8 \u05de\u05d9\u05e0\u05d9\u05de\u05d5\u05dd +Missing=\u05d7\u05e1\u05e8 +Model=\u05d3\u05d2\u05dd +Moderation=\u05e4\u05d9\u05e7\u05d5\u05d7 +Most\ of\ it\ is\ taking\ five\ photos=\u05e8\u05d5\u05d1 \u05d4\u05d6\u05de\u05df \u05d4\u05d5\u05d0 \u05e6\u05d9\u05dc\u05d5\u05dd \u05d7\u05de\u05e9 \u05ea\u05de\u05d5\u05e0\u05d5\u05ea +Multipliers=\u05de\u05db\u05e4\u05d9\u05dc\u05d9\u05dd +Name\ an\ emergency\ contact\ in\ your\ profile\ first=\u05d9\u05e9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05ea\u05d7\u05d9\u05dc\u05d4 \u05d0\u05d9\u05e9 \u05e7\u05e9\u05e8 \u05dc\u05d7\u05d9\u05e8\u05d5\u05dd \u05d1\u05e4\u05e8\u05d5\u05e4\u05d9\u05dc +Name\ on\ the\ card=\u05d4\u05e9\u05dd \u05e9\u05e2\u05dc \u05d4\u05db\u05e8\u05d8\u05d9\u05e1 +Name,\ emergency\ contact,\ home\ and\ work=\u05e9\u05dd, \u05d0\u05d9\u05e9 \u05e7\u05e9\u05e8 \u05dc\u05d7\u05d9\u05e8\u05d5\u05dd, \u05d1\u05d9\u05ea \u05d5\u05e2\u05d1\u05d5\u05d3\u05d4 +Needed=\u05e0\u05d3\u05e8\u05e9 +New\ password=\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3\u05e9\u05d4 +New\ password,\ again=\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3\u05e9\u05d4, \u05e9\u05d5\u05d1 +No=\u05dc\u05d0 +No\ payments\ yet=\u05d0\u05d9\u05df \u05e2\u05d3\u05d9\u05d9\u05df \u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd +No\ deliveries\ here=\u05d0\u05d9\u05df \u05db\u05d0\u05df \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +Nobody\ matches=\u05d0\u05d9\u05df \u05d4\u05ea\u05d0\u05de\u05d5\u05ea +Non-binary=\u05d0-\u05d1\u05d9\u05e0\u05d0\u05e8\u05d9 +Not\ provided=\u05dc\u05d0 \u05e1\u05d5\u05e4\u05e7 +Not\ set=\u05dc\u05d0 \u05d4\u05d5\u05d2\u05d3\u05e8 +Not\ started=\u05d8\u05e8\u05dd \u05d4\u05ea\u05d7\u05d9\u05dc\u05d4 +Nothing\ has\ been\ done\ to\ this\ account.=\u05dc\u05d0 \u05d1\u05d5\u05e6\u05e2\u05d4 \u05e9\u05d5\u05dd \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d1\u05d7\u05e9\u05d1\u05d5\u05df \u05d6\u05d4. +Nothing\ has\ been\ paid\ out\ yet.=\u05e2\u05d3\u05d9\u05d9\u05df \u05dc\u05d0 \u05e9\u05d5\u05dc\u05dd \u05d3\u05d1\u05e8. +Nothing\ here=\u05d0\u05d9\u05df \u05db\u05d0\u05df \u05d3\u05d1\u05e8 +Open\ the\ secure\ page=\u05e4\u05ea\u05d9\u05d7\u05ea \u05d4\u05d3\u05e3 \u05d4\u05de\u05d0\u05d5\u05d1\u05d8\u05d7 +Paid=\u05e9\u05d5\u05dc\u05dd +Paid\ in\ cash=\u05e9\u05d5\u05dc\u05dd \u05d1\u05de\u05d6\u05d5\u05de\u05df +Paid\ with=\u05e9\u05d5\u05dc\u05dd \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea +Payment\ failed=\u05d4\u05ea\u05e9\u05dc\u05d5\u05dd \u05e0\u05db\u05e9\u05dc +Payment\ pending=\u05ea\u05e9\u05dc\u05d5\u05dd \u05de\u05de\u05ea\u05d9\u05df +Payments=\u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd +Payouts=\u05d4\u05e2\u05d1\u05e8\u05d5\u05ea +People\ who\ asked\ to\ drive,\ and\ their\ papers=\u05de\u05d9 \u05e9\u05d1\u05d9\u05e7\u05e9\u05d5 \u05dc\u05e0\u05d4\u05d5\u05d2, \u05d5\u05d4\u05de\u05e1\u05de\u05db\u05d9\u05dd \u05e9\u05dc\u05d4\u05dd +Per\ kilometre=\u05dc\u05e7\u05d9\u05dc\u05d5\u05de\u05d8\u05e8 +Per\ minute=\u05dc\u05d3\u05e7\u05d4 +Photo\ ID=\u05ea\u05e2\u05d5\u05d3\u05d4 \u05de\u05d6\u05d4\u05d4 +Photograph\ each\ one\ flat,\ with\ all\ four\ corners\ in\ the\ picture.=\u05d9\u05e9 \u05dc\u05e6\u05dc\u05dd \u05db\u05dc \u05de\u05e1\u05de\u05da \u05db\u05e9\u05d4\u05d5\u05d0 \u05e9\u05d8\u05d5\u05d7 \u05d5\u05db\u05dc \u05d0\u05e8\u05d1\u05e2 \u05d4\u05e4\u05d9\u05e0\u05d5\u05ea \u05d1\u05ea\u05de\u05d5\u05e0\u05d4. +Photos\ added=\u05ea\u05de\u05d5\u05e0\u05d5\u05ea \u05e9\u05e0\u05d5\u05e1\u05e4\u05d5 +Pricing=\u05ea\u05de\u05d7\u05d5\u05e8 +Profile=\u05e4\u05e8\u05d5\u05e4\u05d9\u05dc +Provided=\u05e1\u05d5\u05e4\u05e7 +Rather\ not\ say=\u05de\u05e2\u05d3\u05d9\u05e3 \u05dc\u05d0 \u05dc\u05e6\u05d9\u05d9\u05df +Rating=\u05d3\u05d9\u05e8\u05d5\u05d2 +Read\ what\ was\ missing,\ put\ it\ right,\ and\ send\ the\ application\ again.=\u05e7\u05e8\u05d0\u05d5 \u05de\u05d4 \u05d4\u05d9\u05d4 \u05d7\u05e1\u05e8, \u05ea\u05e7\u05e0\u05d5 \u05d5\u05e9\u05dc\u05d7\u05d5 \u05d0\u05ea \u05d4\u05d1\u05e7\u05e9\u05d4 \u05e9\u05d5\u05d1. +Ready\ to\ cash\ out=\u05d6\u05de\u05d9\u05df \u05dc\u05de\u05e9\u05d9\u05db\u05d4 +Reason=\u05e1\u05d9\u05d1\u05d4 +Receipt=\u05e7\u05d1\u05dc\u05d4 +Refund=\u05d4\u05d7\u05d6\u05e8 +Refund\ this\ delivery=\u05d4\u05d7\u05d6\u05e8 \u05e2\u05dc \u05e0\u05e1\u05d9\u05e2\u05d4 \u05d6\u05d5 +Refunded=\u05d4\u05d5\u05d7\u05d6\u05e8 +Registered\ and\ insured,\ with\ the\ papers\ to\ show\ it=\u05e8\u05e9\u05d5\u05dd \u05d5\u05de\u05d1\u05d5\u05d8\u05d7, \u05e2\u05dd \u05d4\u05de\u05e1\u05de\u05db\u05d9\u05dd \u05e9\u05de\u05d5\u05db\u05d9\u05d7\u05d9\u05dd \u05d6\u05d0\u05ea +Rejected=\u05e0\u05d3\u05d7\u05d4 +Remove\ flag=\u05d4\u05e1\u05e8\u05ea \u05e1\u05d9\u05de\u05d5\u05df +Remove\ this\ card=\u05d4\u05e1\u05e8\u05ea \u05db\u05e8\u05d8\u05d9\u05e1 \u05d6\u05d4 +Request\ {0}=\u05d4\u05d6\u05de\u05e0\u05ea {0} +Requests\ accepted=\u05d1\u05e7\u05e9\u05d5\u05ea \u05e9\u05d4\u05ea\u05e7\u05d1\u05dc\u05d5 +Requests\ by\ hour=\u05d1\u05e7\u05e9\u05d5\u05ea \u05dc\u05e4\u05d9 \u05e9\u05e2\u05d4 +Retake=\u05e6\u05d9\u05dc\u05d5\u05dd \u05de\u05d7\u05d3\u05e9 +Review\ and\ submit=\u05d1\u05d3\u05d9\u05e7\u05d4 \u05d5\u05e9\u05dc\u05d9\u05d7\u05d4 +Reviewed=\u05e0\u05d1\u05d3\u05e7\u05d4 +Reviewed\ by=\u05e0\u05d1\u05d3\u05e7\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 +Customers=\u05e0\u05d5\u05e1\u05e2\u05d9\u05dd +Customers\ who\ ask\ for\ a\ woman\ at\ the\ wheel\ are\ offered\ to\ couriers\ who\ say\ they\ are\ one.=\u05e0\u05d5\u05e1\u05e2\u05d9\u05dd \u05e9\u05de\u05d1\u05e7\u05e9\u05d9\u05dd \u05e0\u05d4\u05d2\u05ea \u05de\u05d5\u05e6\u05e2\u05d9\u05dd \u05dc\u05e0\u05d4\u05d2\u05d5\u05ea \u05e9\u05e6\u05d9\u05d9\u05e0\u05d5 \u05d6\u05d0\u05ea. +Right\ now=\u05e2\u05db\u05e9\u05d9\u05d5 +Roles\ changed=\u05d4\u05ea\u05e4\u05e7\u05d9\u05d3\u05d9\u05dd \u05e9\u05d5\u05e0\u05d5 +Roles,\ flags,\ and\ blocking\ an\ account=\u05ea\u05e4\u05e7\u05d9\u05d3\u05d9\u05dd, \u05e1\u05d9\u05de\u05d5\u05e0\u05d9\u05dd \u05d5\u05d7\u05e1\u05d9\u05de\u05ea \u05d7\u05e9\u05d1\u05d5\u05e0\u05d5\u05ea +Save\ card=\u05e9\u05de\u05d9\u05e8\u05ea \u05d4\u05db\u05e8\u05d8\u05d9\u05e1 +Save\ prices=\u05e9\u05de\u05d9\u05e8\u05ea \u05d4\u05de\u05d7\u05d9\u05e8\u05d9\u05dd +Saved\ places=\u05de\u05e7\u05d5\u05de\u05d5\u05ea \u05e9\u05de\u05d5\u05e8\u05d9\u05dd +Say\ why,\ in\ a\ few\ words.=\u05d9\u05e9 \u05dc\u05e6\u05d9\u05d9\u05df \u05d0\u05ea \u05d4\u05e1\u05d9\u05d1\u05d4 \u05d1\u05db\u05de\u05d4 \u05de\u05d9\u05dc\u05d9\u05dd. +Search\ by\ name\ or\ e-mail=\u05d7\u05d9\u05e4\u05d5\u05e9 \u05dc\u05e4\u05d9 \u05e9\u05dd \u05d0\u05d5 \u05d3\u05d5\u05d0"\u05dc +Seats\ for\ customers=\u05de\u05d5\u05e9\u05d1\u05d9\u05dd \u05dc\u05e0\u05d5\u05e1\u05e2\u05d9\u05dd +Security\ code=\u05e7\u05d5\u05d3 \u05d0\u05d1\u05d8\u05d7\u05d4 +Send\ back=\u05d4\u05d7\u05d6\u05e8\u05d4 +Send\ tip=\u05e9\u05dc\u05d9\u05d7\u05ea \u05d8\u05d9\u05e4 +Saves\ {0}'s\ number\ with\ your\ account\ as\ your\ trip\ contact.\ No\ message\ is\ sent.=\u05e9\u05d5\u05de\u05e8 \u05d0\u05ea \u05d4\u05de\u05e1\u05e4\u05e8 \u05e9\u05dc {0} \u05d1\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da \u05db\u05d0\u05d9\u05e9 \u05d4\u05e7\u05e9\u05e8 \u05dc\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea. \u05dc\u05d0 \u05e0\u05e9\u05dc\u05d7\u05ea \u05d4\u05d5\u05d3\u05e2\u05d4. +Sent\ back=\u05d4\u05d5\u05d7\u05d6\u05e8\u05d4 +Service\ fee=\u05d3\u05de\u05d9 \u05e9\u05d9\u05e8\u05d5\u05ea +Service\ fee,\ %=\u05d3\u05de\u05d9 \u05e9\u05d9\u05e8\u05d5\u05ea, % +Spent\ riding=\u05d4\u05d5\u05e6\u05d9\u05d0 \u05e2\u05dc \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +Standard=\u05e8\u05d2\u05d9\u05dc +Start\ application=\u05d4\u05ea\u05d7\u05dc\u05ea \u05d4\u05d1\u05e7\u05e9\u05d4 +Step\ {0}\ of\ {1}=\u05e9\u05dc\u05d1 {0} \u05de\u05ea\u05d5\u05da {1} +Submit\ application=\u05e9\u05dc\u05d9\u05d7\u05ea \u05d4\u05d1\u05e7\u05e9\u05d4 +Submitted=\u05d4\u05d5\u05d2\u05e9\u05d4 +Surge,\ while\ demand\ is\ high=\u05ea\u05d5\u05e1\u05e4\u05ea \u05d1\u05d6\u05de\u05df \u05d1\u05d9\u05e7\u05d5\u05e9 \u05d2\u05d1\u05d5\u05d4 +Takes\ a\ wheelchair=\u05de\u05ea\u05d0\u05d9\u05dd \u05dc\u05db\u05d9\u05e1\u05d0 \u05d2\u05dc\u05d2\u05dc\u05d9\u05dd +Tap\ a\ card\ to\ make\ it\ the\ one\ that\ pays\ unless\ you\ say\ otherwise.=\u05d4\u05e7\u05d9\u05e9\u05d5 \u05e2\u05dc \u05db\u05e8\u05d8\u05d9\u05e1 \u05db\u05d3\u05d9 \u05e9\u05d9\u05e9\u05de\u05e9 \u05dc\u05ea\u05e9\u05dc\u05d5\u05dd \u05d0\u05dc\u05d0 \u05d0\u05dd \u05ea\u05d1\u05d7\u05e8\u05d5 \u05d0\u05d7\u05e8\u05ea. +Tap\ to\ open=\u05d4\u05e7\u05d9\u05e9\u05d5 \u05dc\u05e4\u05ea\u05d9\u05d7\u05d4 +Tell\ us\ about\ you\ and\ your\ car,\ add\ five\ photos,\ and\ we\ take\ it\ from\ there.=\u05e1\u05e4\u05e8\u05d5 \u05dc\u05e0\u05d5 \u05e2\u05dc\u05d9\u05db\u05dd \u05d5\u05e2\u05dc \u05d4\u05e8\u05db\u05d1, \u05d4\u05d5\u05e1\u05d9\u05e4\u05d5 \u05d7\u05de\u05e9 \u05ea\u05de\u05d5\u05e0\u05d5\u05ea, \u05d5\u05d0\u05e0\u05d7\u05e0\u05d5 \u05e0\u05de\u05e9\u05d9\u05da \u05de\u05e9\u05dd. +Thank\ you.\ Your\ tip\ was\ sent.=\u05ea\u05d5\u05d3\u05d4. \u05d4\u05d8\u05d9\u05e4 \u05e0\u05e9\u05dc\u05d7. +That\ is\ not\ a\ card\ number.\ Check\ the\ digits.=\u05d6\u05d4 \u05d0\u05d9\u05e0\u05d5 \u05de\u05e1\u05e4\u05e8 \u05db\u05e8\u05d8\u05d9\u05e1. \u05d1\u05d3\u05e7\u05d5 \u05d0\u05ea \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea. +That\ picture\ could\ not\ be\ read=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05de\u05d5\u05e0\u05d4 +That\ place\ could\ not\ be\ found.\ Choose\ it\ again.=\u05d4\u05de\u05e7\u05d5\u05dd \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0. \u05d1\u05d7\u05e8\u05d5 \u05d0\u05d5\u05ea\u05d5 \u05e9\u05d5\u05d1. +The\ busy\ hours,\ in\ UTC=\u05e9\u05e2\u05d5\u05ea \u05d4\u05e2\u05d5\u05de\u05e1, \u05d1-UTC +The\ card\ was\ declined.\ Choose\ another\ way\ to\ pay\ in\ your\ wallet.=\u05d4\u05db\u05e8\u05d8\u05d9\u05e1 \u05e0\u05d3\u05d7\u05d4. \u05d1\u05d7\u05e8\u05d5 \u05d0\u05de\u05e6\u05e2\u05d9 \u05ea\u05e9\u05dc\u05d5\u05dd \u05d0\u05d7\u05e8 \u05d1\u05d0\u05e8\u05e0\u05e7. +The\ fare,\ in\ {0}=\u05d4\u05de\u05d7\u05d9\u05e8, \u05d1-{0} +The\ fee\ is\ added\ to\ the\ fare\ and\ paid\ by\ the\ customer.\ The\ commission\ is\ kept\ from\ the\ fare;\ the\ rest\ is\ the\ courier's.=\u05d3\u05de\u05d9 \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd \u05dc\u05de\u05d7\u05d9\u05e8 \u05d5\u05de\u05e9\u05d5\u05dc\u05de\u05d9\u05dd \u05e2\u05dc \u05d9\u05d3\u05d9 \u05d4\u05e0\u05d5\u05e1\u05e2. \u05d4\u05e2\u05de\u05dc\u05d4 \u05de\u05e0\u05d5\u05db\u05d4 \u05de\u05d4\u05de\u05d7\u05d9\u05e8; \u05d4\u05d9\u05ea\u05e8 \u05e9\u05d9\u05d9\u05da \u05dc\u05e0\u05d4\u05d2. +The\ last\ two\ weeks=\u05d4\u05e9\u05d1\u05d5\u05e2\u05d9\u05d9\u05dd \u05d4\u05d0\u05d7\u05e8\u05d5\u05e0\u05d9\u05dd +The\ new\ prices\ are\ in\ force=\u05d4\u05de\u05d7\u05d9\u05e8\u05d9\u05dd \u05d4\u05d7\u05d3\u05e9\u05d9\u05dd \u05d1\u05ea\u05d5\u05e7\u05e3 +The\ number\ is\ on\ the\ front\ of\ the\ card.=\u05d4\u05de\u05e1\u05e4\u05e8 \u05de\u05d5\u05e4\u05d9\u05e2 \u05d1\u05d7\u05d6\u05d9\u05ea \u05d4\u05db\u05e8\u05d8\u05d9\u05e1. +The\ page\ with\ the\ policy's\ dates\ on\ it=\u05d4\u05e2\u05de\u05d5\u05d3 \u05e9\u05d1\u05d5 \u05de\u05d5\u05e4\u05d9\u05e2\u05d9\u05dd \u05ea\u05d0\u05e8\u05d9\u05db\u05d9 \u05d4\u05e4\u05d5\u05dc\u05d9\u05e1\u05d4 +The\ paper\ that\ says\ the\ car\ is\ yours=\u05d4\u05de\u05e1\u05de\u05da \u05e9\u05de\u05e2\u05d9\u05d3 \u05e9\u05d4\u05e8\u05db\u05d1 \u05e9\u05dc\u05da +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=\u05d3\u05e3 \u05e1\u05e4\u05e7 \u05d4\u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7 \u05d1\u05d3\u05e4\u05d3\u05e4\u05df. Morsel \u05d0\u05d9\u05e0\u05d4 \u05e8\u05d5\u05d0\u05d4 \u05d0\u05ea \u05d4\u05de\u05e1\u05e4\u05e8. \u05d7\u05d6\u05e8\u05d5 \u05dc\u05db\u05d0\u05df \u05d1\u05e1\u05d9\u05d5\u05dd. +The\ delivery\ was\ refunded=\u05e0\u05d9\u05ea\u05df \u05d4\u05d7\u05d6\u05e8 \u05e2\u05dc \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 +Refund\ pending=\u05d4\u05d7\u05d6\u05e8 \u05de\u05de\u05ea\u05d9\u05df +Refund\ failed=\u05d4\u05d7\u05d6\u05e8 \u05e0\u05db\u05e9\u05dc +The\ refund\ is\ pending=\u05d4\u05d4\u05d7\u05d6\u05e8 \u05de\u05de\u05ea\u05d9\u05df +The\ customer\ and\ the\ courier\ are\ both\ told,\ and\ nobody\ is\ charged.=\u05d4\u05e0\u05d5\u05e1\u05e2 \u05d5\u05d4\u05e0\u05d4\u05d2 \u05de\u05e7\u05d1\u05dc\u05d9\u05dd \u05d4\u05d5\u05d3\u05e2\u05d4, \u05d5\u05d0\u05d9\u05e9 \u05d0\u05d9\u05e0\u05d5 \u05de\u05d7\u05d5\u05d9\u05d1. +The\ security\ code\ is\ the\ three\ or\ four\ digits\ on\ the\ card.=\u05e7\u05d5\u05d3 \u05d4\u05d0\u05d1\u05d8\u05d7\u05d4 \u05d4\u05d5\u05d0 \u05e9\u05dc\u05d5\u05e9 \u05d0\u05d5 \u05d0\u05e8\u05d1\u05e2 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05e9\u05e2\u05dc \u05d4\u05db\u05e8\u05d8\u05d9\u05e1. +The\ service's\ share=\u05d7\u05dc\u05e7\u05d5 \u05e9\u05dc \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea +The\ side\ with\ your\ photo\ on\ it=\u05d4\u05e6\u05d3 \u05e9\u05d1\u05d5 \u05de\u05d5\u05e4\u05d9\u05e2\u05d4 \u05ea\u05de\u05d5\u05e0\u05ea\u05da +The\ two\ new\ passwords\ are\ not\ the\ same.=\u05e9\u05ea\u05d9 \u05d4\u05e1\u05d9\u05e1\u05de\u05d0\u05d5\u05ea \u05d4\u05d7\u05d3\u05e9\u05d5\u05ea \u05d0\u05d9\u05e0\u05df \u05d6\u05d4\u05d5\u05ea. +This\ device\ cannot\ prepare\ a\ picture\ to\ send=\u05d4\u05de\u05db\u05e9\u05d9\u05e8 \u05d0\u05d9\u05e0\u05d5 \u05d9\u05db\u05d5\u05dc \u05dc\u05d4\u05db\u05d9\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05dc\u05e9\u05dc\u05d9\u05d7\u05d4 +This\ month=\u05d4\u05d7\u05d5\u05d3\u05e9 +This\ picture\ could\ not\ be\ opened=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05e4\u05ea\u05d5\u05d7 \u05d0\u05ea \u05d4\u05ea\u05de\u05d5\u05e0\u05d4 +This\ screen\ becomes\ your\ courier's\ home\ the\ moment\ you\ are\ approved=\u05d4\u05de\u05e1\u05da \u05d4\u05d6\u05d4 \u05d9\u05d4\u05e4\u05d5\u05da \u05dc\u05de\u05e1\u05da \u05d4\u05e0\u05d4\u05d2 \u05e9\u05dc\u05da \u05d1\u05e8\u05d2\u05e2 \u05e9\u05ea\u05d0\u05d5\u05e9\u05e8 +This\ server\ takes\ no\ real\ payments.\ Use\ the\ test\ card\ 4242\ 4242\ 4242\ 4242\ with\ any\ date\ ahead\ and\ any\ three\ digits.=\u05e9\u05e8\u05ea \u05d6\u05d4 \u05d0\u05d9\u05e0\u05d5 \u05de\u05e7\u05d1\u05dc \u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd \u05d0\u05de\u05d9\u05ea\u05d9\u05d9\u05dd. \u05d4\u05e9\u05ea\u05de\u05e9\u05d5 \u05d1\u05db\u05e8\u05d8\u05d9\u05e1 \u05d4\u05d1\u05d3\u05d9\u05e7\u05d4 4242 4242 4242 4242 \u05e2\u05dd \u05ea\u05d0\u05e8\u05d9\u05da \u05e2\u05ea\u05d9\u05d3\u05d9 \u05db\u05dc\u05e9\u05d4\u05d5 \u05d5\u05e9\u05dc\u05d5\u05e9 \u05e1\u05e4\u05e8\u05d5\u05ea \u05db\u05dc\u05e9\u05d4\u05df. +Time=\u05d6\u05de\u05df +Tip\ offered\ first=\u05d4\u05d8\u05d9\u05e4 \u05e9\u05de\u05d5\u05e6\u05e2 \u05ea\u05d7\u05d9\u05dc\u05d4 +Tips=\u05d8\u05d9\u05e4\u05d9\u05dd +Top\ couriers=\u05d4\u05e0\u05d4\u05d2\u05d9\u05dd \u05d4\u05de\u05d5\u05d1\u05d9\u05dc\u05d9\u05dd +Total=\u05e1\u05d4"\u05db +Trends=\u05de\u05d2\u05de\u05d5\u05ea +Trip=\u05d4\u05de\u05e1\u05dc\u05d5\u05dc +Type\ the\ account\ number.=\u05d9\u05e9 \u05dc\u05d4\u05e7\u05dc\u05d9\u05d3 \u05d0\u05ea \u05de\u05e1\u05e4\u05e8 \u05d4\u05d7\u05e9\u05d1\u05d5\u05df. +Unblock=\u05d1\u05d9\u05d8\u05d5\u05dc \u05d7\u05e1\u05d9\u05de\u05d4 +Unblocked=\u05d4\u05d7\u05e1\u05d9\u05de\u05d4 \u05d4\u05d5\u05e1\u05e8\u05d4 +Under\ review=\u05d1\u05d1\u05d3\u05d9\u05e7\u05d4 +Usually\ within\ two\ working\ days=\u05d1\u05d3\u05e8\u05da \u05db\u05dc\u05dc \u05d1\u05ea\u05d5\u05da \u05e9\u05e0\u05d9 \u05d9\u05de\u05d9 \u05e2\u05e1\u05e7\u05d9\u05dd +Vehicle=\u05e8\u05db\u05d1 +Vehicle\ registration=\u05e8\u05d9\u05e9\u05d9\u05d5\u05df \u05e8\u05db\u05d1 +View\ receipt=\u05d4\u05e6\u05d2\u05ea \u05d4\u05e7\u05d1\u05dc\u05d4 +Waiting=\u05de\u05de\u05ea\u05d9\u05e0\u05d4 +Wallet=\u05d0\u05e8\u05e0\u05e7 +We\ could\ not\ approve\ this\ yet=\u05e2\u05d3\u05d9\u05d9\u05df \u05dc\u05d0 \u05d9\u05db\u05d5\u05dc\u05e0\u05d5 \u05dc\u05d0\u05e9\u05e8 +We\ have\ everything\ we\ asked\ for.\ Someone\ on\ our\ team\ is\ reading\ it.=\u05e7\u05d9\u05d1\u05dc\u05e0\u05d5 \u05d0\u05ea \u05db\u05dc \u05de\u05d4 \u05e9\u05d1\u05d9\u05e7\u05e9\u05e0\u05d5. \u05de\u05d9\u05e9\u05d4\u05d5 \u05de\u05d4\u05e6\u05d5\u05d5\u05ea \u05e7\u05d5\u05e8\u05d0 \u05d0\u05d5\u05ea\u05d4. +What\ a\ delivery\ costs,\ and\ the\ service's\ share=\u05db\u05de\u05d4 \u05e2\u05d5\u05dc\u05d4 \u05e0\u05e1\u05d9\u05e2\u05d4, \u05d5\u05d7\u05dc\u05e7\u05d5 \u05e9\u05dc \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea +What\ happens\ next=\u05de\u05d4 \u05e7\u05d5\u05e8\u05d4 \u05e2\u05db\u05e9\u05d9\u05d5 +What\ the\ reviewer\ said=\u05de\u05d4 \u05d0\u05de\u05e8 \u05d4\u05d1\u05d5\u05d3\u05e7 +What\ was\ charged,\ and\ refunds=\u05de\u05d4 \u05d7\u05d5\u05d9\u05d1, \u05d5\u05d4\u05d7\u05d6\u05e8\u05d9\u05dd +What\ you\ will\ need=\u05de\u05d4 \u05ea\u05e6\u05d8\u05e8\u05db\u05d5 +Where\ your\ earnings\ are\ sent=\u05dc\u05d0\u05df \u05e0\u05e9\u05dc\u05d7\u05d5\u05ea \u05d4\u05d4\u05db\u05e0\u05e1\u05d5\u05ea \u05e9\u05dc\u05da +Why\ does\ {0}\ need\ the\ admins'\ attention?=\u05de\u05d3\u05d5\u05e2 {0} \u05d3\u05d5\u05e8\u05e9 \u05d0\u05ea \u05ea\u05e9\u05d5\u05de\u05ea \u05dc\u05d1 \u05d4\u05de\u05e0\u05d4\u05dc\u05d9\u05dd? +Woman=\u05d0\u05d9\u05e9\u05d4 +Write\ the\ expiry\ date\ as\ month\ and\ year\:\ 04/28.=\u05d9\u05e9 \u05dc\u05db\u05ea\u05d5\u05d1 \u05d0\u05ea \u05d4\u05ea\u05d5\u05e7\u05e3 \u05db\u05d7\u05d5\u05d3\u05e9 \u05d5\u05e9\u05e0\u05d4: 04/28. +Year=\u05e9\u05e0\u05d4 +Yes=\u05db\u05df +You=\u05d0\u05ea\u05d4 +You\ go\ online=\u05de\u05ea\u05d7\u05d1\u05e8\u05d9\u05dd +Your\ application=\u05d4\u05d1\u05e7\u05e9\u05d4 \u05e9\u05dc\u05da +Your\ car=\u05d4\u05e8\u05db\u05d1 \u05e9\u05dc\u05da +Your\ card\ goes\ straight\ to\ the\ bank=\u05d4\u05db\u05e8\u05d8\u05d9\u05e1 \u05e9\u05dc\u05da \u05e2\u05d5\u05d1\u05e8 \u05d9\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05d1\u05e0\u05e7 +Your\ cards,\ and\ which\ of\ them\ pays\ by\ default=\u05d4\u05db\u05e8\u05d8\u05d9\u05e1\u05d9\u05dd \u05e9\u05dc\u05da, \u05d5\u05de\u05d9 \u05de\u05d4\u05dd \u05de\u05e9\u05dc\u05dd \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc +Your\ documents=\u05d4\u05de\u05e1\u05de\u05db\u05d9\u05dd \u05e9\u05dc\u05da +Your\ courier\ cannot\ be\ messaged\ right\ now=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05e9\u05dc\u05d5\u05d7 \u05db\u05e8\u05d2\u05e2 \u05d4\u05d5\u05d3\u05e2\u05d4 \u05dc\u05e0\u05d4\u05d2 +Your\ face,\ in\ good\ light,\ no\ sunglasses=\u05d4\u05e4\u05e0\u05d9\u05dd \u05e9\u05dc\u05da, \u05d1\u05ea\u05d0\u05d5\u05e8\u05d4 \u05d8\u05d5\u05d1\u05d4, \u05d1\u05dc\u05d9 \u05de\u05e9\u05e7\u05e4\u05d9 \u05e9\u05de\u05e9 +Your\ licence=\u05d4\u05e8\u05d9\u05e9\u05d9\u05d5\u05df \u05e9\u05dc\u05da +Your\ password\ was\ changed=\u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05d5\u05e0\u05ea\u05d4 +Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Your\ settings\ are\ kept\ with\ your\ account\ on\ that\ server.=\u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05dc\u05da \u05e0\u05e9\u05dc\u05d7 \u05dc\u05e9\u05e8\u05ea \u05e9\u05d0\u05dc\u05d9\u05d5 \u05d4\u05ea\u05d7\u05d1\u05e8\u05ea \u05d1\u05d6\u05de\u05df \u05e0\u05e1\u05d9\u05e2\u05d4 \u05d0\u05d5 \u05e0\u05d4\u05d9\u05d2\u05d4, \u05d5\u05dc\u05d0\u05e3 \u05d0\u05d7\u05d3 \u05d0\u05d7\u05e8. \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e0\u05e9\u05de\u05e8\u05d5\u05ea \u05e2\u05dd \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da \u05d1\u05d0\u05d5\u05ea\u05d5 \u05e9\u05e8\u05ea. +Your\ customer\ cannot\ be\ messaged\ right\ now=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05e9\u05dc\u05d5\u05d7 \u05db\u05e8\u05d2\u05e2 \u05d4\u05d5\u05d3\u05e2\u05d4 \u05dc\u05e0\u05d5\u05e1\u05e2 +Your\ sign-in,\ your\ profile,\ your\ saved\ cards\ and\ your\ settings\ are\ removed\ for\ good.\ Deliveries\ you\ took\ stay\ in\ the\ service's\ records\ without\ your\ name.=\u05e4\u05e8\u05d8\u05d9 \u05d4\u05db\u05e0\u05d9\u05e1\u05d4, \u05d4\u05e4\u05e8\u05d5\u05e4\u05d9\u05dc, \u05d4\u05db\u05e8\u05d8\u05d9\u05e1\u05d9\u05dd \u05d4\u05e9\u05de\u05d5\u05e8\u05d9\u05dd \u05d5\u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e9\u05dc\u05da \u05e0\u05de\u05d7\u05e7\u05d9\u05dd \u05dc\u05e6\u05de\u05d9\u05ea\u05d5\u05ea. \u05d4\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05e9\u05dc\u05da \u05e0\u05e9\u05d0\u05e8\u05d5\u05ea \u05d1\u05e8\u05e9\u05d5\u05de\u05d5\u05ea \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05dc\u05d0 \u05e9\u05de\u05da. +{0}\ away=\u05d1\u05de\u05e8\u05d7\u05e7 {0} +{0}\ can\ now\ drive={0} \u05e8\u05e9\u05d0\u05d9 \u05db\u05e2\u05ea \u05dc\u05e0\u05d4\u05d5\u05d2 +{0}\ completed,\ {1}\ cancelled={0} \u05d4\u05d5\u05e9\u05dc\u05de\u05d5, {1} \u05d1\u05d5\u05d8\u05dc\u05d5 +{0}\ days\ ago=\u05dc\u05e4\u05e0\u05d9 {0} \u05d9\u05de\u05d9\u05dd +{0}\ goes\ back\ to\ the\ card\ it\ came\ from.={0} \u05d7\u05d5\u05d6\u05e8 \u05dc\u05db\u05e8\u05d8\u05d9\u05e1 \u05e9\u05de\u05de\u05e0\u05d5 \u05e9\u05d5\u05dc\u05dd. +{0}\ is\ on\ its\ way\ to\ your\ bank={0} \u05d1\u05d3\u05e8\u05da \u05dc\u05d1\u05e0\u05e7 \u05e9\u05dc\u05da +{0}\ is\ signed\ out\ everywhere\ and\ cannot\ sign\ in\ again\ until\ the\ block\ is\ lifted.={0} \u05de\u05e0\u05d5\u05ea\u05e7 \u05de\u05db\u05dc \u05de\u05e7\u05d5\u05dd \u05d5\u05dc\u05d0 \u05d9\u05d5\u05db\u05dc \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05e9\u05d5\u05d1 \u05e2\u05d3 \u05e9\u05d4\u05d7\u05e1\u05d9\u05de\u05d4 \u05ea\u05d5\u05e1\u05e8. +{0}\ new\ accounts={0} \u05d7\u05e9\u05d1\u05d5\u05e0\u05d5\u05ea \u05d7\u05d3\u05e9\u05d9\u05dd +{0}\ of\ 5\ documents\ added.\ Pick\ up\ where\ you\ left\ off.=\u05e0\u05d5\u05e1\u05e4\u05d5 {0} \u05de\u05ea\u05d5\u05da 5 \u05de\u05e1\u05de\u05db\u05d9\u05dd. \u05d4\u05de\u05e9\u05d9\u05db\u05d5 \u05de\u05d4\u05de\u05e7\u05d5\u05dd \u05e9\u05d1\u05d5 \u05e2\u05e6\u05e8\u05ea\u05dd. +{0}\ deliveries={0} \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea +{0}\ s={0} \u05e9\u05e0' +{0}\ sees\ what\ you\ write,\ and\ can\ apply\ again\ once\ it\ is\ put\ right.={0} \u05e8\u05d5\u05d0\u05d4 \u05d0\u05ea \u05de\u05d4 \u05e9\u05db\u05ea\u05d1\u05ea \u05d5\u05d9\u05db\u05d5\u05dc \u05dc\u05d4\u05d2\u05d9\u05e9 \u05e9\u05d5\u05d1 \u05dc\u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e7\u05d5\u05df. +Fill\ in\ everything\ on\ this\ step\ to\ continue.=\u05d9\u05e9 \u05dc\u05de\u05dc\u05d0 \u05d0\u05ea \u05db\u05dc \u05d4\u05e4\u05e8\u05d8\u05d9\u05dd \u05d1\u05e9\u05dc\u05d1 \u05d6\u05d4 \u05db\u05d3\u05d9 \u05dc\u05d4\u05de\u05e9\u05d9\u05da. +None\ nearby=\u05d0\u05d9\u05df \u05d1\u05e7\u05e8\u05d1\u05ea \u05de\u05e7\u05d5\u05dd +Pay\ the\ courier\ directly=\u05de\u05e9\u05dc\u05de\u05d9\u05dd \u05d9\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05e0\u05d4\u05d2 +Camera=\u05de\u05e6\u05dc\u05de\u05d4 +Gallery=\u05d2\u05dc\u05e8\u05d9\u05d4 +Fares\ collected=\u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd \u05e9\u05e0\u05d2\u05d1\u05d5 +Takes\ pets=\u05de\u05e1\u05d9\u05e2 \u05d7\u05d9\u05d5\u05ea \u05de\u05d7\u05de\u05d3 +Customers\ travelling\ with\ a\ pet\ are\ offered\ to\ you=\u05e0\u05d5\u05e1\u05e2\u05d9\u05dd \u05e2\u05dd \u05d7\u05d9\u05d9\u05ea \u05de\u05d7\u05de\u05d3 \u05d9\u05d5\u05e6\u05e2\u05d5 \u05dc\u05da +Go=\u05de\u05e2\u05d1\u05e8 +Nothing\ selected=\u05dc\u05d0 \u05e0\u05d1\u05d7\u05e8 \u05d3\u05d1\u05e8 +Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=\u05d1\u05d7\u05e8\u05d5 \u05e9\u05d5\u05e8\u05d4 \u05de\u05d4\u05e8\u05e9\u05d9\u05de\u05d4 \u05db\u05d3\u05d9 \u05dc\u05e8\u05d0\u05d5\u05ea \u05d0\u05d5\u05ea\u05d4 \u05db\u05d0\u05df. +Usage\ statistics=\u05e0\u05ea\u05d5\u05e0\u05d9 \u05e9\u05d9\u05de\u05d5\u05e9 +Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=\u05e9\u05d9\u05ea\u05d5\u05e3 \u05d4\u05de\u05e1\u05db\u05d9\u05dd \u05d5\u05d4\u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05e9\u05d1\u05e9\u05d9\u05de\u05d5\u05e9. \u05dc\u05e2\u05d5\u05dc\u05dd \u05dc\u05d0 \u05d4\u05e9\u05dd \u05e9\u05dc\u05da, \u05d4\u05de\u05e7\u05d5\u05de\u05d5\u05ea \u05e9\u05dc\u05da \u05d0\u05d5 \u05e4\u05e8\u05d8\u05d9 \u05d4\u05ea\u05e9\u05dc\u05d5\u05dd. +Help\ improve\ Morsel=\u05e2\u05d6\u05e8\u05d5 \u05dc\u05e9\u05e4\u05e8 \u05d0\u05ea Morsel +Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel \u05d9\u05db\u05d5\u05dc\u05d4 \u05dc\u05e1\u05e4\u05d5\u05e8 \u05d0\u05d9\u05dc\u05d5 \u05de\u05e1\u05db\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7\u05d9\u05dd \u05d5\u05d1\u05d0\u05d9\u05dc\u05d5 \u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd, \u05db\u05d3\u05d9 \u05e9\u05e0\u05d5\u05db\u05dc \u05dc\u05e8\u05d0\u05d5\u05ea \u05de\u05d4 \u05e2\u05d5\u05d1\u05d3 \u05d5\u05dc\u05ea\u05e7\u05df \u05d0\u05ea \u05de\u05d4 \u05e9\u05dc\u05d0. +No\ personal\ details=\u05dc\u05dc\u05d0 \u05e4\u05e8\u05d8\u05d9\u05dd \u05d0\u05d9\u05e9\u05d9\u05d9\u05dd +Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=\u05dc\u05e2\u05d5\u05dc\u05dd \u05dc\u05d0 \u05d4\u05e9\u05dd \u05e9\u05dc\u05da, \u05d4\u05d3\u05d5\u05d0"\u05dc, \u05de\u05e1\u05e4\u05e8 \u05d4\u05d8\u05dc\u05e4\u05d5\u05df, \u05d4\u05de\u05e7\u05d5\u05de\u05d5\u05ea \u05d0\u05d5 \u05e4\u05e8\u05d8\u05d9 \u05d4\u05db\u05e8\u05d8\u05d9\u05e1 +Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=\u05d0\u05e4\u05e9\u05e8 \u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05d0\u05d5 \u05dc\u05db\u05d1\u05d5\u05ea \u05d1\u05db\u05dc \u05e2\u05ea \u05d1\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea +Share\ usage\ statistics=\u05e9\u05d9\u05ea\u05d5\u05e3 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05e9\u05d9\u05de\u05d5\u05e9 +With\ usage\ statistics\ turned\ on,\ the\ app\ also\ counts\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ and\ reports\ that\ to\ Codename\ One's\ analytics\ service\ under\ a\ made-up\ id\ for\ this\ device.\ Your\ name,\ e-mail,\ phone\ number,\ places\ and\ card\ details\ are\ never\ part\ of\ it,\ and\ turning\ it\ off\ in\ Settings\ stops\ it.=\u05db\u05e9\u05e0\u05ea\u05d5\u05e0\u05d9 \u05d4\u05e9\u05d9\u05de\u05d5\u05e9 \u05de\u05d5\u05e4\u05e2\u05dc\u05d9\u05dd, \u05d4\u05d0\u05e4\u05dc\u05d9\u05e7\u05e6\u05d9\u05d4 \u05e1\u05d5\u05e4\u05e8\u05ea \u05d2\u05dd \u05d0\u05d9\u05dc\u05d5 \u05de\u05e1\u05db\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7\u05d9\u05dd \u05d5\u05d1\u05d0\u05d9\u05dc\u05d5 \u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd, \u05d5\u05de\u05d3\u05d5\u05d5\u05d7\u05ea \u05e2\u05dc \u05db\u05da \u05dc\u05e9\u05d9\u05e8\u05d5\u05ea \u05d4\u05d0\u05e0\u05dc\u05d9\u05d8\u05d9\u05e7\u05d4 \u05e9\u05dc Codename One \u05ea\u05d7\u05ea \u05de\u05d6\u05d4\u05d4 \u05d1\u05d3\u05d5\u05d9 \u05e9\u05dc \u05d4\u05de\u05db\u05e9\u05d9\u05e8 \u05d4\u05d6\u05d4. \u05d4\u05e9\u05dd \u05e9\u05dc\u05da, \u05d4\u05d3\u05d5\u05d0"\u05dc, \u05de\u05e1\u05e4\u05e8 \u05d4\u05d8\u05dc\u05e4\u05d5\u05df, \u05d4\u05de\u05e7\u05d5\u05de\u05d5\u05ea \u05d5\u05e4\u05e8\u05d8\u05d9 \u05d4\u05db\u05e8\u05d8\u05d9\u05e1 \u05dc\u05e2\u05d5\u05dc\u05dd \u05d0\u05d9\u05e0\u05dd \u05d7\u05dc\u05e7 \u05de\u05d6\u05d4, \u05d5\u05db\u05d9\u05d1\u05d5\u05d9 \u05d1\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05de\u05e4\u05e1\u05d9\u05e7 \u05d6\u05d0\u05ea. +Location\ is\ turned\ off.\ Try\ again=\u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05db\u05d1\u05d5\u05d9. \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1 +Can't\ find\ your\ location.\ Try\ again=\u05dc\u05d0 \u05de\u05e6\u05d0\u05e0\u05d5 \u05d0\u05ea \u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05dc\u05da. \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1 +Still\ looking\ for\ your\ location.\ Try\ again=\u05e2\u05d3\u05d9\u05d9\u05df \u05de\u05d7\u05e4\u05e9\u05d9\u05dd \u05d0\u05ea \u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05dc\u05da. \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1 +Looking\ for\ your\ location...=\u05de\u05d7\u05e4\u05e9\u05d9\u05dd \u05d0\u05ea \u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05dc\u05da... diff --git a/scripts/morsel/common/src/main/resources/surfaces.json b/scripts/morsel/common/src/main/resources/surfaces.json new file mode 100644 index 00000000000..13b1c3a51e4 --- /dev/null +++ b/scripts/morsel/common/src/main/resources/surfaces.json @@ -0,0 +1,3 @@ +{ + "liveActivities": true +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java new file mode 100644 index 00000000000..4f806095bce --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.io.CharArrayReader; +import com.codename1.io.ConnectionRequest; +import com.codename1.io.JSONParser; +import com.codename1.io.NetworkManager; +import com.codename1.io.RequestAuthorizer; +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.net.Session; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/// The other person in a test. The app on screen is one user; a delivery needs +/// two, so the second one acts through the API directly, with their own token. +/// +/// Their requests opt out of the app's authorizer, which would otherwise put +/// the on-screen user's token on anything sent to `/api`. +final class Actor { + private final String token; + int status; + + /// One sign-in for each account for the whole run. The server limits how + /// often an address may sign in, as it should, and a suite that signed in + /// again for every step would be turned away by it. + private static final Map SIGNED_IN = new HashMap(); + + private Actor(String token) { + this.token = token; + } + + /// Signs `email` in the way the app does, and keeps the token to itself. + static Actor signIn(String email) { + Actor known = SIGNED_IN.get(email); + if (known != null) { + return new Actor(known.token); + } + Actor fresh = authenticate(email); + SIGNED_IN.put(email, fresh); + return fresh; + } + + /// Whether `email` can sign in at this moment, asked of the server and + /// kept by nobody: this is how a test sees a blocked account refused. + static boolean canSignIn(String email) { + try { + authenticate(email); + return true; + } catch (RuntimeException refused) { + return false; + } + } + + private static Actor authenticate(String email) { + final String[] token = new String[1]; + final String[] problem = new String[1]; + CN.callSerially(() -> Session.authenticate(email, E2e.PASSWORD, + tokens -> token[0] = tokens.getAccessToken(), + (status, message) -> problem[0] = status + " " + message)); + long deadline = System.currentTimeMillis() + 30000; + while (token[0] == null && problem[0] == null && System.currentTimeMillis() < deadline) { + try { + Thread.sleep(50); + } catch (InterruptedException stop) { + break; + } + } + if (token[0] == null) { + throw new RuntimeException("Could not sign " + email + " in: " + problem[0]); + } + return new Actor(token[0]); + } + + Map get(String path) { + return send("GET", path, null); + } + + Map put(String path, String json) { + return send("PUT", path, json); + } + + Map post(String path, String json) { + return send("POST", path, json == null ? "{}" : json); + } + + private Map send(String method, String path, String json) { + ConnectionRequest request = new ConnectionRequest(); + request.setUrl(AppConfig.serverUrl() + path); + request.setHttpMethod(method); + request.setPost(json != null); + if (json != null) { + request.setContentType("application/json"); + request.setRequestBody(json); + } + request.addRequestHeader("Authorization", "Bearer " + token); + request.setAuthorizer(RequestAuthorizer.NONE); + request.setReadResponseForErrors(true); + request.setFailSilently(true); + request.setDuplicateSupported(true); + NetworkManager.getInstance().addToQueueAndWait(request); + status = request.getResponseCode(); + byte[] body = request.getResponseData(); + if (body == null || body.length == 0 || body[0] != '{') { + return new HashMap(); + } + try { + return new JSONParser().parseJSON(new CharArrayReader( + new String(body, "UTF-8").toCharArray())); + } catch (IOException malformed) { + return new HashMap(); + } + } + + /// A courier's position report. + Map online(boolean online, double lat, double lng) { + return post("/api/courier/status", "{\"online\":" + online + ",\"lat\":" + lat + + ",\"lng\":" + lng + ",\"heading\":0}"); + } + + static String text(Map json, String key) { + Object value = json.get(key); + return value instanceof String ? (String) value : ""; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java new file mode 100644 index 00000000000..ebe3269ce19 --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.ui.CN; +import com.codenameone.examples.morsel.ui.Layouts; + +/// The admin console in a desktop window: the places down the side instead of +/// in a drawer, and a list beside what was picked from it. +/// +/// It runs in the second pass of `run-e2e.sh`, where the simulator is a +/// desktop and not a phone. There the app is under the platform's own theme -- +/// GNOME's, on the Linux machine the tests run on -- and the console wears it. +public class AdminDesktopTest extends E2e { + @Override + protected boolean desktop() { + return true; + } + + /// What the app is given to draw in, in pixels: the size of every capture + /// this test takes. + private static final int WIDE = 1280; + private static final int HEIGHT = 754; + + /// What the window adds around the app, as it was last measured. + private int frameWidth; + private int frameHeight = 46; + + /// Makes the app's part of the window exactly `width` by `height`. + /// + /// The simulator is asked for the size of the whole window, and what is + /// left for the app is that less the title bar, the border and the + /// simulator's menu -- which differ with the window manager and the font + /// the menu is drawn in, and are not known until the window is up. A + /// capture of "a 1280 by 800 window" was 754 pixels high on one run and + /// 750 on another, and a capture of another size cannot be compared with + /// its golden at all. So the frame is measured and the request corrected + /// until the app has the size asked for. + private void window(final int width, final int height) { + for (int attempt = 0; attempt < 8; attempt++) { + final int askWidth = width + frameWidth; + final int askHeight = height + frameHeight; + onEdt(() -> CN.setWindowSize(askWidth, askHeight)); + int[] size = resized(); + if (size[0] == width && size[1] == height) { + return; + } + frameWidth = askWidth - size[0]; + frameHeight = askHeight - size[1]; + } + fail("The window could not be made " + width + " by " + height + "; the app has " + + CN.getDisplayWidth() + " by " + CN.getDisplayHeight()); + } + + /// The size the app has once the window has stopped changing. A resize is + /// carried out by the window system some time after it is asked for. + private int[] resized() { + int[] size = {CN.getDisplayWidth(), CN.getDisplayHeight()}; + int still = 0; + for (int waited = 0; waited < 60 && still < 4; waited++) { + waitFor(100); + int[] now = {CN.getDisplayWidth(), CN.getDisplayHeight()}; + still = now[0] == size[0] && now[1] == size[1] ? still + 1 : 0; + size = now; + } + return size; + } + + @Override + public void prepare() { + if (desktopRun()) { + // The window is whatever size it was left; the captures need one. + window(WIDE, HEIGHT); + until(() -> Layouts.wide(), 20000, + "the window to be laid out wide (it is " + CN.getDisplayWidth() + ")"); + } + super.prepare(); + } + + @Override + protected boolean run() throws Exception { + assertTrue(CN.isDesktop(), "this pass runs the simulator as a desktop"); + assertTrue(Layouts.desktopLook(), "the platform's desktop theme is installed"); + onEdt(() -> AppConfig.setMode("admin")); + signIn(ADMIN, "Admin"); + + // The console, not the phone's home: the places are on the screen, + // and there is no drawer to open. + until(() -> has("nav-applications"), 20000, "the places down the side"); + assertFalse(has("menu-open"), "a wide console has a drawer"); + until(() -> findByName("chartDeliveries") != null && has("alert-applications"), 20000, + "the dashboard to be filled in"); + // The numbers are one row, and the charts side by side. + // Waited for: the charts are added when their numbers arrive, and are + // laid out on the turn of the event thread after that. + until(() -> sameRow("deliveriesToday", "revenue"), 10000, "the four numbers to be in one row"); + until(() -> sameRow("chartDeliveries", "chartRevenue"), 10000, "the charts to be side by side"); + shot("admin-desktop-dashboard"); + + // An application opens beside the list it was picked from. + click("nav-applications"); + until(() -> findByName("Applications") != null, 20000, "the applications"); + click("application-applicant@morsel.example"); + until(() -> findByName("ApplicationReview") != null, 20000, "the application"); + untilText("applicationStatus", "Waiting"); + assertTrue(has("application-applicant@morsel.example"), + "the list is still there beside the application"); + assertTrue(leftOf("Applications", "ApplicationReview"), + "the application is beside the list"); + shot("admin-desktop-applications"); + // A document is shown over the application, and left with back. + click("document-licence"); + until(() -> findByName("Document") != null && findByName("picture") != null, 20000, + "the licence to be shown"); + assertTrue(findByName("ApplicationReview") == null, "the document covers the application"); + click("pane-back"); + until(() -> findByName("ApplicationReview") != null, 20000, + "back to lead to the application"); + + // An account, the same way. + click("nav-users"); + until(() -> findByName("Users") != null && has("user-" + CUSTOMER), 20000, "the accounts"); + assertTrue(findByName("nothing") != null, "nothing is picked yet"); + click("user-" + CUSTOMER); + until(() -> findByName("User") != null, 20000, "the account"); + untilText("standing", "In good standing"); + assertTrue(has("user-" + COURIER), "the list is still there beside the account"); + shot("admin-desktop-user"); + + // Asking why is shown in the detail's place too, and refuses nothing. + click("block"); + until(() -> findByName("Reason") != null, 20000, "the question why"); + click("pane-back"); + until(() -> findByName("User") != null, 20000, "back to lead to the account"); + untilText("standing", "In good standing"); + + // The other places open, the dashboard again among them. + click("nav-deliveries"); + until(() -> findByName("Deliveries") != null, 20000, "the deliveries"); + shot("admin-desktop-deliveries"); + click("nav-payments"); + until(() -> findByName("Payments") != null, 20000, "the payments"); + shot("admin-desktop-payments"); + click("nav-pricing"); + until(() -> findByName("Pricing") != null && text("baseFare").length() > 0, 20000, + "the prices"); + shot("admin-desktop-pricing"); + click("nav-home"); + until(() -> findByName("chartDeliveries") != null, 20000, "the dashboard again"); + + // A window dragged narrow is laid out as the phone's console, with + // its drawer, and dragged wide again is this one. + window(520, HEIGHT); + until(() -> has("menu-open") && !has("nav-applications"), 20000, + "a narrow window to be laid out as a phone"); + window(WIDE, HEIGHT); + until(() -> has("nav-applications") && !has("menu-open"), 20000, + "a wide window to be laid out side by side again"); + until(() -> findByName("chartDeliveries") != null, 20000, "the dashboard, wide again"); + + // The map takes the whole window, and comes back to the console. + click("nav-fleet"); + waitForForm("Fleet"); + shot("admin-desktop-live-map"); + back(); + waitForForm("Admin"); + + // The screens that are not the console's are the phone's, kept to a + // column in the middle of the window rather than stretched across it. + click("menu-settings"); + waitForForm("Settings"); + shot("settings-desktop"); + back(); + waitForForm("Admin"); + click("switch-customer"); + waitForForm("Customer"); + shot("customer-desktop"); + signOut(); + return true; + } + + /// Whether the two things called `first` and `second` are level. + private boolean sameRow(String first, String second) { + return findByName(first) != null && findByName(second) != null + && Math.abs(findByName(first).getAbsoluteY() - findByName(second).getAbsoluteY()) + < CN.convertToPixels(3f) + && findByName(first).getAbsoluteX() < findByName(second).getAbsoluteX(); + } + + private boolean leftOf(String first, String second) { + return findByName(first).getAbsoluteX() + findByName(first).getWidth() + <= findByName(second).getAbsoluteX(); + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java new file mode 100644 index 00000000000..310fcaf088b --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +/// Flagging an account and blocking one, from the admin's screens, and what +/// each does to the person it is done to. +public class AdminModerationTest extends E2e { + private static final String OTHER_CUSTOMER = "customer2@morsel.example"; + + @Override + protected boolean run() throws Exception { + final Actor other = Actor.signIn(OTHER_CUSTOMER); + other.get("/api/me"); + assertEqual(200, other.status); + + onEdt(() -> AppConfig.setMode("admin")); + signIn(ADMIN, "Admin"); + // One account arrives already flagged, and the dashboard says so. + until(() -> has("alert-flagged"), 20000, "the flagged account on the dashboard"); + click("users"); + waitForForm("Users"); + type("search", "CUSTOMER2"); + until(() -> has("user-" + OTHER_CUSTOMER) && !has("user-" + CUSTOMER), 20000, + "the search to narrow the list, whatever the case"); + click("user-" + OTHER_CUSTOMER); + waitForForm("User"); + untilText("standing", "In good standing"); + + // A flag is for the admins. The account works as before. + click("flag"); + waitForForm("Reason"); + type("reason", "Three couriers reported the same thing"); + click("confirm"); + untilText("standing", "Flagged"); + untilText("flagReason", "Three couriers reported the same thing"); + other.get("/api/me"); + assertEqual(200, other.status, "a flagged account still works"); + + // A block is for the account: signed out, and kept out. + click("block"); + waitForForm("Reason"); + type("reason", "Blocked while the reports are looked into"); + click("confirm"); + untilText("standing", "Blocked"); + other.get("/api/me"); + assertEqual(401, other.status, "a blocked account's token"); + other.post("/api/deliveries", "{\"pickupLat\":37.8085,\"pickupLng\":-122.4125," + + "\"pickupAddress\":\"Pier 39\",\"dropoffLat\":37.7955,\"dropoffLng\":-122.3937," + + "\"dropoffAddress\":\"Ferry Building\",\"distanceMeters\":2400," + + "\"durationSeconds\":540}"); + assertEqual(401, other.status, "a blocked account asking for a delivery"); + assertFalse(Actor.canSignIn(OTHER_CUSTOMER), "a blocked account can sign in"); + assertTrue(findByName("event-1") != null, "the flag and the block are in the history"); + shot("admin-user"); + + // Through the app, too: the sign-in screen says no. + back(); + waitForForm("Users"); + click("filter-blocked"); + until(() -> has("user-" + OTHER_CUSTOMER), 20000, "the account among the blocked"); + back(); + waitForForm("Admin"); + signOut(); + click("signIn"); + waitForForm("SignIn"); + type("email", OTHER_CUSTOMER); + type("password", PASSWORD); + click("submit"); + until(() -> text("error").length() > 0, 20000, "the blocked sign-in to be refused"); + back(); + waitForForm("Welcome"); + + // Both are lifted again, from the same screen. + signIn(ADMIN, "Admin"); + click("alert-flagged"); + waitForForm("Users"); + click("user-" + OTHER_CUSTOMER); + waitForForm("User"); + click("unblock"); + untilText("standing", "Flagged"); + click("unflag"); + untilText("standing", "In good standing"); + until(() -> { + other.get("/api/me"); + return other.status == 200; + }, 20000, "the account to work again"); + assertTrue(Actor.canSignIn(OTHER_CUSTOMER), "an unblocked account cannot sign in"); + return true; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java new file mode 100644 index 00000000000..16c612be5c1 --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.components.Switch; +import com.codename1.ui.Component; + +import java.util.Map; + +/// The admin's screens, and a role granted from them taking effect on a token +/// that was issued before the grant. +public class AdminTest extends E2e { + @Override + protected boolean run() throws Exception { + // Someone to promote, and a car for the map. + Actor customer = Actor.signIn(CUSTOMER); + customer.get("/api/admin/stats"); + assertEqual(403, customer.status); + Actor courier = Actor.signIn(COURIER); + courier.online(true, HERE.getLatitude() + 0.003, HERE.getLongitude() - 0.004); + + onEdt(() -> AppConfig.setMode("admin")); + signIn(ADMIN, "Admin"); + until(() -> text("couriersOnline").startsWith("1 /"), 20000, "one courier online"); + shot("admin-dashboard"); + // The charts are under the fold. + reveal("chartRevenue"); + shot("admin-trends"); + + click("fleet"); + waitForForm("Fleet"); + untilText("fleetCount", "1 car online"); + courier.online(false, 0, 0); + untilText("fleetCount", "No cars online"); + back(); + waitForForm("Admin"); + + // The one application that is waiting, with its documents. + until(() -> has("alert-applications"), 20000, "the dashboard to say one is waiting"); + click("applications"); + waitForForm("Applications"); + click("application-applicant@morsel.example"); + waitForForm("ApplicationReview"); + untilText("applicationStatus", "Waiting"); + click("document-licence"); + waitForForm("Document"); + until(() -> findByName("picture") != null, 20000, "the licence to be shown"); + back(); + waitForForm("ApplicationReview"); + back(); + waitForForm("Applications"); + back(); + waitForForm("Admin"); + + // Make the customer an admin. + click("users"); + waitForForm("Users"); + click("user-" + CUSTOMER); + waitForForm("User"); + onEdt(() -> { + Component toggle = findByName("admin"); + ((Switch) toggle).setValue(true); + }); + // The token the customer was holding all along now opens the admin API: + // the server reads the account's roles on every request. + until(() -> { + customer.get("/api/admin/stats"); + return customer.status == 200; + }, 20000, "the grant to reach the customer's existing token"); + Map me = customer.get("/api/me"); + assertEqual(Boolean.TRUE, bool(me.get("admin")), "/api/me reports the new role"); + + // And taking it away closes it again. + onEdt(() -> ((Switch) findByName("admin")).setValue(false)); + until(() -> { + customer.get("/api/admin/stats"); + return customer.status == 403; + }, 20000, "the role to be taken away again"); + + // Blocking signs the account out wherever it is, and says why. + click("block"); + waitForForm("Reason"); + click("confirm"); + until(() -> text("error").length() > 0, 20000, "a block with no reason to be refused"); + type("reason", "Asked to be locked while travelling"); + click("confirm"); + untilText("standing", "Blocked"); + customer.get("/api/me"); + assertEqual(401, customer.status, "a blocked account's token"); + click("unblock"); + untilText("standing", "In good standing"); + until(() -> { + customer.get("/api/me"); + return customer.status == 200; + }, 20000, "the block to be lifted"); + assertTrue(findByName("event-1") != null, "both are in the account's history"); + back(); + waitForForm("Users"); + back(); + waitForForm("Admin"); + + // The rest of the console opens, and the prices can be saved. + click("deliveries"); + waitForForm("Deliveries"); + until(() -> has("delivery-0"), 20000, "the deliveries"); + click("delivery-0"); + waitForForm("DeliveryDetail"); + back(); + waitForForm("Deliveries"); + back(); + waitForForm("Admin"); + click("payments"); + waitForForm("Payments"); + back(); + waitForForm("Admin"); + click("pricing"); + waitForForm("Pricing"); + until(() -> text("baseFare").length() > 0, 20000, "the prices"); + final String base = text("baseFare"); + type("commissionPercent", "95"); + click("submit"); + until(() -> text("error").length() > 0, 20000, "a commission of 95% to be refused"); + back(); + waitForForm("Admin"); + click("pricing"); + waitForForm("Pricing"); + untilText("baseFare", base); + return true; + } + + private static Boolean bool(Object value) { + return Boolean.valueOf(Boolean.TRUE.equals(value) || "true".equals(value)); + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java new file mode 100644 index 00000000000..e4913a5613e --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.analytics.AbstractAnalyticsProvider; +import com.codename1.analytics.AnalyticsCrashReport; +import com.codename1.analytics.AnalyticsEvent; +import com.codename1.components.Switch; + +import java.util.ArrayList; +import java.util.List; + +/// What the app reports about how it is used: nothing until the user agrees, +/// then each screen and what was done on it, and never who did it. +/// +/// The reports go to a provider that only writes them down, so none of this +/// leaves the test. +public class AnalyticsTest extends E2e { + private final Recorder recorder = new Recorder(); + + @Override + protected boolean run() throws Exception { + // As on a device the app was just installed on: nobody has answered. + onEdt(() -> { + Telemetry.use(recorder); + Telemetry.unask(); + }); + + // The first sign-in is asked, before the first screen. + signIn(CUSTOMER, "Consent"); + shot("analytics-consent"); + assertFalse(Telemetry.asked(), "the question is answered before anyone answered it"); + click("consentNo"); + waitForForm("Customer"); + assertTrue(Telemetry.asked() && !Telemetry.allowed(), "the answer was no"); + + // A no is a no: screens are shown, a fare is worked out, and nothing + // is reported. + quote(); + click("back"); + click("menu-open"); + click("menu-settings"); + waitForForm("Settings"); + assertEqual(0, recorder.seen.size(), "reported without consent: " + recorder.seen); + + // The switch in Settings is the same answer, and can be changed. + reveal("usageStatistics"); + assertFalse(((Switch) findByName("usageStatistics")).isValue(), "the switch shows yes"); + // Further than it needs to be seen, so that it is whole in the capture. + reveal("privacy"); + shot("settings-analytics"); + onEdt(() -> ((Switch) findByName("usageStatistics")).setValue(true)); + assertTrue(Telemetry.allowed(), "the switch did not grant consent"); + // From the yes on: who, in the two words kept about them, and the + // screen they are on. + until(() -> recorder.has("screen:Settings"), 5000, "the Settings screen view"); + assertTrue(recorder.has("property:role=customer"), "the role: " + recorder.seen); + assertTrue(recorder.has("property:language=en"), "the language: " + recorder.seen); + + // Each screen, once, under the name it was built with, and where the + // user came to it from. + back(); + waitForForm("Customer"); + until(() -> recorder.has("screen:Customer recorder.event("delivery_quoted") != null, 5000, "the quote to be reported"); + assertTrue(recorder.event("delivery_quoted").startsWith("delivery_quoted[delivery] {products="), + "the quote: " + recorder.event("delivery_quoted")); + + // Asking for the delivery, with what an operator would chart about it. + click("request"); + until(() -> recorder.event("delivery_requested") != null, 20000, + "the request to be reported"); + assertEqual("delivery_requested[delivery] {product=standard, payment=cash}", + recorder.event("delivery_requested")); + // Nobody is onboarding, so the server may give the delivery up before the + // customer does; either way the screen ends where it began. + until(() -> { + clickIfThere("dismiss"); + clickIfThere("cancel"); + return findByName("whereTo") != null; + }, 30000, "the delivery to be cancelled"); + + // Signing out, and in again: both are reported now. + signOut(); + until(() -> recorder.has("event:logout[account] {reason=user}"), 5000, + "the sign-out: " + recorder.seen); + assertTrue(recorder.has("screen:Welcome reported = new ArrayList(); + onEdt(() -> reported.addAll(recorder.seen)); + for (String line : reported) { + assertFalse(line.indexOf('@') >= 0 || line.indexOf("Riley") >= 0 + || line.indexOf("Ferry") >= 0 || line.indexOf("37.") >= 0 + || line.indexOf("122.") >= 0 || line.indexOf("userId") >= 0, + "personal data in a report: " + line); + } + + // Taking the yes back stops it at once. + click("menu-open"); + click("menu-settings"); + waitForForm("Settings"); + until(() -> recorder.has("screen:Settings ((Switch) findByName("usageStatistics")).setValue(false)); + assertFalse(Telemetry.allowed(), "the switch did not withdraw consent"); + final int before = recorder.seen.size(); + click("signOut"); + waitForForm("Welcome"); + waitFor(500); + assertEqual(before, recorder.seen.size(), "reported after consent was withdrawn: " + + recorder.seen.subList(before, recorder.seen.size())); + return true; + } + + @Override + public void cleanup() { + if (!desktopRun()) { + // The tests after this one are not asked, and report nothing. + onEdt(() -> Telemetry.allow(false)); + } + super.cleanup(); + } + + /// Writes down what it is told, a line each. + static final class Recorder extends AbstractAnalyticsProvider { + final List seen = new ArrayList(); + + @Override + public String getName() { + return "recorder"; + } + + @Override + public void trackScreen(String name, String referrer) { + seen.add("screen:" + name + (referrer == null ? "" : "<" + referrer)); + } + + @Override + public void trackEvent(AnalyticsEvent event) { + seen.add("event:" + event.getName() + "[" + event.getCategory() + "] " + + event.getParameters()); + } + + @Override + public void setUserId(String id) { + seen.add("userId:" + id); + } + + @Override + public void setUserProperty(String key, String value) { + seen.add("property:" + key + "=" + value); + } + + @Override + public void reportCrash(AnalyticsCrashReport report) { + seen.add("crash:" + report.getMessage()); + } + + boolean has(String line) { + return seen.contains(line); + } + + /// The last report of the event called `name`, without the `event:` + /// it is written down with; null when there is none. + String event(String name) { + for (int iter = seen.size() - 1; iter >= 0; iter--) { + if (seen.get(iter).startsWith("event:" + name + "[")) { + return seen.get(iter).substring("event:".length()); + } + } + return null; + } + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java new file mode 100644 index 00000000000..775b5c3c60b --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.ui.Button; + +import java.util.Map; + +/// A whole delivery from the courier's side of the app, with the customer as the +/// other actor. +public class CourierModeTest extends E2e { + @Override + protected boolean run() throws Exception { + onEdt(() -> AppConfig.setMode("courier")); + signIn(COURIER, "Courier"); + untilText("courierState", "You are offline"); + shot("courier-offline"); + click("goOnline"); + untilText("courierState", "Looking for deliveries"); + + Actor customer = Actor.signIn(CUSTOMER); + Map delivery = customer.post("/api/deliveries", "{\"pickupLat\":37.8085," + + "\"pickupLng\":-122.4125,\"pickupAddress\":\"Pier 39\"," + + "\"dropoffLat\":37.7955,\"dropoffLng\":-122.3937," + + "\"dropoffAddress\":\"Ferry Building\",\"distanceMeters\":2400," + + "\"durationSeconds\":540}"); + assertEqual(200, customer.status, "the customer's request"); + final String id = Actor.text(delivery, "id"); + + untilText("courierState", "Delivery request"); + assertTrue(text("fare").startsWith("$"), "the offer shows the fare"); + shot("courier-offer"); + click("accept"); + untilText("courierState", "Pick up Riley Customer"); + assertEqual("ACCEPTED", Actor.text(customer.get("/api/deliveries/" + id), "state")); + click("arrived"); + untilText("courierState", "Waiting for Riley Customer"); + click("start"); + untilText("courierState", "On the trip"); + shot("courier-on-trip"); + assertFalse(findByName("cancel") instanceof Button, + "a delivery under way cannot be cancelled by the courier"); + click("complete"); + untilText("courierState", "Looking for deliveries"); + assertEqual("COMPLETED", Actor.text(customer.get("/api/deliveries/" + id), "state")); + + // What the courier earned shows up on their own screen. + until(() -> !"0".equals(text("trips")) && text("trips").length() > 0, 20000, + "the trip to be counted"); + click("goOffline"); + untilText("courierState", "You are offline"); + + // And on the screen that says what onboarding has paid. + click("menu-open"); + click("menu-earnings"); + waitForForm("Earnings"); + until(() -> text("balance").startsWith("$"), 20000, "the balance"); + shot("courier-earnings"); + back(); + waitForForm("Courier"); + return true; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java new file mode 100644 index 00000000000..d6a14feffaa --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Photos; + +/// Becoming a courier, start to finish: an account that asks to drive, the +/// application with its photographs, an admin's approval, and the courier's +/// screen that only then opens. +public class CourierOnboardingTest extends E2e { + private static final String[] KINDS = {"photo_id", "licence", "selfie", + "vehicle_registration", "insurance"}; + + @Override + protected boolean run() throws Exception { + // One address per pass, not per run: the server is new for every run, both + // passes share it, and a timestamp in the address wrapped differently from + // run to run in the screens that are compared. + final String email = "dana." + (desktopRun() ? "desk" : "phone") + "@morsel.example"; + Photos.useSource(camera -> photograph()); + + click("register"); + waitForForm("Register"); + type("name", "Dana Courier"); + type("email", email); + type("phone", "+1 555 011 " + (System.currentTimeMillis() % 9000 + 1000)); + type("password", PASSWORD); + click("want-courier"); + click("submit"); + waitForForm("VerifyPhone"); + until(() -> text("demoCode").indexOf("code is ") > 0, 20000, "the demo code"); + String notice = text("demoCode"); + int at = notice.indexOf("code is ") + 8; + type("code", notice.substring(at, at + 6)); + click("submit"); + + // Asking to drive does not make a courier. It leads to the application. + waitForForm("Apply"); + assertFalse(Session.user().courier(), "a new account is not a courier"); + untilText("applicationStatus", "Not started"); + assertFalse(has("goOnline"), "an applicant can go online"); + click("apply"); + waitForForm("Application"); + + untilText("step", "Step 1 of 5"); + type("legalName", "Dana Morgan Courier"); + date("dateOfBirth", 1990, 5, 17); + click("gender-female"); + click("next"); + untilText("step", "Step 2 of 5"); + type("licenceNumber", "D7781204"); + date("licenceExpiry", 2031, 8, 31); + click("next"); + untilText("step", "Step 3 of 5"); + // Moving on with the car left out is refused. + click("next"); + until(() -> text("error").length() > 0, 20000, "an empty step to be refused"); + type("vehicleMake", "Toyota"); + type("vehicleModel", "Corolla"); + type("vehicleYear", "2022"); + type("vehicleColor", "Silver"); + type("vehiclePlate", "8DNA204"); + click("next"); + untilText("step", "Step 4 of 5"); + + // One by the camera, one from the gallery; three of the five so far. + untilText("status-photo_id", "Needed"); + click("camera-photo_id"); + untilText("status-photo_id", "Added"); + click("gallery-licence"); + untilText("status-licence", "Added"); + click("camera-selfie"); + untilText("status-selfie", "Added"); + shot("onboarding-documents"); + // The server will not take an application with papers missing. + click("next"); + untilText("step", "Step 5 of 5"); + untilText("documentsAdded", "3 / 5"); + click("submitApplication"); + until(() -> text("error").length() > 0, 20000, "an incomplete application to be refused"); + click("backStep"); + untilText("step", "Step 4 of 5"); + click("camera-vehicle_registration"); + untilText("status-vehicle_registration", "Added"); + click("gallery-insurance"); + untilText("status-insurance", "Added"); + click("next"); + untilText("documentsAdded", "5 / 5"); + click("submitApplication"); + + waitForForm("Apply"); + untilText("applicationStatus", "Under review"); + assertFalse(has("goOnline"), "an applicant under review can go online"); + Actor applicant = Actor.signIn(email); + applicant.online(true, HERE.getLatitude(), HERE.getLongitude()); + assertEqual(403, applicant.status, "an applicant going online through the API"); + shot("onboarding-under-review"); + signOut(); + + // The admin reads it, opens every paper, and approves. + signIn(ADMIN, "Admin"); + click("applications"); + waitForForm("Applications"); + click("application-" + email); + waitForForm("ApplicationReview"); + untilText("applicationStatus", "Waiting"); + shot("admin-application"); + for (int iter = 0; iter < KINDS.length; iter++) { + click("document-" + KINDS[iter]); + waitForForm("Document"); + until(() -> findByName("picture") != null, 20000, KINDS[iter] + " to be shown"); + back(); + waitForForm("ApplicationReview"); + } + click("approve"); + waitForForm("Applications"); + back(); + waitForForm("Admin"); + signOut(); + + // Approved: the same account now opens on the courier's screen, and + // going online works. + signIn(email, "Courier"); + assertTrue(Session.user().courier(), "approval makes the account a courier"); + untilText("courierState", "You are offline"); + click("goOnline"); + untilText("courierState", "Looking for deliveries"); + click("goOffline"); + untilText("courierState", "You are offline"); + return true; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java new file mode 100644 index 00000000000..c960708550b --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import java.util.Map; + +/// A whole delivery, from the customer's side of the app: the courier is the other +/// actor, and everything the customer sees change is the server telling the app. +public class DeliveryFlowTest extends E2e { + @Override + protected boolean run() throws Exception { + // A car near the customer, online. + Actor courier = Actor.signIn(COURIER); + courier.online(true, HERE.getLatitude() + 0.004, HERE.getLongitude() + 0.003); + assertEqual(200, courier.status, "the courier going online"); + + signIn(CUSTOMER, "Customer"); + quote(); + assertTrue(text("price-standard").startsWith("$"), "each kind of delivery has its price"); + shot("delivery-quote"); + click("request"); + untilText("deliveryState", "Finding a courier"); + + // The offer reaches the courier, who takes it. + String id = offered(courier); + courier.post("/api/courier/offers/" + id + "/accept", null); + assertEqual(200, courier.status, "accepting the offer"); + untilText("deliveryState", "Courier on the way"); + courier.online(true, HERE.getLatitude() + 0.002, HERE.getLongitude() + 0.001); + shot("delivery-courier-on-the-way"); + + courier.post("/api/courier/deliveries/" + id + "/arrived", null); + untilText("deliveryState", "Courier at pickup"); + courier.post("/api/courier/deliveries/" + id + "/start", null); + untilText("deliveryState", "On the trip"); + courier.post("/api/courier/deliveries/" + id + "/complete", null); + assertEqual(200, courier.status, "completing the delivery"); + untilText("deliveryState", "You have arrived"); + assertTrue(text("fare").startsWith("$"), "the receipt shows what was charged"); + shot("delivery-receipt"); + + // The stars are sent as they are tapped, and the sheet stays for the + // rest of what a customer may want to say about the delivery. + click("star-5"); + click("done"); + until(() -> findByName("whereTo") != null, 20000, "the customer's screen to reset"); + + // Rated once: the server holds the delivery as the customer left it. + Actor customer = Actor.signIn(CUSTOMER); + Map delivery = customer.get("/api/deliveries/" + id); + assertEqual("COMPLETED", Actor.text(delivery, "state")); + assertEqual(5, ((Number) delivery.get("rating")).intValue()); + courier.online(false, 0, 0); + return true; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java new file mode 100644 index 00000000000..2a07e8f0ff1 --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.components.SpanLabel; +import com.codename1.io.FileSystemStorage; +import com.codename1.io.oidc.OidcTokens; +import com.codename1.io.oidc.TokenStore; +import com.codename1.maps.LatLng; +import com.codename1.maps.MapView; +import com.codename1.maps.vector.BundledTileSource; +import com.codename1.maps.vector.TileCallback; +import com.codename1.maps.vector.TileSource; +import com.codename1.testing.AbstractTest; +import com.codename1.ui.Button; +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.Image; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.spinner.Picker; +import com.codename1.ui.util.ImageIO; +import com.codename1.util.AsyncResource; +import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.map.Locator; +import com.codenameone.examples.morsel.map.Maps; +import com.codenameone.examples.morsel.map.Routes; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.morsel.ui.Look; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Photos; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Calendar; +import java.util.Map; + +/// What the end-to-end tests share. Each of them drives the real app, through +/// its real screens, against a real server -- the one `run-e2e.sh` starts on the +/// `test` profile, which has the four demo accounts, a geocoder that knows ten +/// places and a text-message sender that hands the code back. +/// +/// Three things are pinned so that a run is the same wherever it happens: the +/// map draws from tiles bundled with the tests, a route is the straight line, +/// and the device stands at a fixed point. None of it touches the server. +public abstract class E2e extends AbstractTest { + public static final String PASSWORD = "morsel-demo"; + public static final String CUSTOMER = "customer@morsel.example"; + public static final String COURIER = "courier@morsel.example"; + public static final String ADMIN = "admin@morsel.example"; + /// Where the device stands: the middle of the bundled tiles. + public static final LatLng HERE = new LatLng(37.8080, -122.4120); + + protected interface Check { + boolean ok(); + } + + @Override + public boolean shouldExecuteOnEDT() { + return false; + } + + @Override + public int getTimeoutMillis() { + return 180000; + } + + /// Whether this run of the tests is the one in a desktop window. + /// + /// `run-e2e.sh` runs the tests twice: on the simulator's phone, and then + /// with the simulator as a desktop, where the app is a window an admin + /// works in. A test belongs to one of the two -- see [#desktop()] -- and + /// passes without doing anything in the other. + static boolean desktopRun() { + return "true".equals(System.getProperty("morsel.e2e.desktop")); + } + + /// Whether this test is one for the desktop window. The phone's are not. + protected boolean desktop() { + return false; + } + + private boolean mine() { + return desktop() == desktopRun(); + } + + /// The test itself. + protected abstract boolean run() throws Exception; + + @Override + public final boolean runTest() throws Exception { + if (!mine()) { + log("Not a test for " + (desktopRun() ? "the desktop" : "the phone") + "; skipped"); + return true; + } + return run(); + } + + @Override + public void prepare() { + if (!mine()) { + return; + } + Session.useStore(new MemoryStore()); + Maps.useTiles(new Tiles()); + Routes.useStraightLines(true); + Locator.pin(HERE); + onEdt(() -> { + LiveChannel.stop(); + // The tests read the screens in English, whatever the machine + // they run on speaks. + Prefs.setLanguage("en"); + Prefs.setTheme(Prefs.THEME_LIGHT); + // Answered, with a no: no test is stopped by the question a first + // sign-in is asked, and none reports how it used the app. The + // test of the reports gives its own answers. + Telemetry.allow(false); + Look.apply(); + Lang.apply(); + Session.start(AppConfig.serverUrl()); + AppConfig.setMode(Nav.CUSTOMER); + Nav.launch(); + }); + waitForForm("Welcome"); + quiet(); + } + + /// Puts the server back where a test expects to find it: nobody onboarding, + /// and the customer with no delivery under way. The tests share one server, and + /// without this a test that failed half way would fail the ones after it + /// for a reason that has nothing to do with them. + private void quiet() { + Actor.signIn(COURIER).online(false, 0, 0); + Actor customer = Actor.signIn(CUSTOMER); + String id = Actor.text(customer.get("/api/deliveries/active"), "id"); + if (id.length() > 0) { + customer.post("/api/deliveries/" + id + "/cancel", null); + } + Photos.useSource(null); + } + + @Override + public void cleanup() { + if (!mine()) { + return; + } + onEdt(() -> { + LiveChannel.stop(); + Session.start(AppConfig.serverUrl()); + }); + } + + // ------------------------------------------------------------- onboarding + + protected void onEdt(Runnable work) { + CN.callSeriallyAndWait(work); + } + + /// Clicks the button called `name` on the screen that is showing, once it + /// is there. + protected void click(final String name) { + until(() -> findByName(name) instanceof Button, 20000, "a button called " + name); + onEdt(() -> { + Button button = (Button) findByName(name); + button.pressed(); + button.released(); + }); + } + + /// Clicks the button called `name` if it is on the screen at this moment. + /// + /// For a button the server may take away while the test looks: asking + /// whether it is there and then clicking it are two turns of the event + /// thread, and the screen can change between them. + protected void clickIfThere(final String name) { + onEdt(() -> { + Component found = findByName(name); + if (found instanceof Button) { + Button button = (Button) found; + button.pressed(); + button.released(); + } + }); + } + + protected void type(final String name, final String text) { + until(() -> findByName(name) instanceof TextArea, 20000, "a field called " + name); + onEdt(() -> { + TextArea field = (TextArea) findByName(name); + field.setText(text); + }); + } + + /// The text of the label or field called `name`, or "" while there is none. + protected String text(String name) { + Component found = findByName(name); + if (found instanceof SpanLabel) { + return ((SpanLabel) found).getText(); + } + if (found instanceof Label) { + return ((Label) found).getText(); + } + if (found instanceof TextArea) { + return ((TextArea) found).getText(); + } + return ""; + } + + protected void until(Check check, int timeoutMillis, String what) { + long deadline = System.currentTimeMillis() + timeoutMillis; + while (!check.ok()) { + if (System.currentTimeMillis() > deadline) { + Form current = Display.getInstance().getCurrent(); + throw new RuntimeException("Timed out waiting for " + what + " (on " + + (current == null ? "no form" : current.getName()) + ")"); + } + waitFor(100); + } + } + + /// Waits for the screen called `name` to be the one showing. + protected void waitForForm(final String name) { + until(() -> { + Form current = Display.getInstance().getCurrent(); + return current != null && name.equals(current.getName()); + }, 30000, "the " + name + " screen"); + waitFor(100); + } + + protected void untilText(final String name, final String expected) { + until(() -> expected.equals(text(name)), 30000, + name + " to read \"" + expected + "\" (it reads \"" + text(name) + "\")"); + } + + /// Signs in through the app's own screens. + protected void signIn(String email, String homeForm) { + click("signIn"); + waitForForm("SignIn"); + type("email", email); + type("password", PASSWORD); + click("submit"); + waitForForm(homeForm); + } + + /// Whether there is something to press called `name` on the screen. + protected boolean has(String name) { + return findByName(name) instanceof Button; + } + + /// Scrolls whatever is called `name` into view. + protected void reveal(final String name) { + until(() -> findByName(name) != null, 20000, name + " to be there"); + onEdt(() -> { + Component found = findByName(name); + // The nearest thing above it that scrolls, which on a home screen + // is not the form. + Container holder = found.getParent(); + while (holder != null && !holder.isScrollableY()) { + holder = holder.getParent(); + } + if (holder != null) { + holder.scrollComponentToVisible(found); + } + }); + } + + /// Goes back, the way the arrow in the title bar does. + protected void back() { + onEdt(() -> Display.getInstance().getCurrent().getBackCommand().actionPerformed(null)); + } + + /// Signs out through the menu of a home screen. + protected void signOut() { + click("menu-open"); + click("menu-signOut"); + waitForForm("Welcome"); + } + + /// Sets the date field called `name`. A test cannot turn the wheels of + /// the platform's date picker, which is not part of the app. + protected void date(final String name, final int year, final int month, final int day) { + until(() -> findByName(name) instanceof Picker, 20000, "a date called " + name); + onEdt(() -> { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + ((Picker) findByName(name)).setDate(calendar.getTime()); + }); + } + + /// Books the delivery to the Ferry Building as far as the sheet that prices + /// it, which is where a test chooses what to do next. + protected void quote() { + click("whereTo"); + waitForForm("PlaceSearch"); + type("query", "Ferry"); + click("place-0"); + waitForForm("Customer"); + until(() -> text("fare").startsWith("$"), 20000, "the fare"); + } + + /// Waits for a delivery to be offered to `courier` and returns its id. + protected String offered(final Actor courier) { + final String[] id = new String[1]; + until(() -> { + Map delivery = courier.get("/api/courier/active"); + if ("OFFERED".equals(Actor.text(delivery, "state"))) { + id[0] = Actor.text(delivery, "id"); + } + return id[0] != null; + }, 30000, "the offer to reach the courier"); + return id[0]; + } + + /// A picture for the app to take as a photograph: the tests have neither + /// a camera nor a gallery. It is drawn rather than read from a file so + /// that it is the same picture wherever the tests run. + protected static Image photograph() { + Image picture = Image.createImage(640, 400, 0xffe9edf2); + Graphics g = picture.getGraphics(); + g.setColor(0x2f6fed); + g.fillRect(0, 0, 640, 70); + g.setColor(0xb8c2cf); + g.fillRect(40, 110, 150, 190); + g.setColor(0x8795a7); + for (int line = 0; line < 5; line++) { + g.fillRect(230, 120 + line * 38, line % 2 == 0 ? 340 : 250, 16); + } + return picture; + } + + // -------------------------------------------------------- screenshots + + /// Captures the screen as `name`.png and holds it against the golden of + /// the same name. + /// + /// `morsel.shots.dir` is where the capture goes; without it nothing is + /// captured, which is a run from an IDE. `morsel.goldens.dir` is where + /// the goldens are. A capture with no golden passes -- that is how a new + /// one is made: run, look at it, copy it over. + /// + /// The comparison is loose on purpose: a pixel counts as different only + /// when it is far off, and a capture fails only when a twentieth of it is. + /// Text is drawn a little differently from one machine to the next, and + /// this is here to notice a screen that changed, not a glyph. + protected void shot(String name) { + String out = fileUrl(System.getProperty("morsel.shots.dir")); + if (out == null) { + return; + } + settle(); + final Image[] captured = new Image[1]; + onEdt(() -> captured[0] = Display.getInstance().captureScreen()); + FileSystemStorage files = FileSystemStorage.getInstance(); + try { + files.mkdir(out); + OutputStream png = files.openOutputStream(out + "/" + name + ".png"); + try { + ImageIO.getImageIO().save(captured[0], png, ImageIO.FORMAT_PNG, 1f); + } finally { + png.close(); + } + String goldens = fileUrl(System.getProperty("morsel.goldens.dir")); + String golden = goldens == null ? "" : goldens + "/" + name + ".png"; + if (golden.length() == 0 || !files.exists(golden)) { + log("No golden for " + name + "; captured only"); + return; + } + InputStream in = files.openInputStream(golden); + Image expected; + try { + expected = Image.createImage(in); + } finally { + in.close(); + } + assertEqual(expected.getWidth(), captured[0].getWidth(), name + ": width changed"); + assertEqual(expected.getHeight(), captured[0].getHeight(), name + ": height changed"); + int[] want = expected.getRGB(); + int[] got = captured[0].getRGB(); + int off = 0; + for (int iter = 0; iter < want.length; iter++) { + if (far(want[iter], got[iter])) { + off++; + } + } + assertTrue(off * 20L <= want.length, name + ": " + off + " of " + want.length + + " pixels differ from the golden"); + } catch (java.io.IOException err) { + throw new RuntimeException("Could not capture " + name + ": " + err); + } + } + + /// A directory the build names as an ordinary path, in the form + /// `FileSystemStorage` takes one; null when it names none. + private static String fileUrl(String path) { + if (path == null || path.length() == 0) { + return null; + } + return path.startsWith("file:") ? path : "file://" + path; + } + + private static boolean far(int a, int b) { + return Math.abs(((a >> 16) & 0xff) - ((b >> 16) & 0xff)) > 60 + || Math.abs(((a >> 8) & 0xff) - ((b >> 8) & 0xff)) > 60 + || Math.abs((a & 0xff) - (b & 0xff)) > 60; + } + + /// Waits for the screen to stop moving: transitions over, the map drawn. + private void settle() { + waitFor(1500); + until(() -> { + Component map = findByName("map"); + return !(map instanceof MapView) || ((MapView) map).isMapReady(); + }, 20000, "the map"); + waitFor(500); + } + + /// The map of the neighbourhood the tests happen in, read from the test + /// resources, so that a run draws the same streets with no network. + /// + /// A tile outside the bundled area is answered with an empty one. A tile + /// that fails is asked for again and is never drawn, and the map then + /// never reports itself ready -- which a test waiting to take a capture + /// would read as a map that is still loading. + static final class Tiles implements TileSource { + private static final byte[] EMPTY = new byte[0]; + // At the root of the resources: a Codename One resource has no + // directory, and the simulator refuses a path that names one. + private final TileSource bundled = + new BundledTileSource("/mt_{z}_{x}_{y}.mvt", true, 13, 13); + + @Override + public boolean isVector() { + return true; + } + + @Override + public int getTileSize() { + return 256; + } + + @Override + public int getMinZoom() { + return 13; + } + + @Override + public int getMaxZoom() { + return 13; + } + + @Override + public String getAttribution() { + return "(c) OpenStreetMap contributors"; + } + + @Override + public void fetchTile(int z, int x, int y, final TileCallback callback) { + bundled.fetchTile(z, x, y, new TileCallback() { + @Override + public void tileLoaded(int tz, int tx, int ty, byte[] data) { + callback.tileLoaded(tz, tx, ty, data); + } + + @Override + public void tileFailed(int tz, int tx, int ty) { + callback.tileLoaded(tz, tx, ty, EMPTY); + } + }); + } + } + + /// Tokens kept for the length of the run and nowhere else. + static final class MemoryStore implements TokenStore { + private OidcTokens kept; + + @Override + public AsyncResource load(String key) { + AsyncResource out = new AsyncResource(); + out.complete(kept); + return out; + } + + @Override + public AsyncResource save(String key, OidcTokens tokens) { + kept = tokens; + AsyncResource out = new AsyncResource(); + out.complete(Boolean.TRUE); + return out; + } + + @Override + public AsyncResource clear(String key) { + kept = null; + AsyncResource out = new AsyncResource(); + out.complete(Boolean.TRUE); + return out; + } + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java new file mode 100644 index 00000000000..1db091a0f2a --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import java.util.Map; + +/// Paying: a card that is refused, a card that is added, a delivery charged to it, +/// a tip on top, and the receipt that says all of it. +public class PaymentsTest extends E2e { + @Override + protected boolean run() throws Exception { + Actor courier = Actor.signIn(COURIER); + courier.online(true, HERE.getLatitude() + 0.004, HERE.getLongitude() + 0.003); + signIn(CUSTOMER, "Customer"); + + click("menu-open"); + click("menu-wallet"); + waitForForm("Wallet"); + until(() -> has("method-cash"), 20000, "the wallet"); + click("addCard"); + waitForForm("Card"); + // A number that is not a card number is caught before it is sent. + until(() -> has("saveCard"), 20000, "the card form"); + type("cardNumber", "4242 4242 4242 4241"); + type("cardExpiry", "12/34"); + type("cardCvc", "123"); + type("cardHolder", "Riley Customer"); + click("saveCard"); + until(() -> text("error").length() > 0, 20000, "a mistyped number to be caught"); + // A card the bank declines is refused by the server, in its words. + type("cardNumber", "4000 0000 0000 0002"); + click("saveCard"); + until(() -> text("error").length() > 0 && has("saveCard") + && findByName("saveCard").isEnabled(), 20000, "the declined card to be refused"); + type("cardNumber", "4242 4242 4242 4242"); + click("saveCard"); + waitForForm("Wallet"); + final String card = card(); + // Tapping it makes it the one that pays. + click(card); + Actor customer = Actor.signIn(CUSTOMER); + until(() -> { + Map preferences = customer.get("/api/account/preferences"); + String chosen = Actor.text(preferences, "defaultPaymentMethodId"); + return chosen.length() > 0 && !"cash".equals(chosen); + }, 20000, "the card to become the account's default"); + shot("wallet"); + back(); + waitForForm("Customer"); + + quote(); + until(() -> text("payWith").indexOf("4242") >= 0, 20000, + "the delivery to be paid by the new card (it says \"" + text("payWith") + "\")"); + click("request"); + untilText("deliveryState", "Finding a courier"); + final String id = offered(courier); + courier.post("/api/courier/offers/" + id + "/accept", null); + courier.post("/api/courier/deliveries/" + id + "/arrived", null); + courier.post("/api/courier/deliveries/" + id + "/start", null); + courier.post("/api/courier/deliveries/" + id + "/complete", null); + assertEqual(200, courier.status, "completing the delivery"); + untilText("deliveryState", "You have arrived"); + until(() -> text("paidWith").indexOf("4242") >= 0, 20000, "what the delivery was paid with"); + Map receipt = customer.get("/api/payments/receipts/" + id); + assertEqual("paid", Actor.text(receipt, "status"), "the charge"); + assertEqual(0, ((Number) receipt.get("tip")).intValue(), "no tip before one is given"); + + click("star-5"); + click("tip-15"); + click("done"); + until(() -> findByName("whereTo") != null, 20000, "the customer's screen to reset"); + until(() -> ((Number) customer.get("/api/payments/receipts/" + id).get("tip")).intValue() + > 0, 20000, "the tip to be charged"); + + // The delivery is first in the history, and opens as its receipt. + click("menu-open"); + click("menu-history"); + waitForForm("Deliveries"); + click("delivery-0"); + waitForForm("Receipt"); + untilText("paymentStatus", "Paid"); + assertTrue(text("tipPaid").startsWith("$"), "the receipt shows the tip"); + assertFalse(has("sendTip"), "a delivery can be tipped once"); + shot("receipt"); + back(); + waitForForm("Deliveries"); + back(); + waitForForm("Customer"); + courier.online(false, 0, 0); + return true; + } + + /// The name of the row of the first card in the wallet. + private String card() { + final String[] found = new String[1]; + until(() -> { + for (int iter = 0; iter < 6 && found[0] == null; iter++) { + if (has("method-" + iter)) { + found[0] = "method-" + iter; + } + } + return found[0] != null; + }, 20000, "the card to be in the wallet"); + return found[0]; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java new file mode 100644 index 00000000000..c13959f746e --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.net.Session; + +/// A new account: register, be signed in, verify the phone, arrive. +public class RegisterTest extends E2e { + @Override + protected boolean run() throws Exception { + // One address per pass, not per run: the server is new for every run, both + // passes share it, and a timestamp in the address wrapped differently from + // run to run in the screens that are compared. + String email = "new." + (desktopRun() ? "desk" : "phone") + "@morsel.example"; + click("register"); + waitForForm("Register"); + type("name", "Nico Newcomer"); + type("email", email); + type("phone", "+1 555 010 " + (System.currentTimeMillis() % 9000 + 1000)); + type("password", PASSWORD); + shot("register"); + click("submit"); + + // Registered and signed in, and asked for the code. The test server + // sends no text; it says what the code is. + waitForForm("VerifyPhone"); + assertEqual(email, Session.user().username()); + assertFalse(Session.user().phoneVerified(), "a new account starts unverified"); + until(() -> text("demoCode").indexOf("code is ") > 0, 20000, "the demo code"); + shot("verify-phone"); + String notice = text("demoCode"); + int at = notice.indexOf("code is ") + 8; + String code = notice.substring(at, at + 6); + + // A wrong code is refused by the server, and says so. + type("code", "000000".equals(code) ? "111111" : "000000"); + click("submit"); + until(() -> text("error").length() > 0, 20000, "the wrong-code error"); + assertFalse(Session.user().phoneVerified()); + + type("code", code); + click("submit"); + waitForForm("Customer"); + assertTrue(Session.user().phoneVerified(), "the right code verifies the number"); + return true; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java new file mode 100644 index 00000000000..577cd0d8a3f --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codenameone.examples.morsel.net.Account; +import java.util.Map; + +/// The settings: a language and a theme that change the app on the spot, and +/// a delivery preference that reaches the server and the next delivery asked for. +public class SettingsTest extends E2e { + @Override + protected boolean run() throws Exception { + final Actor customer = Actor.signIn(CUSTOMER); + signIn(CUSTOMER, "Customer"); + click("menu-open"); + click("menu-settings"); + waitForForm("Settings"); + untilText("signOut", "Sign out"); + shot("settings"); + + // Another language, at once and on the account. + click("language-he"); + until(() -> text("signOut").length() > 0 && !"Sign out".equals(text("signOut")), 20000, + "the screen to be in Hebrew"); + until(() -> "he".equals(Actor.text(customer.get("/api/account/preferences"), "language")), + 20000, "the language to be kept with the account"); + shot("settings-hebrew"); + click("language-en"); + untilText("signOut", "Sign out"); + + // Dark, and the screen behind is dark as well. + click("theme-dark"); + until(() -> "dark".equals(Actor.text(customer.get("/api/account/preferences"), "theme")), + 20000, "the theme to be kept with the account"); + shot("settings-dark"); + back(); + waitForForm("Customer"); + shot("customer-home-dark"); + click("menu-open"); + click("menu-settings"); + waitForForm("Settings"); + click("theme-system"); + + // A woman at the wheel: kept with the account, and sent with the delivery. + click("courierGender-women"); + until(() -> Account.lastSent() != null + && "women".equals(Account.lastSent().courierGender()), 20000, + "the preference to be sent"); + until(() -> { + Map kept = customer.get("/api/account/preferences"); + return "women".equals(Actor.text(kept, "courierGender")) + && "system".equals(Actor.text(kept, "theme")) + && "en".equals(Actor.text(kept, "language")); + }, 20000, "the account to hold what was chosen"); + back(); + waitForForm("Customer"); + quote(); + click("request"); + untilText("deliveryState", "Finding a courier"); + Map delivery = customer.get("/api/deliveries/active"); + assertEqual("women", Actor.text(delivery, "courierGender"), "the delivery asks for a woman"); + // With no woman onboarding nearby the server may give the delivery up before + // the customer does; either way the screen ends where it began. + until(() -> { + clickIfThere("dismiss"); + clickIfThere("cancel"); + return findByName("whereTo") != null; + }, 30000, "the delivery to be cancelled"); + + // Back as it was, for whoever uses the account next. + click("menu-open"); + click("menu-settings"); + waitForForm("Settings"); + click("courierGender-any"); + until(() -> "any".equals(Actor.text(customer.get("/api/account/preferences"), + "courierGender")), 20000, "the preference to be put back"); + click("signOut"); + waitForForm("Welcome"); + return true; + } +} diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java new file mode 100644 index 00000000000..dee3991fe36 --- /dev/null +++ b/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel; + +import com.codename1.ui.Button; +import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.morsel.ui.Nav; + +/// Signing in, and what a role does and does not open. +public class SignInTest extends E2e { + @Override + protected boolean run() throws Exception { + shot("welcome"); + + // A wrong password says so and stays put. + click("signIn"); + waitForForm("SignIn"); + type("email", CUSTOMER); + type("password", "not-the-password"); + click("submit"); + until(() -> text("error").length() > 0, 20000, "the sign-in error"); + assertEqual("SignIn", com.codename1.ui.Display.getInstance().getCurrent().getName()); + assertNull(Session.user(), "nobody is signed in after a wrong password"); + + // The right one lands a customer on the customer's screen. + type("password", PASSWORD); + click("submit"); + waitForForm("Customer"); + assertEqual(CUSTOMER, Session.user().username()); + assertTrue(Session.user().customer() && !Session.user().courier() && !Session.user().admin(), + "the demo customer has the customer role and no other"); + shot("customer-home"); + + // Asking for the admin's screens without the role gets the customer's. + onEdt(() -> Nav.switchTo(Nav.ADMIN)); + waitForForm("Customer"); + // Asking to drive gets the way there, the application, and nothing a + // courier has: there is no going online from it. + onEdt(() -> Nav.switchTo(Nav.COURIER)); + waitForForm("Apply"); + untilText("applicationStatus", "Not started"); + assertFalse(has("goOnline"), "someone who is not a courier can go online"); + onEdt(() -> Nav.switchTo(Nav.CUSTOMER)); + waitForForm("Customer"); + // And the server agrees, which is the part that matters: the same + // person's token is refused on the courier's and the admin's API. + Actor customer = Actor.signIn(CUSTOMER); + customer.get("/api/admin/stats"); + assertEqual(403, customer.status, "a customer asking for the admin's numbers"); + customer.online(true, HERE.getLatitude(), HERE.getLongitude()); + assertEqual(403, customer.status, "a customer going online as a courier"); + customer.get("/api/me"); + assertEqual(200, customer.status); + + // The menu offers no mode to switch to -- only the way to apply for + // one -- and leads to the settings. + assertTrue(has("menu-drive"), "a customer is shown how to become a courier"); + assertFalse(findByName("switch-courier") instanceof Button, "a customer is offered onboarding"); + assertFalse(findByName("switch-admin") instanceof Button, "a customer is offered admin"); + click("menu-open"); + click("menu-settings"); + waitForForm("Settings"); + click("signOut"); + waitForForm("Welcome"); + assertNull(Session.user()); + return true; + } +} diff --git a/scripts/morsel/pom.xml b/scripts/morsel/pom.xml new file mode 100644 index 00000000000..50830ab81be --- /dev/null +++ b/scripts/morsel/pom.xml @@ -0,0 +1,314 @@ + + 4.0.0 + com.codenameone.examples.morsel + morsel + 1.0-SNAPSHOT + pom + morsel + morsel + https://www.codenameone.com + + + GPL v2 With Classpath Exception + https://openjdk.java.net/legal/gplv2+ce.html + repo + A business-friendly OSS license + + + + + shared + common + + + 8.0-SNAPSHOT + 8.0-SNAPSHOT + UTF-8 + 1.8 + 11 + 1.7.11 + 3.8.0 + 1.8 + 1.8 + 1.8 + morsel + + + + + com.codenameone + java-runtime + ${cn1.version} + + + com.codenameone + codenameone-core + ${cn1.version} + + + com.codenameone + codenameone-javase + ${cn1.version} + + + com.codenameone + codenameone-android-compat + ${cn1.version} + provided + + + + com.codenameone + cn1-binaries-javase + ${cn1.version} + pom + runtime + + + com.codenameone + codenameone-buildclient + ${cn1.version} + system + ${user.home}/.codenameone/CodeNameOneBuildClient.jar + + + + + + + + com.codenameone + codenameone-maven-plugin + ${cn1.plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + maven-antrun-plugin + org.apache.maven.plugins + 3.1.0 + + + + + + com.codenameone + codenameone-maven-plugin + ${cn1.plugin.version} + + + + + + + codenameone + https://repo.codenameone.com/maven2 + + true + + + false + + + + + + codenameone-plugins + https://repo.codenameone.com/maven2 + + true + + + false + + + + + + + + cn1-client-tests + + + codename1.platform + !backend + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + com.codenameone + codenameone-maven-plugin + ${cn1.plugin.version} + + + + + + + + javascript + + + codename1.platform + javascript + + + ${basedir}/javascript/pom.xml + + + + javascript + + + + ios + + + codename1.platform + ios + + + ${basedir}/ios/pom.xml + + + + ios + + + + win + + + codename1.platform + win + + + ${basedir}/win/pom.xml + + + + win + + + + linux + + + codename1.platform + linux + + + ${basedir}/linux/pom.xml + + + + linux + + + + + backend + + + codename1.platform + backend + + + ${basedir}/backend/pom.xml + + + + backend + + + + android + + + codename1.platform + android + + + ${basedir}/android/pom.xml + + + + android + + + + javase + + + codename1.platform + javase + + + ${basedir}/javase/pom.xml + + + + javase + + + + cn1libs + + + ${basedir}/cn1libs/pom.xml + + + + cn1libs + + + + diff --git a/scripts/morsel/run-e2e.sh b/scripts/morsel/run-e2e.sh new file mode 100755 index 00000000000..fa37f293605 --- /dev/null +++ b/scripts/morsel/run-e2e.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# +# The end-to-end tests: starts the Morsel server on the `test` profile, then +# runs the app's test suite against it in the Codename One simulator. +# +# run-e2e.sh [--native] [--prebuilt] +# +# --native runs the tests against the compiled server +# --prebuilt uses the server as it was last built, without building it again +# +# The tests drive the real screens: they sign in, request a delivery, drive it and +# administer it, while a second user acts through the API. They capture a few +# screens into target/e2e-shots and hold them against screenshots/. +# +# Needs JAVA_HOME on JDK 17 or newer, and the Codename One artifacts this project +# builds against in the local Maven repository. The simulator needs a display: +# xvfb-run is used when there is one, which is what makes this run on a CI +# machine and in a container. +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +MVN="${MVN:-mvn}" +PORT="${MORSEL_PORT:-18080}" +LOG="${MORSEL_SERVER_LOG:-$HERE/target/e2e-server.log}" +SHOTS="$HERE/target/e2e-shots" +MODE="--jvm" +BUILD=1 +for arg in "$@"; do + case "$arg" in + --native) MODE="--native" ;; + --prebuilt) BUILD=0 ;; + *) echo "[morsel-e2e] unknown option: $arg" >&2; exit 2 ;; + esac +done + +mkdir -p "$HERE/target" +rm -rf "$SHOTS" +mkdir -p "$SHOTS" + +echo "[morsel-e2e] java: $(java -version 2>&1 | head -1)" +if [ "$BUILD" = 1 ]; then + "$HERE/backend/server.sh" build "$MODE" +fi + +echo "[morsel-e2e] starting the server on port $PORT" +CN1_PROFILE=test "$HERE/backend/server.sh" run "$MODE" --prebuilt --port "$PORT" >"$LOG" 2>&1 & +SERVER_PID=$! +cleanup() { + kill "$SERVER_PID" >/dev/null 2>&1 || true + wait "$SERVER_PID" 2>/dev/null || true +} +trap cleanup EXIT + +ready=0 +for _ in $(seq 1 90); do + if curl -fs -o /dev/null "http://localhost:$PORT/oauth2/jwks" 2>/dev/null; then + ready=1 + break + fi + if ! kill -0 "$SERVER_PID" 2>/dev/null; then + break + fi + sleep 1 +done +if [ "$ready" != "1" ]; then + echo "[morsel-e2e] the server did not start; its log:" >&2 + cat "$LOG" >&2 || true + exit 1 +fi + +RUNNER=() +if command -v xvfb-run >/dev/null 2>&1; then + RUNNER=(xvfb-run -a -s "-screen 0 1280x1024x24") +fi + +# The simulator is a JVM the build forks, and JAVA_TOOL_OPTIONS is what reaches +# it: where the server is, where the captures go and where the goldens are. +# The heap is given a ceiling too. Left alone a JVM takes a quarter of the +# machine's memory before it collects anything, which on a shared runner is +# enough to have the kernel end it between two tests. +export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS:-} -Xmx768m -Dmorsel.server.url=http://localhost:$PORT -Dmorsel.shots.dir=$SHOTS -Dmorsel.goldens.dir=$HERE/screenshots" + +set +e +${RUNNER[@]+"${RUNNER[@]}"} "$MVN" -B -f "$HERE/pom.xml" -Dcodename1.platform=javase -Ptest verify +STATUS=$? +set -e + +# The second pass: the same tests with the simulator as a desktop window and +# not a phone, which is where the admin console is laid out side by side and +# wears the platform's own theme. Each test knows which pass it is for and +# passes untouched in the other (E2e.desktop()). +# +# The simulator is a desktop when its "desktopSkin" preference says so; there +# is no switch for it on the command line. Java keeps preferences in files only +# on Linux, where java.util.prefs.userRoot can point them at a directory of +# this run's own -- so that is where this pass runs, which covers CI and a +# container. Elsewhere it would mean rewriting the developer's own simulator +# preferences, and it is skipped with a word. +if [ "$STATUS" -eq 0 ] && [ "$(uname -s)" = "Linux" ]; then + PREFS="$HERE/target/e2e-desktop-prefs" + NODE="$PREFS/.java/.userPrefs/com/codename1/impl/javase" + rm -rf "$PREFS" + mkdir -p "$NODE" + cat >"$NODE/prefs.xml" <<'XML' + + + + + +XML + echo "[morsel-e2e] the desktop pass" + # The last two are the project's own build hints, from + # common/codenameone_settings.properties. A packaged desktop app is given + # them by its launcher; a test run starts the simulator in + # common/target/cn1-reports, from where it does not find the project's + # settings, so it has to be told. + export JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Dmorsel.e2e.desktop=true -Djava.util.prefs.userRoot=$PREFS -Dcodename1.arg.nativeTheme=native -Dcodename1.arg.desktop.titleBar=native" + set +e + ${RUNNER[@]+"${RUNNER[@]}"} "$MVN" -B -f "$HERE/pom.xml" -Dcodename1.platform=javase -Ptest verify + STATUS=$? + set -e +elif [ "$STATUS" -eq 0 ]; then + echo "[morsel-e2e] not on Linux: the desktop pass was skipped" +fi + +if [ "$STATUS" -ne 0 ]; then + echo "[morsel-e2e] the tests failed; the end of the server's log:" >&2 + tail -n 80 "$LOG" >&2 || true + exit "$STATUS" +fi +echo "[morsel-e2e] passed; captures are in $SHOTS" diff --git a/scripts/morsel/run-web-e2e.sh b/scripts/morsel/run-web-e2e.sh new file mode 100755 index 00000000000..7416b683076 --- /dev/null +++ b/scripts/morsel/run-web-e2e.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# +# The web app, end to end: builds the Morsel server and the browser build of +# the app, starts the server with the app staged where it serves it from, and +# drives the hosted app in a headless browser (web-e2e.mjs). +# +# run-web-e2e.sh [--native] [--prebuilt] +# +# --native runs the compiled server; --prebuilt skips both builds and uses what +# `backend/server.sh build --web` left. +# +# What it proves: a person who opens the server's address gets the app, can sign +# in as the demo customer and reaches the home screen, with the live channel open. +# Screenshots of each step and the browser's log go to target/web-e2e. +# +# Needs JAVA_HOME on JDK 17 or newer, the Codename One artifacts this project builds +# against in the local Maven repository, and Node with Playwright and its +# Chromium: `npm ci && npx playwright install chromium` in scripts/, or a +# NODE_PATH that has it. Nothing here opens a window. +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +PORT="${MORSEL_WEB_PORT:-18081}" +OUT="${MORSEL_WEB_OUT:-$HERE/target/web-e2e}" +LOG="$OUT/server.log" +MODE="--jvm" +PREBUILT=0 +for arg in "$@"; do + case "$arg" in + --native) MODE="--native" ;; + --prebuilt) PREBUILT=1 ;; + *) echo "[morsel-web] unknown argument $arg" >&2; exit 2 ;; + esac +done + +if ! command -v node >/dev/null 2>&1; then + echo "[morsel-web] node is not on the PATH; the browser is driven with Playwright" >&2 + exit 2 +fi + +rm -rf "$OUT" +mkdir -p "$OUT" + +echo "[morsel-web] java: $(java -version 2>&1 | head -1)" +if [ "$PREBUILT" = 0 ]; then + "$HERE/backend/server.sh" build "$MODE" --web +fi +if [ ! -f "$HERE/backend/target/webapp/index.html" ]; then + echo "[morsel-web] no web app is staged in backend/target/webapp" >&2 + exit 1 +fi + +echo "[morsel-web] starting the server on port $PORT" +CN1_PROFILE=test "$HERE/backend/server.sh" run "$MODE" --prebuilt --port "$PORT" >"$LOG" 2>&1 & +SERVER_PID=$! +cleanup() { + kill "$SERVER_PID" >/dev/null 2>&1 || true + wait "$SERVER_PID" 2>/dev/null || true +} +trap cleanup EXIT + +ready=0 +for _ in $(seq 1 90); do + if curl -fs -o /dev/null "http://localhost:$PORT/oauth2/jwks" 2>/dev/null; then + ready=1 + break + fi + if ! kill -0 "$SERVER_PID" 2>/dev/null; then + break + fi + sleep 1 +done +if [ "$ready" != "1" ]; then + echo "[morsel-web] the server did not start; its log:" >&2 + cat "$LOG" >&2 || true + exit 1 +fi + +fail() { + echo "[morsel-web] FAILED: $*" >&2 + exit 1 +} + +# What no browser is needed to see: the app is at /, the API kept its paths, and +# the script that is most of the download goes out compressed. +headers="$(curl -fsS -D - -o /dev/null "http://localhost:$PORT/")" \ + || fail "the server did not answer at /" +echo "$headers" | grep -qi '^content-type: text/html' || fail "/ is not served as a page" +echo "$headers" | grep -qi '^cache-control: no-cache' || fail "/ is not revalidated" +headers="$(curl -fsS -H 'Accept-Encoding: gzip' -D - -o /dev/null \ + "http://localhost:$PORT/translated_app.js")" || fail "translated_app.js is not served" +echo "$headers" | grep -qi '^content-encoding: gzip' \ + || fail "translated_app.js was not served compressed" +status="$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:$PORT/api/me")" +[ "$status" = "401" ] || fail "/api/me answered $status without a token, not 401" + +set +e +URL="http://localhost:$PORT/" OUT="$OUT" node "$HERE/web-e2e.mjs" +STATUS=$? +set -e + +if [ "$STATUS" -ne 0 ]; then + echo "[morsel-web] the end of the browser's log:" >&2 + tail -n 40 "$OUT/browser.log" >&2 || true + echo "[morsel-web] the end of the server's log:" >&2 + tail -n 40 "$LOG" >&2 || true + exit "$STATUS" +fi +echo "[morsel-web] passed; screenshots are in $OUT" diff --git a/scripts/morsel/shared/pom.xml b/scripts/morsel/shared/pom.xml new file mode 100644 index 00000000000..9fced81ac51 --- /dev/null +++ b/scripts/morsel/shared/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + com.codenameone.examples.morsel + morsel + 1.0-SNAPSHOT + + com.codenameone.examples.morsel + morsel-shared + 1.0-SNAPSHOT + jar + morsel-shared + + + + 17 + 17 + + + + + com.codenameone + codenameone-core + provided + + + diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountApi.java new file mode 100644 index 00000000000..3f466402096 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountApi.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.PUT; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +/// Opening an account, proving a phone number, asking who is signed in, and the +/// settings an account keeps. +/// +/// The app calls these through the generated client. The server implements the +/// generated `AccountApiServer`, and its build routes each path to that +/// implementation; neither side writes the other's half. +@RestClient +public interface AccountApi { + /// Opens an account. The one call here that needs no sign-in. + @POST("/api/account/register") + void register(@Body RegisterDto request, OnComplete> callback); + + @GET("/api/me") + void me(OnComplete> callback); + + /// Sends a verification code to the phone number on the account. + @POST("/api/account/phone/start") + void startPhoneVerification(OnComplete> callback); + + @POST("/api/account/phone/verify") + void verifyPhone(@Body PhoneCodeDto code, OnComplete> callback); + + /// A ticket for the live channel; see [TicketDto]. + @POST("/api/live/ticket") + void liveTicket(OnComplete> callback); + + @GET("/api/account/preferences") + void preferences(OnComplete> callback); + + @PUT("/api/account/preferences") + void savePreferences(@Body PreferencesDto preferences, + OnComplete> callback); + + @GET("/api/account/profile") + void profile(OnComplete> callback); + + @PUT("/api/account/profile") + void saveProfile(@Body ProfileDto profile, OnComplete> callback); + + @POST("/api/account/password") + void changePassword(@Body PasswordChangeDto change, OnComplete> callback); + + /// Closes the account for good. Refused while a delivery is under way, and + /// without the account's password: a token is what a borrowed phone + /// holds too, and this cannot be taken back. + @POST("/api/account/delete") + void deleteAccount(@Body AccountClosureDto closure, OnComplete> callback); + + // tag::morsel-push-contract[] + /// Records a device the signed-in account is to be sent notifications on. + /// Registering the same token again moves it to this account: a phone + /// belongs to whoever signed in on it last. + @POST("/api/account/push") + void registerPush(@Body PushDeviceDto device, OnComplete> callback); + + /// Forgets a device, on signing out: the next person to use the phone is + /// not to be told about this account's deliveries. + @POST("/api/account/push/remove") + void unregisterPush(@Body PushDeviceDto device, OnComplete> callback); + // end::morsel-push-contract[] +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountClosureDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountClosureDto.java new file mode 100644 index 00000000000..8b13c16ad82 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AccountClosureDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The request to close an account: the password of whoever is asking. +@Mapped +public record AccountClosureDto(String password) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminApi.java new file mode 100644 index 00000000000..4a6e59772d1 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminApi.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.PUT; +import com.codename1.annotations.rest.Path; +import com.codename1.annotations.rest.Query; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +import java.util.List; + +/// Admin mode: the numbers, the fleet, who may drive, and who may do what. +@RestClient +public interface AdminApi { + @GET("/api/admin/stats") + void stats(OnComplete> callback); + + /// The charts, over the last `days` days; 1 to 90. + @GET("/api/admin/stats/series") + void statsSeries(@Query("days") int days, OnComplete> callback); + + @GET("/api/admin/deliveries") + void deliveries(OnComplete>> callback); + + @GET("/api/admin/users") + void users(OnComplete>> callback); + + @GET("/api/admin/users/{username}") + void user(@Path("username") String username, OnComplete> callback); + + @GET("/api/admin/couriers") + void couriers(OnComplete>> callback); + + @POST("/api/admin/users/{username}/roles") + void setRoles(@Path("username") String username, @Body RoleChangeDto roles, + OnComplete> callback); + + /// Blocks an account, or lifts the block. + @POST("/api/admin/users/{username}/suspend") + void suspend(@Path("username") String username, @Body SuspendDto suspend, + OnComplete> callback); + + @POST("/api/admin/users/{username}/flag") + void flag(@Path("username") String username, @Body FlagDto flag, + OnComplete> callback); + + /// Onboarding applications, newest first. `status` narrows them to one status; + /// empty lists them all. + @GET("/api/admin/applications") + void applications(@Query("status") String status, + OnComplete>> callback); + + @GET("/api/admin/applications/{username}") + void application(@Path("username") String username, + OnComplete> callback); + + @GET("/api/admin/applications/{username}/documents/{kind}") + void document(@Path("username") String username, @Path("kind") String kind, + OnComplete> callback); + + /// Approves an application, which makes its account a courier. + @POST("/api/admin/applications/{username}/approve") + void approve(@Path("username") String username, + OnComplete> callback); + + @POST("/api/admin/applications/{username}/reject") + void reject(@Path("username") String username, @Body ReasonDto reason, + OnComplete> callback); + + @POST("/api/admin/deliveries/{id}/cancel") + void cancelDelivery(@Path("id") String id, @Body ReasonDto reason, + OnComplete> callback); + + @POST("/api/admin/deliveries/{id}/refund") + void refundDelivery(@Path("id") String id, @Body ReasonDto reason, + OnComplete> callback); + + /// Every receipt, newest first. + @GET("/api/admin/payments") + void payments(OnComplete>> callback); + + @GET("/api/admin/pricing") + void pricing(OnComplete> callback); + + @PUT("/api/admin/pricing") + void savePricing(@Body PricingDto pricing, OnComplete> callback); +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminStatsDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminStatsDto.java new file mode 100644 index 00000000000..43343db3a01 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/AdminStatsDto.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The numbers on the admin dashboard. +@Mapped +public record AdminStatsDto( + int users, + int couriers, + int couriersOnline, + int deliveriesToday, + int deliveriesActive, + int deliveriesCompleted, + long revenueCents, + String currency) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CardDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CardDto.java new file mode 100644 index 00000000000..a23e25bd2d2 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CardDto.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A card as typed into the app. Read by the simulated processor only, which +/// keeps the brand and the last four digits and nothing else; with a real +/// processor the card never reaches the app or this server. +@Mapped +public record CardDto(String number, int expMonth, int expYear, String cvc, String holder) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApi.java new file mode 100644 index 00000000000..820de82a985 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApi.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.PUT; +import com.codename1.annotations.rest.Path; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +import java.util.List; + +/// Courier mode: going on line, answering offers, running a delivery and being paid +/// for it. +@RestClient +public interface CourierApi { + /// Goes on or off line, and reports where the car is. + @POST("/api/courier/status") + void status(@Body CourierStatusDto status, OnComplete> callback); + + @GET("/api/courier/me") + void me(OnComplete> callback); + + /// The offer waiting for an answer or the delivery under way, or one whose state + /// is NONE. + @GET("/api/courier/active") + void active(OnComplete> callback); + + @POST("/api/courier/offers/{id}/accept") + void accept(@Path("id") String id, OnComplete> callback); + + @POST("/api/courier/offers/{id}/decline") + void decline(@Path("id") String id, OnComplete> callback); + + @POST("/api/courier/deliveries/{id}/arrived") + void arrived(@Path("id") String id, OnComplete> callback); + + @POST("/api/courier/deliveries/{id}/start") + void start(@Path("id") String id, OnComplete> callback); + + @POST("/api/courier/deliveries/{id}/complete") + void complete(@Path("id") String id, OnComplete> callback); + + @POST("/api/courier/deliveries/{id}/cancel") + void cancel(@Path("id") String id, OnComplete> callback); + + @GET("/api/courier/earnings") + void earnings(OnComplete> callback); + + @GET("/api/courier/history") + void history(OnComplete>> callback); + + @GET("/api/courier/earnings/summary") + void earningsSummary(OnComplete> callback); + + @GET("/api/courier/payouts") + void payouts(OnComplete>> callback); + + /// Pays the whole balance out to the payout account. Refused when there is + /// no balance, or no account to send it to. + @POST("/api/courier/payouts") + void cashOut(OnComplete> callback); + + @GET("/api/courier/payout-account") + void payoutAccount(OnComplete> callback); + + @PUT("/api/courier/payout-account") + void savePayoutAccount(@Body PayoutAccountDto account, + OnComplete> callback); +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApplicationDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApplicationDto.java new file mode 100644 index 00000000000..d85d146110f --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierApplicationDto.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +import java.util.List; + +/// An application to drive: who, which car, and the papers that prove both. +/// +/// @param status `none`, `draft`, `pending`, `approved` or `rejected`. +/// @param dateOfBirth `yyyy-MM-dd`. +/// @param licenceExpiry `yyyy-MM-dd`. +/// @param product `standard`, `comfort` or `xl`. +/// @param accessible Whether the car takes a wheelchair. +/// @param petFriendly Whether a customer's pet is welcome in the car. +/// @param documents One entry for each kind of document asked for, uploaded or not. +/// @param submittedAt Milliseconds since the epoch; 0 until it is handed in. +/// @param reviewedAt Milliseconds since the epoch; 0 until an admin decides. +/// @param reviewedBy The admin who decided; empty until one has. +@Mapped +public record CourierApplicationDto( + String username, + String name, + String status, + String legalName, + String dateOfBirth, + String licenceNumber, + String licenceExpiry, + String vehicleMake, + String vehicleModel, + int vehicleYear, + String vehicleColor, + String vehiclePlate, + int vehicleSeats, + String product, + boolean accessible, + boolean petFriendly, + List documents, + String rejectionReason, + long submittedAt, + long reviewedAt, + String reviewedBy) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierDto.java new file mode 100644 index 00000000000..cc94de4e43e --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierDto.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A courier, for the admin's fleet view and for the courier's own status. +/// +/// @param approved Whether the onboarding application was approved. Only an approved courier can go on +/// line. +/// @param product `standard`, `comfort` or `xl`: the kind of delivery this car is offered. +/// @param acceptanceRate The share of the offers this courier was sent that they took, 0 to 1; 1 +/// before any was sent. +@Mapped +public record CourierDto( + String username, + String displayName, + String vehicle, + String plate, + boolean online, + double lat, + double lng, + double heading, + double rating, + long lastSeen, + int deliveries, + boolean approved, + String product, + String gender, + boolean accessible, + double acceptanceRate, + boolean flagged) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierStatusDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierStatusDto.java new file mode 100644 index 00000000000..6aae5625bb6 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CourierStatusDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A courier going on or off line, or reporting where the car is. +@Mapped +public record CourierStatusDto(boolean online, double lat, double lng, double heading) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CustomerApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CustomerApi.java new file mode 100644 index 00000000000..1654403aff7 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/CustomerApi.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.Path; +import com.codename1.annotations.rest.Query; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +import java.util.List; + +// tag::morsel-contract-api[] +/// Asking for a delivery and following it. +@RestClient +public interface CustomerApi { + @POST("/api/deliveries/quote") + void quote(@Body DeliveryRequestDto request, OnComplete> callback); + + @POST("/api/deliveries") + void request(@Body DeliveryRequestDto request, OnComplete> callback); + + /// The delivery this customer has under way, or one whose state is NONE. + @GET("/api/deliveries/active") + void active(OnComplete> callback); + + @GET("/api/deliveries/history") + void history(OnComplete>> callback); + + @GET("/api/deliveries/{id}") + void delivery(@Path("id") String id, OnComplete> callback); + + @POST("/api/deliveries/{id}/cancel") + void cancel(@Path("id") String id, OnComplete> callback); + + @POST("/api/deliveries/{id}/rate") + void rate(@Path("id") String id, @Body RatingDto rating, OnComplete> callback); + + /// The free couriers around a point, for the cars on the map. + @GET("/api/deliveries/nearby") + void nearby(@Query("lat") double lat, @Query("lng") double lng, + OnComplete>> callback); + + /// Tells the server which Live Activity this phone shows the delivery in, so + /// that it can keep it up to date while the app is in the background. + @POST("/api/deliveries/{id}/activity") + void activity(@Path("id") String id, @Body DeliveryActivityDto activity, + OnComplete> callback); +} +// end::morsel-contract-api[] diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayAmountDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayAmountDto.java new file mode 100644 index 00000000000..8c9adef147d --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayAmountDto.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// One day of a courier's earnings. +/// +/// @param date `yyyy-MM-dd`, in UTC. +@Mapped +public record DayAmountDto(String date, long amount, int trips) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayStatsDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayStatsDto.java new file mode 100644 index 00000000000..5623bc235d9 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DayStatsDto.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// One day of the service. +/// +/// @param date `yyyy-MM-dd`, in UTC. +/// @param revenue Cents: the fares of the day's completed, paid deliveries. +@Mapped +public record DayStatsDto( + String date, + int deliveries, + int completed, + int cancelled, + long revenue, + int newUsers) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityDto.java new file mode 100644 index 00000000000..c7abf2d8d86 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityDto.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The Live Activity a customer's phone started for a delivery, by the id the +/// platform gave it. The server addresses its updates to that id. +@Mapped +public record DeliveryActivityDto(String activityId) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityKeys.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityKeys.java new file mode 100644 index 00000000000..637160c4d8f --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryActivityKeys.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +/// The words the app and the server agree on for a delivery's Live Activity: the +/// "your courier is on the way" card on the lock screen. +/// +/// The app starts the activity with a state built from these keys, and the +/// server sends later states with the same keys. +public final class DeliveryActivityKeys { + /// The kind of activity. + public static final String KIND = "delivery"; + /// What is happening, in words: "On the way", "Your courier is here". + public static final String STATUS = "status"; + /// The courier's name. + public static final String COURIER = "courier"; + /// The car, as the courier described it. + public static final String CAR = "car"; + /// The car's license plate. + public static final String PLATE = "plate"; + /// When the car gets where it is going next, in words: "4 min", or empty + /// once it is there. + public static final String ETA = "eta"; + /// How far along the delivery is, a number from 0 to 1 for the progress bar. + public static final String PROGRESS = "progress"; + + /// The values of [#PROGRESS] at each step. + public static final double ON_THE_WAY = 0.25d; + public static final double ARRIVED = 0.5d; + public static final double IN_TRIP = 0.75d; + public static final double DONE = 1d; + + private DeliveryActivityKeys() { + } +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryDto.java new file mode 100644 index 00000000000..5e3fd241515 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryDto.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A delivery, as each party to it sees it. The same object answers the customer, the +/// courier and the admin; nothing in it is private to one of them. +/// +/// @param state NONE means "there is no such delivery", which is how a question like "what is my active +/// delivery?" is answered when there is not one. +/// @param courierLat Where the courier was last seen. Meaningful once a courier is attached. +/// @param courierLng See [#courierLat()]. +/// @param courierHeading The way the courier's car points, in degrees clockwise from north. +/// @param fareCents The fare, before the service fee and any tip. +/// @param requestedAt Milliseconds since the epoch. +/// @param updatedAt Milliseconds since the epoch. +/// @param rating The customer's rating of the delivery, 1 to 5, or 0 before there is one. +/// @param paymentStatus PENDING until the delivery completes, then PAID or FAILED; REFUNDED once an +/// admin has returned the money. A cash delivery is PAID when it completes. A refund the processor +/// has not finished is REFUND_PENDING until it ends REFUNDED, or REFUND_FAILED when the money +/// did not go back. +/// @param offerExpiresInSeconds For a courier looking at an offer: how long is left to answer it. +/// @param product `standard`, `comfort` or `xl`. +/// @param paymentMethodLabel How the delivery is paid for, as the customer would recognise it: "Cash", +/// "Visa 4242". +/// @param cash True when the customer pays the courier in cash, so that the courier's screen can say to +/// collect it. The label above is for people and is not compared. +/// @param serviceFee Cents, like every amount here. +/// @param total What the customer pays: the fare, the service fee and the tip. +/// @param courierPhotoInitials The courier's initials, for a badge where a photo would go. +/// @param etaMinutes Minutes until the courier reaches the pickup, or the drop-off once the trip has +/// started; 0 when there is nothing to estimate. +/// @param courierGender What the customer asked for, for the courier to read: this and the four that +/// follow. +/// @param courierPhone The number to call or text the courier at, given to the customer. Filled only +/// while a courier has the delivery and it is not over, and never for anyone else. +/// @param customerPhone The number to call or text the customer at, given to the courier, on the same +/// terms as [#courierPhone()]. +@Mapped +public record DeliveryDto( + String id, + DeliveryState state, + String customerUsername, + String customerName, + String courierUsername, + String courierName, + String vehicle, + String plate, + double courierRating, + double pickupLat, + double pickupLng, + String pickupAddress, + double dropoffLat, + double dropoffLng, + String dropoffAddress, + double courierLat, + double courierLng, + double courierHeading, + long fareCents, + String currency, + double distanceMeters, + double durationSeconds, + long requestedAt, + long updatedAt, + int rating, + String paymentStatus, + int offerExpiresInSeconds, + String product, + String paymentMethodLabel, + boolean cash, + long serviceFee, + long tip, + long total, + String courierPhotoInitials, + int etaMinutes, + String courierGender, + boolean quietDelivery, + boolean accessibleVehicle, + boolean petFriendly, + String note, + String courierPhone, + String customerPhone) { + + /// The answer when there is no delivery to give: a question like "what is my + /// active delivery?" asked by someone who has none. Its state is + /// [DeliveryState#NONE] and every other component is empty. + public static DeliveryDto none() { + return new DeliveryDto(null, DeliveryState.NONE, null, null, null, null, null, null, 0d, 0d, 0d, + null, 0d, 0d, null, 0d, 0d, 0d, 0L, null, 0d, 0d, 0L, 0L, 0, null, 0, null, null, + false, 0L, 0L, 0L, null, 0, null, false, false, false, null, null, null); + } +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryRequestDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryRequestDto.java new file mode 100644 index 00000000000..95e450dca33 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryRequestDto.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +// tag::morsel-contract-dto[] +/// Where a delivery starts and ends, and how the customer wants it, for a quote and for +/// the request itself. +/// +/// @param distanceMeters The length of the route the app drew, when it has one. The server prices +/// from this and [#durationSeconds()] only while they are plausible for the two points; +/// otherwise it prices from its own estimate. +/// @param durationSeconds How long the app's route takes, when it has one. +/// @param paymentMethodId A saved method's id, or `cash`. Empty means the customer's default method, +/// and cash when there is none. +/// @param product `standard`, `comfort` or `xl`; empty reads as standard. +/// @param courierGender `any` or `women`; empty reads as any. +/// @param quietDelivery Whether the customer would rather not talk. +/// @param accessibleVehicle Whether the car has to take a wheelchair. +/// @param petFriendly Whether a pet is coming: only couriers who take pets are offered the delivery. +/// @param note A few words for the courier: which door, what to look for. +@Mapped +public record DeliveryRequestDto( + double pickupLat, + double pickupLng, + String pickupAddress, + double dropoffLat, + double dropoffLng, + String dropoffAddress, + double distanceMeters, + double durationSeconds, + String paymentMethodId, + String product, + String courierGender, + boolean quietDelivery, + boolean accessibleVehicle, + boolean petFriendly, + String note) { +} +// end::morsel-contract-dto[] diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryState.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryState.java new file mode 100644 index 00000000000..09f045efbba --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DeliveryState.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +/// The states of a delivery, as [DeliveryDto#state] carries them and the server stores +/// them: by name, on the wire and in the database column. +public enum DeliveryState { + /// No delivery. Never stored; it answers "is there one?". + NONE, + /// Asked for; no courier has been offered it yet. + REQUESTED, + /// Offered to one courier, who has a few seconds to answer. + OFFERED, + /// A courier took it and is on the way to the pickup. + ACCEPTED, + /// The courier is at the pickup. + ARRIVED, + /// The customer is in the car. + IN_PROGRESS, + COMPLETED, + CANCELLED_BY_CUSTOMER, + CANCELLED_BY_COURIER, + /// Stopped by an admin, at any point before it completed. + CANCELLED_BY_ADMIN, + /// Nobody was near enough, or nobody took it. + NO_COURIERS; + + /// Whether a delivery in this state is still going on: someone is waiting for a + /// courier, or a courier is involved and the trip is not over. + public boolean isActive() { + return this == REQUESTED || this == OFFERED || this == ACCEPTED || this == ARRIVED + || this == IN_PROGRESS; + } + + /// The state called `name`, or null for a name that is none of them -- one + /// a newer server sends, say. Unlike `valueOf` it never throws, so a + /// caller reading a name off the wire needs no `catch`. + public static DeliveryState named(String name) { + DeliveryState[] all = values(); + for (int iter = 0; iter < all.length; iter++) { + if (all[iter].name().equals(name)) { + return all[iter]; + } + } + return null; + } +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentContentDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentContentDto.java new file mode 100644 index 00000000000..9edd16afe7e --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentContentDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A stored document, picture included. +@Mapped +public record DocumentContentDto(String kind, String contentType, String dataBase64) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentDto.java new file mode 100644 index 00000000000..29308f0a737 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentDto.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// One of the documents of a onboarding application. The picture itself is read +/// separately, and only by the applicant and the admins. +/// +/// @param kind `photo_id`, `licence`, `selfie`, `vehicle_registration` or `insurance`. +/// @param status `missing`, `uploaded`, `accepted` or `rejected`. +/// @param uploadedAt Milliseconds since the epoch, or 0 when missing. +@Mapped +public record DocumentDto(String kind, String status, long uploadedAt) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentUploadDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentUploadDto.java new file mode 100644 index 00000000000..8534aee8862 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/DocumentUploadDto.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A picture of a document, sent as base64. JPEG or PNG, and small: the app +/// shrinks a photograph before sending it. +/// +/// @param contentType `image/jpeg` or `image/png`. +@Mapped +public record DocumentUploadDto(String kind, String contentType, String dataBase64) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsDto.java new file mode 100644 index 00000000000..36830d416ac --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsDto.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// What a courier has earned. `deliveries` counts every paid delivery the courier ever +/// completed, and `todayDeliveries` those of them that `todayCents` is made of. +@Mapped +public record EarningsDto( + long todayCents, + long weekCents, + long totalCents, + int deliveries, + int todayDeliveries, + String currency) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsSummaryDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsSummaryDto.java new file mode 100644 index 00000000000..4a376e70779 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/EarningsSummaryDto.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +import java.util.List; + +/// A courier's money: what came in, what is owed, and how the last two weeks +/// went. Amounts are cents, and are the courier's share -- the fare less the +/// service's commission, plus tips. +/// +/// @param balance Earned and not yet paid out. +/// @param onlineMinutes Time on line today. +/// @param acceptanceRate 0 to 1. +/// @param daily The last 14 days, oldest first, every day present. +@Mapped +public record EarningsSummaryDto( + String currency, + long today, + long week, + long month, + long balance, + int trips, + long tips, + int onlineMinutes, + double rating, + double acceptanceRate, + List daily) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareOptionDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareOptionDto.java new file mode 100644 index 00000000000..047d7ef6157 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareOptionDto.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// One product a delivery can be taken as, priced for the delivery being quoted. +/// +/// @param product `standard`, `comfort` or `xl`. +/// @param fare Cents, like every amount here. +/// @param total What the customer would pay: [#fare()] plus [#serviceFee()]. +/// @param couriersNearby Couriers of this product in reach; 0 when there is none. +/// @param etaMinutes Minutes for the nearest of them to arrive; 0 when there is none. +@Mapped +public record FareOptionDto( + String product, + String name, + String description, + int seats, + long fare, + long serviceFee, + long total, + int couriersNearby, + int etaMinutes) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareQuoteDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareQuoteDto.java new file mode 100644 index 00000000000..ea464bb7a6f --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FareQuoteDto.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +import java.util.List; + +/// What a delivery would cost and how soon a courier could be there. +/// +/// @param amountCents The fare of the product asked for, before the service fee. +/// @param couriersNearby Couriers online within reach of the pickup right now. +/// @param etaSeconds A rough time for the nearest of them to arrive, or 0 when there is none. +/// @param options The same delivery priced as each product, cheapest first. +@Mapped +public record FareQuoteDto( + long amountCents, + String currency, + double distanceMeters, + double durationSeconds, + int couriersNearby, + int etaSeconds, + List options) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FlagDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FlagDto.java new file mode 100644 index 00000000000..c9d8b9c2dfa --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/FlagDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Marks an account for a closer look, or clears the mark. +@Mapped +public record FlagDto(boolean flagged, String reason) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/GeoApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/GeoApi.java new file mode 100644 index 00000000000..23e4823d6e7 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/GeoApi.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.Query; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +import java.util.List; + +/// Finding places. The server asks a geocoding service on the app's behalf, so +/// the app holds no key for one and the service can be changed in one place. +@RestClient +public interface GeoApi { + /// Places matching `text`, nearest to the given point first. + @GET("/api/geo/search") + void search(@Query("q") String text, @Query("lat") double lat, @Query("lng") double lng, + OnComplete>> callback); + + /// The address at a point. + @GET("/api/geo/reverse") + void reverse(@Query("lat") double lat, @Query("lng") double lng, + OnComplete> callback); +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ModerationEventDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ModerationEventDto.java new file mode 100644 index 00000000000..2cca6481d2e --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ModerationEventDto.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Something an admin did to an account, and why. +/// +/// @param at Milliseconds since the epoch. +/// @param by The admin who did it. +/// @param action `flag`, `unflag`, `block`, `unblock`, `approve`, `reject`, `roles`. +@Mapped +public record ModerationEventDto(long at, String by, String action, String reason) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NameCountDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NameCountDto.java new file mode 100644 index 00000000000..8d12bba318b --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NameCountDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A slice of a chart: a name and how many. +@Mapped +public record NameCountDto(String name, int count) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NearbyCourierDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NearbyCourierDto.java new file mode 100644 index 00000000000..93b1aabb2b3 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/NearbyCourierDto.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A free courier near a point, for the cars on the customer's map. Says where and +/// what, and not who. +@Mapped +public record NearbyCourierDto(double lat, double lng, double heading, String product) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/OnboardingApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/OnboardingApi.java new file mode 100644 index 00000000000..c6ba2aba2a9 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/OnboardingApi.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.PUT; +import com.codename1.annotations.rest.Path; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +/// Applying to drive. Open to anyone signed in, and only ever about the +/// caller's own application: the COURIER role, and `CourierApi` with it, comes +/// once an admin approves. +@RestClient +public interface OnboardingApi { + /// The caller's application; its status is `none` before one is begun. + @GET("/api/onboarding/application") + void application(OnComplete> callback); + + /// Saves the form as a draft. Nothing has to be complete yet. + @PUT("/api/onboarding/application") + void save(@Body CourierApplicationDto application, + OnComplete> callback); + + /// Stores one document, replacing an earlier one of the same kind. + @POST("/api/onboarding/application/documents") + void upload(@Body DocumentUploadDto document, + OnComplete> callback); + + /// One of the caller's own documents, to show back to them. + @GET("/api/onboarding/application/documents/{kind}") + void document(@Path("kind") String kind, OnComplete> callback); + + /// Hands the application to the admins. Refused until every field is filled + /// and every document is there. + @POST("/api/onboarding/application/submit") + void submit(OnComplete> callback); +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PasswordChangeDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PasswordChangeDto.java new file mode 100644 index 00000000000..c3536bc40b2 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PasswordChangeDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A change of password: the one in use, and the one to use from now on. +@Mapped +public record PasswordChangeDto(String current, String replacement) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentApi.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentApi.java new file mode 100644 index 00000000000..d5df1da1e61 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentApi.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.Path; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.util.OnComplete; + +import java.util.List; + +/// Ways to pay, and what was paid. Everything here is the signed-in user's own. +@RestClient +public interface PaymentApi { + @GET("/api/payments/config") + void config(OnComplete> callback); + + /// The saved cards, and cash, which is always there. + @GET("/api/payments/methods") + void methods(OnComplete>> callback); + + /// Begins adding a card; see [PaymentSetupDto] for the two ways it goes on. + @POST("/api/payments/methods/setup") + void startSetup(OnComplete> callback); + + /// Finishes adding a card. `card` is read by the simulated processor and + /// ignored when the setup was hosted. + @POST("/api/payments/methods/setup/{id}/complete") + void completeSetup(@Path("id") String id, @Body CardDto card, + OnComplete> callback); + + @POST("/api/payments/methods/{id}/default") + void makeDefault(@Path("id") String id, OnComplete> callback); + + @POST("/api/payments/methods/{id}/remove") + void remove(@Path("id") String id, OnComplete> callback); + + /// The caller's receipts as a customer, newest first. + @GET("/api/payments/receipts") + void receipts(OnComplete>> callback); + + @GET("/api/payments/receipts/{deliveryId}") + void receipt(@Path("deliveryId") String deliveryId, OnComplete> callback); + + /// Tips the courier of a completed delivery, once. + @POST("/api/deliveries/{id}/tip") + void tip(@Path("id") String id, @Body TipDto tip, OnComplete> callback); +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentConfigDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentConfigDto.java new file mode 100644 index 00000000000..48614c0e622 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentConfigDto.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// How this server takes payments, so the app knows which way to add a card. +/// +/// @param provider `simulated` or `stripe`. +@Mapped +public record PaymentConfigDto(String provider, String currency) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentMethodDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentMethodDto.java new file mode 100644 index 00000000000..3c91e1dae8b --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentMethodDto.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A way to pay. Cash is always one of them, under the id `cash`. +/// +/// @param kind `card` or `cash`. +/// @param label The method as a person would name it: "Visa 4242", "Cash". +@Mapped +public record PaymentMethodDto( + String id, + String kind, + String brand, + String last4, + int expMonth, + int expYear, + boolean isDefault, + String label) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentSetupDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentSetupDto.java new file mode 100644 index 00000000000..fabcdcd11fd --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PaymentSetupDto.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// An attempt to add a card, begun. +/// +/// When `hosted` is true the card is typed into the payment processor's own page +/// at `url`: open it in the browser, and call complete when the user returns. +/// Otherwise the app collects a [CardDto] itself and sends it with complete. +@Mapped +public record PaymentSetupDto(String id, boolean hosted, String url) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutAccountDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutAccountDto.java new file mode 100644 index 00000000000..76e4e5ddeb7 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutAccountDto.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Where a courier's payouts go. Only the last four digits of the account are +/// kept. +@Mapped +public record PayoutAccountDto(String holder, String bankName, String accountLast4) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutDto.java new file mode 100644 index 00000000000..b010d3304d0 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PayoutDto.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Money sent to a courier's bank. +/// +/// @param status `paid`. +/// @param createdAt Milliseconds since the epoch. +/// @param destination The account it went to, as its owner would recognise it. +@Mapped +public record PayoutDto(String id, long amount, String status, long createdAt, String destination) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneChallengeDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneChallengeDto.java new file mode 100644 index 00000000000..a57ecdb1ad8 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneChallengeDto.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The answer to asking for a verification code: how long the code that was just +/// sent stays good. +/// +/// @param demoCode The code itself, but only from a server that has no way to send a text message +/// and is running on a development profile. It lets the sample be tried end to end without an +/// SMS account; a server configured with a real sender leaves it null. +@Mapped +public record PhoneChallengeDto(int expiresInSeconds, String demoCode) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneCodeDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneCodeDto.java new file mode 100644 index 00000000000..5856d701769 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PhoneCodeDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The code the user typed in. +@Mapped +public record PhoneCodeDto(String code) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PlaceDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PlaceDto.java new file mode 100644 index 00000000000..bee12e9d489 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PlaceDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A place a search or a reverse lookup found. +@Mapped +public record PlaceDto(String name, String address, double lat, double lng) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PreferencesDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PreferencesDto.java new file mode 100644 index 00000000000..30f7ba7d26e --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PreferencesDto.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// How one person wants the app and their deliveries. Kept on the server, so it +/// follows the account to another phone. +/// +/// @param language `en`, `es`, `fr`, `de` or `he`. +/// @param theme `system`, `light` or `dark`. +/// @param units `km` or `mi`. +/// @param courierGender `any` or `women`: whom a delivery may be offered to. +/// @param defaultPaymentMethodId A saved method's id, or `cash`. +/// @param defaultTipPercent 0, 10, 15 or 20. +/// @param shareTripsWith The phone number trips are shared with, or empty. +@Mapped +public record PreferencesDto( + String language, + String theme, + String units, + boolean notifyDeliveryUpdates, + boolean notifyReceipts, + boolean notifyPromotions, + String courierGender, + boolean quietDelivery, + boolean accessibleVehicle, + boolean petFriendly, + String defaultPaymentMethodId, + int defaultTipPercent, + String shareTripsWith) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PricingDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PricingDto.java new file mode 100644 index 00000000000..34e0cb9f438 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PricingDto.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// What deliveries cost. Amounts are cents; the percentages are of the fare. +/// +/// @param serviceFeePercent Added to the fare and paid by the customer. +/// @param commissionPercent Kept from the fare; the rest is the courier's. +/// @param surgeMultiplier Multiplies every fare while demand is high; 1 otherwise. +@Mapped +public record PricingDto( + String currency, + long baseFare, + long perKm, + long perMinute, + long minimumFare, + double serviceFeePercent, + double commissionPercent, + double surgeMultiplier, + double comfortMultiplier, + double xlMultiplier) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ProfileDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ProfileDto.java new file mode 100644 index 00000000000..e7a30236fb4 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ProfileDto.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The parts of an account its owner edits. +/// +/// @param gender `unspecified`, `female`, `male` or `nonbinary`. +@Mapped +public record ProfileDto( + String name, + String gender, + String emergencyContactName, + String emergencyContactPhone, + String homeAddress, + String workAddress) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PushDeviceDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PushDeviceDto.java new file mode 100644 index 00000000000..315a684ebc3 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/PushDeviceDto.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A device that can be sent push notifications, as the app registers it. +/// +/// `provider` is the push service the device's token belongs to -- `fcm`, +/// `apns`, `huawei`, `wns` or `web` -- and `token` the token that service gave +/// the app. `platform` is the device's platform name, for the record only. +@Mapped +public record PushDeviceDto(String provider, String token, String platform) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RatingDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RatingDto.java new file mode 100644 index 00000000000..b0d8374cf00 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RatingDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A customer's rating of a completed delivery. +@Mapped +public record RatingDto(int stars) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReasonDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReasonDto.java new file mode 100644 index 00000000000..4ef0969d988 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReasonDto.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Why an admin did something: rejected an application, cancelled a delivery, +/// refunded one. +@Mapped +public record ReasonDto(String reason) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReceiptDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReceiptDto.java new file mode 100644 index 00000000000..691e4b34898 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/ReceiptDto.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// What a delivery cost and how it was paid. Every amount is in cents. +/// +/// @param baseFare The part of the fare that does not depend on the trip. +/// @param distanceFare The part of the fare charged by distance. +/// @param timeFare The part of the fare charged by time. +/// @param total The fare, the service fee and the tip, less the discount. +/// @param status `pending`, `paid`, `failed`, `refunded` or `cash`; while a refund the processor +/// took has not finished, `refund_pending`, and `refund_failed` when it then did not return the +/// money. +/// @param paidAt Milliseconds since the epoch, or 0 while unpaid. +/// @param customerUsername Who paid. Filled for an admin; a customer's own receipts leave it empty. +@Mapped +public record ReceiptDto( + String deliveryId, + String currency, + long baseFare, + long distanceFare, + long timeFare, + long serviceFee, + long tip, + long discount, + long total, + String methodLabel, + String status, + long paidAt, + String pickupName, + String dropoffName, + double distanceMeters, + double durationSeconds, + String courierName, + String vehicle, + String customerUsername) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RegisterDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RegisterDto.java new file mode 100644 index 00000000000..3f33973f20a --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RegisterDto.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// What opening an account takes. The password travels once, in the body of a +/// request over TLS, and the server keeps only its hash. +/// +/// @param courier The older spelling of [#wantsToDrive()], kept for apps built before it. It no +/// longer grants anything: it opens a onboarding application like the newer flag. +/// @param wantsToDrive Opens a onboarding application with the account. The account is a customer until +/// an admin approves the application. +/// @param gender `unspecified`, `female`, `male` or `nonbinary`; empty reads as unspecified. +@Mapped +public record RegisterDto( + String email, + String password, + String displayName, + String phone, + boolean courier, + String vehicle, + String plate, + boolean wantsToDrive, + String gender) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RoleChangeDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RoleChangeDto.java new file mode 100644 index 00000000000..924cc672f29 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/RoleChangeDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// The modes an admin grants a user beyond riding. +@Mapped +public record RoleChangeDto(boolean courier, boolean admin) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/StatsSeriesDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/StatsSeriesDto.java new file mode 100644 index 00000000000..cd9564197bd --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/StatsSeriesDto.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +import java.util.List; + +/// The admin's charts: how the service did, day by day. +/// +/// @param days Oldest first, every day present. +/// @param byHour Deliveries asked for in each hour of the day (UTC), 24 numbers. +/// @param topCouriers The couriers who completed the most deliveries in the period; `deliveries` is that count. +/// @param averageFare Cents. +/// @param averageWaitSeconds From a delivery being asked for to a courier taking it. +/// @param completionRate Completed deliveries over deliveries asked for, 0 to 1. +@Mapped +public record StatsSeriesDto( + List days, + List byHour, + List byState, + List byProduct, + List topCouriers, + long averageFare, + double averageWaitSeconds, + double completionRate, + int pendingApplications, + int flaggedUsers, + String currency) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/SuspendDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/SuspendDto.java new file mode 100644 index 00000000000..44b68f6e75b --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/SuspendDto.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Blocks an account, or lifts the block. +/// +/// @param reason Why, for the moderation record. May be empty. +@Mapped +public record SuspendDto(boolean suspended, String reason) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TicketDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TicketDto.java new file mode 100644 index 00000000000..0ec2857eedd --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TicketDto.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A single-use ticket for opening the live channel. +/// +/// A WebSocket handshake cannot carry the bearer token on every platform, so the +/// app asks for a ticket over the authenticated API and presents it in the +/// connection address. It is good for one connection and for a few seconds. +@Mapped +public record TicketDto(String ticket, int expiresInSeconds) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TipDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TipDto.java new file mode 100644 index 00000000000..26e7a7b8d64 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/TipDto.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// A tip for the courier of a completed delivery, in cents. +@Mapped +public record TipDto(long amount) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDetailDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDetailDto.java new file mode 100644 index 00000000000..6a046cdbc66 --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDetailDto.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +import java.util.List; + +/// One account in full, for the admin deciding what to do about it. +/// +/// @param deliveries Deliveries taken part in, as customer or courier. +/// @param cancellations How many of those deliveries were cancelled. +/// @param rating As a courier; 0 for an account that has not driven. +/// @param spent Cents paid as a customer. +/// @param earned Cents earned as a courier. +/// @param events Newest first. +@Mapped +public record UserDetailDto( + UserDto user, + int deliveries, + int cancellations, + double rating, + long spent, + long earned, + List events) { +} diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java new file mode 100644 index 00000000000..818e796500e --- /dev/null +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.api; + +import com.codename1.annotations.Mapped; + +/// Who is signed in, as the app needs to know it: enough to greet them, to +/// decide which modes to offer, and to tell whether they still owe a phone +/// verification. +/// +/// @param username The sign-in name, which is the e-mail address the account was opened with. +/// @param customer Every account can delivery. +/// @param courier Granted: the account may drive. +/// @param admin Granted: the account may run the service. +/// @param suspended Blocked by an admin: the account cannot sign in or call the API. +/// @param vehicle A courier's car, as customers see it. Empty for anyone else. +/// @param plate The car's licence plate. Empty for anyone else. +/// @param gender `unspecified`, `female`, `male` or `nonbinary`. +/// @param courierStatus Where the account stands on onboarding: `none`, `draft`, `pending`, `approved` +/// or `rejected`. The COURIER role follows `approved`. +/// @param flagged Marked by an admin for a closer look. The account still works; `suspended` is +/// what stops one. +/// @param flagReason Why it was flagged, for the other admins. +/// @param language The language the app is shown in: `en`, `es`, `fr`, `de` or `he`. +@Mapped +public record UserDto( + String username, + String displayName, + String phone, + boolean phoneVerified, + boolean customer, + boolean courier, + boolean admin, + boolean suspended, + String vehicle, + String plate, + String gender, + String courierStatus, + boolean flagged, + String flagReason, + String language) { +} diff --git a/scripts/morsel/web-e2e.mjs b/scripts/morsel/web-e2e.mjs new file mode 100644 index 00000000000..a558d892f88 --- /dev/null +++ b/scripts/morsel/web-e2e.mjs @@ -0,0 +1,326 @@ +// The Morsel web app, hosted by its own server, driven in a headless browser. +// +// URL=http://localhost:18081/ OUT=target/web-e2e node web-e2e.mjs +// +// It checks what a person opening the server's address gets: the app loads from +// the server, reaches the welcome screen, signs in as the demo customer, lands on +// the home screen with its map, and has its live channel open. Then it signs +// out, signs in as the demo admin and reaches the admin console. +// run-web-e2e.sh builds everything, starts the server and runs this; see there. +// +// The app draws on a canvas, so there are no buttons in the page to ask a +// browser automation tool for. What there is, is the accessibility tree the +// port keeps over the canvas -- an element per component, with its role, its +// label and where it is -- and that is what this reads to find a button and +// what it clicks at. Text fields become real elements while they are +// being edited, so typing is ordinary typing. +import fs from 'node:fs'; +import path from 'node:path'; +import { createRequire } from 'node:module'; + +// require() and not import: it also looks in NODE_PATH, which is how a +// container whose packages are installed somewhere else finds Playwright. +const require = createRequire(import.meta.url); +let chromium; +try { + ({ chromium } = require('playwright')); +} catch (err) { + console.error('[morsel-web] Playwright is not installed. Run `npm ci` in scripts/ and ' + + '`npx playwright install chromium`, or point NODE_PATH at a node_modules that has it.'); + console.error(String(err)); + process.exit(2); +} + +const url = process.env.URL; +const out = process.env.OUT || 'target/web-e2e'; +const email = process.env.MORSEL_USER || 'customer@morsel.example'; +const password = process.env.MORSEL_PASSWORD || 'morsel-demo'; +const admin = process.env.MORSEL_ADMIN || 'admin@morsel.example'; +// A cold runner translates nothing at this point, but it does parse several +// megabytes of JavaScript before the first screen. +const startSeconds = Number(process.env.MORSEL_WEB_START_SECONDS || '120'); +const stepSeconds = Number(process.env.MORSEL_WEB_STEP_SECONDS || '45'); +if (!url) { + console.error('[morsel-web] URL is required'); + process.exit(2); +} +fs.mkdirSync(out, { recursive: true }); +const logFile = path.join(out, 'browser.log'); +fs.writeFileSync(logFile, ''); +const note = line => fs.appendFileSync(logFile, line + '\n'); +const say = line => { console.log('[morsel-web] ' + line); note('# ' + line); }; + +const origin = new URL(url).origin; +const requests = []; // "200 POST /login", in order +const problems = []; // anything that must not happen +const sockets = []; // { url, frames: [...] } + +const browser = await chromium.launch({ + headless: true, + // A headless page counts as hidden, and a hidden page's timers are throttled + // to about one a minute once its budget is spent. The app's threads sleep on + // those timers. + args: ['--disable-background-timer-throttling', '--disable-renderer-backgrounding', + '--disable-backgrounding-occluded-windows', '--disable-features=IntensiveWakeUpThrottling'] +}); +const page = await browser.newPage({ viewport: { width: 420, height: 860 } }); +page.on('console', message => { + const text = message.text(); + if (text.startsWith('LF-SHIM')) { + return; + } + note(`[${message.type()}] ${text}`); + if (/internal application error|Missing JS member/i.test(text)) { + problems.push('the app reported an error: ' + text.split('\n')[0]); + } +}); +page.on('pageerror', err => { + note('[pageerror] ' + err); + problems.push('the page threw: ' + err); +}); +page.on('response', response => { + const at = new URL(response.url()); + if (at.origin !== origin) { + return; + } + const line = `${response.status()} ${response.request().method()} ${at.pathname}`; + requests.push(line); + note('NET ' + line); +}); +page.on('websocket', socket => { + const seen = { url: socket.url(), frames: [] }; + sockets.push(seen); + note('WS open ' + socket.url().replace(/ticket=.*/, 'ticket=...')); + socket.on('framereceived', frame => { + seen.frames.push(String(frame.payload)); + note('WS frame ' + String(frame.payload).slice(0, 200)); + }); +}); + +/// What the app has on screen, as the port describes it to assistive technology. +async function components() { + return page.evaluate(() => Array.from( + document.querySelectorAll('#cn1-accessibility-tree [data-cn1-accessibility-id]')).map(el => { + const box = el.getBoundingClientRect(); + return { + role: el.getAttribute('role'), + label: el.getAttribute('aria-label'), + text: el.innerText, + x: box.x, y: box.y, width: box.width, height: box.height + }; + })); +} + +async function screenText() { + return page.evaluate(() => document.body.innerText); +} + +async function waitFor(what, seconds, probe) { + const end = Date.now() + seconds * 1000; + for (;;) { + const hit = await probe(); + if (hit) { + return hit; + } + if (problems.length > 0) { + throw new Error(problems[0]); + } + if (Date.now() > end) { + throw new Error(`timed out after ${seconds}s waiting for ${what}`); + } + await page.waitForTimeout(250); + } +} + +const button = label => async () => + (await components()).find(c => c.role === 'button' && c.label === label); + +/// The single-line fields of the form on screen, top to bottom. +async function fields() { + return (await components()).filter(c => c.role === 'textbox' && c.height > 40 && c.height < 120) + .sort((a, b) => a.y - b.y); +} + +async function click(component) { + await page.mouse.click(component.x + component.width / 2, component.y + component.height / 2); +} + +/// Waits until nothing on screen has moved for a moment. A form slides in, and +/// a click aimed at where a field is half-way through lands on what was there. +async function settled() { + let before = JSON.stringify(await components()); + await waitFor('the screen to stop moving', stepSeconds, async () => { + await page.waitForTimeout(350); + const now = JSON.stringify(await components()); + const still = now === before; + before = now; + return still; + }); +} + +async function typeInto(index, text, type) { + const editing = () => page.evaluate( + wanted => document.activeElement && document.activeElement.tagName === 'INPUT' + && document.activeElement.type === wanted, type); + await waitFor(`the ${type} field to take the keyboard`, stepSeconds, async () => { + await settled(); + const all = await fields(); + if (all.length <= index) { + return false; + } + await click(all[index]); + await page.waitForTimeout(500); + return editing(); + }); + await page.keyboard.type(text, { delay: 10 }); +} + +/// Fills in the sign-in form the welcome screen opens, and sends it. +async function signInAs(who) { + await click(await waitFor('the welcome screen', startSeconds, button('Sign in'))); + await typeInto(0, who, 'email'); + await typeInto(1, password, 'password'); + // The form's own button now: the welcome screen's is gone with its form. + await settled(); + await click(await waitFor('the form\'s Sign in button', stepSeconds, async () => + (await fields()).length >= 2 ? button('Sign in')() : null)); + say('submitted the sign-in form as ' + who); +} + +/// Fails on the dialog the app shows for an error nothing handled. +async function noErrorDialog() { + if (/internal application error/i.test(await screenText())) { + problems.push('the app is showing its internal-error dialog'); + } + if (problems.length > 0) { + throw new Error(problems[0]); + } +} + +let failed = null; +try { + say('opening ' + url); + const first = await page.goto(url, { waitUntil: 'load' }); + if (!first || first.status() !== 200) { + throw new Error('the server answered ' + (first ? first.status() : 'nothing') + ' at /'); + } + const type = first.headers()['content-type'] || ''; + if (!type.startsWith('text/html')) { + throw new Error('/ is served as ' + type + ', not as a page'); + } + + // 1. The welcome screen: the app started, in the browser, from this server. + const signIn = await waitFor('the welcome screen', startSeconds, button('Sign in')); + await waitFor('the welcome screen to offer an account', stepSeconds, button('Create an account')); + await page.screenshot({ path: path.join(out, '1-welcome.png') }); + say('the welcome screen is up'); + for (const file of ['/translated_app.js', '/port.js', '/worker.js']) { + if (!requests.includes('200 GET ' + file)) { + throw new Error(file + ' was not served by this server: ' + requests.join(', ')); + } + } + + // 2. Signing in. The form is the app's own; the flow behind it is the + // authorization-code flow against the server the page came from. + await signInAs(email); + + // The first sign-in in a browser is asked whether usage statistics may be + // shared. The answer is kept with the page's storage, so the admin's + // sign-in further down is not asked again. + await click(await waitFor('the usage statistics question', stepSeconds, button('Not now'))); + say('declined usage statistics'); + + // 3. The home screen. + await waitFor('the home screen', stepSeconds, async () => + (await screenText()).includes('Where to?')); + await page.screenshot({ path: path.join(out, '3-home.png') }); + say('signed in: the home screen is up'); + + const expected = ['200 POST /login', '200 GET /signin/code', '200 POST /oauth2/token', + '200 GET /api/me']; + for (const line of expected) { + if (!requests.includes(line)) { + throw new Error('signing in did not make the request "' + line + '"; it made: ' + + requests.filter(r => !/\.(js|css|png|res|gif|json|ttf)$/.test(r)).join(', ')); + } + } + + // 4. The live channel, a WebSocket to the same origin. + const live = await waitFor('the live channel', stepSeconds, async () => + sockets.find(s => s.frames.some(f => f.includes('"ready"')))); + const expectedSocket = origin.replace(/^http/, 'ws') + '/ws/live?ticket='; + if (!live.url.startsWith(expectedSocket)) { + throw new Error('the live channel went to ' + live.url + ', not to ' + expectedSocket); + } + say('the live channel is open'); + + // Give a late failure -- the first location request, the first map tile -- + // the moment it needs to show itself. + await page.waitForTimeout(3000); + await noErrorDialog(); + await page.screenshot({ path: path.join(out, '4-settled.png') }); + + // 5. The session that carried the sign-in is over: the app ended it when it + // had its tokens, so the cookie the browser still holds is worth nothing. + await waitFor('the app to end its sign-in session', stepSeconds, async () => + requests.includes('204 POST /logout')); + const again = await page.evaluate(async () => (await fetch('/oauth2/authorize?response_type=code' + + '&client_id=morsel-app&scope=openid&state=x&code_challenge_method=S256' + + '&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM' + + '&redirect_uri=' + encodeURIComponent(location.origin + '/signin/code'), + { headers: { Accept: 'application/json' } })).status); + if (again === 200) { + throw new Error('the browser can still get an authorization code after signing in'); + } + say('the sign-in session is over (asking for another code is answered ' + again + ')'); + + // 6. Signing out, from the menu. Its button is an icon with no words: the + // one button at the top of the screen. + await settled(); + const menu = await waitFor('the menu button', stepSeconds, async () => + (await components()).filter(c => c.role === 'button' && c.y < 140 && c.width < 120 + && c.width > 20).sort((a, b) => a.x - b.x)[0]); + await click(menu); + await settled(); + await click(await waitFor('Sign out in the menu', stepSeconds, button('Sign out'))); + await waitFor('the welcome screen after signing out', stepSeconds, button('Create an account')); + await settled(); + await noErrorDialog(); + await page.screenshot({ path: path.join(out, '5-signed-out.png') }); + say('signed out: the welcome screen is back'); + + // 7. The admin, who lands in the console. + await signInAs(admin); + await waitFor('the admin console', stepSeconds, async () => { + // "Operations" on a screen this narrow; "Dashboard", down the side, on a + // wide one. The figures are the same. + const text = await screenText(); + // A caption goes onto two lines where its tile is narrow. + return /Operations|Dashboard/.test(text) && /Fares\s+collected/.test(text); + }); + await waitFor('the console to ask the server for its figures', stepSeconds, async () => + requests.some(r => /^200 GET \/api\/admin\//.test(r))); + await page.waitForTimeout(3000); + await settled(); + await noErrorDialog(); + await page.screenshot({ path: path.join(out, '6-admin.png') }); + say('signed in as ' + admin + ': the admin console is up'); +} catch (err) { + failed = err; + try { + await page.screenshot({ path: path.join(out, 'failure.png') }); + fs.writeFileSync(path.join(out, 'failure.txt'), await screenText()); + fs.writeFileSync(path.join(out, 'failure-components.json'), + JSON.stringify(await components(), null, 1)); + } catch (ignored) { + // The page is gone; the log is what there is. + } +} +await browser.close(); + +if (failed) { + console.error('[morsel-web] FAILED: ' + failed.message); + console.error('[morsel-web] the browser log is ' + logFile); + process.exit(1); +} +say('passed'); From 228289be463ef7eb056d6befc05ff16494a5b100 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 17 Oct 2026 05:20:12 +0300 Subject: [PATCH 02/26] Morsel: a merchant role and mode; one template-sync script for both samples The MERCHANT role sits beside the courier and admin switches but is not one of them: it comes with a place on a restaurant's staff. Nav opens a restaurant home for it, and /api/merchant/** is the role's alone. scripts/sync-initializr-wayline.py becomes sync-initializr-template.py, which takes the sample's name; Wayline's resources are byte-for-byte what the old script made. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wayline.yml | 6 +- .../codename1/initializr/model/Template.java | 4 +- scripts/morsel/README.adoc | 2 +- .../codenameone/examples/morsel/Roles.java | 9 ++- .../examples/morsel/SecurityConfig.java | 1 + .../examples/morsel/account/Accounts.java | 18 ++++- scripts/morsel/check-template.sh | 4 +- .../morsel/merchant/MerchantForm.java | 70 +++++++++++++++++++ .../codenameone/examples/morsel/ui/Menu.java | 13 ++-- .../codenameone/examples/morsel/ui/Nav.java | 20 ++++-- .../examples/morsel/api/UserDto.java | 4 +- ...wayline.py => sync-initializr-template.py} | 69 ++++++++++-------- scripts/wayline/README.adoc | 2 +- scripts/wayline/check-template.sh | 4 +- 14 files changed, 169 insertions(+), 57 deletions(-) create mode 100644 scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java rename scripts/{sync-initializr-wayline.py => sync-initializr-template.py} (77%) diff --git a/.github/workflows/wayline.yml b/.github/workflows/wayline.yml index 2eef7f25286..fcdc5463adb 100644 --- a/.github/workflows/wayline.yml +++ b/.github/workflows/wayline.yml @@ -47,7 +47,7 @@ on: - 'Ports/JavaScriptPort/src/main/**' - 'scripts/build-android-app.sh' - 'scripts/build-ios-app.sh' - - 'scripts/sync-initializr-wayline.py' + - 'scripts/sync-initializr-template.py' - 'scripts/tests/generate-initializr-fixtures.py' - 'scripts/tests/GenerateInitializr.java' # The generator, the template, and the generator's own tests, which this @@ -61,7 +61,7 @@ on: - 'vm/backend/src/**' - 'maven/backend/**' - 'maven/backend-test/**' - - 'scripts/sync-initializr-wayline.py' + - 'scripts/sync-initializr-template.py' - 'scripts/initializr/common/src/**' - '.github/workflows/wayline.yml' workflow_dispatch: @@ -88,7 +88,7 @@ jobs: # A change to one without the other is a template that is not the project # this workflow tests. - name: Compare the template with scripts/wayline - run: python3 scripts/sync-initializr-wayline.py + run: python3 scripts/sync-initializr-template.py wayline backend: name: Server tests (JVM and compiled) diff --git a/scripts/initializr/common/src/main/java/com/codename1/initializr/model/Template.java b/scripts/initializr/common/src/main/java/com/codename1/initializr/model/Template.java index 2f821007a9d..1a37ef81720 100644 --- a/scripts/initializr/common/src/main/java/com/codename1/initializr/model/Template.java +++ b/scripts/initializr/common/src/main/java/com/codename1/initializr/model/Template.java @@ -36,8 +36,8 @@ public enum Template { // A whole application rather than a starting point: a ride-hailing app with a // rider, a driver and an admin mode, and the server it talks to. It is the // project in scripts/wayline, which CI builds and tests; the resources named - // here are derived from it by scripts/sync-initializr-wayline.py and never - // edited by hand. + // here are derived from it by `scripts/sync-initializr-template.py wayline` + // and never edited by hand. // // Its preview is a resource of its own, read when the template is chosen, // and not an image of the theme: the theme is loaded at start-up, so a diff --git a/scripts/morsel/README.adoc b/scripts/morsel/README.adoc index 8ca66855f96..815981f120e 100644 --- a/scripts/morsel/README.adoc +++ b/scripts/morsel/README.adoc @@ -207,7 +207,7 @@ Android and iOS through `scripts/build-android-app.sh` and == The Initializr template -`scripts/sync-initializr-morsel.py --write` copies this project into the +`scripts/sync-initializr-template.py morsel --write` copies this project into the Initializr's resources; without `--write` it fails when they differ, which is what CI runs. Change the project here, never the copies. diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java index 36ea24c95df..0f4cf0a4d05 100644 --- a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/Roles.java @@ -18,13 +18,16 @@ */ package com.codenameone.examples.morsel; -/// The roles an account can hold. Every account is a customer; onboarding and -/// administering are granted on top of that, and the app offers a mode for each -/// role the account holds. +/// The roles an account can hold. Every account is a customer; delivering, +/// running a restaurant and administering are granted on top of that, and the +/// app offers a mode for each role the account holds. public final class Roles { public static final String CUSTOMER = "CUSTOMER"; public static final String COURIER = "COURIER"; public static final String ADMIN = "ADMIN"; + /// Works for a restaurant on the service: takes its orders and keeps its + /// menu. Which restaurant is the restaurant's staff list, not the role. + public static final String MERCHANT = "MERCHANT"; private Roles() { } diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java index 2fc76ed7999..ab5d4a48f8c 100644 --- a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/SecurityConfig.java @@ -389,6 +389,7 @@ public SecurityFilterChain api(HttpSecurity http, JwkSource signingKeys, "/api/account/register")).permitAll() .requestMatchers("/api/courier/**").hasRole(Roles.COURIER) .requestMatchers("/api/admin/**").hasRole(Roles.ADMIN) + .requestMatchers("/api/merchant/**").hasRole(Roles.MERCHANT) .anyRequest().authenticated()) // end::morsel-api-rules[] .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(decoder) diff --git a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java index 2adc7e51c98..9e3e2c02952 100644 --- a/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java +++ b/scripts/morsel/backend/src/main/java/com/codenameone/examples/morsel/account/Accounts.java @@ -266,7 +266,11 @@ public UserDto setRoles(String by, String username, boolean courier, boolean adm // ledger, and settling it is the admin's to arrange -- the role given // back for the cash-out, or paid outside the app. A removal for cause // must not wait on a payout, so it is not refused here. - users.updateUser(User.withUserDetails(signIn).roles(roles(courier, admin)).build()); + // Merchant is not one of the switches: it comes with a place on a + // restaurant's staff, and stays as long as that does. + boolean merchant = authorities(signIn).contains("ROLE_" + Roles.MERCHANT); + users.updateUser(User.withUserDetails(signIn).roles(roles(courier, admin, merchant)) + .build()); // The role and the approval go together, whichever way it was given: // being approved is what lets a courier go on line. if (courier) { @@ -424,7 +428,7 @@ public UserDto close(String username, String password) throws IOException { live.drop(username); // The account as it was, answered as what it now is: unable to sign in. return new UserDto(was.username(), was.displayName(), was.phone(), was.phoneVerified(), - was.customer(), was.courier(), was.admin(), true, was.vehicle(), was.plate(), + was.customer(), was.courier(), was.admin(), was.merchant(), true, was.vehicle(), was.plate(), was.gender(), was.courierStatus(), was.flagged(), was.flagReason(), was.language()); } @@ -553,8 +557,15 @@ private UserDetails load(String username) { } private static String[] roles(boolean courier, boolean admin) { + return roles(courier, admin, false); + } + + private static String[] roles(boolean courier, boolean admin, boolean merchant) { List roles = new ArrayList(); roles.add(Roles.CUSTOMER); + if (merchant) { + roles.add(Roles.MERCHANT); + } if (courier) { roles.add(Roles.COURIER); } @@ -587,8 +598,9 @@ private static UserDto toDto(Profile profile, List authorities, boolean boolean customer = authorities.contains("ROLE_" + Roles.CUSTOMER); boolean courier = authorities.contains("ROLE_" + Roles.COURIER); boolean admin = authorities.contains("ROLE_" + Roles.ADMIN); + boolean merchant = authorities.contains("ROLE_" + Roles.MERCHANT); return new UserDto(profile.getUsername(), profile.getDisplayName(), profile.getPhone(), - profile.isPhoneVerified(), customer, courier, admin, !enabled, profile.getVehicle(), + profile.isPhoneVerified(), customer, courier, admin, merchant, !enabled, profile.getVehicle(), profile.getPlate(), profile.getGender(), courierStatus, profile.isFlagged(), profile.getFlagReason(), profile.getLanguage()); } diff --git a/scripts/morsel/check-template.sh b/scripts/morsel/check-template.sh index 52dfcfcb08f..9e248491f7e 100755 --- a/scripts/morsel/check-template.sh +++ b/scripts/morsel/check-template.sh @@ -5,7 +5,7 @@ # check-template.sh [work-directory] # # The Initializr offers this application as a template, from resources that -# scripts/sync-initializr-morsel.py derives from it. That the resources match +# scripts/sync-initializr-template.py derives from it. That the resources match # is one question; this is the other: whether a project generated from them, # under another name and in another package, still builds. The real generator # makes the project, and it is then built against this checkout: @@ -26,7 +26,7 @@ WORK="${1:-$HERE/target/template-check}" log() { echo "[morsel-template] $*" >&2; } -python3 "$ROOT/scripts/sync-initializr-morsel.py" +python3 "$ROOT/scripts/sync-initializr-template.py" morsel # The version this checkout installs, which the generated poms are pointed at: # the release the Initializr generates against does not have this project's diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java new file mode 100644 index 00000000000..575881ac21b --- /dev/null +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.morsel.merchant; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codenameone.examples.morsel.ui.Menu; +import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.morsel.ui.Ui; + +/// The restaurant's side of the app: the orders coming in, and the menu they +/// are ordered from. +/// +/// Which restaurant is the server's to say. An account is a merchant because it +/// is on a restaurant's staff, and every call this screen makes is answered for +/// that restaurant alone. +public class MerchantForm implements Nav.Home { + private final Form form = Ui.home("Restaurant"); + private final Menu menu = new Menu(form, Nav.MERCHANT); + private final Container page = Ui.page(); + + public MerchantForm() { + menu.item(FontImage.MATERIAL_RECEIPT_LONG, "Orders", "home", () -> { }); + Container bar = new Container(new BorderLayout()); + bar.setUIID("WlHomeBar"); + bar.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(menu.flatButton())); + bar.add(BorderLayout.CENTER, FlowLayout.encloseLeftMiddle( + Ui.label("Your restaurant", "WlHeading"))); + page.setSafeArea(false); + Container screen = new Container(new BorderLayout()); + screen.setUIID("WlForm"); + screen.setSafeArea(true); + screen.add(BorderLayout.NORTH, bar); + screen.add(BorderLayout.CENTER, page); + form.add(screen); + menu.install(); + } + + @Override + public Form form() { + return form; + } + + @Override + public void start() { + page.removeAll(); + page.add(Ui.empty(FontImage.MATERIAL_STOREFRONT, "No orders yet", + "New orders appear here as customers place them.")); + form.revalidate(); + } +} diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java index 94e3f26c427..33d871112e0 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java @@ -138,6 +138,7 @@ private void build() { top.add(who); // Which of the three apps this is, said in words at the top of each. top.add(FlowLayout.encloseIn(Ui.badge(Nav.ADMIN.equals(mode) ? "Admin mode" + : Nav.MERCHANT.equals(mode) ? "Restaurant mode" : Nav.COURIER.equals(mode) ? "Courier mode" : "Customer mode", "WlModeBadge"))); Container list = new Container(BoxLayout.y()); @@ -145,14 +146,18 @@ private void build() { list.setScrollVisible(false); list.add(places); // The other modes, for an account whose roles open more than one. - // Everyone may delivery; onboarding and administering are granted. - if (user.courier() || user.admin() || !Nav.CUSTOMER.equals(mode)) { + // Everyone may order; delivering, running a restaurant and + // administering are granted. + if (user.courier() || user.admin() || user.merchant() || !Nav.CUSTOMER.equals(mode)) { list.add(Ui.label("Switch to", "WlMenuLabel")); if (!Nav.CUSTOMER.equals(mode)) { - list.add(other(FontImage.MATERIAL_HAIL, "Riding", Nav.CUSTOMER)); + list.add(other(FontImage.MATERIAL_RESTAURANT_MENU, "Ordering", Nav.CUSTOMER)); } if (user.courier() && !Nav.COURIER.equals(mode)) { - list.add(other(FontImage.MATERIAL_DIRECTIONS_CAR, "Onboarding", Nav.COURIER)); + list.add(other(FontImage.MATERIAL_DELIVERY_DINING, "Delivering", Nav.COURIER)); + } + if (user.merchant() && !Nav.MERCHANT.equals(mode)) { + list.add(other(FontImage.MATERIAL_STOREFRONT, "Your restaurant", Nav.MERCHANT)); } if (user.admin() && !Nav.ADMIN.equals(mode)) { list.add(other(FontImage.MATERIAL_INSIGHTS, "Administration", Nav.ADMIN)); diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java index 62336b158af..2cb7dfbb10d 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java +++ b/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java @@ -31,6 +31,7 @@ import com.codenameone.examples.morsel.courier.CourierForm; import com.codenameone.examples.morsel.courier.Shift; import com.codenameone.examples.morsel.live.LiveChannel; +import com.codenameone.examples.morsel.merchant.MerchantForm; import com.codenameone.examples.morsel.net.Account; import com.codenameone.examples.morsel.net.Session; import com.codenameone.examples.morsel.notify.Notifications; @@ -38,17 +39,18 @@ /// Decides which screen the app is on. /// -/// The app is three apps in one -- for riding, for onboarding and for -/// administering -- and which of them a user may enter is the server's +/// The app is four apps in one -- for ordering, for delivering, for running a +/// restaurant and for administering -- and which of them a user may enter is the server's /// decision: it is the roles on their account, as `/api/me` reports them. Each /// has a home screen of its own, with its own menu, and the user is in one of /// them at a time. Hiding a mode here is a courtesy and not the protection; the -/// server refuses a courier's or an admin's call from anyone without the role, +/// server refuses a courier's, a merchant's or an admin's call from anyone without the role, /// whatever the app shows. public final class Nav { public static final String CUSTOMER = "customer"; public static final String COURIER = "courier"; public static final String ADMIN = "admin"; + public static final String MERCHANT = "merchant"; /// The home screen of a mode. public interface Home { @@ -127,7 +129,7 @@ public static Form build() { LiveChannel.start(); String mode = mode(user); Telemetry.role(mode); - // Onboarding is for the approved. Anyone else who chooses it gets the + // Delivering is for the approved. Anyone else who chooses it gets the // way there: the application, and what has become of it. if (!COURIER.equals(mode)) { // Out of the courier's side of the app is off line as far as the @@ -135,6 +137,7 @@ public static Form build() { Shift.leave(); } Home home = ADMIN.equals(mode) ? new AdminForm() + : MERCHANT.equals(mode) ? new MerchantForm() : !COURIER.equals(mode) ? new CustomerForm() : user.courier() ? new CourierForm() : new ApplyForm(); home.start(); @@ -147,7 +150,8 @@ public static Form build() { /// /// The one they chose last, while their account still allows it. With no /// choice made, the one their account is for: an admin administers, a - /// courier drives, and everyone else deliveries. A choice is remembered for the + /// merchant runs their restaurant, a courier delivers, and everyone else + /// orders. A choice is remembered for the /// account that made it, so the next person to sign in on the same device /// starts where their own account does. public static String mode(UserDto user) { @@ -155,6 +159,9 @@ public static String mode(UserDto user) { if (ADMIN.equals(saved) && user.admin()) { return ADMIN; } + if (MERCHANT.equals(saved) && user.merchant()) { + return MERCHANT; + } // Not only for a courier: someone who is applying to be one is in the // courier's side of the app too, on the screen that says how it stands. if (COURIER.equals(saved)) { @@ -163,7 +170,8 @@ public static String mode(UserDto user) { if (CUSTOMER.equals(saved)) { return CUSTOMER; } - return user.admin() ? ADMIN : user.courier() ? COURIER : CUSTOMER; + return user.admin() ? ADMIN : user.merchant() ? MERCHANT + : user.courier() ? COURIER : CUSTOMER; } /// The user chose `mode` from the menu. diff --git a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java index 818e796500e..4a4b859ca5c 100644 --- a/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java +++ b/scripts/morsel/shared/src/main/java/com/codenameone/examples/morsel/api/UserDto.java @@ -26,8 +26,9 @@ /// /// @param username The sign-in name, which is the e-mail address the account was opened with. /// @param customer Every account can delivery. -/// @param courier Granted: the account may drive. +/// @param courier Granted: the account may deliver. /// @param admin Granted: the account may run the service. +/// @param merchant On a restaurant's staff: the account takes its orders and keeps its menu. /// @param suspended Blocked by an admin: the account cannot sign in or call the API. /// @param vehicle A courier's car, as customers see it. Empty for anyone else. /// @param plate The car's licence plate. Empty for anyone else. @@ -47,6 +48,7 @@ public record UserDto( boolean customer, boolean courier, boolean admin, + boolean merchant, boolean suspended, String vehicle, String plate, diff --git a/scripts/sync-initializr-wayline.py b/scripts/sync-initializr-template.py similarity index 77% rename from scripts/sync-initializr-wayline.py rename to scripts/sync-initializr-template.py index 2b0b464f43b..92a42cbaa04 100644 --- a/scripts/sync-initializr-wayline.py +++ b/scripts/sync-initializr-template.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 -"""Keep Initializr's Wayline template aligned with scripts/wayline (--write to update). +"""Keep an Initializr full-stack template aligned with its sample (--write to update). -scripts/wayline is the project CI builds and tests. The Initializr offers the same + python3 scripts/sync-initializr-template.py wayline|morsel [--write] + +scripts/ is the project CI builds and tests. The Initializr offers the same project as a template, from five resources this script derives from it: - wayline-src.zip the application's sources (common/src/main, less the css) - wayline-css.zip its stylesheet - wayline-pom.xml the common module's pom: the one every Initializr project + -src.zip the application's sources (common/src/main, less the css) + -css.zip its stylesheet + -pom.xml the common module's pom: the one every Initializr project gets (barebones-pom.xml), plus the dependency on `shared` - wayline-modules.zip the two modules a plain project does not have: `shared`, + -modules.zip the two modules a plain project does not have: `shared`, the contract, and `backend`, the server that implements it - wayline-settings.properties + -settings.properties the build hints the application needs and a plain project does not have, from common/codenameone_settings.properties @@ -24,7 +26,7 @@ out. The developer guide includes regions of this project through them, so they belong to the guide and not to a generated project. - * `wayline` in lower case -- an artifact id, a configuration key -- is written + * `` in lower case -- an artifact id, a configuration key -- is written `myappname`, which is the token the generator replaces with the project's name. The Java package is left alone: the generator replaces it as a package. * The licence header of this repository is taken off: a generated project is the @@ -35,7 +37,7 @@ server's tests go along -- they are how the developer finds out what they broke. Without --write the script only compares, and fails on a difference: a change to -scripts/wayline that the template did not get. +scripts/ that the template did not get. """ from pathlib import Path import io @@ -44,7 +46,14 @@ import zipfile root = Path(__file__).resolve().parents[1] -sample = root / 'scripts/wayline' +# The samples the Initializr offers as full-stack templates. Each is a project +# under scripts/ whose Java package is com.codenameone.examples.. +SAMPLES = ('wayline', 'morsel') +args = [a for a in sys.argv[1:] if not a.startswith('--')] +if len(args) != 1 or args[0] not in SAMPLES: + raise SystemExit('usage: sync-initializr-template.py ' + '|'.join(SAMPLES) + ' [--write]') +name = args[0] +sample = root / 'scripts' / name resources = root / 'scripts/initializr/common/src/main/resources' # What the generator rewrites as text; the rest is copied as bytes. @@ -76,9 +85,9 @@ def text(path): # The application's name wherever it is written in lower case -- an artifact # id, a configuration key, a demonstration account's address -- but not in the # Java package, which the generator replaces as a package. - data = re.sub(r'(?wayline-shared' not in (sample / 'common/pom.xml').read_text('utf-8'): - raise SystemExit('scripts/wayline/common/pom.xml no longer depends on wayline-shared; ' + if '' + name + '-shared' not in (sample / 'common/pom.xml').read_text('utf-8'): + raise SystemExit('scripts/' + name + '/common/pom.xml no longer depends on ' + name + '-shared; ' 'the template pom is built on the assumption that it does.') pom = (resources / 'barebones-pom.xml').read_bytes().replace(b'\r\n', b'\n').decode('utf-8') marker = ' \n' @@ -200,23 +209,25 @@ def leftovers(entries): def main(): wanted = expected() - poms = dict(wanted['wayline-modules.zip']) - poms['wayline-pom.xml'] = wanted['wayline-pom.xml'] + poms = dict(wanted[name + '-modules.zip']) + poms[name + '-pom.xml'] = wanted[name + '-pom.xml'] bad = leftovers(poms) if bad: raise SystemExit('A Codename One snapshot version is pinned in ' + ', '.join(bad) + '; module poms take ${cn1.version} from the reactor.') - stale = [name for name, entries in wanted.items() if current(resources / name) != entries] + stale = [resource for resource, entries in wanted.items() + if current(resources / resource) != entries] if stale and '--write' in sys.argv: - for name in stale: - entries = wanted[name] - (resources / name).write_bytes(entries if isinstance(entries, bytes) else archive(entries)) - print('Initializr Wayline template updated: ' + ', '.join(stale)) + for resource in stale: + entries = wanted[resource] + (resources / resource).write_bytes(entries if isinstance(entries, bytes) + else archive(entries)) + print('Initializr ' + name + ' template updated: ' + ', '.join(stale)) return if stale: - raise SystemExit('Initializr Wayline template drift: ' + ', '.join(stale) - + '. Run python scripts/sync-initializr-wayline.py --write') - print('Initializr Wayline template parity OK') + raise SystemExit('Initializr ' + name + ' template drift: ' + ', '.join(stale) + + '. Run python3 scripts/sync-initializr-template.py ' + name + ' --write') + print('Initializr ' + name + ' template parity OK') if __name__ == '__main__': diff --git a/scripts/wayline/README.adoc b/scripts/wayline/README.adoc index 733744c5399..5f1e2784e2b 100644 --- a/scripts/wayline/README.adoc +++ b/scripts/wayline/README.adoc @@ -207,7 +207,7 @@ Android and iOS through `scripts/build-android-app.sh` and == The Initializr template -`scripts/sync-initializr-wayline.py --write` copies this project into the +`scripts/sync-initializr-template.py wayline --write` copies this project into the Initializr's resources; without `--write` it fails when they differ, which is what CI runs. Change the project here, never the copies. diff --git a/scripts/wayline/check-template.sh b/scripts/wayline/check-template.sh index 03fa00ee1c4..fa507600258 100755 --- a/scripts/wayline/check-template.sh +++ b/scripts/wayline/check-template.sh @@ -5,7 +5,7 @@ # check-template.sh [work-directory] # # The Initializr offers this application as a template, from resources that -# scripts/sync-initializr-wayline.py derives from it. That the resources match +# scripts/sync-initializr-template.py derives from it. That the resources match # is one question; this is the other: whether a project generated from them, # under another name and in another package, still builds. The real generator # makes the project, and it is then built against this checkout: @@ -26,7 +26,7 @@ WORK="${1:-$HERE/target/template-check}" log() { echo "[wayline-template] $*" >&2; } -python3 "$ROOT/scripts/sync-initializr-wayline.py" +python3 "$ROOT/scripts/sync-initializr-template.py" wayline # The version this checkout installs, which the generated poms are pointed at: # the release the Initializr generates against does not have this project's From 8ca0233bdb138789642c60638476b47b176ee9cf Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:28:15 +0300 Subject: [PATCH 03/26] Savor: the food-delivery sample's name Co-Authored-By: Claude Opus 5.5 --- scripts/{morsel => savor}/LICENSE | 2 +- scripts/{morsel => savor}/README.adoc | 40 +++--- .../backend/application-dev.properties | 10 +- .../backend/application-test.properties | 8 +- .../backend/application.properties | 46 +++--- scripts/{morsel => savor}/backend/pom.xml | 12 +- scripts/{morsel => savor}/backend/server.sh | 10 +- .../codenameone/examples/savor}/Caller.java | 2 +- .../com/codenameone/examples/savor}/Days.java | 2 +- .../com/codenameone/examples/savor}/Ids.java | 2 +- .../codenameone/examples/savor}/Roles.java | 2 +- .../com/codenameone/examples/savor}/Rows.java | 4 +- .../examples/savor}/SecurityConfig.java | 18 +-- .../codenameone/examples/savor}/Services.java | 136 +++++++++--------- .../examples/savor}/SignInCodePage.java | 2 +- .../com/codenameone/examples/savor}/Text.java | 2 +- .../savor}/account/AccountCleanup.java | 22 +-- .../savor}/account/AccountEndpoint.java | 30 ++-- .../examples/savor}/account/Accounts.java | 30 ++-- .../examples/savor}/account/DemoAccounts.java | 60 ++++---- .../examples/savor}/account/Grants.java | 2 +- .../examples/savor}/account/Moderation.java | 10 +- .../savor}/account/PhoneVerification.java | 16 +-- .../examples/savor}/account/Preferences.java | 16 +-- .../examples/savor}/admin/AdminEndpoint.java | 50 +++---- .../examples/savor}/admin/Statistics.java | 46 +++--- .../savor}/courier/CourierEndpoint.java | 22 +-- .../examples/savor}/courier/Couriers.java | 50 +++---- .../examples/savor}/courier/Earnings.java | 50 +++---- .../savor}/delivery/CustomerEndpoint.java | 26 ++-- .../examples/savor}/delivery/Deliveries.java | 76 +++++----- .../savor}/delivery/DeliveryStateMachine.java | 4 +- .../savor}/delivery/DemoDeliveries.java | 46 +++--- .../examples/savor}/delivery/Fares.java | 30 ++-- .../examples/savor}/delivery/Matcher.java | 26 ++-- .../examples/savor}/domain/ClosedAccount.java | 2 +- .../savor}/domain/CourierApplication.java | 2 +- .../savor}/domain/CourierDocument.java | 2 +- .../examples/savor}/domain/CourierHours.java | 2 +- .../examples/savor}/domain/CourierState.java | 2 +- .../examples/savor}/domain/Delivery.java | 12 +- .../savor}/domain/DeliveryActivity.java | 2 +- .../examples/savor}/domain/DeliveryEvent.java | 4 +- .../savor}/domain/DeliveryPassed.java | 2 +- .../examples/savor}/domain/LiveTicket.java | 2 +- .../savor}/domain/ModerationEvent.java | 2 +- .../examples/savor}/domain/Payment.java | 2 +- .../savor}/domain/PaymentCustomer.java | 2 +- .../examples/savor}/domain/PaymentMethod.java | 2 +- .../examples/savor}/domain/PaymentSetup.java | 2 +- .../examples/savor}/domain/Payout.java | 2 +- .../examples/savor}/domain/PayoutAccount.java | 2 +- .../examples/savor}/domain/PhoneCode.java | 2 +- .../examples/savor}/domain/Preference.java | 2 +- .../examples/savor}/domain/Pricing.java | 2 +- .../examples/savor}/domain/Profile.java | 2 +- .../examples/savor}/domain/PushDevice.java | 2 +- .../codenameone/examples/savor}/geo/Geo.java | 2 +- .../examples/savor}/geo/GeoEndpoint.java | 6 +- .../examples/savor}/geo/Geocoder.java | 4 +- .../examples/savor}/geo/PhotonGeocoder.java | 6 +- .../examples/savor}/geo/StubGeocoder.java | 4 +- .../examples/savor}/live/LiveEndpoint.java | 6 +- .../examples/savor}/live/LiveHub.java | 4 +- .../examples/savor}/live/LiveTickets.java | 10 +- .../savor}/onboarding/Applications.java | 32 ++--- .../savor}/onboarding/OnboardingEndpoint.java | 12 +- .../savor}/pay/MisconfiguredPayments.java | 4 +- .../examples/savor}/pay/PayReturnPage.java | 8 +- .../savor}/pay/PaymentDeclinedException.java | 2 +- .../examples/savor}/pay/PaymentEndpoint.java | 22 +-- .../examples/savor}/pay/PaymentProvider.java | 8 +- .../examples/savor}/pay/Payments.java | 50 +++---- .../savor}/pay/SimulatedPayments.java | 8 +- .../examples/savor}/pay/StripePayments.java | 20 +-- .../examples/savor}/push/CloudPushSender.java | 6 +- .../savor}/push/ConsolePushSender.java | 4 +- .../examples/savor}/push/DeliveryPushes.java | 66 ++++----- .../savor}/push/MisconfiguredPushSender.java | 2 +- .../examples/savor}/push/PushDevices.java | 18 +-- .../examples/savor}/push/PushSender.java | 10 +- .../savor}/push/RecordingPushSender.java | 2 +- .../repository/ClosedAccountRepository.java | 4 +- .../CourierApplicationRepository.java | 4 +- .../repository/CourierDocumentRepository.java | 4 +- .../repository/CourierHoursRepository.java | 4 +- .../repository/CourierStateRepository.java | 6 +- .../DeliveryActivityRepository.java | 4 +- .../repository/DeliveryEventRepository.java | 6 +- .../repository/DeliveryPassedRepository.java | 4 +- .../savor}/repository/DeliveryRepository.java | 14 +- .../repository/LiveTicketRepository.java | 4 +- .../repository/ModerationEventRepository.java | 4 +- .../repository/PaymentCustomerRepository.java | 4 +- .../repository/PaymentMethodRepository.java | 4 +- .../savor}/repository/PaymentRepository.java | 4 +- .../repository/PaymentSetupRepository.java | 4 +- .../repository/PayoutAccountRepository.java | 4 +- .../savor}/repository/PayoutRepository.java | 4 +- .../repository/PhoneCodeRepository.java | 4 +- .../repository/PreferenceRepository.java | 4 +- .../savor}/repository/PricingRepository.java | 4 +- .../savor}/repository/ProfileRepository.java | 4 +- .../repository/PushDeviceRepository.java | 4 +- .../examples/savor}/sms/ConsoleSmsSender.java | 4 +- .../savor}/sms/MessageBirdSmsSender.java | 6 +- .../savor}/sms/MisconfiguredSmsSender.java | 2 +- .../examples/savor}/sms/SmsForm.java | 2 +- .../examples/savor}/sms/SmsSender.java | 6 +- .../examples/savor}/sms/TwilioSmsSender.java | 6 +- .../examples/savor}/sms/VonageSmsSender.java | 10 +- .../resources/db/migration/V1__accounts.sql | 0 .../resources/db/migration/V2__couriers.sql | 0 .../resources/db/migration/V4__deliveries.sql | 0 .../resources/db/migration/V5__payments.sql | 0 .../db/migration/V6__pricing_row.sql | 0 .../main/resources/db/migration/V7__push.sql | 0 .../migration/mysql/V3__courier_documents.sql | 0 .../postgresql/V3__courier_documents.sql | 0 .../sqlite/V3__courier_documents.sql | 0 .../examples/savor}/AccessMatrixTest.java | 20 +-- .../examples/savor}/AccessTest.java | 8 +- .../examples/savor}/AccountTest.java | 4 +- .../codenameone/examples/savor}/DaysTest.java | 2 +- .../examples/savor}/DeliveryFlowTest.java | 8 +- .../examples/savor}/DeliveryRaceTest.java | 10 +- .../savor}/DeliveryStateMachineTest.java | 6 +- .../examples/savor}/LiveSocketTest.java | 12 +- .../examples/savor}/MatchingTest.java | 4 +- .../examples/savor}/ModerationTest.java | 6 +- .../savor}/OnboardingApplicationTest.java | 6 +- .../examples/savor}/PaymentsTest.java | 10 +- .../codenameone/examples/savor}/People.java | 8 +- .../examples/savor}/PricingAndStatsTest.java | 10 +- .../examples/savor}/ProcessorFaultsTest.java | 6 +- .../codenameone/examples/savor}/PushTest.java | 16 +-- .../examples/savor}/ScriptedPayments.java | 4 +- .../examples/savor}/SettingsTest.java | 6 +- .../codenameone/examples/savor}/SignIn.java | 4 +- .../examples/savor}/StripePaymentsTest.java | 30 ++-- .../codenameone/examples/savor}/Trips.java | 2 +- .../examples/savor}/WebSignInTest.java | 28 ++-- .../savor}/delivery/OfferRecheckTest.java | 18 +-- scripts/{morsel => savor}/check-template.sh | 6 +- .../common/codenameone_settings.properties | 10 +- scripts/{morsel => savor}/common/icon.png | Bin scripts/{morsel => savor}/common/pom.xml | 10 +- .../src/desktop/resources/NativeTheme.res | Bin .../common/src/main/css/theme.css | 0 .../examples/savor}/AppConfig.java | 20 +-- .../codenameone/examples/savor}/Prefs.java | 8 +- .../codenameone/examples/savor/Savor.java} | 22 +-- .../examples/savor}/Telemetry.java | 6 +- .../savor}/admin/AdminDeliveriesForm.java | 18 +-- .../examples/savor}/admin/AdminForm.java | 10 +- .../savor}/admin/ApplicationsForm.java | 20 +-- .../examples/savor}/admin/Console.java | 16 +-- .../examples/savor}/admin/Dashboard.java | 30 ++-- .../examples/savor}/admin/FleetForm.java | 22 +-- .../examples/savor}/admin/Panes.java | 8 +- .../examples/savor}/admin/PaymentsForm.java | 18 +-- .../examples/savor}/admin/PricingForm.java | 14 +- .../examples/savor}/admin/UsersForm.java | 26 ++-- .../savor}/courier/ApplicationForm.java | 32 ++--- .../examples/savor}/courier/ApplyForm.java | 24 ++-- .../examples/savor}/courier/CourierForm.java | 48 +++---- .../examples/savor}/courier/EarningsForm.java | 24 ++-- .../examples/savor}/courier/Shift.java | 20 +-- .../savor}/customer/CustomerForm.java | 70 ++++----- .../savor}/customer/DeliveryCard.java | 22 +-- .../savor}/customer/PlaceSearchForm.java | 12 +- .../examples/savor}/live/LiveChannel.java | 8 +- .../examples/savor}/map/CarMarker.java | 2 +- .../examples/savor}/map/Locator.java | 10 +- .../examples/savor}/map/MapStage.java | 6 +- .../codenameone/examples/savor}/map/Maps.java | 4 +- .../examples/savor}/map/Routes.java | 2 +- .../savor}/merchant/MerchantForm.java | 8 +- .../examples/savor}/net/Account.java | 16 +-- .../codenameone/examples/savor}/net/Api.java | 16 +-- .../codenameone/examples/savor}/net/Net.java | 4 +- .../examples/savor}/net/Session.java | 10 +- .../examples/savor}/notify/Notifications.java | 24 ++-- .../examples/savor}/pay/CardForm.java | 18 +-- .../examples/savor}/pay/Cards.java | 2 +- .../examples/savor}/pay/ReceiptForm.java | 18 +-- .../examples/savor}/pay/WalletForm.java | 16 +-- .../examples/savor}/ui/BottomSheet.java | 2 +- .../codenameone/examples/savor}/ui/Chart.java | 2 +- .../examples/savor}/ui/ConsentForm.java | 8 +- .../examples/savor}/ui/DeliveryListForm.java | 6 +- .../codenameone/examples/savor}/ui/Lang.java | 4 +- .../examples/savor}/ui/Layouts.java | 2 +- .../codenameone/examples/savor}/ui/Look.java | 4 +- .../codenameone/examples/savor}/ui/Menu.java | 6 +- .../codenameone/examples/savor}/ui/Nav.java | 32 ++--- .../examples/savor}/ui/Photos.java | 4 +- .../examples/savor}/ui/ProfileForm.java | 18 +-- .../codenameone/examples/savor}/ui/Pulse.java | 2 +- .../examples/savor}/ui/RegisterForm.java | 14 +- .../codenameone/examples/savor}/ui/Ring.java | 2 +- .../examples/savor}/ui/ServerForm.java | 6 +- .../examples/savor}/ui/SettingsForm.java | 26 ++-- .../examples/savor}/ui/SignInForm.java | 6 +- .../examples/savor}/ui/Skeleton.java | 2 +- .../examples/savor}/ui/StatRow.java | 2 +- .../codenameone/examples/savor}/ui/Ui.java | 12 +- .../examples/savor}/ui/VerifyPhoneForm.java | 14 +- .../examples/savor}/ui/WelcomeForm.java | 6 +- .../common/src/main/l10n/Bundle.properties | 0 .../common/src/main/l10n/Bundle_de.properties | 22 +-- .../common/src/main/l10n/Bundle_en.properties | 22 +-- .../common/src/main/l10n/Bundle_es.properties | 22 +-- .../common/src/main/l10n/Bundle_fr.properties | 22 +-- .../common/src/main/l10n/Bundle_he.properties | 22 +-- .../common/src/main/resources/surfaces.json | 0 .../codenameone/examples/savor}/Actor.java | 4 +- .../examples/savor}/AdminDesktopTest.java | 8 +- .../examples/savor}/AdminModerationTest.java | 4 +- .../examples/savor}/AdminTest.java | 4 +- .../examples/savor}/AnalyticsTest.java | 2 +- .../examples/savor}/CourierModeTest.java | 2 +- .../savor}/CourierOnboardingTest.java | 8 +- .../examples/savor}/DeliveryFlowTest.java | 2 +- .../com/codenameone/examples/savor}/E2e.java | 38 ++--- .../examples/savor}/PaymentsTest.java | 2 +- .../examples/savor}/RegisterTest.java | 6 +- .../examples/savor}/SettingsTest.java | 4 +- .../examples/savor}/SignInTest.java | 6 +- scripts/{morsel => savor}/pom.xml | 10 +- scripts/{morsel => savor}/run-e2e.sh | 26 ++-- scripts/{morsel => savor}/run-web-e2e.sh | 26 ++-- scripts/{morsel => savor}/shared/pom.xml | 10 +- .../examples/savor}/api/AccountApi.java | 6 +- .../savor}/api/AccountClosureDto.java | 2 +- .../examples/savor}/api/AdminApi.java | 2 +- .../examples/savor}/api/AdminStatsDto.java | 2 +- .../examples/savor}/api/CardDto.java | 2 +- .../examples/savor}/api/CourierApi.java | 2 +- .../savor}/api/CourierApplicationDto.java | 2 +- .../examples/savor}/api/CourierDto.java | 2 +- .../examples/savor}/api/CourierStatusDto.java | 2 +- .../examples/savor}/api/CustomerApi.java | 6 +- .../examples/savor}/api/DayAmountDto.java | 2 +- .../examples/savor}/api/DayStatsDto.java | 2 +- .../savor}/api/DeliveryActivityDto.java | 2 +- .../savor}/api/DeliveryActivityKeys.java | 2 +- .../examples/savor}/api/DeliveryDto.java | 2 +- .../savor}/api/DeliveryRequestDto.java | 6 +- .../examples/savor}/api/DeliveryState.java | 2 +- .../savor}/api/DocumentContentDto.java | 2 +- .../examples/savor}/api/DocumentDto.java | 2 +- .../savor}/api/DocumentUploadDto.java | 2 +- .../examples/savor}/api/EarningsDto.java | 2 +- .../savor}/api/EarningsSummaryDto.java | 2 +- .../examples/savor}/api/FareOptionDto.java | 2 +- .../examples/savor}/api/FareQuoteDto.java | 2 +- .../examples/savor}/api/FlagDto.java | 2 +- .../examples/savor}/api/GeoApi.java | 2 +- .../savor}/api/ModerationEventDto.java | 2 +- .../examples/savor}/api/NameCountDto.java | 2 +- .../examples/savor}/api/NearbyCourierDto.java | 2 +- .../examples/savor}/api/OnboardingApi.java | 2 +- .../savor}/api/PasswordChangeDto.java | 2 +- .../examples/savor}/api/PaymentApi.java | 2 +- .../examples/savor}/api/PaymentConfigDto.java | 2 +- .../examples/savor}/api/PaymentMethodDto.java | 2 +- .../examples/savor}/api/PaymentSetupDto.java | 2 +- .../examples/savor}/api/PayoutAccountDto.java | 2 +- .../examples/savor}/api/PayoutDto.java | 2 +- .../savor}/api/PhoneChallengeDto.java | 2 +- .../examples/savor}/api/PhoneCodeDto.java | 2 +- .../examples/savor}/api/PlaceDto.java | 2 +- .../examples/savor}/api/PreferencesDto.java | 2 +- .../examples/savor}/api/PricingDto.java | 2 +- .../examples/savor}/api/ProfileDto.java | 2 +- .../examples/savor}/api/PushDeviceDto.java | 2 +- .../examples/savor}/api/RatingDto.java | 2 +- .../examples/savor}/api/ReasonDto.java | 2 +- .../examples/savor}/api/ReceiptDto.java | 2 +- .../examples/savor}/api/RegisterDto.java | 2 +- .../examples/savor}/api/RoleChangeDto.java | 2 +- .../examples/savor}/api/StatsSeriesDto.java | 2 +- .../examples/savor}/api/SuspendDto.java | 2 +- .../examples/savor}/api/TicketDto.java | 2 +- .../examples/savor}/api/TipDto.java | 2 +- .../examples/savor}/api/UserDetailDto.java | 2 +- .../examples/savor}/api/UserDto.java | 2 +- scripts/{morsel => savor}/web-e2e.mjs | 24 ++-- scripts/sync-initializr-template.py | 4 +- 290 files changed, 1465 insertions(+), 1465 deletions(-) rename scripts/{morsel => savor}/LICENSE (94%) rename scripts/{morsel => savor}/README.adoc (86%) rename scripts/{morsel => savor}/backend/application-dev.properties (87%) rename scripts/{morsel => savor}/backend/application-test.properties (86%) rename scripts/{morsel => savor}/backend/application.properties (71%) rename scripts/{morsel => savor}/backend/pom.xml (96%) rename scripts/{morsel => savor}/backend/server.sh (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Caller.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Days.java (99%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Ids.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Roles.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Rows.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/SecurityConfig.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Services.java (64%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/SignInCodePage.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/Text.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/AccountCleanup.java (87%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/AccountEndpoint.java (82%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/Accounts.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/DemoAccounts.java (87%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/Grants.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/Moderation.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/PhoneVerification.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/account/Preferences.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/admin/AdminEndpoint.java (81%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/admin/Statistics.java (91%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/courier/CourierEndpoint.java (86%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/courier/Couriers.java (90%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/courier/Earnings.java (89%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/delivery/CustomerEndpoint.java (80%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/delivery/Deliveries.java (95%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/delivery/DeliveryStateMachine.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/delivery/DemoDeliveries.java (91%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/delivery/Fares.java (90%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/delivery/Matcher.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/ClosedAccount.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/CourierApplication.java (99%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/CourierDocument.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/CourierHours.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/CourierState.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/Delivery.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/DeliveryActivity.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/DeliveryEvent.java (95%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/DeliveryPassed.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/LiveTicket.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/ModerationEvent.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/Payment.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/PaymentCustomer.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/PaymentMethod.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/PaymentSetup.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/Payout.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/PayoutAccount.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/PhoneCode.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/Preference.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/Pricing.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/Profile.java (99%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/domain/PushDevice.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/geo/Geo.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/geo/GeoEndpoint.java (95%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/geo/Geocoder.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/geo/PhotonGeocoder.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/geo/StubGeocoder.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/live/LiveEndpoint.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/live/LiveHub.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/live/LiveTickets.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/onboarding/Applications.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/onboarding/OnboardingEndpoint.java (87%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/MisconfiguredPayments.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/PayReturnPage.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/PaymentDeclinedException.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/PaymentEndpoint.java (83%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/PaymentProvider.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/Payments.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/SimulatedPayments.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/pay/StripePayments.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/CloudPushSender.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/ConsolePushSender.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/DeliveryPushes.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/MisconfiguredPushSender.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/PushDevices.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/PushSender.java (87%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/push/RecordingPushSender.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/ClosedAccountRepository.java (92%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/CourierApplicationRepository.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/CourierDocumentRepository.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/CourierHoursRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/CourierStateRepository.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/DeliveryActivityRepository.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/DeliveryEventRepository.java (92%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/DeliveryPassedRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/DeliveryRepository.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/LiveTicketRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/ModerationEventRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PaymentCustomerRepository.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PaymentMethodRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PaymentRepository.java (95%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PaymentSetupRepository.java (95%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PayoutAccountRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PayoutRepository.java (94%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PhoneCodeRepository.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PreferenceRepository.java (92%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PricingRepository.java (96%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/ProfileRepository.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/repository/PushDeviceRepository.java (92%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/ConsoleSmsSender.java (92%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/MessageBirdSmsSender.java (92%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/MisconfiguredSmsSender.java (98%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/SmsForm.java (97%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/SmsSender.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/TwilioSmsSender.java (93%) rename scripts/{morsel/backend/src/main/java/com/codenameone/examples/morsel => savor/backend/src/main/java/com/codenameone/examples/savor}/sms/VonageSmsSender.java (92%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/V1__accounts.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/V2__couriers.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/V4__deliveries.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/V5__payments.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/V6__pricing_row.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/V7__push.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/mysql/V3__courier_documents.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/postgresql/V3__courier_documents.sql (100%) rename scripts/{morsel => savor}/backend/src/main/resources/db/migration/sqlite/V3__courier_documents.sql (100%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/AccessMatrixTest.java (92%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/AccessTest.java (97%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/AccountTest.java (99%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/DaysTest.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/DeliveryFlowTest.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/DeliveryRaceTest.java (97%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/DeliveryStateMachineTest.java (96%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/LiveSocketTest.java (96%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/MatchingTest.java (99%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/ModerationTest.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/OnboardingApplicationTest.java (99%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/PaymentsTest.java (99%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/People.java (94%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/PricingAndStatsTest.java (97%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/ProcessorFaultsTest.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/PushTest.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/ScriptedPayments.java (96%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/SettingsTest.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/SignIn.java (98%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/StripePaymentsTest.java (94%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/Trips.java (99%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/WebSignInTest.java (93%) rename scripts/{morsel/backend/src/test/java/com/codenameone/examples/morsel => savor/backend/src/test/java/com/codenameone/examples/savor}/delivery/OfferRecheckTest.java (91%) rename scripts/{morsel => savor}/check-template.sh (95%) rename scripts/{morsel => savor}/common/codenameone_settings.properties (90%) rename scripts/{morsel => savor}/common/icon.png (100%) rename scripts/{morsel => savor}/common/pom.xml (99%) rename scripts/{morsel => savor}/common/src/desktop/resources/NativeTheme.res (100%) rename scripts/{morsel => savor}/common/src/main/css/theme.css (100%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/AppConfig.java (92%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/Prefs.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java => savor/common/src/main/java/com/codenameone/examples/savor/Savor.java} (85%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/Telemetry.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/AdminDeliveriesForm.java (94%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/AdminForm.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/ApplicationsForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/Console.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/Dashboard.java (94%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/FleetForm.java (90%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/Panes.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/PaymentsForm.java (90%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/PricingForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/admin/UsersForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/courier/ApplicationForm.java (96%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/courier/ApplyForm.java (93%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/courier/CourierForm.java (94%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/courier/EarningsForm.java (92%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/courier/Shift.java (92%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/customer/CustomerForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/customer/DeliveryCard.java (94%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/customer/PlaceSearchForm.java (94%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/live/LiveChannel.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/map/CarMarker.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/map/Locator.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/map/MapStage.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/map/Maps.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/map/Routes.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/merchant/MerchantForm.java (93%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/net/Account.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/net/Api.java (86%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/net/Net.java (97%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/net/Session.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/notify/Notifications.java (91%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/pay/CardForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/pay/Cards.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/pay/ReceiptForm.java (96%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/pay/WalletForm.java (94%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/BottomSheet.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Chart.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/ConsentForm.java (93%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/DeliveryListForm.java (96%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Lang.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Layouts.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Look.java (96%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Menu.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Nav.java (92%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Photos.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/ProfileForm.java (91%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Pulse.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/RegisterForm.java (92%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Ring.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/ServerForm.java (92%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/SettingsForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/SignInForm.java (95%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Skeleton.java (98%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/StatRow.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/Ui.java (99%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/VerifyPhoneForm.java (91%) rename scripts/{morsel/common/src/main/java/com/codenameone/examples/morsel => savor/common/src/main/java/com/codenameone/examples/savor}/ui/WelcomeForm.java (93%) rename scripts/{morsel => savor}/common/src/main/l10n/Bundle.properties (100%) rename scripts/{morsel => savor}/common/src/main/l10n/Bundle_de.properties (95%) rename scripts/{morsel => savor}/common/src/main/l10n/Bundle_en.properties (95%) rename scripts/{morsel => savor}/common/src/main/l10n/Bundle_es.properties (95%) rename scripts/{morsel => savor}/common/src/main/l10n/Bundle_fr.properties (95%) rename scripts/{morsel => savor}/common/src/main/l10n/Bundle_he.properties (95%) rename scripts/{morsel => savor}/common/src/main/resources/surfaces.json (100%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/Actor.java (98%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/AdminDesktopTest.java (97%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/AdminModerationTest.java (97%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/AdminTest.java (98%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/AnalyticsTest.java (99%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/CourierModeTest.java (98%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/CourierOnboardingTest.java (97%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/DeliveryFlowTest.java (98%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/E2e.java (94%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/PaymentsTest.java (99%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/RegisterTest.java (96%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/SettingsTest.java (97%) rename scripts/{morsel/common/src/test/java/com/codenameone/examples/morsel => savor/common/src/test/java/com/codenameone/examples/savor}/SignInTest.java (96%) rename scripts/{morsel => savor}/pom.xml (98%) rename scripts/{morsel => savor}/run-e2e.sh (80%) rename scripts/{morsel => savor}/run-web-e2e.sh (79%) rename scripts/{morsel => savor}/shared/pom.xml (90%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/AccountApi.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/AccountClosureDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/AdminApi.java (99%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/AdminStatsDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/CardDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/CourierApi.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/CourierApplicationDto.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/CourierDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/CourierStatusDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/CustomerApi.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DayAmountDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DayStatsDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DeliveryActivityDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DeliveryActivityKeys.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DeliveryDto.java (99%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DeliveryRequestDto.java (95%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DeliveryState.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DocumentContentDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DocumentDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/DocumentUploadDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/EarningsDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/EarningsSummaryDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/FareOptionDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/FareQuoteDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/FlagDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/GeoApi.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/ModerationEventDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/NameCountDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/NearbyCourierDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/OnboardingApi.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PasswordChangeDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PaymentApi.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PaymentConfigDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PaymentMethodDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PaymentSetupDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PayoutAccountDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PayoutDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PhoneChallengeDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PhoneCodeDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PlaceDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PreferencesDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PricingDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/ProfileDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/PushDeviceDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/RatingDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/ReasonDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/ReceiptDto.java (98%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/RegisterDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/RoleChangeDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/StatsSeriesDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/SuspendDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/TicketDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/TipDto.java (96%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/UserDetailDto.java (97%) rename scripts/{morsel/shared/src/main/java/com/codenameone/examples/morsel => savor/shared/src/main/java/com/codenameone/examples/savor}/api/UserDto.java (98%) rename scripts/{morsel => savor}/web-e2e.mjs (93%) diff --git a/scripts/morsel/LICENSE b/scripts/savor/LICENSE similarity index 94% rename from scripts/morsel/LICENSE rename to scripts/savor/LICENSE index 200c0c412d0..6a09ed8e735 100644 --- a/scripts/morsel/LICENSE +++ b/scripts/savor/LICENSE @@ -15,7 +15,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -This applies to the Morsel sample and to everything Codename One Initializr +This applies to the Savor sample and to everything Codename One Initializr writes into a generated project: the application templates under maven/cn1app-archetype/src/main/resources/archetype-resources, the agent skill under scripts/initializr/common/src/main/resources/skill, and the Initializr diff --git a/scripts/morsel/README.adoc b/scripts/savor/README.adoc similarity index 86% rename from scripts/morsel/README.adoc rename to scripts/savor/README.adoc index 815981f120e..91a14e923fc 100644 --- a/scripts/morsel/README.adoc +++ b/scripts/savor/README.adoc @@ -1,4 +1,4 @@ -= Morsel += Savor A delivery-hailing application and its server, in one Maven project and one language. A customer asks for a delivery, the nearest suitable courier is offered it, both follow the @@ -15,12 +15,12 @@ delivery-hailing template. Two chapters of the developer guide cover this project: -* https://www.codenameone.com/developer-guide/#morsel-users-guide[The user's guide] - (link:../../docs/developer-guide/Morsel-Users-Guide.asciidoc[source]) shows the +* https://www.codenameone.com/developer-guide/#savor-users-guide[The user's guide] + (link:../../docs/developer-guide/Savor-Users-Guide.asciidoc[source]) shows the application as a customer, a courier and an administrator see it, screen by screen, with the demo accounts and test cards. -* https://www.codenameone.com/developer-guide/#morsel-implementors-guide[The implementor's guide] - (link:../../docs/developer-guide/Morsel-Implementors-Guide.asciidoc[source]) +* https://www.codenameone.com/developer-guide/#savor-implementors-guide[The implementor's guide] + (link:../../docs/developer-guide/Savor-Implementors-Guide.asciidoc[source]) explains how the project is built and what to change to make it yours: a delivery-sharing service or the dispatch service of a small taxi station, real billing, your own map data or native maps, sign-in through another identity @@ -81,17 +81,17 @@ mvn verify -Psimulator -DskipTests -Dcodename1.platform=javase ---- The development profile creates these accounts, all with the password -`morsel-demo`, and a fortnight of deliveries for the charts to draw: +`savor-demo`, and a fortnight of deliveries for the charts to draw: [cols="2,3"] |=== -| `customer@morsel.example`, `customer2@morsel.example` | request deliveries -| `customer3@morsel.example` | a customer an administrator has flagged -| `courier@morsel.example`, `courier2@morsel.example` | approved couriers -| `courier3@morsel.example`, `courier4@morsel.example` | approved couriers of the +| `customer@savor.example`, `customer2@savor.example` | request deliveries +| `customer3@savor.example` | a customer an administrator has flagged +| `courier@savor.example`, `courier2@savor.example` | approved couriers +| `courier3@savor.example`, `courier4@savor.example` | approved couriers of the larger kinds of car -| `applicant@morsel.example` | has applied to drive and is waiting for a decision -| `admin@morsel.example` | runs the service +| `applicant@savor.example` | has applied to drive and is waiting for a decision +| `admin@savor.example` | runs the service |=== Sign in as the customer in one simulator and as a courier in another to follow a delivery @@ -147,7 +147,7 @@ Maps, search and routes are real and need no key: the map tiles come from OpenFreeMap, routes from the public OSRM server, and place search from Photon, which the server calls so the app never talks to a third party directly. -Text messages go through the provider `morsel.sms.provider` names: `twilio`, +Text messages go through the provider `savor.sms.provider` names: `twilio`, `vonage` or `messagebird`, each with its own credentials. On a development profile they are printed on the console instead, and the app is handed the code so a sign-up can finish. Any other profile needs a provider; one whose @@ -156,8 +156,8 @@ settings, and the app is told the code could not be sent. Payments are simulated on a development profile. Simulated, the card `4242 4242 4242 4242` is accepted, `4000 0000 0000 0002` is declined, and no -money moves. With `morsel.payments.provider=stripe` and -`MORSEL_PAYMENTS_STRIPE_SECRET` set in the server's environment, a card is +money moves. With `savor.payments.provider=stripe` and +`SAVOR_PAYMENTS_STRIPE_SECRET` set in the server's environment, a card is added on Stripe's own page, so its number never reaches the app or this server, and a delivery is charged when it completes. The Stripe provider is an untested stub: it is covered by tests that answer for Stripe, has never been run against @@ -170,7 +170,7 @@ to the deployment. database is the one named by `DATABASE_URL`: a `postgres://` or `mysql://` address, or the path of a SQLite file. The SMS and payment settings are listed there too, each also read from the environment in upper case with `_` for every -dot (`MORSEL_SMS_TWILIO_TOKEN`). +dot (`SAVOR_SMS_TWILIO_TOKEN`). == Tests @@ -201,19 +201,19 @@ mvn -pl shared,backend -Dcodename1.platform=backend test -Dcn1.backend.compiledT `npx playwright install chromium` in `scripts/`). Screenshots and logs go to `target/web-e2e`. -`.github/workflows/morsel.yml` runs all five, and builds the application for +`.github/workflows/savor.yml` runs all five, and builds the application for Android and iOS through `scripts/build-android-app.sh` and -`scripts/build-ios-app.sh` with `CN1_APP_DIR=scripts/morsel`. +`scripts/build-ios-app.sh` with `CN1_APP_DIR=scripts/savor`. == The Initializr template -`scripts/sync-initializr-template.py morsel --write` copies this project into the +`scripts/sync-initializr-template.py savor --write` copies this project into the Initializr's resources; without `--write` it fails when they differ, which is what CI runs. Change the project here, never the copies. == Licence -Morsel is released under MIT No Attribution (see `LICENSE`), not under the +Savor is released under MIT No Attribution (see `LICENSE`), not under the GPLv2 with Classpath Exception that covers Codename One itself. Take any of it into your own application, under any licence, with no notice to keep. The same holds for everything Initializr writes into a generated project. diff --git a/scripts/morsel/backend/application-dev.properties b/scripts/savor/backend/application-dev.properties similarity index 87% rename from scripts/morsel/backend/application-dev.properties rename to scripts/savor/backend/application-dev.properties index 92bfe0a5322..cc6975aabef 100644 --- a/scripts/morsel/backend/application-dev.properties +++ b/scripts/savor/backend/application-dev.properties @@ -23,8 +23,8 @@ cn1.datasource.url=:memory: # spelled out to show where the choice is made. The console senders are refused # outside a development profile. See application.properties for the real # providers. -# tag::morsel-properties-dev-providers[] -morsel.sms.provider=console -morsel.payments.provider=simulated -morsel.push.provider=console -# end::morsel-properties-dev-providers[] +# tag::savor-properties-dev-providers[] +savor.sms.provider=console +savor.payments.provider=simulated +savor.push.provider=console +# end::savor-properties-dev-providers[] diff --git a/scripts/morsel/backend/application-test.properties b/scripts/savor/backend/application-test.properties similarity index 86% rename from scripts/morsel/backend/application-test.properties rename to scripts/savor/backend/application-test.properties index 9bfa8d84825..31d340f0e12 100644 --- a/scripts/morsel/backend/application-test.properties +++ b/scripts/savor/backend/application-test.properties @@ -9,12 +9,12 @@ cn1.datasource.url=:memory: # The tests sign several accounts in from one address, one after another, which # is exactly what the sign-in rate limit is there to stop. -morsel.signin.per-minute=1000 +savor.signin.per-minute=1000 # Codes printed on the console (and handed back to the test as demoCode), cards # simulated: no test reaches an SMS provider or a payment processor. -morsel.sms.provider=console -morsel.payments.provider=simulated +savor.sms.provider=console +savor.payments.provider=simulated # Push notifications are kept for the tests to read, not sent. -morsel.push.provider=recording +savor.push.provider=recording diff --git a/scripts/morsel/backend/application.properties b/scripts/savor/backend/application.properties similarity index 71% rename from scripts/morsel/backend/application.properties rename to scripts/savor/backend/application.properties index 3e9e474761d..835fead2970 100644 --- a/scripts/morsel/backend/application.properties +++ b/scripts/savor/backend/application.properties @@ -21,22 +21,22 @@ # cn1.datasource.url the database # cn1.security.authorizationserver.issuer the public address of this server # cn1.security.authorizationserver.jwk.keys the key tokens are signed with -# morsel.admin.email, morsel.admin.password the first admin, who grants the rest -# morsel.sms.provider and its settings below -# morsel.payments.provider and its settings below -# morsel.push.app, morsel.push.key push notifications, below +# savor.admin.email, savor.admin.password the first admin, who grants the rest +# savor.sms.provider and its settings below +# savor.payments.provider and its settings below +# savor.push.app, savor.push.key push notifications, below # -# and may change the fares (morsel.fare.*), the currency (morsel.currency) and -# the geocoder (morsel.geocoder.url). +# and may change the fares (savor.fare.*), the currency (savor.currency) and +# the geocoder (savor.geocoder.url). # -# Text messages. morsel.sms.provider names the provider verification codes are +# Text messages. savor.sms.provider names the provider verification codes are # sent through, and each needs its own settings: # -# twilio morsel.sms.twilio.sid, morsel.sms.twilio.token, -# morsel.sms.twilio.from -# vonage morsel.sms.vonage.key, morsel.sms.vonage.secret, -# morsel.sms.vonage.from -# messagebird morsel.sms.messagebird.key, morsel.sms.messagebird.originator +# twilio savor.sms.twilio.sid, savor.sms.twilio.token, +# savor.sms.twilio.from +# vonage savor.sms.vonage.key, savor.sms.vonage.secret, +# savor.sms.vonage.from +# messagebird savor.sms.messagebird.key, savor.sms.messagebird.originator # console prints the code; development profiles only # # Unset, it is console on a development profile and twilio on any other. A @@ -45,7 +45,7 @@ # the code could not be sent. Nothing falls back to printing codes in # production. # -# Payments. morsel.payments.provider is simulated or stripe; unset, it is +# Payments. savor.payments.provider is simulated or stripe; unset, it is # simulated on a development profile and stripe on any other. Simulated cards: # 4242 4242 4242 4242 is accepted, 4000 0000 0000 0002 is declined, and nothing # is charged. Stripe is an untested stub over Stripe's REST API, a starting @@ -57,32 +57,32 @@ # environment of the deployment instead, where each setting is read in upper # case with _ for every dot: # -# MORSEL_PAYMENTS_STRIPE_SECRET a restricted or secret key (sk_...) -# MORSEL_SMS_TWILIO_TOKEN and so on +# SAVOR_PAYMENTS_STRIPE_SECRET a restricted or secret key (sk_...) +# SAVOR_SMS_TWILIO_TOKEN and so on # # Stripe's page sends the browser back to /pay/return on this server when it is # done, so the server has to know the address it is reached at from a phone. It # uses cn1.security.authorizationserver.issuer, which a deployment sets anyway; -# morsel.public.url overrides that when the two differ. +# savor.public.url overrides that when the two differ. # -# Push notifications. morsel.push.provider is cloud, console or recording; +# Push notifications. savor.push.provider is cloud, console or recording; # unset, it is recording on the test profile, console on the other development # profiles and cloud on any other. cloud sends through the Codename One push # service, which delivers to Firebase, Apple, Huawei and the browsers for you, # and needs two settings from the Push section of the Codename One console: # -# morsel.push.app the app's push id -# morsel.push.key a server key with the push scope; put it in the -# environment as MORSEL_PUSH_KEY, like the other secrets +# savor.push.app the app's push id +# savor.push.key a server key with the push scope; put it in the +# environment as SAVOR_PUSH_KEY, like the other secrets # # Without them the server still starts and serves deliveries; the missing settings # are logged at start-up and again for every notification that is not sent. -# morsel.push.activity.seconds (45) is how often, at most, a moving car +# savor.push.activity.seconds (45) is how often, at most, a moving car # updates the customer's Live Activity. # # The prices are settings only until an admin changes them in the app. From -# then on they are in the database (morsel.fare.* is the starting point, and -# morsel.fare.serviceFeePercent and morsel.fare.commissionPercent join it). +# then on they are in the database (savor.fare.* is the starting point, and +# savor.fare.serviceFeePercent and savor.fare.commissionPercent join it). # ${DATABASE_URL} is resolved when the value is READ, so this line is fine in a # checkout where that variable is unset: the dev profile overrides the key, and diff --git a/scripts/morsel/backend/pom.xml b/scripts/savor/backend/pom.xml similarity index 96% rename from scripts/morsel/backend/pom.xml rename to scripts/savor/backend/pom.xml index a6b2ff5f49f..a336210b612 100644 --- a/scripts/morsel/backend/pom.xml +++ b/scripts/savor/backend/pom.xml @@ -2,16 +2,16 @@ 4.0.0 - com.codenameone.examples.morsel - morsel + com.codenameone.examples.savor + savor 1.0-SNAPSHOT - com.codenameone.examples.morsel - morsel-backend + com.codenameone.examples.savor + savor-backend 1.0-SNAPSHOT jar - morsel-backend + savor-backend ${project.groupId} - morsel-shared + savor-shared ${project.version} ${project.groupId} - morsel-shared + savor-shared ${project.version} diff --git a/scripts/morsel/common/src/desktop/resources/NativeTheme.res b/scripts/savor/common/src/desktop/resources/NativeTheme.res similarity index 100% rename from scripts/morsel/common/src/desktop/resources/NativeTheme.res rename to scripts/savor/common/src/desktop/resources/NativeTheme.res diff --git a/scripts/morsel/common/src/main/css/theme.css b/scripts/savor/common/src/main/css/theme.css similarity index 100% rename from scripts/morsel/common/src/main/css/theme.css rename to scripts/savor/common/src/main/css/theme.css diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/AppConfig.java similarity index 92% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/AppConfig.java index f83bd874336..f96dbd910a8 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/AppConfig.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/AppConfig.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.io.Preferences; import com.codename1.ui.CN; @@ -30,7 +30,7 @@ public final class AppConfig { public static final String DEFAULT_SERVER = "http://localhost:8080"; /// The id the server knows this app by. The same constant is in the /// server's `SecurityConfig`. - public static final String CLIENT_ID = "morsel-app"; + public static final String CLIENT_ID = "savor-app"; /// The address the server hands the authorization code back to. The app /// reads the code out of the redirect itself and never opens this address, /// so it only has to be one the server has on its list. @@ -38,30 +38,30 @@ public final class AppConfig { /// The path the server hands the code back on when the app runs in a /// browser. The same constant is in the server's `SecurityConfig`. public static final String WEB_REDIRECT_PATH = "/signin/code"; - // tag::morsel-push-key[] + // tag::savor-push-key[] /// The app's push key, from the Push section of the Codename One console. /// Empty, the app registers for no notifications: everything still works, /// and a phone in a pocket simply hears nothing until it is opened. public static final String PUSH_APP_KEY = ""; - // end::morsel-push-key[] + // end::savor-push-key[] /// Where the map opens when the device will not say where it is. public static final double DEFAULT_LAT = 37.8080; public static final double DEFAULT_LNG = -122.4120; - private static final String SERVER_KEY = "morsel.server"; - private static final String MODE_KEY = "morsel.mode"; - private static final String MODE_OWNER_KEY = "morsel.mode.owner"; + private static final String SERVER_KEY = "savor.server"; + private static final String MODE_KEY = "savor.mode"; + private static final String MODE_OWNER_KEY = "savor.mode.owner"; private AppConfig() { } - /// The server's address, with no trailing slash. A `morsel.server.url` + /// The server's address, with no trailing slash. A `savor.server.url` /// system property wins, which is what the tests use; then the address saved /// from the settings screen; then, in a browser, the address the page came /// from, since the server that hosts the app is the server it talks to; /// then [#DEFAULT_SERVER]. public static String serverUrl() { - String url = System.getProperty("morsel.server.url"); + String url = System.getProperty("savor.server.url"); if (url == null || url.length() == 0) { String origin = pageOrigin(); // In a browser it is always the page's own server: see @@ -107,7 +107,7 @@ public static void setServerUrl(String url) { } /// The mode the account `owner` (see - /// [com.codenameone.examples.morsel.net.Session#owner(String)]) chose last + /// [com.codenameone.examples.savor.net.Session#owner(String)]) chose last /// -- `customer`, `courier` or `admin` -- or /// empty when they have chosen none, or the choice on record is someone /// else's. A choice recorded with no name is anyone's: that is how a test diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java index a959d471782..64c1173b854 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Prefs.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java @@ -16,10 +16,10 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.io.Preferences; -import com.codenameone.examples.morsel.api.PlaceDto; +import com.codenameone.examples.savor.api.PlaceDto; import java.util.ArrayList; import java.util.List; @@ -69,7 +69,7 @@ public interface Listener { /// The one way to pay that needs nothing set up. public static final String PAYMENT_CASH = "cash"; - private static final String PREFIX = "morsel.pref."; + private static final String PREFIX = "savor.pref."; /// The plain values that are a copy of the account's; see [#ownedBy]. private static final String[] ACCOUNT_OWNED = {NOTIFY_DELIVERY_UPDATES, NOTIFY_RECEIPTS, NOTIFY_PROMOTIONS, COURIER_GENDER, QUIET_DELIVERY, ACCESSIBLE_VEHICLE, PET_FRIENDLY, @@ -299,7 +299,7 @@ public static void addRecent(PlaceDto place) { /// by anyone: reported, they would be saved over the new account's. /// /// @param account the account, with its server, as - /// [com.codenameone.examples.morsel.net.Session#owner(String)] names + /// [com.codenameone.examples.savor.net.Session#owner(String)] names /// it, or empty for none public static void ownedBy(String account) { String owner = account == null ? "" : account; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Savor.java similarity index 85% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/Savor.java index ebc5320bae8..d7a8c717603 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Morsel.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Savor.java @@ -16,18 +16,18 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.system.Lifecycle; -import com.codenameone.examples.morsel.courier.Shift; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.net.Account; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.notify.Notifications; -import com.codenameone.examples.morsel.pay.CardForm; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Look; -import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.savor.courier.Shift; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.net.Account; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.notify.Notifications; +import com.codenameone.examples.savor.pay.CardForm; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Look; +import com.codenameone.examples.savor.ui.Nav; /// The app. It opens on whatever the stored session allows: the mode the user /// was last in when they are still signed in, the welcome screen when not. @@ -45,7 +45,7 @@ /// - `Prefs` -- what the user chose in Settings. /// - `Telemetry` -- what is reported about how the app is used, once the user /// has agreed to it. -public class Morsel extends Lifecycle { +public class Savor extends Lifecycle { @Override public void runApp() { // Before the first screen is built: a screen keeps the look and the diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java index 0249ee1f571..5a75f081e00 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/Telemetry.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.analytics.Analytics; import com.codename1.analytics.AnalyticsConsent; @@ -25,8 +25,8 @@ import com.codename1.analytics.CodenameOneAnalyticsProvider; import com.codename1.analytics.LoggingAnalyticsProvider; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.api.DeliveryState; -import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.savor.api.DeliveryState; +import com.codenameone.examples.savor.ui.Lang; /// What the app reports about how it is used, and whether it may. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminDeliveriesForm.java similarity index 94% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminDeliveriesForm.java index 2c202977c50..f6fcf1f29ca 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminDeliveriesForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminDeliveriesForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.ui.Container; import com.codename1.ui.FontImage; @@ -24,14 +24,14 @@ import com.codename1.ui.Label; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.ReasonDto; -import com.codenameone.examples.morsel.api.DeliveryDto; -import com.codenameone.examples.morsel.api.DeliveryState; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.ReasonDto; +import com.codenameone.examples.savor.api.DeliveryDto; +import com.codenameone.examples.savor.api.DeliveryState; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// Every delivery, for an admin: which are under way, which finished and which diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java index 1e6269a17d2..70ca88cccae 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/AdminForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.ui.CN; import com.codename1.ui.Container; @@ -27,10 +27,10 @@ import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.util.UITimer; -import com.codenameone.examples.morsel.ui.Layouts; -import com.codenameone.examples.morsel.ui.Menu; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.ui.Layouts; +import com.codenameone.examples.savor.ui.Menu; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Ui; /// Administering: what needs an answer, how the service is doing right now and /// over the last weeks, and the way to everything an admin looks after. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java index f63503795d9..bb87a99b95b 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/ApplicationsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.components.ScaleImageLabel; import com.codename1.io.Log; @@ -30,15 +30,15 @@ import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.plaf.Style; import com.codename1.util.Base64; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.DocumentContentDto; -import com.codenameone.examples.morsel.api.DocumentDto; -import com.codenameone.examples.morsel.api.CourierApplicationDto; -import com.codenameone.examples.morsel.api.ReasonDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.DocumentContentDto; +import com.codenameone.examples.savor.api.DocumentDto; +import com.codenameone.examples.savor.api.CourierApplicationDto; +import com.codenameone.examples.savor.api.ReasonDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// The people who asked to drive, and the decision about each. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java index 6be76a60c7f..a4dc6c4b72c 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Console.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.ui.Button; import com.codename1.ui.CN; @@ -29,13 +29,13 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.util.UITimer; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Layouts; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.SettingsForm; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Layouts; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.SettingsForm; +import com.codenameone.examples.savor.ui.Ui; import java.util.ArrayList; import java.util.List; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Dashboard.java similarity index 94% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Dashboard.java index b2f6e6fae88..0684ee54e9b 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Dashboard.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Dashboard.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.ui.Button; import com.codename1.ui.CN; @@ -28,20 +28,20 @@ import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.layouts.GridLayout; -import com.codenameone.examples.morsel.api.AdminStatsDto; -import com.codenameone.examples.morsel.api.DayStatsDto; -import com.codenameone.examples.morsel.api.CourierDto; -import com.codenameone.examples.morsel.api.NameCountDto; -import com.codenameone.examples.morsel.api.DeliveryState; -import com.codenameone.examples.morsel.api.StatsSeriesDto; -import com.codenameone.examples.morsel.live.LiveChannel; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Chart; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Layouts; -import com.codenameone.examples.morsel.ui.StatRow; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.api.AdminStatsDto; +import com.codenameone.examples.savor.api.DayStatsDto; +import com.codenameone.examples.savor.api.CourierDto; +import com.codenameone.examples.savor.api.NameCountDto; +import com.codenameone.examples.savor.api.DeliveryState; +import com.codenameone.examples.savor.api.StatsSeriesDto; +import com.codenameone.examples.savor.live.LiveChannel; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Chart; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Layouts; +import com.codenameone.examples.savor.ui.StatRow; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// The numbers of the admin console: what needs an answer, how the service is diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java similarity index 90% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java index 063979aebbc..65801456471 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/FleetForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java @@ -16,23 +16,23 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.maps.LatLng; import com.codename1.ui.Command; import com.codename1.ui.FontImage; import com.codename1.ui.Form; import com.codename1.ui.Label; -import com.codenameone.examples.morsel.api.CourierDto; -import com.codenameone.examples.morsel.live.LiveChannel; -import com.codenameone.examples.morsel.map.CarMarker; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.map.MapStage; -import com.codenameone.examples.morsel.map.Maps; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.api.CourierDto; +import com.codenameone.examples.savor.live.LiveChannel; +import com.codenameone.examples.savor.map.CarMarker; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.map.MapStage; +import com.codenameone.examples.savor.map.Maps; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; import com.codename1.ui.util.UITimer; import java.util.ArrayList; import java.util.HashMap; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Panes.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Panes.java index 32ac1c0a024..2c5a5e3d564 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/Panes.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Panes.java @@ -16,15 +16,15 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.ui.Component; import com.codename1.ui.Container; import com.codename1.ui.Form; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.ui.Layouts; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.ui.Layouts; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Ui; import java.util.ArrayList; import java.util.List; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PaymentsForm.java similarity index 90% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PaymentsForm.java index 0527b514a43..204b651afd7 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PaymentsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PaymentsForm.java @@ -16,20 +16,20 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.ui.Container; import com.codename1.ui.FontImage; import com.codename1.ui.Form; import com.codename1.ui.layouts.BoxLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.ReasonDto; -import com.codenameone.examples.morsel.api.ReceiptDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.pay.ReceiptForm; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.ReasonDto; +import com.codenameone.examples.savor.api.ReceiptDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.pay.ReceiptForm; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// What was charged, newest first, and for each whether it went through. A diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PricingForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PricingForm.java index 60f982809c3..8249f8ef563 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/PricingForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PricingForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -26,12 +26,12 @@ import com.codename1.ui.TextField; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.GridLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.PricingDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.PricingDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; /// What a delivery costs: the parts of a fare, the service's share, and what the /// roomier cars and the busy hours multiply it by. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java index 446a2fcf827..1d351a0a0c5 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/admin/UsersForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.admin; +package com.codenameone.examples.savor.admin; import com.codename1.components.SpanLabel; import com.codename1.components.Switch; @@ -30,18 +30,18 @@ import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.layouts.GridLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.FlagDto; -import com.codenameone.examples.morsel.api.ModerationEventDto; -import com.codenameone.examples.morsel.api.RoleChangeDto; -import com.codenameone.examples.morsel.api.SuspendDto; -import com.codenameone.examples.morsel.api.UserDetailDto; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.StatRow; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.FlagDto; +import com.codenameone.examples.savor.api.ModerationEventDto; +import com.codenameone.examples.savor.api.RoleChangeDto; +import com.codenameone.examples.savor.api.SuspendDto; +import com.codenameone.examples.savor.api.UserDetailDto; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.StatRow; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// The accounts, and what an admin decides about each: its roles, whether it diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java similarity index 96% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java index 867bce089ec..7474ba3a7f8 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplicationForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.courier; +package com.codenameone.examples.savor.courier; import com.codename1.components.SpanLabel; import com.codename1.l10n.ParseException; @@ -37,20 +37,20 @@ import com.codename1.ui.layouts.GridLayout; import com.codename1.ui.spinner.Picker; import com.codename1.util.Base64; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.DocumentDto; -import com.codenameone.examples.morsel.api.DocumentUploadDto; -import com.codenameone.examples.morsel.api.CourierApplicationDto; -import com.codenameone.examples.morsel.api.ProfileDto; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.net.Account; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Chart; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Photos; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.DocumentDto; +import com.codenameone.examples.savor.api.DocumentUploadDto; +import com.codenameone.examples.savor.api.CourierApplicationDto; +import com.codenameone.examples.savor.api.ProfileDto; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.net.Account; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Chart; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Photos; +import com.codenameone.examples.savor.ui.Ui; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -82,7 +82,7 @@ public final class ApplicationForm { "Non-binary"}; private static final String ISO = "yyyy-MM-dd"; - private final Form form = Ui.form("Drive with Morsel", "Application"); + private final Form form = Ui.form("Drive with Savor", "Application"); private final Runnable done; private final Container body = Ui.page(); private final Label stepLabel = Ui.plain("", "WlStepLabel"); diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java similarity index 93% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java index b59eb7e3703..72c5c5b66ad 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/ApplyForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.courier; +package com.codenameone.examples.savor.courier; import com.codename1.ui.Container; import com.codename1.ui.FontImage; @@ -26,16 +26,16 @@ import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.util.UITimer; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.DocumentDto; -import com.codenameone.examples.morsel.api.CourierApplicationDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Menu; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.DocumentDto; +import com.codenameone.examples.savor.api.CourierApplicationDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Menu; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Ui; /// The courier's side of the app for someone who is not a courier yet: where /// their application stands, and the way on from there. @@ -60,7 +60,7 @@ public ApplyForm() { bar.setUIID("WlHomeBar"); bar.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(menu.flatButton())); bar.add(BorderLayout.CENTER, FlowLayout.encloseLeftMiddle( - Ui.label("Drive with Morsel", "WlHeading"))); + Ui.label("Drive with Savor", "WlHeading"))); page.setSafeArea(false); Container screen = new Container(new BorderLayout()); // The strip the status bar is drawn in is the bar's colour, not diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java similarity index 94% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java index 7074a9a0a56..0a71d6d651b 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/CourierForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.courier; +package com.codenameone.examples.savor.courier; import com.codename1.maps.LatLng; import com.codename1.maps.Marker; @@ -31,28 +31,28 @@ import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.layouts.GridLayout; import com.codename1.ui.util.UITimer; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.CourierStatusDto; -import com.codenameone.examples.morsel.api.EarningsDto; -import com.codenameone.examples.morsel.api.DeliveryDto; -import com.codenameone.examples.morsel.api.DeliveryState; -import com.codenameone.examples.morsel.live.LiveChannel; -import com.codenameone.examples.morsel.map.CarMarker; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.map.MapStage; -import com.codenameone.examples.morsel.map.Maps; -import com.codenameone.examples.morsel.map.Routes; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.BottomSheet; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Menu; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Pulse; -import com.codenameone.examples.morsel.ui.DeliveryListForm; -import com.codenameone.examples.morsel.ui.Ring; -import com.codenameone.examples.morsel.ui.StatRow; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.CourierStatusDto; +import com.codenameone.examples.savor.api.EarningsDto; +import com.codenameone.examples.savor.api.DeliveryDto; +import com.codenameone.examples.savor.api.DeliveryState; +import com.codenameone.examples.savor.live.LiveChannel; +import com.codenameone.examples.savor.map.CarMarker; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.map.MapStage; +import com.codenameone.examples.savor.map.Maps; +import com.codenameone.examples.savor.map.Routes; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.BottomSheet; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Menu; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Pulse; +import com.codenameone.examples.savor.ui.DeliveryListForm; +import com.codenameone.examples.savor.ui.Ring; +import com.codenameone.examples.savor.ui.StatRow; +import com.codenameone.examples.savor.ui.Ui; import java.io.IOException; /// Onboarding: go online, answer the offer, drive the delivery. @@ -534,7 +534,7 @@ private void message() { return; } try { - CN.sendSMS(number, Lang.tr("Hi, this is your Morsel courier.")); + CN.sendSMS(number, Lang.tr("Hi, this is your Savor courier.")); } catch (IOException failed) { Ui.say("Messages are not available on this device"); } diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java similarity index 92% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java index 58d951469ae..f0e03a1ddef 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/EarningsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.courier; +package com.codenameone.examples.savor.courier; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -28,17 +28,17 @@ import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.EarningsSummaryDto; -import com.codenameone.examples.morsel.api.PayoutAccountDto; -import com.codenameone.examples.morsel.api.PayoutDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.pay.Cards; -import com.codenameone.examples.morsel.ui.Chart; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.StatRow; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.EarningsSummaryDto; +import com.codenameone.examples.savor.api.PayoutAccountDto; +import com.codenameone.examples.savor.api.PayoutDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.pay.Cards; +import com.codenameone.examples.savor.ui.Chart; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.StatRow; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// What onboarding has paid: what is waiting to be cashed out, the last diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/Shift.java similarity index 92% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/Shift.java index 0200310adf8..1f5a55c6634 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/courier/Shift.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/Shift.java @@ -16,20 +16,20 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.courier; +package com.codenameone.examples.savor.courier; import com.codename1.background.ForegroundService; import com.codename1.maps.LatLng; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.api.CourierStatusDto; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.map.Maps; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.savor.api.CourierStatusDto; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.map.Maps; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; import java.util.concurrent.atomic.AtomicBoolean; -// tag::morsel-shift[] +// tag::savor-shift[] /// A courier's time on line, as far as the phone is concerned: the position /// keeps going to the server while the app is in the background. /// @@ -52,7 +52,7 @@ public final class Shift { /// The longest the server goes without hearing, well inside the minute /// after which it stops offering this courier deliveries. static final long KEEP_ALIVE_MILLIS = 20000L; - private static final String CHANNEL = "morsel-onboarding"; + private static final String CHANNEL = "savor-onboarding"; private static final Net.Failed QUIET = (status, message) -> { }; private static boolean on; @@ -84,7 +84,7 @@ public static void begin() { && !CN.isSimulator()) { RUNNING.set(true); service = ForegroundService.start(CHANNEL, "You're online", - "Morsel shares your location with customers while you drive.", null, + "Savor shares your location with customers while you drive.", null, Shift::keepAlive); } } @@ -133,7 +133,7 @@ public static void paused() { public static void resumed() { background = false; } - // end::morsel-shift[] + // end::savor-shift[] /// The foreground service's task, on a thread of its own: wakes now and /// then for the car that is not moving, which brings no new positions. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java index 50f2108f066..eb86d916d70 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/CustomerForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.customer; +package com.codenameone.examples.savor.customer; import com.codename1.maps.LatLng; import com.codename1.components.SpanLabel; @@ -31,38 +31,38 @@ import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.layouts.GridLayout; import com.codename1.ui.util.UITimer; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.FareOptionDto; -import com.codenameone.examples.morsel.api.FareQuoteDto; -import com.codenameone.examples.morsel.api.NearbyCourierDto; -import com.codenameone.examples.morsel.api.PaymentMethodDto; -import com.codenameone.examples.morsel.api.PlaceDto; -import com.codenameone.examples.morsel.api.RatingDto; -import com.codenameone.examples.morsel.api.DeliveryDto; -import com.codenameone.examples.morsel.api.DeliveryRequestDto; -import com.codenameone.examples.morsel.api.DeliveryState; -import com.codenameone.examples.morsel.api.TipDto; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.live.LiveChannel; -import com.codenameone.examples.morsel.map.CarMarker; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.map.MapStage; -import com.codenameone.examples.morsel.map.Maps; -import com.codenameone.examples.morsel.map.Routes; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.pay.ReceiptForm; -import com.codenameone.examples.morsel.pay.WalletForm; -import com.codenameone.examples.morsel.ui.BottomSheet; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Menu; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Pulse; -import com.codenameone.examples.morsel.ui.DeliveryListForm; -import com.codenameone.examples.morsel.ui.Ui; -import com.codenameone.examples.morsel.ui.VerifyPhoneForm; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.FareOptionDto; +import com.codenameone.examples.savor.api.FareQuoteDto; +import com.codenameone.examples.savor.api.NearbyCourierDto; +import com.codenameone.examples.savor.api.PaymentMethodDto; +import com.codenameone.examples.savor.api.PlaceDto; +import com.codenameone.examples.savor.api.RatingDto; +import com.codenameone.examples.savor.api.DeliveryDto; +import com.codenameone.examples.savor.api.DeliveryRequestDto; +import com.codenameone.examples.savor.api.DeliveryState; +import com.codenameone.examples.savor.api.TipDto; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.live.LiveChannel; +import com.codenameone.examples.savor.map.CarMarker; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.map.MapStage; +import com.codenameone.examples.savor.map.Maps; +import com.codenameone.examples.savor.map.Routes; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.pay.ReceiptForm; +import com.codenameone.examples.savor.pay.WalletForm; +import com.codenameone.examples.savor.ui.BottomSheet; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Menu; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Pulse; +import com.codenameone.examples.savor.ui.DeliveryListForm; +import com.codenameone.examples.savor.ui.Ui; +import com.codenameone.examples.savor.ui.VerifyPhoneForm; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -129,7 +129,7 @@ public CustomerForm() { () -> WalletForm.show(form)); UserDto user = Session.user(); if (!user.courier()) { - menu.item(FontImage.MATERIAL_DIRECTIONS_CAR, "Drive with Morsel", "drive", + menu.item(FontImage.MATERIAL_DIRECTIONS_CAR, "Drive with Savor", "drive", () -> Nav.switchTo(Nav.COURIER)); } stage = new MapStage(here, Maps.STREET_ZOOM, menu.button()); @@ -802,7 +802,7 @@ private void message() { return; } try { - CN.sendSMS(number, Lang.tr("Hi, this is your Morsel customer.")); + CN.sendSMS(number, Lang.tr("Hi, this is your Savor customer.")); } catch (IOException failed) { Ui.say("Messages are not available on this device"); } diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DeliveryCard.java similarity index 94% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DeliveryCard.java index a374e5343b5..84c2940ef16 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/DeliveryCard.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DeliveryCard.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.customer; +package com.codenameone.examples.savor.customer; import com.codename1.surfaces.LiveActivity; import com.codename1.surfaces.LiveActivityDescriptor; @@ -28,12 +28,12 @@ import com.codename1.surfaces.SurfaceRow; import com.codename1.surfaces.SurfaceText; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.api.DeliveryActivityDto; -import com.codenameone.examples.morsel.api.DeliveryActivityKeys; -import com.codenameone.examples.morsel.api.DeliveryDto; -import com.codenameone.examples.morsel.api.DeliveryState; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.savor.api.DeliveryActivityDto; +import com.codenameone.examples.savor.api.DeliveryActivityKeys; +import com.codenameone.examples.savor.api.DeliveryDto; +import com.codenameone.examples.savor.api.DeliveryState; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; import java.util.HashMap; import java.util.Map; @@ -69,7 +69,7 @@ final class DeliveryCard { private DeliveryCard() { } - // tag::morsel-delivery-card-follow[] + // tag::savor-delivery-card-follow[] /// The customer screen has `delivery` as it now is. Called on the event thread. static void follow(final DeliveryDto delivery) { if (delivery == null || busy) { @@ -131,7 +131,7 @@ static void follow(final DeliveryDto delivery) { }); }); } - // end::morsel-delivery-card-follow[] + // end::savor-delivery-card-follow[] /// The activity's state, in the keys the server sends too. static Map state(DeliveryDto delivery) { @@ -161,7 +161,7 @@ static Map state(DeliveryDto delivery) { return out; } - // tag::morsel-delivery-card-layout[] + // tag::savor-delivery-card-layout[] /// The layout, filled in from the state on the device: an update sends the /// state alone. private static LiveActivityDescriptor descriptor() { @@ -198,7 +198,7 @@ private static SurfaceNode content() { .setValueState(DeliveryActivityKeys.PROGRESS) .setColor(SurfaceColor.rgb(ACCENT))); } - // end::morsel-delivery-card-layout[] + // end::savor-delivery-card-layout[] private static String text(String value) { return value == null ? "" : value; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/PlaceSearchForm.java similarity index 94% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/PlaceSearchForm.java index c66b6ef0ff8..8c1ffa4b945 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/customer/PlaceSearchForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/PlaceSearchForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.customer; +package com.codenameone.examples.savor.customer; import com.codename1.maps.LatLng; import com.codename1.ui.Container; @@ -26,11 +26,11 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.util.UITimer; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.api.PlaceDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.api.PlaceDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/live/LiveChannel.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/live/LiveChannel.java index e66a8b9a4a0..69654333b4c 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/live/LiveChannel.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/live/LiveChannel.java @@ -16,16 +16,16 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.live; +package com.codenameone.examples.savor.live; import com.codename1.io.CharArrayReader; import com.codename1.io.JSONParser; import com.codename1.io.WebSocket; import com.codename1.io.WebSocketState; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; import java.io.IOException; import java.util.Map; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/CarMarker.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/CarMarker.java index e44729f6426..b1fd32c2e6a 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/CarMarker.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/CarMarker.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.map; +package com.codenameone.examples.savor.map; import com.codename1.maps.LatLng; import com.codename1.maps.MapView; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Locator.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Locator.java index 0751c8f03da..f696921c159 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Locator.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Locator.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.map; +package com.codenameone.examples.savor.map; import com.codename1.location.Location; import com.codename1.location.LocationListener; @@ -24,7 +24,7 @@ import com.codename1.location.LocationRequest; import com.codename1.maps.LatLng; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.savor.AppConfig; import java.util.ArrayList; import java.util.List; @@ -118,7 +118,7 @@ public static double heading() { return heading; } - // tag::morsel-locator-track[] + // tag::savor-locator-track[] /// Asks for the position precisely, often, and on in the background, or /// goes back to the ordinary search. A courier on line tracks: customers follow /// the car on a map, and the server offers deliveries by distance. @@ -126,7 +126,7 @@ public static double heading() { /// The platform decides what "in the background" allows. With the /// `location` background mode an iPhone goes on reporting after the app is /// put away; Android does while the app runs a foreground service, which is - /// what [com.codenameone.examples.morsel.courier.Shift] starts. + /// what [com.codenameone.examples.savor.courier.Shift] starts. public static void track(boolean on) { if (tracking == on) { return; @@ -161,7 +161,7 @@ public static void resumed() { start(true); } } - // end::morsel-locator-track[] + // end::savor-locator-track[] /// How the search stands: one of the constants above. public static int state() { diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/MapStage.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/MapStage.java index 92c4566dcbc..70db8b9c5d8 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/MapStage.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/MapStage.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.map; +package com.codenameone.examples.savor.map; import com.codename1.maps.LatLng; import com.codename1.maps.MapView; @@ -30,8 +30,8 @@ import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.layouts.LayeredLayout; -import com.codenameone.examples.morsel.ui.BottomSheet; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.ui.BottomSheet; +import com.codenameone.examples.savor.ui.Ui; /// A map that fills the screen, and what floats over it. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Maps.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Maps.java index 5897184c23d..eedf1465260 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Maps.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Maps.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.map; +package com.codenameone.examples.savor.map; import com.codename1.maps.LatLng; import com.codename1.maps.MapBounds; @@ -34,7 +34,7 @@ import com.codename1.ui.plaf.Style; import com.codename1.ui.plaf.UIManager; import com.codename1.util.MathUtil; -import com.codenameone.examples.morsel.ui.Look; +import com.codenameone.examples.savor.ui.Look; import java.util.ArrayList; import java.util.List; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Routes.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Routes.java index db2e632ece6..45e59d8e6b1 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/map/Routes.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/map/Routes.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.map; +package com.codenameone.examples.savor.map; import com.codename1.maps.LatLng; import com.codename1.maps.routing.Route; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java similarity index 93% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java index 575881ac21b..6066fa9e718 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/merchant/MerchantForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java @@ -16,16 +16,16 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.merchant; +package com.codenameone.examples.savor.merchant; import com.codename1.ui.Container; import com.codename1.ui.FontImage; import com.codename1.ui.Form; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.FlowLayout; -import com.codenameone.examples.morsel.ui.Menu; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.ui.Menu; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Ui; /// The restaurant's side of the app: the orders coming in, and the menu they /// are ordered from. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Account.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Account.java index d599d284ec8..5f3e7b0cb63 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Account.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Account.java @@ -16,14 +16,14 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.net; +package com.codenameone.examples.savor.net; import com.codename1.io.Preferences; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.api.PlaceDto; -import com.codenameone.examples.morsel.api.PreferencesDto; -import com.codenameone.examples.morsel.api.ProfileDto; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.api.PlaceDto; +import com.codenameone.examples.savor.api.PreferencesDto; +import com.codenameone.examples.savor.api.ProfileDto; /// Keeps what the user chose in Settings with their account. /// @@ -50,8 +50,8 @@ public final class Account { private static final int RETRY_MILLIS = 30000; /// The keys under which each half is marked unsent; the value is the /// account it was not sent for. - private static final String UNSENT_PREFERENCES = "morsel.account.unsent.preferences"; - private static final String UNSENT_PROFILE = "morsel.account.unsent.profile"; + private static final String UNSENT_PREFERENCES = "savor.account.unsent.preferences"; + private static final String UNSENT_PROFILE = "savor.account.unsent.profile"; /// True while the account's values are being put into `Prefs`, which /// reports each of them as a change like any other. @@ -331,7 +331,7 @@ private static boolean signedInAs(String owner) { private static PreferencesDto preferences() { String language = Prefs.language(); return new PreferencesDto( - language.length() == 0 ? com.codenameone.examples.morsel.ui.Lang.current() + language.length() == 0 ? com.codenameone.examples.savor.ui.Lang.current() : language, Prefs.theme(), Prefs.units(), diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Api.java similarity index 86% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Api.java index 9baf3a3e4ae..64041fc7e20 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Api.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Api.java @@ -16,16 +16,16 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.net; +package com.codenameone.examples.savor.net; import com.codename1.io.rest.RestClients; -import com.codenameone.examples.morsel.api.AccountApi; -import com.codenameone.examples.morsel.api.AdminApi; -import com.codenameone.examples.morsel.api.CourierApi; -import com.codenameone.examples.morsel.api.OnboardingApi; -import com.codenameone.examples.morsel.api.GeoApi; -import com.codenameone.examples.morsel.api.PaymentApi; -import com.codenameone.examples.morsel.api.CustomerApi; +import com.codenameone.examples.savor.api.AccountApi; +import com.codenameone.examples.savor.api.AdminApi; +import com.codenameone.examples.savor.api.CourierApi; +import com.codenameone.examples.savor.api.OnboardingApi; +import com.codenameone.examples.savor.api.GeoApi; +import com.codenameone.examples.savor.api.PaymentApi; +import com.codenameone.examples.savor.api.CustomerApi; /// The server, as the app calls it. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Net.java similarity index 97% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Net.java index 075ddbf5848..4a67652ff30 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Net.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Net.java @@ -16,11 +16,11 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.net; +package com.codenameone.examples.savor.net; import com.codename1.io.rest.Response; import com.codename1.util.OnComplete; -import com.codenameone.examples.morsel.ui.Lang; +import com.codenameone.examples.savor.ui.Lang; /// Makes a call through a generated client and sorts out what came back. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Session.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Session.java index 8a69e53026c..479ca66435f 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/net/Session.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/net/Session.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.net; +package com.codenameone.examples.savor.net; import com.codename1.io.ConnectionRequest; import com.codename1.io.JSONParser; @@ -32,9 +32,9 @@ import com.codename1.io.oidc.TokenStore; import com.codename1.ui.CN; import com.codename1.util.SuccessCallback; -import com.codenameone.examples.morsel.AppConfig; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.api.UserDto; +import com.codenameone.examples.savor.AppConfig; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.api.UserDto; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -57,7 +57,7 @@ /// refresh token ran out, or the account was suspended -- it says so, and the /// app goes back to the welcome screen. public final class Session { - private static final String STORE_PREFIX = "morsel"; + private static final String STORE_PREFIX = "savor"; private static final String SESSION_COOKIE = "CN1SESSION"; private static String base; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/notify/Notifications.java similarity index 91% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/notify/Notifications.java index a08d9e7cf45..bb0dbebfe01 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/notify/Notifications.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/notify/Notifications.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.notify; +package com.codenameone.examples.savor.notify; import com.codename1.push.PushClient; import com.codename1.push.PushError; @@ -24,17 +24,17 @@ import com.codename1.push.PushMessage; import com.codename1.push.PushSubscription; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.AppConfig; -import com.codenameone.examples.morsel.api.PushDeviceDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.savor.AppConfig; +import com.codenameone.examples.savor.api.PushDeviceDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Nav; /// Push notifications: the delivery news that reaches a phone in a pocket. /// /// The phone registers with the platform's push service once, and the token -/// it gets is handed to the Morsel server for whoever is signed in. Signing +/// it gets is handed to the Savor server for whoever is signed in. Signing /// out takes it back, so the next person to use the phone is not told about /// the last one's deliveries. The server decides what to send and when; see the /// server's `DeliveryPushes`. @@ -50,7 +50,7 @@ public final class Notifications { private Notifications() { } - // tag::morsel-push-register[] + // tag::savor-push-register[] /// Asks the platform for a push token. Called every time the app comes to /// the foreground; registering is idempotent, and the platform answers /// with the token it already gave when nothing has changed. @@ -93,7 +93,7 @@ public static void signingOut(Runnable then) { Api.account().unregisterPush(device, Net.to(gone -> then.run(), (status, message) -> then.run())); } - // end::morsel-push-register[] + // end::savor-push-register[] /// Whether this build can be sent notifications at all. public static boolean available() { @@ -112,7 +112,7 @@ private static PushDeviceDto device() { CN.getPlatformName()); } - // tag::morsel-push-listener[] + // tag::savor-push-listener[] /// Called on the event thread. A message is about a delivery: the app opens /// the screen that shows it, which reads the delivery from the server. The /// message's own words are for the notification shade, not for the app. @@ -144,5 +144,5 @@ public void onError(PushError error) { + error.getMessage()); } } - // end::morsel-push-listener[] + // end::savor-push-listener[] } diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/CardForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/CardForm.java index 554b4656f82..420edeb8a21 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/CardForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/CardForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.pay; +package com.codenameone.examples.savor.pay; import com.codename1.components.SpanLabel; import com.codename1.ui.CN; @@ -29,13 +29,13 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.GridLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.CardDto; -import com.codenameone.examples.morsel.api.PaymentSetupDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.CardDto; +import com.codenameone.examples.savor.api.PaymentSetupDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; /// Adds a card. /// @@ -98,7 +98,7 @@ private void begin(PaymentSetupDto setup) { private void hosted(final String url) { page.add(Ui.banner(FontImage.MATERIAL_LOCK, "Your card goes straight to the bank", - "The payment provider's own page opens in your browser. Morsel never sees " + "The payment provider's own page opens in your browser. Savor never sees " + "the number. Come back here when you have finished.", "WlBannerIcon")); page.add(error); page.add(Ui.primary("Open the secure page", "openHosted", e -> { diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/Cards.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/Cards.java index e25886424c3..812b203089d 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/Cards.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/Cards.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.pay; +package com.codenameone.examples.savor.pay; /// What a card number, an expiry date and a security code look like. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/ReceiptForm.java similarity index 96% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/ReceiptForm.java index c9287f98681..249083fb27d 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/ReceiptForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/ReceiptForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.pay; +package com.codenameone.examples.savor.pay; import com.codename1.ui.Button; import com.codename1.ui.Container; @@ -28,14 +28,14 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.ReasonDto; -import com.codenameone.examples.morsel.api.ReceiptDto; -import com.codenameone.examples.morsel.api.TipDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.ReasonDto; +import com.codenameone.examples.savor.api.ReceiptDto; +import com.codenameone.examples.savor.api.TipDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; /// What a delivery cost, how it was paid, and -- for the customer -- the chance to /// leave a tip. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/WalletForm.java similarity index 94% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/WalletForm.java index 5c921b52997..50a86514519 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/pay/WalletForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/pay/WalletForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.pay; +package com.codenameone.examples.savor.pay; import com.codename1.ui.Button; import com.codename1.ui.Container; @@ -26,13 +26,13 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.PaymentMethodDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Ui; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.PaymentMethodDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; import java.util.List; /// The ways the customer can pay: their cards, and cash, which is always there. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/BottomSheet.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/BottomSheet.java index 58f27795723..d7d55c97160 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/BottomSheet.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/BottomSheet.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Component; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Chart.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Chart.java index e4aea2cb7f3..3017965d3dc 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Chart.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Chart.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Component; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ConsentForm.java similarity index 93% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ConsentForm.java index 4423e46a911..53a98bb8716 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ConsentForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ConsentForm.java @@ -16,14 +16,14 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.Container; import com.codename1.ui.FontImage; import com.codename1.ui.Form; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; -import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.savor.Telemetry; /// Asks, once on a device, whether the app may report how it is used. /// @@ -39,8 +39,8 @@ private ConsentForm() { public static void show(final Runnable then) { Form form = Ui.form("Usage statistics", "Consent"); Container page = Ui.page(); - Container banner = Ui.banner(FontImage.MATERIAL_INSIGHTS, "Help improve Morsel", - "Morsel can count which screens are opened and which features are used, " + Container banner = Ui.banner(FontImage.MATERIAL_INSIGHTS, "Help improve Savor", + "Savor can count which screens are opened and which features are used, " + "so that we can see what works and fix what does not.", "WlBannerIcon"); // The page has its own top padding, and the banner's on top of it // pushed the last line of the promise below under the answers. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/DeliveryListForm.java similarity index 96% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/DeliveryListForm.java index 6f186db2044..a00685c1430 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/DeliveryListForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/DeliveryListForm.java @@ -16,15 +16,15 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.Container; import com.codename1.ui.FontImage; import com.codename1.ui.Form; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; -import com.codenameone.examples.morsel.api.DeliveryDto; -import com.codenameone.examples.morsel.net.Net; +import com.codenameone.examples.savor.api.DeliveryDto; +import com.codenameone.examples.savor.net.Net; import java.util.List; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Lang.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Lang.java index 5798c454611..16e1bef7676 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Lang.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Lang.java @@ -16,12 +16,12 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.l10n.L10NManager; import com.codename1.ui.plaf.UIManager; import com.codename1.ui.util.Resources; -import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.savor.Prefs; import java.util.HashMap; import java.util.Map; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Layouts.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Layouts.java index 6305eccc554..af3bf8c02ff 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Layouts.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Layouts.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.ui.CN; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Look.java similarity index 96% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Look.java index 8bd00995ebb..4e1679c96c0 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Look.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Look.java @@ -16,11 +16,11 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.plaf.UIManager; -import com.codenameone.examples.morsel.Prefs; +import com.codenameone.examples.savor.Prefs; /// Light or dark. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Menu.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Menu.java index 33d871112e0..10a1412f2a2 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Menu.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Menu.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.Button; import com.codename1.ui.CN; @@ -27,8 +27,8 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.net.Session; /// The menu of a home screen: who is signed in, which of the app's three modes /// this is, the places that mode has, and the way to another mode. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Nav.java similarity index 92% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Nav.java index 2cb7dfbb10d..46ee56d167b 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Nav.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Nav.java @@ -16,26 +16,26 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Container; import com.codename1.ui.Form; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.AppConfig; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.admin.AdminForm; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.courier.ApplyForm; -import com.codenameone.examples.morsel.courier.CourierForm; -import com.codenameone.examples.morsel.courier.Shift; -import com.codenameone.examples.morsel.live.LiveChannel; -import com.codenameone.examples.morsel.merchant.MerchantForm; -import com.codenameone.examples.morsel.net.Account; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.notify.Notifications; -import com.codenameone.examples.morsel.customer.CustomerForm; +import com.codenameone.examples.savor.AppConfig; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.admin.AdminForm; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.courier.ApplyForm; +import com.codenameone.examples.savor.courier.CourierForm; +import com.codenameone.examples.savor.courier.Shift; +import com.codenameone.examples.savor.live.LiveChannel; +import com.codenameone.examples.savor.merchant.MerchantForm; +import com.codenameone.examples.savor.net.Account; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.notify.Notifications; +import com.codenameone.examples.savor.customer.CustomerForm; /// Decides which screen the app is on. /// @@ -265,7 +265,7 @@ public static void changeServer(final String url) { /// There is a session, and no server to use it with. private static void unreachable() { - Form form = Ui.form("Morsel", "Unreachable"); + Form form = Ui.form("Savor", "Unreachable"); Container page = Ui.page(); page.add(Ui.label("No connection", "WlTitle")); page.add(Ui.text(Lang.tr("The server at {0} did not answer.", Session.serverUrl()), diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Photos.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Photos.java index 0c6c13eae24..c229369cd55 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Photos.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Photos.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.capture.Capture; import com.codename1.io.FileSystemStorage; @@ -26,7 +26,7 @@ import com.codename1.ui.Image; import com.codename1.ui.events.ActionEvent; import com.codename1.ui.util.ImageIO; -import com.codenameone.examples.morsel.Telemetry; +import com.codenameone.examples.savor.Telemetry; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ProfileForm.java similarity index 91% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ProfileForm.java index 946ffeb9290..4e182ea3e47 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ProfileForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ProfileForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -26,14 +26,14 @@ import com.codename1.ui.TextArea; import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.api.PlaceDto; -import com.codenameone.examples.morsel.api.ProfileDto; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.net.Account; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.customer.PlaceSearchForm; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.api.PlaceDto; +import com.codenameone.examples.savor.api.ProfileDto; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.net.Account; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.customer.PlaceSearchForm; /// Who the user is to the service: their name, who to tell if something goes /// wrong, and the two places they go most. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Pulse.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Pulse.java index cfeac069004..fbb1df5b4e0 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Pulse.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Pulse.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Component; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/RegisterForm.java similarity index 92% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/RegisterForm.java index 9e5b0fffccf..4dabae8ebb0 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/RegisterForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/RegisterForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -25,12 +25,12 @@ import com.codename1.ui.TextArea; import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.RegisterDto; -import com.codenameone.examples.morsel.AppConfig; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.RegisterDto; +import com.codenameone.examples.savor.AppConfig; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; /// Creates an account, signs in with it and goes on to verify the phone. /// diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ring.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ring.java index f24476b45d7..8cd4637b6f1 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ring.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ring.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Component; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ServerForm.java similarity index 92% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ServerForm.java index 1ae24cafc32..887444130e9 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/ServerForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/ServerForm.java @@ -16,14 +16,14 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.Container; import com.codename1.ui.Form; import com.codename1.ui.TextArea; import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.net.Session; /// Where the server is. A released app has one answer and no use for this /// screen; while you are developing, it is how a phone is pointed at the server @@ -37,7 +37,7 @@ public static void show(Form previous) { Form form = Ui.form("Server address", "Server"); Ui.back(form, previous); Container page = Ui.page(); - page.add(Ui.text("The address of the Morsel server this app talks to. " + page.add(Ui.text("The address of the Savor server this app talks to. " + "Changing it signs you out.", "WlText")); final TextField url = Ui.field(page, "Address", "url", "http://192.168.1.20:8080", TextArea.URL); diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java index 30bc951919f..b0df109c919 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SettingsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -28,16 +28,16 @@ import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; -import com.codenameone.examples.morsel.AppConfig; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.AccountClosureDto; -import com.codenameone.examples.morsel.api.PasswordChangeDto; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.pay.WalletForm; +import com.codenameone.examples.savor.AppConfig; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.AccountClosureDto; +import com.codenameone.examples.savor.api.PasswordChangeDto; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.pay.WalletForm; /// Settings: the account, and everything about the app a person may want /// their own way. @@ -184,11 +184,11 @@ public static void show(final Form previous) { Ui.section(page, "About"); Container about = group(); - about.add(Ui.row(FontImage.MATERIAL_INFO, "Morsel", + about.add(Ui.row(FontImage.MATERIAL_INFO, "Savor", "A sample delivery service built with Codename One", null)); about.add(Ui.tap(Ui.row(FontImage.MATERIAL_DESCRIPTION, "Terms of service", null, chevron()), "terms", e -> legal(form, "Terms of service", - "Morsel is a sample. No real deliveries are booked, no real couriers are " + "Savor is a sample. No real deliveries are booked, no real couriers are " + "sent and no money changes hands.", null))); about.add(Ui.tap(Ui.row(FontImage.MATERIAL_POLICY, "Privacy policy", null, chevron()), "privacy", e -> legal(form, "Privacy policy", diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SignInForm.java similarity index 95% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SignInForm.java index de11e9b8f65..40ad6ca5345 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/SignInForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SignInForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -25,8 +25,8 @@ import com.codename1.ui.TextArea; import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.net.Session; /// E-mail and password. The form is the app's own; what happens when it is /// submitted is in [Session#signIn]. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Skeleton.java similarity index 98% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Skeleton.java index 41d063a0f83..27390289e79 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Skeleton.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Skeleton.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Component; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/StatRow.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/StatRow.java index 3829ac22256..a67f3b09676 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/StatRow.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/StatRow.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.CN; import com.codename1.ui.Container; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java similarity index 99% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java index a7130718710..8901f5bd8bb 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/Ui.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.components.Switch; @@ -45,10 +45,10 @@ import com.codename1.ui.layouts.LayeredLayout; import com.codename1.ui.plaf.Style; import com.codename1.ui.plaf.UIManager; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.Prefs; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.DeliveryState; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.Prefs; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.DeliveryState; import java.util.Date; @@ -711,7 +711,7 @@ public static String state(DeliveryState state) { return "Cancelled by courier"; } if (DeliveryState.CANCELLED_BY_ADMIN == state) { - return "Cancelled by Morsel"; + return "Cancelled by Savor"; } if (DeliveryState.NO_COURIERS == state) { return "No courier found"; diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/VerifyPhoneForm.java similarity index 91% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/VerifyPhoneForm.java index e119b131f6e..2955b919f09 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/VerifyPhoneForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/VerifyPhoneForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.components.SpanLabel; import com.codename1.ui.Button; @@ -25,12 +25,12 @@ import com.codename1.ui.TextArea; import com.codename1.ui.TextField; import com.codename1.ui.layouts.BorderLayout; -import com.codenameone.examples.morsel.Telemetry; -import com.codenameone.examples.morsel.api.PhoneCodeDto; -import com.codenameone.examples.morsel.api.UserDto; -import com.codenameone.examples.morsel.net.Api; -import com.codenameone.examples.morsel.net.Net; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.Telemetry; +import com.codenameone.examples.savor.api.PhoneCodeDto; +import com.codenameone.examples.savor.api.UserDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.net.Session; /// Proves the phone number is the user's, which the server asks for before the /// first delivery. diff --git a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/WelcomeForm.java similarity index 93% rename from scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java rename to scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/WelcomeForm.java index 61d0af7a3b3..0b216e75df3 100644 --- a/scripts/morsel/common/src/main/java/com/codenameone/examples/morsel/ui/WelcomeForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/WelcomeForm.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel.ui; +package com.codenameone.examples.savor.ui; import com.codename1.ui.Container; import com.codename1.ui.FontImage; @@ -24,7 +24,7 @@ import com.codename1.ui.Label; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; -import com.codenameone.examples.morsel.AppConfig; +import com.codenameone.examples.savor.AppConfig; /// What a signed-out user sees. public final class WelcomeForm { @@ -41,7 +41,7 @@ public static void show() { Label icon = new Label("", "WlHeroIcon"); Ui.icon(icon, FontImage.MATERIAL_NEAR_ME, 10f); hero.add(icon); - hero.add(Ui.label("Morsel", "WlHeroTitle")); + hero.add(Ui.label("Savor", "WlHeroTitle")); hero.add(Ui.text("A delivery when you need one, and a fair way to drive.", "WlHeroText")); Container actions = Ui.page(); diff --git a/scripts/morsel/common/src/main/l10n/Bundle.properties b/scripts/savor/common/src/main/l10n/Bundle.properties similarity index 100% rename from scripts/morsel/common/src/main/l10n/Bundle.properties rename to scripts/savor/common/src/main/l10n/Bundle.properties diff --git a/scripts/morsel/common/src/main/l10n/Bundle_de.properties b/scripts/savor/common/src/main/l10n/Bundle_de.properties similarity index 95% rename from scripts/morsel/common/src/main/l10n/Bundle_de.properties rename to scripts/savor/common/src/main/l10n/Bundle_de.properties index 84bb4e612cb..333e1058968 100644 --- a/scripts/morsel/common/src/main/l10n/Bundle_de.properties +++ b/scripts/savor/common/src/main/l10n/Bundle_de.properties @@ -1,4 +1,4 @@ -# Morsel in German. The key of every line is the English text the code +# Savor in German. The key of every line is the English text the code # names; see ui/Lang.java for how a line is found and how to add one. @rtl=false language.name=Deutsch @@ -9,7 +9,7 @@ Live\ map=Live-Karte Deliveries=Fahrten People=Personen Operations=Betrieb -Morsel\ administration=Morsel-Verwaltung +Savor\ administration=Savor-Verwaltung Today=Heute Manage=Verwalten Where\ every\ car\ online\ is=Wo jedes Auto gerade ist @@ -145,7 +145,7 @@ Create\ account=Konto erstellen No\ deliveries\ yet=Noch keine Fahrten {0}\ with\ {1}={0} mit {1} Server=Server -The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=Die Adresse des Morsel-Servers, mit dem diese App spricht. Eine \u00c4nderung meldet dich ab. +The\ address\ of\ the\ Savor\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=Die Adresse des Savor-Servers, mit dem diese App spricht. Eine \u00c4nderung meldet dich ab. Address=Adresse Save=Speichern Account=Konto @@ -183,7 +183,7 @@ Their\ phone\ number=Telefonnummer About=Info A\ sample\ delivery\ service\ built\ with\ Codename\ One=Ein Beispiel-Fahrdienst, gebaut mit Codename One Terms\ of\ service=Nutzungsbedingungen -Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel ist ein Beispiel. Es werden keine echten Fahrten gebucht, keine echten Fahrer geschickt und es flie\u00dft kein Geld. +Savor\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Savor ist ein Beispiel. Es werden keine echten Fahrten gebucht, keine echten Fahrer geschickt und es flie\u00dft kein Geld. Privacy\ policy=Datenschutzerkl\u00e4rung Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Dein Standort wird w\u00e4hrend der Fahrt an den Server gesendet, bei dem du angemeldet bist, und an niemanden sonst. Einstellungen bleiben auf diesem Ger\u00e4t. Sign\ in=Anmelden @@ -267,7 +267,7 @@ By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ doc Calling\ is\ not\ available\ on\ this\ device=Anrufe sind auf diesem Ger\u00e4t nicht m\u00f6glich Cancel\ this\ delivery=Diese Fahrt stornieren Cancelled=Storniert -Cancelled\ by\ Morsel=Von Morsel storniert +Cancelled\ by\ Savor=Von Savor storniert Card\ number=Kartennummer Cash\ out=Auszahlen Change\ password=Passwort \u00e4ndern @@ -296,7 +296,7 @@ Documents=Dokumente Documents\ checked=Dokumente gepr\u00fcft Done=Fertig Draft=Entwurf -Drive\ with\ Morsel=Mit Morsel fahren +Drive\ with\ Savor=Mit Savor fahren Couriers=Fahrer Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Fahrer bewerben sich nach der Registrierung: ein paar Angaben zu dir und deinem Auto und f\u00fcnf Fotos. Bis dahin kannst du mitfahren. Couriers\ online=Fahrer online @@ -316,8 +316,8 @@ Flag\ removed=Markierung entfernt Flagged=Markiert Gender=Geschlecht Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Ein letzter Blick. Bis zum Absenden kannst du alles \u00e4ndern. -Hi,\ this\ is\ your\ Morsel\ courier.=Hallo, hier ist dein Morsel-Fahrer. -Hi,\ this\ is\ your\ Morsel\ customer.=Hallo, hier ist dein Morsel-Fahrgast. +Hi,\ this\ is\ your\ Savor\ courier.=Hallo, hier ist dein Savor-Fahrer. +Hi,\ this\ is\ your\ Savor\ customer.=Hallo, hier ist dein Savor-Fahrgast. History=Verlauf I\ have\ finished=Ich bin fertig I\ want\ to=Ich m\u00f6chte @@ -435,7 +435,7 @@ The\ new\ prices\ are\ in\ force=Die neuen Preise gelten The\ number\ is\ on\ the\ front\ of\ the\ card.=Die Nummer steht auf der Vorderseite der Karte. The\ page\ with\ the\ policy's\ dates\ on\ it=Die Seite mit den Daten der Police The\ paper\ that\ says\ the\ car\ is\ yours=Das Papier, das dich als Halter ausweist -The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=Die Seite des Zahlungsanbieters \u00f6ffnet sich im Browser. Morsel sieht die Nummer nie. Komm hierher zur\u00fcck, wenn du fertig bist. +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Savor\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=Die Seite des Zahlungsanbieters \u00f6ffnet sich im Browser. Savor sieht die Nummer nie. Komm hierher zur\u00fcck, wenn du fertig bist. The\ delivery\ was\ refunded=Die Fahrt wurde erstattet Refund\ pending=Erstattung ausstehend Refund\ failed=Erstattung fehlgeschlagen @@ -519,8 +519,8 @@ Nothing\ selected=Nichts ausgew\u00e4hlt Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=W\u00e4hlen Sie eine Zeile aus der Liste, um sie hier zu sehen. Usage\ statistics=Nutzungsstatistiken Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Teile, welche Bildschirme und Funktionen genutzt werden. Niemals deinen Namen, deine Orte oder deine Zahlungsdaten. -Help\ improve\ Morsel=Hilf mit, Morsel zu verbessern -Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel kann z\u00e4hlen, welche Bildschirme ge\u00f6ffnet und welche Funktionen genutzt werden, damit wir sehen, was funktioniert, und beheben k\u00f6nnen, was nicht funktioniert. +Help\ improve\ Savor=Hilf mit, Savor zu verbessern +Savor\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Savor kann z\u00e4hlen, welche Bildschirme ge\u00f6ffnet und welche Funktionen genutzt werden, damit wir sehen, was funktioniert, und beheben k\u00f6nnen, was nicht funktioniert. No\ personal\ details=Keine pers\u00f6nlichen Daten Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Niemals dein Name, deine E-Mail-Adresse, deine Telefonnummer, deine Orte oder deine Kartendaten Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Jederzeit in den Einstellungen ein- oder ausschalten diff --git a/scripts/morsel/common/src/main/l10n/Bundle_en.properties b/scripts/savor/common/src/main/l10n/Bundle_en.properties similarity index 95% rename from scripts/morsel/common/src/main/l10n/Bundle_en.properties rename to scripts/savor/common/src/main/l10n/Bundle_en.properties index c9adac326b1..73a40207577 100644 --- a/scripts/morsel/common/src/main/l10n/Bundle_en.properties +++ b/scripts/savor/common/src/main/l10n/Bundle_en.properties @@ -1,4 +1,4 @@ -# Morsel in English. The key of every line is the English text the code +# Savor in English. The key of every line is the English text the code # names; see ui/Lang.java for how a line is found and how to add one. @rtl=false language.name=English @@ -9,7 +9,7 @@ Live\ map=Live map Deliveries=Deliveries People=People Operations=Operations -Morsel\ administration=Morsel administration +Savor\ administration=Savor administration Today=Today Manage=Manage Where\ every\ car\ online\ is=Where every car online is @@ -145,7 +145,7 @@ Create\ account=Create account No\ deliveries\ yet=No deliveries yet {0}\ with\ {1}={0} with {1} Server=Server -The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=The address of the Morsel server this app talks to. Changing it signs you out. +The\ address\ of\ the\ Savor\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=The address of the Savor server this app talks to. Changing it signs you out. Address=Address Save=Save Account=Account @@ -183,7 +183,7 @@ Their\ phone\ number=Their phone number About=About A\ sample\ delivery\ service\ built\ with\ Codename\ One=A sample delivery service built with Codename One Terms\ of\ service=Terms of service -Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel is a sample. No real deliveries are booked, no real couriers are sent and no money changes hands. +Savor\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Savor is a sample. No real deliveries are booked, no real couriers are sent and no money changes hands. Privacy\ policy=Privacy policy Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Your position is sent to the server you signed in to while you delivery or drive, and to nobody else. Settings are kept on this device. Sign\ in=Sign in @@ -267,7 +267,7 @@ By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ doc Calling\ is\ not\ available\ on\ this\ device=Calling is not available on this device Cancel\ this\ delivery=Cancel this delivery Cancelled=Cancelled -Cancelled\ by\ Morsel=Cancelled by Morsel +Cancelled\ by\ Savor=Cancelled by Savor Card\ number=Card number Cash\ out=Cash out Change\ password=Change password @@ -296,7 +296,7 @@ Documents=Documents Documents\ checked=Documents checked Done=Done Draft=Draft -Drive\ with\ Morsel=Drive with Morsel +Drive\ with\ Savor=Drive with Savor Couriers=Couriers Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Couriers apply after signing up: a few details about you and your car, and five photos. You can delivery in the meantime. Couriers\ online=Couriers online @@ -316,8 +316,8 @@ Flag\ removed=Flag removed Flagged=Flagged Gender=Gender Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Have a last look. You can change anything until it is handed in. -Hi,\ this\ is\ your\ Morsel\ courier.=Hi, this is your Morsel courier. -Hi,\ this\ is\ your\ Morsel\ customer.=Hi, this is your Morsel customer. +Hi,\ this\ is\ your\ Savor\ courier.=Hi, this is your Savor courier. +Hi,\ this\ is\ your\ Savor\ customer.=Hi, this is your Savor customer. History=History I\ have\ finished=I have finished I\ want\ to=I want to @@ -435,7 +435,7 @@ The\ new\ prices\ are\ in\ force=The new prices are in force The\ number\ is\ on\ the\ front\ of\ the\ card.=The number is on the front of the card. The\ page\ with\ the\ policy's\ dates\ on\ it=The page with the policy's dates on it The\ paper\ that\ says\ the\ car\ is\ yours=The paper that says the car is yours -The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=The payment provider's own page opens in your browser. Morsel never sees the number. Come back here when you have finished. +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Savor\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=The payment provider's own page opens in your browser. Savor never sees the number. Come back here when you have finished. The\ delivery\ was\ refunded=The delivery was refunded Refund\ pending=Refund pending Refund\ failed=Refund failed @@ -519,8 +519,8 @@ Nothing\ selected=Nothing selected Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=Pick a row from the list to see it here. Usage\ statistics=Usage statistics Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Share which screens and features are used. Never your name, places or payment details. -Help\ improve\ Morsel=Help improve Morsel -Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel can count which screens are opened and which features are used, so that we can see what works and fix what does not. +Help\ improve\ Savor=Help improve Savor +Savor\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Savor can count which screens are opened and which features are used, so that we can see what works and fix what does not. No\ personal\ details=No personal details Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Never your name, e-mail, phone number, places or card details Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Turn it on or off at any time in Settings diff --git a/scripts/morsel/common/src/main/l10n/Bundle_es.properties b/scripts/savor/common/src/main/l10n/Bundle_es.properties similarity index 95% rename from scripts/morsel/common/src/main/l10n/Bundle_es.properties rename to scripts/savor/common/src/main/l10n/Bundle_es.properties index 8c9c88365d5..9512061a934 100644 --- a/scripts/morsel/common/src/main/l10n/Bundle_es.properties +++ b/scripts/savor/common/src/main/l10n/Bundle_es.properties @@ -1,4 +1,4 @@ -# Morsel in Spanish. The key of every line is the English text the code +# Savor in Spanish. The key of every line is the English text the code # names; see ui/Lang.java for how a line is found and how to add one. @rtl=false language.name=Espa\u00f1ol @@ -9,7 +9,7 @@ Live\ map=Mapa en vivo Deliveries=Viajes People=Personas Operations=Operaciones -Morsel\ administration=Administraci\u00f3n de Morsel +Savor\ administration=Administraci\u00f3n de Savor Today=Hoy Manage=Gestionar Where\ every\ car\ online\ is=D\u00f3nde est\u00e1 cada coche conectado @@ -145,7 +145,7 @@ Create\ account=Crear cuenta No\ deliveries\ yet=A\u00fan no hay viajes {0}\ with\ {1}={0} con {1} Server=Servidor -The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=La direcci\u00f3n del servidor Morsel que usa esta app. Cambiarla cierra tu sesi\u00f3n. +The\ address\ of\ the\ Savor\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=La direcci\u00f3n del servidor Savor que usa esta app. Cambiarla cierra tu sesi\u00f3n. Address=Direcci\u00f3n Save=Guardar Account=Cuenta @@ -183,7 +183,7 @@ Their\ phone\ number=Su n\u00famero de tel\u00e9fono About=Acerca de A\ sample\ delivery\ service\ built\ with\ Codename\ One=Un servicio de viajes de ejemplo creado con Codename One Terms\ of\ service=Condiciones del servicio -Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel es un ejemplo. No se reservan viajes reales, no se env\u00edan conductores reales y no se mueve dinero. +Savor\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Savor es un ejemplo. No se reservan viajes reales, no se env\u00edan conductores reales y no se mueve dinero. Privacy\ policy=Pol\u00edtica de privacidad Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Tu posici\u00f3n se env\u00eda al servidor en el que iniciaste sesi\u00f3n mientras viajas o conduces, y a nadie m\u00e1s. Los ajustes se guardan en este dispositivo. Sign\ in=Iniciar sesi\u00f3n @@ -267,7 +267,7 @@ By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ doc Calling\ is\ not\ available\ on\ this\ device=Este dispositivo no permite llamar Cancel\ this\ delivery=Cancelar este viaje Cancelled=Cancelados -Cancelled\ by\ Morsel=Cancelado por Morsel +Cancelled\ by\ Savor=Cancelado por Savor Card\ number=N\u00famero de tarjeta Cash\ out=Retirar Change\ password=Cambiar contrase\u00f1a @@ -296,7 +296,7 @@ Documents=Documentos Documents\ checked=Documentos comprobados Done=Listo Draft=Borrador -Drive\ with\ Morsel=Conduce con Morsel +Drive\ with\ Savor=Conduce con Savor Couriers=Conductores Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Los conductores presentan su solicitud tras registrarse: unos datos sobre ti y tu coche, y cinco fotos. Mientras tanto puedes viajar. Couriers\ online=Conductores en l\u00ednea @@ -316,8 +316,8 @@ Flag\ removed=Marca retirada Flagged=Marcada Gender=G\u00e9nero Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Un \u00faltimo vistazo. Puedes cambiar lo que quieras hasta entregarla. -Hi,\ this\ is\ your\ Morsel\ courier.=Hola, soy tu conductor de Morsel. -Hi,\ this\ is\ your\ Morsel\ customer.=Hola, soy tu pasajero de Morsel. +Hi,\ this\ is\ your\ Savor\ courier.=Hola, soy tu conductor de Savor. +Hi,\ this\ is\ your\ Savor\ customer.=Hola, soy tu pasajero de Savor. History=Historial I\ have\ finished=He terminado I\ want\ to=Quiero @@ -435,7 +435,7 @@ The\ new\ prices\ are\ in\ force=Los nuevos precios est\u00e1n en vigor The\ number\ is\ on\ the\ front\ of\ the\ card.=El n\u00famero est\u00e1 en el anverso de la tarjeta. The\ page\ with\ the\ policy's\ dates\ on\ it=La p\u00e1gina con las fechas de la p\u00f3liza The\ paper\ that\ says\ the\ car\ is\ yours=El papel que dice que el coche es tuyo -The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=La p\u00e1gina del proveedor de pagos se abre en tu navegador. Morsel nunca ve el n\u00famero. Vuelve aqu\u00ed cuando termines. +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Savor\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=La p\u00e1gina del proveedor de pagos se abre en tu navegador. Savor nunca ve el n\u00famero. Vuelve aqu\u00ed cuando termines. The\ delivery\ was\ refunded=El viaje fue reembolsado Refund\ pending=Reembolso pendiente Refund\ failed=Reembolso fallido @@ -519,8 +519,8 @@ Nothing\ selected=Nada seleccionado Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=Elige una fila de la lista para verla aqu\u00ed. Usage\ statistics=Estad\u00edsticas de uso Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Comparte qu\u00e9 pantallas y funciones se usan. Nunca tu nombre, tus lugares ni tus datos de pago. -Help\ improve\ Morsel=Ayuda a mejorar Morsel -Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel puede contar qu\u00e9 pantallas se abren y qu\u00e9 funciones se usan, para que podamos ver qu\u00e9 funciona y arreglar lo que no. +Help\ improve\ Savor=Ayuda a mejorar Savor +Savor\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Savor puede contar qu\u00e9 pantallas se abren y qu\u00e9 funciones se usan, para que podamos ver qu\u00e9 funciona y arreglar lo que no. No\ personal\ details=Sin datos personales Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Nunca tu nombre, correo electr\u00f3nico, n\u00famero de tel\u00e9fono, lugares ni datos de tarjeta Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Act\u00edvalo o desact\u00edvalo cuando quieras en Ajustes diff --git a/scripts/morsel/common/src/main/l10n/Bundle_fr.properties b/scripts/savor/common/src/main/l10n/Bundle_fr.properties similarity index 95% rename from scripts/morsel/common/src/main/l10n/Bundle_fr.properties rename to scripts/savor/common/src/main/l10n/Bundle_fr.properties index cbd23551b6c..31fc293478e 100644 --- a/scripts/morsel/common/src/main/l10n/Bundle_fr.properties +++ b/scripts/savor/common/src/main/l10n/Bundle_fr.properties @@ -1,4 +1,4 @@ -# Morsel in French. The key of every line is the English text the code +# Savor in French. The key of every line is the English text the code # names; see ui/Lang.java for how a line is found and how to add one. @rtl=false language.name=Fran\u00e7ais @@ -9,7 +9,7 @@ Live\ map=Carte en direct Deliveries=Courses People=Personnes Operations=Op\u00e9rations -Morsel\ administration=Administration de Morsel +Savor\ administration=Administration de Savor Today=Aujourd'hui Manage=G\u00e9rer Where\ every\ car\ online\ is=O\u00f9 se trouve chaque voiture en ligne @@ -145,7 +145,7 @@ Create\ account=Cr\u00e9er le compte No\ deliveries\ yet=Pas encore de courses {0}\ with\ {1}={0} avec {1} Server=Serveur -The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=L'adresse du serveur Morsel utilis\u00e9 par cette application. La modifier vous d\u00e9connecte. +The\ address\ of\ the\ Savor\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=L'adresse du serveur Savor utilis\u00e9 par cette application. La modifier vous d\u00e9connecte. Address=Adresse Save=Enregistrer Account=Compte @@ -183,7 +183,7 @@ Their\ phone\ number=Son num\u00e9ro de t\u00e9l\u00e9phone About=\u00c0 propos A\ sample\ delivery\ service\ built\ with\ Codename\ One=Un service de courses d'exemple cr\u00e9\u00e9 avec Codename One Terms\ of\ service=Conditions d'utilisation -Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel est un exemple. Aucune course r\u00e9elle n'est r\u00e9serv\u00e9e, aucun chauffeur r\u00e9el n'est envoy\u00e9 et aucun argent n'est \u00e9chang\u00e9. +Savor\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Savor est un exemple. Aucune course r\u00e9elle n'est r\u00e9serv\u00e9e, aucun chauffeur r\u00e9el n'est envoy\u00e9 et aucun argent n'est \u00e9chang\u00e9. Privacy\ policy=Politique de confidentialit\u00e9 Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=Votre position est envoy\u00e9e au serveur auquel vous \u00eates connect\u00e9 pendant vos courses, et \u00e0 personne d'autre. Les r\u00e9glages restent sur cet appareil. Sign\ in=Se connecter @@ -267,7 +267,7 @@ By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ doc Calling\ is\ not\ available\ on\ this\ device=Les appels ne sont pas disponibles sur cet appareil Cancel\ this\ delivery=Annuler cette course Cancelled=Annul\u00e9es -Cancelled\ by\ Morsel=Annul\u00e9e par Morsel +Cancelled\ by\ Savor=Annul\u00e9e par Savor Card\ number=Num\u00e9ro de carte Cash\ out=Encaisser Change\ password=Changer le mot de passe @@ -296,7 +296,7 @@ Documents=Documents Documents\ checked=Documents v\u00e9rifi\u00e9s Done=Termin\u00e9 Draft=Brouillon -Drive\ with\ Morsel=Conduire avec Morsel +Drive\ with\ Savor=Conduire avec Savor Couriers=Chauffeurs Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=Les chauffeurs postulent apr\u00e8s l'inscription : quelques informations sur vous et votre voiture, et cinq photos. En attendant, vous pouvez voyager. Couriers\ online=Chauffeurs en ligne @@ -316,8 +316,8 @@ Flag\ removed=Signalement retir\u00e9 Flagged=Signal\u00e9 Gender=Genre Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=Un dernier coup d'\u0153il. Vous pouvez tout modifier jusqu'\u00e0 l'envoi. -Hi,\ this\ is\ your\ Morsel\ courier.=Bonjour, c'est votre chauffeur Morsel. -Hi,\ this\ is\ your\ Morsel\ customer.=Bonjour, c'est votre passager Morsel. +Hi,\ this\ is\ your\ Savor\ courier.=Bonjour, c'est votre chauffeur Savor. +Hi,\ this\ is\ your\ Savor\ customer.=Bonjour, c'est votre passager Savor. History=Historique I\ have\ finished=J'ai termin\u00e9 I\ want\ to=Je veux @@ -435,7 +435,7 @@ The\ new\ prices\ are\ in\ force=Les nouveaux tarifs sont en vigueur The\ number\ is\ on\ the\ front\ of\ the\ card.=Le num\u00e9ro figure au recto de la carte. The\ page\ with\ the\ policy's\ dates\ on\ it=La page indiquant les dates de la police The\ paper\ that\ says\ the\ car\ is\ yours=Le document attestant que la voiture est \u00e0 vous -The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=La page du prestataire de paiement s'ouvre dans votre navigateur. Morsel ne voit jamais le num\u00e9ro. Revenez ici une fois termin\u00e9. +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Savor\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=La page du prestataire de paiement s'ouvre dans votre navigateur. Savor ne voit jamais le num\u00e9ro. Revenez ici une fois termin\u00e9. The\ delivery\ was\ refunded=La course a \u00e9t\u00e9 rembours\u00e9e Refund\ pending=Remboursement en attente Refund\ failed=Remboursement \u00e9chou\u00e9 @@ -519,8 +519,8 @@ Nothing\ selected=Aucune s\u00e9lection Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=Choisissez une ligne de la liste pour l'afficher ici. Usage\ statistics=Statistiques d'utilisation Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=Partagez quels \u00e9crans et quelles fonctions sont utilis\u00e9s. Jamais votre nom, vos lieux ni vos donn\u00e9es de paiement. -Help\ improve\ Morsel=Aidez \u00e0 am\u00e9liorer Morsel -Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel peut compter quels \u00e9crans sont ouverts et quelles fonctions sont utilis\u00e9es, pour que nous puissions voir ce qui marche et corriger ce qui ne marche pas. +Help\ improve\ Savor=Aidez \u00e0 am\u00e9liorer Savor +Savor\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Savor peut compter quels \u00e9crans sont ouverts et quelles fonctions sont utilis\u00e9es, pour que nous puissions voir ce qui marche et corriger ce qui ne marche pas. No\ personal\ details=Aucune donn\u00e9e personnelle Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=Jamais votre nom, votre e-mail, votre num\u00e9ro de t\u00e9l\u00e9phone, vos lieux ni les donn\u00e9es de votre carte Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=Activez ou d\u00e9sactivez-les \u00e0 tout moment dans R\u00e9glages diff --git a/scripts/morsel/common/src/main/l10n/Bundle_he.properties b/scripts/savor/common/src/main/l10n/Bundle_he.properties similarity index 95% rename from scripts/morsel/common/src/main/l10n/Bundle_he.properties rename to scripts/savor/common/src/main/l10n/Bundle_he.properties index d5e8a0530de..8c3fed222f1 100644 --- a/scripts/morsel/common/src/main/l10n/Bundle_he.properties +++ b/scripts/savor/common/src/main/l10n/Bundle_he.properties @@ -1,4 +1,4 @@ -# Morsel in Hebrew. The key of every line is the English text the code +# Savor in Hebrew. The key of every line is the English text the code # names; see ui/Lang.java for how a line is found and how to add one. @rtl=true language.name=\u05e2\u05d1\u05e8\u05d9\u05ea @@ -9,7 +9,7 @@ Live\ map=\u05de\u05e4\u05d4 \u05d7\u05d9\u05d4 Deliveries=\u05e0\u05e1\u05d9\u05e2\u05d5\u05ea People=\u05d0\u05e0\u05e9\u05d9\u05dd Operations=\u05ea\u05e4\u05e2\u05d5\u05dc -Morsel\ administration=\u05e0\u05d9\u05d4\u05d5\u05dc Morsel +Savor\ administration=\u05e0\u05d9\u05d4\u05d5\u05dc Savor Today=\u05d4\u05d9\u05d5\u05dd Manage=\u05e0\u05d9\u05d4\u05d5\u05dc Where\ every\ car\ online\ is=\u05d4\u05d9\u05db\u05df \u05e0\u05de\u05e6\u05d0 \u05db\u05dc \u05e8\u05db\u05d1 \u05de\u05d7\u05d5\u05d1\u05e8 @@ -145,7 +145,7 @@ Create\ account=\u05e6\u05d5\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df No\ deliveries\ yet=\u05d0\u05d9\u05df \u05e2\u05d3\u05d9\u05d9\u05df \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea {0}\ with\ {1}={0} \u05e2\u05dd {1} Server=\u05e9\u05e8\u05ea -The\ address\ of\ the\ Morsel\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=\u05db\u05ea\u05d5\u05d1\u05ea \u05e9\u05e8\u05ea Morsel \u05e9\u05d4\u05d0\u05e4\u05dc\u05d9\u05e7\u05e6\u05d9\u05d4 \u05e4\u05d5\u05e0\u05d4 \u05d0\u05dc\u05d9\u05d5. \u05e9\u05d9\u05e0\u05d5\u05d9 \u05e9\u05dc\u05d4 \u05d9\u05e0\u05ea\u05e7 \u05d0\u05d5\u05ea\u05da. +The\ address\ of\ the\ Savor\ server\ this\ app\ talks\ to.\ Changing\ it\ signs\ you\ out.=\u05db\u05ea\u05d5\u05d1\u05ea \u05e9\u05e8\u05ea Savor \u05e9\u05d4\u05d0\u05e4\u05dc\u05d9\u05e7\u05e6\u05d9\u05d4 \u05e4\u05d5\u05e0\u05d4 \u05d0\u05dc\u05d9\u05d5. \u05e9\u05d9\u05e0\u05d5\u05d9 \u05e9\u05dc\u05d4 \u05d9\u05e0\u05ea\u05e7 \u05d0\u05d5\u05ea\u05da. Address=\u05db\u05ea\u05d5\u05d1\u05ea Save=\u05e9\u05de\u05d5\u05e8 Account=\u05d7\u05e9\u05d1\u05d5\u05df @@ -183,7 +183,7 @@ Their\ phone\ number=\u05de\u05e1\u05e4\u05e8 \u05d4\u05d8\u05dc\u05e4\u05d5\u05 About=\u05d0\u05d5\u05d3\u05d5\u05ea A\ sample\ delivery\ service\ built\ with\ Codename\ One=\u05e9\u05d9\u05e8\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05dc\u05d3\u05d5\u05d2\u05de\u05d4 \u05e9\u05e0\u05d1\u05e0\u05d4 \u05e2\u05dd Codename One Terms\ of\ service=\u05ea\u05e0\u05d0\u05d9 \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea -Morsel\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Morsel \u05d4\u05d9\u05d0 \u05d3\u05d5\u05d2\u05de\u05d4. \u05dc\u05d0 \u05de\u05d5\u05d6\u05de\u05e0\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05d0\u05de\u05d9\u05ea\u05d9\u05d5\u05ea, \u05dc\u05d0 \u05e0\u05e9\u05dc\u05d7\u05d9\u05dd \u05e0\u05d4\u05d2\u05d9\u05dd \u05d0\u05de\u05d9\u05ea\u05d9\u05d9\u05dd \u05d5\u05db\u05e1\u05e3 \u05d0\u05d9\u05e0\u05d5 \u05de\u05d7\u05dc\u05d9\u05e3 \u05d9\u05d3\u05d9\u05d9\u05dd. +Savor\ is\ a\ sample.\ No\ real\ deliveries\ are\ booked,\ no\ real\ couriers\ are\ sent\ and\ no\ money\ changes\ hands.=Savor \u05d4\u05d9\u05d0 \u05d3\u05d5\u05d2\u05de\u05d4. \u05dc\u05d0 \u05de\u05d5\u05d6\u05de\u05e0\u05d5\u05ea \u05e0\u05e1\u05d9\u05e2\u05d5\u05ea \u05d0\u05de\u05d9\u05ea\u05d9\u05d5\u05ea, \u05dc\u05d0 \u05e0\u05e9\u05dc\u05d7\u05d9\u05dd \u05e0\u05d4\u05d2\u05d9\u05dd \u05d0\u05de\u05d9\u05ea\u05d9\u05d9\u05dd \u05d5\u05db\u05e1\u05e3 \u05d0\u05d9\u05e0\u05d5 \u05de\u05d7\u05dc\u05d9\u05e3 \u05d9\u05d3\u05d9\u05d9\u05dd. Privacy\ policy=\u05de\u05d3\u05d9\u05e0\u05d9\u05d5\u05ea \u05e4\u05e8\u05d8\u05d9\u05d5\u05ea Your\ position\ is\ sent\ to\ the\ server\ you\ signed\ in\ to\ while\ you\ delivery\ or\ drive,\ and\ to\ nobody\ else.\ Settings\ are\ kept\ on\ this\ device.=\u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05dc\u05da \u05e0\u05e9\u05dc\u05d7 \u05dc\u05e9\u05e8\u05ea \u05e9\u05d0\u05dc\u05d9\u05d5 \u05d4\u05ea\u05d7\u05d1\u05e8\u05ea \u05d1\u05d6\u05de\u05df \u05e0\u05e1\u05d9\u05e2\u05d4 \u05d0\u05d5 \u05e0\u05d4\u05d9\u05d2\u05d4, \u05d5\u05dc\u05d0\u05e3 \u05d0\u05d7\u05d3 \u05d0\u05d7\u05e8. \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e0\u05e9\u05de\u05e8\u05d5\u05ea \u05d1\u05de\u05db\u05e9\u05d9\u05e8 \u05d6\u05d4. Sign\ in=\u05d4\u05ea\u05d7\u05d1\u05e8 @@ -267,7 +267,7 @@ By\ submitting\ you\ confirm\ that\ all\ of\ this\ is\ true\ and\ that\ the\ doc Calling\ is\ not\ available\ on\ this\ device=\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05ea\u05e7\u05e9\u05e8 \u05de\u05de\u05db\u05e9\u05d9\u05e8 \u05d6\u05d4 Cancel\ this\ delivery=\u05d1\u05d9\u05d8\u05d5\u05dc \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 Cancelled=\u05d1\u05d5\u05d8\u05dc\u05d5 -Cancelled\ by\ Morsel=\u05d1\u05d5\u05d8\u05dc\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 Morsel +Cancelled\ by\ Savor=\u05d1\u05d5\u05d8\u05dc\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 Savor Card\ number=\u05de\u05e1\u05e4\u05e8 \u05db\u05e8\u05d8\u05d9\u05e1 Cash\ out=\u05de\u05e9\u05d9\u05db\u05d4 Change\ password=\u05e9\u05d9\u05e0\u05d5\u05d9 \u05e1\u05d9\u05e1\u05de\u05d4 @@ -296,7 +296,7 @@ Documents=\u05de\u05e1\u05de\u05db\u05d9\u05dd Documents\ checked=\u05d4\u05de\u05e1\u05de\u05db\u05d9\u05dd \u05e0\u05d1\u05d3\u05e7\u05d9\u05dd Done=\u05e1\u05d9\u05d5\u05dd Draft=\u05d8\u05d9\u05d5\u05d8\u05d4 -Drive\ with\ Morsel=\u05dc\u05e0\u05d4\u05d5\u05d2 \u05e2\u05dd Morsel +Drive\ with\ Savor=\u05dc\u05e0\u05d4\u05d5\u05d2 \u05e2\u05dd Savor Couriers=\u05e0\u05d4\u05d2\u05d9\u05dd Couriers\ apply\ after\ signing\ up\:\ a\ few\ details\ about\ you\ and\ your\ car,\ and\ five\ photos.\ You\ can\ delivery\ in\ the\ meantime.=\u05e0\u05d4\u05d2\u05d9\u05dd \u05de\u05d2\u05d9\u05e9\u05d9\u05dd \u05d1\u05e7\u05e9\u05d4 \u05dc\u05d0\u05d7\u05e8 \u05d4\u05d4\u05e8\u05e9\u05de\u05d4: \u05db\u05de\u05d4 \u05e4\u05e8\u05d8\u05d9\u05dd \u05e2\u05dc\u05d9\u05da \u05d5\u05e2\u05dc \u05d4\u05e8\u05db\u05d1, \u05d5\u05d7\u05de\u05e9 \u05ea\u05de\u05d5\u05e0\u05d5\u05ea. \u05d1\u05d9\u05e0\u05ea\u05d9\u05d9\u05dd \u05d0\u05e4\u05e9\u05e8 \u05dc\u05e0\u05e1\u05d5\u05e2. Couriers\ online=\u05e0\u05d4\u05d2\u05d9\u05dd \u05de\u05d7\u05d5\u05d1\u05e8\u05d9\u05dd @@ -316,8 +316,8 @@ Flag\ removed=\u05d4\u05e1\u05d9\u05de\u05d5\u05df \u05d4\u05d5\u05e1\u05e8 Flagged=\u05de\u05e1\u05d5\u05de\u05df Gender=\u05de\u05d2\u05d3\u05e8 Have\ a\ last\ look.\ You\ can\ change\ anything\ until\ it\ is\ handed\ in.=\u05de\u05d1\u05d8 \u05d0\u05d7\u05e8\u05d5\u05df. \u05d0\u05e4\u05e9\u05e8 \u05dc\u05e9\u05e0\u05d5\u05ea \u05d4\u05db\u05d5\u05dc \u05e2\u05d3 \u05dc\u05d4\u05d2\u05e9\u05d4. -Hi,\ this\ is\ your\ Morsel\ courier.=\u05e9\u05dc\u05d5\u05dd, \u05de\u05d3\u05d1\u05e8 \u05d4\u05e0\u05d4\u05d2 \u05e9\u05dc\u05da \u05de-Morsel. -Hi,\ this\ is\ your\ Morsel\ customer.=\u05e9\u05dc\u05d5\u05dd, \u05de\u05d3\u05d1\u05e8 \u05d4\u05e0\u05d5\u05e1\u05e2 \u05e9\u05dc\u05da \u05de-Morsel. +Hi,\ this\ is\ your\ Savor\ courier.=\u05e9\u05dc\u05d5\u05dd, \u05de\u05d3\u05d1\u05e8 \u05d4\u05e0\u05d4\u05d2 \u05e9\u05dc\u05da \u05de-Savor. +Hi,\ this\ is\ your\ Savor\ customer.=\u05e9\u05dc\u05d5\u05dd, \u05de\u05d3\u05d1\u05e8 \u05d4\u05e0\u05d5\u05e1\u05e2 \u05e9\u05dc\u05da \u05de-Savor. History=\u05d4\u05d9\u05e1\u05d8\u05d5\u05e8\u05d9\u05d4 I\ have\ finished=\u05e1\u05d9\u05d9\u05de\u05ea\u05d9 I\ want\ to=\u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6\u05d4 @@ -435,7 +435,7 @@ The\ new\ prices\ are\ in\ force=\u05d4\u05de\u05d7\u05d9\u05e8\u05d9\u05dd \u05 The\ number\ is\ on\ the\ front\ of\ the\ card.=\u05d4\u05de\u05e1\u05e4\u05e8 \u05de\u05d5\u05e4\u05d9\u05e2 \u05d1\u05d7\u05d6\u05d9\u05ea \u05d4\u05db\u05e8\u05d8\u05d9\u05e1. The\ page\ with\ the\ policy's\ dates\ on\ it=\u05d4\u05e2\u05de\u05d5\u05d3 \u05e9\u05d1\u05d5 \u05de\u05d5\u05e4\u05d9\u05e2\u05d9\u05dd \u05ea\u05d0\u05e8\u05d9\u05db\u05d9 \u05d4\u05e4\u05d5\u05dc\u05d9\u05e1\u05d4 The\ paper\ that\ says\ the\ car\ is\ yours=\u05d4\u05de\u05e1\u05de\u05da \u05e9\u05de\u05e2\u05d9\u05d3 \u05e9\u05d4\u05e8\u05db\u05d1 \u05e9\u05dc\u05da -The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Morsel\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=\u05d3\u05e3 \u05e1\u05e4\u05e7 \u05d4\u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7 \u05d1\u05d3\u05e4\u05d3\u05e4\u05df. Morsel \u05d0\u05d9\u05e0\u05d4 \u05e8\u05d5\u05d0\u05d4 \u05d0\u05ea \u05d4\u05de\u05e1\u05e4\u05e8. \u05d7\u05d6\u05e8\u05d5 \u05dc\u05db\u05d0\u05df \u05d1\u05e1\u05d9\u05d5\u05dd. +The\ payment\ provider's\ own\ page\ opens\ in\ your\ browser.\ Savor\ never\ sees\ the\ number.\ Come\ back\ here\ when\ you\ have\ finished.=\u05d3\u05e3 \u05e1\u05e4\u05e7 \u05d4\u05ea\u05e9\u05dc\u05d5\u05de\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7 \u05d1\u05d3\u05e4\u05d3\u05e4\u05df. Savor \u05d0\u05d9\u05e0\u05d4 \u05e8\u05d5\u05d0\u05d4 \u05d0\u05ea \u05d4\u05de\u05e1\u05e4\u05e8. \u05d7\u05d6\u05e8\u05d5 \u05dc\u05db\u05d0\u05df \u05d1\u05e1\u05d9\u05d5\u05dd. The\ delivery\ was\ refunded=\u05e0\u05d9\u05ea\u05df \u05d4\u05d7\u05d6\u05e8 \u05e2\u05dc \u05d4\u05e0\u05e1\u05d9\u05e2\u05d4 Refund\ pending=\u05d4\u05d7\u05d6\u05e8 \u05de\u05de\u05ea\u05d9\u05df Refund\ failed=\u05d4\u05d7\u05d6\u05e8 \u05e0\u05db\u05e9\u05dc @@ -519,8 +519,8 @@ Nothing\ selected=\u05dc\u05d0 \u05e0\u05d1\u05d7\u05e8 \u05d3\u05d1\u05e8 Pick\ a\ row\ from\ the\ list\ to\ see\ it\ here.=\u05d1\u05d7\u05e8\u05d5 \u05e9\u05d5\u05e8\u05d4 \u05de\u05d4\u05e8\u05e9\u05d9\u05de\u05d4 \u05db\u05d3\u05d9 \u05dc\u05e8\u05d0\u05d5\u05ea \u05d0\u05d5\u05ea\u05d4 \u05db\u05d0\u05df. Usage\ statistics=\u05e0\u05ea\u05d5\u05e0\u05d9 \u05e9\u05d9\u05de\u05d5\u05e9 Share\ which\ screens\ and\ features\ are\ used.\ Never\ your\ name,\ places\ or\ payment\ details.=\u05e9\u05d9\u05ea\u05d5\u05e3 \u05d4\u05de\u05e1\u05db\u05d9\u05dd \u05d5\u05d4\u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05e9\u05d1\u05e9\u05d9\u05de\u05d5\u05e9. \u05dc\u05e2\u05d5\u05dc\u05dd \u05dc\u05d0 \u05d4\u05e9\u05dd \u05e9\u05dc\u05da, \u05d4\u05de\u05e7\u05d5\u05de\u05d5\u05ea \u05e9\u05dc\u05da \u05d0\u05d5 \u05e4\u05e8\u05d8\u05d9 \u05d4\u05ea\u05e9\u05dc\u05d5\u05dd. -Help\ improve\ Morsel=\u05e2\u05d6\u05e8\u05d5 \u05dc\u05e9\u05e4\u05e8 \u05d0\u05ea Morsel -Morsel\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Morsel \u05d9\u05db\u05d5\u05dc\u05d4 \u05dc\u05e1\u05e4\u05d5\u05e8 \u05d0\u05d9\u05dc\u05d5 \u05de\u05e1\u05db\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7\u05d9\u05dd \u05d5\u05d1\u05d0\u05d9\u05dc\u05d5 \u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd, \u05db\u05d3\u05d9 \u05e9\u05e0\u05d5\u05db\u05dc \u05dc\u05e8\u05d0\u05d5\u05ea \u05de\u05d4 \u05e2\u05d5\u05d1\u05d3 \u05d5\u05dc\u05ea\u05e7\u05df \u05d0\u05ea \u05de\u05d4 \u05e9\u05dc\u05d0. +Help\ improve\ Savor=\u05e2\u05d6\u05e8\u05d5 \u05dc\u05e9\u05e4\u05e8 \u05d0\u05ea Savor +Savor\ can\ count\ which\ screens\ are\ opened\ and\ which\ features\ are\ used,\ so\ that\ we\ can\ see\ what\ works\ and\ fix\ what\ does\ not.=Savor \u05d9\u05db\u05d5\u05dc\u05d4 \u05dc\u05e1\u05e4\u05d5\u05e8 \u05d0\u05d9\u05dc\u05d5 \u05de\u05e1\u05db\u05d9\u05dd \u05e0\u05e4\u05ea\u05d7\u05d9\u05dd \u05d5\u05d1\u05d0\u05d9\u05dc\u05d5 \u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd, \u05db\u05d3\u05d9 \u05e9\u05e0\u05d5\u05db\u05dc \u05dc\u05e8\u05d0\u05d5\u05ea \u05de\u05d4 \u05e2\u05d5\u05d1\u05d3 \u05d5\u05dc\u05ea\u05e7\u05df \u05d0\u05ea \u05de\u05d4 \u05e9\u05dc\u05d0. No\ personal\ details=\u05dc\u05dc\u05d0 \u05e4\u05e8\u05d8\u05d9\u05dd \u05d0\u05d9\u05e9\u05d9\u05d9\u05dd Never\ your\ name,\ e-mail,\ phone\ number,\ places\ or\ card\ details=\u05dc\u05e2\u05d5\u05dc\u05dd \u05dc\u05d0 \u05d4\u05e9\u05dd \u05e9\u05dc\u05da, \u05d4\u05d3\u05d5\u05d0"\u05dc, \u05de\u05e1\u05e4\u05e8 \u05d4\u05d8\u05dc\u05e4\u05d5\u05df, \u05d4\u05de\u05e7\u05d5\u05de\u05d5\u05ea \u05d0\u05d5 \u05e4\u05e8\u05d8\u05d9 \u05d4\u05db\u05e8\u05d8\u05d9\u05e1 Turn\ it\ on\ or\ off\ at\ any\ time\ in\ Settings=\u05d0\u05e4\u05e9\u05e8 \u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05d0\u05d5 \u05dc\u05db\u05d1\u05d5\u05ea \u05d1\u05db\u05dc \u05e2\u05ea \u05d1\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea diff --git a/scripts/morsel/common/src/main/resources/surfaces.json b/scripts/savor/common/src/main/resources/surfaces.json similarity index 100% rename from scripts/morsel/common/src/main/resources/surfaces.json rename to scripts/savor/common/src/main/resources/surfaces.json diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/Actor.java similarity index 98% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/Actor.java index 4f806095bce..79389d7e793 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/Actor.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/Actor.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.io.CharArrayReader; import com.codename1.io.ConnectionRequest; @@ -24,7 +24,7 @@ import com.codename1.io.NetworkManager; import com.codename1.io.RequestAuthorizer; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.net.Session; import java.io.IOException; import java.util.HashMap; diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java similarity index 97% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java index ebe3269ce19..4bc71269086 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminDesktopTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java @@ -16,10 +16,10 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.ui.CN; -import com.codenameone.examples.morsel.ui.Layouts; +import com.codenameone.examples.savor.ui.Layouts; /// The admin console in a desktop window: the places down the side instead of /// in a drawer, and a list beside what was picked from it. @@ -116,10 +116,10 @@ protected boolean run() throws Exception { // An application opens beside the list it was picked from. click("nav-applications"); until(() -> findByName("Applications") != null, 20000, "the applications"); - click("application-applicant@morsel.example"); + click("application-applicant@savor.example"); until(() -> findByName("ApplicationReview") != null, 20000, "the application"); untilText("applicationStatus", "Waiting"); - assertTrue(has("application-applicant@morsel.example"), + assertTrue(has("application-applicant@savor.example"), "the list is still there beside the application"); assertTrue(leftOf("Applications", "ApplicationReview"), "the application is beside the list"); diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminModerationTest.java similarity index 97% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminModerationTest.java index 310fcaf088b..5b8e9a8be1a 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminModerationTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminModerationTest.java @@ -16,12 +16,12 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; /// Flagging an account and blocking one, from the admin's screens, and what /// each does to the person it is done to. public class AdminModerationTest extends E2e { - private static final String OTHER_CUSTOMER = "customer2@morsel.example"; + private static final String OTHER_CUSTOMER = "customer2@savor.example"; @Override protected boolean run() throws Exception { diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java similarity index 98% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java index 16c612be5c1..0bf516dd148 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AdminTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.components.Switch; import com.codename1.ui.Component; @@ -55,7 +55,7 @@ protected boolean run() throws Exception { until(() -> has("alert-applications"), 20000, "the dashboard to say one is waiting"); click("applications"); waitForForm("Applications"); - click("application-applicant@morsel.example"); + click("application-applicant@savor.example"); waitForForm("ApplicationReview"); untilText("applicationStatus", "Waiting"); click("document-licence"); diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java similarity index 99% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java index e4913a5613e..c15ff64e5ff 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/AnalyticsTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.analytics.AbstractAnalyticsProvider; import com.codename1.analytics.AnalyticsCrashReport; diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java similarity index 98% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java index 775b5c3c60b..b89d92c7389 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierModeTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.ui.Button; diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java similarity index 97% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java index d6a14feffaa..eb6a7b404b6 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/CourierOnboardingTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java @@ -16,10 +16,10 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Photos; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Photos; /// Becoming a courier, start to finish: an account that asks to drive, the /// application with its photographs, an admin's approval, and the courier's @@ -33,7 +33,7 @@ protected boolean run() throws Exception { // One address per pass, not per run: the server is new for every run, both // passes share it, and a timestamp in the address wrapped differently from // run to run in the screens that are compared. - final String email = "dana." + (desktopRun() ? "desk" : "phone") + "@morsel.example"; + final String email = "dana." + (desktopRun() ? "desk" : "phone") + "@savor.example"; Photos.useSource(camera -> photograph()); click("register"); diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java similarity index 98% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java index c960708550b..d264e5ffe3e 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/DeliveryFlowTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import java.util.Map; diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java similarity index 94% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java index 2a07e8f0ff1..4048defb992 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/E2e.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.components.SpanLabel; import com.codename1.io.FileSystemStorage; @@ -41,15 +41,15 @@ import com.codename1.ui.spinner.Picker; import com.codename1.ui.util.ImageIO; import com.codename1.util.AsyncResource; -import com.codenameone.examples.morsel.live.LiveChannel; -import com.codenameone.examples.morsel.map.Locator; -import com.codenameone.examples.morsel.map.Maps; -import com.codenameone.examples.morsel.map.Routes; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Lang; -import com.codenameone.examples.morsel.ui.Look; -import com.codenameone.examples.morsel.ui.Nav; -import com.codenameone.examples.morsel.ui.Photos; +import com.codenameone.examples.savor.live.LiveChannel; +import com.codenameone.examples.savor.map.Locator; +import com.codenameone.examples.savor.map.Maps; +import com.codenameone.examples.savor.map.Routes; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Look; +import com.codenameone.examples.savor.ui.Nav; +import com.codenameone.examples.savor.ui.Photos; import java.io.InputStream; import java.io.OutputStream; @@ -65,10 +65,10 @@ /// map draws from tiles bundled with the tests, a route is the straight line, /// and the device stands at a fixed point. None of it touches the server. public abstract class E2e extends AbstractTest { - public static final String PASSWORD = "morsel-demo"; - public static final String CUSTOMER = "customer@morsel.example"; - public static final String COURIER = "courier@morsel.example"; - public static final String ADMIN = "admin@morsel.example"; + public static final String PASSWORD = "savor-demo"; + public static final String CUSTOMER = "customer@savor.example"; + public static final String COURIER = "courier@savor.example"; + public static final String ADMIN = "admin@savor.example"; /// Where the device stands: the middle of the bundled tiles. public static final LatLng HERE = new LatLng(37.8080, -122.4120); @@ -93,7 +93,7 @@ public int getTimeoutMillis() { /// works in. A test belongs to one of the two -- see [#desktop()] -- and /// passes without doing anything in the other. static boolean desktopRun() { - return "true".equals(System.getProperty("morsel.e2e.desktop")); + return "true".equals(System.getProperty("savor.e2e.desktop")); } /// Whether this test is one for the desktop window. The phone's are not. @@ -356,8 +356,8 @@ protected static Image photograph() { /// Captures the screen as `name`.png and holds it against the golden of /// the same name. /// - /// `morsel.shots.dir` is where the capture goes; without it nothing is - /// captured, which is a run from an IDE. `morsel.goldens.dir` is where + /// `savor.shots.dir` is where the capture goes; without it nothing is + /// captured, which is a run from an IDE. `savor.goldens.dir` is where /// the goldens are. A capture with no golden passes -- that is how a new /// one is made: run, look at it, copy it over. /// @@ -366,7 +366,7 @@ protected static Image photograph() { /// Text is drawn a little differently from one machine to the next, and /// this is here to notice a screen that changed, not a glyph. protected void shot(String name) { - String out = fileUrl(System.getProperty("morsel.shots.dir")); + String out = fileUrl(System.getProperty("savor.shots.dir")); if (out == null) { return; } @@ -382,7 +382,7 @@ protected void shot(String name) { } finally { png.close(); } - String goldens = fileUrl(System.getProperty("morsel.goldens.dir")); + String goldens = fileUrl(System.getProperty("savor.goldens.dir")); String golden = goldens == null ? "" : goldens + "/" + name + ".png"; if (golden.length() == 0 || !files.exists(golden)) { log("No golden for " + name + "; captured only"); diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java similarity index 99% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java index 1db091a0f2a..e78cf8345c6 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/PaymentsTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java @@ -16,7 +16,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import java.util.Map; diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/RegisterTest.java similarity index 96% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/RegisterTest.java index c13959f746e..77525276632 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/RegisterTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/RegisterTest.java @@ -16,9 +16,9 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; -import com.codenameone.examples.morsel.net.Session; +import com.codenameone.examples.savor.net.Session; /// A new account: register, be signed in, verify the phone, arrive. public class RegisterTest extends E2e { @@ -27,7 +27,7 @@ protected boolean run() throws Exception { // One address per pass, not per run: the server is new for every run, both // passes share it, and a timestamp in the address wrapped differently from // run to run in the screens that are compared. - String email = "new." + (desktopRun() ? "desk" : "phone") + "@morsel.example"; + String email = "new." + (desktopRun() ? "desk" : "phone") + "@savor.example"; click("register"); waitForForm("Register"); type("name", "Nico Newcomer"); diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java similarity index 97% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java index 577cd0d8a3f..9d9d8a30196 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SettingsTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java @@ -16,9 +16,9 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; -import com.codenameone.examples.morsel.net.Account; +import com.codenameone.examples.savor.net.Account; import java.util.Map; /// The settings: a language and a theme that change the app on the spot, and diff --git a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java similarity index 96% rename from scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java rename to scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java index dee3991fe36..7ea88f785de 100644 --- a/scripts/morsel/common/src/test/java/com/codenameone/examples/morsel/SignInTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java @@ -16,11 +16,11 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -package com.codenameone.examples.morsel; +package com.codenameone.examples.savor; import com.codename1.ui.Button; -import com.codenameone.examples.morsel.net.Session; -import com.codenameone.examples.morsel.ui.Nav; +import com.codenameone.examples.savor.net.Session; +import com.codenameone.examples.savor.ui.Nav; /// Signing in, and what a role does and does not open. public class SignInTest extends E2e { diff --git a/scripts/morsel/pom.xml b/scripts/savor/pom.xml similarity index 98% rename from scripts/morsel/pom.xml rename to scripts/savor/pom.xml index 50830ab81be..14e10d66f64 100644 --- a/scripts/morsel/pom.xml +++ b/scripts/savor/pom.xml @@ -1,11 +1,11 @@ 4.0.0 - com.codenameone.examples.morsel - morsel + com.codenameone.examples.savor + savor 1.0-SNAPSHOT pom - morsel - morsel + savor + savor https://www.codenameone.com @@ -32,7 +32,7 @@ 1.8 1.8 1.8 - morsel + savor diff --git a/scripts/morsel/run-e2e.sh b/scripts/savor/run-e2e.sh similarity index 80% rename from scripts/morsel/run-e2e.sh rename to scripts/savor/run-e2e.sh index fa37f293605..0e87af7ab66 100755 --- a/scripts/morsel/run-e2e.sh +++ b/scripts/savor/run-e2e.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# The end-to-end tests: starts the Morsel server on the `test` profile, then +# The end-to-end tests: starts the Savor server on the `test` profile, then # runs the app's test suite against it in the Codename One simulator. # # run-e2e.sh [--native] [--prebuilt] @@ -20,8 +20,8 @@ set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" MVN="${MVN:-mvn}" -PORT="${MORSEL_PORT:-18080}" -LOG="${MORSEL_SERVER_LOG:-$HERE/target/e2e-server.log}" +PORT="${SAVOR_PORT:-18080}" +LOG="${SAVOR_SERVER_LOG:-$HERE/target/e2e-server.log}" SHOTS="$HERE/target/e2e-shots" MODE="--jvm" BUILD=1 @@ -29,7 +29,7 @@ for arg in "$@"; do case "$arg" in --native) MODE="--native" ;; --prebuilt) BUILD=0 ;; - *) echo "[morsel-e2e] unknown option: $arg" >&2; exit 2 ;; + *) echo "[savor-e2e] unknown option: $arg" >&2; exit 2 ;; esac done @@ -37,12 +37,12 @@ mkdir -p "$HERE/target" rm -rf "$SHOTS" mkdir -p "$SHOTS" -echo "[morsel-e2e] java: $(java -version 2>&1 | head -1)" +echo "[savor-e2e] java: $(java -version 2>&1 | head -1)" if [ "$BUILD" = 1 ]; then "$HERE/backend/server.sh" build "$MODE" fi -echo "[morsel-e2e] starting the server on port $PORT" +echo "[savor-e2e] starting the server on port $PORT" CN1_PROFILE=test "$HERE/backend/server.sh" run "$MODE" --prebuilt --port "$PORT" >"$LOG" 2>&1 & SERVER_PID=$! cleanup() { @@ -63,7 +63,7 @@ for _ in $(seq 1 90); do sleep 1 done if [ "$ready" != "1" ]; then - echo "[morsel-e2e] the server did not start; its log:" >&2 + echo "[savor-e2e] the server did not start; its log:" >&2 cat "$LOG" >&2 || true exit 1 fi @@ -78,7 +78,7 @@ fi # The heap is given a ceiling too. Left alone a JVM takes a quarter of the # machine's memory before it collects anything, which on a shared runner is # enough to have the kernel end it between two tests. -export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS:-} -Xmx768m -Dmorsel.server.url=http://localhost:$PORT -Dmorsel.shots.dir=$SHOTS -Dmorsel.goldens.dir=$HERE/screenshots" +export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS:-} -Xmx768m -Dsavor.server.url=http://localhost:$PORT -Dsavor.shots.dir=$SHOTS -Dsavor.goldens.dir=$HERE/screenshots" set +e ${RUNNER[@]+"${RUNNER[@]}"} "$MVN" -B -f "$HERE/pom.xml" -Dcodename1.platform=javase -Ptest verify @@ -108,24 +108,24 @@ if [ "$STATUS" -eq 0 ] && [ "$(uname -s)" = "Linux" ]; then XML - echo "[morsel-e2e] the desktop pass" + echo "[savor-e2e] the desktop pass" # The last two are the project's own build hints, from # common/codenameone_settings.properties. A packaged desktop app is given # them by its launcher; a test run starts the simulator in # common/target/cn1-reports, from where it does not find the project's # settings, so it has to be told. - export JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Dmorsel.e2e.desktop=true -Djava.util.prefs.userRoot=$PREFS -Dcodename1.arg.nativeTheme=native -Dcodename1.arg.desktop.titleBar=native" + export JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Dsavor.e2e.desktop=true -Djava.util.prefs.userRoot=$PREFS -Dcodename1.arg.nativeTheme=native -Dcodename1.arg.desktop.titleBar=native" set +e ${RUNNER[@]+"${RUNNER[@]}"} "$MVN" -B -f "$HERE/pom.xml" -Dcodename1.platform=javase -Ptest verify STATUS=$? set -e elif [ "$STATUS" -eq 0 ]; then - echo "[morsel-e2e] not on Linux: the desktop pass was skipped" + echo "[savor-e2e] not on Linux: the desktop pass was skipped" fi if [ "$STATUS" -ne 0 ]; then - echo "[morsel-e2e] the tests failed; the end of the server's log:" >&2 + echo "[savor-e2e] the tests failed; the end of the server's log:" >&2 tail -n 80 "$LOG" >&2 || true exit "$STATUS" fi -echo "[morsel-e2e] passed; captures are in $SHOTS" +echo "[savor-e2e] passed; captures are in $SHOTS" diff --git a/scripts/morsel/run-web-e2e.sh b/scripts/savor/run-web-e2e.sh similarity index 79% rename from scripts/morsel/run-web-e2e.sh rename to scripts/savor/run-web-e2e.sh index 7416b683076..79ee7bd8c28 100755 --- a/scripts/morsel/run-web-e2e.sh +++ b/scripts/savor/run-web-e2e.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# The web app, end to end: builds the Morsel server and the browser build of +# The web app, end to end: builds the Savor server and the browser build of # the app, starts the server with the app staged where it serves it from, and # drives the hosted app in a headless browser (web-e2e.mjs). # @@ -20,8 +20,8 @@ set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" -PORT="${MORSEL_WEB_PORT:-18081}" -OUT="${MORSEL_WEB_OUT:-$HERE/target/web-e2e}" +PORT="${SAVOR_WEB_PORT:-18081}" +OUT="${SAVOR_WEB_OUT:-$HERE/target/web-e2e}" LOG="$OUT/server.log" MODE="--jvm" PREBUILT=0 @@ -29,28 +29,28 @@ for arg in "$@"; do case "$arg" in --native) MODE="--native" ;; --prebuilt) PREBUILT=1 ;; - *) echo "[morsel-web] unknown argument $arg" >&2; exit 2 ;; + *) echo "[savor-web] unknown argument $arg" >&2; exit 2 ;; esac done if ! command -v node >/dev/null 2>&1; then - echo "[morsel-web] node is not on the PATH; the browser is driven with Playwright" >&2 + echo "[savor-web] node is not on the PATH; the browser is driven with Playwright" >&2 exit 2 fi rm -rf "$OUT" mkdir -p "$OUT" -echo "[morsel-web] java: $(java -version 2>&1 | head -1)" +echo "[savor-web] java: $(java -version 2>&1 | head -1)" if [ "$PREBUILT" = 0 ]; then "$HERE/backend/server.sh" build "$MODE" --web fi if [ ! -f "$HERE/backend/target/webapp/index.html" ]; then - echo "[morsel-web] no web app is staged in backend/target/webapp" >&2 + echo "[savor-web] no web app is staged in backend/target/webapp" >&2 exit 1 fi -echo "[morsel-web] starting the server on port $PORT" +echo "[savor-web] starting the server on port $PORT" CN1_PROFILE=test "$HERE/backend/server.sh" run "$MODE" --prebuilt --port "$PORT" >"$LOG" 2>&1 & SERVER_PID=$! cleanup() { @@ -71,13 +71,13 @@ for _ in $(seq 1 90); do sleep 1 done if [ "$ready" != "1" ]; then - echo "[morsel-web] the server did not start; its log:" >&2 + echo "[savor-web] the server did not start; its log:" >&2 cat "$LOG" >&2 || true exit 1 fi fail() { - echo "[morsel-web] FAILED: $*" >&2 + echo "[savor-web] FAILED: $*" >&2 exit 1 } @@ -100,10 +100,10 @@ STATUS=$? set -e if [ "$STATUS" -ne 0 ]; then - echo "[morsel-web] the end of the browser's log:" >&2 + echo "[savor-web] the end of the browser's log:" >&2 tail -n 40 "$OUT/browser.log" >&2 || true - echo "[morsel-web] the end of the server's log:" >&2 + echo "[savor-web] the end of the server's log:" >&2 tail -n 40 "$LOG" >&2 || true exit "$STATUS" fi -echo "[morsel-web] passed; screenshots are in $OUT" +echo "[savor-web] passed; screenshots are in $OUT" diff --git a/scripts/morsel/shared/pom.xml b/scripts/savor/shared/pom.xml similarity index 90% rename from scripts/morsel/shared/pom.xml rename to scripts/savor/shared/pom.xml index 9fced81ac51..b6e5791cf5d 100644 --- a/scripts/morsel/shared/pom.xml +++ b/scripts/savor/shared/pom.xml @@ -2,15 +2,15 @@ 4.0.0 - com.codenameone.examples.morsel - morsel + com.codenameone.examples.savor + savor 1.0-SNAPSHOT - com.codenameone.examples.morsel - morsel-shared + com.codenameone.examples.savor + savor-shared 1.0-SNAPSHOT jar - morsel-shared + savor-shared rwiBPt8Q= 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 R9fnH14D=r8R%^S!{{tpeT$TU; literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-admin-trends.png b/docs/developer-guide/img/savor-admin-trends.png new file mode 100644 index 0000000000000000000000000000000000000000..d51bb7d6d6c9bc67e3a4d906d9067c8046e80044 GIT binary patch literal 69564 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 BGt2-0 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-admin-user.png b/docs/developer-guide/img/savor-admin-user.png new file mode 100644 index 0000000000000000000000000000000000000000..963cee0bda8d152ba5cc6c114f888e7ef1ba0927 GIT binary patch literal 105948 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

5d==jIZT0fcmPvMyu$QE+fPRysL= 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 nt7y3>kb&Q?wqP#=-DU1w^EENV zK7ZtFrLTQ`2U380{Sk8ju|LbPOb262uA`!BZE z5E_uMd1x$f3Kvj+;l1~Z>Qos*BnA;{%tCE&unWT^zQ;@N@%tZ=_1 zrS2;%Whvu*=|~bvrOI^++!2Fl>wV3;Zid*a_k3#UJ01y43L5)f6oxB7Pj|*7O+0$4 znHxhKnYAeRCXbt5{gmh&{UdUKxVNy~6(5}FMGy3eLdBq?7YNB{0a}duh!WePGjq$br0d!%KB^3&OUC<1!vG+ zA$#&;d{o84Q)%^U7O}d;$;pVUT%BmJ&a}T)>=sAi z2@tKR8_hK-5S;q{oOjpMVH10Vvqq`5Yq0HYkc+L%mxx-mU$bHzoX_{*(AH#Ie}{m| z=JObA?TW|30~0d+LS3UQLiYU~r#4?jD)Hg<{+uS?!|RmOUW+Y7F@fx_Y5>pgghc2ert+#F>I%`!2p_bco8 z#F#3*43jvXu0}Xu&ib^Rt^akzd?c-K5cSeeDgWYflP=#lD&W1}EzC_{jxqWSx}JHt zm5B=Qb=jeUr|hFvk1OZ59+h(FMh4qAhsiJ1rR4IAE0mTNd;gZ=iBWPX^}`z={CpLY`UMNZ z{w9;hXR|`9VRf~y#7!k@C6%z-$tY65lKc!^5 zM&gmod|#V>rOgF+lGPT<^5UfU?%ldOZv@>p0-STdfW=XDWO`=<;kPEgHyt)9(#?T> zx@G%r!_oy-LE0`h>EzS}95(A3EW$m*v6!h#L7%r7?28f(hpQ7ICr;(mP?yU!V?iLl z4I0+Y4t*X{i)G3z-KWgV2w1}bh#k?RIl)Gf#vaAGYE$DEh0F>B^F@bRKy|V8bsx|= zxm5X7X^hpzk{1BR&vDnD%I{Q0<|&iR(9}##+ipg4t4wMW7xt{=L%wd_8FiRokAoa< zjg2Pud4c|QDJu{DNs^DlqW?-6;;|lcWK^}cIz<{Ua~O@)4VPG-dhgd3&}1f|mmw>S z6H!qy>iIhm1?xW%-be8QbagWc5`7w2f4)WkOtxAL3AtRYwCHPdkxHazYhA>FtEk)s z0XkM%2h8MWewIr=vq?DkNQtps!d7ozXBD)j6MuXS^R&1TwM0ThT@R<;Zt?CDk1q^> zhr}d0ft9LVGneagH7(1@$tLRYU8Es61ZqUjo&JCagOj}3!=y2r*YYX4pN1)<%)}Lr zsXAD9y>ls@e>1mTZ*^OPSL$d8V~|`)uo{(O>?FgofPf2WKjZQ8Cz=iAfqKO;D}t2(yR% zj&Y>JVB|V4P>Rc6=e0zi`EZ%0mTf^+gypVfl6!E{DHfhw26$vjZ^?*9T^Z%D-ZWgS z(6gOQjB)J0+94{LRHh1EaX8Wz7luWYpUP+kR(UQDs9H~#e^;NdtnNe=9$o>c1NLL) zRu?O^$ue<&CsGENhfytPJYH;Al3}bGIy{}u#PAvI#F3d6pH$zSE=@^eSkY3iD<|QK zLmLpky4NcTg4DyHikh`xGShzoq|S@y^mExyF;Gz0^~VnB#$|v}(ZX-Go2};dj_wnM zC)Sw0zjsDl-`&@g)w!;f6CdAN;7n`me3T<{ButSyid*=^Y#|=8(fpP?Sc99K1Pmbi zF-*o-?5hjL(P`9Dz2m~J;Ss>hd?zddESeGR$muy1*uz$-xQ@x?A z;f5Q~2bzBxL4_&y8Y=~7&Jy`{`s$Ge)B8y`A)6dxOWlfJAjq5rjWNN1Z3(_tQ-3Yl z0vHx~K9#YZk~(Eg<2wCDbrtHt(>{cxin)*4Xi6`L`mw!O>FFTnu%v343{TsS+)F75 z*QkA(6SZy*>n=fk4FcBTx?*1^)@AWT9Z;uMZ^-1K@UlPcKk%dC2sAD(U z9<;u>3W)6XH#t5_(|pX~Qy9rBfZ!k>LKuyXa_cWv>DcPJJ&SFXYGrw?4kw&0(+|!{ zQ2YVvmR9AuHSDIy=+NK@l3$&%gP}bh&T1m6fEU-;N3x~nVKQY$G6#@#S{U#8(5R0)}G=} z`c-7tdRZhzUH*3ooPk!vQG(s;tIHNzJeLs=@}ys0<{n&<8Rr^{JO@zrNHV->SMk2o zb!((N;Ff~aZnUn$l6iaj#W)AmvkUI1C@2Swwx1n)u*47+qK4hCvQ zryhKuUHpf#rGjFpoh|Q~3Q#s7*e&pSAAwl8Hx$J*HJ)PdDvPt}JrRHC|7H1^%Fw)xG3-E|UItAtNw_puN#57a@qR4)g%sVo~yh90|sWcljxS!vwnS8ltZH=u3 ziCcHjW}bArm86vdBS{GKCn2WS>s^EF-#Zrf z<#@ZB3QLs)sF_D@T>OHc&F&PIsqiwIq4urEaDiC?c%>ax|& ztwA4xqEV{NDVhnR%Qbv=v9Px4S~HZf#OEy-8T8;k&a_<0t8e?V_UO%UdfR-KB4Ltk zHJ_76YkB@Vx72%i&tU%~-Q%Hy-~744Sy1;qZ8`Lq5~uH95fJm09f0me9vsA?<& zgkTY6fgeT{9b{|5zec_P@FAbh#63LSiP(1-gS?A#oXq=(2ReM=M zW#lOdKpmkQ9-^>g3Q-~J4inGvk+h@v4;kB@03ueAkI*saG*QG!88fu~pE~S5@-|bA zufy-wMIWC*(+T_W(C0`%<=Dr0MA)UG-JZ6YE4!m$m_UUI1#SMm&+?7*Vg#kp#j3iZ4G-0rD2ql+))Ydr8=dG|)5ES!s2z_AiSBeZo#e}m z@dcgY$?mbJ=>*o%<^rFEOD?rUnm#R7wvLe|2{IaGgq|)ES}U0amLJIzcLvK_O0_xM zmtJG|f8z^#yFN+uLB}ogq4cj2cME3Ci;#Y8e45}Vgnb(BgT6yJ9QG7RQOtz&a}m>m z>+zs@ZyNk6JmZBBYu*1Kcl!Ep{)f~Uv-hW4$!?6_BaYu?M1Tm+rpIE)z6Z4YR~KUn zL#Y_)3gE=;+1H474Wid+%$q0KACMJaU1)n}6r3F%$bd66MmU`Q2@u6} zWWEu=dAm4QFF2Q^o`*ufSylwMGUljCZVau}iw(0t;|#hBG36mr>X%ASMYCC+Qzacm z&lRv@B?K+Eg(y*xcoY3|@8&A1ryDF8edzDHRS7 zIj;x_TEX7C@BT>X^AK~Eq?Zv&27vr${C!r*L~lu>SP6sEf^8meM_!&2pg*?bsDW}X zRlsimA|ET~?}EA)=uU@vH8}(un=%TN-w~K`NO#eI&Gh4|QBpxCT9A~wgQ7ySL`&xC z%+8KWa=D254@K(v2xz$M_}YPyc0yr`ux{%0qtNNL&KsMVq{G_Bb^ioegE=vrPnJod zry+}EQ(Q+FPdVk{Z!}IqK%0+oo!XlZ01U4a3-L_9aghS>!DHnaAc=X+R25_9xC41B z0s%cs&S%Rb^9zGRi@u#)xEV3Qvv}4ZAtI-0s!cT0h>&PCH0R$?42){rdf(xevL&u@ zngazfFcKczd(q#wa|6^MA1F6TVwvMa<8897S;P?##DZrA;#Dn*h;7GyXW5A!!5RC`x5M&w*wjs^8MNeulhJp3Zi%0icX-h zt~KfDT`DK?&-E|cAq>vw?-kMAh<>@hBUUr~O9;G})~=!TuFe|mOo=}Y*%3JyNU++| zxoYkkK7$?{J6^C2dC;rAxD>sk7#2a1_|l$B*oyr9OdAe^uF+;~nGK(#oAhV-kXWjJ ztamqp+fvp>(pB6&A+TK_{jMpzB(cJeGGg_rHR{We=mX^^A!=(dAxXmF=GSk>ttT{daEfB+WJL*{awbiM1a6Dy*RG65r2GSqz5G+{a zQ(^^~;r1Xzxy+)Q;H&y_arZso?VrQM!v2RQG-}I=UoLev`FKUHYM{5D=S$~+=PzB? zp-H}8f?bMT5^%te{-Qce^Qv|oj$sqNW?N1`ZMLhep4&%aB01fv%V3|^j1eckxOSw` zw&+k01tZ2ch4lv5AN@wAK+gj;r=V~Pt!}vJNIyJ^W})_~W98*j#wIDWp)o~|0V}}1 zi%wxJ5e|fCt_13pj!$Uu%)Aeybd^%nX z;s};^SKI=h1_H@ZC!NE#L8vwmx*JnJjR*b7OI)_JiFXES%xMcrzZgQj6AmJ|Q4N_b z4(`48I>@z#oa3Z3K;CwaI`d&;9PK<+h?V(#a2D-XX`J%TB*vsb}l~)JP+595=EM{ZC29*@nk= zERB!`VgHc?38Q*J!VA*-qiBMOMlAB>;%4_-DL3GWl$7>@YF2>JP*mgIT@%8G1gD&g&N&{Dub{6sdfug567#G|Y_+SbgR-49+lEk9K zplLGb>_#sDoaVF!0h$Up?uB5Ioh0^?@R8z>KTC4d=O#xxFucU*Pkxr6FLY!LepsZt z5igP7gYWNfYIIzcm^etm$z2zaO2mq(qhN3^7Cfs|^Ym{LcZ8ATL;Iu?MSqpo68z?4 z!uUQ&B7_%`_A>ZF*d{Cn8mFOmZbpqT$JC;nZ$meg}RFmvv=) zJ5|jGkW0}|3cVswMy7o}J2A(l(vsmMK$exYpqTx-WAIdytk-xN$eB+SPhoCNu4Nu+ zMFQktJ!l%9eo4p*#pFeDrMR}1OvK%Mu;jy45>Ei^U?yJbRBBprFlr#V294wP1I`!T z{CvFo*g7f^VU{1cWn`342*;qIQJHoP6KygQvbQ#=NqSv4Jny)pyxL93g~Ggmm+&zV z?sT)MP3SRx%u;*?6|^0E%3;B@Yep|6+*%|jEx<{K#^Yz)|C+zSyM4Uga!Xt!^)Qd= z8HM(=;$U`O8mnS59n?!sF?k{^9|e$p?2kxW(ZJoUs+iQ}3<`0A>9?Hmi8B@NrL&-f zvA#8xLofe&ay{rOb@Bz=rkYUq5&{A7SRtuE#n72EG{2T4AN)3f^4^dp^Ff=+yJ!V` zEjHNYlfr`fC2HrR)zJNp8&Zg3?OD`5N5Gpm_ILbpH3pq_;9&YQw#g1e!a9eboWgP*Lip~0W5}j)@DqB( z;O!Bl`e5p4Gs%F!J|uYL;OwHv^ktV?JwC!7I{0C7uLaB>UFPvc>_5^T-uJGus4mfp zejF`acpO~Ld6RWxzr`YRWt>NLI7=&&`X*pb`jxgn4xTs-#ZWUj;ES9jrka18Fnp|| zet|&`j~0v2(8cJ+e4`lJ?l{mn@@B^ozj?K{J;=;RkQw7g(teo4i`~@TbgCvBgi!(| zj6sf&w055ge5Jkgn)x2QZBOv@fM)3(K3`m0H8WLP<$wUQ)Ho0-KaV*FDv;&>UdJK& zl>OOqbEU1O*KtTR_<4Kzp*#Bmk^XJ^wWED17{0UaMxu-(OejS~l!pavxkH7`RlS zX^B1D;d!+Uv>_uane2>>lA7-X_~f}kHzV%00Yo|px>X7qcH1iVEydOt+XMdR3B~;yt=cQRM@qc2LV8H7WU~$+)G*kja^R^El9HX(AX1OO~ z?&kgay7#($>xfjr#*D$cozVE~IP;Ck(j23KSCN$P`VvG&ywLQE&4~#X03;o=U6j8R z+`WocCa!-exP(xshQFt(#{QLcB7*-6`HRi9Q{IUA51ac>hTV!5{{H~&L{J&YI=J#> zK9Zz|yj3M0VLMJ!!zGGuL0y^KkHY9&Jf8c%;68|5T zsaq?}DJ38?OuT0n>7(&&i?2O7__m%tv;4bsmP_ofGOFtisb(dhjlfN`#)o!vu zduatd#d92GSgC6xOIW33v-x3^pj}Ilz_&b$=9t-RZhkjqJSBF2`*o}v&(nNiGoeFK z9sDqNr4;MC5~U;IVSok}NKV#iYU_C;&%flTVj7QR+;o zO1;|{Mq&(q;mv;NVa0`BiTyq!s0B-;T8#*UHp{V93B7dU=J&*fPN$ojyZF%_4{7X)tp#B(dvXv-?0hxglJj&D*MNVq3u`Ft(dd^(47%fYbSWouo)< z-AXJBW;>I^WfQE0+StP9K^=f+6!p^sGkFq&V7p-IZQ%zcnT-f%8qGP|7 zqORVbBr%dGh;ayx|MqCAQo|PZau$}geOPaa8+1X9S2E_$}Rv0caU~k4(H-3YM}1^M^9d&`xh}KaVvbARuzXyQ~inG&*CP z;^P=IcD^)-Q~kjF=yh7vAd}jSD2NpThfcwoD_(KwuHX(4Lj4_<(CB(S`z2~NBo&tt zXwsJ8PCd|0XzGUt)j)h%P|sahy5)Il?Qr@~scdZR? zJqM&1^5Q(s6)V_U#`{epeVsYVl+>R{<4BaQ=(0}!>3$;HsgP`JvS8{SB%S-V*eHin zCqOgS>O`O}3jm6|kP?NT8ML}FT=tihR9w5J;jx${<5ZE#iGL|M9nX@*>0?^oUhw}7 zuVKWb^lC?Nh<`mMg5ty@;B{PH<$yxM{Be#dNs7$(A;hb6`Gl~=NEAY@9QWwKx5tEWYna8!vYCYSJDwkD2 zK&QpZfu%E7SSHM_ta-Uhp8q0%s_6YeXL;+l<1Xvx-M7zdB4N1BZ2zksT11>rjZl2G zT7h6r_JK7EG%o%$eUr~4RC9@KC_0-u7NiG9mRu?OE?(i|F#Az=&G%!e%tVGq9^lIh z?Q4rmglSe~rMm!HpP5qP^21eA6S|vvZUy#BKU2tHS0mumD z=OG=|7MiT-6n-I$sb-~6MYV2+>0h$Wp0&+pyER{vE7S-HE+M4jitfS56nOO2XG@K3|Fr0F;`^ct$3)2Xq4>0U30} zf#xrbyfYW;bc|B4QimZ{a>$MX+d3ipB7)XJkXGEfIO=SNf6=mM0nif@o{%#q`ictY zI|%BKR^Eve8yBydpF6sKkjdOQ{SB8=^!m%O@Nbrl1NbI) zd$n|-Q}z5{^_9REcbT1o$DCfei=iutT`z_kklplr#Y6d=bx!%QN|!K{oJcbt`HDhX z@naQsGax}pLu1@JtlyIcOj2NUgAl1*n8kfuGJdht;m@8hfh>k#raDS9@#|dMWrrBG@3`<^&5&Pg5rXDoeBJw`CFvmD7d(|YTY)A ztaP?9cci4Xn6{q3cnfM^Pk%*E!{3~*cXL?5KusvQ%TmQM3DHP|>_-D?Ze^pOkH*JS z=O&(NjObW;lSI=p3=_35mGUdZ3BH#ztikqY`pU`QOe{1Jx;~_DUz)r}Mp9FiE?>lZ zkjS>;@xdeZa=~ur#Kff$FRUZPf3NNYBA0P^*scG^0$7fxUw4ve2KkL($oYY!=8)b?~Q zP6KYBjPN+@{3%f#mMNwHcRrr+>-1`*KwS!{r$cG zIHQY_LBKZPC?jD34=J@Q8P1moNm42Sps_U5YX@|l=M{F56d$~ z>}UgQZt$D!Zn=N}odXMgqZ3vZ1$<|qOHk6pL0&<)BaO*AO74(T7XRnxS%;b9cL`_u z)>wzAahUN`LsTjzsXS8ZBUbE4*0@2VsUN)fudAoeFVF8+t(Pm7I$f_9uDn79q>R8R- z?-fjx^e5>e3e!eZg!7jN3d#ni$NE0hvbsUFw8W>}AU-YkZ#y3>)d1en0FMfLdxwg$ z(_-Qrm?uzB6UhLO*v7QBi*`IwY7`VW#I!4cU?erotCgJdE5(`c6$U-6%;#Ot9bVFqXi&UMw2gz$Yxj= z!JNzje_2*((i}~G(|2HaKysUphJQRMSI5?#WtX(MQ{?gfvJ>1~4Sh~*Br6CABMkhk zDbA>%oqxuDHeG94=4^vY4?-Gg{+Sxo5<^5MF%Lb6u;0 zBC~hmK*VFfg2aR^*rW1ZzH73SoM<9Htfpy^P|5a`rKj_zBI5BIUZ`jpx)Px}mJMP? zN2NPZ{8;u}!#?(h#*-%?lEK(Yq|A9`u(K{!=Cz}G{MgAugqkqJ3Zi6T$(Qgd zE4L&i5K?0xq*cM0&sbOFwY|XQP$}}@C}dxxVP%yiC$bPFUzMsJcze6G$Bo0)867EM znVmy0Hbd^n{o<7vn`Q7a5S4!lbIS}ghTn){EPjtH6Oj%)pp3_q$kA)YG)SR*s399! zy`8fZ6GXw}1sjcliq3S44-EGR{ev$j6+~l+Dh&h1hnfl>%A1Kj>{uZhB$f`jRR}O9 zvnen+LE71C^?$y_6UWqN1(haw+gNp6%l53OJt=rT#2hsOKzUf0n~Qm`cOAqL)4|3~u@H0g9N`n4x-@1#K5f)x zE%JTX@YyniI~lkcN3~D2)@8F&fra^gq9_LK`^d6M04uHt@zqa=a9X95KScHp{ofxz z#q>*HC`GWT{a-qPYl#w=MGqO6r1*sB z-x~dYqXNj>^YAaX-VzcLKKOHH|215k5UgNZ=nZbS%TET>Ow!ugdUk#tw;AKd`&MM0q`_9JlB^_``-f?zm>y`p`AXjK(QsI9ciU#a({@fXy|B99Wn_Q~Cf%d=0WdjHJ z1~zi@pWYTku|owWm(Qy-`8z-Uj+ZEKJQaT21^qYv@xbKvsMd5c|CL4o96nFse2)Jr zlkqos{0~db|4QTkJHybhJ97W43@I>qhN$+K^1q{h73#}Onmzpo;s0)1&_3y^R?>f^ zDg2!bf#SA*XCCk;&cDh3|EB+4tp6_`b(nj%p8nx7zQGuao;DUN8y^}PW!j^oc zRc@_<%8v6JT%uKremp1S5n$DJ6^#DlJh@E-D^2 z1(p(*z1Yb;Gld%)rdmk;y;A}aTNB%85?uB0#q3l;QH$(qPlhj4H;jG#j8@9OvsKUq zf}0RSkUB=6F}L~Zt7e9CK*gk|Vy_5sn*e6FZ4?u!w0L^h2d#GaR$NgdcV8m-GW~?s zf7@IPjG{I=3Mz?uber~Kl+QAg)Cd#YVojKJ?KxPgQYCY~5F)CV+&_sakaMu3>(f}% z>(@&6VMOvW|D?m&N_xq#rNokmasSFr%Ky9Ax>H1bfr-|pZKmxg;z-ta01GxF)qI8# zrt+-PWB;voOl~koN5XS*1Q%k+45FXeGdrWU+fqxrq8|0||1C)YV!>58Y8{6{xm(52|`-<|&x1%{b5qn>w~Y8&XUSE#&Nz&3q8 z{l7B60zu3<&E0ZTy0MhucITYxp*Jxxe&n$F{8LMMb%_6~d|brDZ$H}q_Xin>jGO~M zGs%eP0;&Ic8bG3NxB=M=3EY%HtGZ18d+#uc17B>c;@ryV|7=y=KO_XPW`A4?RsP!m zBM9KOXjDMbfc4Kt6yXfqm?9tW=U@JLt^GeU6FSy&=+iE21-=>ul4X%rc!UlqFIi{< zcgY+A;0}5MEh*v_C`(Ss2?LGhd@#F-7zG{_pyIy~`iNt>dbLDsu+xK@iF?sm{4K2e zhj4dI+s;Hqjx7iGLv8 z{B{BZ{j{Crmt&@px$4>g4_=C-$0|l9=L+_AIR=UxMd;mS7K>rNTgX3btm4@NtKmVN zV8daqwL5iLz*sD7g*4KO5Lvo<^ZYl)M^0~F_Cmx3v6OvVgU3s*>>6-i-ux4?O5+b- z*d?Wjd-R!OuMOf@`K8r(Sv3t+`Xb$<9#6*73!gM11H~Pg%0N^V>N;?dbM}3mSIK^M zv9=|9iZEV0dT9_JJtW91?iy=4u5p^%;uw~CC8#@A$UApv5;AG*S9-H*_ZkTU4){ce zgHN4IBtZqU#!Fzvf85r&DgW%$MI90wDR_p}VA}m2J1bsFX%#ct>#k{c%NZ&Y-M#-a zRO-P8T#;_#hFRDb4wu>gp%HP%^-ld{v?-&-cd?7GGkSjo{4Hl+~Q|BC~wyJ zl)4Q1(&Fl7>C44t5+Sdm-VCkVRSnU!14+ z5THzmQ)ys62ou%34xkO~+u!FkALuxdiL|GxmM7i=>6*k5c=R>>22Z!r^1fhyvZfrW z5S>6{ldWHHn;XRzf62f9nn|-YFswctZ)KDM&*oP%~m*Jzdc86 zZhjU+<`R*-%u2uA`jT=`C>z+g(=&pKf5Y<8Y}|%hV%iCkOFv>N`*x& zViGeRwZda@kTl9z`;Cl^q-CK|-P{nL4P{T#;;tvy$05Ijlo)F9qnbV zJUk%ElPAT*=;y|Z@u(#&zIrArr<-w&r%z#qB4VYtUuUT%bdoZhfxHB7f zJ(O`;x8EP*)gYkJlQ!X!i6`}tBDOK`bdmm4!+A2lXQ*y7*ki)@;bT!|n@FOkE2;-Q zCj8^;6V8~0r6tGvGNQIB@%)y|WorM~G)4cA1IRYiiTR+dnHK`^gZXsVTK*o|QYGhO zPyO#d9_aFjSA^!4HpyylOuu@6`jtgCVx$w}#S`+Py4%>I71!~THs1Bq@avM06WO&|w-H$(08NLZ_nNJZZve@HzVgqgCZi z7xG8Q!^Hn&*(>O*6`Lfeh#Q4NUdUtDgwQ6&lTw%5f0jV1=>0G9 z7CTfYzl}%;YGU@UPoksEn)-L$>xgu)i02N;il42ursidqiFTaHq*14yS>+Z;yqeL3 z$Q;}S(^W(Ee*)vO@JRMdheXif&g>Y-nZ7d@4LZ_9U!qpqZ+q-$G|cbnRIRHNZ|f{h z$+&F@-rW4Mu41rd+RH7fABm`ztmndrdvu+r;QfL`h_ZI8_$@iJl4HCD)XT}#tfdrb z=xZSczl`TFJg2EN+zF$puZ(s2@zT?{B$hz9TdQ5o%lLlywOG*i;79qkTZE+X;~G<0 z8O2~M3bte|QhGXE>!^z$x*;8!m!>EEOaKN)&bZp1Dt00+d8*1DDSy5S7+BCRiLz^T zvIztLlCxw_k929!Xj@02NF0S2a8_TP(r6Wa#SZ>0Id7y@0kM|xD+G3oG@q7FJG{%? zyu>0FF(UN$A7mpJY-rg$rA{`e=9m((_weZaOGaJJWQ4`z9cBYl-qD{sL+xzO-+(WP zkAXSHSW4ul;bumWCq(a`O3Fh0s4S&hgJo3?*Nfj+EFfkIhBiiQ$0BGrDP$nXrwe|C z+TW9Cq8hNQ2L(coT-(x*d_;%oK(spCFN5}?prSzdCladfWTq5UNqzfL-B~x?cIke^ zJ0B)B%mC>0c&;Goj3h)==SL=uS5lMsKg_*TcqQ8x?j75R{Zr3xbp1EqGYP|FJj-iuLdW+l;f`HR#Mt`YJ4y|(;iKgv- zMJ5VY0li6ENW|vqu>JDJEz0Nc(*Dbk>zW863lI1)V<)}kV%nmpwoQKN)0gHn#@ls^ zvUX%$o7iWY7Hr>^&8_yE4VYT4$D4r*St`SA7FQA${@Y1#=2NlSX}L}UBDvr)Oa?*R zs%+xy(rDF-M_2zM+h?o;bqZ*XE>mcdSUT2Jk|6t}UbYuzHQ{@k z8iK#KuyqaBPP=I&*>`SPRnzD9q)tW}U!Y;sNjiCF7Q}K=GCW> z()k3HH3V1&qgge|kylo4D`~2RANg((hI{Eh1AT|T0u>l?-?&Be+ApoaY2UA)mD9qR z5V5X&$ONI`l_aN{bG;zpCCjQm)V)%S?X^?#Ytc6i@6(}l+3rxa6#H<9p2=(rXCVAa^H_U`-RyXmy6b8GNGT}>w)J?5z~`4(l~VPuw_eUPbCevjCZ3VcT2!}3j0N3O7W zwQSmT{9dpSll53eAx0voIy8zOp$aXO?dt`#DxP30TDyFqYnIEf5y zQhoCZ!1$c?g~RPk#5zA;P9GC(vPncA^Ej*$xU0!!R) zjvvcq9qZ=+(-jCo>A;B!0u>WLH-DOWC^|3C@}2AxHl?c!AT+e@D5c2tYmMk<8mpxy z+e@*nayV1>p<$qtu`Zip?fHBtk#_+2ke4zwm$*b8OoAinoxTqY!@*h^-4QLB>7`zC zRXRxyy--&iT9?`FxluX2XN?iBg^=edp)R>(slHk*DYq zw~0Y8RZ7WTP>0}zSX^d=@tZRy2NF0@(`@{bWwdBtXm9oLK8#CyG<;8;7IuZ>Z79GV~xtAHOYSf*IItL9w16 zmSJj`$`gNS{!Hf{r-<*JM~<*FXU7dX$^Cx8K-Dct80}e z#!q%p=*F-_zP&n+&bVnO!A5@{M1XQ zQBC`a)rSJpH=5$&t`n(TsWeB!rADv05g}rC7qm>4o0)u|YLh(?{z5%fksT5bsUE&a zbfa6kWJ0*umU9&BS%kgU^CH>n_ddRzncQz+z$QjdQ!5MN60*DBXNyt%WloX=nQ40E za$dF;?R0!j)h&^QTqbo>aI{3F#k)u*ye#*d)(VzZ>(sz@1bRB%X7H+hSrvsa2$kSA zkX1PxK|)H_z@m6E75sTPm_*7Eo0O<4d2IBtS`ZILRHXE6lIi%ZmqeDyiJgV^c{|VD z^QA{rVN#AsVZe=qj(9* zesdHnD8?`e6cZV5vH@E+vTSYFGfz^#>`fg6tAuUdEF8z2GGi2H%cY(fRSG(YEEBg0j#y0391WwZo#-_o;TgWAKTH`_V$86>x{x6Y zoSWPjo$_g3&$D6b9e=bNbxa69Us!@|+t|rC#n8&$2oWk6q?n(&@2#s~eFcMsVe%*g z%CjOuHQ!bNA;Dxb2@6{pD{OFo%Yb#Z)=5t&!{~($K_FOQV{-bABq$pulVONG1eUJM zhM_&q-1QBf5)uu(>KeR)y3z^;4j}7*1k!5iG|*RgS|*aoe;XKk%GVxU63sCH^BJH+ zB1NGobL4|Y(KOH>g0@7M-ajz)IEz(sGh0TJ4jp$8L*DBGAyYdE8BEr~zvM zQw=3zAaO;h=m*H2ySHSzlL&FR9GUUpFijyA2gBOw(N;WlZ9G@$hbakhCKRtegso1P z;mw0sxHb^(hPbCDh2po=^mxAf-Uj$QiF9qE2WUfzLkrvkk_EYW7L^#kzU;5Z>^>nJ zs4@=1y%*uoUtn=!QB9_WHaGnuF-MZE1P|hp9b?o6j6(hE)dt_>#v>r;J?zLD9V0*( z)ILWk(p(U-AVyrj%XR6NFd5tl-jWy89NQ4wT_dKO<6^)MbT@&az;D4{xT-MWS|(Jh z3z{RxJ|hzxk|<|lZrtHSgo*o<+bFbTi<|4Cc&W0I{YF(X77*o;1Ue|B1SPVy9@92)J zf=l-B?m^ZO{A&=Ndl(ItYqhVV%+I!-_aDloMZuPnZ4{uWNJ-T$>=Nh_T+l#?&+yOd zsG4(`J7g8`HM4B}5QAn+ixgy3WY!f)7WrG5axO6E;$$osMn^80E#gdL^?X&i(zctA z(rG+rOKTg%iM~b!9jV- zAaRMjoYkYh7JcIlw}?&?=~NMwQ`{S!4sQz|^*OZ_wpawpmbQ+p(aRPDEo6iMPwknSz=774qEX_{)3L<@$iuK$E`hQ`T05Ih&F} zDio#>qp2LF#CDwwGm#%3x#Y&=a|LzVX@3g$sD0Ss0rPzM1d%0~r9`Xsrwjk|Z&h9Ofdvs7u8dQKGz-=8d$DMU(MlHk?pV$g+3|x0wmRyCnYB;Xm@gcV6qc}X|wfO<;*vvBmT`dzQz1Zi|%xIXk(KeSTSA&l^zJ=LO-tz z$En;{8M0I*BP|QLh?QHuf>tkFQ1rd+st$y|%CB{c2{~SiUi_sLHk3hf@y@)$qcnH8 za=cdn+EjFKJ!>yLt(kppu~wpC==yWE5x-qJY0;9X*=DP<=_!}hdmHZ538b?RLCq81T_oJy$>X9_d zbGEYU(-VAD3~R6YdEBZ7@O>E54ri^_A|KKUsJg@rrQC zPaP&H_%`@m+RXq-ulF^9E1QyDF5YI}CCDZ0;DUygMr+M2sG=U#)`Xedl8P_oh%|gA(;Phh?@k5fU#Vp8S5F8o+7{7v^;qqFJJ( zUCeBv_-;s{hCR^LO7fWKvJnNFEA1yGz0#NTvsJDgG6o-$uGFid|ZpA4C7 ztFbIl%~#7Lv9^!JSZHlAc%~GgkN==_h9fn~iX|@V0mNw0ohJ7t+AD>44(HAdipjl~ zi(BrcxYc=UiLhWOwGbs$ma@4#qg(bY9PRsHXG5JsPxAJC>R{*UY6&i&gHC&U-4k!SAGrN8m8p0hosGr$MShU?%9 zietj(xXBA2s`=D(Hl4BFFcET|8l=*Jt=viateAg{`qLFg=ACZ5FL~W5;yo}3@AU^8 zQ$@V@N>Me$IqNIYkk~+#_0gc2tNQ;E1^I{*K`GvwZq> zOz{d4F6e_E7b5l8?2}FNh~X;dQpvFc@)*hNo%HKMzU0-U+qIJx|7S)Jv&#fDD&Q#M*; z&fmaN0*4}+h7Mo@_i&>)-_3kmY6a=KT=FN{1?Fu9ip%R|??cxDOR@%G(iqw|7{z?R z^=_nH;W-2rRIFAlSyQby0=Z{B({B5BdcK4owvz3*^-5adXIe9s;z%< zJq$k*_HIy%rd{fKm3$WvXZg?Z`_)#8-5OVX}36ioyLws~wT(%d0wIK7d8U~q-fg_Y4JNtGX z#G${c$dTqF{^oDmK18@Wg=Eoe^Go`LgUG=*(6#S^dNe?aA`~4l({*+sLcOAC|Iz?6 zOWeZkdFx_P2%W7X_ds)_cbO%fjump8vGbv4`Hkqc6IZ{+>JF1}Da+0f4SZP(1CSjb;@CZM}=#zSX^Tf3aKG6>G0?=S6>U>r<6G>sO){MBMK9H1@>EL zw=4PtWLEFtyYd=HSWlR}hI++a?=PFr=cO3fz9y+k9v zX-c$6moY zafThr#MSLP&&6fJ$1Dk6`>^hk3#UYVq{|ASi6Ztssay^W`D_8rn#GiQdrcaNm!eDE zSsmdAP#cd*61L2~y$=hmmtiZ^SHK`3^Tiv*#cVNka3Wbpo#>%28p^GC@ z5u(yfj=<$#DDmIdr{bYoD42Q>6LH0PLO(ALOvC2O$yLcaV-}skTUWD&S_3Nyq7si6 zOq`jMy@V?aa)~GL==9t2r8L_AD3FCB6?JI&5yhY*2g8Av#5B~mCCT|TIenO}Bm;+w z7V^#q_j~l+R5g&XA59$%4L4jWczCjaEpnK+1CCVF*87OfR;`H0@M6f_@^_!hL{x_J ze4c+sr2NE^IRGKKShi)o5c`Te9b>-_mo7@30vG2liZX8-`kWVPB#lfqMD6H8!E8~I z)p>n7I|U1w zKvE#H=dLOJLLDYt(ry>6vCzGxLj(#7zFtd08g5oAVVr&{>M`*f;xXxvCi4&je(aHQ zXXHT4`LGh9z17ZBeq}90Z+t6K9w&9@y>ZJnf|m_o33oo0e2(MZawke95iu3xY5suw zg&LNEL&>%uoj{~aqUHNYR*yMK*UY2bYZD&$%;L^!fWr0aciB5CkvnchJCCe$jzYpp zw1Gsz8qbx$#Q^Ta2V(5^@XE3ZSIk2AYK^fSLp51K30*?lz141p!TbNZRb1>nKrcJZ5(+IK-)Ry4n!M+EC&6u8G8Dea1XDgpL@^< z1c&MWHIwRkV1uGfCnS_v{nl%`3P~}<1r{;k8SL zK?3-QU^gxzuDgL$ohUXlR~!}77$j>W4 zE#+|#7XXJWpA?BTbEzGuT@0L@YpvO&@3INT?$*HHoScX4{&TFv$mDL;C*4c*(!k-O8j zhw5US(ISFZ@e=*KS4Pe(=X)9Ig4WtitWyR@Vi(FS*Xbj3g|fB1 z9Y0Bt0Wss@#j7<+m!5c9Bslau1EFs$LKRj@KVcc6=cQZj6Ln_HGNU(ExMs|Ue(e)r zmvw3(==ESTvEkU6zi~Q~R4rVLcEG+uJxbOhS~}#Xkz$e#L?Hvg>1!1==OezzBx7LV zd2AXS^|9l+QzKnraDACE%n9aa9Y{a{OOH>f;Gq)+vwgtk+`W-X2D4o{JW{YXH7f*{ z$jue|0)Z$V`;+b5%^0p-%oZX>L?@1T3<{I3!E4P)8wNtEw4;!9o9rm6$*CK0m|FCb zsR0pm)?L~znglu0l876tz)`RJ8&HoM{mB_p34to}11cH#v`YcQnFD#(^dNB2)@{7L zCFEX5Ua0kHsJh(gfu;7XRw}lr`M$^UnI{B6emsrl2h+IiTNjjcdEQVX)q!szMBsk- zZBk1O!SV;&u&14~FSBdPDqVC};Umps&uVScX=%=fFcDOi!{g9Eb73rpTuZ! zXJ8`4qHFk5;M|;kuOk;3hVJ2430E0TsyMJX0#V{2>Ru1XRxBcpq~MxFqC{z>ZiRNZ zBEb)MGG@W1uav(~DAzi)EO1_}Uh_O~wW=bbg-FbB20>;x_N4Ki2qYPuiZpTI4>y?) zVZ$3UIwdQ3BiYL;Y82qU)UxKlLL8093{xQb8YL9R%_uxtf9DDcVjkMU{d~_|y>fdu zP~=o**TVEMe${FXR^B*%&DtJ?ZBmXYUU(dMzjvF9jq(!h~*XG_(qpiSo)|1|3Eyhf89_;H1Y|pGdp< zt=E7BtwL9T-!d%u=w264@?st0D&j_tZ~hH(nCA9kHQ?a23LNch%>P)+$m^Vt0%FU} z&T|lJufs`wV59!Sh@b+KlMgq8m|*|=As?%skJH0ZY$4ib|8l`c_C1*>^|)$81B3Dx!hjvFgv8%bc7^HKYUr0Was8FSOZjg^Q&%A z+xK{N1Xu`SFt->9DpFQuJF|?JXS!*1#503(zO-R1{|nowW4|*WgPe)DS#pwn9%!iY zNK^k|%Jsb$`w#ZzBf@0a;=yhxv6kB9{xXy0{tlKg-hvnAJbPWi)@#o9`@^K!GkfCK8Cz`n0j~!-31qelQp*BI&REEe3u{+y7?&c7(YM?_&4))vNv3l+bV28({?w z$i_$88&IevnHZj+U4PqEk%85Thp^GW$7}sPHqEChhs(-u%T?a8`jL10wq@DFi+|j? zdp|*Qn)3BF>e2gJ-h*4e?w5OY&OOtWr`OW@6`6m-JKl4?7>m;u>gGwQy|tmM~< z72Vyf(bLbDQ6y?4O7G61V~Ku=WP82|T^Qk_FDAL`jnJe(09A)H7qtO~6c$(p!qx6r zTW!P9`kb!DCmtJ6bc|uj6~$>4Eg3teVrzaa+8pp`Vve@v+aSS!Wkl~P@;M3l8%`Q6 zYw>Y*D+}n_O5H(JP$$)pTRH%^i>udtb5m0el-9%$&r-j43F=+MP*^Ne_=7?60On@57cPPDw=qam;!)K2?RKco~Ec+P&#aqdRwXCP}~;cvJISoyxK7<~{E z@V1n{08(U6F47;)iIN`C6^2VR5+N$>x|Y>(;oC(|s!+4ct>#}I5%&M%lq{s!!Y38J zMD(|7C8GzD&rJTWOX0CVu@S%*9BdoF#|?q3WFRc0PTXAQY*uGI!j}KTH^D&)SN}jy z3S^R4DsqI3;D5nI~HG|L~eYc|<~Ao%vg)fmwM>xqw$`tr>+z-Ri%d~z<<^F3N_K&X5UeTy zGag3@+Q~(N7Ud$65rQ_dVqu`rq!^w`h;Y1C7|15wa^Zo86|KXO9#Y%>LLXz-_*9Vq zq08CK-iKQ~=xBGw{jT>2&us~x)tHA#(8$c6JQ2+tUsdmTt|H$Qz z=TI&N^fnF-X&i5EJ8{bhgobD3UtZe(`8Os6K}4A|_;}ItZP%E=g8GOSI2_>%YfBXL zlaqQABmRkP@7`RqvvvQFepuyDf!NyASj!lyFtCVeGx11gsRcQ)IbX%7OG-_N{VkL6H5(84b3jXVr_55{E$C8L;dAnPMtv#%yOQxl48<* zuIdU))H=NLTYv=nPwUokr)ZlDKvxYLh=g$9iTmW6@ynV?;Y#5V;N-`m@lI9fCH~ov z1c_!wlcn0F`?I6~Q07{kA-zk!{Fna-XF(*ONs0>sR=5ZP*9Gym&XYIb#H0WCWjGME z&Dn=5Au9lvZHdRap7+)WbFa(7S&IKq!y|{9mjwkrzcYS113!O$`hTn5B@Jm>AnFtLz+vMT$-JpZSC* z+(SGs+e1z!*)u2dm${bU2b{i1_+eUu{__JCE&vq3!F?~O{HIrj3#0%-f59V<<6p+V zHV6RZ*UrYR!TfW>|J#f63cA480&7En@?pM)g{40*wK@6_ehm)WTP5n4&mYc+&SK7` zZ><2VAy;jWPbh0XY*>Y2KG)y5ModW*3W?E9>2phKYxRkqP3YZ(+|C2OQ{S0INk~Z9 z%X}3J?=Zy;)rgTo1HFLxaq|Il>=YMgLdIQ&w~XM3om*d5nbaxN|{8d{oz*F70P zVH#T6!gG~$gE-Bq%F>iKmuD4aePS(fFO2eS!2BZ$1dIR-p!&uA4pOe(H;zJ!96yCv zJboIZ-Q6zMQ^ffm)?{CAGbTDzxa)wo?N3qvy8_9>`e}ibk*%?NJ<=-=t?7@2#Siy@ zQ@a5DYV;uGSiqPmZv~muzU1ZQp+GP8Jl`H4E`MI*T7|E9^bFwj%=`+Xf#8v36S3QX zIfg6)>SS)InL&zxLQJZ+m>vP-J1WfUnq4Wf%(!{zVB8Q@@_6Nan_{toHR08w{J2lm z()Sc=DRnRr?JN~spWWwYqWhQFlk!? zrDT^DkHl!RtVin5@zLgcf0%YN#d|d(ipWnE6BF}3P7umNN2i8B$U~Ro|B#L+=&oyB zR!&aC^TpZ43VyirS4RtnRILznVL$4 zB=kn&Lsn5>q|iL-f3aU{SY2r#c%CIyQPZMw=zfpI<8tibvN1I?8wEw&V4UCmCJbSv zIOHG3(zRVHD)6}n%k9U57+tHsx3_OPG?DJS56?N0+3F%M(BP7y+d{HH7kLAtB!TDeApLdo>Ysr3 zNstHti_MK+^o5KO(((Dq_ZITCkmxhhcJ(Ia?c*^Z-fTQoR>NHbk>s*6o(XAv*}zBi z<5n<$6jP{Ser}FjfLJ)vsnc$q!|U@MF*o%!;q73QGWq}=WY7O)0^95Mq8VHlCQ+&d zkK0#EA%~CJbxga~xrpzH2ph$+sTqVfz(nBn@ha^&+ed+8qd^#h?mOe>+w><~<32O# zbwaD%=1a`^R;R50` z>s2SE;Opx2Vb|*=JIy2%RywDDr?Ns84>|17bdrvJmo^Ts-l3>+TAbjA*2mNEaTo$A ztHngJ&sFEJUk;Yv{h?;hgVpo8!F!CdB8LjfVj&b9DM(ozuEg&~%PXjvc6x4Z?QHK; z%*5nmGK(HM|J#i1mT#QU9*hyEweq-}quuXk-7?Cv;9hsL!w2aW*{K%KkLRp6%>l2p z`}d=7TY+~-z@_2Q#`k6Ahs}nDNk8Q{PCM7_+u;XZJZ^a3`0p2{2b0<nA{hn;7*m zvl){E-iALBhE(VSKDR#f#A43noMbH<9WVpzbY(1#g+NeII_R)(2PqR zfARY9nx!YHw9n15Ca=?kW%c`XvAnP{u1WiTK~bD29?Es{n^0=L7cB^=Sm0}PRfluy z8&D#Oo`T@>0cypQ;IGYDpwOnOCXMW{P!%}L8+cs%`0kH01FU;(Km}L1AhDZ$!cQ&# zr{9Q$#l;fY0UutZ!U+k5qMMDrt1J-_G2qA!NfD@&V|BFE$!6ne<8meABMII~u@y(m zjP{pB3n(z{3)KPd-7#!UXf%C3m#sQWfltj14Wq*6&BnOtmS=fnk9)U_-@V^~PDh6c z0yY-X?z1`EXx6TiH+*j)T80ebkF&iHoQyrfBm zC@721N#+5N_1mA-D@f}WTUKl5U?(Rbb^`SM^(#SeDK+(t;^$q!m~Y7(6*M#AQk*qU zT@y@8MTVb(r3u&RfOjSkYJsWeI6Y^Eg}j~?84VgPE@ktkH!mJqzA0ihzuRfU=xwAp zEIB*-qEtdSbTGEEc(dOt!al}l4=AFPhDPc^ufCXcQW+0RA%2U2iV8`&MrC5?(^Hj1 zJ)eAJ4-+xqrTaZ_+4xl=Ff=Q1oYDU}d7!^0;Cu!8dD9`lVnJyrcRxX#=^HOOsEi<4 zo$62dFtoY7RNIkcsuZ~&Y|<(!D)Oe^3MAt^nDLPO+tCJW&ZY}W^t`0w3{Eu^9qbClKxh{NGyG*g1N(0Eo}9_l*FmAT|LtB`5AhBr8o@QjNp)BeW$f zsrt-OgUvKSfAq;@v)h?Q0YS(05U%ON`Kp9B$vi9qbZT*``6^T+Atz5&_`uSZHiQ7c zDA@nU!)Fv`0DA=sT$Z?0p-=y~9gw43G+XmY&bQiei40=ISYE;gM+?_fsKo`=&@bG_ zs$7JD3Au>(QC>j@smWy9ds{0UKv@qByh|B!u1L~fCe(RNtZayq@MJ=e9ibnb@I+c) z)&pU@lp$xfT32-RjK6KinR6T!N#t~q5C(gUi`Yvpi;+>`e)+cL*nIm8UT5*BpU&gg zHj&OMx#*THboOWj`0$>q5v~S*gaj5%&AaiwiJ&yS+~8J$e!Tb$6cs_XWl-z(Dt274 z`(A=@YgR&8OR0=_3U%}2t3`Im`&Lqs=X8W^)B{CvEJBU6l=>D*z%ij-m#7ivo{wvi zL9KCEEh;`B=?p_k>X^Tj2swEg(R&5Q=_EKT2A2Acktc#m;@Zdi<52}k9uN?RkF=Pu z8im|^S$TD0;eZ&%X(bmRM*d%{fPfZ+fVAs`N-;7W@R2+qB(n37Z;|akBqjIIA*I1O z6vHW342)|;J2n;#GyVa0p>hw%)f)&3B!-18=pX#v^!j6(J4FlS5emH5SY1z@w9#>b zXug)xzgi^A#>NKj76;&^HyPv;xtlE4E}eQ|$vkBEo}R~<Q~i1L zBWB1{MyJ^qR7Z^qYw62=`p@%`*am$>N{~&-cgH2MaSbqEBB9__nB~L!v+5+?tG0Qh z)#=*Z#)iAZYo&T9nnfR03%ReG)beG-(icH4A#RyrDeMIpczm8xGCP$RkXNic_)Mlq zsOd_Mj^BvyVrq^(qx=mI_oo$QoMDe9`@?s*T9urEalarlKwFFZ>^{%03fgi}-OZ9*N9{bX((1s%jl!*weze;cN`S>( z?(lMn#-G%W1-zL10an4nT``04OHL3|o;!lq@thB};{FHas7_9R3w=E3l#YjP!*d_P z1k>w2MKg3Lz{KZQ!z3CeW=2eTW8=LuI!6Z%F3flQMGaGp(%;S|+K>o%p-I98UYs*| zZX>qGfy8=Yol1@DZa2f2*_;H94dmJZlF0K{hiL{w;VT%ePW>^CvN?te9uSyJbB9Xn z%BRmb$P`nVypUI|pGp}z^jA2+6x~+YlAU9nU;)aC)7cR7*igodQ z3mr1kus~1j9(}^DTsEKf$EigC`RDJ)PeUk{oMseh+HS!JC^)j64Z5+(M$S#wBvW(q zks{f&16ZU3e5grQp9i^Ta#FM&{>;w6 zJ|J3;)@*dk_z9zj7SJD0HitVE;m-Eyolt47=6R>Gwc@aq>ffh7pRaE22tSB;-7nd$ zuiy{@Nf2J+Tcn>xDRabLhdg-8OFJv|hD=Bq7u}<$nEYPU_D}8X+{sC&M@9m@ory)5 z?IidF+X@+#u6TSPPwu}72?0ulB5&rV5WQLfD|6rqT!$e;v?MGff}BGoNU5$Fxq`~D zudc3AXk^~g-R`mD<4zfiQiUY5bRgi9P$-UyY-xv6E9sea_PZb)$I7ndz3Qu2vcMA{c_*cYI0p}$9naWjCx`g= zYzyEAAK$o$#T^|#WQVSCe(|kB-XulB$4ZN*QQr{_y+!xMv9;xcN&?@$k5{4YdO7*X zKt`}kW2v&b+eOfq_FJfUBiB@G%BpPiU4G06iu&JER27fUnMOzlG+Co<7JkhEUmiEE z?)UM}{JLzI;Xlj|7q?#@JanIPG4vKM6LM0j@3Svv38w29Iyak-iqOVi&5xB$t=p6X8IucMF)5Z$t6p4hDZ4eIRy&0F(kVE-MH926QPSGl0@Y{%cg-xew`e7)az_44VSGvd z4bu)>1o2~tlUN|d-~P--{HLhO=3X+>u{CV>GJ$RyP8$6CcXB_Wn5Y=7T2Q-y0CKHu zla<`@D$Dy<;hev+8S@c<%LQ5`xFCf`50HXq8!b7?cGZi2<6v^A*Jniu(HK51V2sc$9VFQi~dI} zCqcpjYqKisNpAB)luPhC;vvCO$!T1G{5woTd?2x2t1(_=jX{5hTNDsA&c*M{r~T)k z4g2HTBQAaZ>vM;L223!~U_}Fsze*=2VDEejD?Iwg*&9g0MD}MwLJRn5{}tE`{&+($ z6Mw}0J3)Km|Cm|Hp6<;4eWt(xfCVvXAZ0D^|K9=DVW5}tCcqtbfI!5>2kR`Z1gNR$ z#b>rTq~)aI$z@ZemXb#==U`!`#=HDpi|*#gQIWWqsg+Y1^=MEj6&wW%{!C^jrauP& z*4EYsu3PCri!h2Ffk<+hY|?Wos*rD&Qr>xOZ8#UxNsf4+e0Ha7zTq*2r&fA=NL)@g z$sKQFi)BJXerCSZ$M9Y3JYA!zY%4J)34f|ca=+tJaR4UsASS~I zp@HXYD0j1FtkJZn7Yeb!`r4OTl*7#`uI)*HS9Iq6QFd*yAcGh^lTqaL)AZY7!0RGo z3h^prFI4-{ZbFXVM;ID9`sk^8x#`8JItm^N+>si!lT&SRlf1nA%`XdsvB5c%R`*SI z=fyE}bKA7EvGw6uaZzqAUO3T?fZGXr|GZ#6_Lsmap`*RM=_388g8Kr~gJv6`{m0bttGW zrfP*^;y1c4mlV7R0w{HDZQi3JQfVb6CCY1KwaRuf^~X22wQUG#&ThORo?o3?6>>K zjy>d8T-WK*MVC!>vXLz(R`K}-HbMJ^O}3X$b~ih_k*#4zJ-+WYtJ8*xs)>Q8r+~@l zl-I`0Oki#A;PK80_^oU&iGOY>#$7a9BcnIRjBl2|0Kkeb2w!J)+5w0z52RNctm+HN zC`a)cnI5xG4aVWFmVF{y`Qu>3Y{Y#zQEqc~K^C%7oi;1)w&z9u1Dpb*DZ? zrXXTQ|1k=DxH~}CSb#V1y>GTv-9IUgP&DXze+&(CL z@n5#*c76J$7T+86G%O*BU2u{zo+ft){5#{HW)FQ{3XV-t7D~ z04++!4gE{I$@dpZbKwkWlDbM0ib0`j-o&g-S-&toDSWu*}B1+ zdPo!k5ibFqFF!)vSgLsKyX8yZPiU_@O_;tA^7`vC=yp)uYyJKW_3J6rGhHyCt9M-i zPw?eHl{?}(t}(o}vFm<{*WA}7v!czqXd<1zy_#gw&l{Y8-(5~2hnto?o^2%Fc&CJ! zfM&R{ToX5?mYf2wrqoCHs25wfoDMgtW5A+0fxSw#4?KIYzC#~1Cj#CcVB zTe|h9*6Hl^?MEk|4cBkaA`^Lsb&kEhtjEnJnFeiY<}ao&4RmMKof?UWi5+HwdjYt} zaHWjNV`F2c%a{BtzzmJe+gnL~%GKRI(**S*ZHL8|Vo?Mt?bnwB(5MGHy`ncm>khJZ z@S3Q3D$s~Rv{~QWlKKt}mB@XdeNDnVV5ZpeO74akQcca6bs59Qp z&58&!n?4W5fn;O!xEw8n^kD#UId=vL=VK&dTHOmale<_kx3q%5riI z#C>dKM?szmT3?d7s@t?4r5mJY;(~uC{*DU?l~Na@ez_h<`>DJf;>`G+D7~9MCCIUc zkWxM~)SEDVdD~m^R7sJiTW$Si>?5|>#`t=P_X_7om_j&s4&>J{8`g#q(jlo96rba^ zUt%;k0xBl>^Y?=kCDQ(JiM4A3;;y7?SEWece#+SiN|*9VUOknJ4T z(>tq@oNP{7?KMs5WNw1EKqnXGndrJI?dGK{^z^0YK9bukxl*9}Jua(3E^VN7n*fvu zyjRlhTp&Az+7PX>7vRr(sBj9vZb@H7yzHi_Io9R;V8sis=gYEZ-H8A{sh9%02!)RYqH&p z96VN*?jdPJSAoZjv6xMTTJcJnp_~@s*MfHQ2C)=|$AEhVe^v<;cv>~!TNQ=bZ}B~B zT!y!k9>PyV@gaA-I7G?sq{rZ|KHjS3S1h_ zE_J4)7{9Ip?h=d|8gi6ncwCM$jTGVpA4-0qPQOXMJSz9bLmBffkO^(kW}l0dg_g)L zvkpC=T`?~%Esw2hd6^de9QS+NA|1u@d`Q7IfDih3w%x+!_C2YQ3smsF|DkTa=mVUz zld^0eX>mNNf6lMn>>g7Y+Qq2Nsmi`yz^UJ@e>9a&Ki&P5sC}VKk{gtnDCXrHUxJjkJ9Z1d zQC(V<&|dR}oYmH(i_kw5{aSdn%`SuPOG{6=Oo5IWPj@_%Y9bd9oJ)QE0Yk$$IXrke z^zD6d%*`a{`axs!R$@-kyEvyq3DE=TO=)R)g2&b;QZ$|3(pEE0C@FF%gm;5K83PWW zBAuNSFS&VGD}st0+&9Ky@T<=E%l5jf9^pH`!r4VtoCr|jkLx%BVBpb^@kbjJYyx6L z{<}ajla$eG(K!0Dmu%0sVaHK^>S`mpfx*EMQlf!ttxDE=4<8Y*o8qKtkP7>xq@z_HJsr1_;O;=s4L~LtJV=+hzB*me*3NMg`90gDa=JpuY}X|)4fBo*@K~^Wkd_8^mvT6n(cwL zjiUQ@>(v?ReZmgDo;Tt+KP<|0{>Y(cCTcg_4<~UB&nI<=*@W@IGwf^lcpbUs9kpL| z_YqDyDD`d};M7OODrv)ioIWvyAN=AgMo&mkT^H8k*6jiRgp-V;D?%%xi`i*|Zb7y` z{KOBx&+Flw@7%(^RM*bLZl&iaH38rzKS!7vahV({#dHOq?^OE(F(+~;JvQ8NkL){U zM4xUMyArFJmRe_CqjLC+3MtMyy9A^M*Z80%j%DeROq?FKKvlxqCezs=5xB<55Dv2k zQp1|Jy$}=Ely@J%70+L3JjgZ(+2N?aseY5iGl+Qf-LQaMQd=BLZdEnM^<4UFHiSCjLN1xq0a+Mv^hQeO^CD%E*xmWsCAt|`lmROjO_`A%oz z{mAL&@Nc!CgE#<;W#f8DDQ~e z)JPkY7uo>3%@|t0C)Y{oDR1;6LuxWkXNz9ULOq3Ex1+c`bQo2{Ly7Xm=%OZjBELf<-_y?fNr+YhblVUAVuzm4sJy)b??a6eDHEbR zZO=Ib&tv@Nafg6MNL%Yo3$?Fgg>*@<=rZ-Dx=+?!Iy78o?{Z91DCTwYZ6Owc%1-li zN%zsubK`=mtJ*6DoU6LXDg``&!bFtYdgPTzT}4 z03lDb4vKZISbbSr=}|s>&2IZ6gs5duLwzGB)6SNXoT}`o1ST>Rt=Ic!uKs%0I>Rv$ zzawewU_3>E9YDCka`_sg(Vs*`0J=|0N0WSBVarA7sTtHt8whKS(ZanNrv5cS$afNT zDJ?)Bwa3Oj+z(CeVwOQFYpIlgQgn=w3lV)4PHOrQCk69ZApLV;gRUfi5nDZ* z-$AB)-QzqMgGj=@^_imN*HG(lFFg^(e#YcQAs(KQcA2a~V%s&NL{x!y)1Ivxf*A19eC-=yGhNynZAG!rx- zY3|2&jr2-$)=ROIbO-j(A@A56X=!Mz4yGAVgY#-JH!V$=hc7>?Uiqs_yz{?jCnQj_ zS}1t1lAcMkg#8gaX(-AJq)yI9E1E<^C&m~!DVIwWzd=KCVw*#$1x!xTYh5?47@Xbz za1Zw`k)lw{G$bR#BzEfT>uAmS`T$jBhDa!29zFH|H#vqGeTH#|9>K5fxAo^W`+wT| z%D*_4pxp!(f-D}K;1b*div}k^I1ro!w?NPZvdH2Z+%32-?hXrNaSsj)L6hKa!Qah! z&%J-c{cz_uAE&3MySl2T`+2Gg@=uV!0ki6vDP7^%@XA01?kt=3eI4CUNu{OVIHO(m zFE>BT-ZodD=Y!*~or6S+@O$%tkO_?~x(Ey+ou;t{PB&J3qB5-ZsqO*n)uLVnuT*%I zc1XgytD%hf zasr*oXOjV5ygLF1|^0yjBt)7aB;a*RTvSuk`Twr4UP1w%b7o2nsTM zr&08?dPcJPaP>wD^M&A`y_S|He0TTjGrj4&8t&P-Srt+HSK90?B&_&ZRCV=rUP=s^ zB|KsakI-74S3Fvv1kBQe#6<(f{=9EB>pFU}9HoKOu5Gvc(|U2XH@loyxJ|e!BBQ*| zFJ#Lt|tKkL^?cf%J04HG!A-~;8ZTVu(V3x~s;!+6s$ z=j>9f1VlK=01K(+1OfxMgOvRHs%-b-ki7&sx}~bUFY`uBX@SNC6Q9EFiUC<4k_t}cD5Xw}9=k+75LI~+K-wl|rP z^~w%hSpLRQ?jZnB_%s1aOS{lX&(`r2h;EU)rC;#nvvofg)hoheeJ4`bQbRegB9{T< zkIH%yA^mlOtkog_yTX79A)kMezGD5eFa@vJU?s~Ws$C$P!B@^qY31~A4+NEWeE3+d z(WtAdO$oy+BV{iFB!qqc8kk*Ty;dSS6vk->g-lWUd%1hFm9RY>kkmQ>l#Yhe}_6{CN+E5XqG=azIGGI2_B zz#v?_ogK~B4PX*Yhg_>9eL!}1$jSyD zZ^7GrDr0qh;;hN;XTH~Cw(iiim z(@uaB(VH9*T)NMNJGOsjF9vvsYxki3Fr&`DeFt^jPplU38UZ0SRvuolqubZy50=6y zRfe@_hj)?DJ(xNkj~E2}NbLKsVLPO6$w%C4+NZ{qm9sTb^*nA_XoO2p-b*aPt7{39 ziXRn9ggJ%|s`>bB_U5TbhR55{SX_KR7{B89^05c(-Qg0`UZ8nnbFaRI_QknjkB1j@ z|1Kpn&7RebbcWYbBD)N$DT#>xW%&0*E^NoD0M(=hRZ4w2ZE47F&PnhpP|`y{8^1yB z59g#fKe49(pB3eDh!2aoY&f&%w~(Cbt-E{i=*Q)1+1p|Cs~;B2rQEE;vu?30Ep!~+ za1b@fhp7)tx7As+w7yM*Z4e}+Yj|7Yty;m5BqbzHeRxaqOWT;%@DD+{1G&Pn_DDb? z?hB)SS@T|CRY)n$RwwKmvbP_N?7>#fsjjVSn8M|C12%Y8VYBW152VXa%(-fiL*Pi0 zyI07;B$2yvJ|Pf-tz-0=^o312zeO9rwu!%ZeYFnH6&JW*zh%@J9VsRBy-j~Y9R{OA z>O7I-x%zUUle5i+y+RpvpuI3omW$bm1NPSP1+$CFw^9+44P+LSKr zQ#NmncEJRH+%+B9WL>DJ;A%F@`V~+G(fYHn*IlDU!v>TcwRwWB{5jzzPyY(bJ2fcn z)S)RqQ)29+XZ%n~?=^}T0Q^>*aivEMGMk)Xja;7;eur=vBSCsR+b|U*%)~t7y24iY zCT!%A*SjA;rAm9F<_=KAkjvj=>)ZU`5&l9la2t~%ZKh94YV4kJd08<7pymHt&wb|Z zJbLQs4;$2(A0Sn{uDr4+h4S>X-nBkbLRd+ka z1C|{6)2HZ2HMGqSVQ?n3VjB-T42yN`5a&@J3Ed6Wor$g+q`bOmjhQ266Wjc*__BxR zWV8o;xUDvFzr;ucdLHb&zIB@^wVXTr@@3MLge2cH&CqIVaxQ%=wsZh{fXXo!S@ z{{k-mNaha08u~V~WSRE@5y|_NPm*-OXj})ZZj%*>M@oUr;acr>Y= zpNj-uM@)kdn1J|z!>QxCc+v8oSu^)GVdiBibj1DQRt~khnPXGoXXzm)viuL= z*jd!@!fOPqLh~jD?(O$q>~Y60a)s{st&~d0lZg1!S6yXu{7Z^E}ETFxW}cch#>Gzgi=L2l58~b3}Y+r zblEJ8W42zxWE5xEK3dP@-rNSy#=kiw&D|@Hi223J@7G`N9YCv5k}*0iSgr@^yzr=N z(CogEd6)BRm+4~_d)CfRJ<-O5=MN-H(&>FrPu3Md$O)2oK7V5Q=(@~Wm+ADjhM(E( zql>1%j1Kog-|{VwH&pe9&y`Ua=<+GT`*|86NLvyNyTX(w=IsO%RUwDcW=Pol`WEn)zyoHQUeACEgLX16jXG!&QACr# zD52b=t2gv*Z%~WIfg(Y905h~n5^HJFi_SahodQOj@fCG4%axVU7cs$5EnGl*c!C{EkVMh-+`19Zu95gJL@t!RvhyH5W zHT69z0ny1|ZNX_@Ds{B&yt$GyMLoMIeWwH&v_hn6;y8asrZT!$LR3OKWExXrU8E)%Y4KrfX zWE0Pn--Lrfz<{OtSPb1Z~HQZxoPCte|PFV_mCy# z7jcluOp|%pb|Q*U4-yVy7w{&SP&0q&W0JU>-3+f$Y=sOsXK5NcuW_&MT@0n4sSs)6%?V%2%YA^OeE6i_T=-|xliT%Zij8%opS8y zZWCARAQx309=$o6L1`X5_-U!QiMAe(>saWj_Ueeai>RZA{YtftH>F7&t&wr>wu{v^ zCVZR>O^IA-@s++1@|d%lZgnM#sI_<|PZ0$b2+$BSp?fURS;8$IE#GUuaP|%k;mB?;QYtELtKWY00#!LOx)}t$*WqC zW74_EKc}K|vFG8q;rdbTQ#g(R$HFU;Bek80JX2n=Frxab%Nf0#IDCN!Q4s&_E{T^f z(*bmWV#+jt{^ZwBW|IdwO;qu}`=D&C4C zB`QH(xWt}3gkjbryUW1C;0UkSud?&_k|(^Wd9~8z*_kryNlup+5-<&aTxpLMHs!ls zSly|_yW9%hOUK2pA6CThzE}s(I=o6+@+sxRu^m;TjSTPYMipgIrM{0JFdaTP$Xc(O zR(iqPx%&&LwPj?al<5PXH=xtMg6>7;#YmO|Z>?#C*R#bS#AOZgfNdk2E}`wc6(rxb z>iwS$X(4Q1E~u)dkZ|d3+BTI#Fe4w7D7Uw-K8k~AFIvX%(*TMRdmhqVk{EYm8@GZA zI|^8T<9{iFlyz!z4yw?9Ez2Yblv4eGP5@BM!W$}hxY0pDyr^9A4Hw{Q5~*ldF+p`7 zkv^nyitlgYeTr}M`Tt?aZG`WrHOa}K|D}EBi~1V{@RxS`AFR%Uf{Y<%d3ygLz&&Aq zIeh?=$p0YbZP{+;W~a~xDc`$7Y>7dAQ~oHD5J?;oXEzlp^H&(=LB@Knw|)u)d9pOE zqtPA`_)7+VvL3dI)&JuuoQL>Z0K=(&Q9SuPGUrs7pIO@tCL^TK6pVRfAu*3b; ztuTzSnVTjYK5ex)nlQ=L`RZ{}~DO0W^zWv}2WK9v`|MK;yi_psm2=8ed6mQ?p_#pyHs>k0|iS z@ySX)4gmm75$;R7g>KXvKX%2CyGawweWHqjA5Y&%XaxizeUHDRi|AREZ83#Xl9Fz| zd{(mGZ#LlRkyhR*NjpRLALf4-=+8ti48ub!DW2Te`qz*)HL&|t~V!c;Wb0Z*xS}001 zd7==F-(1YSZlH03x2at5JJT@8i1q3Ny36)=vrqmUJb-6u4of$cG*ZyzOqs-t45qi_ zyu#NsqEPwt5?X)-vf~%E>$V;z#q{XTG*R*bq>e+~t8grG3&Y5LOsoJ|kF&X`{1={U zVCZ-dY8epJzC2jsy(OScu+7#XA?5x{n`ViuJ{}t72{b!gsMQ+Zq)9LS&FZQ9&_1!)Bs#z)=i2U%vpIuaC+q#QREqqG z-O62pu5QUaU}jscXYvD8oQ9^RWb;Y39%OlW?94iV03!BU5j zv*pq5S-h&3bP_L*;E(aaZ$d|U2ws~qa|$r4VA1)W*LojMe)-*Onb|VburW+3V0HT3 z^H(FN5C`y3j5j~r@JXu(9hS;^++tBUYf2~@mtyR50VxgH?0ErN3De4jQOv%Gg*UMY zR9*@n+4eRG(-zJ#pePV5X~wi^KS)jK&!RshsD7U4goi3~cbdnWb?bJf=C*lnMuVbB zp3SCG3oo0h;W-2!L?*1E_DYl=XHW_xG5vrp(&;(z`s*v-4%DHjPu%nAsJ2*`e}b~@ zzhxmzUooL1%qVSmEd^eMpRRnN`zooFA>9!BLY$|(s#7I}*8tgbD4RT+`YsZMYYf6c zD=fHqDw{+-s`FI_b}N$J=V=;~mBx_9Ye^vtHKKTl6j2J|cjB2u0xY1VBH9M+z@3G? z=TA%7C^U&?{Wh;vVK|e}e#=ll4{wk>{+?a4x{B-=cha2N&_VNxEz)!`phU4CSlih6 zBXf=jjfH)Y!>U87_kN#BkH*T?vJcy(X%ST;ZLFADARsNUpghO-hVqlGQ?0SHU-lSp z9NAhzRu+q>O{&$>#^iECi09nueg1#4#UTW9X`)XgnKXS4zw6tS5{A8*`j}|)j^3&3 zEW6{+R8EL6saJmG&ZE>H5*vNr2#JRQyO3yawXr;Rr|@3y&n)1FuzARk_${daY;4TY;sEt@&Rv-lYZ! zOHG=NF`?6imbE|%<01LCMN$a4z~&~8NppjiHMgq==pk%lOAPDnYsA>??YODr-sd~v z=kKeW@iN8t08qwDLxp^V8-GG9V|vi0&EsG;>r5havV zn&UIuIXtXYYXGGP_H)2gVC_*^cMH(ky9G*M3b0S`;QN-m{v!zVoTn0O|080pa%{aL*pLMH0#Jl9+!%+FyDHMXIu$8pg#QN7XG%QOi z@3Ah_bQ8AqJE(3&U2i@coGo)KolUFHEl=&DF??ulM%eAvY-Jl+S;^6UykqnsdP+nX zz{LPw1cZBwNa}=1z5>n7Ti_hM5%=6J%%UJJ3@3@VA=NT~!(oIa?`)EjNekj!Ow~dD zFmcfCN6J5Wg)IxGHBG&~w2Fi7buKqfg`5bab{Rs5BQ$q)=KEURyN2eYCR7c$?j8vn zgLQt%o57w^p4p?n!(9^!h{6ymEa+wj`I^_(@=*9gzTV%qVQ(JH>=*}&HH)240hLVO zA=cQex6?eF6uVBYT+^m4Hs-WL;=$-1AN|tBN~_@CS0f`LD$44JeO~Z6ZWMvi^ilK_ z^S$}%X5)PebGj)~Xj&g(%k>$xz2m)MGutjfMSE%3Ux_yn4~Eh6JMT9ACQB`|W-i$r zXs@)obxZvx&lWlJ$Cn{gX0;Z~kWia`UsE(>G!eBP9^!eN_P|yP#;JZ{JTJO0xxu<% zSwx6<7j}H?yX^G%fId`an6N7HtDn!E)%*TM-s)fLy*KX|9Ol|{oZ$pN0JN0wtyZ!` zXGU0B8nFdR5##o?QUjmy!95|$&KvAL&@u?@xP5|mv4xFc*{P|If z)Z3!RT49Yvp`I5#RqJ%4{oPlTjb`1Cj?=7cG*>^o)D7eGw+_8h=m8l!P&X#)ABr=r z!?e)jfEN0b2Mi2Po(RXEKPi{S8*>-Z9tySWL>#VLujlkoU+UVE_3n`TgUh^mx&O;% zhBGvh34!-q^$Me1;h*9#_kqPk1Nkcp%j?yBB^7DreuyPYz8+C|I<)*v5!ZfT`_&pVs>`oG1PqUsa{kC=KKe2vOnyGOt`CG)`D(wdeIsjM4m(4$hA7D zczef`L%WJss=93HRd3yl+Fb1?{Ib+%f2+?`xxVk;+qFF?A~ylw)4T`S3i?@h4azF~ z>ngIkyB-fFq}9zSM6O%EyS(1$vzE_KKJ?r<{Hhw3?^Pr|AXYk#Dzui-xf;cH$*zk% z!3RwxGU2SPY+d~Dp?R}$N3`h)Wxp2oKW)Nq4*swIQN&Bd)bM%KwR-mXf(r}ownNRuRHK>1_+2*ZE=SHh4)`w>Yzw(eIXfiL0J4* zgeI}y=)+IzG|LBA*Tv+qA6E5Rt!v&wlTeMY44qJ% zc>LZJ&ak!Mv&~W@YH8?UKE<9&HhXg+`|psmyF=bkSo+M*qO||-or9$m=Uo?y{C62W zC5fZQu=j=-{e9G%kzVF>h{`tdZ)X5i>A>eTC$o^htx!d{07GncCER}vD@D)AVHttALnu*{(BX? zAQ$q#-yx$uDh4|vcYuKjfk}%At9pQ+>cRM^ir;sZ3W;2F-fsxU76l@-lepEo4^%z) zl6)!gQNO7+x(G){N2WvjghoB%eSwdHGIQ@gLB@D$=a#T8?{*#E+0S@-?rp!4=IOk@ z(dtau2Oz>h0~Z%UR)j(W7)cP-rCu+dqW|Xvcu)XJyc^WNSHX3Na)V7G%%()BAi@Jk za$%{6EeS!bKI+zdf{>AFvVf)!{x}c1j1qtM-{1J3X8$uJ|1(Sej~32X+X!U~V$cf9 zmHIk7JpA=XKe3XM5(;+d^*9N*8tP|k(5fR2;^AW%9UWyskNWxZ>l->6m&euQ;sKlt zW99|w^KaP{8Rcz9%hjf+xhg&0%s*3YDMC;#U(_IQ$0x)$I=!1K^xD-Istp(|EiE50 zlIkDFGdO|i?ABz=%*>nSw`RStUoUr^A(nc*b%zJXisLj#ZpDOUmS=_1R?q(n>L9?EuLPdX}CbcASpN)Du$Yz zk`frf*g<5iQJq{Lnn?}n=KVva{tXg%nE)xNuS&{ON}WI@EEFLi7a3*5g8F8ebJHh6 zE|p1GSw$tt0C)V?5^{+~b@rO)&>0FQCZ+#tqfpoD9dEtFt}c_zm`d@tq$9z}Sp80K zAbs=N>8w|RGtQG9$STSZLI^Wk5n;bRUvxW7@{{fjPv<+`X_?EqxG2`q_|hwXv2ywQ z8xzP$l&4j14#c(ZA~w%-Y>O?asIgvT@I2bnAZwDtZndAIDpjvccLnYxyi$>qi_gr? zj=O3{Ckf-dd4BQ9MkN%`XtG)6i`jMGY{SIHCf^%PVkeWKj;5)p8;*fGQIL|BW;Y!f z8%0ZZ8yFZ!W%2Yz(zHtkO#U7mOurb*S)@^~NIiHZW*7_FCBPqNNx4p&Qy{XVdBaN4 zWM;U&LA#>W2>F68=gI$j);e5>2o{Z7_VN=6_By{)0Uno!Vs;vnK11itjL>M2qYM=U zUdKh(GlN!}NmzwWxA2DNy`JakwB&fMjFact?P3&hzw4qNt!OCxou+KrQ!=9gga1SZ zJ(k4 zFd$L4-8#?X?c!#ETh@xxXV@f~A;Mz$ge1)ao}Eb6-Ei6}hl$Y>_q+EX7^D$G|FI=72@QY9xZdsxq#$P;jEZdcfA=%gm&IUY zKAy9hFVARseZIZctl9)DYJZ>2_jW(p=**y#=)mqCOU$xJ@V@BO;BhO^(pyIdoR(RW7&h{0-$u^0a*3{^_>_v0Kqohh%d3JAvauAFCk8un? zjqAx=+|%vJ!G7Fk%Gqi&urvQWA&FLF#Pa|lDZ{=^DV@Wbf>O~S-LRYG2tAGKuRjVt zI(_~*ZpdT;iawpHD>^VG1$2zW2)m?%@}}K-w_;r=GhbGBc@e^5 z_u|noSw2&$BH4Nw2&ZeL95U=w8Txl5GL5xRWhh#tKgmfktjTJAW>4$&7c3eEfP|E+ zO!j;*eR`$wq=vdW%VxbzUf<{Mr|LafSy@h+fCz@R;S*M`d=t{CCR?4><1*U45KIG> zvbu_~X(pEkou~T?Mh$(>!lBX1fbi4Kd%?t(2M;mD-dGyx>XE78ia--;9#+DkSdmnl*Ru# z$Uey%-DRc2RQpL*J0vMcu_vsEg9i9cq-YWhrS?gG-!^qRDXG)j9hk`26?@A6oE?!B zxJ+PRbO1hdo#53-M(WW26Pe|;_{c2(bne2R3GU`znCFtCSW@Yq=2iYmG1acQkz9CAYO0Eh8DBg&_QlV1l} z7a|hscL&+d(Q@fGWm+lekHBh)w%YT*tdP)XzR#I~w@s2)vB(z+&wpX0zX<@I?nlW< z^_xW&tMm$J3J7EhCV*0N`abXdF-%5ZvAMqIdh$^pky*N4Oyiqq5uA9Hk3XqaYByGp zjZ*^W%gq%;<2P2u$phaMp&-O^hhdM>t*hc*VGu{(4b8O5)orhpIRnaq;7YS0R9BQ+ zoyZlMZ?vgJ0)ke@o7d=&_#R?S4D0vBG4-fkzwysoBWMV-4|GkV#K%&~a!Z^p{R*l* zU#T}wIo+t2On)XG@fgSAtox3vL8z?BE9LeEt&Mmtsx_KSzvKEVXAoXKosD@AhgO4b zGC>^r@(A&0EM7m8Fjoz~_7^%4xq<>7SB5(Qn@6J#sfAT>a zQq}F)N<_1?YBp3BgF_Uw1b-+~nR@B5%h0WL(r8i?`P#33NnQKhm#bt>Jz8=yJW1WZ zRumV^!n$Ndr9I&%!riZL?JpeG$pNiS1d|(5veXF|rJU{x8>4&l3Y}H3P=P{m$SAA_ zc~|{N{1!YMblJOI&lf2N*}e;>!b-|t3Or>b@yg7cgRbw_B9;x7B)L?~x8CsW0r6mF z0)rWiw;j#qS=AX8S>fA0)is;T_ga34YmX`X?fT5jZ=GIgQk=AKx6s6%qzLvGSmP6< z`6Td$(RiLQH^EQiI=N8A!pKw%P%qxJK2+r9hKY$xpMun;%de(Hk$@n>1tVLSfJ^Et zB|-RE6pE{4k&U2?bWDUdY;2YYwAF&w8zw!+m#LPeU^%H{ zfRpm5qhTt8D7BqD=a-PdVROhGdhk+0rm}+y+~)aNaC0e&G_vl#Zm9}2$@d(&sHNu! znftg32gqLX!A3YTygg=j+Uf!Dh2(47zr8%hiqsXDMqn^V(p|m~#-ohqXBEQo;OI5m zb$&O<{GOwn%@(mOlGWx~pcb(|$lJf^|9&>zN?i%LSiK2W4Ld!PFNxG$xA&ydABsRs z%pECPg-WTcyItSKHZI^rCmaw6RSQ`u;2k z#(&l&bK6w2mt<;?`yJDhW}Zb(pU&Ce1gIRWcwVYC4%L+sYk+_D{v-0}IS3y%ls;6L z(&nX80tv@gR(vNCI4m%;&4qCsGm7F7=>b+|fTHoh&Jw_}ONY8J_tSckiP z6nntTgm)J@F`dc<%;fq(a%X3f;S#qxR!LKUSR2qeZo4u0YpvVaW270;d`)K>kb9{+&{NF+zv*O~Y=gT#M+%I*6v`On1RWnhn zmudi!f%OaT{O=viar&Y1P0WhZw};aR`Xt$GG?iDe6{9KDvF5hn>Ul{u2!LmsZ&l*l zJx)2{AnN`W@PRILUH0oyaz8r zd%5NY#snj2h7109o?A7uoU0)EZ8KWR`B8x|OZ=Ryt0;@t}v@hhnM_dVI&YylOYYVNO*qsyO zmeWSlzRNur4$qvO<$9aCnT`4)m{00w$YFyTdrV z-`l0lTe97c+`J5=;`$6C>}ga_7%r~8LAA1xFif?Kfn{I8I5k3oP$|k%ohCbg46|sN zA*(GZm;YWm-VPTk3$QU~uPg+7lIA>{v7 z{(q)v51wJwkHM7&*9OghIAAa`#59N=#HS^Zb2W2CmQ|{${fGD9LQKagDalBZ3`tm{m*`o&TWd z8UzSp(huIm4RXJ_(A~N4lrf-?QBe~voW&ilUy%e73uRM=K`}ie=2kUX=S{{P12v(LOoB`15CqnC`bH zo%s0p0>)vBWA{xNwAI-nzzlaGzl>YrG1~{-=SPOcCKeYH!*WHM=OvZgARW(Ohasto zOauV(br!4c#6~3wpZ*RJnjYV8%Kkva-wnFoOn{EP`XEJ%y6&AmPa~90w@Dfw>;{tN z@7>wzUbeU7NDc_^@VFk2vM=h=A7r^_vso=o!SAcKE3fE*lGkM|>CI=IG;K(S8;o07Hc`w}VRdXd;DuV)#1-f2GYLab6+f3U} zCwo)BLfn15k=Zzet0=LFfD_2Xvz=}NqMPKoVjMw-nhku z%GKSeKmCK+)zY&R67eNJZ}ILOF1Lge{Cs>oE!W$U@Op5jlIe9qP^7Oh#$T0HI4sv- z1DSB3;Hd=zyqNUAsOz9ZEmthrV{X z`wEJIffIVXAuTgofvJmSYO*U$w$^ifaxpZ&T~8K%`6Bi`kZ3QNgS^ZyPfk+U4UQQc zwm#V&Tqz3OpcNzJ@RbTfi0no@O<5SELY=~(Wln!aN6Ak9lPFKJprUZI+(rkMyv*X z-&^((eN5@ZiZQxU1QuKYn^bs0zgE@XIN{E@rmLsR^?N}`0`XB99H{_yS|Fe?*~;m5 zUSrghiOzwtkQ?iCDAIH0>tyg})ZHK?!B|4S2jRQ(bxO`itUs8$hjZ;e#B1 z;uHhFG+obmwW}-H>fx71|53vV@6MMQU}&tAW_~V)oE#s+MaOlr%jwcIIexvw-IS=) z!0~tE5O&pBslI^0J@6ZYL00VHOE_W-5fR^ z6(BB~JY4cD@k`J&ePndizB{Qn%ZYV{5oD~sIzfep#Lh6Y7TxpCK}1I( z|2kpe|9r;zc-MYG#zLYD4Ua`c%@pu()~t24;e8ut zvl>6-h-c_WRM`zdNS$W%Dy_^44PuJ5h6WC@4d-JA=5l>mX*I z$(yhrzy)V=Gu0nMgzzKN`Houx<-4EY8<%{xk5k3KkOmWEU42`uNv$am!x;Sj>7+WN zP4U=zy{(bWW{yc=`Ko=YLQBPRzKk6D=@X0P3@r-68i)NFnFc%|gZsLtxschlP0SuJ z<+F4fegV8p3IOUGPLtg#$qA=Tta*;71jgGn(%cXHnm05^Y4(gIYHZsOw)EK4?@s`4 zfy4ken^odxr1KP1CSG?$7$W2-&ns#vo`}6n=ZUz9EZ#Jhi}hxMyI%;I&#I3P9!Ec2 zo5+bs;&qc`lK!@%QhdceWS!T0N_QssCn*sQ1DHZVk>FLH+JwUxCN8O1gQRcbQq%-M z@~F|16~zd=X?nR_5W5@o*v_|+&-qziEgeo@jZo%{f&iNdT`vQnfLvAkop$|TFW~RW zNmW7!9OmfhvNIowGTq(G%ijaSnI=76Pv{_X&_+1c$FJs>XjC+rN^ReDivitLU#OIC z!@dN9?oG02#aAXg=4t<XD(-s4^)@ zF|n=h`w^!DcoXuS(-+(km+(%x+Y&Cncx{9qykVoGhjp4b)_F`DeYGX^v^rJMY(6r- zOX02t*6OPAdX4^A%p-bDf40M(;`&3_U>SiiUb}HyF*g<$%B92={-o{@ADofLukZ24 z({ro$hY2?Y76`N_9pvPsX5^jcjE0DGQ$aKd5fucZ^-d!yPq$zA1@v-LxN}&KTTv?b z&4$F*Av>i;rGboa0?2#WE1NVjHrh*6@d6Ij(8zKn6OrcL;kaH5>mCJQsGWVcl#s3I ztod?{#=`V-+T;nl0C-D6+W;vX_we@OoPFAp4m(=|dvw2P-rr}F<}y&>zf=Q^Ou~i{ zMa*lE$Un9pFJm`DC~&{nP>>jilm)H6qvEV&SqZs_f!na$1D`0Ux>-p|APwUUH! z07w`wKeC?kpwfL9`vCkDxQZtyC@CzkaX(!<8)vGU5XxZuw^33|wLkpK0Bb=K8eU_UorH~_{ z@})%HR=kl|*a zt>%lE2$sK4--R(N4689Y7CVZZtu#8HWEb~v-_}wHc%Gkp`d9NnOingo!+od+*Qi~c z&F>2xz^eC@>5N3iRy1-O_F~qxz;!~9)7^=n{8!Vz1b)U*3PUafDWk+E85(sg!kP?8 z;3gNiH0~2k5b_%zo(^S;c9OrQ!coz@6}9-;IooeCN06{=NK4b%3X(J$rpCblKSRy> z%VIMD;Va?eP5oq)^z~73j@g2Qg(DX7pIEOND_SWYBfkb0TDU;-C53rC;=um6Pp(G# zvi%}tX}FEz5g1O-w#^?oO6q!|%D3xD63vU-(B`P?G0Md*zEW^!)Ts|s{#^%3L`DkZ z3mJ-k5a;%Ho=mU<_sz|Y2R@j_y4adjH?%ngc-8SVLEQq7v+>RIURjAgF*}=LJe`f* zaqXS8;M8yW$7Vzgg($Eu8lMM;r7lm`YlNHy?mVbP%WeCeLS}~|DvJ8W3`^0(jHiJ2 z4(^dfXqKi%BFc(Mev{^8v5Ga?yp;pPf{6MP&J`^qB_*kZE~9I1{OkFu{hop5%Ji1$ zIidfsIA$i}{knfQTL6xF5GcA#TA@kBE5|tJ8ukyzk^XAZiIK03*ek>|i2p&^TglU{JVRZ_w4L9rM?rc*X_u3K1>*-J_V@Kn%oj&5)x<*@N zASH8<>Dx5*gFyb)KR6;E+ado3JW{8u@5b-ua7dT_uw!>8z3dGX@dLIc0#+SF2s#kh zelfrTg~NtamJqC4e4p>7wMq_JvP~dJ9Hyc}g+Gn=9U%h-c8(@h(nDG$!Xn?^)eYCf zPoyV)S*I`zTE$?5Z@yo4Mh0O&ugqH;nmBe%3H2{JENGcai+^1sE;-%se;3_pXv}SC zc)8|`Rs4y)E5Bnr!7We;c`%+qIb9~J<4(VIDFD+gM9cD z_Y-HoZ+Kh=3czPbXAS+G5R*W(y_tV0cuao$LlD)I4I8X#I^_Dvi%qk`BaEzfFUouC zI{_h~IYx0MKW)b#*9OGwm>T$=CoC<;#yROa7JSF`!DK>;zMu32u2*(#S%0GL!gqwd z@ezANgwhJ#&$Lxux8Lkg_9PZgwgzsJq!V_}+&5Vv*N?CUXoKi9WxNP;+aeaeum3Ta z_(Eswro|^~vf#WS01A;v%@GOc1Yd6wI?(f z)yTL4F0N-Qv0lKdKC|1v6IzLQqcp8Zfi z-F<{*1Iu=;PL3!c;Jf3Tn@sf%?&*JeRw^u6Pjk|Ypt%PB0DSRI`4I3rB!EoXah~b& zl9EHI&kcRfNO4%Ytr|rNIbCM2roDkf1q2xVti-L;d+>|Qha896Ha@P1V+Z>gc5DPt zU2n&+>~ylF|4{dWw7ee+T^aV8epAfjGE23&vVm*Oxb0*5XKlFJFf# zW=|tP{mev{Xv^({YoqCU2Pim$Ve_`F(S}%hEm8sdc`@&f_+gLgrOMb3Vz|Xs?SD+zK2z+wen0mCDQZUe-8XIF8K};tz=HO# zY*!loN*!+06&|^Ct0;u*D$COJw$eHBJ3mOZrDnz&4S_+T~X)FPEq(k(_T`H^?4$@a_%cDU(R6WJk1dZ%ruMba1 zBvc4WRr)+oW|)O>BC>9TuK%>V7Mk2Ex+py2+@0&^BT7kMo)tBERph8qtVzeqo!Dc1 z5#!_({Zwzzk68Qgv^|Y-_4N>}3bvLaqxs3U{Ygqn6qkg=Odp6P6GFppNx1kE7!-Qt z@Vc|VaLHsQCkD*c@9##OmLIy!^>b3N=f6D=!AnM&goWJ-VEh*^k zD9TcQ7RLV5n-1dK;9Uf4Q1_ zS^q7A+k>#Mr>I2rKb$NVHW&>A)nofA)2Kna6Q~0oq9Ce{%i{GR-~!!{r~p(wv4Uqv<@Vf;l3UtcG{06_Csf9W>)&CqN|&kNUbt>(DFgr6BEghk&#;+ANYBARtow* zvfQ5A1E0gf!s4#>fZkqW)&GpcR~9I~7**lnH_(Q01(HX}s+tsV)zv|*B~Y3am=ACD znoEIpm+LiJYD~YEzmkvO{%U2acM-98-*O?ZB_&j)u+)`y>un$oiCSvV-9=1rAoi{Y!ke#;#cZ|lU9iL8ei#x|S9V}*kPx@drc zNqB)tOs~_W`}RD5MNMd+Poq{v2H_+ttB`Wj;1c#@ceYcKv-LJvXE$-9lN<#Vf_5y6 zS0fwmIWg09U(1OY_Nc(el>b-GKXW1&5fF=vq9zrm6o(Q8cq*f7S zEj0dt_^Z5g-066uvt22RJ(z7rfv5?YDE;Be(P_1^8Has6j#ScQ{#fnme$yWqv-wWs z?RJ)oB;c_rC8xdu5~IcS7UzJ5%DsUBlqCAF!oNd7B$>K76pL>X+pEguVuk%)nW|e6 z#n2%c?aOG6m?iAIbQ={_^z&=rRiW7JQhtmS&s;Jn8@HB^`GUJkpi@+#>-Wf1s#c!t z`L@*?bk!HZINE;EMJfCKs)50%bu1z!I*nMIlMaf*I8Pqqn zV9A#?$dfi)>+hyQks#>@hv_opm~ z%~5J!FW?eoj?2j66LMH@M!|2Ex0@X{EZS3KhM5MN4H3JxM~WkND}$6}KJy30PCjY% zYHe!wHP0Wq4sVa@${|l=y{4wzOu0kY?4`~|tmb2}_Z!bbUiatJ#yMW2rU;g+O}1CJ zoSUO{dq5MxpN~+(v~XmK;Jbpmk-bhJQ5@{&N$!{HNkJCJ?dzEian>{@zm5<6qz~Zk z*KsU{ubS{6rk(>p*K-d&lq+-Rig)C+dDA~7jn#a#sdAN^pAWdslJHsd5m?Y zz|k0$9vmag;9DBWJDon=YO`&MNvs(T9U8EYr&jWwfW>IQb*4xj_u9tsMY7N2@=goS zbcXLNC?M`(x^=o!e(NHz0ql<2_fkfgE*Nb@Y4AslUq5sjzjqaz-ORwwtPY25{Ic_b zR8)EuBq`wr6tJG#e=H89)*Ly&YWX0wbWcOLN%Kllde@j^3AZPU(*2>gg_1D@3`eY+ z)A@uc-8FBZ`&%C^*Hgly8<}1wk1GN?O~^VzBHjSOyUVIPAW(@ytOw%s7zGIm1r%l{ z2sm^70qv50Po8Zyn_UQtf`g6Ft&dJyPxqVTEdFm0ty_I?PSwxXAtl<%;L=Gbi!ns~ ziHRoMkYpdi)0ADQiu!|zgAfT6_4wXAtP24ZM!frR8PgLQ5#g8$dF`QJNREa&^5>g!Vad1sNsA zu7^6Gw?}hL>&SRS{1sV{7E|8W;#Vt@GKKW?mv*=F#1zU-nt5R--I#)#^vpy$|JpTs z1&MW}5!&hw8@a)x9DvFT+N2*x^aHR zPBa4sf`WQ|4%;;<^Gvt&v@}kerKztU(YEPh_#V@A_P&Tz=7>T%oAiDUM3e;d&JaI6 zh^Mi7dm-X+QGvo*{^2vVu>l}9tM$FlW5&#!efMNMJiHu8QAkd=#LyF<)ek>>Pqdj7 z8~Bl1t7@0i zGe3CNc-VwX_xKa|>w@641@yDi-Y7QuhKfQ=5c}5Z6+hWjhF!v_ZJpF z@ZFgXPZey(SJ_XU*k)?A|8nI*o|8qL38!D+s)*3Ormw2CcLA?16%l zK9gU+dVd_Z@E@|eo@A@8I&IG3$NIp)AKGW{T&R|OD)xCSI^!Wk5Qk2J z8x(&HGU>mnuap+DfqVmm=r(cG_<~Y!(BQ>gh7>f1yvW>TZa9>2iCj&;V+$^`7W)d{ zgA-#T1HuPAUT@zMWHeWqqW~BztWqxz7yfUgSv=@jNqSeuuqluJIuK ztZcP@rwR4lADiWlkv)2?db23GoVR)hu&%#@xMR)>keMNy9{mV)ZtFaruZ^4)Kmq~M z;QFT@1U_JvYZk+iNX(nbSVZopOQ1l~_U>!-*A%99nNX;|WA&4U^yK~UTfD^LAM%;2 z9^ZTEq3AcQnJlMK>OR4Pp4+tgxD6OWKdNV;iM8RAt_AUYr>f`M(`C`YC|sC%i}f|K zPvL%kdlJMewwI+i!ROu_xP3PBMPlY*z(A z(Eu<<^W1`j*UFxEdH7-vb*TG?#JbtPLkL%;QaPyB-&%}?6#wKdz)=(bjWUJA6U;W* zATL)MqeG2KN;}G^4MMNZog9ml17*<~*Eo+iK=gAv^%iM2Eiuzbk0rkk_Bnp=^ zdA3G)UZq6!7fD(CUL$Lfd4b`+0N4o@snf`?Oz{_011-&d9!HXqHdVG9A8w|Eye|<} zd9Wo2+&zSA{$MBO-5F2y-;!k}7v&y}Vh<0e0h=n9*ik;pDs}$t#R*{@*1aWLagqdX z#H0cmmD*~?PdV}NRx@?mRa*5pR%7@@W0(dm#2n~3WTYq9_X@IE!ph&5H6?Id$b@Y% z@$o}$DUD87n$9hut}=X+juq=C;XE9`Y9XGR1bg{n+(h$X!`5&d4TyBeli_+#M_rC|pw)E1#t8uFbTN!W`b;f?}=s#P6Z4;1?)^oxg^x~O-7^hi5y@-aR?YU5a6?p(_a zeW62r=gQ!KMnCj^L~v5)dmSSLZ_2GAYOWg#@X`XCqG)i8QddtNAf`&?Kw^ZAXg)l04SULS z$=PF(jHdC$1|O2f&nPK(%b=Jo{X z0gZ^VHhB%oEF0xa8CZQV8g=Nf(rVgU^mPB8^|QXQELJyiAoZF_e%rEjkgtM0@@3}l ztv)cBzBa9qRuu#JYP@-Kgr`D_+5e!YgBqLpwBN57%EOZCN50JQTxr%bmci7=%WeWG z;Q7%)kk8*MLJ!6wHo7d(0HN7gY4D7WZu!^Y+`KnRHH6 zqhV2urfdTPZ-E>Uy~T{SgDfg}B-DS`Yc_lhuIf)%#d*^)HDwuwQN%ZwI$X<>l@a~c z7b^8=yI-El*{oOO5c}2OJDJ7?;k%TALhG#NttMMpkWMDAQMvaV=w%Zqx)vBlQxs${_VOAC9BdTZ_;TTfA6HUD zOz>=|B}oc^B%0X_Sk1XU$iTinawtfb)H?vrmmu18!jft>a{dd7j_o<1%|&ED8tF>8 z$7Aby_oIO@6D-sUR%^9n=_XI&*zDpqtwPgv@Q&MAAE&7<@cqBm3stPovlMb35_X}S zaBUIW5i%^=5du@-(eBdv?6t+^Ubkg+?vnziyiYhmjGg6@>G`}}IZwU}#Fe66HxA!O z;fi*wiLf0}?V$DDHjPb-Ck~_q@TNrgOIjCWTseZFTn)cK(;4WwwHB+a_-AlHN@Q}? zjEA$K`7M~F$z|7kF6_Z1Jwc!KTrQ^&4r z`9U%Rz@FldL7y0+nedYyl@tUEnD@ul*ktRsSq{0*0j$OeRVm=Hj}oMlRHt!HNSWx! zIaG{}>p9JS8EnS+@-TizV^}@7hSAwl-vo$<5~5{Y>F(Um7SJUX*Q{I?C;n=x8|~qN z;&$rSr#tp+nrttx4i}3}L=@T3(-62{0$5w#f9yHv9`wDA#9Q~%%l}N>I-zDQF;`MG z)&BTC&(FeQ-;C=si0y=_OVN2d(SC)yP;zKjB7TiQ(HV3pD0MHy*nJa7TmaIOjd+Sq zCQJ&v>k15Hmw-ff+r!aZJJTJG34>1Otp|NO{Ra-OHz8tuP%mb&_^*ghD3R$^u_j`! zX~p9!UAu&l<}G$=h_?N{^lTGMp#pw)wcitgh?ZCM1y-{qz_pE_Z92kdq)p0r6iO7c zIt`(W!oWl<85U~sC}E~X*em}v+irxQFNK+`Oe*g8LHpF+WfAEdwqJ&_`J~a+#V&)_ z3#5(5vh6wi4(|-FNiqA5o`!r;Dd)c>qImVJ_E8t#OeFBBDcmS6HacS-ZFc7XeG#d2 zIX`4iY{H)HZYLz>Ij&7>UMKWREvq9B9y$=5f}q#`fX=Xlog#_!IuhUbZ&i(9x8j-n zYyfF2VXc$Yz7D7TBccyWe-^5B>@#~oA^)`t131j(P$1^ZBf%Ccy!Cqza2tDCH+7281C1c zn`>KEyNuiM_`MY6PG|o6zX0aOdRh%4O!}Shf`y?Fhz`XwA%8jN!i04ymNK6a2f?(% zulPK+xg3!o$YGO4COX54i8UIVV$t3lIfox{vi&{?bnuQPV+ICvw~KK_MfKq<9}%(J zNfeb+W_OGKs0kZF3C4Be$1dhdiQ1HwQ1w?78RgY;@L##*%!v{6Ep>9NZazu)eI0Rv z(r`{{_Q_USkt}m*rB>h(gYUo$il?Of zZ6HMxoX^-SJJtrPCEx(*I~i*m;MZ@|bvUh!JZ?>;p-Bnj^a*gFOLU&X-tNJYmP%_3^Az)1z1xZVek6_!|WL6A!{+z#bTnUX0q!$@eTrl zTqmZ!>yY=aiGWFNdriz>a9|sL%WU3{7Tycs_8VavY?(o|00ri}+=4py14%8vl=1|? znoqU+FVDQo`F*8%8sln)dbHdRnr0d@l7bUFuG!7d1F1;%t-~bK28byA7C-e)ZC6q) z@BSXI>}p|@D^q-3SzG=;ud}hpPedanxjr4mDCxWv#ac0<{QY{$B-l#)gKt&`J&rN) z@r|L{-?bk7e(C?N^rNfEBp?7ODm{l+n^L%Xrcc{JmfK9o{D*1r7P}WhH&U`5pSQ&q zJiyGUrs#+y(PBD7Kv`_C3;6;E0@RXpe?R;m6o%mQNK*Nk^y&n^{aeo6|m4lo->9Z-!0M_f%rrCewygYwQuS%T`FVLDublF>mAidV`F zVludznPH@Qb=LcB-&k0w&HCOqb($5gBuz=WFr(c3RA&$uEFKpAb|oXCC_?kOvcQzi z35p8eQMfx7+blbhnRibv!@rO`R#Up>u~Itmrfmivso5hjSqNR1?(@QrFW5WiC1$e! zTiX`@ae!9+Ch-tf&xHp^j8I^q@#G8@xe`xaOxp~`SejI-9NL^&a-u3qq()+_j1J@c z6WUL(MJUK+Fd$|Qq`P!O87(YYKJ<1jKCComq6hOvv5U~65V@(+CSi#t<(;Gfh5A76 zK~&=#THenbwfYMZXw`&zK?v2wxWpHIg`V%@pzsf}6F4iM%*#2YBEjUonP5@c#Y*v8 zxT1hO1-i}eKw+9|haqYvE(H8ULV@O!q4bz+U@+QCGf?E4AUb#k(f8;?2f{O7DqNV8 zyyWOZ44~7qSYQEf5o7nuOp9oEuz%NSEdLrj0n5O{=K@Fr8EZPEb&E&&3jO2J8>tpR zdat)Mhj$jFE2~(rg563%Ar|Hy9FA0H;nA$?KHw=);%K-`@I;~-6n#%Fv#g+dmS3qRltr5x

f|VJ(H_yl2 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

s^n_Dn{La7qf{|?HU4bM8{qG*|yEG91FJXQ;tDS zJ|3~xC%>Y+ypO2kgiiZU4|M}9%Es~;Y~ld0JCF*=`VZZl6gKnsfaxatcD^!V!mhZE-hj|4a7jnq}X9p9+4rI7M*)BCm?^V-*;1Q#lqy#yMwG=(H+C1)J zBzInr1%$#|zdvAnC|}OJ$zE8GB_Ba)-kj){dkn(0I-GU&Q40S*?7dY~99^JDg*rDnRa?HAs%$1Rriol`mf@62@~o`(Wx$m&s#6T6G+a*yE#oad6ip0%nZ zV`lzw2?-Rh&#{y?FMT>CWBghq0=xdSWoq;`HkDNX^h(`MA}6X}k4@s0M<$Gd<-o}E$w*>>ky-miP zGFc$Me-TUR6*eZ~a~z~w%7fqe0e|Cpg>02I$o}?Zxc%D!qD~og=}Bz&5|>yXg5~#7yzO zTb7krJ8Y9{uSIQ;>yXyx^x00~iwf0phiQ-n-L|tgkyc${%=d^0%S3zZ{(7k+tWiWpmS~K*=>f2_sj2Burs~b&chYaq zo{pMVgot+gts~7x00$dG`%pTKpC^ieg{3Qnuj=%+VL?yp2xT#MxGz445Ty^DnUI3w z{(N3xsQACD6wR20E36Y(6IfW}d*qB#W)Osb&VKebx4IQtR0MWjV1Zeq_*9byxZOXB z&`Y3r1sHw#CeTW!VIPu{n|mATY4*><7kdZ}5iCf<-#!gwGiy8(N?m@Bh*KIZhBSCX zvoR4`f9AvoLGUYo(UGysIjhkDno-CIa>O_&xsF9+Qso0lt_AM3*@&e+`GfwgdLA zENl5{uzx?!|H{t~U+6Uyd-K;X*asrg8-2n%U6&x^-xCf67)HJ@$}UL$eVSl+0Iy;w z&+iW2|27;Hz?Sr(m&fGOU$0{9Gr+7F>5h5^f(8;e{{M|HzPsIq`=aqBH@88(t9RG8 zv17v1({tmn_{mj(|3+awFc7A&pn$;+^Zonm%NfafGv@E!0sPD}%T-lOm(jNwZC>dL zKGPrftF`fYZIx;*R_QfUTuB20{Q-@=OZ~31_0RHJi4N9GUoXk0uza=x}V$rZMR zrNyyPYpC!jD7_9u^p%yBhI%uLP(VXL9M47HZ5Pq)X{6_sKOzxZNnO)gRSPdk&cJ}g z{Vew>SZ0Kp?nrHJLGxz#_|qaW>HXt9UQgTZ$tSS2b;Y8=DCK6;X)En%3m}J*F_WO8 z7?9qU4h{QO@^=&oAWHs0ox!WMVQ(bg)9O>T+pVR`Ny9EVHMKmU=iN7WB&1`N#L&=C zgXKn~m;^S#pFe*#DyuNlLO#FsQ2-MYdE7;tFZ_~cs;!zF#)l7}^YinV zloSw928!W6ZLU^j|HaD6T4i%d0e^)OwXmR3?(<6)uCSz8Q-+X_1i}Zxc79@zm>xzG zmDs_drpoadrhjN80oVJ`M34)hQa)W0y~gzQfkQ&V89l6wjpKg)G`&n~N_&fd*+;he z903A>G{@$eE3JXjax?H=2Bg@|FNj{xiFT*EXJ!mdEF9vPMvF;50@YO#NLUntnAgWP zoxYvntd5lhP0h^w8gi6> zdVC2uZ6UvgCy`&CM68ijlk=zh(kSdsKX#+2s7Q;)P+6vAlJ=&tp+Ve~-?zCv&HX|Q z9a@<{IWphYZu_`yB?bs`B|WuUbHcGnbRHLk()I+?XE;r#sO~2c3FAnjU{a^!0z^bE z07Wre+g3oaGp?(vOXlHSrOI8tI2yLvABaT3^URN^Z$~=0@>_U(Io}nldb56}u!txQ z2F~%0gTr-9V&b1TD0XQw{}KAl&y$v8C1q5)3onk11VniMwK&CYpIn?o1|m#^pz@^3Mv;Ji%{{i)Der4#3lw|NWKnp&HS+@epchzAvxil2UR)GqEsXJ|J>k zxHcrEu9QBEgn5B~PtgY$Rcw!~cgE<*eZ8tq_#hAKX9#rKjAf`PD13jrjNd7hB?-;mgVj3g}0SCL?@xUc9zfR136>frD{zaW|)> z>_HjY8Mv%C9yp9y*@}RQbFaTJv*!~>$oKC|rt&9{LV~`-;f5}I_&qK{CDFMMH zWb^Fsi=;1>34pqh#P13lhj{1JsDiuQl%mWXZpWh^fReMgxC#6(27wsB9g5j#sNDl= zBc1oer#e6a}qdOe%uy2%xMt?FzqG)af z5e@8wgC$ATTY!*18$B)_4q%Nl=+yd5kUjG>Kk8|k!>~Itjn)*_w+5% z|EdG9Rw2@M7gs5%S0QveVhk$U^L&On+K@@%APL3#h*@it4$g|?JLhQjzW_iCZ5IDu zX1g-?^gQ+xlat$T!9od%OXcaF8Z#x@p@jjSxFa;Oy!U!@@KCv}tpcr~=gY2#Mn}ax zqFt(c53hi(SwEmEC@$k$@FfEo2>~JT{&Hu)sqGO?$&)~QC=>lT1I`%@l2)}WMv(4k ze4Nqt(1>@E8pmOJyT)vG0O8Y%KvgAmX~0d8f+|8v{$5h=A zZA$a?$f9BOqTx|BFTmvOZF|N$$vv$%M;{& zO()=00BrHV8#%Z1rh}eev=TPozB=jo9X>%dj-JMmn(5&wisTWOrJN zFq9+gF*!os#Kh#conTyoc6ZP5^sIKT$M`tg^UR{vpq1w(UPyb(bHnHg@@dP5!m=X& z%`SnE?-921C_W zQm6dGSpduRlphrIbuW4CMG)fMX5k{F-Vj0NE%P*G;4u@YKO_^X+i_lRvZ(ZgB)I*wBBMvXN6XCU# zONBS=9Yq`Nd+-eKk0hIa?8A7CF6*#(m~}smWtgQqk0-W2KfVQPC2DyVE;;OK1?IRY z28sE1AYc1N{b+sK{!|y+=-~pyh0b`8JXUcC(vUS|WErG(wdIYAt0JLTc=4>P6iT<_ zANbji0|Q;}kE=%qaF=3%OvdjR)NviT6&+N2+H}ghmItr#!_+)VeMLFzV>84; z=x(}~JkY5@uj9Jr3Dgb(b_Dge3ZgFnRa;#x4O*T&foguM99}X|(V?L4Z{nGit%|YK z(G2SNr{nOp+oDHO%Ids4nHh^YZ!M3{%?B45yo{e)7Fla5x#96PJ!ty->Y%~z@#QlsWhS))9i2?0Cd%UK<-POu~<$+)=Q_Q14g zZ;SIdX%ntiTdqC^Qb@4Er`K%DX68@a+E-Uf%_ohATX?H?Fh{E5$F*I+F3~S3x4sRj zm=;};BQ;xkQ1{*2iI{)d9_bgX)VMssvEa!~OiX;6;ucOB2G($cT3Tly`=?K{9CGvT z0m1Wzd14hMekfn0QUjWb*+6^qRj~tHJpGrP^NYRPwnzu!Ve_b@Q*^KqU?-<$CK2}8 z6crWSA+ePSP3FyRQ*6Z>`VI(|1v0R0OMHFjuDWj!(M#?4FF9#>Dei0RcCjrTzTROz}&0v(5ji_v>oi)c|BXFqYqg2 z#dgA?^lO5ds)?UdXwNrNBK;a!YAu)Q24XcWH$B81;xc$mrd+*E z^4G`va5MfUHgq)rM6cbF&HyN5Fd`Jye|1h45f#Rjt?G|rnV6j5DwvyV+w6@L+EAm$ z!EpLlT1$YqgC>zPW0< zFZ_eV9oBo+HJ4R9rv(pVIdE54&;=97NHs3)@$uR}sxd8o*+`h2rEjP?^y^b*r{-H#;BudzMvwi^;nNS2>19G)vuRODW8YM}V`O|WvM z1@`erlxFADvXvVa)0fk|>zF81t7cUypEEjTswGBoR3ogZYO>6adx>oi@jx3Q-tGSW zWS)@kch(@2+s?}NX>2OCNE~L38zK%k*g%x9xZeo&?~#GD@u$W81?>o%b%YZSH;d~O z)qHI9H_d8saPe%3Pn}HjP+%x)=MSl$Ec2RukL!&;<9<*TTMG#XiVeh&OODDv-anfg z{}{>v!q7KxQBmmYQg3Q}^$AAVV#0HMPe;yP0y?ipe{X)oD&ky;1wt498Dd-`yvPBF zlDBMG)+N!soB&`XrjI=38W+Q4oetO=;a~txJxgRxD>gxc4*ha6>E?R`k5(QNXLFe7 z%bpf~3$$Z%U}r|efJunubggK#mVM8Q*z`U}=-8HhKt1}rmoVu3TzkE2r^+DFae+zM zGf}5ibK&dW>l@L*g$1r`$84m1pyv~Wv5USW%|>vAuZO+dY*3scsuYsS?bMHmKeX9E zBvEL$s~uMT&0%jhzX!^?w=9*(WFSY|;nCiS=cg>%85^|o>1YbC^wZjBSM=Qj$9C$% zrhQbUbJ@?=aYx-fmogj=PiJj}@osDN_INYjB{P0o*^m%aB1y1Z<+T6?JX^aQjpwoH zPuND-`F)u}Vq1xAPvj$a2KFl$pB6scaFS>hG)7R}3KOJ`s;IV|@t-stt0*-bca@pk zow2yAy*%1DJ2uPj%Lnu@v!{}4>(6Srg?I7CWZ~`ok+M#~QiI<>NW`<`sAKl?XekLr zSCx|sKEp@L21m`&Yc>8_W)r!+s2H;b%W;R(8jH(^&;{5}Ew2@tCq9@sRaBm4x2i%v za66fT4MywcW-b0_k(3{{(o{LDLBAT z6Lj^2(3W`3F#uRRKDJ#u{yrdu|PH;Cta*xJSV+Pr;qsqnU! z_4mqsNSod9S035Nnt&}Sk?rLhTZos zf(i(vV(h;?qSHt^u)vmB6OkhW5k5^$3`deYyO+f}&l+XT7ZP^3uFnrh7Z8Mc0?W)T zS9|N2SWdZq$;IF@UJe?b3-N{(K+#xpeKsZ3*0!6>XbJlM}&6*v78g*e&w;Bjl!HvedYjv>|`HCkx z2<2=&LncaTJ}vDw9=uLwU2)AJ^ObTiUbX&o=<9TcDwhu35BAmiXuJFx)D?&3j8b6& zj+XP6z{#~zg--}rpG^6%47S&4o%F+m=Del7?MU~}zOLxOv0o9&cu&@(WQwh0l1+31EfY28;iN zyIay}YHWj;&FeYu(Xd;EEL;fvuw)`VV{Kes4@IIj*~lA*dmJ+jIqQsb#6u*v3BU#d z&Fo|`hKsId`DH@+wP5y{jZmNc+mo1Koz{zEH^$(w0K1{aHz(^^nl8MJbt|vIs1fN3#Rdr_ zNuH2N%DLYKsWt^QbpcfT;B<f7VbmleJ_GfXi-xL7U-4gn|0=Pj-qL%3_$X@ zsn{-Dle^`+RBYR?R&E%TrZcHP+Dy`Sx5)cJH+sUe@yAP%+H z1)Q~SeT-v%UGphJ`WV9BIk!JAokn~`?= zFiYOL`}w^uk`~@Xat3(o5S zSR^&>c=Bz#0yVc1LCYij*x3;Rf6D{QuCDj2{3ZdB2)hx6-PgOjLu=bYQzz7aK;^Fh zn0;}9;IqQgz|qC^cEyHgGvhB@@8k2`yXdId-SB>mhxa_b^1^U5ZCYUVaC>?7-+!*L z0ob*r&@?-aiDnK0MR?puU~lVwbZyaJ)ZFOLc@fJr{uQ>q^RuJL;VG~~OdkCOx~U-3 z1o+|mnd?nU{~Iq01F(6rw~6ClT>JyXC~=1W7LI0(%D=IXB`M&zB;C%^^cP0|?)`sp ze!&BZuRs5S{}3YW%m8%m0;5{0{C5UKGLnI@!+VC|vj2d78~|zO@hhJBceSKr(!jV8 z{)eIJzdFPjB5-qQ9m;0P|HgY1z<3#2s`_u$6M!vXAjmyzrIz>;o|2M~kBEXzypP6p|H$DO8bc|L2g?jFF_a#6w}BI35TRl;Iob+36= z|NRy)SdhFH!0Ouq+LQV`ZkIp3yu3_IEz#wvvq&p^4+{fu*bnN>&Xw*bH=`0@W3ZLI z_~PF+#E*7H3hgQ_SX5q~c=W!PoPhzHpyOBw0RCVtA-@k8n13u`x5qj8{%P(~1)q8ESv{*LdIJvtYi;cA&T6wNdlI>y0fPUoNS1 zCTi0lV9_g0=REuGxdG2C_7vO|JV}FKGioiU=&b%s(O0*qq$q}6+pCjc9Qb_bm ztvOWXoy0=0{gxH0f7h(4j)%18?UEf(#Gs{)YSz`&&4p7KL3Y0JxEc=v&|J&-a^ESn zws^IZ78Kq$9R_{BOcV_kXDlwBE=NPX_`ONK|Nad*-)Ku!k-X8| zr;S1dpNU;Cq`3!S^Kk7v<(k){9`i#=Ys;Yw&1ILPvnw#``fB!mAO-vgz z1w0j-47=CXS;Xtj_qT0+Ll@gc#pN+TYL-YG)kUQ33Yeno01jm7I)IIt)9-!Vd}^=p zqj?2b%JBTO2WmL0pr_;O*>#foK7j?7+hwQi#I`%~oE9#Zz04qfHA()!jvm8W&rX>8 zHk~Iy&+B3xp$WWq4%^bm9up{k_isdCk^4t!XlVF5;?mD3l|KWVTA!TC<$^ih*ZZb0 zyn0p~d;fv}76oLJt&m=#M8IzmJH)?DQCc)V81Q@oN`Ted3CUIopFUf+b@lev#dZRE zRr0l(X;0&^7ZXG}F~tHLe~{*DYwlYNA`T6FE?cG6mztoh2MpSU!Aa38pyu>@pA_%j zW2=prWH^}}WjBvBK(EjHz`8e|duuGyPmYzX!;fKXO44ooROiVrWn?(sjRe9*ijk4= z*rG}{F3$5gIe+_h^mV1+{jrzTXu-ej2U{5KiiYI=tGtzkMXXW!Gr^Q9dpTCirYC%o~u}pBD9_4yVg*nRWKBWsNn56Sk7q3&e}uDC@vyr&Xw?Q6_j% zz9gztBuQxq+6S)bFW6MeZmxX2aj=-nIA(4n5)NYf3XWj2K7Sk=Tt;W~o%1m%+7r zQM~uL?~5ih$z2ykgacza!}3##c|eIW??>eXYGz4N*GXbxJZPvtqQz*+$yJa@Jg%19 z?$nK0Z<)nc(QD3?J^>jn5W%$bi)Dl=&p`?0KOntoxu4^M@h68#tvD%Cidl_RZD_sT z00ypXbVv3mPVs69CtEzbQ#h5Gkg6i1hfVFw+IIE>8IHNvZ2Ur@#a{;*BGUe(T2(5;Df6A z75Cp@2nGZ4IV}p%5@ind5WAkEEpH}mT3VTLA0>*pd3FG-@MEu=J~u+& z7JyE=zOdq5E#<|+_;7^B1!>iWwT3xTEv2gC5CE{LCti&H@lo0{gfnKtuzqcx9U;Fi zN#7W7Rg=gcI{}36KHhp6uiy%SRo6T2D)V{tO^^UOP*196a38hzfBVog+qU8v*ni+R zO^L1%q(5tY~ zoDzF}$rh`wTxj(l4R8*1k5w=$|BHm}cj&jU4kv1B{=Dg_UBo5Q(!*Nx3qS$mVh^P? z5)Q?r3f*Q?TL_!vL9edk8!;7?oMrPp#r!-aCN^9)f$STWa*Z18^yJ2n|E%2xHo2y%wP9EB# zP8*mfOIZInUw@X}Ea8|~FJv0G?p6R`M&)>bY=Od&o@ozg$h;qnh%Ej(R9~hbAm9@c zVGS7K6_%DUT5j9Bt`UWF&xiObprGVl&;k5T+#_Bg{34@AK+vq+i6fuoo3DRhAlH+O zNl@en98A9g#@-R`$b(7K9|FkM*4B@V$`UdRnieJDP~vFLL>ps%zQ18SL-3!eBF#%! z7*zkc_wMv0;hSgR`z)o-?zEWs2278H{Sw(-)72a9l=)N?vU#L|R#}k9O=0!T(f9o? z|65Eg5@5e~>I5bKLSz0q)V*76$7S47!~ar?GYuftZgw>NrT6buK@SXJX59~s|Hjt; zk3H&o4B?T+-|oudPkAuI1zc-}5$c#&=@nvxUuJ@*2zYpS*xA)Q_Sc5ZCWx%nQ~`(< zPer`-yZCrx!(_d8RvXe}cUrQ?(_Cq19KWE-V9F_TdU`}JgKc8aKka#Yzo{PF2q*=JU1eefV{e4L zZ|Ps_Gq=*OIayega&sX#R)OyGate@kGr*JCf!ryh0fD-XD!{@LqF7AF8Y*aepP}nR zekmO(H7G91>*!dWtXEW2tQ-c{Ze7DUe4*PX%}oa zgAb`ClMruqZm6F4lX*hHLP$Q#U=XsW191@PT8)1f!t{dlz7mkfwYLj;-?B|R#KjoX z0$D05L^%i~A-cq>?rM}u^Ct#$hC7o&k4>Y*ceZnDCA-iNB&Ak|8M`D%N%)gwo{F^F zDkunEtZ*J2r@4@ms1wTx{`<=J5G`%6JI&gC1Qvjs0u#i)M#tP8EKxU^6 z`(M_n?E{QdrTiQP|D~Dy@Z#lvsDFR*hjRXQGa*=)cd3DaJ$8*r4-XH&V6Cr}_;a3+HZ$F@LGn?9TG?(U9M(7#PstQRoAB{S#> zUgx0U?fUxW4IiDjuz=EgR&ZIc0N2pdlc0KwYbi;VS$T^BDH$0oH9D8Mm1ssojeuuo}tkEOjIu=q|I=r6#-7r2o`=KAvWB;#fV|KG-n%ukw1xR-&5RGy0S`y@E4KGHqb`*ho2QG=t;`^*rieruu+FB0~!rAiGYP;1Ul@7b1t$DnT^sj;)w+Z2I z5E4hjJ*3cm6lChPk4QBp2He8d$*a;u%$!iNK!tW36;Vm0(mvDvbxt-(HaS z#hKL{;Tq7C)7~NqrPPAqG@2cSf`K?)L_@(gZgD5RcbK=$FwbVGoe?+DE5q-4^v_vr zu9|I@fIZ0E5I8gtOGXt+WJpW6B=Y0Ez>xwZVGLR54N4C*i$&y6l|P7f|sL7`6ln8j# zOVMZmNR0>kqD|1z&_+(2etr)Pl_nY-7?6fGL}xxE;&uPg)g|ntF|teqbZ&W%QwyB{ zue=@jb|CxZ(+3MCPNRk@=GzY6g^=F_I~(f-gUHEo5a(yh(05M(y~;e~FD{IF8*_8D z+}tv{$;rto}-t|0@9|wa~k%`tx!t$7r zQ@ur1R7`8D`!kWV7T%Qq)`5As#-yF6NWV<<5`pRhmxKg>tn|@Mi#*E}^6ZalXN> z?mmsniUL@F^L3}*bUL1e=ZIgb4O8_E4QrYJt1hfVpsNvp12RoQSLZ3|Z@$!>ctiEd z3pL^sk%JeMAt9gm^`n_=Hk_F$Xa%c1?(JH31zYucEwAj_qQJ>N&9AHR2}JJR^Z?31 zeM=2kIFJryr(6Asd#jF7F46Mo3;6O2V`4h7e4L!}^sRTKLgpg|_YFBYN^BMjkXgKM zAHO~)g~LhBV3VZ_OD%qNcc0Bs($FwG;IJX>HK+1MHr>I0=ui8MBbvIX{;G8?-gZd{ z9pU5s&a_J3*tk)X^N82we(yHt7q6;gV#z^Vl!ndgG2M$?5w4`Z<`s`PG9I~leAD*V zdqa0s#NJ?3alg)vxH)q4J**>!ISS{2W2y(#2zX^CPRq)!P8Ek7-C}(DRSH(=ZS*ZH z3No_1!JIkCuGQJ`o@2T3B$V0*UR3nMzP|lbE-ocD%>J^hNOCG5p6u0Iw#~LwJ$AX^ zCeN|UkemQC+z9N!U}HDSlQTTLfOC#hlYa8;Z{S*vRU6m|5Qc^I8KtJe?y(IElM$Xg5@^`&+`BmJBt z0Ri1r-#QJeIbxNl9FTCBy5^eZUQ0^M>2|t-z)G}XKMy1DO^HtwG2gvD(gAOvm?um2 zi*uApQMC5m4-<(mO3E>)z`M#|JWI0CKB?}YMtlBNI=R*d?1V$jL=lLtN|V#c@Kx0c zgeegY(%{81+Mft8jIJ9nG9VCpRS`EZsIloL;$+f7M@L7bpi7Lkd$-9Q!J9CMp{n`4 zGG9$(*i1wu*ZpptGA2>g+tuUDtti?MTyj;P(^YFQm`_|SOm@!kf%no!|LWzDNPugQ z{fEpC2IL9lI1&#DixjcqR{-)k!sIPmm^^hwfv`Nfi7ScAn-NVy{j3wiPp3y}G&s{( z$B&*a;UeTC4C@f*dG3urpYocQQuGe1X|T%O&s`&kzw7R&gu&|dDH>lgO5|QAi zwPA+^vnjs>`%;TtC0U`S-7p3+0`8C|#cHNd5^)xzHD5YHRmE{Vl?$+P;JL3_)aGQ9 zT8Gp5V7XsR@{n(Q{s?@+z#}#B)2$sH7RZw=_I6Z(x@v1N>+57=IPK^mzXkm0PKr@i zRz`ZLk&hr46y|;$e zthjlH?>(SN1x*$&zukKtpO~2W1S4eLOqinV6o)zAwzQ-lS_0gwgV(#y&L055fp|O0k+B4I5L|3i~OM+K=HTdO2QNJ7C z$6&kO5E>-~CTPruzKq4ED&Pz{>*4ejDO2#@;9Mp!gL9+ z3}sw}g_+OY3C6JfeRD=2GQt-R+TzqhyJVPdtKm=NzT9_`5K?@HQQTgSm4 zj`)r(ZbEq(zf^oh235w2>XPhWk`Qp&PZfxWKOtak1in?DFP59<{Y)-yYBp2y*70p1 z2c>M9ej}i)4tZ^mN0P><*VBKsuNd|)(YgvW@Wzu%(nqte+)+-sgd6o8m?MvTQqFjJ z@sV?(XSgBlsHp>9H~m?*G-MFL5&%{Z21hEe z%@h=}&YkTk$ab<1!Y_qLsc2{*UGT+pEc}HXG^C|dJN#p!X=B#z0G;i3`H|58`X!*U z9|blB(Qwh{`}Iyg{17$*WA{sp*Ad#%vAIHuK8}IJUv?wYHIQ~(B?nZ zRoizlef(%48X(dXVVBid*toC*FUA^6%TzTm7WQZ$p^tBBZmQzBt6J;@ z;1Je#AMedTEt!9Ynz&Oh}>7@%#FUs7OH4zR#W3(oC?4?6kDP@4?iOddD;YK|xRmUe$B6Q*#3j z@#$^zQ>hk#eS$(wJ?-ibB!vL(tdEDW{nPk((FlY@QFOY8-*GM{VUXo^*MQ7285>ng z?bd2yYWmyxkP(Zwr?b4mI=pannvbs6#mv&u|DbY08S{B06g6Wi3PM?f86Q#bxpN)0 zLSv89C|z*vSOvo9Oejhzfnu2i%_o9QpSHX_RQjVA!da(7Pyx!ef;Y3YgNkFFHk3Tu zn-N5d;6Az8gOz7dho!UNke(Bf{vlFOT4j+jUEDmps)XhqL*2q@u~cg5FOTVTZL`Ft zmASb&N2q$c&J(&5O@BcltxsD@kMl9oqeq%;mqxX{4ipOV#1}&O(PZ_L>&jx0o zH#8mlHi=2+^)!bcSCMoDtE7FnL$zjO4LBN zI(UJoq?I~6SzcWAR9ZDr8=9jr9z$RejMG@+Y!Io2h|tvc1}jF z_(jL)*bQ-*pM}NLb@DZLAhvlot;bj~AGzkjV!_JPRw`T+=%zCXf(t&0SwCP*drIBZ+$^d& zR^8fq4ncPyCa!{!pz5KXO0EjM_zQkbmICS?EP`h9L)S4d&m9PH!F*7%P0@MYR(BH>GGW} z4As?La4-O^+7NBCV{}F0Y5)Nxk|jn!v`3q4xWP0TeQKrU5s~_g4tZ3mA$DU!cfCZw zD2(kAJ8~o^=iuh4?c99zN)H<035(Z zllHvcp)*UMNz2w;#Bbo&lZK%#wRxX{OSrC?e!rTt?gp(4xk}t5miCg-1@=$G zacfBk^7tpipVlQt@RFL@>BM(dJV#|{k3Lw<`8e|9;*lf7TD|SIw@Q);O$n0|&0epN z|AZ9$!hm~zztOUk)bU&C`W+@Zq4Rd;AYfmcG$zOsdAZ_7g!_A49SnUP0Wjik&|eL*DcXU&O@wOjB%&NXL+ysT zh~xr*oGEE|uPnZSloSh%Uf$sm;&A54M;V~|}r?m7AU-I3O?V~<%6WPf{> zw+ad}!zAY-Teb^*dnw~Y6B%TnG|Baifi?tjHmp@lP5$OEP0FGuDnRvKZ;z1v=<+yx^t(ULoO7p819|Sb+7rVTna#I%P+{lL^0^xY2@1;;i^xTAZC4NG^a(P$(E{&%&wqRQtX zW>Y1uBb7mtWp~rQn9LMpqS|FmI$2nlqG_<9k^mQ_Xkh)u_d#ifCB5D%{}|%UOZ8)B zNT?7tMn?Ga1703x%U%sSbB>jM~=Sa^Uu83BhDEHk>zpb}5eeWnplUKsek%Nm;&Xz3m>ns zqfIc5kgA6@Ygxw&!6qJ4`$u*Wp4}O|kTknEekvRX(GO)09ZYnR$!OxfXtyxvT2(KN6f4_49L(<@qe-5HIEgEhVDy#brs#ZTzGFRp4>OJgBKug1o4veQN-_&k?aVZm3pZsQ=>fYQqbw}BI5qg!vH?5 zyNKhd;x&()Dn%t=b0Q9sqBB~T6}JQ`38~sxLy}HTH$EfiMvv``cS(H3pFJEc?ljH%#S;5zQLD|(31S2>Eo z7WVlHm-7Gu9b5SBro^{#^9&y` ze(LNz@WHjpQ+8$>%0VVM1bNNA(z$BfZpLCUCE0vss?_f03o}r0{ZkyRdz7peT_-9c z;4fH~$fL`#zZ}3Q+g0V1IAe=By>Dz{ov}2$kpTFqy&3cWu@7P1KrZLpKrrYM`2NQ~ zRQ*G5jGxR({Zn=n`FchI$UfZdXo~%(Eb{-3qhrrvls9ighfBj1mX!kAY)#ss%I@y8 z|LNUOL3#weNq5Ba4|)iI72f@uW+UY@1EX@r&=2E=-wnvl`TJlg)A) zYQ!~!;R|PtwDJB*VYDMd@SVfLzXfM_6$sz=N-E4z0pZS<3DP{Uw^fvu%xKFoKy@W3 zoTEs-cqMV4;U>>g;Te`Y!`9(t7|*$3pTYXa6al0zis4dR>PwEhRo%})ni5j(h!0O) zcn0QE_>oS%+5S@#Ga%rl{uZZ&8i>8IrIKAXKBdncZu<1zxZ;1b_*+Z#5%7MaVtN_u zK-4M2i@t#cq0lIfNx4ez$qNBEgX;ND!NBirDt_vJc$&@0oz9C@u11!UUy~BSMxu1? zUHD~PSrpB~|ER|te9-khcBQBLQ=9%%aCQ8L#LBWxD*rpW=k|X(sypR?n^KlfDe>=U zyBn%;r^M=IUJ@ZC=oCWAE$hEqOaCLYB3qsNf3){iL2))gyM*8bg1fr~g1fs*f(3VX zSlr#+0|W`~vbekJhQ-|-!s2kqcP>xWx&ANy_o}O>x@Wq3-m0mY?x%hKbFU%PKbVyO zTFvm{f8!$lV(rA`y2)_1@mB0$&|X}Be-lUS(e_1h;= zs?_wB=?0fL_iL)_W$$$?_LfiT+i_1Xq9J!@_|rR+unTtn2G_oJp2-V~jitrg0>xWC z(WN1-T_dfkaoh(b4!KEv69lV8bUoRyk=#rUk0S$yvLvMRktTilg*)eS9Ws)BIlJdI z38nd>K<0;d3vL>5 z@VpJ-00n-~yVSF3$N*UA=LIRk&`eHBM7rO#dnS38&H-w8)5SHPgP%=%bF46kwl36? zDk`j#uIJsjk(opz=a)jv$^9}tJ6MdR>t>?SWi^V~QF1od z?=3K4?=5eg3U`Ui6ZFU!yYydYv?3J|oye$@aB5!Oi^P4gGvG%(X|f3MNvjln>}6QH z+h0B(Px0&0OLJpoK9zTNTQF=|tOvU|5QaGrr31oSi5 zPHk%}li)8fL>9$5lAI0v2$xcp_V%d@s&suiOqPMlCF%pQVu|))&6^9 z3FupF0M+MAhKFB4HRAzLiKxb?J z-zh&U(dE)0k>#BkMQGZt%559*u+)hQh@f+=ZFbDgFKysLrlYBzHY9f{KB|i5X}F z#^PIQ^^jfA6yt=C9m=xVgX0y!gYQ?A+$Vk{j4L>(A6{fA*aA z@HAm#=u5p0UM0rVLs^(pSAJE;c^BW}C~O%va>7fWsR8cIx8+^2At0~)iaTCI^ecFA zny-D{;HmyJxZ<&@%dOc!qqWO#?{;2mg-Kfl^Z<__V;S-ZpV;UtY9#8{MJwo{LMa)9 z?_c;QkjJLlSO;aEx&Szq)LtqUm}1kQEUo=;9saF3%S(ZulNQr?5 z$-i0_lhb-w($xuB%mOL~R+(dltRe!2&&LxVYfVlWoY0E~)Db~)W~zk?y_a&*5i*C5 zm~;Kh?iEvRwL}g8In&=IhSDDposEzXKPMn~Xh@nY%4es$4HMM&533z6Sg~7{+N3#x z5yIkP6KXSh;<(#gf|-S~`B*!mp1n@HY!pe~FWr6Pm5~Rlmp05g_dIX4h;*nvMVr9@ zrkf7EU$+>_=p7OokLoa@UhxXV{CTI;z>!f6)(m{4A;qgh%ydGRhg2v=dbPwtxwC1EX!ildXgR8(KOF zm4a{m%iLUeX+|*H=rC4&>IJyD6%w*m2oV!3Xs9203P>1w9XGcbG6A}}#~m~*91jH; zNz|$SsEBUv8onx}!b8y;iTSCJFEpB`W>1oDw`l*0aiAf(pd_Lek89Me{%RjzTl;mr zwU2%ugq(e(Gf(~am80^N^5e4A&>afM4u|K>hsK`U4I4w1HvGt`W)m&}&o_-f!P+#O zX0cqdv6CiSh^RN|*zpPp9!xY<2|V}2ZZCNhZ@r+^tJOJ2;KU=^5 zz(gNVD1+?!!u|Yp0EjFNPcM2F^62t546CMRUXf>Evn?GmQb}l7NV(3OgwJA&%m12b zGo+(4Yxy|zOmv|elYUI^tyVA=og@N*sAc-Pl*jzUWVFd0PlM)Bnt3{XB!wYNvKGzj zVE$0iV>rQI6v$@#Y0U8pwQ>~S%H+)%+n8#3fr;4H>R~7uQN1t6emx0`u+((=27+lS zv87V4+qHeRW=g-dh zD=Z;*(-E1o7|8@k#9iGfL^Y`&$>Mw&k;&*`jkZXb|A>tc=9>PUWr-iL;?Z&>SIi@( z9L1zilvf`_kutX4u}^iU;Oo!!*fCHV%e}j%LPfq#*^88FN@^$(V{1+uIXg%szx2sn(2ybvz<@LM-cOq89BQfCha2 z(ZA6qSGX29N-p&zF#X=ST));F4#eCI$l0qMSj+is1dC1{%3_e1cqnepXiYQ%BZ6U;jEsN*M2EliHsDH7-KX?iitM8`qP3)I$qfe;G}ARdoV8 zjKy>FdAJi7l_>lVmXYQ;ZMwf37GRfEs!EIvubEEtPiF=-yax7YT6b>9GA$8uTVcup zypA-DElFK}|K9QicqA}7p5=I7=DM!-eBl0wqka>aQZa{WgDb|WFNwVVV#kN_gKW0l zs>WLjE7|C|r?6&U!LwN_r@kp+_!SUHlq!A_Wx62^Tff^Cw{zQ1oYGVUvr9yy>F@oM z3XL&kFN4L^$&54>Ymz&lwP7$v*qs|aonDhBlM*rW@NKMwJr-sh5Md?n0w1cGt_ZXF z$O~-Mst2UVku|y|*KKW@2dY*2Zga}E>nf@jcHG?rkDRa=ITmcfSv@DgfYtl?QtDVX zK;C>}j=C%Q!u0rH1y$&na02hY9Xz(1#%(ODZlH|Uef0NJu>f?1$#1G(kH_Y`zO@+Z z07KI)Khf&eqL{x=Hs zT7$+Q!{=H&N7lj|N*5PInFL03hC7E@smLH2Xm(^fLlG4VwA0c z)<;o{olV4y)-oAq5l0k|B5~}xQVy*Zhv6wzp?T?tVJ1>&Hv=LZ&6T)R7zOdiiIZ>n zaZKSS_Shp0M7zTcq)-P6)^@+6=vC6zvr$3DEsBq|eo~ZFvIMi?%JFG+=~NkdtSGA~ z8hv$}ik%GFsMQQ9&MoXKw9Llvf3UJ1GV29olfHg7-;!SY;}LJq)MvEpY(nWAXFP)x zg!~CPVUVnXyl|G~9JSJKiQARrLbf_pQqjEfyT5AvX_iDG_43E?LJJ(u9D+ z%ZhA+lWZme$O<=(2J*5=Lxan_NT@HwjH3Q!$i!jdIRte;jIAJffM>#v@q1Bh#sNIMz z9=dSa)ta@_@ zVUYzQ!~8p}=+_*#YHej#Q&%8GKmlr6>zJ#&1x|B60b9k_xbWxG<)X2$@;Zu;gsWjO z5~#&hA0{lRwZPeH{_^$bS{{up5mR3`WI%6%sYpr=^`FuHWLM+M@H&1oYmP^y(NhIP zEVI0_?Z_~s`0y{Xt0p20kVt(GD-}-Ez~sPwV^{%j$?8Wk_aX@%y_sBde)%946=Cm# z&yfkfbr#5e#p1GCbp-5O=Z802DTcA>7<6_NL=Myox!-5;Mz@QLG5?rlPoq! zHl^}rhU8Eup&!_Q*oRfBaZFQ!*xkys?gJaY@g{Ln?}l2&M|vmG@QhzBgHhcW5B%1U zGlw8tu2@vmy13^we*~|t^V!Nc(Qi=7Nblh6e#3A5={k53Sh`WFCf{6gbhsGxbZ+*~ znihPI%WD5okaLp}E-{RB@PJdI`4;lfR0;%zIagqIO0VN)az#7%d(-R*)6+Z~kw_)S zz|7o&c@Z|{(IpA6J>H74AaRM5f1b``&JBdB?&XG!GDHpL?okHp&`kAAPMEFA=~A_T zn~gZFLXv~-TObAX*DkXMeKSGCb@8ME$J;@=<+&}jp+h3jBJiy?TuEWR{dnKVhI)jS zY31@zE)R_f6=DYWXSUcg&&YUhQ0kpJ60`_yHp?Qe&OKRh%O!`e)MA&Q=V-6MLo52L z5IF276HR;DZ@`9JCH$#3N>J@y#IkBQI~)2{96IJyx5jYNKqxr*w7HYSkRm+6kn3$P zByd(j;V{HfI}X?Ys-0#HnR)h?Pqrds^@;)ja-6yjLx;!mJBNyC(J*Y$1O$Wk&PXr9 ziR)06=pT3-`3VGFIBnuyn%^XDD~>qMxpN(mdIg6Y{1p=>Jc+;Ajh7;)X%_ox`(6;g z9|msw1XKi5q10FQ-h_sjhEmWhO}?%uue;i>yZ=q&udf^Ma977!$mUBHapw3mzv*wN zE*d_o1^86H4_%AqYG!%xxDg}?l7h2yB#Mw)i-R%VLCB`nCqtRFM}SSSY$1dUg3K2} ziM_0Jq@P8!bX5-joI8t}>?moeQiEf7+^9aKQ6IU{Uj3qdM=4?%UKpJ=b1y^(s37bq%oA?oK{vd%@7ERA+Velc@NTl@14SoKDkecY?%pA z6bolpIgc4H$Mt+jgJ>_)eXeE>C?}dioxv-*+Wewkk=23vel@b5fuZ~Ky%*-a{bJi% zH+`v4LACEow6;nw=h$E#K20)n@EzeP->YM`5Dfo_9}htWalu-KSbox$`zrMx4GuyU zex^GB?^M@bkhJ($_pDFE$y|(ZZFl4<$jf`pRWrqCk)VXdB?i7O@!x-cu^qE9J)`g> zj1Eo%b$?Dy-3EqpE;Si~P3bxtRkv$xE^N;mF2EKBNoZ88#ZurVUF!KF7d<+8eso;T z@*Sz3-#$nSB{{K+GWDH}3Fw;VyDV>yTGim8+0CC??F1g~U6g4FShUQ+m?P2;Qm~pR z*;Lb7ODvnK-+zWR9~>Vv#73n<8e|dt%?kVq<8EYsI^?nk8-u|4S!B2HA%=ZCy|@Xb zO2ucBq}H0tPMmRDumMZRlI_hor=h4QhcwKa>)S!Oot+9}E+<=HH1hcxc~%BygVIa7 zm&<@Mmu)3sfJX%R7dbgzN1Q_jR^gAPQesWRup=#zCV|(Xrl8kw2$<&07DHc?bGayf zBt&FqdHmjY5J6Y=g8ONpE)k9HHnEZph3BAd+Tks?{gDhz9W+g}ZF>3LqU*Qm5{#PN z5QT-Zqj`r4rj)4Nn4TB&cXgqjI^Rm7<~0vuZ@UfT>5c;$8mwbI49w}YTT|n;=aYnh zkJOke7hOP^%@&->feR~P-6yd$LDUGOiPu4c)R1Gv65 z({>;pKL&$|uEhs%$CAOv^FM_TUhp68EP(Ehg;lPBP-n!t0cF7lv4exXYA^49&h)}L z(|g}ONPLTt5luC2R4--~UbKCxU2iXi-IzFm^%bKHEx_3@n5U+Qw70qY5iIp6osh$+r&5HG_{UrP9GjyVn z*PcD9NVi&*hRjZkcz7l|n$44G1BG|_Qr5Xg^M@k1kc~~M{Rd8W10%mqoZb&xNx(<2=i7J)9bC2cq&PE$g|T}!e? zo!8hYHVEBJXRnbDYbID7PCO*Z4u+*e0eoa6+Z*_e$g`Sx$oehu)@cDx&9UH4@=~1UF&H~ zB#nVSp4Q9nutLZSmRTT3Wu5+dh1PQB6h-wVyLo3Qy&?8S;*$(Db{&~4MX_GT%vDIp zQ2!rt75?N@f~{V6Jc|+3p7}WIX{YF&E$~MxuvJ69qTH9C#x3N=Bjj+x_PuVScSILf z3-Zz=##rIQ&RV8>b~k)zkA)$-TiQiWR_v=ut(UFYEx|6#;^J?mysXYx0OGLOrAOVTk%W#nIbViMAXqR zQTAgnrAv_4?^AkOl69RO+MI$$f^b0uA$y<0NpjJ%vKUal!5v7d4ETL<=9Peu zf3OHo^RtSnd=oD|?ayD?N$3zW+%SG?{Z>-#|L7KpK$^0gkLra^An+3hgOL(fIhVBU zy`3lC)B)8g;X{OZuO5G1J7x|BQ?yVXGig6(Yirk%k?6LuY&Iy#dv3zRQ^s{IdWpu~ zfXvp9$l>FVysnhdM>HI7K!lPz;)AJnQZ5-IV}GB`Y+qOXYhJc|q^ zxV1d)WboUeEFuq?oM{s8@v*a`-R0Sfn9xRs((9}8RtzHP&$1dvLnXq1q{zP(Mh`w! z4HVPC#aA1C62TCP46qjQ(!Rbzi|I4ks4m0vtseXcb)<+N6dU>?X@P)z)41=hJ;WPI z5=o_J-!q2Rp189&i)24>I<8GzJTR?@Ps|ZB^g;M5Gr6w20@Z6L>84OW(M_uvW0x@c zu>?0UcO~3kklnu%Sy=fKek)phzanuqk#&$#n@Ng!=2lDM%P%ehKEBh+c_>^ZqEC8| zy;+)dg5YR2mO_HB^-g`I2&a7LTTl$8UU-nnhhOX`RJil55*l#|pL$(vqnv2U33@rR z6>}Ylq7-D1!{r~BZUh^vzFy41OOIPh*qQH|GvhTj42Z}XdlZmcvcLdQAiX&QbtRL= zUzZ{iB;?R;s+Rw<^9Ue>2NlMIqy>q$0E0^`PE0q-)S`zGU$KaMP8$WWGX2z(v=4ct z#JrOAzl$^^~As3tu`rxFnYvcF=8 zY6PZ&gogV+=B~1qD4i7;oCnO0d&+DUvc%zfS*DSD*qe#weBV-G&HFBZn6w8Yp%5V( zO8gndI4|NGo{R|%H;Tvf;LEAP(m|06Oa}&f+wf}^H&1_VMuY|3ENjUrNA6c-irv*1 z9lUVMtt)erT`kPz9!?SL+rKu+TVC;oz*?82kjkOA?oRIqyTYh#9=|iNC)Tn60|vcV zK9d|S0;0;G4XhY~I+?ohxNv}Bzi@ANkADwofXX0h9V|H&7n5P&K!F;i>hI9G4o9Bu z6Zqs&p3%qyFwMg{SCz>Iugd{D``vw<#n6MBw|~>?v)Pkcr*vAxsSRbNkzKpgc^I*u z-~PVba_z~+HK!>~g>Iu#dKF4ru&ih1lbah&vC{dBEG{2Q@;X#2!MxDlnvd z%6L8>H`d&BwO#kRk)Teyq$o!@T+z9c-0qu>>oO44ocmEfivY^W|5^c0VRiO|BUsO`<6ey%|gC29?Tjp z2i!;|d&cuUdV$zmchqV5ObO0n+GZdsM<7rRUL2$64}Q=?qom!Dzlvan)KmGs=Bfoe zOYSpS^HvA|r_i+jRUn$>yC4v*`QWb$2mWWi4)SWG18sfH_VQ)Et%Ju;ZFSZ5a8KtY zi#Mh2T5MxNkHcJf->STF*fbfuf>WpQm>jx)d+iyxP_59yp+Ci5s4v{KIsOo~p!dvc zP}Org5;VxNaMQ~rmfZX+I8W!&dN`kJCN4D2vE!U8v2vVoplw3Lz2;OCYvUWkm$%M# zXph*x4nv$ZSd_-=)irzdnfzKlwP&_DQN76n^#crZSWm5J-=LOPnsXmCRS{whlzBW9 zGy>TSHW_&xHCezrtq=-@XvLuMF;)cX0}R?b8|DAc7CA#4J^x~eJk(nFtqx!X^R5kz z;#YKvjWX%2v@Li0oi`FI8QnWKpl1L!C2nDHQO{c0Nx2cHsw^hqSYLHO{C%EcNfF#&#C};~T71Elnu5&f-eXX1onVTxwv-4JfjsentI$JSR zHmZg6eSOz{Lqduw9~PeHA&Rh6MQxfmS7;(ofwIV@4w|HI-FW0p1Pw<+*C$7LAD`4n z{$FAAB!c_xu@x&T0H{l3s%tbl=$~U8nPC=6D&94as)}LEfkJQp4lToT)l!m6R&p3q zyss`9O(n0oOV8Iw0#R0m@8eEW)5=lkUh~{~Z(LKhLsyiR=YYh!fkTvz2~Wl;;#&XkPvfa|5A(2oLQY!~Mg$@2ma*BZWI-)b{b|W)9>&R}~wd z^av@PHBIoqJ*f~#BM&i#BJ)}}af!II;PWSJwn-A~~}^mqCA0JZqr zRe*If>a-PE9b0p*zFudJef4^Mv{4PX!pRrlD0VD%3Lq(|%JDu*uezA8`Ru1~2lSWs zzYvsp%314}`AKoUWLO&g?t=xEna21Z99ou~7W2O$#y_(Cx%3QtcmW38<8=S?7DCLoUm)89WPO%q z&Ff6p^kvOcKewBQT(_Mz;#Dw)8oI`4nYV6za(LL9sBs-!{6AY@hUkA4&A+@uJ<35m z-&&GXobYLr5~5HxS^sSSC}<1>-ZR1^e%7^dC(o5fFmaC#UkCrWz>3QAR1>e{>+)2Mjd? kM$!M2{_h0;6I}ELo%nggcInX~8S4F$l~j_b7BdX`KM%Q+6aWAK literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-courier-earnings.png b/docs/developer-guide/img/savor-courier-earnings.png new file mode 100644 index 0000000000000000000000000000000000000000..70f4f3678a5d78384fba4801113a77afc60b54e0 GIT binary patch literal 80190 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
    ByesG<^YxAYMBh!NgphcH&niWytj+#MI?>S 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

    ;cb5(Qgi#^3d&8Gi}9{m7|L_DrcPAF}wJ?q9V)5 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 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-courier-offer.png b/docs/developer-guide/img/savor-courier-offer.png new file mode 100644 index 0000000000000000000000000000000000000000..f94c7deb5c781aca19a8d03a10aa98a00ba1a65e GIT binary patch literal 88207 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 B_&xvt literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-courier-offline.png b/docs/developer-guide/img/savor-courier-offline.png new file mode 100644 index 0000000000000000000000000000000000000000..9a19b03840738648d322b306136fe435dc8c686e GIT binary patch literal 74309 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 SpcbI@z{iIeKxjG@o%>&-^E6KY literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-courier-on-the-way.png b/docs/developer-guide/img/savor-courier-on-the-way.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ee16bff1ba49b388c131e9c587db0acaff6bb7 GIT binary patch literal 72649 zcmeFZV|!&mvp*b7Jh6=(JDJ$tF($Tc+vbjwiEZ1QU}8I&*mfp(a^L5iZ}7bQzgSme zRrjh!b=Ru;bw?`7OMZvPg9ifx`z{3(Qw9SAF9HLDjD>~%${BOO*!>E?os}g;z-p!l zPQDUirW#Uaa&lm_UvXG4@Ni2osQ-|BJ$PTwSJ6ChFvzbE{6Bel5dZfUyeJRy|KgCb z{~-)Bo55Vi;?n7-as|8}ZOo36drJIDkeI_i>158Co$!9z z^uF|d+Nj+MIQ^4|HCq5n4G|SUmIr&!1`+t~kUW{4Xsv z=?VY;)ei@i45DX2T!Z~zS_(in%-5&>Q;B7O0kXmK@v7O@EQ!rsrPGK1DxO|06?r*eI|L3z5j+;E%l! z5D>q8tp3e9g6y|bKt9pt@mAJ$b_s`vlwDdy-$E8PHs~4N2>N=xRho0E{yXiDU;{jW z!eJ5586g-L5l>)n@Q4sUJ@ryJC22`LgVmS_W#%j)P}$jc#L*!BT|>hnA|~X`kaWRh zq(m7v&&f>ASYiH0wwQn6G@=X_w3UyU9TpFpWkRh8$qEb45p2Y~&;Wyk6nhw@fjtrn z8yVL`PCVMA|%YriXwvp z6XRyq$-hpeLCSLH18Uq=JX6pq z289v0B}_ug1Km<&Ku-E1Ev+nanq7a|q=uxIR4p~2_0W(_R3%9JpB2igpBig?uc19 z_@YwD2*-?)^&h{1AB0d>D5COM^_#8p7F2qcqU@Dq>j$0t7jF2#T~A z>>T`WF=BY-0^hJA3`|s-*?J{3OkDDWgdI@UYg({WnF_DfOr@Y;(3;!Ve0Cr~S-=Y) zddoyXCD3<2>()0v8Xkr&uc%0ExOFP0uT7?n@NH&qR{p6CN!B1E`JJS27#a;##e)P; zf@5nbS8EJFL_wQBhx)enO)7H81eOLP5e)zkl`bYJPN$BHjtxfk&3c)Vm+hJL_KN0{ ziVUpN`tQDT?GDBD`AXZa+lzwc+@h+!4Aj)j@KjY%+p+Hwl5cixt~ObisYpeD9)lA7 z6UnBG91IL*$uO4Zq!o}>Z*C@GLDAIJ%~s)56%EC#FOt-ro@zw|Lc0yP{cec*2o5ZjsIOX!$APz-hzr(|+J2e1#cw_i(Se{s!% zlt)7CCB?XMRn^t;s}Zq({{R=0kic^m8ZqvE&pd=*vK8൒%FDNXCIV`R0Xk)JZ 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
    HN$0zjKJzG3 zUIzZe>vJmMlESK0tLZ87ycQ({yp8+Ocupo7&xBGTn!i{zB<@|PhRf>)4L%10DqhCm zYGz&@2g#8^G7s}?*r=&=@7_bNB^WpMC!EN<3#U0}9yZD2{i{@|0cLms?)2Fr6Mp@L z>XLSfMva^4ok;93#T{4%iye0;sE_2&GtVkxAU0~;L{Ew48G`v8dQ4lkTv=(1 zOAdM6N1=|3@)0}=LI&#_6;)SnOD>07VD-p!WWXNF6Uhe*9Hir9+SDmH`Fo*GzL-BT zu357daBCtfaJin7j_*!I$n5`U`TWgAP6e5TDl@Z=S;sg}t7zS-wRG-wmP9}(1qB9! zjrmC`PNL_j>`QQRJx^`li(3fJSa1r3Z2l5wN{0N!FHkHFINp8J5)Ih{ck%vRDx=QY{EyAnW94kvR3z*?{BhK%vC81;*1D9P zEr$;ul)d}*;1I}jdY;<-4?Qdo;+(ZOoL)e4v}yH&fL}GFaA-o=IED1gnbRs+r}gbG zyCDJEjYNtkuJ-KNONww#4>Q1%lT&b*-Uj;?)kt_81EoYBkjCT1;X%TGo<%g>3igHo z5_!JM*}e8}$*PX;YIufc@(k!dR1Y*ecQ#8l;B1~sho)EOJE~siaXhZu*Hd4$U9UD@ z`#m+xftPFR%bV-=D{A{~f!}_Ury14ubk#L)d#LTc>SICQO$?i=*-@lotCVdxTZ`tn z(aMa3xZYdS=i26O_0CriUmWkNW&Sn31JCnR*eO$|>oM}1adNYs%j77RDbi$G46jg`I z#6IU7`MocVcgMuU=yC0HX3v+z#Lno$-jg@?MEG(K^87Huoj6Y)FrV@D-LAc}U+!Of zKE&JF_B!9iAD?i1#Avw(Rn3PE8%BGo1z)Ta{nI=&G6RA`2@Wc(gsXw?E&zSovT38b zA}CBG_3Vjt@eRJNb8PLOZ>H>x4H00(@#9Bvy6bG|mefNd<1Xb~!a|3sX|7j~o)V8k z?@r^yRr;H{bnUDRh^K#s;2!#zl2dvkLMuUb;&e<#(-8?>3?J6=V{^2FgRuuTL}wgH z&M&j!Kt6T)q@FzLim%uyDi4htG-wbudYC$)$#5-a^v%HI^X+hEP7nA3=tn!5nW^R%n>7*1y1@*P z2&T-Vr!zA%V4^7}FV{wb)v0qL{AJy7O72CS6MYiwHwq?{&L9q5yLHpU)A&FxqOo`? zYWAgzdf;L8dE;F@x=TmEl1IFs!8~34IXV`sK_Y|%`ufhCKI0sRHJY0LRm2CqsV5)u-mdygKHixXW> zpUPCi%y~qD7$#tCxu-gG?0~hx39NYqYUAf=!v#17o|ChCF zA@1{H1pCGI;e#I4TQ(_OoEh54(EM#o;o5%U+$pNF~YlAaRn)f^pz zvicQigE@3hN|J5}DB#}D!^zLY)e9Fc0RK4ms@1?S6sOxW2m$+3EW86ePZ?W=UgYLUZQLm*F^KnLeH{NMttviW~;Xh7u4Z=n_QveTgH@Kl^NnG7xHQoq+gHKJ^VS z&Uk!R1mx^3Pk!ww9pClbYx0NRzkvRM;3|Jsjf|h@JLufGt33VmcL9TI^4s71O0I*c z;$iYk#*NBtv**Z!36tbHj{Sb&x4!~2$ySEV>kR0aKK(YCdFyTR<{PiUl#qpx!w887 z5=@vl34XV!K%(}F^z1GKQr)$y*JR7)jXIBfoQ7*7KmB*pr_GdUGiJ)>O&jF3SN{Uj zLKQ6ObIwssQv zayS1-&AnDgIL3AV0}o3N?1j&kE|E_@UWB<>8V;Z1E4hx-rq7VuXU~;2t5(X%lQ;$( z|3!gN4j(Z}=ShY{(=Z>Pw#FmPckS3A>({S=#+{?4l>hnF zf2%OWdD+(oAb{raC!SPB{(G#`4eSN`y58jLKL8vV<-HF)B&mSehws0GqaBu4TN6)R zl1a3IaCFz*_sGbRqhvddDyLZ-y9GZu@*Vv$%zg?2g(lxJ4SNL zE?c$)!s-rPXPYN+Z%lw+dhEFIh|cY(nkUzv+7)qp>y|CpSD&Z_hd7=9ruF34zlkx& zLy#+yBpkQ#gagNO!TA34``^k{Fn(7YCnF>L#*Uq!0vPwx;X{XjZ*}90&ddCB69oLu zkf_aE%DF5TA^jylYVO;&pKeI3CDi#yNloqp$u&<7fM|^!I~L}<&XSUxEPHligF)gT zb4lyhUvrJ9GSl4T+PEp=u!$Z3QV$$71d_}Nbvlwk4H+^_?zrCoyw3r`cnq8h-)Il|C~1%6dc6+y!_<;8`MYTjcf(8Z;OZ8jfR!l-8?vib|5o zuyLK*1`wO_voG^k3ROe&Tk)Jb722o)%d#O`vC(7 z>cMU{ajL6d&hh&7>sW&f>5pe=n_shfr3x5i!(+S;9NMpDluDTBMvWe) zW8o3+yLRuC^b^NbgY5#TebmS?*b{eyaJHBC-+M<1G#NHwK{GF%_FN11#_QAw;^W)m zC$3?P$gOiS55AZ0_p~Oix04w$FKIS}hACh+$BrG970Z_C9wi)zSHzL2VAS{B`=IW< zwQE+XMof)`fkO{~(O-wqvUTg1PMmOkhwEqdMP|1j_`~3%=%^TI`5EdPCUMU?YiZfU z!$|C%xpVK3S+iy%*DPDUfM7?hkK^i{k}6{W`$r#t0`gR(<)6V=Z3g1{PS?-uHwt_8 zj`?@VbTGnWM~}b{y;JcnIVD9VVm^;P^0?$6w=E+hT?IMo`4CAxW#qy$||z)CO~brXu`eX+^2PM?-{VDl%z19wBiKQ&+$H}v(ZS0v-a2}!~{sO>R4l&wt7k;yPA zbW&3!CAt3n`^)xiUpTLmLD3|2VBbCvmvfMQ1x(%2M2Hbf7B7}{>(*L15O|7#A!oDB zfFM7i#NouzV{-7&K_}rMf-};qCObE;+dn#pI{qP2lAzyq+iYRb%09sPjlcg5lItZr zNhvm5di6??2OoY!O^bDOS9KX<+_7z|O8GParls`<+;&=w9CvZeh72Am-C<%ogor23 z6Dkz6*+kVG+9Dcr>C#VS@uCGvya+3rUk)EWB;R=Y+ekB*2I6r-R&cblY>0R3)~e5h*h8UNHE8Ng9ppjFSqEuyzlLI-jpYw{D$;}0GySTCGWoT7MPV|e1wrJ zoUj%&hd%JoLn<^=@LRoN1@6HJun$eE9|QsdIj-aJ6Gx>Os&aRjV3>W*4cr4hrFJmU zWuzZhQy3){e&4-&cQudE#MHN6KbRca$T2)$04c_k35lEY<3%5VFs}yjfr$#6k?Qj~ zAS^W$RtT`RX%&!2n6iN&m6gz!K*VXTE5s4hXU?2b1f}1{bpukXa&3ylxS!HxWl{<2%nJR$twqe@Flm;Kv#< zYP6J;70ZT=>(tL>jR#I=Am_1TM-=hvB0jDIA!DU)jz7_myho221L9?AGK5VeoaLMw zO_XF@^p9{X!-tK;{z+1adCkhtbzkXNK)4w7xgSZndo|Z5T%~I!Y*SMW_QpAfhK9?y zF%#tOJMV>A_NpxW@ICnFjyahT=Rl^VCM1}+(vKgPLkAB)ONdq&hJ}SHbIOCitrt=- zI(6#mLpXW$eXjW6AkY|^3LebSiH?euBfzcg+rNaiVg(El{>0^VYgY;Vfr+q!a6QS` zYulGrzY#KV68nwPgz78=H8GK%T4E`DX1jOpR38roxmZ{eT-TB&e#LR%u8rfQ4_OZ1 zXa@vKxt@GmBaS=fY3~sV5cJ92bKe7!oSZ7lAox)DDTjI86MeFuGr#MGKwuaWh|6hg zf?W7WNF;kh>MujC5p#}c*QJ`vg**+3i6$^=3Ou2b`+(*i5}Kojk6<=86Ans-G+S)l zwnZZF9@h;)$1VAYCql@L7!eT$#s`TBQZ*SR{acg(iTt+2SvLslz9R%oG5z=n^&!ze z)&-^$A}E6+`VSnW%yR>5>vY_A12gW71XN0f9Lpg1HS%D-qHUT8PN|Su9}zhNa4QD9 zT<6Ts#n4F8aZSK%Q+lVO0tU=)RIU8bm^0QBfh6t0zzg6cJ`J;?`{}8KKeaNS8sGrJ1)E%*ExEWvcy9`&R^ZbWR~L7p0Gg<1~>` z@QR3lW?BpE*>78vs^anK6jUh$4u$oCbK%eMkt3YKEonwu_;_8{k~kht9EVBNiQ_$c zOAK(HIPQrqcmoS+TchD#7(afZhIx}AajdR*=uJQ7o|_@yf0G^FNkV!R#DOJKnC;)U zZyM~&naxSV_a?rrR z@QL-46Yz(12SKINe#@4P2>dv$roi!#`cqQ-fT0`*3wf; z0^$*oufi1QX^tE@qvS{&;@ZT)wvPxxIFbF)`2LO{Y((y22mpEc`B*0`id%D+KHV;R z{FB=4zTJBzXKoG%PofM4VWrkXLNgkg0{v@SHg8sc1qogVgp+oNree$E{9%rA&j+Ee z?>Zt8897DY1f#&tfq4aUBLPh4kRbQL_d_4++BK_X^X5%e<|KRY5IQ0e5lXnQ1XmjT zwLO7L0GQsPs{(}Ke~jp)&x%@lLmPPtBj6a~O}XRB)yt~MyZ1}HU0L(7ahyIQ8^>efVu9nL5r8!H0}MVl zjwvcK3PDkm)%vn++m|S1R;;E&pF1-T+ysG}Df~^hG{P4^0@j~94)emW5hElyIaN1B zj~+>2Iv3>XkjXQ7>pKRJZA0VU!j(IT@R$rfPQ$*WB+{bsG{BBip7T z(T`C?ndzBw5~iVCL;{W+6{exl?$0&oZ;Y)7nhZ@>gJFUjFkql8U%pHRBCM1|;lTd= zx~Y%i`h>}oHHc#s2rqpk)QA}HQBqo@QBttjJEM3szsCbsk;qV!P|^cJZAaPFIR_w{ zh0b-}S?vZQD4(lT(;(O()ez51RqeUD{veaf&b}ar4<3~1Gj5eJqsC}LGK;)2x~vdU zl#B+X>4#D`wFrBz3L6i^mP3Qb20xa-L8+VhBz4c5_tbq?U304Mb^1!kM5!sz&)E~W z!+-)cIXhrY_}gF)fqCxS`Li0(;jRg&b^v%W1e-!m!yDe~+-HCiwQ-N2qOT0F$2e7h z@Wwvlz-z7(Eh!g}`VfY7c5S*YcxNHa^Rt=>;_36w!a}xvt!;tt|Y{CrKv?xs@4fvGIdEXV zj;o}s6egSmlyMoYuW`qz>S*BWNJ3$DJPe;kv6?Kq!2CpW3iGNM@DKr_&L~1wE>B5G z0nzEC<~oL)9!5|C2?wQnA_2{sY?OpOH4>?KL~Rj?S(%$fZJm_Le!zj~_MepeNYJC` zhw`y8jFuW3bAK3}mya=rNEM-L2qCBcZ3mdngb5QR6`FNYk6v1Wip4Dnmvq36NtcwG zsS#;@5yl@Ai<)`xxo{1uts4ke9Ii3gg7-2U9TUg+t4j8ek-IkbK~f(LUrhBYu6OzG zGlK@sW}T6x`qj8KJu8n{ni_r9svi5j!X9k1V8Q}CP?#{_ul!3gkfE7 z9=F95+?iFqW&!w7IG~R>9-rIjzI4YPt^=+?Bc;&5`fpsXo5xi(0`A8Gm5=uff>UHGk(Q@bA_ac?zs=N;v?1#`tQ#M20Nq0t%9^;v5y5WWUd$1nW77so_+%TTXt+B)X9@4HAI^E zBUH~>s{h2XW7G>8JPx7=k$VI-YjjK$nBgAz0+DrOcKn_j+LpdNZ86(EZ6oo`HC3kH z002M$Nkl1i^`VZKz_6>#-1wn#Is{Gs6MrLyi>(3-! z7G|A@+^ex;$Dx}_WmI%h+qU~M_(oXCgC$_-A7UtVrORCO{TOiXoUjg7=V!dS3JG?t9=t=?J)D!dzP2 zAH@(p80^Az?$>{S1`u#|G=Fm6a-Z|Fr5SmTQ`{khANQT^f0wl+fu`WZ!lHy*FTgj1 z!hp*?)Qn=`yP*%2jR~#AFvD?A*VMRYdF)2k*(}w3h7K97c|sh6;%l|}z~GMW2?t&q z4WW{?_x2-n`-?A$-4D>RUjO^6^4C{i(UcJe zipk;6mNgnPJm zeWp*-3{3?ru|tHV1QrLIJDuS~_{4-x`c5JeO(eY6iB#C>lssvU=m_G(fCMKY@M16| zn#^s;%t#0Gi9mwt3`FY0%0A?Gl*4Su>-qVG>N|=Avz$I-rrOS#?^Ik2jmUvKOibLh zgQ4w(>WfPQ(IIR(PX2H%{SaE7mex=6c9?^~XZSpc3Ny)xbO~?*Gm?mzL|o>nkhs%KG9H>t2lPt~gn=PU;2eh}$#Gz?YPYYcqZ+;^Tnh4Y zWj8{X$!I1_nxvug*(h+iXU}enI69uCxn@725wBdn0*2I0lVhnJ04xd{_C^0B&3$A( z`(X;Dq|WaNUgmT$5t(^N44|MXhI2@SwmScgyP=s3cK8=-7_6Varh}eUr9WEup;qD; zH*SL5b=SRGg}@F@AbDrv=@sPNFz=0t!%U1Nqad*+4v+!I!50|;Jaq2wTENi9y?~d`l_g9ab4L5`=(D=g*M_fu0_J1#CLN~#KG*XIF7x_$}F9M=fq?B zt+~d|c@W332OyzAXzTz12>E8f6~fJK=w08b@74>%yEr8Kid#a;6{w$09(4b;f9?pECIt&FW{OE2~-a-u3I&=sMVa+iOEYOrY0 z0$n>5Jn#Rb&(IhC2Ic~2}^vk|#C+)Liffy|jnoea2O zPR}Wrd+hzpce4_H0*EAYxQ;^`w6Bp+PDVup!toHylZOuOSIzcx7PJfCNeTjf=#Qjk zf9h0*Rv_SKYJJpJsR?TRNbG0!#b`^Kq^SvyLfCa7nDJG>i+EOB;~a^*{g4ZmgeWy8 z^5$H=q(LDO$QR^3zeW22&uZQHh;j%{>on;qNf*tTsu9orq7r`~<``4Q)uU+crFx>l{Z<}=2) z$L954D#2yg=#Y80Ast}nRmhRw3&D&t85@Z+6W4vyQWdmd&3z)ETU(QNOO62`<+lszKRb3%H=5> zokph>jVf`^hW%V$%a>1X<3_R?2$5&o__8_)A`xmSi;B$-+G z*+}g@OXUbQ#NQxrCqha-Owv?!9LfFejoQZ@c1$6J3-bQRGa0U-wvpA&XT}pHF@QQ9 z5yH{;WeqZ~qopwC|eM=PGtUgDiaHLDgvU3Gsmu=ZpKcCiD7(|jVDZGG( z#x&njjpdsonEZ~-V3q)NU+21-s4006Q`!986e&fH3T?sC%xUSP&1z798Tj#MO1!Z! zH%2Y)i6TAs=L<7-WCbGUF1(>}3D2zF8OZMJbt+jAf^p$eED=&{WzD{oMC zoua@OOu}z2cX(%u1K#xPZmUI!cC&Lf`Xe^u7b$X|D&d#EiR2T>T8JSizXjw$Ia$FH zimR|F3=mxmQ%xDf*94D>5%AqW;RRTOeupG13q7FP?P_vX8-!;%)_bk2nKJoGl-B5X zFXV@+6ImXsGGzkvpsl5=+=LhDdGo1%n+34cVg76!=&7ts7^@X+CuV760BEKQ$vD3O zv3rO2N3!+dB=NYe;sQbpV5@ov75uuzn8_WvTtxx~*Y9%>pUBr9D7jk{o zPmSb3xF~JR5sACC(dozoRUvfR~uhn9{;f^0$dwYUhsrout$1z3lrdm=|WW%N|pS3v{&tfV}!i}7V>AC&8s5M^C;Mao()MvAl|Q9MJ5k`ZFIVFzT2 z$VeS+$*h!>CZ(mYyGKyTYo2d-$gO0ix)fB2cLoW=avSuqMWv<5Jdj84{w6B2CFMoP zlIZE`LJ-bKkB3o6T1ADtkooA7Nx3vNq#tRmwf^mW)7u<5<EPACz4{`u{AC6*_MF2ZqW?B7uq4l0TS75hCg{v^h@XBZzalo6J zv#Wa-kESTKVjaKAt&(a@V_Wbqu8JQW8s! zighv^aledUsjSm$-WU8N<$M1!I^F@x@tjb^gCiRe{t%J&Mz<4=VsQjx*WZCZV_cVN z#oJVI#adu*)&krJbL}bZKp$hC>*`;(Wow-$GmQ)XTsRbt65eQ|RX%3t5U@ZI zB)s?5J)ZUBC#LwFZC$z7;hOM}E?sa$q^=yH^wF=7nAiIMKYe*dd^f(y0)IBP5S^tW|pKW*;;N?0sd;87?Xa; zOq+HAx{~uAhA3nY#y!17_BfLo8%FM$`<$uv0;|{qBIkwA>pkD*>A=-C&a<@NBe^C3 zX>15cgpPHSCxHl%O>FGRM^f;@;er-lt!gx6|F!p=lR{?BNSlfM?kcw#cNHBc{7HGP zniBEf$2Wdgsx9jd*&-y^c?`MaA`VGZA1_r`;H|Is-iznZfqkq+Z!wGZijGNxBzm@_ z7n3*qJzcNf-ehaZAG0|DYiZzb8_Yj?77ui-aiV*V4WD&zKdf<*!mqXVe93&=XFqmF zwg2sFe18%$)$%k!B0p=qSLD}O6>+QKs;Yhje#(t6mVPQPa&_zNybbgK1%aLSHT&Ov zj^0=FAFcKU*6}vh^11g|7FhN$waRHV$P~?Xx?CMo(=`}7$8*nOuP(kew-jA z3i1^?dWWcgYzGedf0v+e9}}4%=!%OCk>UTWU0eu+|Ia`2 zXS~ukeD^^z*VZJ*JaQ*L#;v9}Pw2;*9j&8K&P|=m$NpLQ$K`jJ>ztWm6`bNM`{cfV z-Rce03Bj%_!A|x2pRg?4Tbun7hR&}M0;YW%*Han=?a=}U`6@G55WjOTO|FXY1?T{C zC&J{kv;&7Jncl=83KJYxmyax8u&*)L0O;uRpf}hrVW5=-`oT12_!PF zUp~%|0&2Z&?6Is22Ep&V14NP@M`jZtpee#vNmrMSoSeKu$OA%()Cb!mOe_f67!w7h zB)odrW80Thu7=!2m;$8)4L0fK28LYB%*fxQvR4^vNV$*|%TwUpo`iQ`s05ySsYl?g8PFW?`WYG%JZE z*hoCpytptJEAI9vzSiWw{5aF4OYu0?SLLIX)1)!5 zfl#}c!5Sn#2d$?-GkRb^_bJ(nBfPYksi*+GI;!wvzo@C%mYS9_e+;j-OOOab>?!r| zOOQy!EI0iC1>M*1g)#6D^XO*3`~lhqB1aOWoft>)lchlF?Ot089w2J%8-REyP#{By zB)yt8@-C{AK;4JA#*j!%P#Eim(@n4s`w`-mPS$YD$5-{j!h}SXa2{J*<8o zS9*!>ifLIClkKhbxzLX*$?RstK@AmO1Oo>(SP_T(tuDj)Nkd^%q0ib7eD3i`I7$K8 z!-1LHJTmUDN2vtfaMqPMIc1$5{JKsGL6z(P?a|mlC2&xWd~bFj+K1_0oj7GK#)*rj z?Pn%MXSH9ww4I)G*TP6rqt}*rutX>oQ?*mfUZM0_2=+K|S!XH3Al#|w^0BRvz+5!8 zp|{|3)#!joJ6VCa{Y%aM%1U4n`8=WGC)iDkm9!Fa3oP`BLD*qB77BU#kD!+cFGKBO zA`Wd}sXVzrPt4HhFexn$lqdfl)vGeYF!`{Gk`fW*>uX|o-hKfIH9dVZmWH$B@v*=c zg;=JcpR2Qu`VS;^sB?`!LHKh;|5n}8XK=y6K#ingnCC`L2Nxn}lJ}ZyR+YV;q!Pct zp=7kRGl3pPa(rGFq3JS9rV1r=hU#RNrGkm>T-p#dc#HNJln%i_Ty%pW-@OXs%qjF*e)IDq{b& zYq#Y+*4H0w26ffz^_YprQ)jSJrAON7ew{3v#&G5c;w2^upfk6@4j18JE{( z8Qtfv$ZSFXp8n<|91fCu7d?O_214Xig!v%BT+s1srznt9&IlyqDVvKd>O~GBSg=-S z2CVex@wS_-yI`}sRsGs-6aK-V3lts*7g$An5HG6E@Q=N(iTfV+k>zXWnVGHKY>@*C zeMgaKJcfpcWUwY`=X6zL4?v=R|0W|$yaV?69=$c&IPr68*r8*{P}7(f;)clKc8`0X za$e3vHK=6pxK;oo?S!(wepL;oU2IdX4Azr9O_ZVje2jYs=ln+O%7e=;emrRHePs_p zz23CpbT{pRg@DlJ6D8>IfviR5J_V&lQj~~A8NHCjf{*e zz+Na)Ajl+$4Fj(zP~yFM`s++yZQ$?u&%Sm-f{7B9ce4C)Px$_4p-4cbXiO@k@J8?) zusu=-3JHe_{0%u-zdNZ|H2et_+#Eg9wV*|;LZZL0sY%YxFbD4L{&4fv*=c{sk=y6y z51yc(ZUduUasjS5k3wT?OouL~>&PR#aKS0i4*|^@rLpPQ9eHyS=X!-khdJlu(E!L$ zp>ir67;xN5aJPUaZx@)I?Fd^BC$E#s5fIh)Ke%?HekZBP_@H(7fSlZ+&n2E`V{#c| zdc9TjD}bm(_alA>Z2w0!l>Gf;EEFEIh_a~^n$SlI=#SWIyUL(;+*_kmRFH$Z^}cF9 zA!lGv5cqsRu*7&NCm}6BS7bD{e@W!K#LIdG0AQLt!iJ`~{kMIv*(_nmc5&g*RpFm2j;j@+H*I?|H$K#{&jEN!(k^qf{X|x)QvRDA%gIq`` z10`J!nJKCBx<})9CWkUIho_wA&uHhp$B{Np-l`I`)Mi&6Jq3^UNHh-pAW@TCCZE=} z|F_=XZW~GxI$8FQ-qfq9VknWmpN9vL!)%rM6Ems7;YedowmL_r358D7l(;sa#EC(W z4zzfgKcOLn9~2Z4_DChh%MwV7ENV^7)H*uauJItIf}x*f-)_-Z4pugZ+7SAs^mbPI z0<%Uxp#p!*#+O~$@ASxm@l8Ggms&+ZHEc1c3O-_N5R3HU2k_cyd=e$j<(n)A~(`Bt67WxO5FyZ zmfVFNYECLu7*D0oBI9n4o66x-G!MFUxmeeW9r$=_?fn#gZ}a*Ggy$|wh9MJPZ3#{3 z$s_&uWjyxx-yUsT2Nm*|!SgrO3uu==TgLclKuF3akXe=9pZ<{}?aXf;$iqXE!G%k)zwMRDCHM!yjfx=WHKdi zze^#4#7h}+j_M@|mls`dlg`*-Ke_@?%TxfaCn#pZr)QP`t|5SE+yG@=iR~`nw6DBV-ILVK_uekS6Qd9dLUbfRo z2bPB+(10EDX-Im=|DHY|KqBNO18yUMR{diCmLN(;UUJ`PAR}+lD6CajbC$n;6%QA) zUJ4s5UHa;N54Zsad-IkqYk{^$dhk{e-dGv zlE6{F?C!goHEJH~kxhtWv-|LP-HY=hvG4lIEXw1`@0Vn%()r@03!`;b`Q_3tpLv9e z#@pZF7|!=HM|zjm+eMeh@TwWCati&l^07@4xPd^q%!7scBdjDaIvVYcI=e*C|H2!m zzR**At%*!1I=#>{t|zjf05^V!l$rw@)c(l(++}V0MFFU$kNWkV?V(VL z6UsnXEi|F^$uVIbk&y6B>bUA|?*#=RxL`(X|A)|VXnA}@^Rd4!D_k(2E(;j9xhLsq<+{L&bPgUxQuc(sihN>RwF9%*b72F`q#RX zh&!eMC+@O6qqlK%BKU5wz|0K#cRy8(0IEo{(~h334!>*-u*Y9Cor4$zl1~^{aS02X zV|qBI%;NQcb+%re1@$MJ&l45rB5llon=15%a7gI4@p2f@BQABFtd^32{Tf z-|05COq;fREm^?f_aU~#fk$Sm^n>R5&GoPrFWEMt#)LdvHyM+Lww3J^3UQmy2MvPF z6FW1Zf%n(ma0U8-6x~pHftjU0CwMOr!PtlFkG&_zkvMsQPt!NEKlfO(j0p)Lu zf4FEc4)d?JXoyCdL3TgM^2Wqod3fvpeu|-#KasZ66)8@%Q9KG*g3`@oh&uxA!asbQ z0LL99^eq#JT}Gcg?VnHFEEY3-)YOci;P8R7RJc8b&voOn%`WmvwRZ5>+2$O$r0MEfPvr)*@Kcr~iRQetCjAg%=xw?ybPxhc<_Inz^MK* zv{v8|Mo%Wx#uwc`o(^^X_;lY9hk90Gf3KtCx0(Mb1h+W&s+$_+dZomw&BRMTkioc0uDIpU_N($KkZ&^Ahd~HB71!~ zh-<1+OOGs2d*NIc)FgbnN@X;l2!=vV9Ey#kQ_+Z_W}oPsBQJbOf2#j=R_b&Fm$=&8 zVpRBG$8!q~0sKNF2v*h`NR0^WMC#uR;bg;f*UIrtoVM$Rx%D;if12o$kSXA;JC8N#m_@KTAEh ze1spcb!XbiHkAC#55f>9T!8FGqSl!%48N$JQ~=mWN**-YR=((5x`yj?2@q3-fmod<4DMF54wJ<+GUe0yw>|B#~nDbX_txLZ)Uqy2nNf02vdlZH(* zLL`V;*>mHLzdFJ9d6Ej0F*=fKPu)$};qGoX7oKE7fV1nVr5VhN9oL}$)8Yv`CWYp| zIr{iHw@Z+)gc+!V1U$piO>F1TLs6lG zJO0;t{LCw4{p)k^oA$+u9{9-Wa{m@F`w{$NQwK(fNWH$h-2t}lD-i52R^{zARk4I% zv~XXL_kSu=g^^6@(5Z-g0`ir-3qVA^Wg6Rhmo5rA`B5e}xx*H-jf^4uL3Ie%ZU+9BNcM2%%5?{d-v zy@p8{YNH8*@I^+}vK6vFj74^s3pFAurd6A+)mixkgUBBbO2#JVeY|*1KNmou!%au5 z172Z6MKe1z&$iwWRcX>SwLCMl8DBPdGJ7=d5~g&6q_k2W_P?n5g|my-Xw5u0z$33D zxbHStIIKsjbBP`AS@9l%p9SN;K8eb?XXK1EnaGcR(rS$L_Va$2 zkC>eY$dsGweOwPH|0Gz91W=4h#WuXXFH;vY02OdPl=+~rm}k|G3@U0?FCmw@)nvtw zO52P6K2^kIDS4s<=>tU6#?Pc1T5z}|*;-iA9hD{5+g@O@z#e6L{9=cM@@J!Ze!kfuDehcAW@tW?zMXU(Bhbl%nt}71szGI}=9W_0WX> zA|ez|-bNcnOe|#YC4M=Rmd$D({Snt82B$k;mLT|M1h>|T={L7a{XFsIWgYr)Yalq( zWVWYsH3$g{=6l6`@FXUuuZ2P7t2+bGMV*-(*kz0{m9!7EH@?u~?2jhQPiZs_&(Ir7r zb@BLpKxj3cf>-N|ib*d9frFXKIKY%a1rOhC{9y|pe?j)zJ5QHA_Fg z98-U{KKLk!d%0|Hq$taG>^pnb*LF3oqC8zNVDo)LP2cmV-<7uNksz>`6Nlh|axgZ`?+g=e=4>2v1#gUo$H`I6EzDh}c}qD1ll@V%ze z%u0>Kz0>ZC4ZsAjieHXR0m*6=B&HnuxAB+ z_`g*K3KmAZ41#sv92oFFOUUp)Nyl){cH8`ar5Gh}pcGR9%5-e=ziLfjAFzIj9pcCK zu`LuB6c}g-$Y6DI0rbG`KX82TmpAiI8jZEv^mw5r^ZxjBa4Y#G^@8ev}d0b{M4k%cM(bil2YJV0VR;%Q6-ZVN`l#P_;$^b5zTuUZ$wliA)bF3|+w zDpBWWbjG+5KOPVwZ%hIwrB;%h`&2-~1T}MW#WA&lawRbuy_h(W?cqfQP%7yF?3%H; z&uz_3?4`jMzD~`AITnLN`1rDhw ztGO2HDA!T9I>;SWm862YCjxkajzQ6Q0wPHGd}OrhboX!Z0t@jlQ{-Th#>oY1cGMLS zNnxC=Bc3-yx&%&Gp{&K}2-DyQgUScB=odqzdYTwH7sCgKT5G8>b-0{%UC{*mGC%`5 z$z&?18>pr5Y&;FkM3H3LHjCArshI#DPCzwk04=oy#!lCT)9t=EJ@UYtNy-|#ZU9ma zPr?3x97wy|N|io&dz<-rINMC@ekx*kJd^WCH7YJ4CGkg)!>GMF9I$JbhNd_#1^#44 zx<3XtjG7&3Ew~1grLeBf;cTNcE?T_3+Ry6qAw-=eKd1|A*EAImNH});w_fgnZRs@w z2S}n&NR=e?*8R_-L8teW5#pU;B89`^2As=ob#lVo5&vy>wrH%hDX)TbX#gTEfVDw1 z;7|z3OG|2>S#pu1KImYA&I|^L_f%9q=MPp(H=Cscyk`{H3rPn4GO2zoVyO(DbVTor zr`V{)l>)x+6Ze9z-#3EU|f$+D|8{D;tj zZ;|Nxek4Q7HmhCR(V&4o&VH~k%3Lt>t9frF52p!akX-vY;f103Li86&DJIi1+1&J# z4mW!Z#d+thh1BdPkiDLlWNxNXg%UA|*M85~!}B(M^%tvw7F=TuwplF;CGuUDAjCV( z-pF&%snDTnScGp z;43lv>1uz-B`E1|Z^6kAKfRyFR%~^O^)}o95FO+3Mxv%SbHod+_L-XjLUpF&qp0Vj zi$JRD3dC$j%_U~IC%+7B1w$nhmn?@t}ArOCX$?-;*z5{y^=H9D5ecRHP^aF9Ky-qn7; z!LySd9Uc8lvO85=fOidq`+Y$3SJn8Y#F4d=Md-p+40LjP#w7F1K-ruAsr`bt>r62H zYOj9LTdDr_aaBJwhDQcJAI*^BzeBNHhsy;`>ug443F=HPm2Lk(P~pFO=N!f6ivTpC zvtP?K^AACcFZi$o-=uPSS%?O_B4v2T@STD(&%bg<`mSH;u8*|qQ|{v>*c5GsbIZyb z>jiCeZ}RpU-`igFza0EEeRcdepZ3b_|FOY*Q`0hry0J+>31dyo^icV~EM6b3%@@wa zSF?scUGLCVu2!iKL(V}MR)tnU(fXW3Q9iNR@;Q%^>bK^O_MR%NV)z1{&Pm-i7~TA9 zdTn2HY=T`~s~X?A284CCJx}PhUiP59GHoFsYL6GVB;U3Oy-rawcr64raoX>Yr_B1H zuch_%{V;lVdp=)?>DF6p7$b8~Q>g#iETiAs3Mjb=g_n@zLIES_LnrOxxGS|k<;zXp zV-w@kVH&p}RiF?<>NqO4D;$oET`hO?ft6z^>7HObocyN3RrwJjIcCkhH{hTT%J9dMPW%@i%uVM z=n_btq_eyVh)Ys3;(kX&2hya|kVl0fck;PhMLa?M;Y0_2U0sB$YqEcJ+aXsel^Tbr zu~Q{YS#$NI@wFlhq8`+v-5d|DX1DHqa@ep;j!pJE3Y@dcm8(l^x)H&6df4Hy*azp9 zaTiXpnLMb>46!Nq0%48`$#?o!8^FTV0s~-SfVyJR32?Y!bY<-7=}i_Qbi*Mh-Z6+L zhWLsz;(NOZ83~3*MYbNJ@&+vLN#4EX9fq7@%(u}<_Zd8-akzkTn960XO_FvGJH|jU zdX!D-yl@4X*UpNCJ%=aeixfoXX=K!c&`&w=^M+XVR~?IpitY20j*ry@N06&=RO~yn zhoo2IFEbh5Ec=(z_iMPfvPS)ybM31@J#)n#;BcdV#iP}0SKW2Lk~Q&*gqcv0UQiIT z^ju$+Goth&weG|phK)B&4N-H=bF*r-AA~C8=Rl`|vG6i$xpyMp`yiol-P@MD@{A1Z!`iM4)4$fk!H!V*`z7q!&!Vgk4}mMF{w8e7G(8oJ*osf{XM zFYi0$j8P~e$(g)^T{nd%egy(Uf?sFP3mDJazO%jyob>VyyYZiYXLDQGFuFQb(-@pC zsvAw=Rx%L&Ak11Av{BitR3(1|i2x}(;eF8z<3O22&X59&JQX zZKR*y4H6-kBJL-x;OeJDwVb_Vuc?IK*X=z`+Xw6c32TSO=rVFY{>fpf;3qSdYADMu zo+v93>GOmlWyNd_Y9s>PcBd2|KVC;BqWQq^8=9#dA)Se((z$eo1#VswST^x<)4@8Y zZ8XIbtwq^(|B!d*E7xU!wf5^D#@}|6a5$VFOvLd+Vq=^=uT2l9+oE5$ah(6f${1Gn z&;q4BRL$dava&O^2mmr;!@fQhp{VXTi57-BePnoeg1&EX!+Z-V$ym`WnQfH* z%cK6+-!5$L=&31fjjQ4JYj35IX#C)2EoxxC*soCw_o^H??lLrRQ_I^Pi2KnercuE* zjX_b9=h*<-Orsc6X9M4#$mR5P$qnm>!o#<<6MvmaFRHQ+-IwH8RPq2CFG7CpRU^|+ z9G|_>Vgp{{3?Cui(}jvwO@Rd?MuZbx8<*O0?4l))yvW&SYBb((eX-L07rWm+7fHU-f| zm$PoO`W#YW2a+y&D!}D@e?2sEjb4N!Cx0ySh3z0v;xjqaAz-n=%=2oPPFE~jE9}`= zX-7&9%^p&#CQA?Zg~do_$E+Ugua6Z;CoL!6tylW@yJijyJZPb!>(d4jF%GDB<(DfG8VyCc+^8=TQcgzUhe0VxsyC@-Rao2Be5`H z`)B@bm{WAn>hx;egY%rA&Gx3Hlx>`zo3Dh_~8X6mqx}SGaEC=H&grq@k!}CW_ zgaU+#e1M9PN>H==wf=a%G*nhDM4~64!tOx76NOmd%QC^5yu%wGQ%VlFtw9?<@Sj1c zioHdmHQe1}tQ3*((+s?%TWojWNW}L&MR{7TLG~YJS?*i3A_6jkZ>vCff+)C07Ipm+K${>{PnSE> znu4VpDE)TegOowRKKl3FiS}x0!w^h)bTKoZqyjc^vEy?%k3kyme|$|V`5$L*xqpM> zoPRI;W}GNzrNpqijPc!e9L;fB<#(UN0OVkPKS2MjGr~92hoG*dzf+-@KqHbNzmZ&! zGeHjY6_rZ%zV;P5K6tg&=}q4GI@WNydyp2h2%DVJu}?Y4`)&^d*snK8FBpY2uBOkb zwB?VU=DuHP>_1K98K@UX2kOWNt<^c7YzH#!qrct^J|Bhdd})i?VV@<#RA-bo2{JCB2^|Uu4uOUr7Q(<15RRYr zEFvcpmVFy${1R6&d`t%hlkj+aU=YUr3dzNkUh0tmRc`|6fQ;gKv82G z^IaJNO2i*%pUX6BS9dA;zU%!)LlL!LgUC^f!-1bhci-}CmFciCG=4`HX3_<}inOUq z@Q?OMZfoOuJ@NlqwH7t*(Q!|Yyy_#M5{6g+D)zvC8-Y-Xe{}M`rT4MXR0gMkya3vW ze7uR&k0xwX-hxOL9u93_S$Z9qYh#)wG#Q3SMF`n~-!DX|h_;(zl%~c|zBK@1ptNzPGCWN6&(9XE?Qu0QE zl`d4u+9UDtAwW*V>;6w^zcA6BUP;xv9Q@geu$VP<>J&B|oC>8I+PLZ0-%?KLs%VLN z+zibuH(Q`On48=lUnWiTeYcwC`0zTD^{W}{?U`V6`f9)3^V{nSFW4<2WU}5v4EmjxmbO+AiD9{HV(+{__|(O!ZXw^aoJ? z)*yA$CEYDcm7U1H;fhY3X<*v6 z2WO`=obB=SO~gm3dc7~hsXxtBW_#>F?WVJ z{aRdLf4vVD8+?P_Db9Y&J(2j^Y<0Lw;cb<_^0Far4V}I?062Bp3KgTxugVuoi1Q!p z(ifly(fM^kTl~KTzjX?6BUK)4_lO!S6mo)Ax}^w~9F9F%eAZz8B5dw7c^0w^b!}dN zupyQ%<3gfBb?ZXjD<^eMnw4tRF>{v5eg}SIkrXp2rV-u6<2K1}cmx$~$5`Q85Wmcy zNQ@|`RtoC4Mn9co;jDAYX|WY|z3dY%QK}*x&WZy>LBSx__)r4Z^~XO)^N2;mCs={P z3y+Ij@^YnMTJPs0qvq5;&bum9aJ^g}k81AQF{kUiU=Ve{PZF%{e-lG=K^O%JU(f^| z0NBF8VhD3?iC;&=hgkxc=VB|q%`qHpwJE~jrgK8DWWK2g>y7TMVfWkN-2U&pJ1K*8 z(|KWxBb~?iY*;|2>U@zXchGxb8_cl|a;q}hVppe&i?{Fr{}(=S!dr;MB=L}z8B z57GzPd=q4`suIWYSxCRqoj%6lZYG4sWgt?jS`cXcTggtt-*EpU3nlA;qvzl+N#R#m z2B26V2R4V*mG;~=O=Ysnl44j)XVTjbgiI#Kbm$;f77CTeyKnnS)`)s{+aA+NOUwU9 z3DN{t(%d!&W-lVLrvjB6?+a`W3E4VQx7+A5B!uo=6weU9-Ru#a{)m_M3?MC%Vf_I^ z4&BM(roHAM@2 zEtD5&M44hj^<@`*Q$^$}!VvSA$rGG{1To%yu%1?d_g-2H&68nQ18JFw*YZarr4EL#zH~PqZfq9>cY&xdu2kqL(S>{PDy#Hh{K@ zwzdKG???Z4Li;^|H1@}~v+?Nqxa8Ni%?l6k+7u$OT-aQl-nD|BNov&+5--OhpMMvM z;Bg{Hv;Lmat+8%HU2IQ-FG z9<5B#fpfJZow0C3dWJA>m3tn@BoO@FT%7sfl8zp$x;Vjia75RwD5)wz(o7n{b)?aQ z?Xe;X#ddLQO(>Y@Yl9a=Yh&O1VM+M=`+RMU*pW#J)xA?G&N1oh>JK&#@2qdr;9{fJoU0KhIn5Q&<91ILIcD?1o zGHP@D+`_@H^Th+%(Ha{X0DI3pAEW*s-`ac`-1e=cf|295=o~uEq}jb7U3|Xu|BnSQ z*aO;#e@}2SomJXnvmf-XjiMkXuP3R9363!Kru+bc+`w!wDv!U@661r>55sD)9R*WL z_g}-rows=Fe7=>dnQ+JGjJLMyWdKJYTydO^Y$`QXXCUD7$NFd-o&d=xvDQc-6&2|t>fVL3Bp~yU z%gq31r=K4{Ncg)Oi~=F3|2f430hhB{;~NULLLK?0_<*7-?P8H&Zy2GSBKlWYZEt-E z{<`a21@%KInq=8e6+b%BIRGT{Kp|QIQaO7$6IfeG5!vKwo{!9ZFwJIq)Kx+mmC+Rr zNZG7v0Qc`dVXhbP&(IPxjd8|67=6piCP^PK{j(LOo!g0Va%{pl+l7(xioz_j<`|P5 zg!2V;%0A9TT^Io?YH$DHAUan|L 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

    @g=u_pZW%({R~N_78}r^r>#q@`>&|8P_Ex~D-_ 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 z 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%0DiNob6Hxz|U%lpM3cDhs^?d#pF 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 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-onboarding-documents.png b/docs/developer-guide/img/savor-onboarding-documents.png new file mode 100644 index 0000000000000000000000000000000000000000..670a44cf3ac7d88b65627013384b785c47ce2c98 GIT binary patch literal 86593 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 Ue9UZjhXDVi#NT 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
        ; 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ٸXGCh9$NkC`KVLT;PRC4 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 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-order-delivered.png b/docs/developer-guide/img/savor-order-delivered.png new file mode 100644 index 0000000000000000000000000000000000000000..8b346fa7896587eb479330d7ffdad177451f42ad GIT binary patch literal 68612 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 D8 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覮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 AF8}}l literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-order-on-its-way.png b/docs/developer-guide/img/savor-order-on-its-way.png new file mode 100644 index 0000000000000000000000000000000000000000..1b610816aa4220f344aca2a7adf0cbd8c7016d1d GIT binary patch literal 40227 zcmeFZS5#AL^e;-4CWul6fk^M5QWOLty-V*+dPgZix=|68-UX!hk{~4@gn(OAdWS>^ zMFpgUfRupryTU&I`*82mc{}5b%@`SEWv%tq`OWg1<&BY{7CkKoEeQz;y^glJ2?+^l z83_q_BK0NUm%hi$zkq+FK_*(NB=F%IYruuNo0X2cfdPpq@SU23G|r2Jg7^sV;Q&6s zt3{+F4Z@#Z!r zZSCF%pc`@rUYss3q#_qu#Ifz=b5rNXQA;ceRLK^1=C7lKu8QFYhwty^5~BCRe&=|+ z&E?n+rDYza)X0F~&hv zFRmE1JE({~Q_!kD1B<@y2H(860)|cg-?0B%?f)NMC14?5jzxrB=E67#NsRDYUj(IFPfnn6=TcaQD zN0m1SkMS-{M88Usam%q3ZlOs%a2T)rh_)zpkh*40%f(_FBE z#}rArAZz@$+$9F?`yc{=YB}YSiHl2W{G3c#RT9-9hgsslFWzM0$_^nN=R_E5u z3!d=`Tm>~N`#BCT9xFl=pB^8C#u=-*yzr^;@%Mis%Zn?L_P0amzrU*^Xqd|{D=W(( z%Ch-Bg)(udwJ&#Dyx9h!IW;-UA91jmX^({TZ`vcv;PBz87L!B{eGXlNnPANKsv)Qi zUlUJl&Xk!YQc%fTFLv9#jF}oF-AGAfuyNyBGu|?OHtE$E&`fK+A;XQkRkCGj& z*UBLhIprM;6;EYyl#X33nfhZ|=k3&7TuSTg4r|06TP9-zrPI8Y?ih^#Ms2tVCqyKlulg&80{ZE#n>U6&7-Uu*m^*fbkf0193B zycNqoLu0N4T(#mPp5kY5U?tH1PSzt(cQ(fL}W%PpS%OpSBPlv~d9xpH4IPR1c(e#)>aRfh>t zVP=b+d&2uc^@H=p`F0?KPNs+_)VciAi8@BCeml$jaPEYYSTGzsI2+t6(2I#*3jmonjFSeulnAUzP#!6%JX+7W5Q;^3%7N z`(A%63E7it>}b+CU+tnxsPBX#dzZ3q(&`hlfqc0AFBFr#!I$M zPRY^cw{=8j*o7@)?YG+1;yCP@7D?G^;$|5&VjZIU;{X*x-5z+mdcs?aKGCMhGer{> ztKQ_`6a3rSV=Y7pk$Owv_q*!N``Pxr*t%oq<0slakrE0P+=#N?8=AClNyx6UuxEh- z5qw-rlbULtp5sR2GsXsZihG2Nwp5pUyLGXW>Q~kfx`_%-xMD%N@0y_pGrjoBl$yZi z^72cILtT%tGviC2tE{PX{U#JUhgDplkbKERMh#2jfjw?rI<^A5KlIVDvkI#r#GVD{ zmdlVf0qFJ`&~5)}h!#Yploe0n)cUlF+mUaga)dhJA8+S~{f)8BnQ3s#t%&^_tyhL1 zD>f(<)28{#K5oC@oBxU=CyE^g{#w~G0vwL!gq63MZ#|ggZF0|F@=sw?Pxaf|L~eE3 zDxYCm`;xd6pMQUuk(K$DK47x$c$`Js&;MNfY0iam_55nDVoIGAgzk+6{*gy0)Sz6~ z?I?kXOavA?7IU_uw6o=S+(KY2qxGAMFn$F6I5%mcluGs z4$yiBcpf?#vZzGQuN2^lpy4Fy z&h3U)#T-v-=t>VdR+IvdSNt)GT^989n#qXi)yt;Iio*-sSn2K*L{;K{Lvb~ zZoM)F)q@*<^aZ<@l`6s5k|9x64@Yg2udFP6UM+}NVt`$r_MB~<4UCW6Y&tJ%g$r{I z4_*EU7cotGLYgrKZame)@x6dCXn##7>csP8VU%fpbKctfkLKXT8Iw?*o+Q{X(x{Ea zialyKZ(&lpy-6%#{?xzibbo|0I&^w=zsG0S#YF3Tv)2<}#m9$VOZpVPzueMCR8OQ$ zD0jH+rrI5Bx0({7J+s>?5T=GmrIIH4i+gUe$ZEv#g=s`gb1M?~I&BA{nA{gE16b zeR3uW+TO}Taix2|qgozbr!V!DTa-*fTovKpVpmBUZ@Nrux{+e%3O_wxt$^D^{!D^} zf_SKn-*~+7xv8S`Y#7Eq4L{i5>{8kHxoHG~T2I=W;Zvk)_8jA_uBu|+xhf%|(}tXZ ztZFpm+c01um1iTWNwy}Jb28a@0X9NTyTqlvPwH3vXeF&bt< ze&}jkbSkpIYh^fpr@HH~%(V^@NRb|^W#VI!tg>G<`2OB5wZpAHyfpZIKZH1LKYwVo zV11va1PXh~3T%I`C1?vge!TtWc#H~{q;z8uqi#;Pg-Lbm4Hd(Pj%mMd z$*zqBk1be{eGba$7B6SHToKA;wf7qRR+^L?;sFMc;0o;^@7rXwH8T_cdXzqLm|H;g zb2exsQ*rBW%?S^T1!mhiESqZUe)e94!|Po8yCVf7wH7clT~9`VbbFPx;PCHiYf1Id8ivY;uGX#Yuq4L= z8te98Yft&X0oU99wtX2>Bw*U;2`pW~*IapU9c(C=F$k?q7-;^na z)9b^1-nyZY8fsp{H~czn`C6lXF*W)g2GYMBeC+T3DP+2{xwW#>JU&)7u@(q8P8sC}?cA_NLSVBHtk6=X zrFh18H>%nJ*VJ&hp{$KQRSfyq!Kxlacw^`!a;DbA0?7mwBZr13VbNP4Lmae32>@(G)_H7vR)*U>eqEHaR@cLOfpdmny)Z*>suXGWO!EVu%P>}UV z)9uru7D@CQ^@fdltb85iMq6Vn9oyS{)7+?WoyMqRh;=EV98`&l2n`A0X}u~OywPzD zCHUq-Ed{YdJCISf4YMf^j-Y<>E!d{eR@`@KJNr+^n=P^9*)y14=bBCyH-sKSn14Le2rSC8)^Mj0+MK z6)g|k6P71gOX9-vT|tKJYU+4Q_;-nl_RoE7doXpxoBWmGVhKP*`LfI|Tca;3n;{zf zazoNmw9&pEFH$TL zufzC+6H;GLUFqGNQ{*F+HoDpBKP`^#_TPIaT3vTTBGa5j218HrHHc`BNYh@{16haF z-5c=McG*+w&__bC(kc%eg=7*K=4#hRk)Jz{)@ef>D{IDBZflaa=toSC`ZFuTWa{+>+O%*6KdE6 zOe<&X9pjY9sYE|tRq=SV_w9&alOAFIEs>`ZTBd?nw6F5+;NA|mVkF8pkO~w`)+akx zuDjPA)!}-_`oLwyT|A*e|DGmK+=|7@A-?^i4fgj?-cosX0cLZZ1+FPy?Ec)!l#A`3 z-?bMki8O``N|DC$k7~6Yl^v{l%xL^-Q&T>2Dbr1laA0`v_dA#1;J8n6QR-x9fvClA z^o@JvY|V0Jl1zg7DKj%PHhLTb??SZrdsT_s|A&?$mzGtxGc@IZu9D4-kZRU?TO&;#qeGEZgZD@_dDZbsgT8Szo#y+jhVKI z#2LRwu87hnQ!aB(A6k^WHMQ2bdfkbFp_{Dul8ND*;TQl3tb^*pM~aVL1cJAUGtj@A zy;1KVI~mpC#@>k9Th>P3M^e%6#m(x-V`YazJpFtZIu)in{hBtgNXxhBGuuH>LlQ&qZ^5hJ7dIx0BNX;#%CB(^1}8A6n88*c z8z-i13=1v?c8!?TbNtjkPrd@e-DRb^U9~s&7Y8RtxnV%5gQcaAA8g1!5;a!p$GkIk zJ3Kh^%xs;@U(bHjSZv*7sf(!I+@!vN0QciNDD@TcQ!QY(WM$>_Tue)q<=T+LGmn*7 zRcTOsusOlGO92LgdNS|dA~@S(jVjNYiQveqhqbS)<2x)0FYa3GSC95ze7d)7NKX7pw3~8>EeSO(*ino-*D<3nFQ z-MAr*7e2#@>*j_~`WSc|)W}1}W{b}E&}D8j()AQBmwBINQ`#@PJ>5>tYwAQa)M#x% ze{d-$o6(Z@#eM7CT~y?j3tEFTA)8ezt^>cH4-VmNs*YuqvX?_I?8VQ_!XLrweHnDc z1tTHmMS=Mo^Y8DBHUHC~vfq$*?-X*?2H7tE4c{~4Gz zA&cb#)ct!Elyc(PAP&6!4D19TuTITGRh$w0DVYSiq!sUW(IXiXD21%l?dwIu(Etpr zVk1t}j6jU1r0mTl&3{M$0Cm@m;gISPsb*c_I%K@WkB3mINi z|LHYNnYyuRTKRI+K@4?FsIHc6?JJXT>iT7Y3z7SRCgC+$R8$i3C_|gyZKObHe185X zvShMawsd^lq#6ce5xHuw)fDWGsQ7ZBLraU3HC*BMDb^Dh`0}|54maF9{2kMGb{aAh zxw5rqt(mtbXz)&$=lHvp+w8|DX@gmS%V^-k4GIe)H~<1&1Ar50U4A_QYm*Y$T*%?i z~qbV)s79 z%q!=++`9`ja);^licqy&kM&URFz0gadX{*Ys;(|VUbP?{wpbj;ljV5wi*3d(8|_!t zj5c5IqLCg6{1Zr2r2NTlT`n_ZQ|~i-Q?R&}u{@BCtZ3(@7ddw9Oow&D)s9}t&7qFg zbz2pD+ME2B^)EI?jqSA~XpOR#RT(m0)z)U6x4zkrtcFVkSWe8bwjFOG%Yi?i2>5hT zt=qB9wmdMw$7N*ObYDVc;%?!E)`{)v&onM-@PTXuo|!Eb7Z@hO?76m*T$M1|{< zE$x>hg)->={FG;NQ@4>I=GJq`d9~t2*?(^))B!pzhoe1gt_zX3&sc#*Tikp6dLENR6%F?91J`a`hsGz z;keg;0e6$sHxFS$`7w|ZJd1{(EGZB zX!^3NJAI)rX}QC9$NhZ~c?m;QwrO>Sex9;{*58V^GQG!dDv4IrO}fjc6Jfo1_Z6hX zvz&86#zz(I?1^s!XxrV0Nv4+z#AhB|W4{TVthPs)-roJfwCmmk7DaOso>+Evh**;n z=~2%~eXp^}klLjuF;m2}LR&MSDtjb&W3n26O-yowe^~slg^J4s$v3c5JRlU1-Ux64Jb6eVf;OLK9C_Vs>>xlgs8Q&d$9(eN%pY zq4vV>jdY6<;gFleGk^QKPfabhD@I#8$}yw2w9;WA0n{<2jG7^Tri zvTg6saa#js7TiPQzew{kgdn!XzvFSToPZjzowQaUIaz+BtU7OMpvu?qVc`vt9P!j& zB`@XZTX4xqOVVF#?OxjZw-5=B`7QW9nU0tM4gE_qokL^brG_LDe-#>(W_2mbeB6uc zS%_z=y=cb4rDdWuqRK+uW4-e_VFOVF+AR(jVl`bLEsW-YNGJGd>Z#fKJ z3}#UJdJzVqMGyJPJdpr8$@m0QW)u}%H$^J@ zEjxEuWZA>HR;13?>zCdL6{Mx5nK|m6UF=-18qiwWTzlj~XOxNQ9f!2dOA%*NHa0e{ zTk}y0927!nC%A1|qU~f0VnuCqgf{rV?KzvHdKlPrwHi;r0~^y#T9B=I7~k!LcQ<%m zLs$NVy)+F$Ua;RO^L7Ep`*^iivmI{nGBR@O8XwghD5cad4kJMBkoria$d7nSG{FH11(?hY6ECmo1a2jp#{6l2ZpHKTk@G;Y{`J@v zbMwq_ESO@pS~lM|HGF(|Stv)_lX|MZ zQkkHWqZkSgu9yh?(I-?Ma@>-7D(Sp+!AlsQHVu52uB%J280++C^*xDk%rL`SmIV&q zXj`k`bX$SvH+oM2zH~JaABFU+JHJP=Rod4@F(;V6>$=l_%cTYerJ{}d;~G^R0I~*n zj|3wYEIMvuwAhbv-OcXe6*}3)n^a)9ttF~a_Ia3B;W%o)^`Mo6y@s&vOVfMHQaS@i z1e3{9v8S!#7a%zG-)pb^Ksl;Uyx!b+x-K%klNivuWE+sl>lZi>+SsI#&*e3}J-5|g z%;u26t023i*MG78LX0?Yqf&G3c;~{IA%}xQ&5II-5s@Y!JY@#55sZVN4xZk*z$KO> z073~I3Yu9)hG1KMuI>9R%`B~a_Ny|83FDEoAlmz19)ixGu04ZfuI#zEU!i}e`2gZ6 zdQ9=ElD_uwekGD0ZDyH4{q(^1M*zKof^2ZpE*)`8T%pfY8shBW9gbGFpOj=67W{&< z1oFvvj(mI+HTm;}!W}LX<&c_;Rm#SHCGjE6mzF;G-a8l9@gezvVV)E4?eO!`pEm}E zd+~e5&cTWL?F3el9HYDi;~!JNIUpgsh6OhoQl+c>iPrJ5k`a&jO3!B|qo27cTDoTpPzw3TId^Qtj34#N$AhlB82M;3E7yWZIZ-o$71B;}>>n?|5rmkj$%$>&ZSF047{pr7ijOD}z6cM#PXG`Mme}H#dWk zPh)O#=dvlcbKr%(HoGEBCf0N=cPLCA-JsG1l0TQq{9Cy~7{LYCt<)~BYFDKDrYy?*_`~2lMDNobS`uMx8SO&$? zM#3awxYjN+dYcQMhAuR$)%^ODH^n*f@w6;(s7=A$+CCc9W;y)LVQ`F2NinOMkNH=P znMuo%mG^I%()T3Cs`n6PKynpZl_0Y5DYo zz#@e8cusSMCw*1;q-W0UcAIZmmX|*^p=Y!8L#93|*x1TsjJLnHWcu-}R^+b`RkqmQ z5>qhIZepZp_tq3BFSV0!FS<6>D$}3N>6q@(T9-N%5vra4$|NWvuS|eXz=pg=Tq2#O zw5R_)jp_1dub`#(i-u`|^hNWgspt+Mt$>${!yRtvutmvxZfR`VefCsdSM!Xq0Z5Yp zpd`q!@J#_X7C4*zLSO|Vdv?pBOI#%aFg_Y0nl=KwQ(EdEX1(jvAlz@i!cLp9CoB1T z9jUQKe#`9EcM0lYBiG}XIu<<+?8}c8VO(6|8NVQgZ@?UX9`Eu4mfd4lGJ$dy_&wOx z_%Xs_k5CyS)@5~A=Tm~y_PLW6n|#`XXlXkSM(OA6i08GR+47h7W$4!zfVLbTS06WY z9UF3SNsN!7OCBCrJzR+8-Is?lH;`*Sxe6{aqEL{1txM<_6P7UAGWw2vYUF?3{cs72 zam>)9r&pv}n!_Uxl&9MS>8iF_N|(t!>_iJY{wjz}I7FZX?JE!n<9h4$(aMN`E==In zqZbVuyulxYMFBu8=~>8-p?7k7=cCawrJu^@e^G%8H6HftEXdUBCzP*Zv!m`}WVzD< zI{$o_-Q7^zY%g<&s2?axD?c)eRQ|TuB!o(0jv*?@4`_qe2@lG{ulkfMXm+*+Hz;~c z&W{!E?*4s^FdE13j0b5fJlzc4={>SM@)Jwd)oDl~8wYXhjDfDO$I)9y0YC-uVw3{*NywtpQLV}1wSbko*=%5RnMnGs2*BL^f}Vk9VpPQ zNRd@PPNEce00baWTgh+18`h3k4~_@2dS4yQrA^KZIi)ku^5er#jcuq*hCCl4AzaAe z@I`uMuT{_8A5FHH-T=p#wgqP$R8rq7|HSSuA=*#wzTF@vqkbux!v5nI#-9FIR@x!h z{v#D<`9EY;s*0+1w-&hbxhDVsAH?cQG=-Z)!ij}UYz0D|sR~V*a z*G{TqbJ_RZP5N>}JS{gN_ZoCE%IqSD_wF@PSn_4Y8&dueYI0LsoXrr|M`rd8BIi%n zcm_(Ce)r$y%Dh>;QdGzEB_wD#OG>KEk=y=!y$A@Y^^;s^ctGyk`o4~BbpQReaEXk$ z54`J4P-X(3_8}1F<_d)mUy71H`qe_%EWsE@Aw6)I5^~ptVrURdDZ^y?^lB;ZcUImMTU*U}t+LDSdc!8-Gc$&}?b=9&Dji z?4WT|3+1J8uw{HEz#VaQnGZY-4D>nK>5EGE;b>7d1&~5?&yalW-L0t?o3*jyCoUBS zUGr5vVjIt`Tl2X`+8kYWbhy*!RwIHhxE!@^Q4ITZ)@=^f7X%1Sd^`_7y-J6MtlV-# ztmGe<$@CW0kZ1Qr*vHCegGD=BRNnwBrigO(dU3SC4IedX`fGP}@R=U@SLJxVPi%!K z6+R80$b~m-hAU4mhQ{aLQh+#gt-;dZ3v{%*G-TD%$#mtaBv3Mz55~Xi*6Ox`BMIuE zif=kcfY8Vs?Bm#le_4$%}zFf3(7aKCQ`&fw+ zCA8|shH}w-a~1rjqs?PVva)9WKWRRc5c0Q{S(5(#B`TC3dB{+_>2Gwc%nfi4{*})FJJW=%+J#J?$@%E0?@#nh3$GEx+~Oi*DT~%tqG^*;vcKe@S2c zf6uX45h5)CdCOsIXt?mcy>eA?##qYQ0=~GX5n{OZXT)i7emSc}HgnY~q%}VV=FlUf ze6aVk$55w|a4D*Q6Std2c9sqn{qaI=3?L&T%I0aqh=Gv(>*ZK~*|XGG%l@gL;S4$Y zy2zl<3b$RGA$vcYB(r@&^1nuT8(Wy-Xuqw>W2S)+X?Wy3I%X-;5?^3KbwitVA|ir(b8)`s)McGoIbB4InuiP$P?qs(9NcEmwmOyq|#$v{4iFSD(TpGD#o;aKdV-flUQ+gi|x-HJyu$KXL9wYhf8S(pLaU zUnvjEGLLBOpZ}&dK8Y5+u0-V~zg#=Xm}@6%XqYEsAo)&N%dqgaffiMLY9pzCxW*;d68$fDpn*bpqQPZ zpSk9;FyI~`^&XvTteJ=PwY%^0QEy#VAAY_5z!`Pc=nw^&7luD_Rm8#2@h?;WKLRPm zC6-Z+_-chq&etlz-&dJ5K-E$DPLGZKxDl_bl=n8R%G5A^FA@Y9f++;SqRw=u&z2eWJEX zO_q;#F|0Rj1of369P=S|!f5x7UnT06u<8*?cnb|qbn{zz6KGtzX?tzMMkus4o*nZx zeCL|BlI&kDA{9xJvPpC3hqqeMW$|+U(yF)Rc-&iL$IcbqOy_KWDKQ1qpUU(<0|Zgn z^?tABQ93tueNhCN#P0`#L^8nmqAUkOHRDryU(1Gpdt=V79 zGw4g3(vF_xW%nIODYigV2C=?B30K^#Dgo=g%`IT49inpXW+M|H{Gd-UI|YiTyX5JVa7FJ^siAsyxy_eAOjK99RsqpOP;(J767y| z#t0yea6j%H7qQ(EAiG;*@vgBhvBQ0!!;LGq6p5j)>S{o{-vBtC*x?2xb=h|wX9&?B z`}!5oZsMZ42(iQObd1^!Dql80#CGX{b~$?9m=HVsZzBJDJHo)1^Ywpf0sOZ+|B=~$ z2mk*t4%YW0e){@};CC{B(>()IlW9QxUH+0)o4%;5s{No*a^B<^Uq@EaMS;8mC2Jr? z8Ih2(JPx5deduAavDn#!qd8q38aa#1-=XFg%a7+C%6Ar#H2m1ZE0R_6QXoayTQpVU zkek;b8DPE4ubxG%huV<5d1dmASBIy`y3hU2W`kH+d*dTv0d7@Lh3T0@S{r+C0v2Mh~zusFAIFY+I+AF4)c0n!st zGCaw`4Rx9-wgl9b$aH+Ps>k8RY_D1<9e|u?fKWbNrTI>+l66l`)WLt9fgke~J2W`- zi|BL(gy#I$*2K^)20?EG(wQPo#q?ZCkcOU;LN)mqm zr1in|r@?hk?@TLWsR!JhSoNvZjf=!y>={PLy(FJH5$0Cy_a)nZFgPjjjB@K`6|L1J zYgQIEYktBigl&qeWR}mg$a%8rYUsRY5ug$+#|#>NpoK_1>I&`n;X3v;A8&5irP+AUi%0`vw3 z@X9&unL}s4^$%cW_i_H~rslF~ko>5`9I%#cJab5JbBG|}Gru53iRfrG;$=&VoYxc& z?ulGlMVUjlv4xFj`O%_3k5U?>ASy8vtS~33krS-t@#J~DvrCAp*6Og8bmEscAdWX$ zO3}yfhsw&e<80QJ=()OH3~&<Q+<5;?X5NJ)rv_nBC_XYsBP zF@DME3!uUAok@@o$<>G*PyqiI)JmQ3l$6ZtOBW?jpa?LW0c}t;VG%4seE%w-izfbU z1*#tbobVAfvpNXT8mcXrE^_%Q5u^ohKQoG?nZy(exShyI*PrLUvmcB(2!@3;s9iV0 z$0vdP)s?-|9)%L=1crBy9#UQ5&JfVcdOg3UFWI@ud;N~P{&x+h2;$*+%|fk|vcI(; zwQ#t#)vwldj`GEJ+P|o>(oC!wOxkWpFa8r^c|$UgM!tJIlF+e+Snr$m*$^6~UYzgxEb`e)%B{kJTo z2!uHVKO+#ja7s@MxfSo>Ry5yi#~ETGS-<`B0bg(5YPi*J%zwwDK-kUAZ7xqKcs1qZ zu<$(Achu;pHRkxac>CX9n_(vxLA)tlU{BeymBN?@ou~N$dU|?!C-_Pg3HgN+Nf8l| zH3U_)nXYCsHw;lV(pLeb2)+?`*qhzC=XNjXA2J|!d)2`r04cfef?!uXa80M20crs7 z=Nk_2E1jF!*vz0#QqN3kNQ3*|-)sK}I~Y3SZjqp?o$^8VW=YtVH8eCJnON)b;bv8H~GE61vxMiI^7v#`xJZjy^{c|Ud}?CHscwZ{cOMe#=ztJ|>H z-B?{sc@jQ63;+fky)7#z1zooH{0_^w<^Au^n!W-ICMRLCV#@KTgE=9p^(*ba;du*F zDhc7N^UA{KdrC2)BT5Gy;Qj zN9(eZ!b$hs-=0jzsfb%Tgt=`BXzhIrn-DxaK0m%9Nqt7xEzDj_RvAWZ#+)6*EPJRR zE!S}e@v*-xyTap6MnccEW)x?ZxdLL4==WzG5vLG`!Tz1SiILSEf~1wGnAlQ=sbOY) zf<;O|4cbPHQi z=f}>Pt)~RX+J9Kaf$BC_l=eQ{@BI5D?3936{sDcFHU?^tR=^G3L7ecUSDRM0|FUw5 zI+`dvRj25IP%HIjC|Pe7LcqEcS*}HS81H4iSi#ehn6ey7B}9 zX|5%~>*vFhS3c1o5HFZlhAKxUg7gqf-fqWezn#H}hsfWkS-aYDaqC7a6{DVB@+E`dM9%iC!3STdpzRnenOb>rp;3&XM)8IEci4LmkWu ziCNU8h?oi@S76sCz01)u3QM&AwJccuvJO)$B>&3InrKIpI+O?MT zS%iR&pWX)Oz5QQ$QUuyE0uD0(WI+$yr_Z-DvYuFtQ#rXAI*l}RWjXxMKUB*T zg@=k^OQ~9JuvZJSZ9(=-KM)uPv96Ej(LgGARs+xLv85>JyG#lOtPy4>F)B=9~$-Ty2rF8AUMti6 zZ9$y&hX{;+YZX1;Wb5@IcV;il2DD%V)+KzPr=8rATzG!c)my22sB^t`C{fDiZX_W| zaY?DQQ!Dypv2agOe%ZY4jW;AWdX?w?chJacn@HZq_A|5*tCwj`$P33JgU3`1G9QO& zPixO~AD68bP>y zuq}A!{Obkb>t1t}BK8L1N{*k1>;hZ>>`PkIN7N}kAn2#_ie;8m%(6uMsSkcQ%18w) zF(~QKyeg4>#RdA$Fc)F2W$MQ7Rmv8g(^aj#@bYsA<_L(Sw~an(DhzQn!XC(ypGIxA zt@K))GjkQJyI z+@F}PgD9`@it&e0I{`}-p`8T)^uI|x4@>XyulY_M*4QC8If2X%Jxi>(@2;cE8 zzp!yW{NLT61$M*Y2M*rl@Ow~MM=A-1*%b|4e{%$9+9t;**f;Glp*}R3=KY0tZ7ST` ze#vynMtpK5?M<0tvKs>VtvjE(gVu>>51sm{z0L7d%d+iC6lVuP+dHm}P*9~raQ9~0 zQNZINBT5U*blG}RsM-AFCby)Cg6O7|_u`6sv!gNVCkm*?2jhBDYg4G%VNU!!$~NzB zKSJ!UmkGSocq#WDer)(Fy__|E4XB}&Qs)puP@+dfY@sl)<9zu!b&ps7j6MZiBd^36 zZ9vl+wJ`^wF;bZ`FK}a8JX2S%p3TdW62#l+M|O|nxYI^fwMntfe4ogayv{6VPNrHz ztwt~}2@!7Q$CSypu3+1wU)@bU=ZZ4)Jf*VAdrDle}4kfY|s*i3FE^cpsbaEUpUuJZTCSb7YvKj}C!y*R?!aI8qF@D*Wg7fxY8qfKM83 z^ve0hPi|=23jS6x&y4)+6x*7Sr|=Rf{@`LHQD@{4S0TWs@Hg3h=Oa#si;qqB0uIR zQr`Ihyh{}^++JxL`nwFm&+a+c7K*|p+h=bANpO}MM?7M?gG>)M9Ojm@9VOA?t&h4j z@dh7td`$Cam|!OJhr@YGw@BNRc<8{}SB>YiC zdYP`tZNrC+*vxatnA^x0Dxwn|-MT#sh+IN2al4r zNi1a?W?Sx;|6ZDj^vF4u%+3oK!79_64d9M*rO$>leTr^RKVe@DL*D!;8@@7c5Ptgo z3c@1da2YjX9Bb^~0+{i9YDF6SZ+kiY$oM{L?pBQ&1H8cpI?IH!*frRm&B2 zfUC#=S~}y(X3&^w>Wz$E^MKr{m#aQ&a4_;IHIya-AP<0w77zNqIiVm0b=LK*jkic) znBsgFXJQ7>^J}1#=V@*zU_(jBIw}74_N8C(mu?`Xl_PK{Aqd7P&!jrz z;Q62isDw;QHpMrxIWbSJ?SYZsoW9fE}b zBpaJ5V-1pW+^zwN3^{NFgPpn9jj4DfL;31Q#yRf}=Y}~ycwA>)x>d<_6rFq9+7O** za{HzX^O72tu@;sVjV5OSm399wDss z2TSD^6P14wJI^RZ3*7?@2b{qvE7I1MIS@2`{Lj1?t2Uos_+p~`(is(Zia}n>$JF^A zvqXq|_-soQ5}Exym&v1F=xoN{DQwSn-tp<008sSr%{=Y0DiGm^GLCx^lWYURkJ@tA zO$0v8Qek49#iP?A>D|I zdUT})ko7UetGX}Ln%7X+HrHJu0Th$uXg4C z)809@M;2}Ex`U2w+a24s)iFDE$F@7RD;*~tvtrw}ZJVd|e&4@vzMSjSr&?9l8nd|O zoX@zQF-Bg^dWk@+lz?Vx4N-OTIctm7mHqe|E&Yv${ZT2%XqfHr^5X|Zg6!ykSN`jX z1;CMOUiWkK2O!Y&>)QoD@G*wxDei7M59(uZdEY)L?QLi~)8pP_g|(@o!`$Duqdan9 ze!ltWyb1devFQltpxgDaeCBjUd|q?ZWK0axgw*BGkZt2UIE;m(=HJ6E^y0&zh|699NK zJ1RccTIN|_fBSdTxr#_uDHb|`f2=s#WNUCmEx@EDDis6RCx3hbRXeJ#)+TRryGSnM z^}0VuGa%Ci+NWr0RwbH^X$%)dF*-ID2gVM6ba@GHPG=_j-ZiWRt^uXn9-26eL28g- zAqQY@J^UTn{~g_G4msGp>rDR@EHeSx|0~{hvra-tdT4a$YuDtG`0lJ+bL@)9N7~xP zW`BdLYmB4uqHd=0>aFF>)N!nBi^Ed+{T0}iv}E9~Up1e#FtOy+a2$Sy?DuYBHT}C? z(ZSrk7nZFx%Hb>C|9&p_l+gUCMK^Fw=9n7brDL#&x0@HNAL!Dax@M0qxZF~Lh|e{; zOQ|%f@iAyCVh$wF6?mg))TKcF*g;*XqE6Z>1}X_%GVtFd2D`egF4fna>x^2Sj=HATrX3Q- z?mt?#qhACp+P3^}$_-B{Pq`vFN^-m>c`kW$Kaafdp9Q=hBNs9?E4>st-JZDCEl(D_ z>~9v`x6TEd-zqDn(0@E|-YFQnkOaQY@OXdOCs z4}V~FcmLsHcJ(x;rX)l@CHNKOHRbXpXt1>BKnRj+*AEmCR~_5VXC<8exxxIxTLI65 zPV~L=IT=X1$JdlpoI`eQi+RMon~GmD(5+N(<7+&*MEv-R{~XgIrgT`fuxU(v8ve?Q zh~%9Vx8QI#@XEmPG%-a~`7#yMtq8{il3G^E1Wy0mht!0s+1KfMRWAZWCSE6$g)lef z9((3XzqGh)gja>=--edl>vVeArUa=gUy(g}Ln7^)HUP$bVG~tqVg;4eL#D*`SzLB* z0;^c33YQw~t*U@|VW+L>#ulesfWvYg@#JIRb12SN}w& zXOK&S`|xN=b!%HQ5wA!02Ha z-S{5q9;{}NM>}jDhlvocScnP9hX}oGyOxk2LpDS|q=ed*9?le)>4<7bgEn;HU;ZW=LM;2ElfF#0lLy#UbTi6%? zQjRoYv%K;f&b%GQ7AJ1K5jpwdJkKiq$&#qt2<5yhJMf#1y}J?4+Yg23V(r#8-T{Q7 z=f2bH>!%gx99Dc=1w$N3Bd3DERw5^e%JL)UN55JmuAJAdks*0UtVaMRpN%#R14B|I zy0w6fyTH=YoxAQ-7$W|?oNn-&ld<9a`3Hx}){q&c*{k3>ZJXPbOXJ_9`>|nQ=ZF5q zio0}IgTimaSI!gcf{j;$%EhOik=6YT=Q<|PIGLqGjaQ~e^W#nxey|>&q1Xq4h~7Ws z7M58qKN=*TChT2rkBC1a2)xg)zyIEHx^~}z4iZSTtn2=%)#wAQV+wx(XbR@?Onnqr>;d!Z^ZJ1$1YHlTBSqYU!S>K*pV+2bpr zTzaFqY%~|?AMW0Wg{SpqurSsw%446{8Hu_*;CAG0H^oI)RW(~H64?cl7LX4=>+f9w8ND1efm$$ zIM*h(c7XrRcaG+*(1*3E_IyKWtP>))Q|R=E(F$B4gDx|}3&p<{_i{tCW@8P-?-7rS zdJIaZTsP&LpG}t=D}Ra4yXFn75fK%f7JfP;!B6oSs;=JjcLr#&%}-r*8ESNNYWUrZ z8IFYC6Z}o7KH4|4zH2?~I&shTOaD9=5+oDqc*4G&9b+2Nw#cDf()Vp-EK_LldXL-J zGqcgnO1%i+D{fqEx8>;BvW{5?uM^lHEfZJm0=de+ALTN^yMFlE& za23JtK1kKwtTc2oZ!KOn6u8Fs{CvSS8%d=gGh4N;unGsVar+u_ z+OHRKoE?0Ck6^TJDnU3bWXb^T*Z$?OaE?Z!gC23CM?O{{`8s-g^jj@5XF`{TxQj`* z5JN6ZYaBV8?g^_xcKH}|Nb)VuDbKquk|A;exP8zMN{*G%OE%L)0!Zgj=*!urkQad=X}C`Dpa?JzDdg z$vl*mO{}7w_0rd{xhu{s(}yUl?3m~Pb_g zUj{R}vn~soPVw%^+x0On)$ndJLK0Ny5n9=ug4C3px8t{-#WRl9$*~6N6>+;iwa&;= zyb2lo62(fnjL?nRCM$owx;oCjCFM)65!b-WX=yoy>8EBFko@{49`rJ=rlO)U!zZES z*F&Hr598%d#Y~P)C0DEWcYBG>ltj*Ee%p7g+2!RCe|7r@EGf zE)Vs1)si%;z|{Q3rN%v!w#b_tWv8FNWIUy$bpH?J)2MU(*n8+!N134;+0Yqq=fc z$19#?zZvUMj)w}3@dw7UMF&<@iEPB*j>}gY(f};B@d!K9iW0QqmbvRV>lbJ8-v+p7 zzMS4hSJWaUVi}(lXFnUQ%_5abW3Mctal=q$tG;JNs{SOQN8LeZx^BKDITM z!T{O^m@Ojig|ivOyKuJ2Jipv&m*^~e6Zlxr(5k&4d*bM5m07q#-T#GHvqn^JA`p z4*&2P>DW`IAK;G**xTI!p%C$8N19csykfx65KoubX1ss=Wf>q-Z_kjMPw8s%@hfR2 zGL?~$Nx_gFWk`o2ij*>5v>B?q6ujJoT64tIZb5FqSw=-R)H!e5|A%$$q-e?(RhmSJ`Rkqr&8Lwe} zsP;D|^pTVf^KCj=J#&IErLL3c$C|AuLoZAnI;?%IoCs=1GEK9%%g@e;-Dic zB)KfJ`43i3zxaR;4SD=6u%F0I+rrrtbiZT@i(myt&P1oKq-1I_k9QNJFC%xMl3)HZ z4y%)oX6M0V)X`F;B9-_35-g+cG#({jpBR8fA{5P?{WuIrIOc=@P?JI;7Rk-gWbL%5 z-to&_tHxmcr&&p5Q}db8HrTXI)OYReLQu@H#q18`eBhGU78U7^Q=n?tu-H9DL%F9> zo{Z7~_68RJl)uL#U-mJ$uGI@twxRMPf~?i|#Cp6@erVU25Kp3ZKE5W+fH}HZWLS$Y z4@!zYTS8tS2X7Lah@O0n$>JKRJNV&qx%DSI+xY84C$_i6&WG1teXKX#3{56g*`lno-e3+Gh9 zKR42^$P&o;%AA3k;phAvLozm!yISm5&Y|dBBd4L`T&Ex3^ET?3Q6Fq&_rhZ2ow$$E zN6IF9^?-!ZVdh;nREsVKi&uZTzeja@8cUxoIs{aD10ykPl&d>g*c|8DI?&M25PD>T z5pz6Y5gI3BdLzhXZ+rwfDADEy74UYh=>7nJmkrh{z1g9X43_=FDz_YovYxU%znSxq zp^+27Rk-Id53AWxY)XP(+g(|Qp)tP)(?m$~gwZiU0uZoBn-D6A+1x-mJ5x1zem5-Z zIaB%q$vBo0NoKsbP%vhMz9Sa((4Ffp1Qn0ettgu_)olI=7{Skp;pmKd`MQP$&LsaK zrRQAcYPwntg>*AJ1X!+=URWZRraN&YD1PQ1S3akpFvPD_$hPahbJ5#Y!k9)l851Bz z4*5{5+>k6ta(@KyQ1D=eInC)c`F%*PG&m;7j8|l&TmGBY>^?4F^XIPmXzcSI_hmf9 zDn}w*wi<*5xOxSH0Um!$bVK5 zA$blghJik}D@TTKaXNHA>o6MnOzOaTkuvlwDAsw!ZfuM<^uj4t9j7hR-&?_@ZsWDD zF^~&}utl$a(jQs8c2!Aju5^BQqdK-CkZumiUt*I> zc}7pkI=6~yzrdsQWlZfA{f;UARoR+B_JUvSXxNOI$vMI6n&Dp0rM+YpgI*B*u;2Mv zznJyr3hX>~NQeyJqI1K-xo%FPKuPy{IFruO=rK#>65IZUoMyU-7PkY$QS~I?NQ(EE z=XUvU4Mo-cDN>`@*Pb-DlkslS2^o@+^-g5#0u*)r6;(WOJ!H}rFRV3TX>b38Dg5y- zr93l!9S1t_Z=oLUg)*PfXK4Mk7iDzRD)^o6dFT?Fk=v=Op%Th9qNM6keFOxhiTNir zDM>&N@+VS91JL4Y-1!a-&03mAyp>0eHUyViSQbcJV$wy86rH%_nXFP1if||S9Q%i4 zDf;DSo3Ya_L4)-S!O@7mbvk7I~rYv0F|hn@Q7^82;MgW*lp!$PBBP@dxg)Cvw1)rUAWirn$a%RnnJpafP1xJxN4e5a z&LQ-DPH$~j*IXsj$tZuw6dYy{kPnV}XNM%1+5+IZk9ab01~|jZWdzUP6fedmvo1Ii#t8uu$Uy{ zyF!=8En@rr#Bo6QY~7?xyOi;yNy3y^drQ-DtS;$cHvS>|423n|dzkD5aA@EzYs`wrS5lX&Yz2?OZjZoD ze`w~9qqsd_qGhFD?wnW9_3%q%uh*D#XQR%-lMp_*v4ylhr?8lS0jrCDh{b(+?JdM4 zMR}~4*xREgf0AQ(D{`KFTu1^F(6J>Z?TI&%{L17#Bdt*Oti^Jixy@)uVo(@xa4yFD z@FJgDUuM%d#mPFDA~))t+OA>%!#J%FTM~X)3R~qX85=E=zcx>)XUgw(rx|qvYmR`y=g~j z4q08Z`Uf~R8u)1Z?X_)D_j$b|c@Q6`LxR=s#H16g!~7)X9E`w`GF@QRELPsV4hEQn zFlzHOs&bM(5kMr<+N1SOgW$9kxkNlWL@3ZN2IrGn@x|#7`TjZW3Oms)IecfaiWdBj zFeCY21llxelIgt@zQ4iQftW6mbCf-7Jvhrou|t8dN>OBgVQ9b;0s>eIrPCz{ga3Po z8W8Yj36(jL7ZNn6G9(!on3;3}a}+Ps{~Ue;9sd7k|8IO6fu-RTU{^cKzqAK@2lL4-aN5z)AQyVoJZW2$5opm z`IA|HP8gT=Ki#=dfqEv=)QZ{ho_9xM8TV{tFRa&-X-v8O7<3elj*ek8Y4qB&7G$x+ zLX21KcU9*bZJO5e(h)de{f>w@%)&?n{4@_|3w%rnw3L(*sZ559QCJMA7i+*VmU~iO zI@w<)CPgk8v#Mj;xDS$v-Dv2np zl*LM2ITsffdqmQ^7TqTMlosd1(N@>9{ow?1G)zLugVEHK!wKELao939y8~=j0*1t~ zL;`g1SoA2D9Z`&}!{c8sUQL2hsK3eLZ4geLw*1rl-}fA4#N0--OTR!TuzrC#i-nB+tNfU!g=mvc{6K>_)#CY4d2j@aiqZ#;)fsWfn( zNm=+079+jY@z6*THNp)wIvt{&>F{=(FmqOThtemJux+AZPMBZvrPU-hlL0*lk>x7W zE-#KI*`6Sy`H(|n6q>`e} z=gTT6yQU|zMN#V21`3MQIasX!%zmVff!I~LADUInTW?SP9L`Q7HP3$vf5p)53AB@E zv3u727eMtK!zxqmY+v{P@^wOGkK?w@pHEiLldQ9Tpxf?JU#u{Q;o-&FMEY*IVU$RG z=R26y_8+VkO|38jgB~ZiwS~GJr3;kG!IF2(#-NEetZKW|fzi>FU8y_hj!!h}3yT>< z0HsU_w7dL*`S0%4W;?u}zLkbtI{BR!rHittlKc~H@*NP6Rk-U8gqU6}mo8K7#EP1J zAah}0(CrjOBIcpb_V0W7vdml+3a$86J5cD;;v^_)Ukd2-ezMQt_xz(?t)Z~0Kf746 z;`{bAcDi6gQT#ia-DXuC?l3=Kf3BAQk#Y$~6$;6S@%ehGYcmM8yQCSGobn{~W?Pjk#9_}y`_YdTU(3huD=bJr7gmHBH>#5K|W_fjL@3bVm z@QMq8c8`#;Vi-9AvKuQXVVc?b@gn9B7Oem-;XZDly z)n=X5oVlmPwD8GvUhsz#;GN%hkbb4vs=)Zu%1V0?%0vb1jsc-qy4_D}@zB94{c1R?I0}APrYF3`H#87??X3K7ZEESlRoKyn|f_K~!<{)fx9 z;6*-hpiT<8)T|AKOhkXSWY=9xLtfqSnaOUmNUwI+K-BYz!t|QRO=V5<33o#|`C@!Y z8>j%7>zL%E&z8xK5>A43HU6?$sNGJgOa;hLP*RW06bzZ- z%NO%CU4alj$E>)wOCq6Jk3tM@0J;kM`m4b7e+)~@n;B; z&p0OoP#CZ-3v@qYl5#%DR#zY}n`)g7WI+Tt=XrCKnUK2aV$=^@Y6U!wRPW0c-GAz3 zyQ>IX4sDA=F?bw>Zx_H3 z5U6C|wQzLjl#U*TNNld|{#Yp*dh0a7v4rRC+?+A1Vadn7)Pqj0>3$YAvC2v^Q-pD) z!D+V&=|3#34etFS^87d7=rT{HslSgTs~?b3b2)q+Z;2-)g7J}%JlyXWnQ-}QjBU-ik969B98@mnu@6!QlPa4? z;Bs^@`ut(6&&Lb*3{O2p8%!%7zw^_Rnda^7k!WVounCB05GBx-68EwWfX#>Q9U zk{J!B5`9B8USy=xZNKBoPB`&(HV<>hJ=e~NU`}6}mC#r8#0x_9>Y4~YT6CEacKQ}P zQ#(mouS@sDZnAQYUG|te6zluvn_ynyA|k?xEk^*zuQ4LW)MYx({qvuC*tYV(GuY91 z2Dut&pR?z4(fGL{PilHw{dna7gB?swladBG^9s_QUUsAMZo7#6k_CN<(umwQFz=a# zUM*|dUl!M639UB^^yr+kHce@f(hQ2GX}1)H3(?jUpo9v z+=qM{&r^qAiKw2E#imU2;LVGro=576qTA_Q768~qtC@U>?qQk9kApOQsv@}*R+U1G zigN_3g?~R;fAvet1Qt%&d;olAA*C^x48VhyWMH79LY?1VEOPProldhp@G?f8Z}t%L zKodDM8Cinxkh&ry2NtM|+D?MJbNzj7I@c+wc>_w6tX; z+un#bwOEKhkcb8K!VqxDTz9-Z5qE|gK@)YYl~nKN*K3x;XaI1F^MeL@sO;zPy@6Mx zxKGmk`HC9R>4MxZS<~iReJXV3=t~>`YXsN zjAU&r)HrQHNV?z?LE)Xka<}`pGw50oxh#c7w+lQ^TrI_K#&mGb6Mq9Cd||*dhCtne zjmJZ)J8$b6tmdF%?`e3zE|Oc!AbK!fgN<2$(U}^5Z$YzWKmvj|g%PbM#Gw=`k@t?* zHWgK{BiX3U3G)xiz<3boS2#N7GJ5<~aVo_PB;07~ABp6DD zklAE&sylL_dmGL|pNL z_h{XZY0Y}EA#z3%n^6lg#;Wlh3T*jA>DO0@LN4POs)rM}cS<1Yx)9--p}mjtBPWFK zW`ieN`r{LtCK(dKlSW=@zfpn%s^^HK3NxKf8-_s3@T(;t6V51y&xOOkOjC@oF6>IQg{T z%RQUz=zkKmQU_#udmZe~pq1&zY_ke_I-=%?t)=WEtM$Bl9k_YXU<{tj7BwIqa$ni; zhNbR04BtMUYs-glhAVWYszbw4h`9T3mnudgd1mDm-;zwH-W;*FJ1gj$pvmuI&nPXR z-5S>F=G`DMD;Rzp{01FDIN|GpGV_b6C&FQImGoy3EdxaGX?I5&3;E?rJ+@yJsS(_W z#?PfES|hA4pNF4pOb#S40WUI&BMHu_EDm8~Zw-B0Cr`XC`=z9r6opTl-f^%U_OM-j z7og(p-?!IR5XeLg(|(pRF#BZjhzE|6K4Z~dGyka+ec2{BqDW5uy<&oPub#epK5!3< zP=-eTEf_V{Ux4fp&sf3La*Ig*-Dy3StX!kAK@;}W8B0SO=G6~GG3M%R=43&nvcq)gZ=D3Z2!ZHXK9|V% zp~c&cXIufEwj2$yPxT#pMlPnDh4}WN50*Q5HUV$rbO_w9_jgQxRTu-z)x?*DX5%Hg zPo2-_Hrk%FfaaZ7!9sovkZP}xCGuJ3vnZAv#@byXq3{2{r5rUa~*Gl zvwN3q2U!lCEIp7N#C}>{pf;h(vy@zzuna{j2%^bg)Lm&I7z$zT$J?aD(IxUZWcS1g z%#ZCyM6_WfP~Q4b?8^$e2z7r`l{*Q>efMziuBj zjp|cxJfXcSspLxEHnDY86VK2qL}Q- z5%D>NNk&OCg03~o$HOr?Ox}=CX8(lYu5cjj>Q`u~Jlom_;1-dL7e+;sZ~U|Ix9CVA zX`Y`&GInM`3Gn25^sL#+owGQGklgi!0zv;Ane@Z+&-uloR?m>*DsEqt69MofDsvWC zr4(ZSjC(PEGa#(RQ+kHQ^n20BWzu%5kZScjI4@}=neLlwDdseuce}fqkgfbDr z$#?BP@}eyaYSEz)sp|g~J1DvjPNkM-wcTn{%?3G1V^G zbUuiW78RpBpk2C2MYRaC{O%Q^;5?Hpm_so|o;K7e(+rlzWN2P@g}ZBo3Fxdr!~-81 zRKt14TBCaMp5oD?(JM?Z%ZYAhLK_nnL^&#GHh#MNP{?Oin&;k-?FkB{Angphab)ga zXLV(b2+*OKAOt0vF*DcyV8w(=I<2R3=1?_*f6q64__rV_MhfuVW-6pIv9Pzlczuh> zYbipR2bsg$lSG^u60dpTbG^@(QT5=*<7q5^PezKUqKNP;)>}%N(uTZF`mrQ%3X=AO zoUUbnUm zVEtk}TUh&_wClZb(V5S)4fXPt)lXV=7^;B+TtTvOqP{`R3awBz5!s&ptr77ooh4pj zbp2=-3~Om`5ySWIvf57;9l-~`;w)-!qkqJp{^mgKT>}X`W^b`Ms^p}C^>7?>p;1_Y z2}M1sb96~PY)gOGtKc5zeiHJTN|w#4Z-R+lsfxQ-^#V+~QGF91!4tsoJ{?bHTasKv z^^3{(OBkNxMAxktFA?eu?UVH^q)&_x;(BgY@XWE%umMQIzeWajU6Uqq_%y?O-Xlj+ zW5Y1yN_Yq(+5Nw{<_-uCib_$^KsBV^u5fm(nG&#&Zob46r0uj!D#-dv6#Yqw@m>Bm z#XIN{e3TXd!3&i2{P(tuf>5x9yyhx1B#Jm+UBK*+u%Z@V1ewx>rpv!be_bH$br?oA z*Cel(twx>DxSCfT7<)l3ZC#kOpq;M%ig?S7)$6G=bU$FHV_`3T)FI+q(hyO*9QsH2 zA@*BvLjyf5SIL`I({4KVBO#yLfs4&yqgqK~^!)_!C=dHdbk=qSG{AnGwLanQJ+KT6 zV#gVu{aD`;QD~GsJqU|YFCzP&$}P=!J)y{b5W~Fu8CNM&5|sicl_6{4%N;gMl5C#} z^T+j#*)k>Ebsw=A$M|dsiOXV@@|c!FX_0nl?AI>kFH%!`Q2&Q^W{UFfTTizKV=@!{ znN+OlBD8K{;&ZE@ylf5Ds7wKrwkQcM)YC7_bQRkB<+q{xD6$%Eg21qPhpkUrgveDb z*Jg6wAjg)MCygF#pI4VZEBKuP0zz9(Ii!x>OCp^xaV4aI+-nn@=++-5+<%TqA}$Es zUDD1_34emod6I@Wu(de6#S(g`rcwxp^FrWGZf6X|5lQuv21O=gckvni&g z8biVx30Ed_Yh5g_ZF9iEnxaHQx?mYtXbC7(+>v-N2kd?zZ*tiTc~+-gDdv%tw9b3o z9>B+v&Rl5|<}D-GYt=fn7*7T+K5|+@B)8t6JcngAyMKQ^DC%vAj#Cnzz4{^jyK5yR z>??qT6za@2wEko3%a3bF9p+6-T8}Ao#eTP6hSg%y*5ptA;eY@}tms|xK{Y)BnSIKZ z-x~FdR(KnWJ24Vb5QmL^4__z2vOkIqhdzxhz-A}YaR)o!>mIqdQLS~ z1jZVIVY3b+0rdhRrXa%wy&(e`h2HeakT)?MeD8y?I0;1(p3Vsz2BErT( zCQ#7{R#c|XRiK}ee5o-E6`>!6B0|8m?*j9TfeVaoD10Pj#?%&sZjJ6u2}kpWcT*S3 zOd>*Nh%bdxL}{ZZEk(^h-bcI@v-Py$4ps400a^Y4{b1iPPv&2;JkQxBH~H9i>Wm(%)GK_?+wL zVlB-Vl;$}zBT5PZV2B#c4QusN2f*NPq+VI4(S4(z&*XHe<4ly_@lZ=yPi`HbZ4`T> ztveKjOX<~Gqw@$ucl_ZbKnhQ6q+ro?Z-gM05C*3G%-$JqW(Vv2QS!1V!0Bj%(kN zkiBGws#rxckJlWm4zL{MSbXKkCpLY2wLb;2gfnMeu!zG!(V0H>HzEY#;BneU>m^qX zO1T{6xSN;D9(L1EY${pQ{A3g@wYIf|h5WJ2duzqmMCqgRBp+H9{@LmEa2i}jts`X7 zsRmc*q6z(39w@yJ>M8SuMC$_>8G3nY`(nlCy*p&~HHy_y2+=Mf$z(`(LRSRC31bf= zoSrP3=m=)ED`03flhcHVvK&Q# z!M{rd>XL4o|0q1xipP)@Q=w^CtX9)3Cc{p&kY=Uu74Q+imsl$MiH{n+z((BnLW4tS z_PW%dB4(+sX=X6?el7K7N%o}5?e?nlF(b!hmvhnKDJjOg23A9aEr}{QX0#_S)!dAU zD?yyVqw)=nE|OyJ^PPKyvgB8=Eg7foNFzodG0(tSQt5T2mwK`{;- zRs^m&Xveu8zc;~g?f%r~#h39!l2^zjbxc5*pJn$5TLF1lsppqT9<>%kKPLwszoi?q zr94=B@%=#L-2F0X4G{4VM|d-#M?qR@1167L@y~f6114S;m4R+ zjBpvZOF5YL{}tePDjU+LWxjomKkv&9~5AN&06~_k(-1z&BQ&AiAuQ6o1p%N_lzCA*qZX%$Y621t{lR8da z^xg~(n2DE8O77O<=l%Kvu>_Nm`jf`0TDJ?StB4#(&HYvJGYvJ3|2)k*HV98)fIpOc zKGgUH9-FbpK#=_5bpBC>fi})nLg?%2x@WptrYm|19-;rwF``6QX+neL=6H5J5Gy*u z#d@S#4xxBH_S_~gcWjn8x}ydk3p>|O`{TX9zo$y=4X0FJ<$0_!(WV>_l?9)w{bmop zK}@3e?6Yz<&9hYGj4j?$$?VS|Jcr`UAwNt3V=9MIBa=iuGM>Q(DuN>z>VLwU^rJ`O z0pvU$?du@spw<-%B*@dKSz1mF5=LQs&M81=P`5vxVOeBc$ z(+14KD5Y&5+A4Pi*5>G%G^3YrDSbyZ3^@PdO5o>nTjw@OU2gY`dXMGx_kX^k>N+{D z-80rlFfDZvW($wVYb5r2&g02xhK(&S-ozXNB74G~iO4T@_xC%$wr+1Ll{Yx}f1fn< z*7}SwaR>A!Fwgk`%;i(rzb10OqL@nySw-6n#RU@u+@s}RPD%ird0T45AM{U;?MjSd z&-4l`2+-8QYe-anY(6NDd;S$inH=D~#uGeTlxJOmYAi%ocOg6pfi{1Idh;%tZlW7^ zcm0`dm`n(@r)s+HcrfhG#TQ#Ag9CCzBU~q56si2~Cb;?#+;JkiASQ13Kv^?(FkJRr z0Mv{=(7tj+w*X7x;#z@1cMjp4p5oRDC3XF^-`g`s6ltpiBR0kE_V-VC!*W4bJY>HQ zvrm59aIx4(*d>%;L|)fZQ~NzwMEU3r$Z&FMGK`#A!MMX0RO&QlheLaq-+G!oSZGIX zX$E2KjmA_Yg&?~vV5*jVN|f&t?tAYHOx7mIE+C_BcHGbr7lY50d~z+rmSzDfAI85z zx=u+-x~XN@j6@|mtON8*l0mhKpzyZ4V1AyUf(M> z63X+n48}{V(`nGhP2>wy4`vR?TFmS7MxYr{g!=_oMv7C+9q4_i+T8eYKR3xQ1HTiR!+XMXR7b4U8spy~ zTMi98k10LW(Z7CGWpyy8haxn~XVeLX47Y|;Wx*?I|X8AKp9_8YEj z=la`EZs>OUw3I1Ab#Eifwj+ac0f58rWJheCY_I}KVn!JMbd7VH0hT%ssuAROeYd2^ zIk`Cp_Lc+QerqN{n1dqm`By;}C)x;+y_>2i7X#C`=2Mw=b+)Ao4OZT@`~me3PP}U~ zFZ5@8=o%ShJ;$wzIlQkhxQhaTn=iyR7<6p1gFJ2+&z2op4VJYPV#2~RWCQVEZoW^QgrkR9RL8f?+!4CAiXr)4dRY?XM-wU?Ft{vc_N{j~n zzU5Jbm|Ivu5?lM;hlj&N6Y*`H7p!U-rt+kNqrYKCaqO%sO*Un3*>o#SvQd7(3QoR> zeGs;&`YY!v0}1{L$U4ij@9&KTZZjuyDU(W9h)w)~PU7vu!u{2T$!SCMU97if4p8>p zF+cr8{tJo&SMq)(5^k+%#Oi2rBmR#YE{9;1DQvyUNrf5yq-Z@hrQx9rfuPH<_<4fX z^)o6Oa$DhKs}I!M&=+aGM!($cyZ=U&RQw!1X`-)*~TvyWYEjLjc`{s_+3igg^8+QY3t`tLJLs)eOIi^IIsu{OEUd#6GkZcwOkP4<{nH8Gt#kDm2Z5PK7^3+7IE zbmDy)T8i))EnMqI&3w-<`aRPdMoJ<4EniZ^cubKz+xPD`5%eDuZudNn)zc@2kc;0k z$@%nGz&yoZGZu~=h@$+zm4dtXY;d=1bapNELJFbU#zc*3-7EkDLy>2w@3qay9PU!X6hJwlq zekXa6B9~>LmNc!F6C&m?x0c|KN}%>*yAH~vVZe18lnWcM6#pFbJ`WXzX=Gz{p;~%H z7q;}w!_8iVD@hsH1U&G-`tBK8*Z7y=aB-+wud;t{XTqh-a5SI?!CJc& zmbtg|;Pu~b^9Q41{;{O>g zzydOv)H6)}$A5$Ug$aQGh1(JSxu*U6Fd#FOfTT3cz)(uV z5Yi3z4Bz{A-~Yh#;(4CS7hIe(XP>?IS$oB2t+m&Ve66WOL_kG=g@r|=qAah2g>|P8 z3+rwy-aYg$eea$uqW|4-*HLn{5L9n5cEu>a@jox(47|L6K{ET-W{_eafGSh83u@^X4UceZlwzcKv@X^St^ zZ?p!aQH#787ih_xXTWDkSZ0a;NMeG={D(#MTuqt&=thTLDukJj`Hqf?8CBt9lXd*5 zVoFg*%cc3rBH;E-c4CcDmBCBHvDCGlt9#3-UTZ^A2RX14Z`;*h>MHI_tCb1#Sa*MW&>!oA2B_9-s;6;JW4du8;4%L-q2@lk|@^ zHaC;hS(!FGN<50k&yiQ-kfrA(uQ^{96&2|e%arMrUzA9)tXOk!aB(F+4$Q;3m6JXC z->LMT*3d$xIR+=E?elTYsw$_k`FU0v8dk-eq@7=%@RJ}ZVBk}-XJk1^G@({6Q#jM3 zq6o^$%M&&?9RjP8`lD1;Hf)PNpQJYEOJ;vaC3%m9lO*3xCeZ0JiY>^+8Wy0{!UL&|Du(L&A(yff5|8x{E4(e?EW z4upLsN=P7L`YR6;8MBj=r}fwz4>K7P3SP%6hDzb$7&k~Fh`rL&^Jq+}EYbt&^Gpg) z>Qu;Bc2D$uceO04cCIAlLTxh1VPTWJe=mdM6-951>V_^iIL&YZ8XS|iIh8duc*ZJu z3dvE$x&oW)BWZ~QiJNf^4yr}vV4bi+cr0mV2F{YYNb5wI@65~$O98fR2$8jTxSpBW zPFxw-KqnTGM0F%-X=R#2F#ZvLXVOH68N;F^e?PRB66i!msYOI77Bqy5xXjNoDaLBV zR%cZg6-`1uam&8#_~@4@XkD(JDXvdNMWv_bFqe^l9~?Yi#R4g zK!{!_1Y|v4tWgcopD*kv%%7+PdTVGPX-atnFKW|_Uj??H#tIxL25S91f$)tSym{x6Y?dJH&$atKjyQ((U@S+CC;?_aEnz<%Sj?fGMieH z7mSF-7}fvL{tqFcbzhQ#UoH=C=?_Rsw^8F9r4-^t=9e{)T?YG7PwaSmgd1X$s{ic8 zJv3>taKg3TOyKmH$WV(oCumTM^rck+^b;B!)XKDx^%YJXA0yp*zee;1U2N}~yzH!j zbFl~s3-{FxOEbCW1@%XM^&Fps$0b5tJvCD{AuMoy`+W5YcoMF>Q92#|ZU2gS?WK50 z3WHVVZJO{bN~dW?z)=r$gIjRt_XpxsrZ~y|*aLavK+-n0Tp#sZgSV<6LWeCQCyp|9 z4N4iaO%z*iVV`b2M6YH^Q@=hX-vh_`6?&1@BV@ZQm07AKoUfpsV9g$H^fDF7AO**2 zv&(c#`uf=N7@ynxTJNb(44lDPKO7(S0EV8-UwChi0k&CZy^47JwAO@;4?kYZ*z0^C zr1lPuuf$KecPor{Nj_DE>E&0jquJQ*{SR?tm>FzS#5rX!tKs{J&9JnrrudnjsIMp1 z4C6(qF>LSp_@u4h9hqua^~gsgTu7T~msP_#p-i{FNXTAC>$kj>;%oj}Epf<5qC(i$ z)r(Ci;yhKE=z`QJ33-qu>XHaQHpqOwMk#jueQ7R9riJ_IPZ@`vm_$U|bpXQYv{ zN7eS^(ksyg#!$37J+AWnQ=~(-|5t|Y9m9IV=BrG=HUkS8)g^zmb2d_(Q|^-){|~%d z+n(J1i7<&L4ytjNTm&UaA6;CRS6pl$cAjC`@~FV{H(Oo+nD2&Yg}~k+HB2oo|Y5g<|1EMYez9UAIxg7T&Z% zIEnNqr{4t^g%oRKF|cL(fUEnrcX8-Ytr7>n5WT$P-ib~#o*Yb0f5a|?CFcx0{!SlT zKq<=zap)5qv6)zvy~Z1=7#(rLtB{=FJV7U?Y`0DOYRDgn)y+*R-$9VsT0W}5#l>ST z4vXNft|r55ll;EL7jf zk>FOM74C^#_sXf5C?srfb`c42a)Tkds|6_^U}&g_qbQ$H+3Hj^s2OA8@pZy$Ztv(@ zXZ*;>MnC1(brFc_$?MP0*X$Z`!zX_Rsb@QykyRB=HIaT`zzU&!MEOXrf4!M$X1G-U zE|z0-#Car?aB(_ zg!3rF9h6bk)#R~oB=6Y^KQ{J5jy0g|(-p8}?U99mjN;kQVF)osgfMi?(u{ z%z3xX2H#vIX7>=FFRz&O4R)YA+WU=XU{n`!Db&EhuiyB2$B@7JGIIv1BL%wM42sJrX9`%aAx z;_Y!9FvN$^ef0cb%qKQD=R|5p%va7pii9*?l_>fBm9cvoxKf;zO$qcERd8D(65SlC z@nlkCDiH$YyzO>|HHky)alTI0yb6nSEqtbYNq}CfH#qnY)DqNv1gB#>yb3+{?M?>$ zz|KFBV3vwzOwAHtQryS(; zn(;B6P2q&ID0_K<7^VlIuM{|n2$SRJ%w-%4Lb26joLi0z$O>w_qDKJ>oTBqSua;p0 z_tx@C=i)hlSFq;=RRu#a9)%K2(OZN=eHeriQuvtic(I(^&uP^5X^Xw#=JA3))mM?n zeO>G1L~p*U{4A9};e`GUy-CpuK34=@3azZ-5UoC2fhuplgA#*04x}q|6@Qjia4oU+ zAfuiLIzFX~Ia&@u6EJ}ryORQK*Fh|vMRmwn12IO4DiM^`#I6HYr(xT%@J7E^-!GtT ze|ABvl=`8Xbq@R-!Q3}~(P zgqhz_4oqDi86L}`U*upqBKPBYn9o6D+eJ>mQH3ZqI)}zBL zZ-aPH|>Yl1DOi#iC-rv-qx-Qq_s4TGm|k`y{Jc^dlW-zABBoP7vJ(vDf&^WOQB z9dC)5By(o_H{3q2PdpqPsXA9rhkP`uN#mrBjm|a)Mf*IFoF>gawJ4E`zY)^w(Cv=i zQ2gn4$&*@KC30j2T31gC*&m)XKO@Enx;;*~l%$J=o`FfR=;qj??ICg1#%g|Tq-|cH z)k=Trei^k(iUJ`86i*^6SSTYeacW}{x?Ue%?Fpa5wz_1uH-EfIcn2@$VaQuTolF94 z&dIA}%@QTk6zA3rF7VCu#lT`%w8s?`v{F`*OA8@75X_8`Ff5-ndC#%kT8H3wsvmV_ zw8gCG>jyaDe99}<&{aT~-a3_6L$5>0b&M4_ZEVn7Tl=#V0JclsFN`qb(i+aGc$xUL zZ~O`Oz~eurY`W&t_voFq+ z6ZhFYWwtP1$p2;d(T81mg~#^Lp4P8H}Ba zp$vJ8pJe=&=ibbx8Zh{!;bdRG*IWgAlu(bnz0%3tOByxsHd}7+^%=Y7r0!}O4?t)F zriL=CmO&Cs(D`lo{r!tJ1y~`EFpCfFt>bLb27K}~HZF6VuXmLv`o-folr3gqsd}*u zt7*R69{|4wsA(4yQNz6=;%5k+K)uYuhE)EqZlpPOvT0)Za2W1HJyW&tZ3tH+Mi0*? z2%?eFdbz^aN65#Wa^o|ewJ$?k+g2svb~FIe`@{(({ASW9(%9Fa9z!G0^#p_ezfhnT zEott{wjxj$$OI9#w4Q{M+O*Cf%A6gm zVst~Q#@Ho7pZbyP-7c6iUQ7o4G|>zc(j803M!Jat%C?uM@+nw&=!KckR8}~TcM}P| z?Z9!`i%o{^Spr8C_0|eaZ_)fZE5Ajk)z4|>X=tsbRmUkla!^>&%;<0S)KHD9O6*Ky zUvra|#7vd7Rle2ehx)k1MY*kySU8L7Z{%^yV5xc>M@LIm7FY5c6HbG9fi`B`(4ESP zok+I4gdvZJ39V9Z3n4RrawU*rZGd_PZFOHfqSTfHM`f`a}nfF3{uc|Gev4iI63b*w_&7{#nVvE zWy5DuFYDj{vg&>?bL812{4D|hL(p)S-9{1%GRa?&RXzJlXrW9ptGKqlFO7$o6?*Py z4*&D$lYD|H0_YnyKg*_SSUhkwH5C&%$_rCRAQGoH1AO!;5DtJ^i>6DIc|7R5UlnLn z!#RT+uHVBv^=E9w2Sd6)c=Bit360c&@P0>*e9155vhlV?^f#B}jV;s(2A+EW=NQ1c z#8L0V3i$xX`I1L9Z1uu=JjrZYX)4LAATE1J5S%EAKJ!*`xkHrof5?|98aihPmHlvH zst=mGmnn0?VejISp84E%)5>HHY{AAn<1u`!?{}Qp)trDlefr3G2M-x+<- z%SZn$884=sKJ)j~lt#f0C(IZTHPdWg7xk(lZb3Z}=$%(K@X#}UAx1B{tb&UvC&#{# zcP?6uq7Nwynk}DWRfLOwel0W4a}TsuU|pYFEvt#v=(2C_hiH=(zK0Tx);Bh8Fjhj> zM$(+X`X{edLEa`Un#DN{0}paiOm=oWFFW#+3fVW1Xu zOc>M4R2-gc!V?F~mudnOVkmHF|B#^_wDuvkZ@M^rg4+6%{@+K-d#y6Z*@ z35#xk>SjFQX$Jz2ILB%1ow7Ut&BCeWuU$DO`Q1prR$hB30|2lnt*j{H61(?JdWHS^ zUFF#slkf@ELCTrIhIg%hDTVV@|LZ>+x|Uuufm-(RBeWMmaXq4&W! z&5sJPV(}wbM9B-~@a>6IzscfV^jYa8=zZGi>H4`3bu9)zW>dB1R9)fJ6uR^;n|3`#zl44XH^LZId+Z>QP3Rb5 zbE`c-Ze{DbejHO_ni?jTO?1N3Wdtwdl?5xOlIypjE zg7r@@{t6cMTXeg(c8L_Xoij4Mr&kD13;l202k3SWc=pkOBXs9h=*jku==|pye`I)1 zANEzj?B7TITd9BShY{F+GWAbUptbHlmHy98{WHG*Z$}hm{FyDo03Ajai6D7Z#p&5f zt2*F$L5b&0%>ITL4F)}Bkg`+B2{LeUae2$I6j@8WiZ4Wa0Ga9$*M*OYGJ*y8>Ci5D zAofoNWh5o1n!d8H0tCbmE8V^GJS{D?jD}-l%P!di3RJMSHSV}Yab5AU+A$2Ns%0tZ zhTD1q1g7RXI-F(us@weB1od)|TW^F!EQIkp$fighz`{~=tyiQ6d!RNDRj~K;jI0+Q zX` z#vSg+!hpW02-!6>vK#p^{wffl0_soAGS=jqpPiU*^b=upu#+CUtwVrSy@P#KrRmJ!0 z+du&p1`evQzOE0QXx+3nzL{BwT%Ssc6;Md5q<(i*6U;L{AM{mEGmrKs2RkVSQ(EAi zlH&7s^h$_Srh8cG7XElB5;xK0RN>)_lD{>wE6viX5h zUBd@1<_#9VC!dAm<#yf<`$ZH(9F=lYk(-0J)8}2uS356eQiqfqxJWn1$y?@rm}3S_ zQlqa8wc_O)9lQWTEk0BmAY3>(-ey#pzX-t09UWn3Hc5Ea^EmB*_6I65|Mm+qvEpxD zDfWc?H|Zp_Taj3(V*KBo6}AGGG7D7PTgA9*vhD{PkfYh^i&Sd(7S%&coCF=bezZ{Q zP5Uc~L)W9N0LYxB8C|s{wI~BL?z0gu^6P$3W@k6$OSbY0(_Pd@+0lE#Xny0><@+uR zpLLC}`kbX7e=n+ta91NbjGcOhUUvb_km|WlTCw6VZWmUF8BUZz0(YT5I?Auw6s52< z>wCAlcO<%({+i#FtExQ7Rf(2?)ci!;E13r8IT+1`BtF8}EQ7IGN(~!-9c-U%4C_DZ z=PEC1219R>gyWrz(b)u?nuVtjzx=ocaux3eq6}N+?XTPz4sc~7xgk&T%JH;*e)mG| z0-<_7T^#WHZp5{HdMTFRPy2N&vBzjGeHOxq=fIxGOJuq}#u5~u0?hRA=Esd%j#m42 z7oF+46T(QsO!Q^^Mtla~?J=>-b;V%uE)(Vd(&>0gU+ggPg_k2=Wf~Bpr6+0AdTb^N zf2yV3Fm7fq!)v|JCldqsokLqqgb!|G5?DC*G`_HRC)@)91aG~?_l-Ct%r3wWZ*Q4I z0ZUFbt*wj)!+|bnmYs**yJa%wXspl_?qFR2h8pvaWfPh`iNZ0$rc93ZJdLl58!)do zv%IJGMNvKY@E-%<>3}aaoG>!)qlVtP1Q({nWP$08(R^femW9L&|2GXQbh!$|{{W+7 zz-a%rZ(kIG@rr-*+`;JsAl^`7mf8yKa3`&}INZiK0N8j*jnzN+F*E67d{56ZurNW6 ze*NEv{9BQKto2Vu{%Ng$>i3@=`DYOSL6rX|uoj04PvH~3xJD6UIvgAv<9UYMeKvJtsoRe?HpdJHYT8hbRMhZv z#w0ZZ(Ro~L#t|O*qM{X@G|uEPuwElc=E#exKC)+nsB1&Hek*7&bgWz}QyV-PKTfY4 zbb5yi4FMUMUYi*3mVpV#m|6zYj4Id--|utH@aVAUa26GD^c6$9Of)h*O0GSa6*EW* zFHxaw7~J_FQ`SILKcK&#izT)E-JJWY4L>edY*rnUYU{Eu*5VS(RWimPMKafy2~kr` zdgwe5r3N;qB9fDnuO>-wI~OIaETs3mUSHoN9~ej`zN%-!x)Il~Q(OEsZSBkzFKO*z zha{TFv^f2GF`S$g`Y+Dgs!T*@yH~ft#jeqgPM|~&Rvt1Wc6rTHE?7)e`raf2mkTr( zShl}IZfFarh|)1RAez86`bN3Cu$7m6Qfx_&0p4_Y9*yY!K&D*92?_v{tmUuP!vh6; zJSh|+m1!EBsysB?Nog^9c7%57)~V{W5@t49xeRPW**ra44K;cdSVx*H$bC+Vvd0^G z0`A(rEzi5IYcRRkpK}~f{VBB37`)vaTf|0>l}sPP8^yaYRIk`KchJ$cl72v&c7`(O zU0qeQ-yM?;jj2tNxSh5<5X<5$i)Jk0PTHtF_KJhlWOQW6= z<)n@;$GPr2k8%F^Utp7^LaHTrWKL|H$N~@kz*aJ&QycUsN+fWuOo zU8NIMzeqIs)tk3go6GEBv*%srD``qMi>oY02M5K9;+kJR zlOE;-r@+JXo>SQ)zJd_OLS{EA3QDm1bik(9J7#}(rKfeoWgR+#_3I)c2`@ryJI}y8MZ@Ih( z95B!w1W;mCJesszJ|CZFEZOo+P#@8M+VAn8Ka_qbFR(t~EaWEHKV5RXhP~)8+ug9O z6uUw57=d5kRHnZ6ayp#MRSQpNR_fE$trxxiP^4F8Be^2&yGo7l*`Mjpqk{rt!SnvJ z#`~)u;?u=}7VOH$7gjkBF3O(IbE=6{rKUGzmLAb)jDBUF9z${;Ab59~lr`xkmKK~4 zjqp1xX1BT4FT{7KX~7A*m-SXlR2&230NIH3offhPF z17X+Q$*Qv%<4Lig51oEyp5sxu3rm-3t`m$ywC!GK-4Tv+PB2`Ywtc0EzuE86=gg@* z-ZOauFeEMNEFRvJSYThn%i>23B_HHoK$Lfu6?9?)cK?cEn4}c%kc>$Jy>~Fb&92cg z(z(I5g3oAS4y#O!Y4@Hc%N2LI$y9O3!2T|J3xZ7En@4D$TKJAA<3vx#Uc<-NGNH+1 zrILh-Mzx-Sc}s2!K0aq1JCGf>gFXA#YNwX3GT0a{EG5g$;P6!>?*Q5!S#`pPQc5)swT6AY{69R*VxShT_E=5>u^!NEJ0NH8li& z-zpt1`r0rwNU=Hm62xKr9T?HzgFNba*)ivTZ3`!*yr7Vcf3Cah`LoVe&N9-Hd5% zNLmM}|1G?Nl)N`#6h2C(l*6!QpIPKv#SSX-B(%JQp?;0yCDkA#ywD3`M!G)cS~!Hw zFbkWQ94OIjlN~}{ylWins;EyruPWdXEsSDIr}2fFx_6*F`xs_8rP%Bn&5H}>Ja1N- zsZr)?pQ%NO4ZAFfu$FEud+K{!4n4g|Wn;6hoIzcadV5H~Ql9q!5Na)7=R9T0F#o~6 zC*%EekV>*A_vSv+@Vihe)bEuN4_larNBt8G@0>|ux&9NuU`(J4PpiVC|mT?+9|Iel>Pr6y46a0)^K9Eck=pu-Lf=+vTI62?9TZ zT?7fLgmK6i8v>R5>qNd(>el!?4%L%vEo4PA%n<#T@+8iR1h+ZG{1MLIvCxIl@+#_; zy>#;a&H?j;HId2`B8{1%rWOrB@q5-;`?1=OJ{yS2J)yxnEJe;#Gu6{UAuaDcH z(zxELnKPWik<_Hr#QQ42&Hk?hMiqP|8G&o6!nSqDCXqUvl0plG9ra~J+6|46-t%Io zv8nmZ2Hu*HrEYzM@+*y;7zE;cv-WDp5{wm5?=>zaz}fMG9LJuWg02T+#CqAj@UT#; zsiqokfUfJO=QGEVg8g>hSyj=tG~OIJoy*WYKU>sMy28=)ckSA!M18L}4lVEg-bYX5 z4&I@fEPz|2A$i*&v2uqBU|_bfN=4T1f^8-9L51AJc{Jc@j!VkmCe9S2<5S#cB!%yB zYA{S6t|nW6g6av0kBdmBz|)clk~JN8gc=SVO2F@NNg1+iCyMg*xVij@^;~f5>BR#u zal|)xIAC&tl7kd=WkLJcit+X^o$YQe9uAbV@QTJeh^|rTcR>2kN9CWt;3aiUn$8u` zjLu{$iwggTYKe@$aTLQ;yrYHRa@4W}dCcqlVEKS8fZTH+waB^0BL4|{8Ba$x)DUB* zv`OAETE3DIzt**g{^GS8I}upElc`LO{GGI_;r22iX)ACOU6&EV8$vUfYiPxG%Hu-= zbPtdK{CV%YP}xO`>4JnElO7m_We{KjpP%ld@uzvJXplQdI5jGyQoI(UFqpxMPO;EcJHZ zc^7Z@g$I#*g9X8Hl=V^sSyj%FlPo~s(-S0*&K#2eNpu9gvN73H(wvl(jd=XTE2HGz zzTr57vDYzwC~rson=V^(FMR&E%5zy>*<2%Ki!E8D!l+EP%qVP|mXJYt?xh|kgre7! zQch`{o(&1T-9t;CAd4+U^e9mflwHbSq=U53(N-;ym;+J1rK+bFW8kbAG0<*`BKhHq z-+Wjqh#|op{O|WvRg4>7*mtrdZ=`tGMvxZYA)ej)Q$djA*;&NNsei;dM>+b2fQ1;{ z99_qg^#YLK1!ccPFiI(Gca(y(7OTfXdT-NWvF@A^RzXCehH_Z4M z4H?}}RDDAW8T`e~^r}Za{>!eumwkexZfWWm8ZNe6Y!n|9f~L zZ1LhklVq7NcBO=RPd&5gmd%clp?2-t$zg@aJ^Dr#r0(vh-%NjmmqfVdMR$*G53`dR z0|4UQgK+HSy%W4X2BNYH^c3<|brT{D@w!4}ZN;lV_GAy)Vn1BHs;!+^^(j2hX%R?a z6uXCApx6%GQul|71CFwhv53G`o!IiFd|F|?clnQ4My97ZQ0UU=?bz5PxB^XbijK9l zj;L3qdZv)dhr~}Y8cDm+Mb*Vp8=9!f@wZGBDN@$P0Wa{eU6~TJ@0hf@6*(fo3WxzP zS$J})W9!9nF{`~3C|SyD0oc}4;}OgpjRT40x-(8YP7mNPwdRPX{5hFC#go zvSDJ31g6W<8rW9A1o+9B@wyUay+0^N#8oa-e=I3~aZZwz+75S`@lf~^Gjp%#@21hu z*uG6iE^g|(mU5x8&+t7!2|M++YA;59#TO9PPUB6*W=v>ck(=VXvb>?PGSM@!NJ=5~ zqCgP5^%lgRfKM=nV~*7G zrG|J-os}|>_9NvIi9+V&onL8b0vrvcl364D8T>2)4Du?6etVNmT*+yVfetJK=>65XrB4ztNi*_4c);zl|^UNe-KWg zX4jHt=B6Qi#$|K2sgG*^~HW6ZsDEG*2UQ_Sfhoy10%(?jU@&`7Y$92*PM z*>5xw?IV_XgNu>vE;QbI9E^{Je*67r3x<&be z5TnO$79x$HJk6ss4bI6jvsX&K*G+5I18F>hHd~Zpr(ZrShZ6E8L=oJabe{LfTr&(y z9#lT_83}Paw=W61-~I?azzGf`MPa&`pUU-7O|Xj7Hl^6ngf7ASCHHXK&6SHvQ9m$O zz_R=BP9xA8w`I5WqR@Hvh1f_&lrEL1p^^bG+5qZ~_ zwrAaOJ+b{>ri*2PALmoS!KP-xJ56r=eQYOZ0pDCMcf8w@XI$EV^MAWV zAF3qmw64*fEzn)oWV`Q()jlfp-@S^s+)r;yDbr;gC~^KS=QV9jVejmaG>FuRdWGr@ zm?_MM+ArO+*MX^^QMl{lPo@dQS_=vu#InYx9fbRb<60C?3YF-5v~SL~gB zJb7GrpElsNnMv)-V5Q~B zxEi93*){)G=59@Qx1urhP+?H!f3&D~r&LUb@7iH4ENF;K3uZJ%EJUr6v zmw?jAndC2O3UiOdS=S4#<$kW3E%&LblLavCNHjF7oG<8wO-ZqahSnmdb$B}-e?lPTonm&zN^`Ltcfx?qYknQp${T(fJ+Wp*-?_!9Ox2e9S% zgkJtRX-j9jIVu!#NFSKW7RV11r>%2a;S?3I((*g-i0ST*-rU^uE4Q%}ahAPkziZWY zb&T?rIW4NI(^a3^Iz^u}VsB@cu)p7-6$u9lJ0a_(MHkU$ouJ?~>1%nxn%!|#WYO}3 zx!v2sQ>%gLW#jcIkm~S42+^?vqfM3E%o#uBQvc=!$e@P2&NYfwG=seFcsmxX4J)a9 zXaiCOSU0rPOjCYR+3~%&Xqn07wYPUpN*a5tl3HZ?G;N_De=|M!GMtDyn^?#1TBU#N zV@2DO?9P6I{yOq_Tun;QL62=@yiWr1IGrEWkSK;UaiB_Ol zB2I2SluG{J>+VO*T6r1KE+M8z|5O9~hm)qsOdEd)*hC0SH8^F#TDM?h3&O^|jdEhg z$g3u2iJVUQQ^^-1Cw`FKHNUI4X|bLSqj%0=sPi;f|MQUvWVdytuTiSk{8J#hyr~k< z*svy%99$U~JeFDC{N1Ch`ESR|>HJstci6Z@Ph0P;{q|fVK}%MpiPAIIkEGdTTn7DP zENVb+qt`&tBM1c2kLz-!b+$xz{bg_?-MYzZ&=|)kq0-Ze0wthCH;#(Fo^>DYlL|gh zmONW-JG3w@p>VH*`3_|Wyqe#Oi(alowD7X$uN_^;oQ7Opp9Cj7A9?|-h8>%nw9ZM& z{0$+FinR3fjO8|OeK{XADsf22U;*^VySZGm9+o=s@O8Nub=uFmxv0wa7*p9#Bn~bu zF}+wpw4KFXF0(a)0*TY9Ju6pLh*nQS;zXwh^VM7)>*||U`>aWlf0#vFALQMnpjGM`Rpd!Fflfi;VX>2?UrDZ8lxZQ48cb!mNkAydS`xbvj=PZ~R)f>&9 zHxTBWB8g6;Zw_v{0y48gsbdM@L7(^`?ECj?#NiL7 zZIh9vIXPkn?*k{9^V)el^L6q z_u*^gOiEw!ipI?ztM3e80l8KjFcTkCC~}#X?T9#6lHTg?yIQlAxoNt*Tx)xGYb2GOyEX&1(g&H+N4sJf=8CbHQ)X}lr*sP- z(%dh8blY-IldkG&Nv-7OFTA@x+wZc}>OCOLO}EYSEmlg{QW@YifsMq;T%Ve~X)OUt zwn0(6(>?tNYC|WdD((@!mM}bk&}`Rvq@bK`>g`GUUE=jvXFo0D<3n_n>sIp~iXr=d7U1rM%wswteeBzj@aCbZ*~%#y0_Av9^?L zrZVWCzj?jxGvEy)7)8_8c+fCED5KERboV>2d8?`OO`n#BSRWpI! zqPI^gCkAhpo7Cc93Vaxy-SHKUbYc ze|3&%OQd#gmP1Xkx@T*lmY}DP6B|yzxLt7`QVuMdv)~0+zBC^r#(< zOpiEy_y!oIoU5d_F4rSuJQ$tV!0z`JaADyl0Fznoude{9fJU!_x`4tQb=fx4M7EYa zs~W#ye=x*y6TA!E?{_q~X{ZmJIo{HCj~k*5{8ek*^46BGby;WdxyZ#=;=sb5Vowy@ zmRS5WVe7|v!QfTy$##0)Y>@vkMcbvd*H1M*>!UK!3em=`MV9C~+tyXcG}Lvej_#(5 z&2ObqM*8p%v26v&q0Z3EQ-9djZ)spN+3q;n{M^m!^5gkdSxHXu3b*UZCcT{jA2PT3 zrpg*V5jSY&wqY%`i`y2k!e!nEkIhSv+WN~>7eVV~bUf%1q-MZ03m!=08+KUtKmJIn z^($_CSmyGN;U>kii!i z!A`_U~0Im5Pp;a)hZ3GG+VEtBU=-cQ~_+q6P!tv>WwHaSv=km-uhLTktMK9>b9 zvY9QO#`(_>KeSVkUD=Q}>j_$jY=0>6n>hH=^7y{Y>07Ujv7+h+b(WLm`qSQ~0ks#^ z>r-}cqm|^}#f<)^nPW*9SE0|AQ~@Vs;I@bO`hK1i$(H z>fvC}A}PXF-){D78|KjWqCCUOQ#|zIdiuylCSGRmW6&D$B*@v~SV`vG^|jS7%6Bbq zchi4SrQ|#6xPa~=!>mNB2_y$do1U{7>etK_=eg*C?e2&v7CiTFB|N6NoG5W#KjXD) zbkULynr^~TEDE#Ia_o5)SbzC4>+iwpV0zWLyr@~;2zUPA=f7x_nd{##e){{va%0<- zOySDMVXN5 zjVvp%H~cVUY0}*5sfKG^s@%&L^5dR`47ssgRU#C+7ijY0IK^*<*k#-`Ti9(#Y^^tS*DoDpfA6TaaRvqQxSfdCrp*+KC!A_j$f<>+_h6^CuER z)3#nF6gg5q$W>R_$euJEj;YmCuQN$$&59U!77lR$iswuT&5TA+uR$DuM(Yn|5{SUx zcfqiRhuE-r-!tod&^ENz;Jn%QVEH4d=DO#WZ19w4!eH9>v>J%p*7?J3i_rbu(+w@_ z$cCteV>scgFl~UddX?I=AK^~Znsd|XqW9tHjL8d-hv~2CtGf>+dm+VlCH8BACE{ZR zkT3G)6$|xzsoE~d(`kacH%efVHm}~~0Rgsoo@t;#fG7l_=MIoa8(x`2Tqj)=<_!-_ z#G?>3$*B5^ElwgvFl~(K!si=4*rCpe)Bw$ZglSL0!$||E!b6jI#jjQ?XoMVzx`v%D zs+kh@&DZ5d1KLic5W-Ht^0n;128r1rAP_Kj0Q`0#9|UR}@%3@>z<+YRs`RGwPXx_s zD?5A`L=jbVh7`618Y|I`O6{(HdqwOy_r&W%Lx{5)9Eg~Qe+}GWJA`IzKSxmwP!L^U zYdEfxS6@1FNZ9nN6fuAjv)|iJ2LAC&f0jAbHA`{^Vt_0Z+E;QD^XU` z+Ix_TS@)rxCNbPRfYf%lgJ{mTzBu0%udx%Kz(w*vM$hAx)nCv3BNKB5wxktd-;d$f zr&WN)-Ya71ksch>@rwgUjrJqy8BbAU?2ozL^JVVK@VP0T77N=y0q62O5gVRXmgZj1 zA=IC9dYo;@?eaBTMB6w|*6-5=^b(y9Pl&yHUaaM9`NV4pLbv1~>Ot)<^oJk|9VS}g zaq+D)%j$tS+t#s^HV&ryDGxh|uDqdo_09|>4HoF*+-3z7&RM{L)XXq#qz0s@$oTeT z8&k4=`js@gX4{${o&qE?q>R+*gj7#NrGhvfmiOO zcm~#LRASD;rNc8J$Co`&KX0D<$J2LF#mkAoJ!4 zKVkpUBr$fu*h_FZlHy(CoR_@=n>e$KPe*P+ra5(ZIq0I@IyVgE$Wkxiz5eHz|hqaa1i57 z+IsRE@wtuj_U(Jd?A986jLIUd7I*Miw?q<5*{25`!{ca-efIJdqA#p!;puZ*AIN3% z9@Lz7|Gu=cRQ2C!Qu^_m02XQ7PYgE)Th*p)#{ON==NnkrZO&g?b@JU_wF|jtbF)c9 zEPH(YCRvIOja06QCgViEHhvPcbStUme_LU-EUlG+=YW7XPLhur5ZT~&Ggo<9>JnT7 zKCoUFSLjZiw0CRX=!OkrXg5$=sh+0>;T5@YW=_W4 zSO2VM{_SU)e{Od-YixB+iDl1l|3HQCr-Gu%svQf{i@Ls6+lyUM*znzyR8WUf6kCWk zDKkir&h_+9wLdzTk@xPApvW6%pt1i}ZA5{s=#uesZ-jGmy2tO6r_Pr3 z%sB1N`sCL^<`+=v3?_Yj8sJ5CDm@zK&m0i5 zINR`SBbl$mww=D6CePscby)>rmM-(nMOv;Gp!W*K`1Ao$X=gSxO z>J`CPrguGeCf9$}0j+Gm+Vx0>t+l*OtRS~Ojt`z)lb@!|sLUq6sQx54ct73ya^&XO z_czgL_3PR+hVDGnsGuqYyarNz2oP$wuj}{vGJO9RMiL(NuCRfEX-`^mDfjlKBO3klU*j7OG zroMoED5BM%U_UM?qJH4K+WcDex~#_At}9a`;A%g;b)p0ydUATXpz*0H>)>m!GFgy{0eb9MeDN!@4t?&{ z@q)J7f+37yySwQnU~UB&n-zRO*RXf>gx~m8JDb+$#nm33K-bKn@y}ygLme(NuOcDy zL*o!?Ed)IUb?MDHqP0wpBsi&}8mel8GR>lIp=xVe@UMxxl4AD%^_j)|eDTJ8Y&N(1Fh+6t&oF< zE2b@Rk@>&O*nY?VA)!2zn8ku`HOy4+sEy7d7h8>vizLwa(2wS=`b?8pgoI}AK78}vhQyOL&60l{v~^Xv-_*&B+7)%Jm)PNs@2stsqmH%l5JMCWq2{$&AY|Weh<0btXTrJP0 zWPDup+Bj?;wdy<`L0rQLqhm{V+J`$1;tKI{s!c_fcdhEswkc9gRBzN#Tj`Qh!QZsY|N` zqQc~R`aKsSoMsN9C-7%wlG@gjv{qJ9&yCH0Y`V+NQ^OsAP(3>E;f2@#V(%Qkd_?gP?=F(pU3fubH;h3)p!3N4q;l@I6(a1iInA-*|(nYiWVhr??z-Ik-t3 z{^Uhsbr&!3=HYy`$_Cr6l2fnl{5!q6H?zmf+QvkW$za!u;DvL-o0CqW*5iGI6Yg`B zlimxDYh4YEpOtoNoD3v5+2N=trFBo^{raGb&t;JkY1Y9T>75Lfy9swbymj=sW(ZdY zkz!alQO#5FE2G@erxypjEAB+LXZ}Gz?`rawEW)I_EL-32YWa_8ZSn!HE{65f2AV~N z)Cn7}Or4Jgs5mQX$%jUVF}~X#O|QQ}ddn90xLbx|#MC>LjVmCU@i#79r5xoEeubx;fu!pq!}+D7(c4xxtc-)e1KG_!U9Sw96zd-B!$BI2_8PDzA)0)T#dx*yNn zZ zjkadia$`EJy<$Fie>m6900uWnrv}3Ewtg)N@}Gq?iwBcGs(<%awO@a0`WaSfX>yZp zwb;?Xz;G;2J2ArVw34AGP0&WMjK}sch>>VMRzaLM_6{`4xzh)GKWu=0U;1b_vxWK8 z4O8%UDuz{bEUm0`WCImV&dUX(T&lS5M^V8-O^aD~d7AZW%=PqhlP{*)SR-(km9UO| zB|=AwD|LHD-+qhS>fn8A;X8?HDlfMKUVL3Wd+I^=5wT^{buO2Em7eN20kIdV+iLLr zo1Q3aqR|p6qF30RRAHrRJy}0WmO?WACBM(>7>%0|Xd=qQRcv=!GsJhkr0r_0Ah99h zGuA)_W?m$0mU?SaQrmoNVA6K%K>^cHg4w%efwAOM7LyA4kSq8CuH8Lg)mW3)Z% zoWkiNJTm!=%aApj7S7p^yT-uoEg7<3GW&EMHa(e_96AmZe3Pe+JJrw+j}aGJ-D7lA za5p>#2}(w?(@oPR!=L@0S8(|^iz`VrId^vx3u1=oVD99kyCF#$Ociwam9dF%Z8>qLwxm zDF@VGB6~I%0~xw|0n__{6D5ee+0uxeI7Aw zpe)a?aLsCD`XbO5yHT{2dYoN%eLA&rm{N`$SYIxLH75lhGIes+L>gKxn)Ju7|UZbZ0r|@dj zEEI!PZ#~h@G<4#8=!|>aQg)9p7(9jA^}DkqDP( zJncH6a#WA>Co||4=zM6-qq|fBI~aq!7Aw*qYO21qwC*x(`(|$ey5a|?#pC1HYO8L= zQXZqXGf3X4v70;t+K95=<;>$v{Q#!OP?Cc8`-Ln<%7DgC2V@^qC$o1eZ@li z3Ac^n=XG77`Uss7s_Y*qtIY$U`;zZrU5brTHchA9h%M_7t3BC9&+{ znO=Y?1igrS(Hjyr$(cZ|Sxl~UHP$^O_qa&f(OgHR{&1sTQe|(_Ru8<{OSV=RaPcWu z@EKznHdXLdWp<1Zlw8FR2=D&#ZM-pduc4Lv^g!q(jr=87$NJv4tufXr=}xRYQ0%mQ z(c7r*q^UgXi$zJp^l6Am=O`W(|0KY<@hHVNa&zliQ$e&P(7gdSmXXO4JD)Lryw&K; zgfw#w1ixd-{j-i%IBjEMLhkK(;d;&4cq#a4akN&oA12Gs{NFk5BB@E_BuM`zX~ke^ zR%8V9mA?w{SgPY_HQjL^nTV*?QG>vxQnU6VSeptkyy$JblxWXM3czp?#F25yykgI* zvz!(yIrC?FJWs8>Z$V6ss#$d%0O{6kOd8YcrDB|e7Tz2Em7V*-ns!TnHOH*tL0vyqpJ1f4WAp#kdjDhGgX3BT-_xMl zoZh>p;t*SM_iveC?mt$$pLz96aDOb9)O-HxMP{B|9Fsyd|A^B(rxBj|@lGQ`>aBxp{%Ju7{TlUd zp|dYI_4eUiaVo)6E7ABMI}{8L$?6}9g|l7Va@_ZA37`sftRtAL>3QmF7b+CR!JXFA z_HJBg6I@wQd7X1S7^!U9+}7~_FK+q714-pv@jBXN!_k^J$}wh;*Wm=`)g~(U9kf8M zaDJkA>eXQQ{LSNeKL)mlPYRlNS?>)YVtCVC>Dm?u z)hm?1jv0=0{xgIYA%)N)?m{Jl|KT3()QZPNzwu%JN0)>{q6TRu_kR7CYUr2q9~AOG zKL`X0`3`|XGRI2)moz900b+fgVaxxAd_{k{iN!j&cKf}&Sl#x}`I0qLlI=C?calF) zZW7qEnA}y%xwl^I94I`#SHHsmYq{DY{0I+~jkR1Fu&dg&_@&5NfmAbiRTYY?vR?H; zUH!5bN*hx@lHXMZkB!9Qc$)c5rByDFQQl$=nS*0lh}DO7?PEw)pz+JBwWqCa0wdG2 z-xQSjF||XnzO_Q_sv5!#1*@hFlVQx(?KZ|n z4Z0P9#slXY8BL0{#`Wr83C4wEgTIYS%m1DQPcD|l_M!$o8^3+1+MXOhw|B_ zqmU%?CjWo!PgBi;Wt)#7+|6fV*9uk{?;G|93eGL1@&x5$3JZt#6j`me^Rq=`P7&dz z>x-zio+q$25ect`u8UxF!CQr=B^8CS7So(y@-qfnH;2tEz>mh5(d_}*_2Z$&qm6`C z%oM>6$&N6??v3Qy94_lW*yx&=`aC06*&HpRO<8oVnhGc-6*GJ;nM_y0&a3sniYRWa zk4W6PEde4ce%OuGZrtI9s84;x$&0wO5(g(tJ&mTZNrshxwXs8+4FUVK#`N>&JKNo^ z&6jhbwbVkVIng!j(R)yOMdjb)GK-A>8piSl&R5^AkHB+@HVudps$g>?q}zHmcp|?3 z%er)pi@eYXd)YkTwyGq1_ANxiIVR!Tk)kLtpXjJ^#@nL!#D}gR&WgP?t*r}O*=hBH zhf4eVgp^ZF%OiY~qqY^SIAQmPu)%PO=Z%9GGAuxxAO)Y3%FrXf3;craF2Q}tf7HJd zO8uiTZtJ(2);aD9<5E#UKq$wWc)3Q~X6Id&=K5gbl)y{o+7%~W!WqqN$c@egBy*JW zL}Z-yxJIs3Xgf>zEw}>MBJ&`%dM+pX?zItieY(^cvRC6kk~R%?{X@R8ukGj@ab0_z zm{|e6fG&I;wuWJI-tG>K6fa1#`l-r$B!IcpS*gCBqm6c8YyY^_Z9Xq~=z5iP@lBtR zRWo;fvyOB7bcFq}H~pj1>!oHV;QFPFDZmj%*JJH*a#H26ncP+{Cdu6Ug-g$t3*AMl zzmcbD$4a2vMj#m}=}gEi$+se6elkn*V=GoH^Gt9Je?rGPu+i`C!}fxItDJArOZbZf zyi)LAEUbK4r%1UA8w{;Om-VcN>)NkV_kgypiQgRKDas5)J?`e zU-OZ|zom=#A$fd!Xh~&yI)j{ogPmHQJ7K`(o|9?pH#uolQ<2`q6*;^-3^7-Ey%}Qd zg@Mo;qx(#UYTng$ZBtJf2eDn{SkWr#CrNh8Bsn$sJP^q;xkLWdDyQz_=_QGt2LWRo zd@gI2gM8Zz9WBefePgZB)SDL`6@?tlDxzzkeJbiQX-Ry6S2Q$;0{^-PGGj2gD$`Lrx&;_%w{y6sr- zhtF}9Or@H~GXNBbFVqyrb*nM_cWjM!FZE|h?zW^0$>cHsD;Uv+_UrCqy_MYzb(um> zr|hR&-m0Ydf*SFwG^59ymd5l9{j$@_>M2|Z``77RMq%PS6OMajRsU>wZD5q|{vPuj zw+L1fF+`*=v6uKo_p;7>{W=ii=+YP0#i&`+o7s>UP*C+Q z)bfQXcSib^mSm^*3x;MVMoKuh;$^IsRHaaeOys4VJza#g`hIyFf?APWfo^X2n<~0| z^$KjP9BiDladAFqDa%U4_ChE-b*3@rlVkfY6)leT#7~$6cDlkxu2X{h+?&FVlUB_q zlfA!H#a%8osXp#XK3Jjr7AJ7}ZC+XT445x(#3kHaF25oxVUCYYB$%-B zz%GAxOH@TuXnTXX`G$VpCCz<+JVc7k4BF|}^MG?3=ef$)<8?Z$W_UTK9}y9VbW;u| zK-rr^9eYB?)r%yF_3?heFW*p4DH=;2i=O{=6kQ|e(2t;xVm-0iu>a$ZrM(0ZD@@AZ zw5hLMx%CC}ubsa{luj@4Xy=_V!qxyvSpI06rF_6BA(E#qAr1lAg1>~ z!MahQN+-V6F~tkj--yGhncm&Y5fC#kejYtlds-I%zT@*x|{qt9ksO(rrWr1HFUN*?)a6ihx-cby!Z&-TEo=FX?5A2hI1% zQ+~!0iuRK(psq%f#okLZxXaz>mUd1!luLV#IBFARNWX65w8ZdeC`vB}rTEN5I;fA2 zyid2)s|E9XzWFB4$|!RdW>)c~M=}{^W-k$d3Thd7NoAri6NjbptD@!-S0T z>?eLr=q?oW9vOAyAnwB_hl{jqRRZ8MR`Fwt@t`?4fCwrcQDTfuUyzV&awmpe`gqR=Mj9IiN$b5w*6@*z?NwG6}F^~MHyYBM6%s&A(?j0U%_=^N1Qv=vfWn}|H zG}JuA$m8PMT#}`xop|(`$Xj}HMDi2-BhC3Mklj3XXCPy|9EYOub1|p81=2ZTKM}}K zXkqCxsT-_2ObNbuhg&bMUqTZN-?=et8T{*b{GMM<)nbX4Cs})zK{D<2lHPOCa{R>1 z*fM82nHt%>Y$6W`QC3W49D70mivrrPj>cB1|42m<+u)Nl9R5oDZtcYl!^TK%x=O#H z%x*fBkWiu^U3D&Ky#6pKf*ov$@li0gO>dSvK1`Xh!#^sspO(?P{Gv|m{kdt;o`nFZ z!!1*f-|14XAWFv%_IVSYj6l{HneA?rGS-Y_UD$!TwvVrOfY7?y*w=c!9ua?>t2g~X z>D4_qA^5w`^l{Vhd8tkNgI$j%0&+J;z7bX?!9I#+Xi|H8S4!S?lFJOh5v zS(4OEFA=5rL8vsOANw2m!In%quV>@wyGz%2I=fo7B+B%X$5%uZog1yLg`G;xhD5UW zE!d~i8}(Y_6WpVXwvJENpwSc+4_C=Za@r656&0Uuwzg9WV+y zvOPB%He{8g9CNro< zOB04X;Repde3wCinT4HyID6Pk!A_%%3f`fQ4T}-32xmHwCv<~LV!00a{PxmE7#(5| zPjlgQ>2G9|$wKkTgCX(U2IiX$zI^)oB72ucihp zqxRbs-H>FVFB;Zr6;FA7S5bkNo$c#hrN28u1UQ}lgcE(aVZQ;e@ZI!OahGZ~;iz|R z)AI&#s3T25W_m2kO6_a}+(}{n6}Kf$Ke%-VGk}2ZUdvj2<9zUAI}jL`^W?|yEAT?c z4!#oDDJCz5uQJUGI}73Z_0@}HIo4$jNqxGT@vJqE>U?)ScYZQZK-(~YU2uN41~F&z zwF!ndBfNiyTZI34H}ick4gWKNcpw~Vp}2Cq{eV$-Ej==)=&KE#mUm}O`R*EsHF0Q>HtqYH2cgsWW7p)`wjc%p9+2XU*!i5@;X8A41Fcn5cZDjtJf2^Pk1%GP&#U;?k&oVwVs_pQ+W&2v@ zBzpE^R6VWtLRXr&HODC#rdJhlN`pj zTQW^=RX^vS+^Y^Uh`Iar)WZnBc!h+27kpl(0u*l(qj%aK&bCCL ze1`67(iN`nD!|OX=>n3Id9zK>d38R(Uvq~Pve>XczbgUWPcqd2oFx0$Nx`9HKGQog zw2$QXo|PIu=DY8-5bA4{g$5X7aK%KpU?yz{ecVzv7JAipx-ADtVDQmL$HyhS{;^?kg2p(v zj*sXc*xW1eUAT2u@a@CxZZ&OuKwAEu(N35k$w4N{UcsSmaQngu6&7eys(Q88C`X-p z*O@i!9SE(qd&+ymA}>)apB;3SwvC6Zd6u}#G+X3I+gCyGVfY$-63aya_+gdpyKlZT z+4uneJd(GHk(>r;v{&bYqmbe?4Uk%=#PZWe@}0)|83-d9Uu(Zqy22Ac zeF4P&y>fK@)Gtx0p=5L zv%=VzHmYL9mqkrj@#NQMU67QqaZRb9_ceBZ^HDLpeV&*i)6$-b1RNFu5WANG+ zbp%IkMLG4-;>>6^y77$zezMGr{w|Ls*9UAvz~4e|JD%oBxwQ6G4i+KD^=Jd78~ zuo3W8j$?wGjA@w)E@LpO>_92pPu>M!B=ejupz+K{@)Y#Nu|6MqwLhZ|#93E$>R*JX!y)&jw#y*Kk zRYv7;7b*Q4{07y@4q(?jO61QBs9Qcts}@7=>-ffaprdUd_;9?HT+f)NYP z_&eins@Yndk0`<7jj5LS*sGJuFpo>N%X$D*yntsAW@(rWW!;fKD#Ov{a-C_;h}l}J zAw0fn-tE~!f!MlfB>N2Kh@arnR5K>`R=dL!Qc(Qb!q#)t(HUgwCPzTHWk%|R*#@Y- z9|{xhJ*Zo~KPuH;4ew;UA|medM|y*$%p_-FX^tQjNzo%*trLMMg=QQ<+WVqki}_2W zf_8r^@s7E(e~Z8}C-)EI5OO@U7~fMR^Ni`n@snt;a+AwGy?<_bvOsA}1TL7@N2AfE zbS<7%R1`rh_B;jfrpc1NN!6E^Q2a6P9}WAxQ;vF#ZPnG)wSTn@0_?KJpO6oXui}dw zb}yAGeupPTXYu_8CYhzN0&`prbEQ+9J0B4YSWpyo#Z1z#3gBB54Ujy!T$pont%GmP z=p+BE!sIDp&AcT?Sg~?b-b@B2&GUiel%9nHPZ90ymlJQ{24{5CxUrhbM!VC~>44u! z;%yKa*v>7&n3bv(A9=#tm|1Z98Vn;9T9j}qG_mGDwxNfl+5EmjjSnpl#`li1CbyO; zz}b`8(6ywdoC_AK?sF23zn*xm7|d+v8+^4Y`JB-o=kw)}<}{w8jrW0}A)=7qia@{71sMo!M{O~%w?#bF@t~dGSO|K2$bewYZfm~WwQ?%an}A|61b<)fQ&6; zH<{pSlUkOXUgwW3osJBhmymf7G5WXCj5oCC=MRl>Q*Iqipx*FoSqEHAo*3x5*=NQ8 zmCKl=AT(8olm9CYkt2kjW2vKp=a$)nS~6TF*)E7FNi$0E0H#Jn6_kJdg!z zMj~^M$q;ed4%QtFQ&!sS|KwXtXDvQ{b-kR{hdjHtrsJ{>RB~CJc@oj7lBp zzy0-hdw>Z$(5rnc?e}4OxWC(Cin9ZjT7g3PW3xJk%zmTAf`uWgJ#*u|UVe-L$!r@C zf7sOld$M*K^^~-mZ;krXL7mYpoOt6Ss#Qr45vR*`N;SxOuDK4;;`Cq;73 z&o?EP2O({`m0HHBU%7o6R-#~sgY>Of87O~l_Mm-Qwz|+xwJAg4Ch}v^&Z8 z14i31LBXAw43_L0sN%OjM+=rDUH7bKiRmLD49x4C7+&~2w7jH;uwG;5%NH(-UF^+8f}E_GF;Hsq9hyOZiwSvXc5dQ5uHbtNhvznL z4*eOwc8W>69&uX>>xAWWTO`4`k^W%USo{7YhSy}v*q=HtjY%49*UfTp zAen!yea)H~Iv&4HVB=zqil>$B+!(XCEAE7Mxw7jyhp3HBiy^Esik8_GVoITq0sVWJ zC|Fewxb$x`&vTgUO0UzcFwHRwOQnA!jA=XW9iNnl#1_t*5C( zbAWKO_1~kLZ&kts?BOxC^m1`W`?6ZFWH}fY4O!7Q*=8(iItQv!{#da4JSP<5px-Bfm9ccO%Q;bm9KDlE4ZGpe2@|A#YZx)RIxfFZ z2}M)wH)M6!FHYcg>$KYD@gFKA14Q69(jeUVbCpNps9QVupV@njulk*_t=GDoI1rJcjW{{2R?s;4c)=r_WvDVad zD$PM%;kFZ&>b)D{YF*KXZE+t6qC+nJ^e5Uz?^4k1v06!$jv>{28O^ZxqvIjnx@I(; z7&F-Ee6ey>3H{R{QRfLg7bEP1ew!NhC56=Hu2`#FZ*S-f5>A9uS$%lLq?2pamRrGGlV6U#+us zKC62zT$=#*7bO4WMmgEBbu~_&Xq~&J5Z^0wq`HxB#I)WM?2L@F&ZFtYw%=HsO?4G% z+eLEnEk9J(HH;Jaq4epBM1br7{BcQUa`!4L{34we&9^1L;_i$K`1I*pk+g*9uXW4F zi<>VdOPBoY;MAjoMH3TIoi>k&`8xWnG;6b(*ZH-7R)3Zymf*=&Su@16~`6 z@fB{ZCUJn}Cak~A7^uEx*pvztOe85>0>IZw~f21n&iQli>Z>Q<*5!L?ZCd7?hSU znaHL>#$#qZPf78;pQG#{IW>zDy~)vRTVEaX^d=y_pz1Tpq*biFkkpr2hJ7a1PEAzr zbVk^9?@VywpMq2F?DBlnSzJTn=mewM`c9o({1GuGS9JMZ%4VS+3?9iUYbg*3jV6g4 ziy<^~xi_vWJxq5diVXgqlF~qV0rz*9_>I+!yZFeZYR$W-p`-92`PNE3DBG6T#`&S+ zK>l^&L5h68xDa7o4&ko|()Q+P;0p~S>Ig|F+87ed$XBO|Had7vq=ub7#6b4uz^r_f zg!5Ta1haV!Q!zywJEuGBO8tzH#n7|659aWEA9agaLx zS}Xo<34@_{#|Oiwl#z=-2lYpUjyxCYD~^ZK+W%c5{I~r7-~PY&H89+{IkzEGmDw!s zty=(IXXtAgG&U^WEp^nAlXx?pgn>%xWm(-T&74XKqI_%}J6x^WD#;5SW( zc|Sz45FYK2IW8_~P$v*2t-D;sJS+~;)KnLWYBFi2;Ja)FSLNPw!-<&a-NfXtl}_)r z)76H#*SF*H59Rq59N;USjM3WeQ$=d>j9jxV*oPPL4#?zp-9+fWw2KMWD6!Et$Hk^% zeZ(VMk}B)7sAMn@RIAW1hEaYv|N0rZkll0=qKAWJCNvrfJs)h884*(Zdc;Hb);c`* z`bV%8g+Q_rz~=cIMyHxWm+Yi73!jR3^5oMy$t&N}H!t+E(J;LAn zBNIt;FFeG06>YvcFFVrk;I%Y0KK@g^)hVssd_&_I>{_K}Z#tRHxleLnYd?3My=;(lg3QR5L|56CM{d1T8`ID}MJEa3( zqZhUskU35hs~k-#%tYqPrEQQg@FQh1>)UVgnNiKlJ<65OgE^?56{)@<1=IrCp}DCK zWTLHB+a=TUvIF7J65k*0%N2u9dV?d_4EKLh5r4(MaPhBqSXhd8VL?qcZSGl${<5Rw zPdQejQ{^KlZLu^7WUZM#men)%i@c>zdjx8v z&orM!G|Pr}FgDx$o?xZcnyCK4F+Mq2)x^Ia|I3D6rk?T4<%qy=VYz5vsdh=UM0eC) zjM<)cvt17h2G}j7%k+^S9ET1VBOL+A6cQ0f<)Sma}39A@l z1tI7O(bXY`$&*8myvP&oH^mdza>q;w%MN>^#sq>6g|@7&rx(K(akDT#<5AX^2Kuk$ zDnA5*R3Uj3g`fA0t5rlrNh_wx>0W7ZkX2S^pA$3$D|{#yk8#1 zc4CAmN%)=UnoN`iisR2@7kYLK(zy~sPz>1)dk_m)}87{t_ZyJEBI@l^Ts~8jL%V z;~@Gd;2@LPyRL5soX>itN&ZdCA>2&-ET?+5$MH+*FGlT%yfuS&Jl)~4r1D)isgP;8 zUj-qwEe40NdDjrvxQF0(e@@qPfhVY`LUC+U@3ls2`UG0JL`XSt-#B@t`f?r=BLwFd zKp*a0p!zy3j!q<3Ng0F%JKp}JJ1mJXgO(bU0Ep&(w_&3kyA3z=@@l_5+K>%HCOFvm zA$^9m_wl|vgcY3%RL6ngR$4H6X*{cS zncze47rcg;xd+Cu3_lN1D83)%_W>H{NP32DQ$VV*IZ+*lkL~{j5Q4?_Vg{eHqaQ@M=NrIzsm~u&{(Ip}E2G%YCZZ?(UkZBeYWyR&bq&{0XDg_akKv(g9MRRsDz7!vfw84ekd zF_c1DkK&fL7r`aQf2|@a*?sxZ_wvcRM2`DV2ZZS9YZ&OmtXdcXjpzPM!2x*HKr)+h zkx!oZ+4^E3%zJvgaEt<*D%gghn~0`qoe%sUCLdS4n-^=klP^#&(GH4&+yXqs7e{M& zNTza6mpkUwiSkev#@D;pDsP<+$}U{1&ag-hoIoB_D4T>j&`+H!l9h(36;>orNoea)Dp14+v8pWchfKb;Gu7# zO|M7Hd^$I;2BsymTiN5;zQQzvs=;C@o>s3RHk?VfMtK&kNHlPA;GzLlDi9u(0U%;U z;O}JbVQ7#gh3YwU6kCzF{s7hn~0#(vHYR+(k|h7JBmv!DPWE_0=NezNc2L(CEP||9 zmLgVS6vOSE)$Fj~e6HvQ+ERs}GJebUZh_hihK5HQHg?1NI}rD;$!tcexYu$Efd7ZI z^T~Ls$V1w&n-djGT2MRLGyX_j4zjsHOXcOGG{9ciZ$5U@r0 zp6o{-{s=2+25{aM0vZgKId@joUqBAxY@6YS&ogZ1voMA<#>U1Yrrp^$RsyT*h4zL^ z2FW6N=gV<_@OM5PMc5mCg+8CJ>eMjCO5kZa#I;%|D~co*TK8IgIXwu8A~K%fo5FwI zObs<+Tz`0Fq(8}23IqBWt0*E+vw2y3;+<|p7cKXC$#UJLNAbDE(Xv+flhE=49Z7gb z&)j7NbwzBKoF)V@$zI>DT?{W-E)IVND#-iS6CU+P;4vp_u?*7Yc*y9uy}K8DT{bn` zQl@V&Rl4lRY`4v;Ai9+P*ogU%f4)8b-ERa*tKe0yw!^F_W2}Wso~4S3q%S*;Bw#l_ znA8Te?e9V0+Jwz^=pAM_`{?FJw=@CcZ}l^$`KF^UV>1OqTLEp9an*zgZB89$h*O^h zOgEttC`N)u&aez=n7P4^`(0YCI0+AaKhp$8nyIp^b6;!;<55y!CC_-7&^|jE5ohuH zin{c96d7mm-Ug-N`H2ffLGPckv$IWy&AtmG45v5)vjX3p zKlO&-mrUQe31F9w_DH~&HnM<`ud$Qay!4(u^_ELz*2)nO_~rAuW1fH{5q*+PLqdss zuW}``{V)(U5kxWO{P7MAP#tYlZ;igFM;cxx_|oF@MmRpgGBP!hphX9je7Ko?f8?|h z)jGgo`OSJ~JBl*RnMqbYlgGSs%~STdBI2~kusf?J3!;E@_bIii6&7C0v`JaEs!fsB zej!af9b0kO9aduYm-u|w^PNnbmj_VEI++l=oG?&>E@QKI_cD&E`clY$)=MfCajn%D znKziV7ItL>=NF_s@5IWzOF)N4EY6w7z@S+JrT-^5M0fT$pVKOQiv4I?{=wfn=caBJ z7Hi_;zks6aqms+tRC^hL*ZtwxaDU0(E<>+T{7{2L;|OMIuV^f`PzgCX0=EQaGqUQf zp4hNy^SV9ZphjAmP6YvZpYQ7WsgW)GCt^{iFS0%V1YuU)te}y^>+x3fkb^a-V;y=p z4zb9gNjQwE?IB|W9;H)$3#2;U4gVPF2lU+vz;orLs`Mp zwUINGZVaqrg(6paAeXn2Cq8JC=f9;8f}$@Ub;;Fc#Z^XqC6_00QPq&IuNk zD{VL0f+-^1XM#_tnXrfb>`ntD@b7uUWxB^#9ObyK-yUL;^DvAs|6xj*r?R^WG2}$m zIz2hKI~?Ow+2e)0@8ORQ3)tYW6c>Z%O3-oo2}96ptQx(!R)wG}MY6oREx(9vR#t^^ zKeq>V9%{2>mFGDw6X1M%n_)F1C8c_M-cVuK-??$C5iZ?RGCK`seuqiDTqx&tz*ogv zqOjuu=KK8!{3@THmJvI>sB`_&;tLikkXF=uEzy0iTgsPW%bcIL4N{l=c~vj5FNTM* zCdD|+{O^fh`X{%^O}RLK*=OWEmoU}+6?S#IJQt1?1DxVyi#{=u2qAut)2Uk12hx=Y zPI;%S_+@k_-J0;oBnW5bp}&NeusvMkgpm4sUw4Tqt0TzIJM?Y6TE9TL00jMy&v+ZA znJmLoPQWwgYDKJ#Sw6B)gk0Z1C06jRAIDHS&OQDsJAVYqw$-A#p*JuEduXw2!O{pp z%e@FQP0GiIDTry?lG{+dNKhR1FVnUfb*mV!l=Zk@rO2+-WVSG~VB16NRZJAS3`P~V z#LsFft>L4ip*@)TwOVYM2mf%=P`D`k2z{7HBsJ@$N0<({`uXFo-T#(VwjnHRnAu7*(lWdnWR4^WPk;oWg6^5xHI!utODt1Nz3Swp(og749O zYY(bgA>+RE$pzEa-bg#Ey7`HWCyTVKOxgY^kwN7Hz&0oRKaZ@pWJ#2=DB_B&0gHeR zj}3>X*)b8)C9LzlIJ>M>H&g5J&^$#CSFf9>8sLt zdw(pU6<4o|W>&*OSPq2<#I+vk2)oFf_I4=jgW3V3o?iJ-=WR`2&y6Q(eTSWp@1K#- z(Y?~uZ9nBakm%AW85pLI73>VUxkHaHw;qu+Be@#1V6xQiqW=TfQO$HX!ZdrexP`AT zRgoRKlP)YP{ns#3~-9{}ljw!jkbTslIXVrKMYgLqQ?Edbb>PXk+i42bxAb zVn;tM7AFzB7kjXvKkkl~I6>_w&D79%KN?{!6k1QQv9aV#E~82K+hMR&&L2uhg7%hw zwHi#S=XaB%TdcGuDLG}`QQbG>3TOp+#@!67kL`RUOX=B&NFw-bsklNr+IJ0jr7`@a~UF4qh;sFED5 zeH?rj4!DntHno*2M*uSXG@|SO+wS!gpY~cLmrIBdZ*+>%{(d&Wtpx+9mv(BNyZg=< z8X5f2ER+|K=J4FKZT!KlQmKw+UxI{fJ@5PlRY ze$mi3s+vhUB>38d&sN1cJQ0*366Q7+6|b3!%>6@e$}SSPFXkFu^-_$1&lMzjFu!cA zT@j0N1FG$Kg%H|bWXCGt4InV}2(y^LJrTpZT!`^Fb#(2y0A?u4((^Eup;pj%>gb?3 zP|ryvnTy-D#NSC_rbxaF5nyw)JHoDvy2FT_7ueN)1=t651Sq`5Io}gkfQd|=f_6r& zz4tPYW;!xzER_GL0g}DnzIOA2#OZj$?~14O41NICsg)D~`TEwX;_gkd$wQCj zF`6xa)Qf?v_4DgjRrC?WUm*^lz2USv^>){``m?mb-dY81LyNO$(e=sc-Z z)qlh5{FT>D8bmmdw=z)y{1)r(ULD4=$#b5{IjkT{%pFC<8Itr1eA);puS_{VuWZ)~ zYchjA72*XD_>CVIoK#9x653ICCOUTp-i-yr_B~Vhw*lYW6zYmu6$NUV$-JhbGSBRF znyjnFubf}lS6AX?-sR!i@O(_!T;Sl29PHz(SZbFb`H4((aW2xyjcd=h++6EO5e6Nv6DB$Ef5YsiIEp3M zl|loOaaE-I8(duu&^3@hw{2zj+U?hj2|GM5p0`e7mn*i8xcioQS0Cj44fW$Q(| zOtxBU)V?QKj+sabP4D;^o&9dy6DbhbTY|Q&Yn_`J&-4CQWHzB28#Wb5HU~G$pS2zOcj+=Ba1|BQqG3E(W#A93$TrX{dWHA6xd~>-S+7rZlK(*&WEe<*DKtE z?)Z9JsX!ap{=hdF{_dO>EmC7RnPN^OoI{Ukk(%|K_3dVA;uEcS(U;vqeMn=oV>r2B z|7DQKR!?x2GNE4Y;-8rGvvZ}og6~y=f{=OYvFAk>a}Gt+N-HpPiY2@UtL{~bQZ^LS z(-3CgaWmOAC`JLj|6I7F^TPc!m-9|tzYMkS(s;?{HAvMtS7dA9nI;1NH#G`p4E-S7Zn(4-R%M>$QBU7WySgP;Qse3T;cy76Si7sS# zIFR%XRA+h#u^;hEv+?*qeB!DS*2y&g!RE*%VFZjx_{p%Z;q&tx5ZPRF|=0PjbrczU1EfjX69AqoY-xBAE& z7EH^+O({WI3Ji7lRgHHNB9zQ|@7Ug-26{d{z04g!RcgVu@1l7l8_Uut`+)udeKkKK zfPPQqRC5TmpkSX7heN!k3`|0)b~)O%=Rx)PLG|0pNrGpK(&dmrS5+$hPaZsjQF|&Q zA-Ob;&C|bLD7}Z+RzPVy3rR)nW($o5EB_s3o5~fsqmB+t&2Y)cbp0PXDNlx0ymv7hnb?=l{Rk;|RJ+K)3REC_$Sv7}zG&Hc5j}nu*z4BP8 zb^chDqb5Hn4xTfNf1Gup&=f;g1hHWsZ^s-er3Kd?VVB73Jv^EbM8CoEHZjuBy87R# zU}q;_W1y^^O}XB(3kglS=`x8bGU+rDznqdt)c!U+a$r|kc)H%*HDVt^Gi0(y)pf^O zBZa*PVHfNW7-O!i(CkoL&D!(IiWlPI8f1_{p1Jd^NS9`|7qtwpPIn70Dv31L2BrP5+188JoCj^zBD?X6Y2&5L<6&|MaqB{TFRa79VK3v)N;`2dj=SYrfLizN z2wGX$7cZBPH)m_J(1g4&qT2Tole(6*y1vsuSVV;!;}a~Bi)A{_GVf{Sdud+ckP1$@?f!Y%vj4Q6Xb#T&bAX5_@Xuz!nu(wW}Ah z;+tb6cO7*rmTY=dI=+v*UCWO|SB}L9r)VMpfzpNj0|xYJA;nIK@*QUS(TJf7x1_&q zC8g!Az8D8O7ZfPd`ZlRSQMcutdhcCZRNza!+TSZ1a*L(9ciae@;FEiA7cOs)Y53jI z{$7n4*b5`%?@N!mE>F6Y#1~|$=faQgLfzxn){*`?dmZIZFNV#zf(VgfWXw4<vSb31L`yOPcJiDu7iADS+mFel=1&XYf0#k@r+uZ}&bYR*V)az_d|)9Jq(ElKO?70#oyZtrt-y>}x}s zVS26OOKkL}h5(?WoW_~vT zv1D#Al^01Ii(IZe-zNj0`38Ud+!xs?75Rrs&JTJ?rE|g z?r(b=q1sr_(~Al4yZ?8p76+lj38y^3X>+WquL17)Gf5Ed3jfw|v45jy3>Y82*ew&U&G?QaFt&_1fhPzV zfbla%W5{CfFxu*!NaL)6-Opuc7(1h^p8;cJ^Jb$G0V?MRDSW6`CM*AA_^?To8kEnBodTMl&b_MNi zi|;_+-tzl^;55RiryySGoo^6)P55zHb870Ul4aB<;6NItWV@^{hu7)wn5I)7%GeMmrQvADE1LQm-ZCbh#Ay&m32|z#hYT zBIc`v52s5=g7*?uQ(wWPc2Mkivqaya0-c3!^gwVC)S4X@H+AMM2GMJ5z3 znteOc3qtUs&9G=am<$$e3vo3t$rkp?4;c)gbYl~B6h3zN+?}d9t6TK>qO?ksZB&u+ zh>yze5tBKy*I7TOB-*)EN9+>FPNMz`8`E7i!?<~%9~GO8sneLFh0;9WQ|UD0s83g+ zYO+UlfR8i7{F-}^0hK&Qca>{`#xKO*88W~GT>Yg>JFA;!4<+W64<5}*`h)e~>wt?| zw8T4NR+ArWyUY-I<}x8Q9niG6DUG~);V7TCVZyod*3Nu@FrE*f`>1y1(aSWH)|ofx z#I{{N^QzCR>025|7flvv^XE(28j^*RrBw`Y+0y1M``d=fdJ&H~&WqHE1iC2Qd_ON? zl48Q3Dmtz>QVi0fm+H8{WIxXu_B6Na_hb;;QNt-sc5xS49GGOvvmGgXMW3(Ojm6H5 z4!bl)ZXSidwRk$Wxq3LQQP9QHYtt$ptEQY;yeXeU-yywg9Y+<{nUSljmwtM{X`PD? z7}N}}OY%W;TXamE{<6j#5Q}@3xjb!=7XWPtli((0YN&+SeJE5EZ~eJffC;z4f3w*$ zgg^3pz4WR)_Xl*MVqGDk{&1||+W=1x6Rg7I$ez;KPYSbc#k&Xz)P13a&`3sz8e>t1 z`#=M+1L;kKFyfDB%S+hDn}tn`$RX`~aFCg|`Fw0S*V?sLcp%rny<56qw$qRmQcts% z-Y!gkm=B-aarm<2|d< z7tI@Pe;7`HdIVTtUN4;86;Bc3B89?lIA4G4 zJf{E~3UB7ih(R~4SSV*u91zZpI>@iBIL8J_J*9IYs?po}*M%IQ<;kGj>BxLcU;5 zt3pceEXKZDFsxnghEUrw3x%~W3$F2AJq;i~kx;n}z4G5|(bIVsO5EMCioT>Lz!!p> z{-NIf5Nffxhg-`SLye8ac39vG#c@mC`2bRSiY2!a6a>Nl5VNcYWA48~2fC2M!&~&! z1h)TBu84mqm%+~~cguc3ue@uQIi2$4*nt0JQf2FXm;fxNu5j0;{0g0A0ixHPAMmK2 y`c;X8n?=hKU&u9!{lD=4pPJPJ|FgNMC+yTSXKq6Vv!JI~$I8O)x4NsoiT?&>|7e^5 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-register.png b/docs/developer-guide/img/savor-register.png new file mode 100644 index 0000000000000000000000000000000000000000..6a85b93b02044ea6b5cf8ba586c1dcc8c500bc35 GIT binary patch literal 105818 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 z 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 t%dLj2m= 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≦@W+oI;9G0# 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 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-settings.png b/docs/developer-guide/img/savor-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb6158585413a2432776cf449f63e9652b3c369 GIT binary patch literal 102538 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%0Db7TMLy@z+C0A!sfr=AM_{u_y1`f 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

        0fYCE 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�q1g}VFC zrIkT5B+l3VH0goTV3)y+FV73HmBG_dS}1dsik?>`j6VOYYc}N7@a*ecyc~cQ&u3Z` z0l#`Hjn0S3zmo1B{i8TzcU(SB8d8fSvI%7Y?W6PB_0gw373WBw4-LU#%DOmKWnRpbKmGJNQHAuE-%7XKv8eXZpxE(?v-CZ%J&z(WdyN8gm*mrEKY>rq@A=}1? zC8PQFm;RI*Ye9~wixlLWUo8tYo#{`_y&T4b7A*|ZU4|3IcBlmnF(Usy3pdhr>?EA) zR1xB{w!|a@gcRyPDFKg`Y8SAWz`TWQFhv(QAVybpAlaMkU~T%mFry-Op}Yr!3PG9> zwsMy4njQaw|1(et4~fKpL}TKrlER&X81_2wE)tlyNQliKqtJsEQK&3{oYv`&6uGEY z%F`1Xuj50PAZy{bqQUa3523tMIf@TgG9!!`3&-Y)dI-CV*PXQ3Wg$Evu5zCM?y&N8 zLm4A+2Ov#UzrbEN(cMT{#=)u~*KZKb{LN#ZR&QC=jBqsYN76gfuG%oqPAcTNMytunzg;zd-0Hku-h{3B`{B}$@m5aCA z_1gEQFL_S4Z+%EJi*T{8AUX799=qST-LbWTgSg-J*Y=qL^qTC~ffPKu_+|ym(3A<^ zYQuM2RQ6(gxITMv#@4j;2AP4&RxSxhyh+*o0I4hp{WJo)k-r_%bFfkb)onW#T;R=f zXd#fYHh($(@Z(<(Dx{3x-eHoZv1zVxSE_hWPCqdAzx_x5*{A=@UtPP#|L$G8c8%p} z$O5}LpFO*I1h>{rMj>v(vcrG-6IQ|izi&ox>!yNRH&1XFH@T%9W}(g2-f~=w!z=3a zFvTba!!0!`R^Bnj+KH;1!IEA}(Gjj2&Pd`EOtusUDc)8%Dj)$BCb;M-`W@payn_Da zRDBRh8>Kh0`RiyOYP9Z!3xU&n3JavgP#uHBMN}Go!J)h6HJsV&i|(6;7hl;%aYI^SOkCGx%S(d=|GjJ1_}?}@ zd40x#;w=R8;RvlL@^1(SDb{ddMvB=c?&3l3`Y|#rJtF&i_1-sh&rFBz*Hd69@!#-!>S;cCJVi&;xF0wqO!Es=nO_c9_ z20T$#&F}c*_6>h^Y14rlyf{^h=3}l!#SZIo77nFlEgGk@<}?U#Wf!#A#T7Dp1+-qE zWXSTpU*Rel7!sJ{-Hq6h(aa1~mAdqrF$q-*YT1!P+hmGP`2Pj{yw8#G=9b9!>f zH%EXs4eWJ<{50J1JfA!uAxkLAw}I3x!n-s(YdlLio^7d%{C z*a*I$=VM;*aaFM4cJzkV`kZYD#h@byG%Ilq+mOCAdZuomN;7dvzP#oiSCk3Th19%N zKTVidzep(bK*34!Cqrg%6(P~aGs6)%VMRj%rvuX~#!qlIWlU%-L&y^scf200`Jr2@zBmc2Q%o|=KsSt|Nj5=?tgIY z8vh%=4a~7pi1m{baTmuqSor0zE!}T?*%xNxqsu+|&(0uDF862! z=-~59kiK^|l{Bl{fpXd@BAueNW>;@L-K%D+(9?D_W$rYP85TY=vnd-qUR>Rwl9|ec zA%!u2?f+%(U1Q|T((|ykb`{$LAwir(h%ZWNd+lX0Ll&EJ+g;9Z#3I?8>0OfD>Mkx< znjLjlcU3oQ*;Uo5svfdEnjlc@0C6HDj->!j5F=15#5WlZauJ{aiGe6c97BlyII#Ug zF%SXy<0wG_|B!+hd7k%j&iQV2X%=UC)z?FcGiuB6b1}lpb;z$lAB$#YlYC7;1QZBqUP1*)Ri}!A4jp?>0iS)F0`6 z?PNZ#S%TuyWl>f!avM&MFGD-b)i6r3Kb)=eWNgUSX8%``cX=q0rJi=ETCy_q0Zoldb~pEJnWyk4G%7Bw~eUQc6Z?&UNSHyqtC;e%8=${5EuJ z91Z13gbTwhp?J`5!~e{0RJaxK^iJWG?@HQjj8xees4k`S`rY6oX5M6wYFb48h}yCy zwcgp9bOp>(Q!_pKF2WTi_^hkBCG?=aifGU-J0I%pC@v4*KwE?>vE6V-Dx>~ZgRVp{ z!PQf@i8^tZgq_)pJCeE9xoou^Dq%4^vy3&@+1fCqc=D>((&g;^?~4`43}{N7+kvH#Xq8$t>wjr@uJCT1Dc zzhNK3k!+*ZC-FRgJP)MdYmNoU4K*B($6rL+kV~mTJLXf~3 z)O?IS?3__0tJfMF9u6eMc^nB8j zci6}C&zx_-it01f9=o|S?V)QRQBSV4;dby;LmCCCgD9`}pD6`_9t)pC#wF;0-Al+o zih`1P#9kIM_|rg$l$qIeFWl`i3UcXEQ2<-u%IjORU|keluAZSh_h5h<#&R zSa)dI}nP~Un3K_jT#Ee00mlQAHQ1{|3R>edMYJwQ?V z2(R2fgU)0s(w@dYZ$KUZ;fEa)_J>;HS&{jq6g6jK(2`83_7Pv75G|^}`~gLx2?%8D zjLIMWRLz%zppHR7LvjbyZssY_fFkoz+Y+G^cA+OokpbsP$qQ}y*f;}JP8d-|^N|mO zq)F(@G^jOzs=}l8$rHW}32jG6c*8N#$$?PEsfHCl^{iDTIV`ajmsy6@WZ}=pt31Q3 z;tyP@#h1`RO!=4R|5$8L5ylA>CIQBY4q-r{=XzY*`by#doWln-C!f%&Wr@B7 zi6M|nd|5rV)6nUV_9IFOTt1CBRYo22UDP^NqIhEQNceK9nlw z(HhXu=-xCWpoECPeYjI-Bf+gj)*9Ns2kHaBiJUA`hvAJttZ0>fj{p zT8^li;4^jD4%}r3iycf_XD3~%oD=+)$8K^qKJhD3kXAEy1_P~H z5mw+3^)rnd_D>=}%0cQtIj7wE+S4ET*MI5DD=Yl@V0m)u``~%w$mzZiT0yD{{`3f+ zQaN>^T`BOxu*aP}u-t8u_J+>a-C}?9M|GrYZ$h z41IXY)?<`br}?U6P3M)Taws-Kn3B;|k`&qiftaxHf$d%-V}u{lMhy<*_Ahk@0W7E; z0T8x3>g=S05u_*xW~5aNTf7AI8cYh57{xv~gmMN%4^&bYQCRA80Q%q`Who3fMH{R@ zgG{+B)3A9$3u)E@h8h#);1g-|k+hGsBmdfeeYJMOJ}hu6WkpY-10m0fo`7}P!7KlQ zJypd8tIh`4by*V_-mq01u8zf&w)Hm)2 zh=5T`+^RrG_2S?=D(IjUDlL;>P>^BCr@_&5Iw_0F(#cuTNXc#@uFas4@|rRazUcd| zE-4*TQYc%e<&cWI(@rfAM2!G@RFZ}BiaoEAns*FI0ew6G<2gPn*QGMbq^uB{Wbm?e z8t^27U735c(_n$XovswaD)n#54DVvC_wm#{IwKhb9!PhKr=+k;PB2+9YJx!xg4e7& zegcg-B+2g&pVP=dDVe7Vw{vfD%Jg}Ff%HrMV%!KJl^BB51Ok1_{0K4xAA&N6$)2Ez z7}zl!4d#KyoR|nq0H340mD!Py>^BfcH=3DApbI*CiG7!U`IxBsldlQW3+h#PA)%lP zf|d+aruHP2aJXTfY7O4Tk!BZ((FMLgq&bPsCX3p~353~DR@(ZOvlNBg2IMeFR191v zcmvH2olzUXm|jxHMG_6|2{R!A=Ro3l$w)mFrkbhIu|yl&~^qQLJ@EwaQndffNP^UQEc`8+r4ls zGugp{ouIKyhGBg~lhCC1kmZa}zz|CoHG6^Eq68>h736?#KUx>eOzsN`x4_#8tS*{q zxMYrG4&&q*FV~GRP6qk)_~OO@!j1llF;Kq47{(6h5V&wBRnxkjL(FoAd*X%{zm*Z( zX0)H;ASwiu;2yw!{8C*m4%R)Dy?haGe{L3{OtOs;Yy&D<%PyQd)zC~-B7#O@BtZNZ z0LnYy1ImJYjBc?N|kv|fRk*9?3tYd>5TDkZnhZsl7pK%_ZWc-qU zmCwR+j^p@OoWsuo-&_2Ge^NeI3J^Twun{T)mKwOlA<{ zp&h5#2|uuqo5)?iN>9^=YKfLmwnzfa(pV%Iw{k+hhQK-6D3K>AQYt2tVJ;7vfC-FJ zO;mv?q6%B&kWLoDB}fGNL~>uye@5LH#3=(^yY(>|1; zP9y#W6$>nB1TnBc!F-s&FL_M=eic!ybDkNrt! zYFW8EfJV#(=nW{Af=2C*SapSC3U^lI&&rG0TE!j4Oc)0XCOO6KQipIBolWGsS-aP~ zzq39sOekAyqMsq;zB!uCr@%UC+R$@ztEu$oKF6Uz>Yh{$ndE{8>u! ziAgt`3^j}d@bjRLx-oPT#zPMgUV`;{tT)1piqf;n#={1>EaEBrKeyrYP{$_N z62&GH%-w_U+!a%H>UGDgK9i;Y{49=;0N#WsV7US&ngaRHXTq9Yw#KiHHBqrx3eYll zj9>u$$F)2k025ec<_+R=70~ph5um<^>7XE%wjgSID`ztXqHdR5ctJ`AjWBvlE*p?s z%4K~P=tqb`t2Yu(NaIjTR65WoXbIj|4a4}SsVwuOs=jF^+$)i_6d_9xHy|rliSwhr z!Cxb`mt%4M=;$2)0qr4Go7YfJ@UuE5TCdNVk|ToEzc}5H`98(7m=1Ei99YufTlhW5HS(6VgiXWB7>!$1+}Y=e(DhHs1s4ILF$4L1o?{>*7t(}E ztRkeNTp{3%vS(rzyP_6WP%PMeqY8)GX&*CC)xNGsJ0^N#A)*IAjHBzr%jF7#qk7O5 z=X_NBklicIr@~rQJVH=5o2i^Mkv|k^hqSmB+dds~>h20y@KKadas4-#aM9}4ZB)zGksQw$^ol6PqzhJT1=&)Bddtd0BP;xtqc zPr!ko8V?_qv!z*u5dqBG!MDuodxld{4t(NcI4S=%8qYgdyVT!`ae9CJ=y`dVeXmC0$dr!~+HeSUS1HEIcA0ms?!~KI6T&)eR@@ zNEys!oeSwJ6c<=tq{x8Z^BhYp9{%bKvoAo{ftfRNls>HFR2I&ih&3Rjj8M6wa~F9; z=#B1_a*0v{jN}=jT;Nekj9O*>ETYYos5)%$zwjW2J(|YSD@|tx~p{PmTR+-}{4q z>PP>;$_jsWmylv`*P=X|s1)%L%C>FC3U#cJHD?cRST(GcUi5iQtz?%i;8yBnD>w*) z-Z$Uyq>{(H4}1p6w4jH12c)?Y^CuG_TUxxV1=@U{5wv!a%w2WFr`C3S3^ESdjKDtQ zIUK=4=it)z2{i1|41@eq7IH=AAC4GS<&Dfgk#|U@j(Z=AZeVr|JBHC4K`KqhKSFjY zl_DSQfg34J^J2v!MleGUf}gf?KnNImV`(W5ax&@hz_>~aRZtJi-i^QjwNx7UrZt2j z{bAvG#>FrQk}OX{JkCVYKu{;e6Fwns0d0;s4;yqy_)UgDMB6_?_#pW}x&Wn{VzFzx zByzSP7XJuEA(vs6cTfjL8W<;vGIDi${Xi4rlaVyW&#Khja&6gPrHWtHzeVnoIVr-- zUsMBVA%Fssd)PE$i|x?lNE!EOeYvzkk7tZT|3R6YaW_XQ1AygSFAm{2O=^W;a@#sL z^MU|Q5xfORwUywL2&lfA1DygJRoCUTy|6j}$_TMsNHr?5fsAwy+PF=Ey3&T;Qc;1- zkc$vTU8oeC6Qix_TM3v|N^3V9La51YSm~bxv@8nIs(Mj-@#j-HIsdkIrLmuO^9>L$ zL&NE%qV8F5LE_|ske+(iaY!hpq54{W>A=SY0F~Td1dyEwirtp8p1N(IQC=kZUhUWZ z+yC%~S62A5jDRTL7vf_ElgKSDTtL)C3RrTO?T}ZMiaWCf4T1LzDMQ44Z6E~l3Ld8E zj7AwXS0WfbkV+s`DS)t0=nlYj=>tQg|5PDEmT@i#BZn}v#0wdomn~Aw2%K~{%Bw}V z%|m2@WB^`Ni{6K@CF%MI>|`2$CO1^leazh{;m>;?TSVk^3s@IUQhB+UZR%Y!5BgGM zKQ#y?5)a>vK7V(u7HMNJ(Av}~WsDXCv&#xN0&(z3!Q_1bf_#ir6-RE$a2_*vDT697 zt*{_0MW2iN0a^I2b=+L-%3r z`$MShEQNAp^$wz;!d^gbNd86%QOsAY+#ty~lA~j!*`jJKu%??xPUuJ32B`el z@v>lPh}gv;SwV8AAIZI`&a++y_FUI zJYFJ^h(~_m$iqJ=MywajAopuEgI?J%@aiyQ7p5icH% zP5NGr)diYsg&%ZGoYMA3=xuKzBaKh{XTINoidDg@h7`kN3P_w`G&QnSO;`IA(4J_c zRHhEXpJv1fLY4@LjPe1fHTxK}Eb$}2A$WkE%rXMQ{YiUpd_@*X;Kf7H)(0F+4TSO& zSE|K2{W~dupX*8>Q_b0#04BAvt^^UH*t7PCH2aBqviNB&=}3SB6~Uhij2j3o3FyQn zCunM`0$9?CP{Db2D1{QBh2c~4kGiFoRZ=*q0owkQ$-+nUj0#*aA+?j-6SX5v2_$gY zx-{aV7WTtMxq4M*6oKB`6~%2JZib~Ba7T2-lpy+Onkwcvsgwwh zvOkN{=e0fpy9Ka?i;-X=wY3QQJ=y0N7DeFe0Ob)?CvcL&K+TR+LdYZ&?2smu#jX2u zGN9nnD_fQ&44!{gI)SUT$)fa&oN8OTCa80E#I$Y!v6$YeguxM!$-;@mm;kUnzGDvC zJ;D$Z1m} zmZ~Wg&wM^&f|HqZJ+|Wyl_Iy zY+R2!1j$1URaU_WSF*5IF8~q)t-`(#aFyiIsPS6)zZi_&T_fr)3y%ozXWuqN%N*gQ zN!^G=KqROKP;I4e$KN4~C&_$CAX=AoyF1u%tsF^}T{!*92Ly?tA-f7AJUJ=FB$7IM zmngLmG2*ttHqTJDmibJzefYKZ{P8*O-Qt^nIGlXy`)>x%C~k})SagI$d}82U0gpuX zV|8H0Lrr=odT}xuqU(lE=t`i{>YYfKUxE=qCG?XoLyOFUEbj}8(2&7IiOg(~B@4ir z4$+k@?)HjxB*7PK;vz3A*+qHgrM1YQk*O@lGmmV^0)#IZq0d6UNV*nWVxGosGS!87 zo-p3gh?iTdQQi!cXVa(*U?Vh1cvUt4J<$xg8GtPc2s75}XpytG3YpKct0eRR6e8PW z26o61UCjm9FS>dmb`?v`fYC|Tv{jKJCH1QkG!;R@#jKq9VpK4K24~+0Va?&#F`4J7MC}aR zN=54&vH_`Fz@7|;PjQ<^5VaAs1A}ZAk{m0IBGgt9EnTa#o*)JnxJw9IQ|S1zb2^83 zMB*Z#?RAe2HJAlf;lsnA#<~njnI5=?r8yE*FAj%iNR~U*;$kz9!wYDO)TJddkz~KnZ#|wYsoM8N9Y%&aEdV~3#K>uUf~L2 z+>h78Lf8*;en)fX{EBl4Pr=31h7HB%0e_R6U;EIvl!n(PAq6x!HNznV|58;vEa8xM zE#Z3bEs}193a>p+)D&ic05C{Qy0Al_{92kSvj+vhINDyV&c?Q9H&8QTf7m&@ zOy)?NsiEVij3Q(N_GSqP4eKnPwfY3+J2DUbk&PPv%LCg0OF(G}vr!Xf2ph+di;`u`UBlHYS z6c*!Ik})J!bJAIlzx7bEFc&F|I@Yjp1xjsAw=UDuR)>(40|pOSdRcT(F>bmYGr5#T zQyphfTM@k4xOXQ^l7&B}+aAH6Rmkp_rMRML;C4wP$X0&FsrWPZ6)Jr=FKa0 z^%>Auf6!`E#JogvTQso*l#Xst(F6fjvSxC5XCs=5qcSw%Hi!9Zy z&>4eW>=J*q?uR^zQJlhUv`~ie4p3_7xu{Mar~vEEf^JLlZb_C4XnWuTI&ukBC7h-O z(kvu0j)p_YH|_7=^{jw}E-g4)YYJma6?h^_U9cQ+oscd5vd zl;f*}0pCO|=CI_yLDOWljwa^q_*|~{yd(U(ad&|<$}CQ3L86~1NTcH%=jXCrUV#s^JrJ3F*^)~hL9D1; zf#)UcE%Qh?%PLzMar1PHb2RI>{+-f>9dGvr#~}rG3dKe(SYiT(zW|2dJCMLBhok?u zcFQZS2NobeNAwv;gnCE#4jc>&1Ww+UXW?|mC#gB#24JH+xjZy1V8-_P zy+;r4QVb|yWQ7P(m|}~_l>ksMXmaX{5j|H-9a_Sr$F z1GrESVu)r9!)GuVLJVg!W|hJwwLjS%v0fd#fRbhBHV|QkjLWpkK?$7CK0}-46PzHU z6hg_VMx((ZQ&1AaRSLNWc6yW=CCt-}h7=k(iAX?@`S9nQ*=xb@=(z@?T!KN8!{qXn z)#eCkYGDpLDDe=-bS5m4O#$wx0v7Ix%m&hV*N}ckq&N9Ay)HUxb_|mPZ?QyMESF|4NEo+b zUy4~De3h7@55lWy+H!Kmgh^>=qUuzt7*(N$TPH08@j`>JcuIN{QGW?Zigu|>puNL7 zhv7a~fjo*z0z?;<W-5BLj3-c%Jaom5Ux+_PY``dOGxF5+Puz8W}zIk$^E!E>SVG-MogPJpMvGA8VK z2$#tKt4-?M5y=7#72XV0h^!n!c@_boe>$18z@7?fdEt=BSus(@Zsrcz-OTHnUP)pG zbP42K;e-KGQTJ^&Kht>t;`&HZJu_h#%e+}07)?!(C+EVtTpfM@U1u>a5n#X`kCJdJ zxJ!0H4P6^WWiXk-7)X`p>4?1|LFL4p0i4t7e7q|($tqssKaOh+_(q??I=Y>`pj2`4&!}2U7Q+6sF%OaSNgN$SwAvFb@ zd5|Sgsss)0_JHm>h*2^#wGsKn6nIxu3-lr#1kFzA>Rp%UV#C&Qpd`w^ZAe&JxvIZI zRX*Yq)r1UirtCYpKJ$RUC|xY=_Y%w=Y_?R#B+@O{sotd^QEDS8 zfNo*D+Jvbo)P8~q%C91%@bX$eL?~8HPPQG53$m*#LivO|<%(ZB@@1_gpnUkB8)8Jb zhhqr{oG*Uu!~z-mGTtQVb7iiWCSOf^+vEn3Ae>~2QM7^>N50x$yEjOHdhTvd@G%)3}k zitt$6QDs$%FxIn~-mQHs_^%mo;cL$;zKc&bI+lC6#md__)TC2sN4|H=oH_}T(&T$r zZ1mC4k2NiwtZ((ytniLJ+Wz_HA3f^`8fI4Fq+{bY;X5sC}#i!Fhm zlMV(-0vUIWR!G$5VDV=oC5+MMpzg{{W7Gw9SH8NKI2zhG7zeIJ1*XRZWpQ1F0sX85 z&*Nhe6jhiW$L9G60RWW^MSiHi+IB?ywC>9cC6yY=5&$Fc8FiUh35nDxhSV(B%OpiGa<D=cDDi51|F}-Pu>hI%B{az{<_=(@*6ihr*9bxz){U^Xzky>V%sC!IR zRZC|2rghH<34O$c_hPWWCmp>21^C6+SKj<8_#uCOY6;Tb0|+}N3Pj}kys<^#WJOb# zWP5WkyC*=L_=xQq)bF1h^nbM}q(E zjsyC*b+N(25{Gztfq&wBgj;Qf9YOq%+Fws#orZwI=pGmoD=TE>)~O@}709lyt=&F* zG(`s9=p^Qk{F2Ve+WydX|9AfY*cyM9A>0b_yXd4NqWXfuZQR|A`z?lY&-)H{dB|FA zFVsIIE?}Xn9v_Qaui}1jZ2p1^^NU|Q?~_l0r7mb~39b&gZ)sJQ*RB3i_doj=e-x<4 zpYJY#eaTwxqttcW=MRot6*e~m`KWQR8EPU@$q7{OkTC7yD3yiuRE*-#YJ}pq<@i)$ z`IXJY&UXQNzwFsE%hx|TT>H|$12Kv}%MhiuG|1|X8E`HzMuyD!QgT@6QF&2% zLRk;V9Gr1E$ewu#-1B%c8Xlme`EUeIO({<6c9>*UWo^z)yU^vxJsXd{>WbtZ$X^r@ zt+X0S6r`{jMk27*F_V4~?>0D6ou46eJ;Wxknudlwa-d`i2jNQ8Y}9icqumlr((ENT zz9Et*#FtS`b4T@+Fd}HR6DF`Sl+uET0@}!=E~9CrTP?Sv+5ueUSR@?ZD2dQzQL3An zztnks&~Uk?L{_7S!!0c!sGUc;SU{y}jZUXFv)x2gRz}GLZ4_iEECab;u$XFcg5^!% zK@daKhn}%8z!7y=O+RxOF`CC>CCb2+*N00NDLEMJ4pIV=}6I%x9eJ4+DvZO0dwq6Qv0uV{4y)dg{K4f||Bs`+c4C3uq+#^znyZ!Jp@nK2ra4ex{B|7qZ2|^_-2DKLqHjKXh|*vRgiOG zaX=kq`k7!)W7szdEJG_mVK6WVfzvsRuW(0-ZHStPykjRiiGB~~461QVZx)=^PC?ZJ ztHxuCm#k0)5q21scWYYZ4fB;95WIAPsSq0O+Z34;X)lyTPbBK{bWBOpcW^@aR4Onu zrI|Q9UKQbgnWP%b0JcJSCzD>%jQd?Lj$;-_ilo@@c6FJ9c<$M7zhc3Y@<_k-#KXhe z6Y%c5&E-G-#@Bx2CqRJt^TG0nFbb}mB8TXNky^vi*@csB17>`l39A4((Qri+12Uf? zt1!sD``sf*XCjaZRq$jl*Mg^0QhCLB$Y~A%X3$g_T}D`uuF7my*%YpnZwtO(>?#OI z8Cgx{N@d9Nh-zMtMPD50WvCWHGRgvRPM-X~(+dO%ney2dB2&66s4<{bV|buES?*@7 zwf$(_q`NV@srvI5q&(yKEm%P#d`cNgwWk>JTZFRFa_EUr)iOnBprkTnKPL@a+EX-ZKU;P&w!CNtI+VDS{rVMG=<;HJ&L#p|k)-N@vBG z_ZtnF?C8ZzrV)x7L&-l8SUQiGLYjqfSd^4MPXRgz$Ya3TXewOq)<{l;$RIiBpO$GF z?VJ%3HKY%LTDKeY2fqQj?7yih-p;7vwu^*5XN;TSYqu}h2TprZ!jZR1IL$1f8Gqr2 zf9x;+S&TE`(!j>^+=57SJF+vYUXId{_{1_ymQWG^T zwjt4wWyQy=(tJ{Sg*}GlQBM}Uf{w}bUXYxzJ{i9~qYq1Hj1@e_W($Isr!AC~=$k%M8FN<=PJ%wcJ1d0KX}-<_b}&p;ML&oW^r z6LN8Qqkkr=2$Km~aXXOWI9k)K`K*ad;;F{**_Mk^TMJ`l6&CN!2zN+O+G!h`dD=a} zC-_BSkAp<{SIr#|`A|Mivh0U)4?rMcdOHF#U?H@SvYQBOx2VW$hmUiT_V1zVhQ`_# z7r2gYH>{J!O;ft!qs;I=wo4C<`%J+3cdPZGBA7nlb+Z6DeGe z_X&Ez>4xcnZ1@1CX97{q<=k~(!_5y4{5DESWBW9l9yG!quhy=YnNYD9J>q>I;hNyZ>;o@+-e@MSfb#5Z9j!C&NgOFC3eWODpBmWKrGcZJNTy z4Onf4qlQNTabpR0Jn0=ER0VewlrF~5lV_mv_p~fiVEkmbD4$DVI)%rveV->GYw~ky zA=^%p2;BM=mtGn_`S<>{&8xoy^x)4j!gtlA|HjixQG#R}5 z!*~AbfAvqTtnggCqq;}lz9cdO&O@mBOa%gfzFkD0!I0W`WMY}zcSPCO-{zl>E1n&porKqd* z7*$M~Fe7n%sq$h7QemaM!_tYuMdI7qP;5@Tjqz<(y|@9*M$N}3!Pl1vw5m)6X;z$i zJ*=Fb126qUSbIo-!ri|Sc>7oKN4ZcdK*zYw@N^%y+|IyfiD37s;wKgJr7$l(gyV^; z#{tAQ^E3q4n03s5z6KmX93`jW-$d}o0r9i)+0qb-xRU)d%wW-e`)4J_^_6e^cfa^| zURhb;&vH2B@DNn>3r-15HYQ;fjCBz{iz*ExfiE~^uYbZ^&+oGo-sz-w++e7!anLOt zl}d%Ahuy1v)X;`P;qE7tOFu&PCP+J(WP|KdSY{J+-NRy%n0!R}!HXpd3RwZHDQq^6 z*q}*o0^at6R)eD>Cr*+!?hfNhk*gE;~It3Imxb%sLGmFPqKOxa`6omHi9{CG-lT4)@Qa!FtxijM3I6GGZbJ74N6?~40d`}y- zJNO)?Przl|gJBc{U?Kt?)LlX*hFV`44X!7g6+UJZh%l^(xK}NE2$JQ6aY_m=)k|ve zK-vq(n73IJ5e2BNz(jr_n4q}mvA2UDU}K%{)96Mk8}UWZXQUY=d|Jrq1a{YZFhIfQ z!``)m!RgFJW>F*?RJ^yAGL5XhA~SESYnHR3bCHy`A<0C`y4PL9E; zH2V%Td(4Xd4t-D_c35(lU7>-F!yn=xP!E)zj<9WNL5ICA6WbgDILdIebYdK1P2>w= z%s&v820%(fAmgZEHc4|+*3vHrS%3nuFIiU}Ys)}+L$;fusn_~^H{D{iMca7AG&dAF zSOO}gHp6?z?IX1PEJUR}rn@99Afbd*qUTUiorRFu!(77)Ds~>Jg&=@kvD85w$Vn*o zoo<%NCGUz*^eE{PQ7rCVB1qr*-Jkl^`^X9B&r;0ZqhbFdo4u2+qopEjLF5&xKi|+O zp_GmX-K#RG6KFGJV+Vj7oLie!qvraLrZ znZeS*GcXc>NKBi8a#>189txtFhEDFqSE4C|{k!by39A9Q1d3{#6s$hDzOlOT(M zIGlQfkak*FG$8E87-qI7^3EFryi|Y17`6}bG0zzI&u4F7i5`O^WF@ZSUF`4n@Of}w zP~9y_a|~8~d2cl1%b(>Id|6f;DSt*wKGzn%{&JDgwxf$uXYS+s$qa%l=iD`8h6oQil5ho)(AO-8$+9m{0e z+D(fd1Sq8Hr*6G`n?&nohkAOAzGTJ^!hs1R9H4Ouv+o4v4Oo^3`VH#4O2Raid5^U= zy)@7|*@{EYf(etIjU@R3y;bCG(uo#kt?b{gO0F**q=md*9|%K-c>c0~Gek{weCZVj za~WN2LH0uVhHZImK;>+DT|_VJel!N|2^i&%5)ISFWTw1Aaup~ty2ag#{PWLzCGKj_JxXlQkY;OXRcldLxdVypiR46v-+p<_bL$ygC zjK@%g-s$MP%q6&)4#*>Qo+T#bF2Y3yq^NtR3~>sM23|3TPM^o;-~1!p|K$GxF7xN% zdBcFm{qeOs!@(Fu+jsio9TwdfErP;$9y=ymN8A-E+=@RK)%3=&f<^c62!3O5c2xG~ zizvTNSX`bqe=GVyPZ(yAndmTtuG~Eyo-ow{(;T0ndu!_MFTvWK?ub?fGCwrB zUfzS@{;+d~Sr57>1EVly7LRxu5!Hox5b%63K|aZ|CkP9WX;O&kawQG`veQ5yA}2#5 zmu97zvlo(FZ>0Btv)2}SgpNkxk|6rR&U6k1Ozw%AN@q{ zTInkGhrl$%VHoLw`zd*+g|i_?k9I?yDu2EH*8lc5zV)YJKH$&xi(Ibs&_{g&MbYX?)qg8rPaz8+5n2 z^N0Z){azCvwY-{x-ro8;!;uayHu}b~0JAFgmdoMvw z&m^Ep9tL&9)*Fe|PFG`D(c80shFe9q9l{aEnVlk}bgx#oJ|~=SEL}=2li(}P<`SxNf@C6X3G3kMeZ~%;hAxJ=OP=gE;N-N6j+^-reZ0_30A=cnC~IYA z=SSQ3p;E2Rb-b&_9(}&dL<$g`k6y{rF%b$rlhUrT4LSV@^9C9eXGy67qdFR4F!mIF z*9DPI$e8#z2E{*|k_bU5B}#LVMqajO<63fk6UxP_X_(7wE5;Ltn@kTo*T{d0+}KX1 zQ(vvT2PCgylT}ZHAxv<&uC~rj5OHRzKdH*K=U1OSdv=vK{pu;gbq3@vP=MnqK$jZZ z-7#uEs*(FPRZ`zoa>Fbl@l!Ho6w7!}ktWg|L7M@3bWy!)nF;-QLVDoUj2?}RAs^*#A z=AU}E{-dY?#-DTh*g^HKJ?M>H3hFL_?Frw?^E9EDQa6wXU@{K{4+NtzsS>6cjZfTp zu!FCaK1&J$LLIE!&<%~1k*SQPBXz%bOe--M%vj2pK0zNT7fxGv6=XF;JohPw9S<35 z0n9*xZ+M?C1y&tM#GI!sIiD=4ED@Akq^3@QO^B^aHef4M2^02P3#*rKNfk5^ivz1I zWny*+8FDs=iAWG24$3C5Af~={0RVj0M@S72T};W`7OlcqHJM|Aje7`>c}jCJ;;T#y zmAN33L5z2)lR%a`Si@U`uL@5oyA+VWx%N7lzX1R%L_8IsFSCUc0C$zJs-q!T)xc(h zn5$whnjE)f?Er(pi?=P$z)JCJ9u}5iId+o9OQ^=&I%%VNW;kMr0tCdK0qRGHTq`>+ z1|L0_J82SdIr2L@vUjGWM!1S&6@u~!nusx-Bx)+Y`|jiB$_Zq4ob8AQ#IUB=fDG3H#98z`Tn&jd#gnQ@$vt{D zJUvF=G$L;Y-OD5h1sanQ_iGtF)38?6M=9NbgO{v*PRtnqa#3J;f4zQZDWiW;I{fQ@ z`{%#>V_#ZX;m`VuC?C#ENkC7aEl@2_b%+v08svKcWnzHpcVOV_!u&^gkY{w80p&_& zX37hD!c3jW@TxKEi6Q1djII(jKhp6fVp7NAy_vV&ct!8C>aMn1lb2k8v`>Qum>|wpX9LM0y0Ok6nuU$%n4}Pfv2!G?hWN5^IhKhl8fy4(~=ZBEQ z1bVBNFNdjbUQ$1a&J_RO2TYrg8&4;Vq$IA-G<+^`GE~UcFH_*VjZ+)feL+=n&=396 z&;1Ky*wy&+-V5Hp9VHe^y@7JeI5ChnLvfYW<5t%YENdl+jIK?FrwGEATSE#uFwWkDFqf_n4~%3J3-l+grADnX zw7*!W^Enw+!pa{%(M-s8U`G&@j$t&ze-sib&mcvChM?$3&ro>@8-vNRjAi7h7L;!u zNQo(q8#NY^wZMgGap~|DJ2nkJ&YY}U1#u`x=&C86Q?n2%St|0JVx8Y#_)GWy*T01d zPyBiCB6!_;);sPE0|qSFo^>0^iEq6TEK@1NIT|tcwuZ_mCN`sh!0k$wx z=cbsyu0g$AySnzq)wS!_n%0iu+Q$gw_~e>D-1z=@I4Cz~QTl+UPB*mP{f)b-STN>d zGGw72Fsb)jt*sh>M`JEpBtYou0kc69;bIG6qK?^X!y25$~~^8g-sZ+tx(p zZD13$&}D5`_MNOvtnKou@Ft5$>M}F&B?P2$Me@O2c zImUdBvjHCwm)RI*RuLEyml-bME}cx^#{6~M1>c+SugP#v4UT(6e)E3QRD-}H=a{;{ zL`qnbwzXzfarqq<7jt*NEz$y1EI?b#ieQYwDfjeRND&yxH^XH@-ZCv#Y$E ze+p+t;xSbeS&Z0J)fh_^N2P$i48sfvS&?-`*i?!@9|=-74Cq-A$iA~v;La30rK9v* z;Mx1AH=3Nny=FoXZCViSj!&RMoC0ki+o`OBavmz~2qkin$!;q6h^qX))=aECq7?P1RK-0mO17{G2ZxL^&`vOmT(;b08?;XMJg`}C> z27@#sZwqoum^*yJ!%10y$|&*_1(am5WDfqVE>H)giw$Eq?WZWgn8Owb4jVIrGT7|~ zcjYmVrM~(q0#JY4KR?|cPY{i9gepe08~S0CzRo2Hy>v+gSQll2j2n@p5Em4jUr#C< z9nRBO$w$U)8?Fg0-#O?!D@_Q`{=^@_+6@ z^N3x0qj#7ru>tZo?&^2J>q*I{Id-#{$dmO|C(Wq?m)rSD-M^8*tu|a;AeY2}%e9~V z*;>7>zxtrd5Sv&F^{gl~P2DjVqslyzt1Tt-Y|3;&(ZVNIQQNcfmm9VGQWD2`0wELH zZYn28HwD&>D^RcFznAq}gN;6d3xbj>XkZwx0;d%qfsHlLqz&qJT?ZFLMh-QxQ4~gc z7T&Jy2~WE!tQd@Z9*;67nhHv1(5W`BE>YXM$dn?wCaBA4BY5?& z&42x^f9W^>=#Q+d@aG$4wkYro)E7XI%(c$`wNQQT+IOz+?wXE(dY?aYjDWe|1g4Ju z6k$ogC8>r(&Z$-t@bt4GRCFrhtjUAuJGGVCAN-QS(m zAB_-G2G1}g7yP9zA)g*1NO#bNTe!mrKDKXDX97eb{0-8A_7o`LGvN@8=(FIhP))CX zFu30EfsHXNl>IjJL7DS%3@G>INxc&fJ8j@oezC6Z<%3ZVg;_+t!n(X@i#VxyZFYX* zJ`D5d&4xO-N24zOq-;`qaE9NcrVwU2LZKcwVzC`iPV5;agb;j%ZNXimk#(48(!&*< zp!6!mu`?VLMp06OJlni~f8$}Rws!5h$UT@5hH4Dw_x;}SaSuDidGIjkJ12Be5Q)K! zII`O9jrZ_$&@1Bt<}7Jf1MU64o8a5nS8&iE^t66>@+*- zBlIpgz*>Et>m@HbW<-?_D89&&e0JdtQ*h)Ka*W>2r}XFf6yL}%^-v2zRIQO;@4^-i zE)nK-evyOZ8Y4Hsqo zz4h&Y9e!#pUMZmpcBAy3EvRUO+WPE;L34##06aU zGn^J|twKum1)@#ym=JCTstw8q{%yf2M?e86dKLqtIwRkPF`TbE5ak#$scMy&Rah03 zm#5wC2^E)Ez6B8_qNB0yQVtb#-Od?u=&&nq^mBYGImjZR@QDi7E(L5R1jg*Y4m0ka zwVk+h2w=5M7XBw5*|x1#J=H+~K#oK8PYq58#iPJyNbacCf=r?S-7!2rLk2S>Ar^W? zfH<7XjtamA73Fzo&LDn6q&T&0F>B?n5EX%4*~0kPCWI16Sn)aha_E;tuZAz(n{tliywL}_|!d;QMF&IZ7lm0X#xKVt|T zFT~^-!?;ZtRUp!p%CE%c!O<=o?@aq%eBSvHytTMx589Ih>5vohu>{O^R^c>4y##E( zH#wC63}nqXA5?5};2Qt|7yrbLL%%@FQ9&)fz6n(&US^UDW58NX9kr8PBtSe_2P&6D zk|921gzhh}RRKFl9uRGY-#8&wFH;H`$s; zuxsXRANWS)K;h_TQ)V>HY?ll(3@gqThJgqj&oS<-i_2Xuz4%lI!{dZ}zLp4~NrJ#%aRr4MnL=a(DTYanrSLn@Tk80~g2e#|D6Km%ut0a|>Meu==*%MZ zrA9M(+6dMOR#GXwR&cc-wC^-?8+99K zh4l3FWEYfX78m!Cv-!P^bQ^%bJ!4Wb{2vWZ0JC{s-|?Zd-|s``Sim9dO1tdlFfPAj zu-39CP$X!t=OtN~6OnfS&IA({^DSq{LLJKnr;|y{o-@W&8HbxAv`V6s3{ z&`qLN--`&cC)v5UyfAt8PH;h`aiVAgia{FONC;t?J&duB)zsxQ1vC#DKl_zn5y|^$ zS!V@wdn%SlgWR+kuaZm3X-&&^@OQS!=Ahk~Twr?Tr{q=4Wb*6RK#a>n3$1HX~`k{368yeo0J zIh2*nIfb&2%p&9?lSS13qWxEdEr!M7{AFOQ*X9kFC|+@_7+Ib`>S2q73XXR^-9G?e zDn9Af{Kgfz<4o7?Jf6xY&37Nn_e^f{v z%vW-Wg^WRo0}YheUMN;j&v^-vsVFFjOk{~Kgw?1ii>uFSE zHpeA?PSrfA`m%%y)S|<%t|CFKMpGr2z^Z0I9b8-A`;p{y(YgsPpas7`w3+Nmj&9W5 z1US&6Lx^)q%t>OWFG)ww7-ZOv*~mil5yf=!CbX>0zFTk?AYqXSU^09UL~<865wFFD zm#{uy3&eYKm_zZ|N1I+w4~Z7?jWHzK1NJmym0>H@Zbb#NZ=ghgA-V9&?f!L0C8j;d zRwUb)x6@B2?DUFOwdZ$ge5Tjgj4Qb79qg29MV`XiJdx4Nqsn9;qA*EK6kOHU6!ZMh!vmd95e4N{E0j-R%&>*CW z=Nl22Nzht)mG>Xi5n--`D*r-6F1j~~iy^Bz`+Jd$kFqff>d*6>Z1DnQBs{(QagS9} zr!2qh0Q?F;x1qPn8KKf?IE|Sdr-aa`gM8LUagidDf2nBXCtl|roS^6-p& z`4GE-zp{V(s6-+ZN1jP2Y^8R9ASVzzhM>3wT{wse>AWsdu>vL(7}CMwQE|3YyWXfh zq6TYx1DFO9N<)m2Y1Wwwp(#`qAp}_Q4zplsyh^|*J8zJwRWh|itX7)tN^pcv3260R?fbPr;I)s>j`Jsx5G?m;H3xK{B z#r$aF#X0%TJ3X<^0Rd2N^@Pu7o(lRgd?wUR3p19JsB~DEqI{^Gq02AS1T}4urzs?x zSS9@_A*2C@G)y6|OTu>e)ghujba8x>4VVQ)%s1~)EHJpgeQAsg$Sgq383`KUMu?S# zE9A$zLL8EHJDvCKvkY1O{WSMN*4%ykZ~Xb6zx+chEBtwT5p(xjP=WN0?t&5|_HJH% zd_pyy186I|t%Y&_gq-)>QC z+mty8q&oZ3M$&>xUkMqZ%Lk+h|6zOB^rovQk$xQRA&e~Xq5y>PDo}IIBPhuN{gL#6 z#oy7)^_A~5?>}1KsnxGvsjbQX!Y}%#{{JPG$;e{hkN)Pb{_+}XXz}NbMKJK|z(6$E zNKg;O1ccp)6U((B+&u;Y)i{=FT`0#yF4d?Zbp}X<3qz!msI%dru`)p2%HvR0B zA_tsD=nQ)bVnxAToR#^nuN8F;x?O>}{UDp9CJjZlh$neUpGs;PPr#$GEsNAIamSQk z;^$H9PZZw!3=V4|kme(4`5M$-O1KQwv#xc~x+MVw%D8!)K~&%|;Kt#kJbVtPni%|9 z(N@k%Q3(eJTL!~e+`2JF!5}hROlmYsF8QgE0LTTP`}B&+w3Lw~QjtXN3>WYj<`rK@ z6vr7XRU1Q2><&29pxZ?NI2bYlO5k^JwH5-EviKp|a*{BD(G$@c2o{BlmIi}#C%$3M zCwh8?KNe&O>9i|qNXfxjfRJ7yFZBbjut0EngDnE>M4x4Gy>Q%V%o z!*0R?!YqlUJb{6KO|+jeQ@H-+Gx^sA3jAmOM(v;5`NJzK{8>l__{J_QT(}3)jv~f; z#m`9Ph&U!Gd1Ys0G$!@xpuuAWhliluAO^&= zlQworWd$x|xnC;ub3WjodNM8M*VYAk;WpJAQYKponurt5l;#zYa8~Zb!D$cf>FC@^ zXH(o`zY->=q&nc6YXyyX4j1kmuIo8M*#JM*x`G%2DrLb_bOl-HCJd+J(=kW5Z+)f~ zj2XA|u)u%KGIMx%195@G52b}hJ1G1p9;obWhg@NF!o)40oyMAgvZZ*0X_;0tI7M-* z8iLD6*;(2gpLGVkqbDYu6T>73JFj@^;Yj5Bl<#2|gWb*EQa%N~?e$Nms({$XCU68~ zA0ZD27(o+Oxli!>o|4Ild`&S8nTw`(%4fSQaC}^4lCx;S$q-Fr$|;+TfN~4LDM#IO z|6m1^VM;uU_-jE)n89yM34xs!txxbPMPZu;YBs1P-cbbDz#t%{IR{SOsw}}56 z9D!6;O`tWya?j(U`Ew*UkKtsCjZf0vJ>arJ+Qc29YLfhrCtOL3xOz}LrATr8 z(~rZ+|FqM{IMFf_C;e9nrl(|}EG#3#<-aTyL@y%O&|n4r7AFzfAc7Xk zk|3~zRoXzzDe+$+$X!xLu+Iqh9HJk_z`#F%$f6U1HP9oH11?vmb*ku7p0K~1KcDmOyK&>n(IU=^)kNGHRS(__f$m0#<@ z6nr#=qvuKGj$98fchnK;Gmd?Cs1nA4r!VWMkjRy8&%$={u4(2^N7gxEqTARNU{L-g zoP2Ax*eoP)&M@f6K~gSdasq5J7^6&;gv3QFTCER;=&vA}yXb|gzX=mC9f;Hi^dI6x zjA`L*#4DmL48fcrSd?i-UFI6I5;oKeb`pn5N8y7@;Z&|Dm_0s61!3Gkg9Vk9sgLn% z3BKs%owaUCRdZ0$qLA|Kq7>^-Ph%Px%nJk4c@-g&aT(kwx$fk{Q8X^v9}wCs(#tgb zaRBZHQ&+hpV>-ahVR@Tc`E0bGO`$))L=mc{jbZ%2lqqIzJh9qMnZ^~KcwqUkO{nnw zp=LX}09QOz4N%j{jS*H2eqx{27cXMzrWtx;fME{8~4}ml0t2--+P2ivb(Nb%^6xc zhnk}suN2>vl1&NTh4|k&t9(2Fta3!Pe&A$jR`?C5QCSpjfj2v`bj!O3;eZ`wze=Mq z19;-!Oi0EFppx* z&UZ=-2h|Z2g?pLoBoA5Jo-a5bBVm_Dm88S=N1(mnHj3!;=+lYFGZoRRH={_H(aZhC zw7mh0VtfT!NPH_L$0lS|D;TXQf8#b=y(D# zg;d`cP4kvU>GLrF@Y}a-!&2G|C)8U1ZcAM-_ctHD$G>zdWG4D9JPEM^F*h9)JOg~` zfyIks?ygn3@T3dU8BO#A|g@H_@mTK#(l} zQNfqF^pbRw|NQX&?|lVvR{VKqfk+p-yWd2-3%xFWVYdVK7X+lRT=NT@zZx->N~}cY zb-@7&O2&dKjvl~S*u2F3Z?4jCn@|}O3is=mtsR_1U&)=FbzhRTo#(zh)>f#qGbn^Ch_W@pFFx)PwfIvWH*rjxTt}7rdhoC^sMP5Z3u3eBtV?g!r$ zJ4LCP6lM?vL~5xAHZB}%|0c|U00Tr5zew5ZC`&YxofXz+s8abNc!kXK4yEMd+c_u!eB-27*3>7+k0=lwWlR6nBKxH zS(>-dfCNPXq@o9WToRVS(-a^^?-@*?miEPM&<0F6s<%}_xmZ=z!X`$&j~KXQ7b0!+ zgzncn8}B__zq`Bf5bb6@mbWp7b`@)@UBk`1W|#SzmQ;{}BCE>ztHw!>hte^QZXmLQ z!`{*KF&ZK>o<7Ly@y|C3-7T)7OWBjF9)(rr5SqTg&}+fgdCe-PrVnpDn(tIf#uZLwYjY<*8R!rD zrDWb%7zOa4VBYjGOtzXm0!1tP!;dPaN>3qoYQRa&#VUVAz%s+2Yf>s>k+HxNaDT8L zmf)yZU*RqCjJ0)`^JVhLG$4p`l}%X`ZRO8vh{Vgx?>I3V_u`S4kB!Kec|kydil?-s zt3C2bww+r!(XracI~6p?{4>{_)m%e~>3(~J{$C{o$_L{>;5{m*7uhjaji#m3MbKo& zX66gmDmx}O-&_!A-hF^f>WckKK+H9Q4x%R-26o3k&bqfZzn-o*QfTtvYh*6tYaS|f zD|RR};cUvI38l(xH}{5gm{YgGP;&GO(&nM?EBrcE`Cjv@i&pnH7Yr?vi9=4mHI3eqgpaV_Nb-U|AS{E0y# zotQ*l>r*?)d}Abp1G4fy#2%>`S+}-`6m-)bkLV?euA&Gw#5Lo04cH{Q&o>Y2mugJ- zr~JZy_Ox;f9}1d=Kmg!O9VG%}lHwC0I@;86yuR_(+V$jt>b2v2l(jVvuj7 zi|y)V|GvKZ@h9H$&-%~#Df39QNnmR8TU4uFkR4!m+=#xAnJobk@c(;xi^s^ut$Q&BGzc5p+;$_TQ9C9ON1?vG67en z$t#)PYH4fAcXV2_>GVX8l9mHI$Qtg#8*A5VU;COUjfhl{4_?n4B0IXnwMA0agYaU2pA){Sw~x~4wX_Q$-m{CrTK3hr<(T$x2tW_0?^wL!#`xQya+{}<&BM)LcWDkf&5UVf`u?2( zdTjqqr*^U$r7o#8u5stlPHXeQ`u3Gt=HspA&d%e_?YmQ9<}xPt_d^nukg>cxN3-!5&>t>OM_aD%BfgI+FuYa3zu=C$JNP&_TGd3m2(f3F`TbDf)f#$|t$9XMf?Z{4@XK zKm4OBEBv|pVtz8RyDvOs>Pf8fkQKaRsmp6o&)SS<+2yXm+GH(lY8f0%<(?%TU-Vj< z4xYXuIxi+LiRAJ4VYR zmRrT&ppRfckhj2r#N@&v%|1)xl}gVTk;s(ydCWfK8WrRGrg)YG9LiHhCuolPxNJ^} z@$!HhB1-HC?Qy;2Q`>M>$4-@DQ|R;7ffB1jut>~nIVcyMC^BuMX`snYsLZhW446HF z0?~TXZJzYz5|~`#p3Rl#*A-k1S-QmPqQp{!ydcrQ=+c0o1v^N;j>D(HZ56Um?^?gR zN`r-IQSK58FB(LRKm zx0^~Em5Z?_=Umji;b^~yK6p@=6LGb2auo(i(Z=&^IXnAoK<3u2Fjxvat<_?H=jv@h zwf3DZtgp4LQLm4rpHUljl4$Li0RyrB3k;^OG7AFIApWDW>S7p`(dej$#R;h&Itp!} zs!+~6eVXby$1()@jHYq&%87F)EPpQL1iVy7Jc36%ch3d!*=w5n6b<17nU^?ibfWNR z7MYlcVJe&$9YduZn=)D`ZRvC1O7>PwT}=^N%Xvzh4COs!Il}6>Icg4!-R^kl#1`wq zt)mS4!DMp)F4Lk>cVqBA2;*GbhPO*HiEq}nxyA<5V4K) zi_xQ$SNYUeeC84()>8QgSH*voS1x-#vhu1Mmpz{@|vQ z3F?RGbiMKa-BB}#Bj3EyM;I!g z+9AHILksT-JRyH;2Ubjy(96<_u-+(rR*cG{-f>~9*wzJgOHO_%AT%Q z0=2^dGbI8$aZ*wACjOaU`F;QBNn>S&KkF~xO=JccT?tEcC*DQ{Jm9q8itndt{pDTRr9~3CQ zKY{vL5Qfekg0ad&;!)iKGZ_waMjnKG^Xm0l{Y_SyUc0__^_%!7-#>#%cibA@?NUA; zVc=@Xt87DDCKhR~6`AzC5#nmatDs^i|(I zJ~9X6iy3Fl$|{z7SXJdFcREmtP}*;R21N*#znKC~uB1 z22|bDczfamB>MsThoJwNHU>s(2lfv^FFYw1XT#A0i`bZf(rPy$%pW*+aJEorl+V)^~O`HXkMl zvpnzgp9!`Z?`7s#qtQ0=aXjsL#l2F1RNRg&su&MN)!P_vF?G4a?5x(4?i~b;l2trz zk9Sb@cz*2pR0*g^GI{1u6qr>sn-_rPo&=)P7^TmSs5X0q4@=3gT27kFBt2(CED$y0 zvM_$fjh7=Hnsm7%nW9l#=#0>QGzbb@LPnc|hr*?(xd5B`*li+dww!hIlpuu6&LRjS zS{$cHftAu;cqkuls~eb`dX%3v59M4A9~jeEs0T@GgCnM<)54hX=ny`MgGgx9gpt$; zqJk1n!5Uq+k?+Rx@+fRH9=DHB@(Af5(4UaqYE|1wrWn>ul`7III|KP5bvF5e=&h=+ zc2Jpxw>yo*%5o-7kStQa)Y4V;x8Q2P*Ooyn+&3JOQCz6F9bMfR_4e7|w$>H`%h(-B zaz-U@1Ui%aqHTS$(YTeAi(F^~g2&?lNttFjFbK5)CC`l%;JswaRw( zk=58eK*Rd_>Z7f@&DOdaf3-K~4$4$ej$s4Do8h;swYSK-YLDMv-(HWg%QtI3UZNGj zid)kd7Zf`Ac(r8DpA>g!cO}2YAb$Kna!NhcMLFe5U<5yq&^44=6uddA9xxEMjPZL3 zY31!QI!Xy%cqW`H&<;jOvqZl;jP-O)X9m5ZNRJ9CqYf@x`Q^Tt*Yyw~7~neRUo^^+ ze$*SDj&~J+W2HCOUj~I?Ri3X1kQ+#XMgCr0S6?W`^YU*2x#=wJ_S_P=Oztkof>f?p zR(%=dX$S)*OeXG;+=bvk{)?rtUOwDj8q6HnD1DaGVhqEp0hSnd5?~2&&iJrJ7F6RB zt&~>dMs?_5NjjptjY1p6wf!yz(y(+0-uVQS12v8hgD7}(3d2Ene1jzCX}6p7dV7N0 z8CFCtmD(^l<+j^0_qX{RWQO;2u8qn2?aqBXc|k3K`==dPH$E$&ZN9e`MVgsH8-ip< z-r(<$2w+EBt8dYCtLmFpB5pires{OoYOO!mYVBM`VMB;$Q6(c@l~;s&qpA#?DrhE4 zqH;LUV^o(FBLsn3NmVTPOlt6HGpv&1nP0wCQYLx8xDKXfkrAV80<|kW5>}D~z;QWo zDb>7z^NH}E227Pg7KA2bjfYeS!;&Cdphzx}#c-rtwqRYExGZ6Oa+uEed@&ZS<-5;^jsxESgrbBgExc z!c2ZN?aM6N_m#ru`vozDb6D|;FghxO{5G=YOqoW9)Rby4+g*l9Hc_V=IS@5FOWVBa zodJ|QnylAlO8HM)@5_@EmM$SwO9q0IK`zMfdWW#13qa&ibf&cfnJa6g+EY}&fnRgM z3P}Hh=4LTeF}Pi>aI=)VJBp#~Z~xqH|Fye6jG|chxqaSH_PelkF&gJwaqQ)hy_~KP znq5A$nXg6E9F5v%>axKUlrLMoK1D~QXFz;}X@_0a`@PP9{uvY)deT#e48rxIyAm7@ z+NqVRLr2jL=oW(av_XdN*cb)h@!HyJZ(Ut`{VM8JfYin;_fBk;#U#W5S#)FOf_cwMd$Tx3d#8v1{4xTg*MniP&v7Ky; z@7OZmZyz7FhU@S%VJZl%AE#RSYp>sU^P4x`Sff2vSNhr-?Ay<9t2sA1^h7nHl<7Gw~@$(FZ5g648mmF)IQ}cOR&^X1_+Vv}dAO79H!gu-~pTx@gB>en$m%v5vac>e{g~$C_ ze>?b;e&jJM6+r}c5DXmX4K@c|%)SB5p*`ViGLT>5c3JC=Tb*-vsM)Q6NjCR}eB_%W zT!6ys;NCQJKS#NiW7Hjvmqgr^I6@vPx?vN<#qx|Z4``uPU$sqM30EYy+8xsN+Jf@+WjYJ=#o z+uHOlVkz9po<&PBKghe2;oPrG9wY3_bHhsBXQ4i4{!(=82=6Rs##z3d%e1nTp7<%O zaawL^(r8bH{oX;NDgU{x87bjiv!B>Ng&)|J3nD`b(9gKCV>!SI(u)f&=gNO-#Wrb8 z57hDk^5D!;BWBE$1eA=|WMoo#(n|4d_lJEkoIsutxoH#P5=E9SKV~&WNCU9xGk!%| za#7=!0l_6}$y5A71bGy#r*x#lAsbk>g%qi7&@YFG`U!+RP;J=sgue{%4$*1FFH4;r z4tDuOrb53opbGdDtKYL6&hVHS8`5}<<_E}W;;yGKV|5>8b6PQ2VbDj|icAJ|19RZz zv|ivXboU~F47;T;$YC`kgW&Dtdf+aS#_^;J3sCIrhD~5~IuDC@%Pttx#AS0r>_9$y zzt!4;+2jlv0_b9gR{|RQU2cUC=yqg91|dWb4aO{+=8~AqEHQTx*8UPa1D`qTg<~5G z%PUPsbIRc?z{J8**_1{1TkmhLH}A5U&o@+y6H%HH!07}>fK$RDgK9_%1dWhzf}wCi z_}~A}-n%Tvd1dKhim*rRaD)fP^K7`glq?a6J4vv_l9B)@p#=gc02EJmRUImUOadt; zGSQg{LTDY$9C7>zXY7$9Jn+Z^hbJC5JU;zvTrW#p)JTkF5xC!Ni!RXhk?PI}wFS z_3Y`&)*K|xM>`EJ&L!oZLKsSN*mwXNF|C+*eZO_u>$o->m8lPyUJ3vL9f--Wxuads zLv&$5m|t7ZTs5Lz_jHI>R-!*$UUl&;t{=ntVpRe9*E`&`^H#SH+adjfyzol;@wr?T zTo7nBK}}FyN%?|NdAax08>j{(-^2wd;(^9cSlu-qWgo|*PUIp3Htt62^Y*cHudw*P zKPz$(0SYW-v~ty^YdN9|b;lyN39FP>X|s{=BEHNPreE21ba|o{xp@-hgX8$~^#M*~ z2rBMRML)0AuC7m!Qcx3A8D2AD{^#5?e3oSH79EG65tAu2`|WQKSQ#m6ZbJSmIuCs} zudcAr<6989OJuMOhZsqn{AIU?rkHrw7qvCfXwsd!si2@k>#YCn`9RDXg%&^eEUkwX zFZz8=ms7SqH;r0ZikHJ@f(g-$aR2I2G;X_{9tz+T&rm#?C*Ba)=SQU@enCFh5Q6+7 z;|ax{nvO_NMBA*yO9_+R;ehN=6dxE|`yhkvSrDsoLcL@GU)hVm9rg&Ufs3#%!F>Tt@B#KVug15CFkg1D?D?b2a;3*;o zS-|uelB|`5Xr9Lpj(`6VVRhp_|L{~UIyuQ7|C?zoxM-G?|{}N8!LjE|o z&EkrXJV-}0d%$9VqQ5EUaa4%8_gEENXMKpA-tH?!gXu|&KJ6kPL_Ywx2LX1gOieiY z#0jm$#>B4sh><|WpiVrV5rg9x1I=toj^WmpqjAO5aT>}v2Iu>^kX|~W3+kGa8_2m# zP=W|Sg^&=kv<0+zYDZ{H6mmZfAbM<=#Laz75xvpe+S)lh+1ai=^V8o-Cob01`l==; zizw^Gm2e5%%)fx!fa%uiB^tUOey1G+)Q#o8xs{n%M{>=@U4zC~yw#s(4a{lkcNA@e ziwXGP0flJ`UZ0#-pcmHX(k_`3DtQ^eA1R26en5Y6jHL1ns}iJ8;`0cVfMml)Wd4oi z)W?Bx0{J1EbB{Vn;2EUu?E482{>U1>_#&);A4YrjE3agR?M=u0rXg(6;GINsUY**dAA zZPETo{kIQl&Es0X_TuQ^rK2XdZ{(#GBxWS$_6-{BAriG#R3T%bxxcLnJRiZv38xU8 z%2gBbJ8{H-VMx8MxpNP=RCPD?kzUo{=D(L(@CLMqTb&qJ5q3nT;@0Sxj4Ld_t_(wV zZup_w{vqWo21PegXrF_!8}4BZTB-<8(sdDl*7rQCY3D)~V9@faR?oRL+OpadQ6YV+ zYA(3bPD<*B3#L#EJA9^Za*jEvTY{{WFu>sIHU@$M(8DGH{!4`TDpb&`*YUvvKebiK zlY?4#021Gqej0kiZM*BM z!eg+hN)Nzuwe|oCMu*BB95nWO|Ge^a^h5@IdhPT~{A@A}nwCf4GrSOF*O5pluwizB zsU$)8=!0W4t*m28(17Iz^pMquHOw9V<-Uu*S#=hBPNZU<<%umEym)~?@NL{DcbbZ( z0Y8D5(b|*IymY#*B4mV166NP2Tr`+y^KfnOpwATL0a8w1=%VgL^m4wSnb}#ygw6JH&z>~tDBGE zU|eY|udJ>Dke+v@A^{dbYRV7Ik``GEidm%%318+1I^h*8RxAWI#DAP0QO zncoQ)PLTJ2AeuS}BxacGQ;GneK-U7Z^d=~oIffiN#z%rg;P25q$t8iiR}Rz*LAWzi zU8qbslO*Wd;GGaKcMIe5PNp+Rs2@2y@%r*6r=@v{rt@2k22>MgY!>DXh~Ge5r!IDZ zDq*a~WYl#C`!xdEX>Nv|6?f4{mb3y-w#g6#^@0=cle?RY2#-udcZ@9;-*bAf_gYs- zjM-(sLb=?pX@%Jx?~b0+WGx>(Z=Io8%!K>i1f!=#nq-nc?y638-&^C0!uy_Rj)s$! zw_Ya7-}+>pNp~NGAvd3_J;B{yYdl_kyb)tC4qKv3z(FrrN`QRyl(7$eGm{EU0skXV z)rt$ZfN0OOgQ92W75o88aS0{Nha|Jx4ieY^YAV_4+ja)?R!=Q29;^j%r4H?D%vzH7 z7pD`bfLI{?#bTclw-&@ZM)x_&wo*dVc%gIDEPgnz^#UlIoZSrg|H}`uwy+G5;GYqB zphhGUpkS-^^<9rO4}&RMq&goIK;F5+0ZH+{;WduSa>FQ?j&E4Zm7XXRd3vu7B@b$w zOHULBe$LI3Jd6gs(YO!zFc3<$a=XI_7lG3x7Z`-bfPg(jTeGWbuXT~Hx_-Wv3%1um za8^GT{w;9qaSHx?Fir)bDL!S-ma4f`X4I#{v6KZmo|@scAH{xTXhTC0><^%}fwAgQ zLXCIU8!tCh*--*-^}F?v;DW zvPF1D4AZ%j!dJ6%olJ4IJHg@@uqb$#5EXbO!C%+qn$sz+Bt@ea6Zjs zVgF!%2SY!oc4BI5gy(Ujuh+_(?3q{Bm9VB&J+73&@0JoJ9p|&KgmCb)P7gJy6G~1r zW_lCgD%D3(;gq@$i-1DxO9~Q6vkrHt)dCcSL5fF|pY{aJaF$ytfS_a^nk zZd0k$n@Yx|tV|i(7!X1^PNZxDiV%Wu6C4XA2lux(o-$^qzSR?N| z{!)GkiA`yOdr3SmSSfsSPZ9CAj!9)z7)Un701-EWMywIyBBb3zwuAMD7SAg9M8fQi zhw-zRQG3<_y>c$u%~Zj8rMHAm37BodR1!)an~I1gBjzB0pDgjowz&T9E?)od|M9>4 z>@)s%GFO}bJa`m8Hm6nhI2*s$FX;ikaK~M_Z5|xtk%a27Kht$Gr1ta;zyg# zT>uxI{CcOzyccYY>K8{Q2XE4pFSe*YOFbw#qN-@F?RX4scyn(Lbl~UYDSIMksa1NI@)T4tv8Eu zo_x4>R7&Vt3Ku2p-tmT0R6iNKMqDKh^d4?@2_QU;bxRC4duCXdfg$&fVlb#O1RK}+ z@py2BBG{ciWNa!_AY4Ouu4C)je8k7EBzICpbUY7|S;=V$$4ME&tK*Y{mpe!Kx6OP2 z5mwKG6jzE#5`!8>AsQJR8@PwNucOqfs5USuxka)ekWaSA{H8>12gYZM*ki4Z)bXwU- zQ`v@d@Wf1KC@*&7)`1gdq8F6h5-r?G9>j(6Lydag0OSgMWu*pCY7vy(>dMg zzwe@|v*ZLmt5I@Bo}E~UUMF8Rju2gV@bb;!!O_VBE1W>&352D}W&sOj?PTZQvM|E( zPZqPB3C0EW$2R%k*51MKjy#D}HC5*`S64oC<2mF??lYN4N%|I-YB*~E1ny3F9`~Yl z!MjoM+#S->WLkgjE#y$W9WuC|p#Us+hUmR3mS0gtQySW}ZhHgPUw{)D#l}^NjKQD- zbiwZP)7vq#!>)9!11L{e7N7xP3Yr6=6tHj$feyew27opD?L)yJ)B^wrAv299)^srh zedYiSMmIv9NKD*(W1|P+2u+iYz%fwQ10A?7f&PfJ#H#8GOE;q>UXHJH_Gb*&)L$d6 zn~&TlFkcX?bvI-5IL|c^80a}-UoR=|&7rq+93cz<&`t}YbEu?cobW3*HzIrD8E8)s z&r>S5++j_~xR)8rmINwt46wOS6ox7RS5Rs>@oRuYNX{!a;eLSVjkZ||dH(0HZ=u3I zK|Z`)TmcXgQOYMM@bk-oV|e~tvmdLh4*0|_UcUPdz;LURu7BOO9Gd%V5qxh3otZX4mRfCg7q2AQ*`7t&= z(Jo5q{Dit`0dv+6+x`0*6cm3DLXD%7pfn(&ef9<;snnV1PMrA{YJ;$|7iD6}Y8o;&8$n}6 z0M5W4lEZ73Gn7&|AbYNRtiIV&>L5cg0E2ucg{UCZaBx*j2r^p?&~n@$^T5==N*)BD z;BzkqR2h)m0`f3YMY-5iB2^v0v6`;I-slMRp+;Lgqev=X{Z#2lS}hBhJj+Qb%4+de zp1_ckQf_7S1;QK_@*B`PiFu(p-9E#&t-ik$m`*xHYI%P~$si;X{dR^}FhyRZvUea9 zYt)Z>O(o(M_u zw=lvfeO2@{@jJnkMD*auO5tTrS2#B?TB7jfwj#ke2Am&|zKL}<-NQ41v5btBUN$|M+3s3@a3WgV{=j7I>dU0~DpLITVgz*k z*~`P^P&wJS;4`E*tgUZ6di-Rwb$ZtByj`%&NW#dQ6D3_)?1A#zm(UvUK5rA`hk2_Y zZ^DQbP8fY>S3$-VX4~bFoGS*o{F~Rm zB7o=5D4dVn*XY?Ccqft}j&MKKBHLz8WQ&|o3zSj8FS5eo1evXT{nY~7W=>#pd*Hv^ zhj1XLIwg>{=mZFRq>+wQ|YCrW2H?wl>c z5~pUi#qYs=QA7%_UgL=L%kMh3PiyUOE_%D)?q8h#+vlTyI(~kAy1Mb>KmFm^Gd?aa zi8gOw!h{X6Wz-D}$k%uXk}M#}J4Ez!%>GIlj2X#HAxXZ7C3BMn;PmP;Ms zglAYZsoHax$55`pAy5PzVi)sJ&M@b4)`7+^Pci;oSPnl59Dskx$|PT`U9UqDMmdDD z%nSU!upV?ev_Jp8w#q-3kth85pN5|!3;6Rt^*?9f2YUdC8Tb%O!&CCaSMouO*h!1Jo1~68x&Yan>_!1|aop#~^ zHPT)h5Up+|O$a-$PF^g+)KRz$IqaZkMx?`Lv}p{5aM-JWGm4@BzU~#wLi!CX*Kgkf z!GK{8YX83GzgzL&6~14KFagul44Kla3B-+S*Gn=9S&69nWC5~@$yTmt1pc~|}F&tbst~ zZ>&F&qA$Z+!hNsxEZK;IChzi}7m2=yu=IQ;xe+hcaA9LIHFU;u4{pE!JZ$a#^I zE`Da@sN%2aY7x>rOPS+KygmB@J}}F9szyv!#$}U_!Fb0%bpe@I2O#l1GD0yg3rD-_ zT8rSU*F|{@WUC3%(Am^MwMBLS{2eUM=7`c81kl#{yBg0Y* zJ=`VZ`P5V7m<}I^gDSJgfyTKLZf<-ZEmbZsMhSf59v8+#{B?D#1J4Ggp~zOe7sDtO zJB?+&XT_EH;@d;e72{>gV_x!!aSpjGENLW%g%Jo3GJPI1*nqN=#uIj&9uM_BlwX?T zIwy*-Di92V1jf7VW|JDWf4lJi{VZoL|n3d*P&ix0MNK6N+Hn3W=`Y2%@*D6F{yv`jcRVjQyAT8;N#%jzv zWeW!|W+=HZkIQWDy0}U#>I)TRC}j@7^~G<24@u@==Qex5lkw2@y<7{T{H06@{F^rMMgne$-}v zfX5Y8mwrH{A^byc5*!?7i1c;kAzU6mVNW(l!HMY2D$nWhv8f%xV1CC}ptUW0U>fD~ z_N{nG@M#E+z_B$(?pQ!1 zD!g{@yPc|h>C;;*ejBfhX3@RHu0Kw~b;W0FtB?t7_Y>!^t;EwPqQ>>m{E{BlNHP6I z*aJP(KORMbSIKx=Q8Wep?3e;Wz2VHZF!c?^a%7HZR^HHfVbRJ8FM%OWWvR0mq($}4 zK<%tyt7Faeg}-aC0Gc+YtFsQ9-z2Flf1)c853{>#BYly#nAF^DBSfybm$T2>3=`MV z{1wY4L|`Q-SrisNZfkPr4*^%ci9V7dUm&`Z#-;YKb<>A!p9KM0UY4P11H;6nN%!Qb z8rM4L$CJ@Y450@Gs;Kp&U4^%A`K=ZS^mi%3%f!g&4;nLYfrk*r5)}^d`WSUJPdwkoZ;2pFK zxHkphA($ti;>CbDs6fFzkR!oG z{xT5>Pf(<6*rAgW<;Li*@a34i+^_xoNHbCVQiP^4dm&n>v-soyO>_{!GyL981-S!l zi-W^4$~ORh3o)F%cPLNCPOlnjj=}L<21jU_EMt5LIU4m-Xz;qlnlVfLnp6xU2oj)C0a-$;#B#a(0De82V0zg^g;XzP$#gMa ztlUIUx7@-nC4%7IS1}b&tWX%_#7#*of~ubHx;x(|4g&e4yQU0Q&t)ylIxdc%@ndH*mhN?< zthK+|P$lcEk=ljt--bF@7e2}1^`}g#J8P|<8DGo31(3sNA@l;6#QC)$RI6d1bHcgPZc5zdk(7fzM!VqG7wpwMIa!W{`h#xZjFcvEqw3wlcqj$-8SNNLBBox9 zVPa+k=yCVFzuRxfpQ-|5;)l*acGx^VetmGX9cjbtLSWT2^X(EOYA>?Y$loGyjF#Ze zNBz>T&@_1T>@jQ<8J(f27&yY$kO@)AlIRBCA)

        =T9@!h<;9~V7-j?AP8x(G@JzJK9$el_$;6iQRmgdP-NhE-f9~@CMhc)f6)k?hYap& zTpnGsy8|R($jv5MjU@AM>Wzuz#>>|ZSdo<{Hl7*$yhzBf(CRm3M<2=B{R9g|8^Ezl zyP%O~`LMs1S@bHU)F71dQZ*RqhBgcfjOv69#qQF#2gk^Cc96*a>j%Lzbuk!r|3zdq zj9z$7dcM|nG|cM;*%Jsg3OFRqpMTVsFje7)V)6A4={rL}oiLV~>;V!UFO2m+=N zrsxXH+LN-R+=Ce!0=o@sJ``}9;6JjwyN0b3|BH}sPxGHl4pF<04c-{K&oPmS4uWPPQv_T(|th1JH& zqbHAGvcU(N_QB&P@M)i$}4gH;6_%pe+0J43^IJE zS%v;c;~mYzhUPqpl&nLjAo^l)_@_Gr!v|e|24F$VyR4ITKNw z`u3NCDB)6Up5+-j(pZ^15$JbLJkIc;lOU$j`YE!#bX8*%jRZd)df8=h2JHdWY# z)ElH(vf*}T43!Q-x3s5<{-|FJNI7PF7}Qu1clIQ5CfsQWKz*(=d1MDfJL_~U_llADYTpUH#^ zqN_r7l7DsO+ATdrht-nU9y0;AcV)=oK)$XZ?*nZ+@w<~N2ZC)GCZptCCC`VD$tHkPLZiP+_3Co zk~b}^>>g<!W>&v zR0Q^rEKvmnHD%Q^FrYRDNh4nN4Eq=I13AIAAJ6jfHy=q;{^;AC(b=$j+R-G&2Mxje zIxGT`rKnd;KI4_-otH56cpp0J3*x&E;Y3BVbXu?F%G5MX%MC|g8nxoP35%&DFo$v@ zb45@Cr};vy$j;2c7^LR65T*yi38w!$DFun-`eLv7%{SH=RwZGb|)$l|e# zuWCZLmI*FDAK>b=`i+yLS3772iaAs$CqsnYJP}8t(@bPzeHjkWX{4-og-%&|pa^s6 zv+Zo{9EF)dgVnZ|+{$D{Vp)WUZwgPnLTgdg2cZsyibV0rT1}2EDcL=0_7529e*%?m zck~LiyN0h@6OI%t=38_L>h|AtX~iN(Gc%<{jgQm@rA9C5>!mG+oVh;7UR*39POpN) zTy%O>Va`*B_&!3=%A-vLl&!8eHrJlOZplxcEF&$CpP*_&wd|rtpEX9vja^a%kfK3Y zPipX6_<0ts;at-w8!p^_ z1{u}mFfj9f;-n?i0XU(@-2<^xOGe)28$^7AH^r;>*aiZS%r1-@nlm88Qt=d%GLack z-|?KeGb~ZlrUAgTbD1SkS$;UFBLprskI|qnzB`~42dwK$K8Al)-gM3l$i|RQ`_OBK zQoPK#(_4!0fkMPUQbsxF^~>$g;d}<)o9C@=e>M!C=q8wY;r+qr`V1~Mkm2Ad_<$?V zsZ0b4POWJUC_N(-;-KAy&0%j^bVfnwq-+Uqg2Zcrr7;Z*QHU_obh=kjyn1|mRXFbL z(>8jiQ*QRU%07wA$lC))+*^H>(|$YOFOGRT%cO3!{?2h=?uh6)qU#f&b;Sn z;!&O;`%72C@wYSz^gHwoW_3eqQm^{ReZ=qER*MV2-ZqDwgFd9a9iDe}L1nGL+ zepE)%lsyN%YGyv}RWfsP>_>Cx7QDDi;fhn}$4-_d`YL^hvyJQ_6hzu=tgk&*KL2=Q zEsk5Y#ZN6nWT(=}*WKigFnOd6#n>0Ffo&;r8QMyhq(ig210qI1dqB{VuoN9uZL5i{ zLqVPj#dpn3=?`w8F~dmV0FHm*X>JeP1xHhGCRj>tCGNjF+<6IfbjfrZvbl*R6-lFQ zg>d7lj-0QjI5kKn66|Tt$ixc-2*u)_7N3XOFlF#@#X-eLGc^ZMDIt|Sr)3~pb|g4w zCOd$<&&CH0(|8H_Gu!$<4a|tPkPmjgdi2}>?f*fPGyglDx2VHG|8#(sd;o4UBvRdT z-1A=}m*tTCH_f5Y377nUb@m%gv#M}4e z*{$yT?wKsgzWPUmnBtq&<<(^bjM;}W;~$jl1U&HiAs2Cc;##54KN;KV7P%iZ&%`B6?1l-URo3T&o5IwUN}J5VvS;EVfDNN}r=`gqq@YzE|jC*eR5 zAS->fu@oA_dKa!)Y)l$>84bl#e75_NC-so-bC7fgtS1Y4#{5I96qY}bRV(I@m-ZvL ze@!V+=G8_;{YXIE9c`24blTryvQB%qe`LA#TZ17?CitS+Z{vBXZB(tg8{n`sP!ofQ zQ|n^&T6W$^+!Qu+=0pq+EbtjvXJ|O8-F7U7%q&UduwOusW_6VXPs=rS{!dphR;WQAM z3}>2JTG>8ICD4$>av!KxPnXmLjAd5XS3YC#+17uBNcZKo z3vBbwq|cKWnz-wQ_21Xa%D$U1Nvud>%+2*u3)dK!iJXEVdCObayr+fmM`D`p{`_jh zCK#|EiqBwH5+eemKTEfr$T|}n7@SZr`s4ba8)W}pqKORY^Vm)>UDkG@IFbqftp;;2 zo|K5Qe*H@UY%>Y~Gr=pe5J!*_Gx3jComsMdf;HJsM6yI7(jQ(ma6Z(9mPl%dmPidbR$E)G)vk=Al|-2Kw8EvH-y-Vo zj3)I=9aC6hP%XmR`0|A%+XTq@#}Yn~!Y?ld!&B5b>GVaM^wa}~iU>r@Y=UPPR}R+S z*5yE29y8^Qc?7U#>(L@6lCAX+r>4&LaGa9xZO=k)P{aVe#IpeA zNX$f49kFO(w>u>464gKPudg=m@Dq^T_61q6N@L=@P#~q8 zj@Cw}E%R4M1B$MD$*TNzoeb{&rTZ(&1|pF*^r}ht3t@KMCp~VR_7G2l9Z& zl_W#MlFca%94tNA`WE8^wV zkc|$yM4g+@J8>tR4>E~|le?!SIJ zWYgGw7Fv-?WkFoKQ@sJzAc7n#%A{uELD{Rw!w*A}O#X`MVaam~w*QdyuP>FX{<7?T zqH8*f$(X)!Kr~5F=`Dxv=_Hf5bmi^at1H7c7;74=nqG-@)IHurV0wFu-4SAeWg(fJ9d);71cbXKWts zn!JHYUjmAC4V;# zG^0hZtUF1J)y-hK9wCwidavP|1ywS-g}I-+K`^mtqKNnxPtjdC;ByQ)m4S-SUDcqF zz2_BP+)UU=%F%$Dr6CPs)CCFdkkUERIoNBb;zGLXIGq?fEI`-d%kvQ+cjs)_c|hmc z|BM)10{KXpFNWh^iI7sX64|tjFmO;Y6Gj!Fz}a?3yW`QpO&?Mg(g+{{G}(k>bO1&@ zRW8ARLm?sADLEN2&L)5owkVIqT+eu>lqs8HIM{R2p42panHF|6pN^Hvkk}3cN0AZW zUudH(^oYJ9wF$Dk7U&~2Aynss_=48JOFd`OzP7U0j?VXpGmQq)DUp5s)jwsn~c>p0_?1?^s=iikP)E#BdHn=CSXg2>Cq@|L9e11{quA=7QsJ^BA7!SQo-pL74*SL46R5t z@R8GnK_iju6A_Y$3!d`o;au);#j;SE{-qhZb#J#lb4hTU#nnQbmN(@Q!9nzoq>=&JqS5~5OJZmVDb|J) zrh!oI*61BQ5P&$OY0K{P!c~knk)O>7<&suQps3$C3rX&%d>j;(B?4d}FKcj>{BZ;8 zG%|E9Q8-!B;BV+YNy>{$viub+pf@PcCeO4Mdk8vO44-9c3+M~d|JND|=&jys-TJZ& zulX{a{{lQiCjVk(V`F1^Wn~SYr)3(_3H2|TR@Qtn*w#92W+|bU-GkE&EvAn%-568P zJrdlHcJDG%l_%!wD*f&MdHI`v*#7J@{`bw?b(L5&@kMWNbI-Muwox|W7?kl0C1I>I zPH|0~Pod}%RnL#n05*m|!8@%|&Mj&$^$<1Bw2ngH6%3U+iH!puW*Adk^oIwH0AxFq zzc!Zrgd5AAL=}}>Y0XF(7R$q#g)XC&Fx89t$pf>;G{)I0GMNh$#u~A6Gy?~MB|Yy>11$XGNc;O zs?{pU3Wr@-#OTq{8sR)Guy8c2V03S*b~+r~z^J1qWA$TEnO-9j;EdyW!YC6=X=W{S zvA3}lk=K-h(EtWNx0A2nc7L|^xbbB9$>Tix1-t!CuYdjwrH@t{>&u%@eltRDH09}Ux)=^RiEZaH(QTPnd+wHO{xnBM8-j_ z;!%BJhjp83zo~5vp}*5MClA=zGW)OK%?n&Y9P1XYpq%e9@886egEJnXTIF%JKHJwO(_dzRvGS#@$_RZGL{>ibc zv2LBlWG=GX(3m5 z*%>lPYaF%tzO;|5V*{^%UE7BHs#?X0RB*->5?&TohN|V!CUB-r+B89+?F9%paj!rE z3vdlzgDb*E1p)zlpurPF)M`b>vFLhnzB96;lR6TR5FL^(S|qTPo11PO4a*n*tvSy?O%gOKyzJLhTdw9qX^2ydTXIwkHGX-#A-vqX!s7M zY>ki+xpc^%AB%n#)G7yS#2Rd1zFsxr`At=lWR*wu5b|>$(DtEFyzas4KppO81 zcqLCe(z1)!-;P!Zj3W>v2XEiPwaIh^AwAk+ES@VGY=ItmY>FcrY;I+JtE(H18E*U< z_)~#LtAa-cmI#yl-rd+@)SAg8Vsyui z21(Z8Vboqkg`BA=RhCRmyM+u84qm+2Im-9?K&6Np16w`gbL@xYOtWN(sHho>=z@rc z72iYWxs5KO2`xo6&V6P2YFKb1)+QneyLJduycCl#qm{U$xO1WY7*R4%j!UDhIBrAB)US43#K{eeu=+L=>cvO zd@RfKbVeB*g=3g@G=ISpiMN9u%(pbvgmx5AmWeI+uh-)jm%rox$J5!4lBYZ|kf$xH z7z|UA`}$UWTQ!Ct4N#JHYZ-lyL@_T=bE9S2RiMrZaU%X10gDNJA`&kg?rwdDhIX$G zAxgWa<;^PJ8aasg1efTfUaU?#-4%><&&0}a`s!Fie(2{k1x_yx9T^FZr878J#tIhi z-j|F&q}L;vKp?D>PX@5v`v6~ux&_=XkjL^h+VBXz4sgW_73bZ=Wi}Zo7pWClBuETK z(!i_~9aO=wbno!?oUStg$hcwJ!nTGAOfaZgyqYPd>R8F!Tor{;nclzi*p<>B7Wi~?`# zmK_m}Gf6&&&E4%BJuuP{nJ+#RwcXy?gP=mO)e|fvy$N>aDQ!weWaYV0nND~SolZt4 z_u6lF)@z+i`3ZF;M6L~+zzV8)hUf9w-G)dHi*T*LZZgA2J2a=5h2tfK_45?psQbA0 zn{(a!Dau{sof&r_PwR!tbO#g2l!uu6DmpWQ+O(!wb>|^0w{}G6R38}ihdY;*!VQOw z7VoQ=9)q`>MA9fk$g37?rj$^f%u|E8Sp1LtaNNCmC>AIFLG>qaS_(gCWznBN=}N^O zjs<4}I3v|2z%!u_iZ5mo_ap~w%Psmvr(;%iu(i^su^W3T&)xhrUlTs+!V z2VK>(i4y0PnLtnA)IEla7;*u*{t29F>~DBLC+;?UhZ7xH$EXN_7CPaSOdfLBQY{aS z-v4uE-60K7ur3UhJJ6EyKvig>nONI{q)skVvO7a@=KkRJXi`0^9Y+Hho z{R3KK4|xwq^r~*@O+Im9mB7pXH*A~q3J?MXq4&GC^AoM-D9;cTs0Gb=*=i5FFkdrQ zuf{M2r@TCc3#iC6ECh&Ou`Y6tQZRF+-_GlTy==7$5!=wQkNY0o3?o^Q|CQtUP^=`_ zST>XjN=ETeJQI&pe2?waocm#IeYvrYM&7tLYw&g?-8}O{FsF`KeMGm2AK}?veARR~ zVi>btH2q>sf3d#(iJJ?*@RK!hx~w%GZKhqsjgfHS(6V?^;3aqgxPf%!(%5(L?@6Qz zo(M##_!gCNil89#b<H9ozKU?k_?i1h!#k#73?8)VE8HIj)M2`c1o4yzKRv7D8LFsxUh^L zLx()HwSyZTrb#tmDoXZRS4ct$Csn|4o%AKVRz|()u!&k{GzZJA1osC-=tNLFaA71Sot1J;ImO_r&jBV4BBqPvTL_;Af3XWNH zk6<|opiLYVqJeL)Xj402Oov|@CIfo0P{sLRdKA)EzCn}IE(2j^lGr}Q6Wc#{W9bja zp!^;<6V5GThH>_QoZQA~5-sUNSy4&^L=y;KUkpqPJSILkb>*Nh@pt>3pT-l#!y^cI z0ZUA9aUydhT{$a9B$+`lt&;4tJ7|@B#tG!9SVFTUj8Kb5%A%SD!=%1Egh|JAL>ygSav%JQs)^O%J6@J0wPcXZE!ipn7oxWG<%Dq(M zb0jc`fh8;?4;lrZ!DiiU=<&oDAH%<*z?MJ^;#TGmrOrA%3JP-I^X{M$ltBeFWLXN* z>lAN|>GubdPZZ$Q+m9D=|00T`fW@G6*F3;7aa<&>$Z3gc2AHYCxs1TsLbe2NM$bD4 zw(5YsnX#s+WxOU*xBV76G>zt@Zz!%*Mc*i0Pxf%uC~Y>_8|y>KJhqkU`x0S7M(Qa< zVU4vY#$Sn-C7E8IY+G6IbE3 zx27=kO$F|Xb>)n0l`DyeroDKnSb}T~hp>1fGCp!yRY5L7h#YC)d4W5<7}DK#j=U!c zCZs0aht6bN>YU;9W;v6(xVFB)nd}0|7{_lX7Hre-)a)N1L*Gyyg&>;SgTau!#gW%Fk*XfHhTTX^i!r9; zj%tCHD8E1NR*Y|D$lJf}Ye|ETmAe1R-dB>nzp7>Kr^Mt8X;awn|BHmkClDeZJ$hJM zZLFaNCdf5Dc(kk^tUZ1dae^4$vqcQ>I_fGiDP{#>Obdme2hob?U36<-B;8%yVcMOW zj^UyfYazqs-M=2u(Q86+0{-2PTi7k4sI*(k(3FJDH? z!3&jFEtGF-f2IFCW2sN#puIVg5_51b#JVGJJ zv!PQ_A*4QmQ_Pnu8UOeH^MCyF|Mb~s{O`rQ&;yISea!gE{1lmxHr+N9cNm->v)av_(VAz};x7gb@mdR0Ff&#>G+E z)4mgKhlV@EX~!6|Dy)k#TaA-FgkAc#{;Mi|5}*I5adjQGNjal?mRyhm40`$g3?&t= zd*hcaTGfW5hSPgIyyl%q1)c0P#Ru1vRC6|zYs5~cIR{7eQ-E^_8?e+F_z+pG*4Js2 z8jO}Fdcmg6>YqdfE>{@E#fGEE3dp~fQGqBbH5?I$pks%fx9o4niJ9LZxiy!FsL&`C zX+^?(94XQvL5-5MBa=CWtJb8BhRP6RGNbP0H4_8dHOTR2D8M^FFpd`Bh1*l-LkPqW zrGhl(sXvg(-l4$;TuN`d=cppt7T;=N)fRUTNlgDTinh2FBc4?GfU?9H3b$NAwPvnm zo@(l$)eF8DDs_$9p%0}LiL2b)8mW=R9xSwd=t@n|bgPhx3?s~SH9B}}1qEuZW^ZM< z8{=XT3kP`=5fixl!`p*Cf)tN;_7K2VgFkrxr2g9nwdQfHUwd(M@X|r-jl9i*6y2LJ z3qlohVq_Y+=pK#(7!3^d`u;U*sXXA4G>#Hw<{mV-z3*E+lwL6n%(l-usK*^F;END8bSJgyXOu&kJq-3-y(QSXdi2gvwitA#4*lKjq^ zqC42XAHeQ!(;aK1wl@<#M6XmS)~B?q@+2ljtk^5w=u`xCb%>}w_Ye`JF{BIL;uOO; z5>>B7`MV~ZasJ)KmJwDc!~p_rn?qE=xw=9fS*d{jqTBC|F66J~DMEdqYqs&o@eSfN zt4$*_X}f~Ns5ppqBr%Ubg%>cLNd-kGpKHhOJ)|WexSwpaN?Wy{?!=9&APNrFUw9MQt zinZMO`s!x9CqW4!rA7^QaGdX8?HmqJMvbRqMeD@ z4eolN(vI!PN6r0hM)rUC`ziS49+;m-Rgtydj>cd>TI6ZWz({!4kQfS@xLDg<-mCq# z_DKGJ1>U8-30L+Z%0-OEF?UeQLDezNcxq|H-U)?KCMl3lcc66&t3|(Qq4kXXR1uew zuRjl(83zc3VVrJv>q zn>ZFOta**U_FVNj9-}-$ME93<0J* zTXKvsn@(cNvUA5kQopLZ7v*rlUM#Jr*4N!q9$_ig3Z5>(z?zhj&huus#iBQb`Kc0t zK~Zrl=LzBQ0`lZlC8nNTU!kpdmSvUXo+Y43hy&NND69JWT77lBnB3rrJe_!m0s?k^ zG0FJAS)1S?+Ts%FT0DK_M}`(6um6s$!?earSnKKD29A9m7>tJoCc8jLR%eKyEs+|i z2v}D`gEdks!NnkcT=Ek0d(jC7QKDGFbw~sikt~@MLGDn5LMCaPtC{0j<(Zzchd@1` zVh2po_aQxkX)1PO^s1)1_t&)c$mT@>W;{KWxMS!+?X3%R1nEPQJ3?Or{9R3%9Pu!h zQA9nQ6Uhw40Qv&ovv>i1pDJuNhpWlw$WPe>@?3mftGP)C9dJAh!Zz(<6t#n>=~2P) zjbhD0BY?YEV2Gjt%}sQCRih{JiJ6EO{A>OQ?k4UUdO?FA^g6e1KM!2oQYH z+&kVe6;72sP!aH?1Wg^S7+fMsSt{@@EVZt>Oa5LaHVaXlWj{fC)F@XQzYyqNH3BOz zf*ad0_BfWqsoL8+TYJ0vJM)QxNNk-V6T;E8>6W#yzMA}xy{?OmIXs}5=~;HkK;9&X znf?pntm=7G<7h7lhE%@v{-Ey+9YW%(se)Q5r5)QGvC9Qa;ofOlKu=0S@4KVcR2Q=x zEj&>m(D|c}lGKzMiozn2dn>FMg$cMSyhV+%_8zGC7cYF2hwBR|xz#g1T`U z9?mY8?@gW2aRGV5I&FZiDIF58GgT9-kFLtt#wx7-T{KXbpfdRDZTjO1ikIi+P=NY-ijd=-dKq6NMrvh>$o{*>(8y(NoByK3@qKDJm|m_Qe1?AfIH8(opn3{xvPtsz#D2 zn0o}EMv=M@F7Dm#pb?174V+?u(e+EHr#tL>Tb2T!bm4=qDl2%Xa9$qmsn3ta@V466 zSS^=ISJu!I2GyE2@You1`tT>>vlmd~UR~8M#aFqjP2@-$!@2xv0UUhh-Dg67{au6r zAXW`Jj>z$yek|buu>3%sqUa7fy$@5zNzX+2!vW9Cw`U{#;CDvk9OsEeqiTe$ zzR=Sxyz)*%ZSsfY?obKAB+U(bnU!ExQ0)64D#RcpY0z*s7cb8st^-B&=~UsHEa(%F zE{feFtwU+SB3U-0qt02Udo@13K!q=e>fgdvsJYP1{;ktTGxZ)^4-S4*=u9h$R zV`)PWi25Su4`GvYDNt$=L*%wMsiPWhV06xAF_nGueYaESVNpt1DLsqk8!*sbD@J@T zyy`TCq2UoNM%v5j@)OY4l}D(Jx5naH_+({cIZvPY1QHsHogkF|hk6`dZ?(U(3IMvc zgr%nsX9&V*gaB?L6I)JsIl`)t9q?blPS{RxkqG%}rZP1*?RPlDp;Z%XF5&J z;lLiB=InP>M-(O_Q{X|Wu!=m(8_Y)bY!2AEk9-jkeQJFajJJ|lMy#VCUwY<9x2QM= zp!0q*w9a2nXV%Q#PJ^h^)5l(5ixy_G89wprbG%2SwKP@J0G(1NuElinM772~LCNV= zBZkq|G6Hqi3>r^!Q3y8LFgA<2FZp%_jtKigvLc+qWoI3-*xrjldWc;=d&fdj5=R2d zd7pmG06Ou%q7q$zovZhXJwPz!S600>&^5wa-sb^MiAI5)&y@?;UIplI*Bxn!r7u~K zY{E}&E0WPzQrzY%tSKRwT(vvo%xDMaP`kF2U>~y5IO=u#(=-|V*ea4w8Yl}^JeYQ5 zOz3m(Rox&@?31l$3`AiPbMTZvY~gvbQ$tat_l^g<{qDHi>Wzdy`8#2_?ARhWI&6AZ zXy(Q?tSD^9e*>D`4+kZ(e|X-RfKw@2+}KY)F|4xf11nvd!E!EMfRtf)gqGzqENA`b zVLXZ~mya`=FmdHjH%p#LmgjN6(Z$kXOvdP*b#T`f9+Xc$xVmR&Y*fng_$$?f6b3h@dP8m7by zKO;ukrOi!%tGX-BSK*w-Ib7&RWc32~SX@;0svHU*bo*z$Yc%o=SNW5W@rZfF3(=KF zy;O|!j-ZQ*LKVrH2C(@>&GKb9&t_nLxph!0c4Alc5-RNlQxspBaxsA&=dNI}0iw_# zIptWItc5}D1`CF^yz~W8 z&|I8YmV4@wDN#;jo4{qUylP4In|(a4;5H}~MnTqwgVuRd*|hT9pW(Xx%_f=#1MGqSdPWC4AM z##RnpK?q#P8BLA~A)`nY7ky62PsjL(}Ex#t0_Lgcb;SXSgsY#vc#tK~jO0wP*>+gxhozbw`7WTput{3E_3? ztLpq+h>IROh&osv}vfE$A7K+|(^h)A;VXOj8LJRA`5HZuFO&Z(? zDA5)sV51%-q;$U3z zwF6wB=imm?04^(X?@wX!k|IOi#}1+e^As%-o}K|>^s&=Y^%jvaH!$!`^fDSSin*x; zTeGGdA}otYrz4>hD6W9JHz4s+y^nrZvtu1LkB?s;9BorVlzLqV377`DW&AbX?-uqi zS(w;#pGDQA^B_vWGmL+L7zD%{v6cc&cb!W#c(Ft?BVu!-3N#VG8Ki8=z1&s(uw#mR zbB@8yx{@-ex|1xHTI~HnXh~ncXqYlJJfc<1}m9$-7`A%1M9BbK#b?t*vl+@qYUl!VF!BA!~B)H29V$<;0&H+?gjOmJIJJBsH->@&vSR|%7kQtOrBf}DK0j6; zoRnLNOrToU*xp1HCc@`aOcnjf!xZ03oIDXBHGYw02Unhs{%p$Ov!~ZD= z$vx*Jp>jv~O(zu>fWDsUFl2gxF-!*h5LHxE(3j=MDpyQB^ps+zjyxgb>Uv@_+!|Fo?KX ztU?$aIyNu5JD{VT7q9lW(N~+)$j;@J8fDAXEU zVY({{9mUhJEGZRHc*HF7f_cyyH(~jr(U-;uTto)rPoUW*wnQn~#guC5vml|+twF9V0!G619Fvd zP-YK;mZ3~PS7PBIN@FzEHdm10u+dmsU0sO=6-LbIOtXaTWnPeXjNpOwWm~}-R!~vi zqyp)QsFRA0;N0Gvu?jQb3NJu)Im5J!VezGRZo){Nbi%tYW=9*PToSsj?+|8RC|*=A zOAjmSQ2%iD)~Q^ID(^`Wbh4$G^^++Zr8*o$7Vo!V*6~;>$Z!?jj@i z!wZnW&ZNe|GsFQ1DZqfwQP|?m*~J@t^0}dcqjGN~OmuQ-(N7IWMDW&YKQXu(IbcU7 zj=sL=_By*LPTsxRMISe4uoihek(3^cdQk&YN4X3SstM^}bmvY>GDV(%PD?4LqK~N0 z1oDVZN-N7K1ck1VVbnbl$m|>P6p~M@x-~pv=gOGKd(0Pp4{T;S7+f^CXy}~M&B_weFpt|PziBNym&0G0-3A_9fxKK4;fH5RYPblH94yT!AweY zQ(%LaPCCiQ=S0v;mn01zS6z#;njWvD6TxZIoM+MXx|RtJz5~<|XzHdZ(tM z{8Q+bhm7QotzG~YeU|_VFq&{OH;O2NqR3H=^Zv?r(EH?3QS)T;QA8wd<)_l75~N9~ zL{a#YiSl8)&UI5@yYz=Z`GY_9Y`lkR4NVtB<5X5t@ge^QeTfy&6vC=flnoqW1zqBul3*&!uDn_WepG=%$l~2L;sw6&ooM1;YA% zJ!Vf_(klAD8Y&e8vdD^xaH;ShA(>Xp;Snohd2>vY$BLNBd=%onKe(BVZr{HFQ}1qt z{!%Qjf#NmUlHnL-4ql@&7jA<&fp?@bU$fudAH0O2Y@($0eYe-|UPqA{GYM<&`-8h` zyfVdy7S`4qkDfe69_i{vWA*Xo;|OzYyTSu=TJ_+At1>zc>*mSFSf>k8qnzD&n z%`FJQBNV;vWlf%Nx|bLnpxw{1CKWaypL`OF0uz84E3sQ8>Cwo2hFls18@GBDR$J8g zAmmVZ@ZiCesW6ln$2dDJ%fJd%Jp;cF3sOV@Z}q~X)1;>s-wAFOTJ_ry;bw$WWkO?!sl-cj zO+iegGSiJT1@Q}c{q4@t4ojO)(+NZuVLnAAMLH=&&V0`g0+?L>zRmw01nvY2L`J^1 z-5Ddy6C2A)s>(Bicq)G{L7^C`3)0UQkw25960C;|tq0J;VIaa0m*f-`TWTW|1n;1c zkzZ)S^<_v#1Ptc(i9$p5@E`*^;7okCf(ds}#oe4&>Hs0RqB(#L1dRyNFSe_yD^&~p zZQ`;)A?&{39(*u^lDj5u_xFu^Me51D;#vN`Dl{z<~J|=s@s9OvDJ~tu&Ce5Vk7~$i#~^8V@8Z>Y~+qD~>3uuGEET0fK`U zF5=mcE&^Q84r;!=P7RHHE?Ow;EhZWjK_E+r-I~pw#<0Y-QA!|?msF&n$!8&W*ux3T zElz+ps^@_Qq)E}$Q3Qx!_#6U+2a&soCD8@y2MFWjo!K{UCj!gJgX`L=Uja# z0v9fC1^L`~+<3hFg#Lw<#^&SoxaGI)M}%7+n5k3Eq_Ae*P@u5li50hoC+lcGt~*T? zSAVf7OEMA9Q#bTd7`qe_dJsSaO);W@nW_|w&CYDC#h4jh6%gp7U(e&-uV4~-V_-=r zT)g#PF`Vm-*I*;1Azs@@G1Se$a}po2V>wRSakNk3Jbl|IFb>O5AHx@Bd_?$lz)0iD zqXz0ABSJ-mLQuzWrxQu{PLX}_#Zqpk3QG_wNOF<7+U>!&!5|%TxIv8Jhh&C*im4Df z;<+4a)O(77auX%KM}0+-NXsmyu9itIId+TKR^Q&pFbkRLD6b zZogs_Pnuxf&GvLpG|wT_+&4kCGMD_d2bg|z@vD$T&36~xC~6{GLs=HEmSqyNp{@jw z@mwbnMwcvx`;^pCCcH2S^UNm?2EavrBM2h<&kn@U_+tycl5Aio!*3*I=RA2RJ zx!4^YYdqf`QlN(uMajMH=rr~|JNa!YF%seX_t729Pm>oX6-M`@C9gecJYHQznBC%L z1K}ibGPc&vKG{i{QkR8|fgRt}4o-jUoQ?nZ=i1L{m{@_r?}e|(K#ooN8wP8| zC7cmx;ng}Hh~~={z#1k(xmrUbyu9#Km-u-HrU_Kf406pJBp2xl z_egL(Ft8|pKm~G%_W?}oC+BBzS0O!~y{Q|XZiEhV*6%di?fNeT#@RR<5+UQz{&tse z?25{{WCGc0X$4$85uK}R-i6X`{t$67oJ|OAbgd@^ZuAJ!FmPGKT(P&gAW;?JXTAs% zS6M07$emCEmgsx)a^5X*faW6JcSpR%hpKM_EVJ3C4~MPu>qz>ai348^&+mf+`)TqV z*ez#vgUz$)Fa1idl+4{D5^bHzh#bJ|<7TAFlnYK5U8_SP3&;(#*o!vm6 zL{#!lOglR--1B(T6THbIxmz9Iy#5#ku`4uSAER-} zlFg)wiNuL}ID%M^C-xg0DmDItvnGYD?QZ2ju zHA_P(r%V;At6V#urQtBdm)Z*|T3&PyRxs{AFx%@bQ@HZR6z z6a9m*W0ZqDOFy88&0cxqpzpL?z${Uzak=di74U;)Bi{ku~ zVjEH77E#?~(ph_Aw@%7(Vh{S@%-d$Xd&SPmyW?4dS-#g6HU9LRbUu~2j*UoQ__(X* z*H3ew%7XNU4o~W}&WsTr=4c)aFBip^6eM69o95Iw0Ij(EYleH%m z?pj>+z(!~=1C!{FZaPB=U!|0#ak8FyAfYH+{Q`n+Myx8QkhGkB19Edmr%*z8HkJ|H zt-w?f<>0LXGtdxcbJV9K`_<#D16?EvCI(3fl|i7O7H%o1BkdSjQkn;(br+I6OmY|K z%?imB&M^&G22+Cg>O@4*yP#w|&o8jmZ;1wi$D3Ho6Jv72*iY~`O_DAsw-(&7wy!J|rnF2v_Nac`=5!t=~ z`GyrB4~UZ+7X?U_iQRq-P)$rc5=Eh2Tvw|> zhMl|mdO!YA0R5#F{LDu&Q#z1b7ps+nH^vs3c*`H<=KMt`K5ao%+v{F-$KOIV86oBt z!rb-M=y;5VHJ!HPtV5_7c3!teM}xs-wYza9VTBe9#Lf14@ZSuEapWvXZ5;)7L5)Fd zpVgMNqFU|C+T*g?YD!^eaU+HrX4KhkSYVbLr+I`s*WxTgY*~l^J%?OyxYx8zYQJ#j zQLJ*!MU>8%`pDO=kQb*$gWxFQYqTw=_ZWPT_r$kmTAE$Flcn;~s9z|=f)pr{fRon< z0zF4U+FHs)&BLd5e?{~?Bl3TwU1mFjfADRV#b>dmvY7We-SZ3NS^=(6#z{3af_j)e z=M+~x*L0jY;QK68O#oXL;NfC-Ea+3%Neq})^UE`yM}f!7!0L_bRe~kdlB!FtV~W3XjT}Ii2VzP+8-}G zY}W>?bbLKZ+>`0REldx8Zf@XA4em~kUhTwlaJGtfdKe@xCWtBq$()WxmuNG5X1AiS zA~0(oN)!?PhM^WLFC>WMte~##u0DgWrSLmtYCa1-98fH#Uo%O1x=5kQssMtbdE9qo z%>A25{Zz%EkI3rY?QD^Qg+w~u!?b)iQl_Jws6t)24X-bu>F-At((%UX@~kndPzD~d zRs`|LE&T2%vw`Z6p+VV)F&%mrk5cQs1j|60SY*LG@Z)P~%Xm6?JYkj{ONC0@15I5{ z3^WwQt|bKT*QRMS#XGhkW#*GhYjjMPno=_TY%(p2U9<5R9!wR5AXgH9ZaipOI;c+L zNujK6Tl0pqMh2@$D37j`dx4OLk8-Id5{8+GTeAL{$(tk2ptV|l`yOZMhz%CKm~SBu$8!m6FrRb&?UEgXp5+Qn4mY0@) z5iR-1#S^2IMOV;4o+14>8L-;vRgQU;zi1^N5=I$?RH@NC6>GjlNPnj-)|wq$k%NBo zuyx*{dUOmTcGTwM3Y&-f_VOr+dpA?5c$nEJe<%!UPL2?TPNW1&qgpyw2{RXY7xA0fL@sJO9hXY zGk*D-U;gIsyZ`Jz|2LoM&)t0RI&QmjIhc9uRkv}} zIcEck;r4hCc1Xgu1xPxAX-SI6q{PO*o?fDRt;?O1OjT%4Rj4>z6O;dut>I?vX zIwk=f?Yo7<)KSl88UVx&R%ve3c3LRQEpuKX*_I*$wsO<=VClDI zR`f!9hc6+l*!O3EGXBOHwO|vzqJim*fzhoVi+=(vipjN~g_PL$c7Xbr2^oj3qZ;y^ zTZ;^~g9l%muOYQrN25|cAYfC1uIr>`HRC}ZAm^37<^Vk@LwvrTA&0!Cr z+4M#oqffTU;&oUY%LiHpyQpMVVFzL7XnU1CRIdTenys&l#UNC|Cc6BPiiK=}jWH-I z`le1;QAPQh*z4#PlqZK!4w?&JtZZy-EU&DrEaYt*3*WE^QSEy$<`%{-oYW|A>M^*b z278xnm^F~(?a%zdH-<*A>bBqq4fj;S*Nu!eSk>c@*!ewzBi^DGe%(DD@g+9Faf>jV zcajB=)WL!T2Ubdks5+OwSiDg>m%S|-zReKAug-@pdX++i9p}Sb5@n|ZfHcMsQZCe$ zUKU3`0t4nyhq(-WJ3^7!Q9UO3jpp{t-F?*MH+Z3l2~aA_Gr*`W#hXrg077q_er-EqGeH*v4t69LR1qax zXlQAnV-Oey<1pIDk^+(UbmIry0!S|>K*%3RZ#b9~NDMTi?m1`_NGQE~cyF(W!sIzM z&UVYiniPENp^?E&MFq0r?sc@ahXk2YWkjwq6~d9K75) zibS6RnqIIpEZhiG!IvL#-MH4ek%L!9yL0WM$>NyxPh%Hyr{*||mz^Pkh5F-9WE(&z zq|5A|RPV%)8mcKQ5rcl+aS@m?AiYreVe?~Dm`2o+`@Mf&fcvyz>mW3tB#GBQ$Uh$b zX#wNyWBlem>z}*NKw$N`3s4J?TYSC=^Rc5&Sw-MK{nw-a^uN|W`;7m+oLdnXcF*uC zc>Rt+zJ#PS?oTV&hD3Ud%Xx-2X_e}Ogp5h-^r+Fi9uIE12+5f+M)>h_)qY3{r+J)r z3j+JJj~@L+DKm$9mZHlPKpPywi7$vDryAH0Q&8g%*KkJ#m1}@vNIzcpQTzVK>nlcv zG+NM<6vFNli6V__1l~4~_8uS~K86k7137$phSFEp!y&8@jn)`N`p+6o`RBPxZ8MD6 zg-zp}`lVZu$%?TSTcDi=Wz7`S5iKPCy>m}Oqt-play{21G#kBajn5!?KbJT!Ng5{}u=!>KG~b z!sM$0R_L9qD%E&FEHAmI${)pL%HJ-mxb_y!pdl1f$d%k6AzBY>|4gbS2_Pomn$jqR z9rQ~pGZq>;U=kQXMzx)v&N^sp4(r6vaww7Xs)pj?R1a61G7^<5yUcp00)f5kjz)l8!I@M1;aEwWF|me!n_PoxlHXmr?;b5fB9gi7LHXSu2akk5DiMt? z;f6XuUzVX8zOo9CU;_Vz+bm||SNY&L%!KFl?&IM@2&!O2czlHujaxs3PoCg|u+i{& z(WK!yt~H2jvXigLHs!bX17s_;koVp{?;N}(tM2F@BOv>J=cln;-D+n7ZpNK++#TY? zaiAvtnzb*rX9eVdCHZ@a$O5quQuWM^3N!CJKE;SNl?l#nLmFVVMt3#y0sU{r@e_i;-PK2u2f}BbpMYnAXi8l5}* zCN+T=Gbn;#a_I){coy{T&4ZF94YThHJFuk9jTvuA#gY*dJd)9I>pd>Uq0rfr0b~G>@@B7xk0*ENX+mQ$ z*Cu5yI0)H@#8n`N6fPGLjsg+G(W8OJbC*J>4ShXSg_(dG;k}?om`e@t$^g9i9KHdD z5sf~#(54Y;jFE@HVuz%8KN+`!$6zIK0Zq?TUutkZd=ooTijvCosamd1SJHtMPN2AK z+VYVW3&NkhE{^#>5Dgf87a{))oxVsJ^HBv~l4J z-Q=Ic4)GO?$QS^P7r#*Qqhr%P&OM%&%IG~R&JEMSWeH-Wi~;y)s5-{0X+;cc<0JgB zrTvlL{yAA-U7WCpa^SALBVI>^|Nr^`XgBP(L!xo`t_ImuVg*F+g6XGp7LJi4L=X<} zl648epuPdxM^`qUn7@mBbKZU5d3A+_AKxNA0nsyR+B`uS=9j1q)Mfd3=}8CSFTZ;? zpP1S^7*7UWp_U)#M|J9Rj+;%&?q#$Da41W(SBl}GrC$di{ z)#8mV^vCiL1GTL$P^m*jPe21;A!dYopPf1W`w>Ow74QZsRQOf@e`Oh;Ji&jf%k7ve z$0ObE!Z>C*-d_-!1$%0(4k+r?s{zv{fn4wAAGtH4Ge(r3dNJ7ZpZ)@g*Z9&R{Yktl z1$dcUr3S=XSe7!6rE}jAfemT(-YD9BX_U4IN|A_PaaSB_W6R_hwkMY;8XGL!!5Wf} zdY#TyePyq5$GBgR5b9C1iJRpvq%H;gj|2E`VWI<`X4b-YG0wtpL`mM?f+)k#T?8>c zkGUZx@fJ0meSHT(T*LZ;Fj0pNz={Rq3%FkK3kuedVnO;>B9|zYhvG+}ODPl1g^V7h zTFJwy*~*vMgAvpct6xsAishR0ZbTR&Lw_$l?e|Cw8ZAwQt3`L1ztpH2jcD@i4$42e_5&E!5q~dn#7F<(){T2>iura~jVpBWiSak7z zhvQK7jYbwtSCr$83M|XtOElUbbXDH?RU2W~&mPnC7*NhTCpPU^NVVi?i0so6ghhmc}|B}&^lhC za0WYgg^K`$j@}jMc!EKz>!e88>u`F@8%m9?2sW|3uakAg_rE~_6WmT z*NptjUpBGsx#9L+?s_HKL>6IId^L*9z%ryMgSMiqpM;l=Xasa$;rWSwwdDA-NK-OZ zG)=Ujl2b7`m;_LdG`K&a!Rhgbhfi;h8E2Rrf3)%Zaictz#m5n69PD9D-H6_1j71{D z8AdL~8gD>xX+mN$fwF|UWfDmaF6N%`mj^Sw?X}L&UEDK_K*$>COIZYXarHXF@&c2q z@W9J}W6?H}i_FdAoZw6#0d8JhAqHGyh4ktqg5QKmY;AUh1O;PJZNg~CJ*Y9=5l5sZ z#*_>WazHpa8@z3G>t1&I*YL9NOw`f(eGB>WV4O&|X-7w6URW5)(%!ORVt63g7XU8V zBgKq#!oVe?-N|%(6D2J9gZ6c(Q}lb{#I z;Ce88(L$`%uNEZ<4l8+md){BSbUIma64fH z{mYFgjB1+4X9(V8*afq_8ObHAqHum_UfGC9i?W(I5e<2qjl<${c2l?|1fs>Gj!w}Mr>ijf=R2sheD~CEJ!G(@4_+y!2Psv0acjqWYiSong zLAroq$8yvVfLl?qL2)C~vcfv>aA#O@os_Z|4W4Sk26zN}bjV~T-`K+z`$h*RltZ28 z3MM+snFBk8PCpdSRu4fF)G}LCDBSO9ke0_cG=cqH*|7EzZh~(mo-|EaE&$q8@W}(VU?r z(Ts+e_Z;=;E*@%$xEo0At7Hwwl7QT4hfywPQ$>lAVMP&>U+3tIhyV_mH9^tM9=VCX zCQbFy!q-MJ{Rg=fYEp1mGNinG3ffM773$)ZLRQrKw^@Eu8e1R7P zcmTVGG`TC{hq^5)?7%cFDU-Hi`rBl1t5-iV_l=-yyxzQDfp2bzk0uCn(XR0J@VPh5 zmoP(pf2SS4F`CP^M#bw`NJlAn+}8X)w8TeUom&<%=OQgpi77gf=rr8u+=&%CucmQY z<>ANu@yDS*vIN;4yB<~t(U;CLqb{+2t|J-EU@nh{5mh-of^m8m8SLk+9>Ll1JgiQC zNjNqCmIP~q!2XLqnh8*Kz{8nd!tI}e-bGQtfYpn}kTq zIE}$Q5@c#`0a12G+nq5o(>v|v7);hD@4x5{M|8zj*szhVswx348~sWVvO7$)5GAO{ zwpWJ(Q6}~Rv0<^Tt4NBox>=Zakn0{>S+cOS0=!K*wtlyEqOe651e54PFOCjgI@FZtoW}5zCZ$McYFX;AXU*=pL{T;nvI1)ZW3nC)pVI zZrFax%z%!|wDtG^yOVSXlzxI*)BsHB)+(1XjsVkBqLBjA&Rl;5hn>_O!i$mcFOtM(MlbwS`hVlN%@GlJy~oTWZMmdZ5la1~rsTQCvG&F86((QIF5${t3_C6Y(4oE&T_@ArPyoN+5+x@x=HovBdOhoqOQe+?<3MY#__#H zIJEt#}Bc>|B9cm`=+2F(kM0FA^W2oTadfW#Xw2niTKgM@g47jz^f#2|jZ|NsA8)>_|MRlcOV z_tE&A?6%kCTi@k>zd9f#vehGuepti*VOBM}X^{}Z9gHvhl#5{J$jQ8<(L6DpTp^HB zB^0tRSD~Cl*hR1M@_>cqo#q2Z7Dy9@Gls_)11U}+91>;(6FMvsLE#(4JY;O)#EkHG zi#)l&7Jr~~`)QQ*&K=*J9EAA8Ed%GU5*@Z%{U=m17^~WD?3D z!%$>?9U=M%b$BuCj<$zVT+x{;dfm;7Yhs{&yZxh106V8t&83=+``=x*BE1Zu+H5T6 zD2JP^#05I8Z)i0UE9|brqGJ1}t$VlbeTqofTdkGl+p7`c+j2!$^V*&c<=}g+D)EIc zl*58JhudxrE0}|+d_=y)vxr)wQM zXh2_Fg!!Ehqrnub=4)V`FtS1~E2Io$$1G*IxEPNmk7iSpR_Y;Mo*{amZ|o0*wBxR3 zoY?1FpGXM|3&7>)*y_T5F+5Tj2P@DOFQAEbIMDF0-X#HK)qxhEV!<*5T2I=~_BPJ@ z5-Ze4@8sx8K}i)WA*L0}W+s4;!ec%XZLY1BT%w#6O;En&?ZwO$bUuf8SDg(Ko2iG( z%6<$=GH0nBBIzB-aRqeAf7zX`Q9`AS)pW@{9&oXMxU^+CBlHzo z@N5-4k15Zg@!#VM7wDR1 z$MiD(7*)9{Tx{NHpN=>A3c=hn9*M(Ce0iK1@6dDo9wSD|wQZ8~xx$bU7itn0#F_h> zsK~Vg$XG`#=0Wcq6~9=@Ynydc#9cS<=T7md=zAf;C5C{+*7*1K5W6{lHE4NlS0pcp zQC#N*-j`MK-I=A$9!p;XRw(xSF?Zz7%o=De&WG&&LP0zKxU|?+mKM8gT``N=veQh1 zN!w*>Wi3k4(0^R01p%8vZ~xFmFhkF<{u^w+1QD6yR9CQC>?UW-5}s)NSGo5L(wmIe zKA7~sWp!-S-*D>eA-X&=&55cL`fT8{{?o5XKVMT`IHY)|J~d6}iYLU7AlBMu#VyW= zu}I9fM%5R3<77CF1f7Oi#Zsx3HyT<4TODtqZ!m_ah#bOBkgfeF;XZ{#XAv|l6Od~M4a{ECuK6oSu=uArV@yk{H^KE?wy>>s^XFa zMe}4C)r_;IYaeHwptfZ#U(+&qY7UH4Jr+eSz_g~|={F*2i?gP@pZL`E+(8tgKn&N) z3>wA0yJz#%nWcu8hzYUCIE2hfgN*&N)80Z8@pmr0V2STR3AFnQGK`*PdWvRlQS^23 z64+X(B)4=&k|+HM>WMdA%g`GBMnHv=#0WD&{h13u;GLc#H-?=uv>;>FXf{>1b;#-h zEM!c#kG4zS`(x6ltN5^;+r(5AJ=Sqb^QFGK+u&cn(6XuN!tsx&@tG=I}< z0ONrt_Kc0Mxy^il_vK5nEIO$26D3?iUx& z#3#4*>QAH2(P0PSP?hI#!LQr6^%JmUAYjB1Y=s$@18_V!0npb*E7>LozYmo(Si8nq zPoZMLn_+CYV59;(n90h078g7)6F~Tr$~j7JV&%p$l~uA8A5=Z^2%b*n_6A(Ch4He? zSbouf6Upz(;e0U?Oqn$s+$!aRuVJ_^VPHbBv+Z}oy;C&k0an%rMi}0EIjlC~i_~oq zeH@a$g-a34(xDguZ5|z*z%B_J<eVQf?>y;(y>>z*ZxbbW{&*$!sZLrfzD zI)P-z@5@ZX%lXOcxf1YTZEPbVf6!-nDfKl)>*g2h;1DDo3 z1oQPzy(|N}JoGCm65)fGBRZqzzpwkOvJGe>%hpSlR;+F`j@V+Gk7w)@5dK21fEh>Y z+a?rcq7&<^E{3T*-KJR?c+;me~8KfiI6GCZ@CA}fQ~`{OA-!Y07r#H zMh5H9&tDLT)K1~|#>^LfNBZz28cRhkl2|z?k3-az)__dzIQMqBn6)B)rO--0S%p|` zN}JH9OiOc@R7<;9$L9sVIHMH?RRy61_MtUf3q%zoNNt8v^w#uD+T-2^MEm{GfEWAY z9VrG?w_{+TyS=lwNH^@ija#*`Vk4|eG%`u*TGksJ2KIRYq37+lNukG$r1J-9b@B2B zmRfRWo#dy()7-1V~UbDG+ zx}SgF?k=KpAfhMY@9GXDBTjTL5piOB$r3i6Y66%5b`7^>8UbeY`vuw5ZLB3e8$Gn3 zlLDQMvtH!!H;70Du1G?EF=~ZJoC`T2Jn)BgOKQ}K$>?ii=?`-XbdzUNy*GK!;}*Qc zvNFglqUUA{$@e4h$_+qrN8J-@yUj*a8DCmQ#@<9EP1OKR$Kx=8x`EhGEHR3h1cL%t zoJCPhCI!VPVisen>_UW;Mwt>Hu`G#ZVUL{3vS92; ziaDUp2#!I;d}sk^a5>E0F@aX$Kzhhg{v)J-nZ%(uxx;8WfFQFo2-uP$5DWH~#0OH< zI34$(L346vKure%{)6trF-Rnq>mHj4sYc#GglOe)rXGPbBZh7hgRGff8UfY$#*62m z#KnzJ32EC;o*?^&bsP=QY*u-QrO7r0X$}Y+@va}G0$kl7N(CpQ{0vY;SM9C2VqUrr z2IzX6kRgh%7L8$(0pT2iM^u_622fju3(p}~k*u3o9!{a+In*viZM~T08tt^_MV$o~ z2kS;G&S+!-X^_7^lS>YmTqM;Si3LKeng>AUxIrgvD}J7;slNP2;Qf^m+e zq#uL9$s%pTj^*L@k|^o7!=W0Kr{DbCqb#=%8T5=xLl{+jL*ySjr95eOTc5xeV_l?@ zKvk1*#lca)S)-M12*IG0>uG9R+!=vnej-VQ6_=^l@R10Y0behX(@m3e2doiQ9K9D2 zFy>?OSPq{gl!>d@ngCDdHbg6RDy%fN-|;BBx} zHtk;1Tk3B(Miw4-9n3%e;$yeZX?>$J6oRmi)XWVys5d&}!P!1+qS?#i5#7fxJK4() zx+cytkplFd{R$t>xQykhrsQt95(%Hj3%-%BSwi%AH#gY9$ZuiWLTv`{Ttp z>JbQp*B6Q)5(Iw^V0ROOzwPejW$(1z!W~g$q7eZXrUpn{W@*q0i=<%gkn8QnM5!t2 zZKGahWS1!;zeikSbNU^L+I3=DYICA<&J%)&L#9q878Za2k6DW-8=!aMea3xAZByi1 z0hthPaTr2Dym(8=HqYyWtq~P;qe79e~s8hGy z(G>Mngs-0wn7U%7+)hC5I@dF9h( zM6A={8IWVIjgrEw~$Dq|S}@W{;F5fr|EK$eM8e zh)IlrJ&ZK~`o_VH+NXOClVQMHk#ihoVy({XLt#jpM4A@^-zl^?neB;2d+i@ zNnt=}_yHL5%pF#JS#j<~X0%0baN=U^z_ia9Q&IKVoupYwzoLqcN;|oE<0%#*M~uYk7I4Y#1}Ei2^DVS=x4VEQFx=iqEJlOm*Lg@z-Wm&CSvA zERSFn=92lW7R#;MT%=o%R~_dC8Zf8Qf-7cNHq@jalpTo4l0nYZf%I~VEl>QKZT!?y zHIW1&pbJ~g(P)}UX3E1~cLW~5{VhoSv25y1DIHhLu)!cZ0~ z$kN%8Y}c4f$HP`DD6#ZsYWD?#M{Lne5jiWO6KF+(|V>-~rZ=Qko@|!`RlIN9IYqUXXl10uXj! zs1L*N(P%R14b8NtQa*B55Lyk}x)KD~2m!Jru?DpMG%%L>iBW&%WEA&JA(iYLvRlJ@ zAd+{bt2#vTJ2{a&x2N*)bJNI5{cX>hRIc|9z+Ya%s=A3boBi(N>11m(vA>T;@7S-d zCdLIDwueL>+L8QjiYt^{hD|cXiKB?6#wEAE((9JlJvfMX|ILmh9XKuhdoV(0XOx~L zyVDGCga~9zNQ&M0_$=;6efs3$bO^pI7vCa1qN-cGWJaAi!@>&*|L0p$rw&8weNc#A z|5S#XjKKp_UE-0vp%3pMIuHq9qhUNww|B_G@;h2n;du`(ny!Tx9iSRQ^#tw3{r+ia zQornyJjex-mLEs)7u5I@T5KPHEDk5=?%qS7SPz^%BOR3u#(y2!Uepv@r=-VcT&1Lw zC#THxt5B}*Af9LSX6xqaD%9;;tF1d?ujiGOl~@3}GIhIJ@x@X=e^vG65%8y4^S!Xh zP`_h28a6{x$UU3q_CY!jaeukwo=hiQIF09jlnv<#kmW7Hm@9XRwLlSx1 zV@cs`e`Manz5Wr{)#h+}xH);o^p9GuXUMB%NoHFLJ15zqJR7UzHiqQ`%r}TWoEXKV zNSv~Bp;VYYITd?29>!BdnPzY{@bT=pGwyZEw9vn!vpVNXi4V!8gdea@1zK(0glQC% zwsmj$R#|VVpwzeJHF$F5HQGgSL4+_|Ks%X8NS=>Q+iE6Y3P;xI~sp&S&x zDVR~Tc3Cf~nxl;akPc;)AW#MgEJ;oaGC`=jY0f}N%3RXA#t;hpTpSs7sYd3bPV5H@ zwWF-|oc^C5YLa>A$Bv(8l4&hcnSBh0l@`s40WNHC%)zIJoe;ZJhV24etqn#Q;fk3R0Mf-}{^i)zpW9nDdvy;hA$E^eu)m@6+A-bm5c$g8ZOGUA=DT~^ z$u?L)t)Bk@@1G)Ix<7o|w>n><=7D^eNsM*sw$XoD@4CV#4gqMIWFMgq@qV#mEf zogy|jHeD8$mmID@pKZ59GPj6-Rv zme4zaH;(1}11u+rcC;Cy6B*x&Dkb9dMg%3giv-UN(gb>N;^Ol{B&GkC;!+{?sxt(Q z2E~R6N9Ja990okNDB5frR!_dqPjJV*vm`FKk<83GQ~i)}$^_S|5`j$msEPQ~WnQar zos61DB?mbn(dI3F8y#W>r7k3G!KWojTO{6+dxRF0wY0jz!(1X_K57BW_TO~S;f5LW&k_2Ns1bLn;P75rh*yk{g_^4%~=)!dDCKlDIU6?88@h(iiufSCp zy^Lgsy_|mA95xM+ z&J(59xdC4H7(;=6$7?O>F$nb+t^b&YXdKAs!Z)`z@o&t%=T{91PldBRZJRk zUx^)M7DG{G%_>0-@_QyI-$62wd$r&m%YBckMIP58c$&5CxsfblK)k;~miRxY_=E7v zz$zTQ&RY#*ZYwCHn5GE@KsGW+tCeA#*!7&8j7D!&rGO$kkav4y&u))lYf$5ZlFcv^(K|MKNpjNk__KBk|L%v~0i8M<>*e zAUfZ=c?U8yw4pnqQdn69Z7p@IUcp=(4qXq)(```dess6b^9F+90}po0!Hf6=ui;nf)YpN|k@&Iv%ZQ(qr9wz`NX^lzT!CVpi|-XqzZuWDIv^$|<;AnG z=~me4_SqnDpE(&%;H_`t=#d=RR_C}_V^|m)x_-8gq8pvl`qMSSe2DbMIrz&#=X8;e z#$vXOFn54FhA0KdOXem|rjJQ1e}?}&XxznrKKUe0E+i@PN+G8K!ea(~H?c*@(u$6| zB1iIWWFX~8u`C7&`Z;!&z6mp;qW=J-1Gp>5{dt+;Di+61k0EQ0c8*5D#1-R=g%1** zUYT{8frILF?y6M1Vp6pf6~h+H?yQ8)oO%?CH=D7P?Dxll+sxd9u?<`17jJv%N!bk4Z-`b}`8=6Y{Cs}id3+LsjscQJ**j4a32BH3;~}$r zz?%+=e`s_UFFAjMHC-;F7;sii78ir7GT)xnQ8Tvs6#wj-%=Ocw>{1WHEG?-a|J zM*uN~_JY9U{{`m6lHSQrx-&%}&ba@lU?3Lw3pA3}7=B{(HLy(z#o6(6sL$z=@OkS) z+B21~$TjF}LF-v1EW1`A27Qp$rT+6=WW|x_IYzu@pDH!VXQ+6xiI7y~0o6RT;X)RjAP!ioD7j))`<(QvY-4wm9&`VQ9> zZ4EG%m>P=&F^4wDez7j3+G%ai5>ICnM53lcpwyb<#pXU4E6H;QJKtE`7a1)`M%;*H>i~)k+;bA5M0bwLfaWaCp84MB20n+LL zxX&`&T@C`U=5fT$Kyg&wGLVCX)*&k@?;*+(wayJGQN?Tov5h@v-Z9|NkA{z)h$O}; zd=h;QH5@B2=1{;frobZUS0Quzu>Az<_XsD75t;b5EORb|lMM_8SEr0P90KGL6Ba>I zS4)h1ekPp6CS@MgnB>2yJUW3yfmi=9{8P7J@pg`Zp7+zM7h>J^>DAn)FTSnV^+hJ2 zl<3m=hV(g!1P_k+hGX_eh_j3+NDlqa#rqwB37~MhdgPcQg%?;4GzjUrsT5=YT>*pd z&-45n|9nKefP>Vqd<}Uzqd?1HS~?~3hgFwVJsv>bbb&J+LJ({vd&qsAyHz7 zAco9ED1>kp;T3uOK23J88-QB`u|o~aA!&DjyP#rS#IAdyL+x*Zsu%KQM}+wY3Rbwv zJO{9n$ww&*`0tp(4v+f5G&dOYhFeNwbND?IY4Bj7G7G7OYystBnAV`TrmbP%MT6d& z?Aac6V@o%oO?x}0sYI5xc^z9}^;=cCf9?PQp`x06xe;ZM@ZwC4EE4`c+R=Ey=4I5IJU@PBA^ zPVIF`(a+J4`7CIuaZ_RE(lRV3?1JJvG+@n_UAUgjY(5ibQZxv*zIePqNUXSk5)dF^tFcL|pwc-QCOU7a z8IdsgxWITIXDm`piu5LjvA7^sG&;jesC(2MI?$T~d+~HV_C~`+2pqpwiNJUWGpM$7 z$3y$o^Jw@J8@q2IS(*VOD|cI;-ujfrZe00PB1drL<_u$Z{Fw&+;4)w-!*3--F5AT% z7U5~XPj0~_EJ}z?juE|5@R4ju0yZ}0AUdTAk9 z03HFozn;RrOnIZZxAAOaZNHJ|hVI41R9N6nxa$k1oMxIaukUHOCrgGNGI^T}Q zY+Kyg>z_=i3;7ShEoj~9P}sZE>W^BR+rG7d?{p~lq>EvDJnoz^#v^f0FZ*V4nfn&a zH^>sqAZpI`>;vktOCVaEgg1NWQ@8RN=cDy$kB{_m_OlH_KF;H#j|%V%rwhyVE+9qMfY3yl;&;W?Bd{MWf1yd>M6SQE zst8`XP!(wWQiRUAQ{*7H5)(4ADQaqA8qy8CbO`bO>mJq}tiYba!y0hJAFY`^e0z|* z{1q)II!`$YK{3I{_rD(=jyv71wc>LtE=m}G@W1@8|H&I%+osf0R)6#0I+jQ<;)b72wBBhsf(v5t%p#>>QBqhl7 z;WAQoF4ou?wk!Yr3lBp3-xZ89z0e>TYF0e&uqD;U(L>7I^GgNyqrD1r(?)P&r^9>&^i0*{{D zknz}S0)Igm8YAdRhd|hO_6~BP6IMmI)u*HlGVA;*q*7FcCB5~*7i7%9FoM?yCMiZJ zY^1BAVKCd^KL7TBW?>~N70)^Ja9?+Qg};!ml57i&MUJQ|_0b;p_OR$K90t3H+2thb zT?3{Ab64seqVf&6xB~C2H)@MjR~k~;))gy~;FBq9Fw}^O)~}vdapC-ldU`p)U1Csr6>g@xD~03Gcr>PrF1A>;7S}R*fHvg=ywrN4g%3 z9}EFQY|!guP41_`f(XYYvn=?aaB-ZvG;DF%nBQ#fwUd#=4zi$OK{HE~_BlpDpIm95 zZfE?qP}t|BUtKpRxmhy{`y^}l)Rp{6@1v9*vbsKh(oB``VYj|{kwtCU^HuVp%8Kr3 zatQU%phP$nRG$|E$_R9vujWR37>pKjtU7h#pVqs!MD32RO};Q6B?wy;Gl7LBJ`MK` zKc-f`Fu8~nTI`@F0R zt6}ZsZ0Q6d!Ke%NHwW59yyzFpziiQjZ_I?dP>-_3ce8b^D&~g}b$k9XR`C7O7|KTi z!p*xIWQ*rpe}WeqDT$AMgDnmKG?ehnsPb)RFooy_3ljXAk{S;q-I#ylIjW}&|9}KB zh~PpQ&M$u1csHiQ<6+}_CP+JaAq4xo@0b`3(8dj+337D^*)m)YRpkz)0DeBrhHsYe z0;8;~VqCu{B6{?snT>Nb;Y_4u@4Sp~!3q2}oJj~X)4GWa_vUX8bamDiMlY7*zv-q_ zZ9k&X$_|iNC@j28rz0kmTL>4!pVL`2=49W0!D;@|!AV#UN62BWEa&0F+oWS z&_ug=Nmg>J0Nn9ZZS*i~u(iw!#TOMvBh;({>(aJWNT>pO9;KVYubk`CSyb{Oeg~AC z@zBUqe^27Q3=dKjw)XrRDr3w=BQ7eMFjXdO9GnH*6wG?=X-7bwf@ima(n%70`9rMc zB!qCFOM1f&M)21`ed+B84lcU#Za+h)>)BRr1L6ssV-r#mnOixj>fHRLl|PRQPUlL` z#42CRTt##aiN5GnSO^M6L&0Wd(MXTaUTO=iuZN>|7KOY$D;&>hMzeJ8aqn&KEZ8K^!L!MyN{J|ND|jDF$EX_$-iOA>kY^V`trneyMAS1>0v>b@pe5Tw<}g~M z?|l(WT#ulH1nwK>IhrS<@tc0P+k<`mLMVPUyTEj*LIEmVK{;5cuf@&CkZs}`=bUGy z2_WQ#{m=)VSZd`#x&y7+iZoozCNL3dN+Yn+1R`=8b7dDm;2DJMe_&`i@q<1uxFAr1 zZ*zz~tXYOYUX_gS){WwE$IzD>Mym){kAqlqaX)+HKreO(J^Z=>bOos~0<&XC%ZUno z0XB{;M+XIeuEw~B>IvI>hjYIUhb8X6Eu=*vdUc`s6vPR-h;7Z&?Y;f7jk5F-oH&xsrhRXq$vlR47J32K zBWI;n63~2r^efqK2nOYWBNQs4n|Yu4Y3J~k#^Q(wf#wCbJNwbW_6>3VEz}EYD#+n; z9KvkqqbUN7X*~D)fU%e|MT-DfD5LDWxZ~D9ATNV*t$$g&gxGnm^Z*YnLasgL`K8o6+axHN?K_)5ojO9bL&=8|#IqcC zlkI5J47@a|UC86EN105t_EKCC8R|QJf^s*sp{^0JjHdf5wLe# z%Ni}%FhC|J;(^lwO@4>#1hGnB@h{a$Q6KT^efJ8V6g!26sL01iPjA}d3f@)$e=A_?IFfX#9g|?l2IRS1ovMVNi+o+;FFcxw{I__ zRglPj<%OofGbcp&$qO_nJ%QTmP8k3Jmf*^hNR=Bz6uX!h+RyN zF=3pp#qrJfoPTt;hxEB@@_5=usgiM6NJ!uL$pxiK$Rw>UKmw^%$MgnJCJpznKue|@ zpyviO9Nyt_EkS44107?k2?~y?-0O3492t?>yjsF z9YdCaj%!NOHygW~Pc}Dp8P(Q({Hw#sSG~V^Yweqz#goVPR=-}oGd+Iu;;R=crz_)E z?_RDPJ$dPB9B+D5+b)|VR`dgI>n-r#uDS>1m3?UV7w^Zl=mrfW|- zqu$pmle;f=U%r`s`PE)FdAE4*+=fm~mWD-+c5;R#5~o@`q5F52?=IslAvkVEYF>kp z;(NJlwhbGUz@CR-9+2AMY+p`0lVcawkMhd0&B)Zy(MUw}IYWSkPQ#258$m1|mEFa9_1L+*2h@!Xtq8t_K152z9$yL98J9Gexn176Mr4FIe%Gn#N7NS2@17sUz+{q_#$|jA*sbYD=tYbh@MajGljx+T>cZVXLhleBtA9)|Z!*baeM9SSGybQo4ZKgFN3Pii!*C}lpUZd z%R~Gd_PZahE?czCM}(N(!)$Toqal+z9}e*wSNsZY1snttG#+x3Ju4HSbK}PUC9bTq;;p=-|~zxocbB>%JJH@NFJ218k@#xAB|bAVE># zrPYV__8p?^0DfKU#C3%1l^D-V(1kpm3CDKG>H{9_|s_f&a0pEPbq zt3jY3#Gcd(V1_WVOrp;iW^gL;3)%Ed22=F#Xu%2=bH#|_^HU&9Opd{V@AF&b0Zk5w z(JZ3Zc@JMDFcmz1x!rrkDHp;sV?dOn0>S5FWn;U05>CQ^j%CMQ3At)U;)h-#7Z$bB zdSeXb1j@QsF5*(WAU_fVLOav*j)C7eD|IZ?D;Q@zturH%mYr+Fv)XWyh;v#QSKwDE zF+iTVLf)e@Gfy=dLyCT&9xg}zBMzVPnnzf|G+xaD1s4JR1KET2p!;*ZC{@md+`_JH zcHEemY{|CXvS=aya6vl7vTB~4{F^8LHFR?Rdv;;foc^Vr#V%+2lo&3nrkB-uDSKhx zKtFXU# zsL*1Ci4+TgRTPz_tgMK;!ZNo=6VSf* z9LGvjs9AQ{LmU%QPQ+|$2H7{r^R$$BzK3K2#yQw)Vv(g4Pv%5pg;b+P(51-BFk46& z6ZOL1_w(Y6nVX;%U{{jl;|7}jo=OUfH*&CT3gcXiR-Cen>sNc*TlDW+WfMqDwjjR~ zw4%WIkD72Uu`~yt1VEF7o!)MTm$OmqZtTC<-2$I(9#1Bx+5M%ZU;wbnGfPNsL3=ss zB~7yga8uh-B?JXJn3iG73+>v(R&D#p*hk$f?J*_igpwa#%@D68PCM-~1VUjyynx$q4 z&FK-^Tu?}Zv6jaH1>U`z7d8v|@Eup)ax;65P|KI7Qnb&yxe}3;FNOZ_xL;=r=)MLE5*%fHBz_=H9#tG)t*qXZi15)H_7HWKCvaqwE3H?R#GI9>r{Npy=@H|@GK&f~82ESpq^F@hY;PGa= z+i85%SR3WCP4a{9(BQoE?aC6qBCF5%9VtuGQZ%CxCD-$j8=~=`C)fsiR?$qK4SNR$ zwSj~=?dOA|6jm+if*@{_V}xnJ%yWV&HW_VRwD~k>^lqGBGUB}h0FQG;SzWy==wgGY z88(4ip<2D653wg7-dZm2N^~gEJN)QF?A9kAu73In_bV(RdJ4n_!%jLM-hS){entJ{ z!`tigd=R?<&_+frR_ItY>usT08Y4eVU}EJAI|~h{kmo_EOc3$x1H(yODY2QIx?0)J zAC8(E%F9VU?8q=kI5DAp#RH3wANO0b{TXo*780A(B5)nWka*h@18j@hPHymew0i~} zSAmCyEEfR&aF{vR$4ot_zx&E| z;1vp+@tGTS#$IF$(efkj!(xoqfd{=;Cw;g+heu!$HB&)9bMhNJ5g?Bk8055zaS?+d z&^e%OApby2va|SWIXG{wqx$RqMxM%(-pgAa1uCe0ZYT|BN|5|$rBq5J*Fa(;eNPlX zAuAn1MnWDYg&;(cMigrPwQ>C6nMzcQKA~<$638w!fJbBog>W+$FsrHPFoH?bZR)mB ze0I6D&is=h00ybA?R5SP!q-Bx9o2P{b@R&*`#gIiO3}|@P#zzlZ=Ms2!A@|DQkV9C@o{!v$dRu}SlUewC_jNMn8g4M+Jy-k z#}M_XYEgmp&3@v&Fh||?HNc=qtj6M+qzEx}&2hD+SAz0HB3t5XOFHCCwaA#poD(?T z)A{LsiC&9T*;2DjP4i|f(?WW%7@cCRlGUmLC1nOlY(C!#9RAlaOTj8dDNkS$tWrr& ze3~5mT>O$R%+E9RNFnw)&RK$Oj>n%_9F@-m!%i)KB?xb3ZKTsf`ffA*l!eBRdSJ1; zkQ(MF7FZ*AmljZEDM3~$EZAjk3gUn8mMW&X>}Si;h+`5e;tbqj1d)sp5OA_|AV%(k z@X-`Yo@=UV0adbv22|8jW>3-a_B^#i*#J~zo}RvfyIzYZ1WOWb^ks8q(9vi*RyMl_ zl?Ef{Q5YW%W1A2&V`Q)55P4LpkzY8_8(8Ai;`MDm>&Q*E&W6egBiOCgTZp}$r2zq; zU!5RE{TT1u#eDhq8T`cfb9MRV%5U)#BjVl_XgND7s5#$YYmb``Lo@#u<|c&}F-#Ce z`CWNlf`&fSlFrtbFGjVC_c*N>q;LS(3w(CBw8U zwa>-IE$F%rWwr6vMGrXacxZA!#bbGhmJq892D4?_$OxkquUP<=-)N*`>uho-8BIs< z2wC&k#D}u^AKq+#xcUVD?`?A~7#-IzEBvoo(LcP2UswS5!<*|}2=`SpV&K5wlH*&W zC#?(PsV?N{tx*snK~?TEvxHhP@mX@cDQ3cXsl6WHiaZ7F4@yT7Nr(zMphponuKL+= z8b@LXr|MG)hECKdLE%yu!Y_;5jXyB|e-1(c;)8?dxeHhs{bUS5q==>Dm7EPOhnf}k zhw2wp@I$Z=_NOIL7?n%?1~d}0ktc<6Rz6A6>7EpVTJ_{z8xEWCoJ6$hIg--w=+8l4 z?a2xg-5!Y&yh=~xh#?@1T|UkRE7Y;-7@86C=<9WDz#$FNG+~E48Gs!?%}mc4kVg}X z%4Li%gdG|NVJJDeSN`}fV(BwhPfi3jEACI>PW(=iI{~)Dfn%PUf;T4yG>l_feNZ&Y zl5E+BTJ++y=0POFLjjS93AFdX3^YVunT`V7mN{w}Qd6g60_4S5!|+m=jh8VmZUYi) z83jwqvxs*L`x9hSwedMO*9?2DouYcwqEm!SS1o#_Px9WqdkPB#@tnvNjT_5QW%)=3 z&Qlq{LQb&KZxGe>PDxU#7&VAbEBKL##uCy>NkUpKN=WYw0&mtspwu;rj1&MvN#eN# zAWEV@5egOJ_pO0!2N(cG3)~O`t(cfPgCx9Pt(nugJe<%{1L-uD3CW{3o^;0r3n7sb z=AP3L!*mf8BqAprpAZ=WBfbcpKmgB4%@=|RnGQ+3FoUuLoHKVsw8-d>BjKd=B6!Gn z`TBL97G3SxNd2KBh+RqIQ^E4!Yw9Bg_e_fU!T$lJ_&|aMwxsMzdjduV2GnEmIkm`V z4IhFLMIv2*Nu8>S_b1ILR=|-LrxPahC?P`?MC(tQ+MNZ|XXWm#i$XEub*2?&#v3JGkkBBKWHq%h;du78_PKU+k?X!m*o>GGIz_qs$Xxkl(cT zrO4>cKIH6ULKCSDS~KzB?BO)4R7cG4c`jw=VbRJMeRFhp_{vD`LwYi+riLmh{qafN zBVCjrhA)whu7`34pM*0AoILt;GHgMifWo6wX7~XjZT35U%&@cgbj)% zZoyxZSO#X91oD^%#Q0O*@93S^KFFkk1gy!If#;7q!?!Ql3$A@C;hm6w)kL4cU}gf- z`=A18dr1+`>0?GBP$nHUwN`bstrw#84rSjGjT4v(tGh?kM)SM)7-PzBN7s4R=}*YW zkn)J=Sfu-k;)!rw$+_Yw#v^zx@E#>nH6jdQh;-l4`DjCqokM@5^GAh zZE;RwPtjp9$$Iy=LlJlm7z!lBA}yD8Zt^t zqZk6sxEyR!1-dwFx-D?@<-LOBhDOgcXYLS|1o>WKHFN&-2%&?@L_x9`nBWCqL_i@V z8tNZ{+*K$L$Yg|xf+$pK*wei-jll?*u?5J^*2Sa4wAA?Z6k)USphdRK3%)QnRDtjO zu`+o`?m)}{Er-q~u{HwBF*t7O^+tPVv+*^!3TY3*Zh@!S+gB|0pObprDG=<@hkidlt6DvcMH3_WoswDPCI6*=ye9i|jY=#0yo zIU_?BsNXrpSA%J!kfz7ic(a1J;VKkNlLdDy9}KxLT%L3f4o*6q(}L4KMW2i~XI?kp zrU7Fpeb%_!BoA&u9)hKY`C63&dJmdTW45|Ul}QOlD~@IdvB)c~4@IAjzFYlbg)UlL zIrnbSY3ZWeg^*sa6%pXd6v>qO8C%VF%@!@xd)qaY$vQoy-iV}q4 zhR^y@o9l4-Cs*?@Uriz2mQZCf-uPXK{P8`s)f5vIIzX7^DS{o?<$`q(ab$?e0kL)U z!L4(qB+rAyN@r^AaLtJWFdv~4hqh_oBxrHBlpM?l^eULmosM7jTR;iSx<_qf^ z=(4f3A>*SGe*Ci6@5eNzK3Nw7AV^dDDaw-LIU&yy1Ey?Xg{ zW9t=)VC{aHn8?jrEAvi7zRDBE4v^ZS8-dN3nXaPJ&)kr$?N{xsy_Xxi^|M-8xfjh! z_1nY-eHJ7DII%4eO66$wM~An`rD4@<(%!S}m#9NVo+)4y9nzgUtDj;X3P-o-Mq*V-Wp{r; zhEe#p8MHRKzK+zvaeB z*G#Ui^uCK2o2n{@g{o2}!4x)X048zaZ6b#OZOkTDxlaom^?D<07mG+iI2==>M-c}( zPZaw+U*GymZ6AD9EKqbQn!E5@PdKqy$3tNVC4*s9v0>Q(Ql zfSd=4JbtsiAR^*U-k^CF5khgwvV*7BW`x|4tf%rSvOGG0n{0H$zRD-AvZNp*Bq@r$gudpaGKipnBRWg zpCA-=Xp}ADo%#?>Ygo7YR1o`Zb8=7hyY=@uw~8maBk=lOW6(i$D^7wxxlgzUhDTAo z8Jqk0A9Vu$-o_#GCvX!ZOs)8ATd$zTue*Dy8zRG-BQ<8}Kxau5&Y zJQD9=p?ER3T>s_(8;*NiDX;M-XG9`ZeKJL$Eos2vb&p9>HWHfUeJQ9z>#XgyiMfpGJ5&K*TE{c?tmb9pq7fXl;h<-$v zxzqt}HgyBIi@6yYT%h;CxD$aCw`0M;#MbRW#g^#*mes_64Uas$$+$_tvQcrw_FFh+ zPEvr+zT}>XH^e_9U&j-LJ&o@RAQd*$- zW(FHBLYGArk{Yp`3&caciGuf|VU64>ly!Y`@9C>G^rqXxlveI8FBc>*@(C}>gZ@6y z^SjY78t|y`_2&K>gkHYS7--W3ANbN+g%Uz!KGsnSb)!_;%jp|}7$AR_0f!g>Oz`2{ z1jy|4QM#V!MbI&Lk^_U0-ub|COo0;uYaO9X0O4Qrl4%w}6=pff*R+uxmN7=!gjoSN zSuHWk$?^edQsm+)cA^?F&bu#)S&B5#*6afeBaR8!>nK2i48WEq+NniX3Bx=>^d&%4 zm}G!qzcZ6QYTBrBA%-tT*0dTMjMy|y6$SV3Jz=^?H^2=Tw?_5~Td$;cGCLa{9HWzb z|4$(%Io4}Xn3sU6IF+QERz!jVHh}RAa1+)-6Z#F7tje(x|5db%9^yfVHX@kd-N>}Z zGGTA!v7Z5_m{k)o3epNYI(a5gF{L29@9^uBPr}^-uf`0T_86z?-r(H75Hp7vf|_B3 zSiBPztF|Kz5DAg;`HnJ4)AEYlHd|{#Ca9k%=ibv<)OAqUvzoa?QYqx zkzbn_+{?{l;75SvzD}GE+h7|h5{OAa*Hul!2Kg4koovk_Kc!6WLbO)_Te5kY9kQfU zevR0L_`GCI2h%?4$>@VS{XhLv&C-lQJY|S|h_-kE=&@#=9czNJlVNNgZMD~*Z*EnT9^a54FsrM`aw=KWRh08G=mPI%TrRM*4#b|@ z5=$WF){b0H87E71ej>OU92DqR+W0z38QZ-WOr_j8(jW?*{;D2&q4W8w&?CPNU@oyzp0U8*G&X|A5&MSal{X#c-v6psI34c$_<%e~?!yxfy~(5l%2U z!wEB?OV4|=YbX9o;@Wt0ihzD7XLyG2CQ}x9G&O4UhNr;p4Nr(|OcZ>;>iDfcX+lOb zY7QT-@`yiarkKEjj3$y^XrY5f94zeLY@(9C3O3=X>!gIPGi(GcI|FKh0vf6*Q#;X( zM+7k-(}j++iU>i)jxi;?(dr|Go%68bfXLYF4Uz6;18afDPG9V*b24mM0ZFKc{sGch zBhTf9IoE8o2b7FGLcsGAZHEiJ)neD&1Ml)tKLpi0Zd7VNqzEe?$nVEKH58tV7y0|$ z+MT#08YMaCY>uRxxfJtJRK-7?RFVCnlps6_DC%h5oKjo;Y$KNM#$75;RO%KI404|@ zA!?0LxI|SAPrN~iXo4uP7$wwe2$hKUcWxgoO`UE?%Vmu|m;^P{uA@V@B&pe{Es--$ zNn=XjWYQ@>6v;Ml28oan*gI*Ejy(q^)a=C}nrhc?#*evZm6;e$jxfecuh;=D@@t$8 zTRjZ1q79U4Hx40Xu}lq(pNGI27&Pcw5sTb-Iz)*!bH@YgobQ+giRDossy|JPM!ZHO zx^$K$E^{Va88v|?ibN>cox4UMyc7`O@l+7Ysiq%RBp zSXf8FJg60;g}pO`Ae^)`M{%+byUO$a0B$lAh;D`EJ=oD(=wCGNvn@ttnG-soijjGxfL< z!KC+|v1r^Cp@>1Kph=nrHMI(XurYw@4W3T3QHmANc-II7IVYHd4lh{P1qUKEsLwIA zEW^WElR(68PPK|jwxkv4GpKARSRlfNO~8p3+`$xwN7tmXv)4wV(4&rBg#qgp&H`4IA$Ld4&15BzeTK<^;aLGmGB=*9XcGtz zp|4p_+Vd_udHV{-&yHzk!yx#OEdfTl6nEp9xkb(g2=+sn~I#hed8W2tY!_jlhp%R6pi?z-h;nHZQw7HK=I3 zK5<&7BtM4jCGFDZlp(pM> zlTwR{eWd@#KYMoYuim+KjsL#9Xz=CB-W%;+OT*?58-$6TLL>Ud(3G>)o=!&Z$k%;$ zB%CPXgS4AS|h0vuGniuejIOr8EDa6fFwK zhEwqTv6vDJV{u+mf8WpDOit+oiFao=AJ9ny!48`1UV0#v?2E&sx25gsexsR;{Hjss z7kqhZG}(gp=%R05{8a|o;~2@fiJlV1h@f$2EQH~_0pZzQ?;vhq^bS&+Q%kcRgf=K= zXALlMX-FkQxsWFSK9hYBmhM_JlFu`X8I4CE(^)7d%Zfn*?u#hFyHwv>AAAwvj&<6~ zQ4w>s6};BDcpKNZkv_DaR3q@ZNk84#mGuNoiK6t$w-2BUVV$C;tR3|**LM&K=|JQIb`t- z@Y&Oh3W$t~xzq2h9rZVkp@B4*Uo?hHXJm>ve zd$G5_{d{9rvORUu$Suk}veVw%d%3;4Ue421$Ur`Dq1ADG4CS|Kgn8aljS0?1U<#O= zv=MiKkNhC0G&luxY6r74Y8XjI#gplPHpIaIq?mpms&ra|LX5_{Jm39q3eT1MWx4^*8V9q%KkZBOEN zW+d9e!VBM)t1|e|aV@)`+H?-R0tV(Td#nq(wy`GHvKmCeZ>noUf1^RhSqY}@)zSl! zXXx(6?XQFngZ6(;WzRhjLo?7(>J!oA;jmEKbHOjC-XWJX#KKD=)tYRMd&$FG4E7wzT?N(71c^Pw?qDz9di0csD*{D)8Ey8Yfg5 zIF@e8oaraq8J&<_c3(I+kseq-MECOx^GeT8+?3A=K@klw5RxOt3Hz`ZJ#)eznk`B* z@K!R*wI$bl_<1>io1L8_PvB3ai0-Bz^M^$)X%d>LH5ij366N! zKWdfqek!V;OLwh@MzUbmL`v~T!iXOl|Mg(+D2w}V9a)-Jl|gl_UCnocs9X9H$#ga!w_IT8TQt1zF4hO;C351>o%OiK1P;1>h4MBKwesh4OI@R$0(ZL*0)$Qea|ClnB0?7(dRy+_ zTfT>K+O<@?f=Yx zrDXk{=k={d^N5+j(vFC9&>0LwZkAHJ7c~65ZyFnRuYS3M(Glum{G=C=mWqsNKY^Di z*p?`{QBH#0*et*Gd|Nn%v$|yLg|3k%Z1lyp^00zJDLUw6z0bt_NzZ*}jBu+V!czc! zAbJcrb_=9RN2kVPh}n6aiqdhIr4y)hgf9Z03B8APYRRVDP4Wo7|1p!OGn)ps%3wjbC-2rl z)#jJs>;ipus;7x}>6@{9b-oV;AerYwOQft`PABk3U;Eib*3~Krt7Rk;x%ONOpEZ>$ z+u>OsO3wQJ-CMV$hP9Jc%BLM4D99iEY7pMQF9~XbIL_KZ50O?KVNoeoQKI7qh$1nu zNH%9Lyk5x%q%jQj09QCqYO{4Oq~<(?^+nC&5*c9SQHHS53=!}SY#NJPikogdu-s59 zbSc9D#>8utoRSB~G!Md8_Kk}F0Ad@#C9XR3 zdETXYkSF-?SqBxvS>K$-wc;22Q#6wuK$g-%=(!Kgf}agkpf$q|%uy(+04vl!?XdGf z%TLT*miLDB5y0~uOpD?YpxsXD#5>1Sbc$wXR7Xnz^lAKdul#tvtZMMBaWMUkcz?Xt zqU4}Tc)rT4)XS?btN zA;kfriWJ`ENo1{7024$Sd$QE z1EbclIfZL!YVN_bhdDo5BDK&VNYk2TqeKQMOWq%ZnyRP{^=fmy*1?<>?#f6M?O~+^ zSYh=9fh#n7RerZ_i4Aq{p$-XXB2@nY0*0Kq+Of2bif~=6=p&NBma?X8V6X8AXBNAV>s`GO2rIXO-X zc0-M%@thN_-%D%`tdkiRCSuJ*rVHboi2aXQ&*vP&*lCI-?m1 zi6@MupkWrKH0hw=ENAc`s6h%=XYo?uOkj_S#iJapL-(@6YSIS=#Es-jJ%*Of+S9;d z3f`an$A`c7Z~P0_uJPaAMN{xx4(`%66H#}2Ais$5W!BmJJrP=N5U5d>`<2E>3Iuz| zt~@|`QSE~l${oPKo3Bw+G2b84YHZZr4shYdOsO^gha8phjk5p^2ek z$s9q}Zo$vR|DOB3I97V@Z=UvBYntr3Cz%-QlhJwJUANjJ7krK1OanqwWYK72=xJiAziU@YWmUk)h7B$s-7gtwNaXwh^QUZXyJBw&XRf047pQK zC9M#12vrTiG@`sY6l{o+$4fF^2379h7|m2!`4ksVTD&Z`){aN0Ndh|uNt}FC{(#Om zd57vWV6It5tIw%fKFoyKct`NAp`?cNw2THVsPtBzG(GuTmcEt@2(6J zt-_})`VF%3yUZ(;t+4_WXASs>d#%PMs>Cuara#o7q>3*>E+LF#@&_F>n&4}8Ugn5| zH(6pDc!T02Oh|}5brqCRR4bXmxLu&V<_rW+dOU&!Y1mQQflJmi(A1kF9-t>q|VoveLSFYxPsJe$A5_NJ*^XIY+tZlra)k^R!~5*5L*+*F0%d z24x9cu_OFi`-agp7WW4CM|p+Xrvp=Jbl=!kxUG}?wGOmSb>i4~=(jZ*4SK)=EkSgl zGDB`|>>Y{dD+t&!*Beq9aIHX<7u<(jSwObC>N;V{)T7*_A)g3T!MALakE*)+Z`rMP z$7i)^n<~qeyP%{Ax z8MO)hPznc$syh(@E{tEH6WY#gjh>-NKyUn{KS4Cy{&CNJU0iy$Gn3>?qPre@y4)aoqR|+7p)`bEjz<6)paTtLld>bsiW1h2kQHqx zq}7vIkS}XVgrA)TUAru(3=20tM@SN3YJ^LSO>=nFV5cK+NN}nH#8l)bwJDkLJS6f-W3bUY3_{^!Lp;>5 zyATVCTI<&1ubU~pOzAj=!Q^tzu@cAlZBL4e*cw4-AikQw5bIOjHxR>NnE?T4zSvoB z?{Bz$c@p-!31Jr;Q-%Tww)PIN6}^~~1#+}B+Q%9RWBbVyU_Rsvj7(cO1&FFQ{qTI& z{&-;|Blni5ldywHm(9m#T5&u@Qc232WTvE~JQ9=|AM+@=hy0lZCFOpXoB!~?{6G5t z^tEgJ_m^Ssk&2()rBl=@`tD@#;B)*>10P}d(!=ITYq^Pxepun4)je#!*nhHkxB2;_ z4<4ZC`L9q_yn&~O*+Z8Ia)P>i!&c{%=JytK-ld)G=S#O*V75(t^8R;O-#`8C-MiMi zoATW1^76{kA3uM#hZj#eixQ*>;KW;5|GuOXbe@gWG2vgY{#e5WofoI`UDjQa*A`_` zE&SblguQ%l0yE>_8(sAyx%Xfxca0B0D}zCs&*P6HA}au;i|CE~vfbCUcRgJcvH&C$gxZP@K;L z_ucB*;J~5K0z@ADsx#)?+@G8(8D?0m|A<_sVD#W;i;J@CeZcuHqYHn!J6CMe5D z8A8bs;1JZ+#gyJSLYdDo@LkOT>eN6{jG>GUbKC$qF0f-ug?@-tl&TtEptQzB9TU*_ z;orewtLXu~PVjArCN#C^vxoqK6-I`W_M0kqtGlSN@P>JrLsV;vW+}2TtU1;5l+iTj z25Ucri2>iEDVke_1L6W2589{wM|)CsA(|zqr(c!Zd7T`O1GPUed8sxG$mg!eB9J+S zJp!%Jl!{gZog*QEL4cqEA94ad?#wSTnAliEn2?f>XdVp8N&}SC7NKQ|3b)PH4sI)0 z$=#uJ8=G7E+n5h%A`lg7hEp$a0qPw=1tIv*+a-d6-edxtOko?g`P0FaG2JxJKq*Fz zDHe}pttbI0m4bOvvS5rg?DYUBq{NX?Sw?d(ssI8r%>mL!d3cQp{Fne!Dbma%z*b;# zx*HR;avFULr3c#40X9)Gigi3d=o?nMAh-iI*>KK%!(ihCs-*0i)ZP0Q!vaT z*w?+@DKBD0k`^Pl!2RazAn8xBatO;WHnp1$XUR_>)a0x6uN&>1O|DdC)9umtl%dk_ z!CQsT0nT?y>lN1W75W}DR_>_e0BFYQ2RPe-EgExjz!!;gA()m`5hYHJOrb0zUQC-u4!@wc9%|jAdxDv?OOCAXFET4a9=c@&|{4M-lr! zo*uwy%mHOEG23ycqBkI=BYbx>7U_0zQ5XbH3y?kmI2rv{?ql#WZe>uo4|_g zg6O$L!fTNg2^ZNSVi*^yGJ_| zxWjwcKIZAQ9;1|k_n*X&ncr%8_REd^c^_pE<=oG{nETPd%qs@(k6?Gjvhbi3)T($) znxk(I*T7r`Jvyrdgtj4k7}%Iy06{Ti3IwHww5PIC+W;tyCAG52WUXKvRM{M1But`^ z^F;Ut0MSN-=?)FZgSTeiL?H4gVQ1ofP-02?8z`@`vz$ocr2~0W))1b54bU~T zo;kT7PsL&bi))ZOk-f@5er!Ru&%keCtN^kdG_<#p-f+E_0_EWe45``0T?aYJJ z6(-~zAxjNzI@yWaAYCA+iLh;D&cgGR;e#V`v!RfvV*UjoZGl~%`b)OOOJfV=v1~~( z_Pc8fUl8IOF4~70s>J}@XasBjD%>SMP+p#3lwTtsG`37j`({H__*k zvzDhILPFSt6j`woi8IhI%W9C)5k9R~WK=r}{Q~$Q&w~8!ya%DwA5Pu3z2G+;AQ1}g zQLu~N;4>g%uknk0c>-VgWqHH+_>vA0&z6_Xg( z!fRFg|H47yj8B0t_oCt43FJON-sYy64>n9ebu?PV4>$>$5N&t4oZcvhVPUpN}m!dp{DuY4}2!uzzQl{H-7`RwCQ;>e zuSx&Dm>`Hsdo&t#TOf3YqJxX!81%&qabit`uLA^K9PLi}=6JM9(=!oK7|?gOG*+RM z@;OvDM3+I5?r4lUpJ0H3^@8jii0vc5Bt|3074Zgye`=SA8DKjBgbPrOkvCHNiP?fZ z$AF?&+DiCrGwm<#1GHP&O#St|N6Ja>stP1qMNLb#LHRqFjw88TJPr-ZK(8oK{XEsC z^+H#q!)aJ4We9GllU`)lh97x;E}}emmdv#4pG1LhLY&-Q*5@lVi}F zU%i{B(+qB=K#Ltu+`)ARm-uyEG)!)z>YC|Y*J#USGg>U$3A;-BHZYnPXJhr&Km_%oP-e;K&gQ@besBZ zfQ-P6k9T7_d|&%f)DHgPKezt-|1-1#{@c2|b`aqHJtzq|9cv$_ zTy^~!8sQ%y7kzH^F{lO=TG@q_H?tIk`08f}<#%=g9xg4CYSA+Z8um4kp2I<}$F7HH zeshB6PDVYHUHOY@vYVyb7z~+a6DbGUtd@?doYW^5lJbr?pZ55CTI3;RB_=|!K%NL_ zDr9O|4XJo*QOZA+ln@dU!vcw{ng?ej&Y|GKU@_^9OTor@1(D>vY$u zm%660NmZ+jwL5{E;&PYZizq-4aw>Uko`su2j>m&AECbF~K4gEA$~kf5F@sXUc)ZXi zp7KHe5E2QIUt(7hVnTY27H1P5tqZ-G<8*bAdm5{Qjc(Yxmsw*@U`|i^aL_r5XUxCn zKa(eTnsen_mzgLJV|(~!gm@M)rG=a}&=G{_(37sw2#wdIJyX!HAzVdl9e{&ED`SM7 z!T6!~)YZ;0dnc!pGsIG4dY}j!roV+I+7kA|72@*fa>x;1_q0yG18Qe6e){+99T2)? z$kYxWtir1zIgaO2K}@1Y<~T;#3T_8~Qp$V9T zDwu1=OLOeS)g5Aqj#tfBgY06up;zXZZNsQ}uLEt4v;*!u{5Jd8<^@Hb z9EG0~)LWo)k=nNg?-WQ$nhxlr@zV54HFMl%ib(|ub21xZw>3u-$Q4|ybX`+fwQ@{I zoS+g+mLu&kX%=Cgg?I`al*x2Cp2GyVOr&r5++5L6H!g=^t9f>PKkJ0`bl}u(ca)^kWDR5&YTP_Jz z8eW)iiH?@H!&(yOHpH$~Z%ItK=1^skY;*u2J@*a0l>Eew-m5s!$TwqqRvKJ4O)mRw zlfN(|Hco!6+B)}KUi~jxTmRsn@4Wr>U$}OS|Gv7cm2l6@GCzc+Reik{3<+m1w-Cmc z>{v9ZN=qsqbJNTRt%%+pL^0ZOOrp)wS5bTe&FAy?goC5z9jrj98*QYPew%#73^u(; zhL>oZF+3T=X>W~cLm1JeS53!sm?Dj>9AxwMqrd#|-@>8t-})sEmaWDCZSVU?+baDr ztPx>|h{HeyCXs+Z%IT*=wyH+)!$Wd19e_^3pEe$89JHNFRy1v{>L6O2>Uo{-G!tI5_OqY799=04R8qNDDZ3 zJrkjrqPCKK^#+HC+p)9fw|I!~Iy2>vzB7CgKE*!p4+;hblEJ<}9DC=4ul4A^(>bbv zkS@t1z@@N7rzcDM_xR08|5R|f_%_1BVX$V$4M}(8d}Nur2K(%(@-r1tz zeto=)`1uYysA^jSgFM_o{tU#zK5j;2NbNQ-rauFX zwerHPVPq(?Z`xZ{!6>}x@B%DUE>c#sFdJlj-PA&?>i<60GnO zF_e&9{(1@puRn=6pTLeD!lTAWN@Q(zv(*0ohky2Vd6mo$BcE*PNVP({>Z9;gzFc6Sg9k8B7~claSA$`7gs)9Unw z&{_aG@;*;d#uyPVu%e3y0OP`$#F@aofEEdm83ZB78W4N|9N5KnX)a7TI5U7%6r8!Q zOoP_TSPP{{0QO~O9dabhcmxEY4X(rG$b=Au4g^HPqQ#_;#n6sLo@Mm@VA@Bm5^w_k zQ+toOH%# zWR!W$t7b>U?vUP=N<)L&k_&EDY9(P2GW$B68{tQjk=je*dCTRhDW^WU!*SAIBObJA z5u}U_0EV$Ahw-gO8@@|X`yjcgv!ai)6vMG%CasA>Eds&OP`?Ic3)F2PhzF8!@`g!1 zhxDEuB0^i>C~^fv@QEO5Z=Q_T%*$t{orfLV==LdNQK)n_&9tH(Ua8&&5}Gg*ke3kE zr5^zO0V@}pKw5#|41|ecNz8l4vqC3I7Sb?cR<||_w@w;3=9zrk5n>@f^f$*_VhMks z*qUw@({vs?H8~Mc<~*4t0m0(=*ct@doFT;^k70xp#9+=3okbJM|Mu_w?f>RK{ovX) z{=1auqI$%ibJ1n~be8jkS`iSHcUQ$$Ri$BB6;LXHgkel1Id8yF*gm$CM>o@hZC_3xJF|O z2t%3PnZ-+jpQbtEvQiU69$WaHP#BUqy_?1&dpJ$rA`!uTd#-$u+CfR|Ni(Gi146j_ z6j40+(I(@jynBaY6znU*Rl5jF3YR=vO4MIm+d(C2?@WUYMIYLH0sIb}nnnE+xko z?I4F{wQi>3I^c{Hbs|1aw;t0plAm?UinWn4g(>eJEM>5L=C;o+B|jsp+mUx<1Iue8 zDaHe8CWl-)KxAPDIr&H(kpu-b^6NKTrb3*T10C7PhF%#(mZOa!5-n#_o;a~(?3M4pp2e&n8KrmA<&H zNU$HHw#u1^Qc_QaQHNBY-9t=damwiQbb#P2`~`Um4wKvjGuN8DJ!w2gwSj&H-JG#v zuy3I7#~ml zg$Nu~0H-%U=EEQlH^*5LpA8X8u!t98%qY}r_P7?A)=Sr7qq;rE7LedQ;cIqXE&t)N zUSa}_qYEM#0~y?etEHD z&)GTNr^Jy%K14v%WKlHc03p?u!<%JmPK6s+vCJ=G!EGR-W`HwQb-ev`JO9)^{}2E6 zm;dkoh5hBf%}ea_dgrWt!W7pZfWT3TL3kFafX`U~?Q&XGiE@#7=A_+=a>gxTffa{nM z)NE&7qGQ-wLtx$!s7#(sW}_BA0<*OXXFVY$(Ba*1p)sNZ1d(#PkDIaCltorN61#17_OJM#o?$}YOzzBNtW{cfBTRA-v9kiUc1JBe{_ZC z@^ueosn5}M2&J4XcbnU7{(bxgHtJoU#65^uoue@-`+$c~QVV;HqsX0-pqK1;6UHBe zdYe=RvB+X1Pz0q32uHzfyTII06oSO?s))(koDXLqC#(h^x1}zs61PB1S+5j1jR*ke zS;JHL7JJ}zWf{v>Kw}PzIkIJ_ybB+Oi)4~oA$+st+#}i=L~4^Zis#I2j$9?2tJ7WG zB-LJI1@8DYG{Vs#P1OH-b88*dnwvrvnjzCtq7_9sMS)bBAB986(cEt|Q3D7;gJjsU zyZSQ9ByY1O3D$$Xo%!Xx{r3KgJ${?>f@3CpoGSrgwCDqzE%nHm1F~*y`^9d1i~UoY z;r+tEL9`d`6vt1yooc))Lo*a}=GWG?U+ivf?DA7KV&!+7XbVFEKftO3Y*G1!O1}ll z)SS>-TR+kn7d~Lak_r);X3lu$87uB%zVdi97<(*+m1E~a5HMpsb0@#D+Ca29`gs8T zG*n0S&)QOu+W}~>Nc7ziwo|jPjr}%fgAki&E>BUi5nK_8y_733kTNWc3S@G{&;-p? zd+`b#3`igi83J58vkwnBSYHBM1p3^I(Ig0e-*yI5<;}nN>+bPivc#*{h>)anTD?FM z_Oan>8^o^V)=F2QJGC9|>D`T;_Aau<gvLvB+#l zSH2Q!zTRc7>`8m zgabG(LwvUg`oWi_OudGjuxKKhJ^N(< zVI`A5?2SON4gy<2U|}_ajp1#t(UiJgK;09VCdA(>6Q&Kq?ZoFzzN`xb<$|Ub+!(Zl za;iC7g{}4(a0V2{Iq8iN(_j2@7PMi3fi-SjAE;4;O6cQEBD zZyzG&;5aupMR<*V=eYa}a-UZGCiOCvp@H~85oKsvK}>gU2rIBO)7G1NNyWoi=Hc8c zYTS_ZJJ`g+?W!4&+-cv?N;0>F&@V?TQur#KHM}*f0QF_nS zI%WazL&>&QWECMJ`h=a8W?Ve$AR(kduA4h1EZQY!3kBp&2Qv`Rr^shVDn1(85i|OT zYHqG!|Iv?!-{DX5n|{`5+*-Z03P~{fxEvE(V~5T0<6>e)Ii0jVlw-}6GIydpAXkNT zJOT?X@pK z7KkmSh0+FE3eo~LP14dOO-KTzK+QTyh9)y1laPkxQ&2=vWGe!SAT%JzrhX_WtAGm# z3Mx>LU5coH6cDr`e9!ZqbMC!o=G>Wf?xerp|NqgKCU<+!@}74;?~z?r6l8yOFdE9Ak`W$kDs!tlV6a0t>2d?wTAxw606{q{Xl|_Y+GeU`nzhB{ zFWu7eUlEAJ=|sk1-j!9SHq40?njEB*=2kmvWDQ+o+BTv2W>Z*p`_&wPy(lG}Hlo*j z7?Ir&%4uKz2{w^{xt#?r$35ypc)p@aJpg3s;^7E-;kEH-dPv8Q7*pQc84ox;{(8v6 zUsfG=zH3aXOoSQH0)ynvUAX=Y(>0_DG5iGS!Te8e{LO6?#GqEG1($zcI}Szb zq@t5?$J1u~>vSqA-Ne6UMn9-+_;>k+Q-1o7*dGXpK>raFNuZ?iuS)xUVUTeH3_J4* zMSy?J467n(tkJ%dVdVZpY`@Qr{p~fMjx&M10eD)f=bwOnOQE!?j?7!sdQiH=hb0Dq=csKMfS>HBC)0r3i zPnlQ!cI$J?vaR%WYfriE%&lWFLX@HjX4uu>73>5S%8<~A@8lIt)T0p0*CcsrC6obm z$`AxMPm)%uJr}FESW|EdiNNG-9CR}%?nFxSp1I`BE@OmXTON+dvGZ2X=y5X|T^huV@7DfgV4tU8F)^-ZCmTxkhvg z82wIGO$*O#6!u1=q_1=^u6>980leNJ=Abl=8Eg$FuP%E$*_UHg?Q)OM?Db+&d z(K5&Fd0&HZrS(AO4X6%@U8FSRkb`9*!vKK36oQpPJrH^Mu5k}%`yg+XuR zs?$$ex0E~AM_g|XXji!!^wfa`faU{~v-qf1Xe z9SZ}W#6?Yh!ApjctZRx=r1>0l^Aq?|fHsu(Ri89%1fUu#S`z476BStGBBJ-?mO8$~ zi)b0NVG)YXEj@GuXi~NJ)Un*d!k1n)W6xAZ0!9 z#H#^5h6U(-N!r7l2<5H9huH>NSYRCp6@hzr2ctCMJjT{eT`0^g{^~`<@*Cdy;rt^g z3@w(8V{>U*9ibiIQ?_#>k$Rq1E!H!*FitjvcoOSVB%O8;mpf-4Kkf=Tft_&6%)AlSqxQu-9`OrwS>T_0_LC|p2x+jDS4S`@i1*6Ae@)bkm zEb5CYBqr4q{ClEwvm{hVt)L#!qXR~(VLo2R{co9DJ*gg)+6g3Ls$5HStVXb7N=;b@ zXD-9;BlmyyFNl>BBI!v%jKfJp%b2CuxcgG%g#m4jw;XqdEDC7kHqTGVa+O9 zYf?6(=&e~6P|*5d=ro{ylvgQ<3hI0)sD~Wv8qL!#o%QY~P$`KJDca^|9}o91)_7vo z8%VABr<^sURoKlNX(@X|`Am^1>@Usi1h}nOK#Q6^J$@z`2iY8I3%%T-NR`NfN|z5Z zPf4X{5*E{#XyYygomS3YS!pRD+ZnVcs!<&!qP!n!8v{;IW_eJQl(5`_)H<+*y{S8q z#>N2I{$O?$$W{o+@Qt_S+6rNwFVDTvK$IEnbJf`Lkt2;@&5-kQ|Rzj4sPRw#XMz&lGv!oR!=`CJKqcqz;pq|my0EL}a;UNdlC|z7zwUByZ?Xrc6 zzS7-LQIumJLH0ALHkFL zy_p3_d3d7p1k%7L`2tr2sUTBvC{Ov78w2t9D%W+1L}lEd2Jer6FRFPxqc{Fd%@kU5 zRA+o{P05>6w~ECGQRKMkzQ50NI0aBH9out!AiySpW^RUqE!YYb2NItC6lEFmC^W?i z7>1=#k=%y90R-JJGs^um05*c~v&0O+7+}Y>;tI?^iYWfc_p}wx=q76C#c5{pGClPO z>mt1ge<=n7LlcTI9t1@ScSq56o~eyTkbs@VSq#=Q3vQ%hy{hRhDf3e z-+huzBmPRsAXT4+{UwxfjdPT73Dez|>HaqxbiJh~D~9Rr8834J{7Zbw8cqL}j+$hK zIrL?FRQKO z#0ODoutBd`XIY^z6^;{hGg94XZ*z#=UE>KoD2&)K=Nh*0_dI^c#o%LviU>Bz!gy*J zYu{k6dq`=R|IH=y5W_`Ij#BcqI}M>Es!|>C6zX$Rb<&YIvLv!cXkB20EpZ-6Om~`a zNPR;eN(pZ|603!t=n@Y`3BX2;MXXAKODup-NmD>wb>LB9W%Se(2Vw!W(tZeexaQc_ zCgvzWwKs+G;BIHrRFZ)>XtRYQk}(Uv!T+ zYQS;rHgF=^+9U}VVo?_eRLHQjJJvX=RqW8FQjVS-1NA#*NBGU8V?h`A-%|7jpWC>V@ z)6e$(c%C&9jX0j-PM6wcYi@YpgB@ZqLKN$wI*z4+;&{Wzae0*Zrn211D#oEU_36#v zq*jZJ>E`Twd4taV;xGH@ilm1GX69j+z1xuJLSclym`UGR@KWM3q@AbP66 zo(6?iz+4kyq6>9{#ra=jI2EQIKCXi^&O?KFHgHY>5+GYGMFQNiThla-uQi>pxu&@~ z)m3_>&U;Dtc98pt5`zggw*5aE|r$A4HO*=w!&^{hR+Ebgk^d}QYTXb zqVJ)Tto0`6Cu@Ncs3hh!y{EZ99d8Sck6iI&in)(MB`Dg1v{7}Tuy(s9$x+!zUvf^5 znR(yR%^_O#umo$m1#G7*G3yPAdM`LA>WZ^P!m?{P0U?6k)ob;=vO=mzDN(Uo=scG0LiTTi6Lhx!YAI z#t7S`%#z_~S966xw2Shdk_`Fjga)N z`5ve+dJpen@L)ENX-aU-Vh$>_WD2@5yif9y=iKyFtDmt)HPf^^Vw;neT@AC25XCi} zEwG2%A;z6K!TQl~bI?7CAcCZJWx09N&F~U8FeV{he=*A2Qz>|-jrZ92PAt!{<*POM zVZ!ZInwQECgx^@Q4#UhvWq$eEjBhfE>s85aX`D|=awntJuDi>#BOG+B>bClCWO#pF z&b{9p`oz8$gAxdlr~!4rRKOSZ!WOCX;wSw;c~v{&xS5Q)4E|O%7*#xBpmQ*bHsF-W zRhQ190c6Q4e-82t*g_S4%1hHs zKVV!DK~02)kiMl;1L?|S*r^+-sbQY*U{g&@Se!KoV{zvjVxX>-^2ew4eE8Q$2q#2I zcJj{x{}1ua{O|I%(1aeIO7PS?%6ZrW4R0!PDl(2Dbv(&M_=;?9Q=Scpd3p&o^x>KV z%ydvv<*q`quxcI_JTq;`BUEmLDVfoWMX$J26P8bJBHjzCXi`;Bc-jR5nmS`&YD;*m zPIC~}rSwO{r00W<2 z84*9Hiqj*u2{V~#ZM-&T;@`$1mO-eB5J(dJ`#(fr{{LV`dIs21-wa>!gv3>b zCFW|jWXmB#C^pdEd`hYpn=T+Lk}5gYt1_1n=I}*1 z3RZU@M`$`UfAcvd((?&k`i*UwYqId5uj=vxttq7Yz_0JiEUaIBPN;)5L$>M~+eJ@1Zuq zN|{XX@99rtBa3^T>2r19vp59>F@u93q>gL32SVrg4wmaLa7fIX`88n2+YbFVpv`%V zJJ?e1q8HLEW}1wwYs$AxI`z?a-+Qgf3b2?vu95ZE4)%ySiOSuS?0B5zyEiDa4|0Ti zN(;l`ZH~{HJW(5*L+@fZZ>@)+*H*4lsbo=QpFtB`;NBO5XlTIcPiQ%r@l}(nC#CAv7uE=dANRxu_$v-a~u>@yM{uWqhd|Ndn@&aABfMHv-{ zijyH8SI}YX1*7&m386Vcl<cRD)iX1 z){VHd2zFn37)dE*M`U}Vsn6x<1oA;)wO)kBb8Ik~066WkNdC+O;w`9&B(08C5UCbt z2#e>1o?mVWo157mRZ`4USu&0mReW2KubKS7AY@cB>Q($t@sU2LsYyD2G&+Sr94M z4^;{mbkK&Ffx)`Cs$x?rAaPtar_K@8I!`z#T)Ao;am=XwLH1$yizT+sLA%0{YEA(~h;)_qK;0lS`@wQUs@8hoA#bKkof#nRyFDAVsP?xxf{`pFnS| zY2m%>0&-t_=aKXVL^}vk_Wjp3r_(%Vq5@kvI;fg%KC{;b7DAw#R_q;KT%e**=+VfP z5167DPH+UL)Y6w3#Wg4}xMnJX4`3CC3F)lPnB)t!ks9m5)1O3lBfoZu&}sL+vd8P& z#A1XfBD7Xj&ioGvDh*c_#%63}se{cmJgF7Ato-Ft=^BZR&ALK~Y$PXpWd#Bqm{HZ8 zq+B4l9cj;x%Ygi-4G7E6nGE4ic=p~BZDOR$P!OU$(Gm0>sVhzomHr|^zj%k1W43e3 z$XoiL+8CE%Ei}q+si_L1K7peNsBLsX@JZLa=&kQO^IE5@Br8s$FFD&Mrmb#dwHR2^ zNOvpe$2(A_g|0A(s7YJrLe0Q|0{Rp6s5ak&FRb*#Kit*Nc0=fPq$E{Aaqpl{F0=L6 z$cgU+hnUt;pM2ffjSTc5qr5;>sn6&EN?dTzrPc&ZJX8B7o#;mGEBa)-y7!zGPjVsb z$C{OxASVBi;Mi#K=D!L}l9I?alpNeZdApGmKZN%PzMu+(*OL9OXIx-&0YzgLwe#GGw>1NAc!uyWQ%GPEyx#2YDi7tp)-kS$N7$6EkCx9skp< zFhivu@snHh1^4XLsR5A2WRZ~!7b-l+3elRp%FFb0V`cbIy>Hd$AO&epP^b-$Xq?RYeQ>#h7nqBp1xaz*FYXA9apL1HPg(0L7qq3_T2Kbu0;8 zT#(E-k;FfN1Siw6Z78^$a}K>>%W|g$g9KV(m*hClWYQN~Q12~Jj#6J`#hZhD2_~M@ z)jidAg&BR-K0(}U!AYdG2=c$;eH-VuxGaX57hN;rK9Cb(X@n#+P2VD~!Fh(AqL4S- z*TfCC zv6CU`Q)wtuPteNEN}ypaLD7y`7f!TFISR7ecjsL}B_5IX0glzlgB zzt+^fh{$~4-oW-+#%Q(xsel4d6btNJg+MW#m}0bmTch+T>l}HhHwAA~FSf9BFrcb< zlG!f9%3DHEtFRY1$5>lkGlSj9@>Mtx#w`ElV= z#3<)H2nrPnFiHveDfojN5rrKkX~XDgLed)n8G&dE&mE*6p2hS}iFc)XBxVBj4VmY1 zObHSaz%|$x>@wh)VenopHsi8fI!+T1CO7uGb5xjoZ z>W7EU|2^5^8o{UT`{m^I5F&&n7{x+MrsEv%b|_KsQlz)QIMQ|p+2QC#Wv?N_K=D;7 z$7IM@&k3k$UUl9XrAwfckvR+Z6-F;6K8~BF7sgK+hIrE204Tf}0Y~VDEE}MYq?M=V zu_$;qhBw*K4^dFs6i;#sGWH+izwN#JZ(Cg1{GAy!yYV?#l}+hEI6=GlU{|7}vrj$S zh60qfZo?!AC_SsX)dSrL`?hv6YQstyor~H^vBOu~<;t>!HcK7EZ3gQvz!y}D@=#pl z2jaX|!fd`V^T1F#wB|c&eVd`lvNEOrSy!c>{#9djVDuC1rX=#6K>R5!ktN(P?Lx|n+${r1qJ&KfD#YC2<&x0AGg0xKwuGFM1@>1uV}{)Z zFF(;FF!Bk=PIgf~(v9P_tyf^L>oUw{Q7Y!8&h6oRWM!_POHy&1v%`T2U$%y@vX-pz zD*9wEnzuKP8Q1+8^KWReBg5`rmCt01Y3N-uNd5!nqh1!M9+a#nkn0(CD{Dkui}&ce zAD{RDL?9vB;uVE|3oKqzHdDQ14Kiulvtw~Z4A*!Ymaq_4n8Q+8CunRIK~S}DjZ@{f zA6YydZU#amrHc%C3ycz!F2nl8{uAw%3q_SZL5LQ9G_W^>C}}T+FCEp~tZ}0H?-L_s z1=a>-DbCLGuLpbpxGsz(<0k5(AYP!jpnuBCgT zDflfiDX@_#L&VxfRzGcFG3B}QiiuvHFMtt$U%OMMqd@PIsWLv?CKDu44qUDaeukE^ zLP8OstK9b?y^-iRBlvELmGPU}TcM4Ka@W+{lN3d+v%h&Do}i94fD58P4a8fT_&CX> zNxlO^2*}hYRSKj=*#Q+yz9*E{(}PGl@^bnh(V)!Hr;OLUCYd|>JV}!Qh{z)%e2~Xo zq2*YtNi#-)84S;Em-cyo8Eh_M8ntTO##3Mm5TYz@vr%?9ZPuK5G4vBjfDU%6WC~KZ zfxC^z4q$(u{L5*iXP&A3ib>KI(AvE)jcVoWk=-aLLrp%Sd`K)}1R|B<6@zot*|93y z0cs0wio(tEAL6W%3f*Ch#SB$8W6?)s_{`w{<;@ln*7A;+_s2x*@WJVF5q9F(>RWGz zWG6&%kbApWhm*2NhUH$m1QceqV^-w`m~c*;r`3j0me*GC>M>2oSv7mvMVFucR}2dw z($JW?qr*Kk+#5!9hexSOIWgv)OOo3hXuo>17$5NK2B8*^Y`f+Go&I5`isQqSWi4DX z+E7nco#~t5vGBEqocQqJxxZfV@lmlDA&MU9r;ZH&c#>HLjX;rb^u9ABVkZ-psz3&1 z7)GCZj#e-Oyc1PWgsF<{r3^^DJ;j`6$0#hs9OXacpx%yUSwX(V0Gpr`F$&mn!Gtn{ z0_{+hjQJ&bX}YAv;LEUt7+14CtJ+n?(lA0X(f|qcvY*J`B^s5q>-EAbcxW1mIyyiG zTNb_qcAG!E0uw|=PJU1HScts{7X0(D?WtEtFbn^-qgFW1X~S=Z6qULm$qjRvcmii0 z{e+}bK?H6GPg^?G>!as^%4P#YyGrJR6w+U8L>yXU72QRBs!=2M1Q*(^78FIBJ^>Zi zJS1{-k<}}9Ay4Q{L?I84V=wxXe8c*LNnAe$+M48CEJu+*d8%}QlXJ*~diA%~a{p$}`Rzr0qL^%MW4%xi$(6SK47Gb{36Jhj_RBqhQ z%txCc9MtS3K(G*21xkl78xXs;l{dQuCrOBAAj`BDMLx&Bu z(~bvO(!B?kp}bG>w$^#eA>BtgCVx!%Vcf%&u}UW77;p8l*AA!TZIC=O4_H zk{Wcj1+OWO3n486CD|{vrrLQfcvlL^Ql;IAwj`q^mh^ClRZUM?pel7x3Dk6dnt~9r z*7}Ch<&=?U)Jji}gbUhxO?(bvQpNNe!=FW#FbF-KsaasswY9&sKY^oM19YSxMmk6< zl1CZ7$oddcQ8DO&s74_A=OH!M9Q(~d=Y6T5(9_Q8Tgt&Lc!g@9}v;6&%6C%omi=>aTxjXZko7`tP{( zwcOyW@U$o?m5KvzB)bM81cfN`tu5V+to8MOelZn;)?{6{-N0c>OcSAA!!BBSXSbf? z_JwRA91RP<&G{F6CI?At z)z+~-NWNFvV4Z`6T?{yO7M+FS$km55Gq};I9@5-X%rpNI^k+Y76-JeE|8bH?OG0Rg zee@nx$Dxb_1g((`ZfRsBhHFtSeD>@+%wejLls*)|hHGTY}3}GTa*T`_SUFq506qoSPB0K7Y-WJxRx%h z37oLzJe@k=J#860ikl3y^^F05{$wh$fOShk40*TNe?Z(*k7=mb?|{;o2P56Ly9Hdz z5(X`UzVt4w;qrtXMq*U)FLVYW>jbh~;0Kn^`&yiGb++`U(GG9JW}vai6oNIt()+3m zkzU5!F2_)rDjlz!mfbAatBIVw(*6sDdMwducs)Kwlt?;_#H(5Dc z0)KmRc7S(^xm&xnDcxVuVlh-U56XS$?mU}&Fd%*8{vssJcEo#+1o4q*{ADh8>DR#Q z$R-*-!cPT^OXo8_rDS|ePqVZnFzfzPqQ2_s5hC+%X+f%QR;q~}tWJoLpY@nM-2QD* zf|Vn?+z;&phJ+93qq*rN4BXLN&p2{jVrAX9&XO?@DTF9-n|kGB*aFtpeE3DIL(aw3 z-j!&FNG6Ap6LYfok?-wLr6Ktw#X3!Ur2eU-gBhjOQBy!yvar^R1tTv9=9QV_J%a_+K5=9>_4kp>qc+%`wz6C~^REH%(+KD%521;o% z#K$c)s*6Zv2tEcqPa$rP4?q+Os|{0Hy!kP~Q`J~{upw_UHk-JX=IXo7nSDQOS3*Su z8y0!aTVPq1b5p&H4ktnj#IV@Cxbbxq)h!=XqxE@J-Q?!3AzCu&7mMCO)PoSkq9EU_ zgf*Jz8s?G8rFPYT*t`%zva}sLn0#KP!~XHsgp^uQS;;5_2{GQnjZ<272X%&kSk|(K z`iCm8F17L!s$!xSp}>;T^JZxmI)^|QWxABR5O`FU(!=O|2;dpumQpY0!1@XqEiC(T z+D#e@%i_v%3dDby!DIruZN1(nyLDQEKT5IDfWAS9xSyHw?-bGDYvxyyl^~JG|9eyyp zD5qLrS1-bQT?ox%5v#2;@-3?NW`CzpT#%YcD|F&Yypv6z4(mX?x*9opL~a_N&H$g0 zQgX>K3g&N65QEa`oMGBecjW^ zN=0MD0Krr>J{ zu6t?H2j)SmQxckD#51)@ecf+87ZtXltV+XH_NJ7kve!6~g=rXP?4>=rL|i91YZB+w zd0o0WzHn9h-Gq{D7lPT*B@^WRA;Swrl>7#M5z-7sG|zUp9KysAIyIcZXW`nG+l<(~ zan!=WoQxkUd?+4>$eP=RYJF7R9Yx{UH)N!;FPrt8eaD+P|Ca90CT12GOIEc=5xf5_ z(_k~PUMy6<=97W{j=p6k)ri275#zQR2XV)rPsYyp&g3!g!ly@=s4mKXhhV$KVmwX` zt0s7M;dErT&p=`a+&g{HwrZEy-}}=QZ|}jw_TK)tdQcT~b$3^0&ss)aTXEHnu^3@& z;N>?;{&u+kGNnMgcSgE1^YuGFJZ$1~&@>410ArXy`pzyV!LVYs#QVv~-M0JF{q?Yf0Zec+KSL!CpHd($_(|cv?HjC+I-JfP{@|d>lUGlV z#R&5aO8h%i^7g|8V-+pP-OFheBWJ#_E%x;TGJ5%sLh(VPS(Id5iCTD%oxPknr~34> z!7B*%Jnl52q_CCOz_R;XqM7S%N&Sc`-r7ADBh*?W`qmBw;?jyjS-DCetUxwwvcJcP zed&3nXWRkyL|{&U`57u%wr_E;S0&qeQZVnF=u^84y|Cx%Sd2hlh4~pO>7QL3^lq|R zG_Uiqrw#?P11vL0@$XQ{#+xJJtwfa>J@9VaZ@+x;Z_gbaixF&+KH}AgxW(ddV}0$% z+bx_p1`->gEr@A|K(S={drsH7X%yqw_l8io(#ng#kmLl%14gv ze-ipB0ot1PGgR{N(ya^qkh2fcg7PrT{ob3S%(EJm>L zPWo*`yj5@#!!7N`d(a;5Jv#d4Sd3uL^p#tWER52$2W_yd(hK`Lm-tO1FTHE>7%XwX za*xEx$H&8vD99elM6KfD)pD4ITQ!{bQtiRN*(DYu*mCsNpGI#)cWOZwT(8dVU$)<6 zC(WM>wHsj1_-nt9in+R(36O5XaHo#*=C6QlPOx>Qi#8UDHzgKpMHfOPZCY7!_PP7d z&m}$%gOp(F4$D7`h+D1gK=y#M0Z0Ad=DpX#{wCPk`3pOaDv;)M=T!sDvG-2w>DR~p z^i>Qy!Cq`{jV}szW{SAi`F)3uJ@afBT?89-`5r|*bXNa2dz0` z*ol7C{!fnh4xDWSd&H-;76(1H*@l>ax6P>7VIk<7P-AiRBNN5J%>#3A&VT8%=g5BR0IJ~bgsjWCDPyJR2B~HXAuNXY)eDsxIOX}~Q z9Sw0{^NthqaeKc~H5DtFU<-}4=SIZrWoLyisAulk{kBsN5|(tTm)M+`0MRO z!7cUYov1(dqsyP3hE+viW~TWWDtYqm$f%Jrc%2ahvMO&OSGvb>}u6m|wyIGs^gPsASylBIDIt=`%gyci(yS#dpDd zL$Ddw3+p1{^xSmL&ffp(`&f)%W4-$Is905@ zahJw(+1>wI@B^$Kf<3Vv??=Ykt!gA>PHfdt{}}r(ut|cgd7SZK5pW`#$~nM4U;pz} zucDiT40;-|btKKhueIhxykzgS7yJ(X0fLQq#`e(=`_3>Y;^~7EAL_&)6Kt7u(w@aa z47;tp-HAQ*gP;B?j*PeevUS72DPYfpa*JKg4Q z$F@zk9S_M%unBto^l0dE`Wg@U=JCyYVrmKY+P&_OXlR)-;P(D}>HbH4_b+&r3APqJ zWljW~h}`t0k{I7`%)3=?Ja`H?GQk#oUtJslbA4ZbOHRCRt+-^yk?1YKUcH|P z&~wiBr?#Ed_|9nfxd1j7S+i52jo^+x1P5j3Mo5y%Y2TYP;So$X!KUvwCqzQaK-H22 z9mz>2tzC6c?I9m5jKv5xtxcL18L`-=?#aHOa{37mp|gZ)OE{d9j)Yg)!@2SsuKGgr zZ$?5U5$v(P^UNZkmPB>uSkL}5T2 zO?0&4GR_s8xnR``yMMMl79-dk^jqJGj^5qn^`HFKYwIAQ2sX~ojoh|CRwApomNjPG zIPa|5=8K;_4E+SyIPcmy5>BZ=>&E%k5wBl50evLcp_r={7l;!@^UJDP@sY4h1^{QBoUs?04fgCqyBzdpNi0S<)wIG6ewx5$O8tsn;L1 z3VtAfts0GaKS~=!Lt+0zbit*U?Q+%k_dwhcVB@{JW%~k&owM#cNZh>qrQJ_H9BC>s z0!ZEafgksbsCaV_+RFV;?SC8EHlfZ#O0VWlXGKAaLb55*_w$h2hoio`&K#h7qJ{tD z^9#h^8}CV_6Md=PK`u2voubaW?&Pd@FCX*Hl(8_b0nJuRZoIGvEy+r*$kUt$AHB2e zaELa-XRPKNy|!r0F{u`L+A?$1=+9pRUO+g(YRh)F6s;|ic#)?euRQ(maYye6MH*l? zWS{>oT0=yq&eM>uzg5#TassRvfK9e<{|y!n)#5cq;;ngFl6?N{$G!_zN?2(P%eXg+*AkUSk*6u& zSbWic-2h#UV0%^$+fclwq1BQOF`<~wda)Lb>uN@J+0VeGuPa}SE+M~~Z1%yekCu8Hh z=#B7`K=L%>l3B6YlQ0g1<1L!_pfY+h0x2PR+VRyF&wAxS3vMH^)N25p3(I z>iD9yBu^W@{i%IN9}WBjTM>G=v1o0Oq?0@i`OI-oO#TVTgJ4g~ns`wff)o^;4U(rN zSB~2Ah6~XO!ZF^0p(XRnM6p_urwJcEf9-_X@Wm1ARr^SH5t?AqQ}VQ;@%3a3TlNGR ztbrKlFG?%=u=&QASWZ*w>Ce?nH|{~3iGF>c2rckZSMoGt+Ng>ff3!9HRsegU{%f!p z&5%TvJZ*?Ae&CWPV8RjXHcURV7;TWWmOO2k_s`>RUkX7&un8jh)uOb4yRmW-#3lFL zxN#P+6Kt*Pfb)yg0;Ig;X~eqe&wQr_4ItQ5`TGlt&hoxY}uatW-*%~$uW7_a`VL-6U)&Kf-TK%xiWfNf~hij z8uHj4@wKK|_UkbqsT-p^3U z4bN=`mV+A`^>#}X0dntzVF3{4 zdVQhheA&C3f#ncj!9h33@oDT$4Y<0q>tnZHdmz{g0h@HapP`aH-ro#FhXN6#tqa4s z-z)uC{qFEB0qm|^@xf+b_*7#f=uaORd`gAQVY{( zCu}+<=!AVJoVZzi{PHvZ{O6wNAEDgBe0XVLm^ZnjM_K6Zn7HZa(8EMueA6c~*h#zE z)5-g8KGvKaEIseTlkY<;ld#z8;Gd`3s8~a6lS*d69>m#9{=yr{5nr#*!<2o4Q>6Vnm@>dk)$EzCQsS!4At@c~F5^{fys0>TzfT zKKWetzKg*o2(~zV@6*YktJn5eko_q0!Yxai~0}>t2VVG%LX7e(Bc=M8*>}WL!gg`0j1(JAD?pP6V6hzqj|!g;A|nJE(?5(2)Yx z8E;(`+JHqb-2ZxFTjT-&kk8}&43)fk(9Y&4#*jL)vr2bc{|w$B*rb2j9Pi!Dssr2H zlVEj7BpjQw9WwW9?la+p>wXNjO|WO_$8#g$q>9O-6(tq3nL?C zdK{9>!#ceC`0ciT;A62E!B(jEUK9a0>-2?ncf!uit4>DjieR(Ss+AE@int2v>Z*Nr zoU#Tu2~a`2pP`by8Vko;fh|vA7`LxIaM!0spx!3H#yGAy62?&ao&K?B4%`F%BOK-R zkluspwrCicN)Xo1@4UO}AZ$j65$sj-Xgms5??6)5s`=wJmp^*@_OTejM*5{>1f(X1 z)4f#hxapqp7mSR>2)2-LAv|bwlFH$tadb<0_{h$2{rjuaz^qTKP!>E!%!|+!3J=yRQa1v?&=4Ytn-RmRZ z)h={IUHiD3ETp!|X_wA=_Y)Al1e>Vle>XbvFs#>&?s~ox6HBo5xd(2Fj`f(Hu2gfV z%JIu_E3Um4cnNloFTJI3+@>~aSdX7uQ}X6ih$MnN$#;D(I#&P0ZKwMD{DRSQo&Z@9 z?6q|CZG|IWfb(L)x;y@!#}By}T_sc)g2ul?C13hs;TTb#AxvdHfBc3&?2H*DEU^%t z{^P}(>F+;@f>;$-4(sj_+nluQYIK!glhg8FM8}$koPKlY z6Z>8a!~`38{GkZQ?F{9x@t)sse&PX4Fu~S$;=d{!`Le()Rk439vi7+nZ5vOIAe~JuMpZ{zsEAu>{*VO1u{tsXCG- zjIL^awsgsBKufSuzyE$@)GAFr4E4;5t{GvrblOha#Tz4|mYno3%=>QGeytIOcBlJ3 zh=kcsLJvdTyX57 z5^OHnzg-l>k`)%#+oSJ(e4^27Y@{>CL_sR4@nJ|Szx~MK=~$}-dsdI%F%r`5P(r%? z`aLJF2ReeSNd0A}2q?Y0^RTXtTD5NDDWD{R-PJ>PDIBlV#R)^1IJWxM+d;Jin;Y&t zzHpRvayE1r&Se)}e)?a5k6`1Rwrd2Oax7>V&JWN1^@@*=!eI;mn{-e8codwi@kDqW zYUy8pxUw2^NwDp%_a;O^+JPgCaK3qnyX57^zC7wCumyrmy*KO~1#4PoS%%?VK4qt; zUc`za*h22^NyWfh)s_f#5xuip&vE;rzXaRux!u0Sz@5k1T=?wScNph|J>*yJ7Xdx$ zzvR}HuiNLeTW4Xo3AO_Fhy4r38{CEwHrW&QDxG*V!Y2e<@U1=|0%j(v)Z=8^c&Ol8 zH?Ff}47y9O7xK)5A|USPsq3NsxF^n`VG;3q9CFmw S{OU*e-`1 + + 4.0.0 + + com.example.myapp + myappname + 1.0-SNAPSHOT + + com.example.myapp + myappname-common + 1.0-SNAPSHOT + jar + + + + + + ${project.groupId} + myappname-shared + ${project.version} + + + com.codenameone + codenameone-core + + + com.codenameone + codenameone-javase + test + + + + + + + + + + + install-codenameone + ${user.home}/.codenameone/guibuilder.jar + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + validate + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + kotlin + + + + ${basedir}/src/main/kotlin + + + + 1.6.0 + true + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + + + + org.jetbrains + annotations + 13.0 + + + com.codenameone + java-runtime + provided + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + read-project-properties + + + + ${basedir}/codenameone_settings.properties + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + -no-reflect + -no-jdk + + + + + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + -no-reflect + -no-jdk + + + + + + + + + + + + + javase + + + codename1.platform + javase + + + + javase + + + + + org.codehaus.mojo + exec-maven-plugin + + java + true + + -Xmx1024M + + -classpath + + ${exec.mainClass} + ${cn1.mainClass} + + + + + + + + + + simulator + + javase + + + + + + ios-debug + + + iphone + + + ios + + + + + ios-release + + + iphone + true + + + ios + true + + + + + javascript + + javascript + javascript + + + + + android + + android + android + + + + + windows + + desktop_windows + javase + + + + + mac + + desktop_macosx + javase + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + read-project-properties + + + + ${basedir}/codenameone_settings.properties + + + + + + + + com.codenameone + codenameone-maven-plugin + + + xalan + serializer + 2.7.3 + + + + + + generate-gui-sources + process-sources + + generate-gui-sources + + + + cn1-process-classes + process-classes + + bytecode-compliance + css + + process-annotations + + + + + attach-test-artifact + test + + attach-test-artifact + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + + + + diff --git a/scripts/initializr/common/src/main/resources/savor-preview.jpg b/scripts/initializr/common/src/main/resources/savor-preview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e93d8f8fb4af1039a6a23fd59e699984cf28b7c GIT binary patch literal 92952 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⪚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 z zPFO`Nmd}4lCIwvUAGeCL&L}82>JfdBV^`2_OBDGz*qwCPl$Qhytd=6|cQz42$h`Z! zJPJZj$~#g4)Rq6{4u&dv{vAM2>*2686$}Wx%zkTb`QQF#=5t8ICkvLNZa2N^Mt{)5#)BHE+$ALQGIiQur zulSvfRzA&@sjF*lamglV|w3~_N*fZ{i(N-Xm;?wmR8BX zN@{q*>_5{$x<^{w3D|EwBHjqn%FXo>?;~#tGL-&uf%7c}iEkj~0nSX~l~-y@LF&kU z4IyXq_acW!mkgGIVgNl#BYbMOX=Zfk93WyDDqqU-ZxU~s+aGuQ$QQnpo9hMsO?rT4 zwZG-pBQ(ONS7CHH)Y~eCGVs#%R)G1d$o)FqGo+3lN+-uK#=1|L?v202S|y!VOWe%!K+*R@xc+kUA@WsvmY@Az+f#aT&yRO%GSVMwcw# zhqN79%n!lc!{z?)oJRbY)AT6us8d_5V8sXU%8i`5nFzp`2tvTb>?*z;Ba)wP}%3;U;Z)T(Vj)2o5EaMtpxubaOZ9n+$sTw4Wz^? zzaI8KzDAkNVrGiY)F+k*;Mu(ZJiA=0k2TAK^XWi;U3Oum0(y#fbSlN-p@X|H{?+uL&0ZFV9(JI?kvKRRF}o{%Pag S3Lxq6Q(^7~?|(OZ=sy6XCTmgv literal 0 HcmV?d00001 diff --git a/scripts/initializr/common/src/main/resources/savor-settings.properties b/scripts/initializr/common/src/main/resources/savor-settings.properties new file mode 100644 index 00000000000..80966f2de3b --- /dev/null +++ b/scripts/initializr/common/src/main/resources/savor-settings.properties @@ -0,0 +1,28 @@ +# A server started on this machine is reached over plain HTTP, which Android +# refuses by default. Remove this once the app talks to a server with a +# certificate. +codename1.arg.android.xapplication_attr=android\:usesCleartextTraffic\="true" +# What iOS shows when someone applying to drive photographs a document. +codename1.arg.ios.NSCameraUsageDescription=The camera photographs the documents of your application to drive. +# What iOS shows when they pick a picture of a document they already have. +codename1.arg.ios.NSPhotoLibraryUsageDescription=Your photo library is where you pick a picture of a document from. +# What iOS shows before Face ID unlocks the sign-in kept in secure storage. +codename1.arg.ios.NSFaceIDUsageDescription=Authenticate to securely access your account +# What iOS shows when the app first asks where the device is. +codename1.arg.ios.NSLocationWhenInUseUsageDescription=Your location is where your order is delivered to, and, when you deliver, where the nearby orders are offered to you. +# While a courier is online the app keeps reporting where they are from the +# background: iOS needs the location background mode and the Always prompt's +# text, Android a foreground service of the location type. +codename1.arg.ios.background_modes=location +codename1.arg.ios.locationUsageDescription=Your location is where your order is delivered to, and, when you deliver, where the nearby orders are offered to you. +codename1.arg.ios.NSLocationAlwaysAndWhenInUseUsageDescription=While you are online as a courier, Savor keeps sharing your location with customers and dispatch, even with the app in the background. +codename1.arg.android.foregroundServiceType=location +# Push notifications. With push included the build adds the +# remote-notification background mode and the push entitlement itself. +codename1.arg.ios.includePush=true +# Android push is off until the app has its Firebase file: a build that +# uses PushClient without google-services.json stops with an error. Once +# native/android/google-services.json is in place, delete this line. +codename1.arg.android.messagingService=none +# The delivery on the lock screen updates as the courier moves. +codename1.arg.ios.surfaces.frequentUpdates=true diff --git a/scripts/initializr/common/src/main/resources/savor-src.zip b/scripts/initializr/common/src/main/resources/savor-src.zip new file mode 100644 index 0000000000000000000000000000000000000000..a8de4538b61da7f4ed1669c25d6b4762fa4924d3 GIT binary patch literal 752481 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

        -_N_dk69!_oVb_lu?B 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

        mYf>qO{#$GG9vqaREg^2Vll1!23 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

        7dJ_ua$W{#k;3Zl`owt@ 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 literal 0 HcmV?d00001 diff --git a/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java b/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java index f04c9e1edae..0f366f8f52f 100644 --- a/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java +++ b/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java @@ -370,8 +370,12 @@ private void validateMavenRefusals() { /// What a full-stack template adds to an app with a backend: the contract /// module both halves compile against, the template's own server in place of /// the generic one, and nothing left that still carries the template's name. - private void assertFullStackProject(Map entries, byte[] zipData, String packageName, - String mainClassName, String label) throws IOException { + private void assertFullStackProject(Template template, Map entries, byte[] zipData, + String packageName, String mainClassName, String label) throws IOException { + boolean rides = template == Template.WAYLINE; + String name = template.SOURCE_MAIN_CLASS; + String upper = template.name(); + String lower = GeneratorModel.toLowerCaseInvariant(name); String packagePath = StringUtil.replaceAll(packageName, ".", "/"); String artifact = GeneratorModel.toLowerCaseInvariant(mainClassName); String rootPom = removeWhitespace(getText(entries, "pom.xml")); @@ -388,15 +392,15 @@ private void assertFullStackProject(Map entries, byte[] zipData, assertContains(getText(entries, "backend/pom.xml"), "generate-annotation-stubs", label + "the server generates its half of the contract"); - assertContains(getText(entries, "shared/src/main/java/" + packagePath + "/api/RiderApi.java"), + assertContains(getText(entries, "shared/src/main/java/" + packagePath + (rides ? "/api/RiderApi.java" : "/api/OrderApi.java")), "package " + packageName + ".api;", label + "the contract moves to the project's package"); assertContains(getText(entries, "backend/src/main/java/" + packagePath + "/SecurityConfig.java"), "package " + packageName + ";", label + "the server moves to the project's package"); assertNotNull(entries.get("backend/src/main/resources/db/migration/V1__accounts.sql"), label + "the server keeps its migrations"); - assertNotNull(entries.get("backend/src/test/java/" + packagePath + "/RideFlowTest.java"), + assertNotNull(entries.get("backend/src/test/java/" + packagePath + (rides ? "/RideFlowTest.java" : "/OrderFlowTest.java")), label + "the server keeps its tests"); - assertNotNull(entries.get("common/src/main/java/" + packagePath + "/rider/RiderForm.java"), + assertNotNull(entries.get("common/src/main/java/" + packagePath + (rides ? "/rider/RiderForm.java" : "/customer/CustomerForm.java")), label + "the app's screens move to the project's package"); assertNotNull(entries.get("common/src/main/l10n/Bundle.properties"), label + "the app keeps its bundle"); // The generic server every other project gets, which this one replaces. @@ -416,13 +420,13 @@ private void assertFullStackProject(Map entries, byte[] zipData, for (Map.Entry entry : entries.entrySet()) { String path = entry.getKey(); - assertFalse(path.indexOf("wayline") >= 0 || path.indexOf("Wayline") >= 0, + assertFalse(path.indexOf(lower) >= 0 || path.indexOf(name) >= 0, label + "the template's name is still in the path " + path); if (path.endsWith(".png") || path.endsWith(".jar") || path.endsWith(".res") || path.endsWith(".ttf")) { continue; } String text = StringUtil.newString(entry.getValue()); - assertFalse(text.indexOf("wayline") >= 0 || text.indexOf("Wayline") >= 0 || text.indexOf("WAYLINE") >= 0, + assertFalse(text.indexOf(lower) >= 0 || text.indexOf(name) >= 0 || text.indexOf(upper) >= 0, label + "the template's name is still in " + path); assertFalse(text.indexOf("myappname") >= 0 || text.indexOf("MyAppName") >= 0 || text.indexOf("MYAPPNAME") >= 0, @@ -678,7 +682,7 @@ private void validateMavenLayoutCombination(Template template, ProjectOptions.Pr assertContains(getText(entries, "README.md"), "## Backend", label + "README should explain the backend"); } if (template.isFullStack()) { - assertFullStackProject(entries, zipData, packageName, mainClassName, label); + assertFullStackProject(template, entries, zipData, packageName, mainClassName, label); } else { assertNull(entries.get("shared/pom.xml"), label + "only a full-stack template has a shared module"); assertFalse(rootPom.indexOf("shared") >= 0, diff --git a/scripts/savor/README.adoc b/scripts/savor/README.adoc index 91a14e923fc..5eaea7dd584 100644 --- a/scripts/savor/README.adoc +++ b/scripts/savor/README.adoc @@ -1,15 +1,17 @@ = Savor -A delivery-hailing application and its server, in one Maven project and one language. -A customer asks for a delivery, the nearest suitable courier is offered it, both follow the -trip on a map, the delivery is paid for when it ends, and an administrator runs the -service. Which of the three a person sees is decided by the roles the server gives -their account: a customer opens on the map, a courier on the onboarding screen, an +A food-delivery application and its server, in one Maven project and one language. +A customer browses the restaurants that deliver to them, fills a cart and pays; the +restaurant accepts the order and cooks it; the nearest courier is offered the +delivery, collects it and brings it to the door while the customer follows it on a +map; and an administrator runs the service. Which of these a person sees is decided +by the roles the server gives their account: a customer opens on the restaurants, a +merchant on the restaurant's order queue, a courier on the delivery screen, an administrator on the operations console. It is a sample of how a Codename One application and a Codename One Backend fit together, and the project the Initializr generates when you start from the -delivery-hailing template. +food-delivery template. == Start here @@ -17,13 +19,13 @@ Two chapters of the developer guide cover this project: * https://www.codenameone.com/developer-guide/#savor-users-guide[The user's guide] (link:../../docs/developer-guide/Savor-Users-Guide.asciidoc[source]) shows the - application as a customer, a courier and an administrator see it, screen by screen, + application as a customer, a merchant, a courier and an administrator see it, + screen by screen, with the demo accounts and test cards. * https://www.codenameone.com/developer-guide/#savor-implementors-guide[The implementor's guide] (link:../../docs/developer-guide/Savor-Implementors-Guide.asciidoc[source]) - explains how the project is built and what to change to make it yours: a - delivery-sharing service or the dispatch service of a small taxi station, real - billing, your own map data or native maps, sign-in through another identity + explains how the project is built and what to change to make it yours: one + restaurant's own ordering app or a whole marketplace, real billing, your own map data or native maps, sign-in through another identity provider, real text messages, analytics, and the database: the engines it runs on, changing the schema, and hosting it. @@ -40,7 +42,8 @@ To see it running first, the two commands under <> are all it takes. other side did not follow is a compile error. | `common` -| The application: sign-in, the customer, courier and administrator screens, the map, +| The application: sign-in, the customer, merchant, courier and administrator + screens, the map, the wallet, the settings and the live channel. Styled in `src/main/css/theme.css`, whose first block of variables holds the colors and the corner radius everything else is drawn with. The text is in @@ -49,10 +52,11 @@ To see it running first, the two commands under <> are all it takes. has agreed, is all in `Telemetry`. | `backend` -| The server: accounts and roles, phone verification, courier applications and - their documents, moderation, fares and pricing, matching, the delivery state - machine, payments and payouts, statistics, and a WebSocket that pushes changes - as they happen. +| The server: accounts and roles, phone verification, the restaurants and their + menus, courier applications and their documents, moderation, quotes and + pricing, the order and delivery state machines, dispatch, payments held at + checkout and taken on delivery, payouts, statistics, and a WebSocket that + pushes changes as they happen. The data is entity classes in `domain`, read and written by the repository beside each service. The schema is the migration scripts in `src/main/resources/db/migration`, and the same server runs on SQLite, @@ -81,21 +85,26 @@ mvn verify -Psimulator -DskipTests -Dcodename1.platform=javase ---- The development profile creates these accounts, all with the password -`savor-demo`, and a fortnight of deliveries for the charts to draw: +`savor-demo`, twelve restaurants with their menus, and a fortnight of orders for +the charts to draw: [cols="2,3"] |=== -| `customer@savor.example`, `customer2@savor.example` | request deliveries +| `customer@savor.example`, `customer2@savor.example` | order food | `customer3@savor.example` | a customer an administrator has flagged | `courier@savor.example`, `courier2@savor.example` | approved couriers -| `courier3@savor.example`, `courier4@savor.example` | approved couriers of the - larger kinds of car -| `applicant@savor.example` | has applied to drive and is waiting for a decision +| `courier3@savor.example`, `courier4@savor.example` | approved couriers who + carry the past orders +| `merchant@savor.example` | runs Ember & Crust's kitchen +| `applicant@savor.example` | has applied to deliver and is waiting for a decision | `admin@savor.example` | runs the service |=== -Sign in as the customer in one simulator and as a courier in another to follow a delivery -from both ends. On a phone, set the server's address on the welcome screen: +On the development profile a customer alone can watch an order all the way: a +simulated kitchen accepts and cooks for every restaurant whose merchant is not +signed in, and a simulated courier takes the delivery while no real one is +online. Sign in as the merchant and a courier in other simulators to play those +parts yourself. On a phone, set the server's address on the welcome screen: `localhost` on a phone is the phone. === In a browser, with nothing installed @@ -121,25 +130,35 @@ server binary as `webapp`. A browser gives a page less than a phone gives an app, and the application says so where it matters: calling and texting a courier are offered only where the -device can dial, and the map opens on a default position when the browser will -not say where it is. +device can dial, and the restaurants listed are the ones near the default +address when the browser will not say where it is. == What each role does -A customer picks a destination, a kind of car and a way to pay, and may ask for a -quiet delivery, an accessible vehicle, a car that takes pets or a woman courier. The -wallet holds their cards and receipts, and a tip can be added after the delivery. +A customer chooses where to deliver to, browses the restaurants by cuisine or +searches their dishes, and builds an order with each dish's options. The checkout +shows the fees, takes a promo code (`WELCOME10`, `SAVOR5` on the development +profile) and a tip, and the card is held, not charged. The order is then followed +step by step and on a map, and can be cancelled until the kitchen accepts it. Past +orders can be ordered again, restaurants kept as favorites, and a tip added after +the food arrives. -Anyone can apply to drive. The application asks for the courier's details, the +A merchant sees their restaurant's orders arrive, accepts one with a preparation +time or rejects it with a reason, and marks it ready for the courier. They pause +the restaurant when the kitchen is full, edit the menu, its photos and what is sold +out, and see the sales. + +Anyone can apply to deliver. The application asks for the courier's details, the vehicle, and five photographs: an identity document, the licence, a selfie, the -vehicle registration and the insurance. Nobody drives until an administrator +vehicle registration and the insurance. Nobody delivers until an administrator approves the application; a rejection carries a reason, and the applicant can -correct it and send it again. An approved courier goes online, is offered deliveries, -and sees what they earned and cashes it out. +correct it and send it again. An approved courier goes online, is offered +deliveries from restaurants near them, collects the food once it is ready, and +sees what they earned and cashes it out. -An administrator sees the service in charts, reviews applications with their -documents, flags or blocks an account with a reason that is kept in its history, -cancels and refunds deliveries, and sets the prices. +An administrator sees the service in charts, approves restaurants and couriers, +flags or blocks an account with a reason that is kept in its history, cancels and +refunds orders, and sets the prices and the promos. == What is real and what is simulated @@ -159,7 +178,8 @@ Payments are simulated on a development profile. Simulated, the card money moves. With `savor.payments.provider=stripe` and `SAVOR_PAYMENTS_STRIPE_SECRET` set in the server's environment, a card is added on Stripe's own page, so its number never reaches the app or this server, -and a delivery is charged when it completes. The Stripe provider is an untested +and an order's total is held on the card at checkout and taken when the food is +delivered; a rejected or cancelled order releases the hold. The Stripe provider is an untested stub: it is covered by tests that answer for Stripe, has never been run against a live account, and has to be tried with a Stripe test key before it is trusted with money. Paying a @@ -186,9 +206,9 @@ mvn -pl shared,backend -Dcodename1.platform=backend test -Dcn1.backend.compiledT <2> The same tests again, translated to a native binary, which is how the server ships. <3> Starts the server on the `test` profile and runs the application's tests - against it in the simulator: they register, apply to drive and are approved, - request a delivery, drive it, pay for it and administer it, and compare the - screens in `screenshots/`. Pass `--native` to run them against the compiled + against it in the simulator: they register, apply to deliver and are approved, + order food, accept and cook it as the restaurant, deliver it, pay for it and + administer it, and compare the screens in `screenshots/`. Pass `--native` to run them against the compiled server. On Linux they then run a second time with the simulator as a desktop window, where the admin console is laid out side by side under the platform's own theme. diff --git a/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/courier/Couriers.java b/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/courier/Couriers.java index d6321005e67..f1c1253ab0a 100644 --- a/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/courier/Couriers.java +++ b/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/courier/Couriers.java @@ -99,7 +99,7 @@ public Couriers(CourierStateRepository couriers, CourierHoursRepository hours, /// read it. public CourierDto report(String courier, CourierStatusDto status) throws IOException { if (status == null || (status.online() && !Geo.isPoint(status.lat(), status.lng()))) { - throw new ResponseStatusException(400, "Going on line needs the car's position"); + throw new ResponseStatusException(400, "Going online needs your position"); } Report made = store(courier, status); CourierDto dto = made.courier; diff --git a/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/pay/Payments.java b/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/pay/Payments.java index 8f79ad0ebc4..ef6feba160d 100644 --- a/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/pay/Payments.java +++ b/scripts/savor/backend/src/main/java/com/codenameone/examples/savor/pay/Payments.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -1028,8 +1029,13 @@ public List allReceipts() throws IOException { @Transactional(readOnly = true) public ReceiptDto receipt(String viewer, boolean admin, String deliveryId) throws IOException { Delivery delivery = delivery(deliveryId); - DeliveryState state = delivery == null ? DeliveryState.NONE : delivery.getState(); - if (delivery == null || !(admin || viewer.equals(delivery.getCustomer())) + if (delivery == null) { + // Until the kitchen accepts there is no delivery, only the order and + // the hold on its card. + return held(viewer, admin, deliveryId); + } + DeliveryState state = delivery.getState(); + if (!(admin || viewer.equals(delivery.getCustomer())) || DeliveryState.CANCELLED_BY_CUSTOMER == state || DeliveryState.CANCELLED_BY_COURIER == state || DeliveryState.CANCELLED_BY_ADMIN == state || DeliveryState.NO_COURIERS == state) { throw new ResponseStatusException(404, "No such receipt"); @@ -1086,7 +1092,7 @@ private ReceiptDto receipt(Delivery delivery, long paidAt, boolean admin, } else if (REFUND_FAILED.equals(paid)) { status = "refund_failed"; } else { - status = "pending"; + status = held(order); } // Every refund state follows a payment that was made. long paidOn = PAID.equals(paid) || (paid != null && paid.startsWith("REFUND")) @@ -1122,6 +1128,27 @@ private ReceiptDto receipt(Delivery delivery, long paidAt, boolean admin, vehicle, admin ? order.getCustomer() : ""); } + /// The receipt of an order no delivery has been made for yet: what it + /// costs, and the card held for it. + private ReceiptDto held(String viewer, boolean admin, String id) { + CustomerOrder order = order(id == null || id.length() > 32 ? null : id); + if (order == null || !(admin || viewer.equals(order.getCustomer())) + || !Arrays.asList(ORDER_UNDER_WAY).contains(order.getState())) { + throw new ResponseStatusException(404, "No such receipt"); + } + return new ReceiptDto(order.getId(), order.getCurrency(), order.getRestaurantName(), + order.getSubtotalCents(), order.getDeliveryFeeCents(), order.getServiceFeeCents(), + order.getSmallOrderCents(), order.getTaxCents(), order.getDiscountCents(), + order.getTipCents(), order.getTotalCents(), order.getMethodLabel(), held(order), + 0L, order.getDropoffAddress(), "", "", admin ? order.getCustomer() : ""); + } + + /// `authorized` while a card is held and not yet charged, `pending` otherwise. + private static String held(CustomerOrder order) { + return order != null && AUTHORIZED.equals(order.getPaymentStatus()) ? "authorized" + : "pending"; + } + // ------------------------------------------------------------- internals /// Charges one of a customer's saved cards. What the charge needs is read in diff --git a/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/OrderFlowTest.java b/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/OrderFlowTest.java index cbc0a0dda29..cf76813dfb2 100644 --- a/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/OrderFlowTest.java +++ b/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/OrderFlowTest.java @@ -52,6 +52,15 @@ class OrderFlowTest { @Autowired private DataSource db; + @Test + void theSeededRestaurantsDeliverToTheDemoAddress() throws Exception { + // Pier 39, where the app sends a browser that has no location of its own. + String found = mvc.perform(People.asCustomer(get("/api/restaurants?lat=37.8087" + + "&lng=-122.4098&sort=distance"), DemoAccounts.CUSTOMER)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + assertTrue(found.contains("Ember & Crust"), found); + } + @Test void anOrderFromTheCartToTheStars() throws Exception { // Lisbon. Each test has a city to itself. diff --git a/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/PaymentsTest.java b/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/PaymentsTest.java index ac816e058c9..d4c4fccc800 100644 --- a/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/PaymentsTest.java +++ b/scripts/savor/backend/src/test/java/com/codenameone/examples/savor/PaymentsTest.java @@ -220,6 +220,22 @@ void cardsAreSavedChosenAndRemoved() throws Exception { .andExpect(jsonPath("$.isDefault").value(true)); } + @Test + void aPlacedOrdersReceiptShowsTheHoldBeforeAnyDelivery() throws Exception { + // Vienna. Nobody has answered the order, so there is no delivery yet. + String customer = People.customer(accounts, "vie-customer"); + Trips.addCard(mvc, customer, Trips.VISA); + String id = (String) Trips.place(mvc, customer, Trips.trip(48.2082, 16.3738, 48.2150, + 16.3800)).get("id"); + Map receipt = Trips.receipt(mvc, customer, id); + assertEquals("authorized", receipt.get("status")); + assertEquals("Visa 4242", receipt.get("methodLabel")); + // Another customer still cannot read it. + String stranger = People.customer(accounts, "vie-stranger"); + mvc.perform(People.asCustomer(get("/api/payments/receipts/" + id), stranger)) + .andExpect(status().isNotFound()); + } + @Test void aDeliveryIsChargedTippedAndRefundedAndTheCourierIsPaid() throws Exception { // Berlin. diff --git a/scripts/savor/check-template.sh b/scripts/savor/check-template.sh index 77103207504..246ce0a7f1c 100755 --- a/scripts/savor/check-template.sh +++ b/scripts/savor/check-template.sh @@ -65,7 +65,7 @@ log "running the generated server's tests" (cd "$minimal" && "$MVN" -B -q -Dcodename1.platform=backend -pl shared,backend -am test) ls "$minimal"/backend/target/surefire-reports/TEST-*.xml >/dev/null 2>&1 \ || { log "the generated server's tests did not run"; exit 1; } -[ -f "$minimal/backend/target/generated-sources/cn1-annotations/$PACKAGE/api/CustomerApiServer.java" ] \ +[ -f "$minimal/backend/target/generated-sources/cn1-annotations/$PACKAGE/api/OrderApiServer.java" ] \ || { log "the server's half of the contract was not generated"; exit 1; } for layout in APP_WITH_BACKEND FULL; do @@ -73,7 +73,7 @@ for layout in APP_WITH_BACKEND FULL; do [ -d "$project" ] || project="$(extract "$layout")" log "building the generated app ($layout)" (cd "$project" && "$MVN" -B -q -Dcodename1.platform=javase -Djava.awt.headless=true -DskipTests package) - for built in "classes/theme.res" "classes/$PACKAGE/api/CustomerApiImpl.class" "classes/cn1app/RestClientBootstrap.class"; do + for built in "classes/theme.res" "classes/$PACKAGE/api/OrderApiImpl.class" "classes/cn1app/RestClientBootstrap.class"; do [ -f "$project/common/target/$built" ] \ || { log "$layout: common/target/$built was not built"; exit 1; } done diff --git a/scripts/savor/common/codenameone_settings.properties b/scripts/savor/common/codenameone_settings.properties index 12d673e1b0d..d22f91865ef 100644 --- a/scripts/savor/common/codenameone_settings.properties +++ b/scripts/savor/common/codenameone_settings.properties @@ -16,12 +16,12 @@ codename1.arg.ios.NSPhotoLibraryUsageDescription=Your photo library is where you # What iOS shows before Face ID unlocks the sign-in kept in secure storage. codename1.arg.ios.NSFaceIDUsageDescription=Authenticate to securely access your account # What iOS shows when the app first asks where the device is. -codename1.arg.ios.NSLocationWhenInUseUsageDescription=Your location is where a delivery picks you up, and where customers find you while you drive. +codename1.arg.ios.NSLocationWhenInUseUsageDescription=Your location is where your order is delivered to, and, when you deliver, where the nearby orders are offered to you. # While a courier is online the app keeps reporting where they are from the # background: iOS needs the location background mode and the Always prompt's # text, Android a foreground service of the location type. codename1.arg.ios.background_modes=location -codename1.arg.ios.locationUsageDescription=Your location is where a delivery picks you up, and where customers find you while you drive. +codename1.arg.ios.locationUsageDescription=Your location is where your order is delivered to, and, when you deliver, where the nearby orders are offered to you. codename1.arg.ios.NSLocationAlwaysAndWhenInUseUsageDescription=While you are online as a courier, Savor keeps sharing your location with customers and dispatch, even with the app in the background. codename1.arg.android.foregroundServiceType=location # Push notifications. With push included the build adds the diff --git a/scripts/savor/common/src/main/css/theme.css b/scripts/savor/common/src/main/css/theme.css index 302e5e3f31e..2e7c4d9feb5 100644 --- a/scripts/savor/common/src/main/css/theme.css +++ b/scripts/savor/common/src/main/css/theme.css @@ -1235,9 +1235,9 @@ SvSegment { background-color: transparent; border-radius: 1.9mm; font-family: "native:MainBold"; - font-size: 2.5mm; + font-size: 2.3mm; text-align: center; - padding: 2mm 0.8mm 2mm 0.8mm; + padding: 2mm 0.2mm 2mm 0.2mm; margin: 0.3mm; } @@ -1507,14 +1507,14 @@ SvCuisine.pressed { background-color: var(--surface-press); } SvCuisineSelected { color: var(--brand-contrast); background-color: var(--brand); border-radius: var(--radius); font-family: "native:MainBold"; font-size: 2.1mm; text-align: center; padding: 1.6mm 2.2mm 1.6mm 2.2mm; margin: 0 1.6mm 0 0; } SvRestaurantCard { background-color: var(--surface); border-radius: var(--radius); padding: 0; margin: 0 0 3mm 0; } SvRestaurantCard.pressed { background-color: var(--surface-alt); } -SvRestaurantPhoto { color: var(--brand); background-color: var(--brand-tint); border-radius: var(--radius); padding: 0; margin: 0; } +SvRestaurantPhoto { color: #f3e7d3; background-color: transparent; border-radius: var(--radius); padding: 0; margin: 0; } SvRestaurantWords { padding: 1.4mm 0.5mm 0 0.5mm; margin: 0; background-color: transparent; } SvRestaurantName { color: var(--text); font-family: "native:MainBold"; font-size: 2.9mm; padding: 0; margin: 0; background-color: transparent; } SvRestaurantTitle { color: var(--text); font-family: "native:MainBold"; font-size: 4.4mm; padding: 2mm 0 0.5mm 0; margin: 0; background-color: transparent; } SvPhotoBadge { color: var(--brand); background: cn1-pill-border; background-color: var(--surface); padding: 1mm; margin: 0; } SvClosedBadge { color: var(--surface); background: cn1-pill-border; background-color: var(--text); font-family: "native:MainBold"; font-size: 2.05mm; padding: 0.7mm 2mm 0.7mm 2mm; margin: 0; } SvPopularBadge { color: var(--brand-contrast); background: cn1-pill-border; background-color: var(--brand-tint); font-family: "native:MainBold"; font-size: 2.05mm; padding: 0.5mm 1.6mm 0.5mm 1.6mm; margin: 0 0 0 1.6mm; } -SvHeroPhoto { color: var(--brand); background-color: var(--brand-tint); padding: 0; margin: 0; } +SvHeroPhoto { color: #f3e7d3; background-color: transparent; padding: 0; margin: 0; } SvSectionChips { padding: 2mm 0 1mm 0; margin: 0; background-color: transparent; } SvSectionChip { color: var(--text); background: cn1-pill-border; background-color: var(--surface-alt); font-family: "native:MainBold"; font-size: 2.5mm; padding: 1.2mm 3mm 1.2mm 3mm; margin: 0 1.6mm 0 0; } SvSectionChip.pressed { background-color: var(--surface-press); } @@ -1524,8 +1524,8 @@ SvDishRow.pressed { background-color: var(--surface-alt); } SvDishRowOff { background-color: var(--surface); border-bottom: 1px solid var(--line); padding: 2.6mm 0 2.6mm 0; margin: 0; opacity: 0.5; } SvDishName { color: var(--text); font-family: "native:MainBold"; font-size: 2.75mm; padding: 0; margin: 0; background-color: transparent; } SvDishPrice { color: var(--text); font-family: "native:MainRegular"; font-size: 2.5mm; padding: 0.6mm 0 0 0; margin: 0; background-color: transparent; } -SvDishPhoto { color: var(--brand); background-color: var(--brand-tint); border-radius: var(--radius); padding: 0; margin: 0 0 0 2mm; } -SvOrderThumb { color: var(--brand); background-color: var(--brand-tint); border-radius: var(--radius); padding: 0; margin: 0 2.4mm 0 0; } +SvDishPhoto { color: #f3e7d3; background-color: transparent; border-radius: var(--radius); padding: 0; margin: 0 0 0 2mm; } +SvOrderThumb { color: #f3e7d3; background-color: transparent; border-radius: var(--radius); padding: 0; margin: 0 2.4mm 0 0; } SvOrderRow { background-color: var(--surface); border-bottom: 1px solid var(--line); padding: 2.6mm 3mm 2.6mm 3mm; margin: 0; } SvOrderRow.pressed { background-color: var(--surface-alt); } SvActiveOrder { background-color: var(--brand-tint); border-radius: var(--radius); padding: 2mm 3mm 2mm 3mm; margin: 2mm 0 0 0; } @@ -1765,13 +1765,13 @@ SvQueueRowPicked { background-color: var(--brand-tint); border-bottom: 1px solid SvCuisineSelected { color: var(--brand-contrast-dark); background-color: var(--brand-dark); } SvRestaurantCard { background-color: var(--surface-dark); } SvRestaurantCard.pressed { background-color: var(--surface-alt-dark); } - SvRestaurantPhoto { color: var(--brand-dark); background-color: var(--brand-tint-dark); } + SvRestaurantPhoto { color: #f3e7d3; background-color: transparent; } SvRestaurantName { color: var(--text-dark); } SvRestaurantTitle { color: var(--text-dark); } SvPhotoBadge { color: var(--brand-dark); background-color: var(--surface-dark); } SvClosedBadge { color: var(--surface-dark); background-color: var(--text-dark); } SvPopularBadge { color: var(--brand-dark); background-color: var(--brand-tint-dark); } - SvHeroPhoto { color: var(--brand-dark); background-color: var(--brand-tint-dark); } + SvHeroPhoto { color: #f3e7d3; background-color: transparent; } SvSectionChip { color: var(--text-dark); background-color: var(--surface-alt-dark); } SvSectionChip.pressed { background-color: var(--surface-press-dark); } SvMenuSection { color: var(--text-dark); } @@ -1780,8 +1780,8 @@ SvQueueRowPicked { background-color: var(--brand-tint); border-bottom: 1px solid SvDishRowOff { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } SvDishName { color: var(--text-dark); } SvDishPrice { color: var(--text-dark); } - SvDishPhoto { color: var(--brand-dark); background-color: var(--brand-tint-dark); } - SvOrderThumb { color: var(--brand-dark); background-color: var(--brand-tint-dark); } + SvDishPhoto { color: #f3e7d3; background-color: transparent; } + SvOrderThumb { color: #f3e7d3; background-color: transparent; } SvOrderRow { background-color: var(--surface-dark); border-bottom: 1px solid var(--line-dark); } SvOrderRow.pressed { background-color: var(--surface-alt-dark); } SvActiveOrder { background-color: var(--brand-tint-dark); } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java index 7834f1078f9..57371c583a4 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Prefs.java @@ -206,7 +206,7 @@ public static void addRecent(PlaceDto place) { /// last one is dropped and not shown to the next: the recent /// destinations, which are kept here only and say where a person goes, /// and the copy of everything the account keeps on the server -- home, - /// work, the emergency contact, the card paid with, the delivery options. + /// work, the card paid with, the delivery options. /// That copy is replaced when the new account's values arrive, but they /// may not arrive, and then the last person's would be on screen and go /// out with the next save as the new person's own. How the app looks diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java index a819dd8cecb..aee9bc63970 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/Telemetry.java @@ -220,9 +220,8 @@ public static void phoneVerified() { // ------------------------------------------------------------------ deliveries /// @param lines how many dishes the cart holds - public static void orderQuoted(int lines, double meters) { - send(DELIVERY, "order_quoted", "lines", Integer.valueOf(lines), "distance", - distance(meters)); + public static void orderQuoted(int lines) { + send(DELIVERY, "order_quoted", "lines", Integer.valueOf(lines)); } public static void orderPlaced(boolean cash, boolean promo) { @@ -343,12 +342,6 @@ static String fare(long cents) { : units < 35 ? "20_to_35" : units < 50 ? "35_to_50" : "50_or_more"; } - /// How far, as a bracket in kilometres. - static String distance(double meters) { - return meters < 2000 ? "under_2km" : meters < 5000 ? "2_to_5km" - : meters < 10000 ? "5_to_10km" : meters < 25000 ? "10_to_25km" : "25km_or_more"; - } - /// Reports `name` with `pairs`: a parameter's name, then its value. private static void send(String category, String name, Object... pairs) { AnalyticsEvent.Builder event = AnalyticsEvent.create(name).category(category); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java index d3d15980cff..cc8e734e8bd 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/AdminForm.java @@ -74,18 +74,21 @@ public void flagged() { } }); menu.item(FontImage.MATERIAL_INSIGHTS, "Dashboard", "home", () -> { }); - menu.item(FontImage.MATERIAL_ASSIGNMENT_IND, "Applications", "applications", + menu.item(FontImage.MATERIAL_STOREFRONT, "Restaurants", "restaurants", + () -> RestaurantsForm.show(form)); + menu.item(FontImage.MATERIAL_ASSIGNMENT_IND, "Couriers", "applications", () -> ApplicationsForm.show(form)); menu.item(FontImage.MATERIAL_GROUP, "People", "users", () -> UsersForm.show(form, "")); - menu.item(FontImage.MATERIAL_RECEIPT_LONG, "Deliveries", "deliveries", + menu.item(FontImage.MATERIAL_RECEIPT_LONG, "Orders", "deliveries", () -> AdminDeliveriesForm.show(form)); menu.item(FontImage.MATERIAL_PAYMENTS, "Payments", "payments", () -> PaymentsForm.show(form)); menu.item(FontImage.MATERIAL_MAP, "Live map", "fleet", this::fleet); + menu.item(FontImage.MATERIAL_LOCAL_OFFER, "Promos", "promos", () -> PromosForm.show(form)); menu.item(FontImage.MATERIAL_SELL, "Pricing", "pricing", () -> PricingForm.show(form)); // The bar a title bar would be, with the menu where the map screens - // have it, and the mode said beside the title: an admin who also deliveries + // have it, and the mode said beside the title: an admin who also delivers // should never have to wonder which app this is. Container bar = new Container(new BorderLayout()); bar.setUIID("SvHomeBar"); @@ -101,20 +104,26 @@ public void flagged() { Container page = dashboard.page(); Ui.section(page, "Manage"); Container manage = new Container(BoxLayout.y()); - manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_ASSIGNMENT_IND, "Applications", - "People who asked to drive, and their papers", null), "applications", + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_STOREFRONT, "Restaurants", + "Restaurants asking to join, and the ones selling", null), "restaurants", + e -> RestaurantsForm.show(form))); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_ASSIGNMENT_IND, "Couriers", + "People who asked to deliver, and their papers", null), "applications", e -> ApplicationsForm.show(form))); manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_GROUP, "People", "Roles, flags, and blocking an account", null), "users", e -> UsersForm.show(form, ""))); - manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_RECEIPT_LONG, "Deliveries", + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_RECEIPT_LONG, "Orders", "The most recent, in every state", null), "deliveries", e -> AdminDeliveriesForm.show(form))); manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_PAYMENTS, "Payments", "What was charged, and refunds", null), "payments", e -> PaymentsForm.show(form))); manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_MAP, "Live map", - "Where every car online is", null), "fleet", e -> fleet())); + "Where every courier on line is", null), "fleet", e -> fleet())); + manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_LOCAL_OFFER, "Promos", + "Codes customers type at checkout", null), "promos", + e -> PromosForm.show(form))); manage.add(Ui.tap(Ui.row(FontImage.MATERIAL_SELL, "Pricing", "What a delivery costs, and the service's share", null), "pricing", e -> PricingForm.show(form))); @@ -169,7 +178,11 @@ public void show() { } private void open(String place) { - if ("applications".equals(place)) { + if ("restaurants".equals(place)) { + RestaurantsForm.show(form); + } else if ("promos".equals(place)) { + PromosForm.show(form); + } else if ("applications".equals(place)) { ApplicationsForm.show(form); } else if ("users".equals(place)) { UsersForm.show(form, ""); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java index 5fb0865a3ec..9b8eb0fd394 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/ApplicationsForm.java @@ -50,7 +50,7 @@ public final class ApplicationsForm { private static final String[] KINDS = {"photo_id", "licence", "selfie", "vehicle_registration", "insurance"}; - private static final String[] KIND_NAMES = {"Photo ID", "Onboarding licence", "A photo of you", + private static final String[] KIND_NAMES = {"Photo ID", "Driving licence", "A photo of you", "Vehicle registration", "Insurance"}; private final Panes panes; @@ -162,7 +162,7 @@ private void show(final CourierApplicationDto application) { page.add(Ui.primary("Approve", "approve", e -> Api.admin().approve( application.username(), Net.to(approved -> { Telemetry.applicationReviewed(true); - Ui.say(Lang.tr("{0} can now drive", application.name())); + Ui.say(Lang.tr("{0} can now deliver", application.name())); panes.home(); }, Ui::fail)))); page.add(Ui.danger("Send back", "reject", e -> panes.reason("Send back", diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java index 208dba88c25..d10642057dd 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/Console.java @@ -64,13 +64,13 @@ final class Console { /// The places, in the order they are listed: an id, and what it is called. private static final String[][] PLACES = { - {HOME, "Dashboard"}, {"applications", "Applications"}, {"users", "People"}, - {"deliveries", "Deliveries"}, {"payments", "Payments"}, {FLEET, "Live map"}, - {"pricing", "Pricing"}}; + {HOME, "Dashboard"}, {"restaurants", "Restaurants"}, {"applications", "Couriers"}, + {"users", "People"}, {"deliveries", "Orders"}, {"payments", "Payments"}, + {FLEET, "Live map"}, {"promos", "Promos"}, {"pricing", "Pricing"}}; private static final char[] ICONS = {FontImage.MATERIAL_INSIGHTS, - FontImage.MATERIAL_ASSIGNMENT_IND, FontImage.MATERIAL_GROUP, + FontImage.MATERIAL_STOREFRONT, FontImage.MATERIAL_ASSIGNMENT_IND, FontImage.MATERIAL_GROUP, FontImage.MATERIAL_RECEIPT_LONG, FontImage.MATERIAL_PAYMENTS, FontImage.MATERIAL_MAP, - FontImage.MATERIAL_SELL}; + FontImage.MATERIAL_LOCAL_OFFER, FontImage.MATERIAL_SELL}; /// The place that was open when the window stopped being wide, or the /// device was turned: where the console opens when it is wide again. @@ -170,7 +170,11 @@ private void go(String id) { return; } mark(id); - if ("applications".equals(id)) { + if ("restaurants".equals(id)) { + RestaurantsForm.show(panes); + } else if ("promos".equals(id)) { + PromosForm.show(panes); + } else if ("applications".equals(id)) { ApplicationsForm.show(panes); } else if ("users".equals(id)) { UsersForm.show(panes, ""); @@ -236,12 +240,12 @@ protected Dimension calcPreferredSize() { items[iter] = entry(ICONS[iter], PLACES[iter][1], "nav-" + id, () -> go(id)); list.add(items[iter]); } - // The other modes this account's roles open. Everyone may delivery. + // The other modes this account's roles open. Everyone may order. list.add(Ui.label("Switch to", "SvSideLabel")); - list.add(entry(FontImage.MATERIAL_HAIL, "Riding", "switch-" + Nav.CUSTOMER, + list.add(entry(FontImage.MATERIAL_RESTAURANT, "Ordering", "switch-" + Nav.CUSTOMER, () -> Nav.switchTo(Nav.CUSTOMER))); if (user.courier()) { - list.add(entry(FontImage.MATERIAL_DIRECTIONS_CAR, "Onboarding", "switch-" + Nav.COURIER, + list.add(entry(FontImage.MATERIAL_TWO_WHEELER, "Delivering", "switch-" + Nav.COURIER, () -> Nav.switchTo(Nav.COURIER))); } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java index 7871b3a11c3..d29504f6e7b 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/FleetForm.java @@ -39,7 +39,7 @@ import java.util.List; import java.util.Map; -/// Every car online, on one map, moving as the couriers report in. +/// Every courier online, on one map, moving as the couriers report in. public class FleetForm extends LiveChannel.Adapter { private static final int POLL_MILLIS = 10000; @@ -60,7 +60,7 @@ public class FleetForm extends LiveChannel.Adapter { stage.addRound(FontImage.MATERIAL_ZOOM_OUT_MAP, "recentre", this::frame); count.setName("fleetCount"); stage.sheet().peek().add(count); - stage.sheet().peek().add(Ui.label("Cars move as their couriers report in.", "SvMuted")); + stage.sheet().peek().add(Ui.label("Couriers move as they report in.", "SvMuted")); stage.sheet().ready(false); form.add(stage.layers()); form.addShowListener(e -> { @@ -136,8 +136,8 @@ private void frame() { private void counted() { int size = cars.size(); - count.setText(size == 0 ? Lang.tr("No cars online") : size == 1 - ? Lang.tr("1 car online") : Lang.tr("{0} cars online", String.valueOf(size))); + count.setText(size == 0 ? Lang.tr("No couriers online") : size == 1 + ? Lang.tr("1 courier online") : Lang.tr("{0} couriers online", String.valueOf(size))); stage.map.repaint(); Ui.refresh(form); } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PromosForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PromosForm.java new file mode 100644 index 00000000000..ba404873c95 --- /dev/null +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/PromosForm.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.savor.admin; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BoxLayout; +import com.codenameone.examples.savor.api.PromoDto; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; + +import java.util.List; + +/// The promo codes a customer can type at checkout: a share off, or an +/// amount off, above a smallest order. A code is switched off, never removed, +/// so the orders that used it still say what it was. +final class PromosForm { + private final Panes panes; + private final Container list = Ui.list("promos"); + + private PromosForm(Panes panes) { + this.panes = panes; + Container top = new Container(BoxLayout.y()); + top.setUIID("SvPage"); + top.add(Ui.secondary("New promo", "new-promo", e -> edit(null))); + Ui.pull(list, this::load); + panes.list("Promos", "Promos", Panes.listed(top, list), this::load); + } + + static void show(Form previous) { + show(Panes.phone(previous)); + } + + static void show(Panes panes) { + new PromosForm(panes); + } + + private void load() { + Api.admin().promos(Net.to(this::fill, Ui.failed(list, this::load))); + } + + private void fill(List found) { + list.removeAll(); + if (found == null || found.isEmpty()) { + list.add(Ui.empty(FontImage.MATERIAL_SELL, "No promos", null)); + } + for (int iter = 0; found != null && iter < found.size(); iter++) { + final PromoDto promo = found.get(iter); + Container row = Ui.row(FontImage.MATERIAL_SELL, promo.code(), describe(promo), + Ui.badge(promo.active() ? Lang.tr("On") : Lang.tr("Off"), + promo.active() ? "SvBadgeGood" : "SvBadgeBad")); + final Container picked = row; + list.add(Ui.tap(row, "promo-" + promo.code(), e -> { + panes.picked(picked); + edit(promo); + })); + } + panes.refresh(); + } + + static String describe(PromoDto promo) { + String off = promo.percentOff() > 0 ? Lang.tr("{0}% off", String.valueOf(promo.percentOff())) + : Lang.tr("{0} off", Ui.money(promo.amountOffCents(), null)); + return promo.minimumCents() > 0 ? off + Ui.DOT + + Lang.tr("over {0}", Ui.money(promo.minimumCents(), null)) : off; + } + + private void edit(final PromoDto promo) { + Container page = Ui.page(); + final TextField code = Ui.field(page, "Code", "promo-code", "WELCOME10"); + final TextField percent = Ui.field(page, "Percent off", "promo-percent", "10"); + percent.setConstraint(TextArea.NUMERIC); + final TextField amount = Ui.field(page, "Or an amount off", "promo-amount", "5.00"); + amount.setConstraint(TextArea.DECIMAL); + final TextField minimum = Ui.field(page, "Smallest order", "promo-minimum", "15.00"); + minimum.setConstraint(TextArea.DECIMAL); + final boolean[] active = {promo == null || promo.active()}; + page.add(Ui.row((char) 0, Lang.tr("Active"), null, Ui.toggle("promo-active", active[0], + value -> active[0] = value))); + if (promo != null) { + code.setText(promo.code()); + code.setEditable(false); + percent.setText(String.valueOf(promo.percentOff())); + amount.setText(amount(promo.amountOffCents())); + minimum.setText(amount(promo.minimumCents())); + } + final Label error = new Label("", "SvError"); + page.add(error); + page.add(Ui.primary("Save", "save-promo", e -> { + int share = number(percent.getText()); + long off = cents(amount.getText()); + String typed = code.getText().trim(); + if (typed.length() == 0 || share < 0 || share > 100 || off < 0 + || share == 0 && off == 0) { + error.setText(Lang.tr("A code, and either a percent or an amount off")); + page.revalidate(); + return; + } + Api.admin().savePromo(new PromoDto(typed, share, share > 0 ? 0 : off, + Math.max(0, cents(minimum.getText())), active[0]), Net.to(saved -> { + Ui.say("Saved"); + panes.home(); + }, Ui::fail)); + })); + panes.detail(promo == null ? Lang.tr("New promo") : promo.code(), "PromoEdit", page); + } + + private static String amount(long cents) { + long part = cents % 100; + return cents / 100 + "." + (part < 10 ? "0" : "") + part; + } + + private static int number(String text) { + try { + return text.trim().length() == 0 ? 0 : Integer.parseInt(text.trim()); + } catch (NumberFormatException wrong) { + return -1; + } + } + + private static long cents(String text) { + String clean = text.trim().replace(',', '.'); + if (clean.length() == 0) { + return 0; + } + int dot = clean.indexOf('.'); + try { + if (dot < 0) { + return Long.parseLong(clean) * 100; + } + String part = (clean.substring(dot + 1) + "00").substring(0, 2); + return Long.parseLong(dot == 0 ? "0" : clean.substring(0, dot)) * 100 + + Long.parseLong(part); + } catch (NumberFormatException wrong) { + return -1; + } + } +} diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/RestaurantsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/RestaurantsForm.java new file mode 100644 index 00000000000..7ece962befa --- /dev/null +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/RestaurantsForm.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.savor.admin; + +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codenameone.examples.savor.api.ReasonDto; +import com.codenameone.examples.savor.api.RestaurantDto; +import com.codenameone.examples.savor.api.SuspendDto; +import com.codenameone.examples.savor.customer.Food; +import com.codenameone.examples.savor.net.Api; +import com.codenameone.examples.savor.net.Net; +import com.codenameone.examples.savor.ui.Lang; +import com.codenameone.examples.savor.ui.Ui; + +import java.util.List; + +/// The restaurants on the platform: the ones asking to join, to approve or +/// send back, and the ones selling, to suspend or let back. +final class RestaurantsForm { + private final Panes panes; + private final Container list = Ui.list("restaurants"); + private String status = "PENDING"; + + private RestaurantsForm(Panes panes) { + this.panes = panes; + Container top = new Container(BoxLayout.y()); + top.setUIID("SvPage"); + top.add(Ui.choice("restaurant-status", + new String[] {"Waiting", "Selling", "Suspended", "Sent back"}, + new String[] {"PENDING", "APPROVED", "SUSPENDED", "REJECTED"}, status, value -> { + status = value; + load(); + })); + Ui.pull(list, this::load); + panes.list("Restaurants", "Restaurants", Panes.listed(top, list), this::load); + } + + static void show(Form previous) { + show(Panes.phone(previous)); + } + + static void show(Panes panes) { + new RestaurantsForm(panes); + } + + private void load() { + final String asked = status; + Api.admin().restaurants(asked, Net.to(found -> { + if (asked.equals(status)) { + fill(found); + } + }, Ui.failed(list, this::load))); + } + + private void fill(List found) { + list.removeAll(); + if (found == null || found.isEmpty()) { + list.add(Ui.empty(FontImage.MATERIAL_STOREFRONT, "Nothing here", + "PENDING".equals(status) ? "Every restaurant has had its answer." : null)); + } + for (int iter = 0; found != null && iter < found.size(); iter++) { + final RestaurantDto restaurant = found.get(iter); + Container row = Ui.row((char) 0, restaurant.name(), + restaurant.cuisine() + Ui.DOT + restaurant.address(), + Ui.badge(word(restaurant.status()), badge(restaurant.status()))); + Label icon = new Label("", "SvRowIcon"); + Ui.icon(icon, Food.icon(restaurant.cuisine()), 4f); + row.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(icon)); + final Container picked = row; + list.add(Ui.tap(row, "restaurant-" + restaurant.id(), e -> { + panes.picked(picked); + review(restaurant); + })); + } + panes.refresh(); + } + + static String word(String status) { + if ("PENDING".equals(status)) { + return Lang.tr("Waiting"); + } + if ("APPROVED".equals(status)) { + return Lang.tr("Selling"); + } + if ("SUSPENDED".equals(status)) { + return Lang.tr("Suspended"); + } + return Lang.tr("Sent back"); + } + + static String badge(String status) { + return "APPROVED".equals(status) ? "SvBadgeGood" + : "PENDING".equals(status) ? "SvBadgeWarn" : "SvBadgeBad"; + } + + private void review(final RestaurantDto restaurant) { + Container page = Ui.page(); + Label state = Ui.badge(word(restaurant.status()), badge(restaurant.status())); + state.setName("restaurantStatus"); + page.add(Ui.plain(restaurant.name(), "SvRestaurantTitle")); + page.add(state); + if (restaurant.reason() != null && restaurant.reason().length() > 0) { + page.add(Ui.text(restaurant.reason(), "SvNotice")); + } + Ui.section(page, "Restaurant"); + page.add(Ui.fact("Cuisine", restaurant.cuisine())); + page.add(Ui.fact("Address", restaurant.address())); + page.add(Ui.fact("Hours", hour(restaurant.opensAt()) + " - " + hour(restaurant.closesAt()))); + page.add(Ui.fact("Time to cook", Lang.tr("{0} min", String.valueOf(restaurant.prepMinutes())))); + page.add(Ui.fact("Rating", restaurant.ratingCount() == 0 ? Lang.tr("New") + : Ui.tenths(restaurant.rating()) + " (" + restaurant.ratingCount() + ")")); + if (restaurant.description() != null && restaurant.description().length() > 0) { + page.add(Ui.text(restaurant.description(), "SvMuted")); + } + Net.Ok done = saved -> { + Ui.say(Lang.tr("{0}: {1}", saved.name(), word(saved.status()))); + panes.home(); + }; + if ("PENDING".equals(restaurant.status())) { + page.add(Ui.primary("Approve", "approve-restaurant", + e -> Api.admin().approveRestaurant(restaurant.id(), Net.to(done, Ui::fail)))); + page.add(Ui.danger("Send back", "reject-restaurant", e -> panes.reason("Send back", + Lang.tr("{0} sees what you write, and can apply again once it is put right.", + restaurant.name()), "Send back", + why -> Api.admin().rejectRestaurant(restaurant.id(), new ReasonDto(why), + Net.to(done, Ui::fail))))); + } else if ("APPROVED".equals(restaurant.status())) { + page.add(Ui.danger("Suspend", "suspend-restaurant", e -> panes.reason("Suspend", + Lang.tr("{0} stops appearing to customers until it is let back.", + restaurant.name()), "Suspend", + why -> Api.admin().suspendRestaurant(restaurant.id(), new SuspendDto(true, why), + Net.to(done, Ui::fail))))); + } else if ("SUSPENDED".equals(restaurant.status())) { + page.add(Ui.primary("Let back", "unsuspend-restaurant", + e -> Api.admin().suspendRestaurant(restaurant.id(), new SuspendDto(false, null), + Net.to(done, Ui::fail)))); + } + panes.detail(restaurant.name(), "RestaurantReview", page); + } + + private static String hour(int minutes) { + int hours = minutes / 60; + int rest = minutes % 60; + return (hours < 10 ? "0" : "") + hours + ":" + (rest < 10 ? "0" : "") + rest; + } +} diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java index f12644288f5..7da0f3b6bb7 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/admin/UsersForm.java @@ -208,7 +208,7 @@ private void show(final UserDetailDto detail) { page.add(numbers); StatRow money = Ui.stats(2); money.stat(Ui.money(detail.spent(), null), "Spent riding", "userSpent"); - money.stat(Ui.money(detail.earned(), null), "Earned onboarding", "userEarned"); + money.stat(Ui.money(detail.earned(), null), "Earned delivering", "userEarned"); page.add(money); Ui.section(page, "Contact"); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java index 8bff4fe291c..080239800fe 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplicationForm.java @@ -65,11 +65,11 @@ public final class ApplicationForm { /// The documents asked for, in the order the server lists them. public static final String[] KINDS = {"photo_id", "licence", "selfie", "vehicle_registration", "insurance"}; - private static final String[] KIND_NAMES = {"Photo ID", "Onboarding licence", "A photo of you", + private static final String[] KIND_NAMES = {"Photo ID", "Driving licence", "A photo of you", "Vehicle registration", "Insurance"}; private static final String[] KIND_HINTS = {"A passport or an identity card", "The side with your photo on it", "Your face, in good light, no sunglasses", - "The paper that says the car is yours", "The page with the policy's dates on it"}; + "The paper that says the vehicle is yours", "The page with the policy's dates on it"}; private static final char[] KIND_ICONS = {FontImage.MATERIAL_BADGE, FontImage.MATERIAL_CREDIT_CARD, FontImage.MATERIAL_FACE, FontImage.MATERIAL_DESCRIPTION, FontImage.MATERIAL_VERIFIED_USER}; @@ -292,7 +292,7 @@ private void review() { body.add(Ui.fact("Date of birth", application.dateOfBirth())); body.add(Ui.fact("Licence", application.licenceNumber())); body.add(Ui.fact("Licence expires", application.licenceExpiry())); - Ui.section(body, "Your car"); + Ui.section(body, "Your vehicle"); body.add(Ui.fact("Vehicle", ApplyForm.car(application))); body.add(Ui.fact("Licence plate", application.vehiclePlate())); Ui.section(body, "Documents"); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java index 26e71e8590c..4ab45227d54 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/ApplyForm.java @@ -123,16 +123,16 @@ private void invite(final CourierApplicationDto application, boolean started) { started ? "Finish your application" : "Earn on your own schedule", started ? Lang.tr("{0} of 5 documents added. Pick up where you left off.", String.valueOf(5 - missing(application))) - : "Tell us about you and your car, add five photos, and we take it " + : "Tell us about you and your vehicle, add five photos, and we take it " + "from there.", "SvBannerIcon"); page.add(banner); Label state = Ui.label(started ? "Draft" : "Not started", "SvBadge"); state.setName("applicationStatus"); page.add(FlowLayout.encloseCenter(state)); Ui.section(page, "What you will need"); - page.add(Ui.row(FontImage.MATERIAL_BADGE, "A onboarding licence", + page.add(Ui.row(FontImage.MATERIAL_BADGE, "A driving licence", "Its number, its expiry date and a photo of it", null)); - page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, "A car with four doors", + page.add(Ui.row(FontImage.MATERIAL_TWO_WHEELER, "A bike, a scooter or a car", "Registered and insured, with the papers to show it", null)); page.add(Ui.row(FontImage.MATERIAL_PHOTO_CAMERA, "About ten minutes", "Most of it is taking five photos", null)); @@ -160,7 +160,7 @@ private void pending(CourierApplicationDto application) { "Usually within two working days", null)); page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, "You go online", "This screen becomes your courier's home the moment you are approved", null)); - Ui.section(page, "Your car"); + Ui.section(page, "Your vehicle"); page.add(Ui.row(FontImage.MATERIAL_DIRECTIONS_CAR, car(application), application.vehiclePlate(), null)); } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java index 2e630c12d87..d3363366e67 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/CourierForm.java @@ -393,7 +393,7 @@ private void waiting() { peek.add(Ui.text(Lang.tr("Delivered: {0} earned", lastFare), "SvNotice")); } if (online) { - peek.add(Ui.label("Stay close to busy streets; requests come to the nearest car.", + peek.add(Ui.label("Stay close to busy streets; orders go to the nearest courier.", "SvMuted")); peek.add(FlowLayout.encloseCenter(new Pulse())); } else { diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java index 38bfa48bc88..544c652de0e 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/courier/EarningsForm.java @@ -41,7 +41,7 @@ import com.codenameone.examples.savor.ui.Ui; import java.util.List; -/// What onboarding has paid: what is waiting to be cashed out, the last +/// What delivering has paid: what is waiting to be cashed out, the last /// fortnight a day at a time, and the payouts already sent. /// /// The sums are the server's. It works them out from the deliveries it recorded, diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CheckoutForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CheckoutForm.java index 2a80802ac83..d8daa579f68 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CheckoutForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CheckoutForm.java @@ -77,7 +77,7 @@ private CheckoutForm(Form previous) { page.add(note); Ui.section(page, Lang.tr("Payment")); - method.setName("method"); + method.setName("payWith"); method.addActionListener(e -> WalletForm.pick(form, methodId, picked -> { pick(picked); form.showBack(); @@ -234,7 +234,7 @@ private void totals(QuoteDto quote) { } totals.add(Ui.plain(Lang.tr("Arrives in about {0} min", String.valueOf(quote.etaMinutes())), "SvMutedSmall")); - Telemetry.orderQuoted(Cart.lines().size(), 0); + Telemetry.orderQuoted(Cart.lines().size()); place.setText(Lang.tr("Place order") + Ui.DOT + Ui.money(quote.totalCents(), currency)); form.revalidateLater(); } @@ -254,8 +254,11 @@ private void place() { placing = false; Telemetry.orderPlaced("cash".equals(methodId), promoCode != null); Cart.clear(); - Nav.home(); - TrackForm.show(com.codename1.ui.CN.getCurrentForm(), order.id()); + // The checkout is done with: back from the order goes home. + Form home = Nav.build(); + if (home != null) { + TrackForm.show(home, order.id()); + } }, (status, message) -> { placing = false; place.setEnabled(true); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java index 091ed459f32..de68cae60af 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/CustomerForm.java @@ -64,9 +64,9 @@ public final class CustomerForm extends LiveChannel.Adapter implements Nav.Home, /// down. With it up, a frame says when to ask. private static final int POLL_MILLIS = 15000; private static final String[] SORTS = {"distance", "rating", "fee", "time"}; - private static final String[] SORT_NAMES = {"Nearest", "Top rated", "Lowest fee", "Fastest"}; + private static final String[] SORT_NAMES = {"Nearest", "Rating", "Fee", "Fastest"}; - private final Form form = Ui.home("customer"); + private final Form form = Ui.home("Customer"); private final Menu menu = new Menu(form, Nav.CUSTOMER); private final Container body = new Container(new BorderLayout()); private final Container tabs = new Container(new GridLayout(1, 4)); @@ -178,7 +178,7 @@ private Container header() { private void where() { String name = destinationName(); deliverTo.setText(name == null ? Lang.tr("Current location") : name); - deliverTo.getParent().revalidateLater(); + Ui.relayout(deliverTo.getParent()); } private void tab(char icon, String text, String name, final int index) { @@ -291,7 +291,7 @@ private void loadActive() { into.add(Ui.tap(banner, "active-" + order.id(), e -> TrackForm.show(form, order.id()))); } - into.revalidateLater(); + form.revalidateLater(); }, QUIET)); } @@ -314,7 +314,7 @@ private void fill(Container into, List found, String none) into.add(Food.card(restaurant, null, "restaurant-" + restaurant.id(), e -> RestaurantForm.show(form, restaurant.id(), null))); } - into.revalidateLater(); + Ui.refresh(form); } private Container search() { @@ -369,7 +369,7 @@ private Container orders() { for (final OrderDto order : found) { list.add(orderRow(order)); } - list.revalidateLater(); + Ui.refresh(form); }, Ui.failed(list, () -> load[0].run()))); Ui.pull(list, load[0]); load[0].run(); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DishForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DishForm.java index 426e5e4c03a..558df3b0763 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DishForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/DishForm.java @@ -115,6 +115,6 @@ private void price() { unit += picked.get(iter).priceCents(); } add.setText(Lang.tr("Add to order") + Ui.DOT + Ui.money(unit * quantity, null)); - add.getParent().revalidateLater(); + Ui.relayout(add.getParent()); } } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/Food.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/Food.java index 7f4a230e63e..5fc82480728 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/Food.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/Food.java @@ -22,9 +22,11 @@ import com.codename1.ui.Component; import com.codename1.ui.Container; import com.codename1.ui.FontImage; +import com.codename1.ui.Graphics; import com.codename1.ui.Label; import com.codename1.ui.events.ActionEvent; import com.codename1.ui.events.ActionListener; +import com.codename1.ui.geom.GeneralPath; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.LayeredLayout; @@ -64,7 +66,9 @@ public static char icon(String cuisine) { /// comes, and for good when there is none. public static Label photo(String photoId, String cuisine, String uiid, float widthMM, float heightMM) { - Label photo = new Label("", uiid); + Label photo = new Cover(uiid, cover(cuisine)); + // Light on the cover's dark ground, in either theme. + photo.getAllStyles().setFgColor(COVER_INK); Ui.icon(photo, icon(cuisine), Math.min(heightMM, widthMM) * 0.42f); photo.setAlignment(Component.CENTER); int width = CN.convertToPixels(widthMM); @@ -75,6 +79,68 @@ public static Label photo(String photoId, String cuisine, String uiid, float wid return photo; } + /// Two deep tones per cuisine, top to bottom, for the cover a restaurant + /// or a dish has until it has a photograph: dark and warm, the way a + /// menu's own photography is lit, rather than a pale tint behind an icon. + private static final int[][] COVERS = { + {0x7a2e1d, 0x2b1410}, {0x6b3f1f, 0x24160d}, {0x1f3d4a, 0x0e1a20}, + {0x6e4a1c, 0x251a0c}, {0x5c2a2a, 0x1f0f10}, {0x3d4a2a, 0x151a0f}, + {0x4a2418, 0x170c09}, {0x6b5317, 0x241c08}, {0x2f4f35, 0x0f1c12}, + {0x6a3350, 0x22111b}, {0x4d2e4a, 0x1a0f19}, {0x4a3424, 0x1a120c}}; + + private static final int COVER_INK = 0xf3e7d3; + + private static int[] cover(String cuisine) { + for (int iter = 0; iter < CUISINES.length; iter++) { + if (CUISINES[iter].equalsIgnoreCase(cuisine)) { + return COVERS[iter % COVERS.length]; + } + } + return new int[] {0x33302c, 0x121110}; + } + + /// A photo's place that paints its cover itself while it shows an icon, + /// and steps aside once [Pictures] has given it the photograph. + private static final class Cover extends Label { + private final int[] tones; + + Cover(String uiid, int[] tones) { + super("", uiid); + this.tones = tones; + } + + @Override + public void paint(Graphics g) { + if (getIcon() instanceof FontImage) { + int x = getX(); + int y = getY(); + int w = getWidth(); + int h = getHeight(); + int[] clip = g.getClip(); + // A hero runs edge to edge; everything else is a card. + boolean rounded = !"SvHeroPhoto".equals(getUIID()) && g.isShapeClipSupported(); + if (rounded) { + float r = CN.convertToPixels(2f); + GeneralPath shape = new GeneralPath(); + shape.moveTo(x + r, y); + shape.lineTo(x + w - r, y); + shape.quadTo(x + w, y, x + w, y + r); + shape.lineTo(x + w, y + h - r); + shape.quadTo(x + w, y + h, x + w - r, y + h); + shape.lineTo(x + r, y + h); + shape.quadTo(x, y + h, x, y + h - r); + shape.lineTo(x, y + r); + shape.quadTo(x, y, x + r, y); + shape.closePath(); + g.setClip(shape); + } + g.fillLinearGradient(tones[0], tones[1], x, y, w, h, false); + g.setClip(clip); + } + super.paint(g); + } + } + /// `$`, `$$` or `$$$`. public static String priceLevel(int level) { StringBuilder signs = new StringBuilder(); @@ -123,7 +189,7 @@ public static Container card(final RestaurantSummaryDto restaurant, String curre words.add(title); words.add(Ui.plain(restaurant.cuisine() + Ui.DOT + Ui.distance(restaurant.distanceMeters()), "SvMutedSmall")); - words.add(Ui.plain(meta(restaurant.rating(), restaurant.ratingCount(), + words.add(Ui.text(meta(restaurant.rating(), restaurant.ratingCount(), restaurant.etaMinutes(), restaurant.deliveryFeeCents(), currency), "SvMetaLine")); card.add(BorderLayout.CENTER, words); return Ui.tap(card, name, open); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/RestaurantForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/RestaurantForm.java index 55a92a9bff3..dce01b5f0cb 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/RestaurantForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/RestaurantForm.java @@ -95,7 +95,7 @@ private void fill(RestaurantDto found) { page.add(title); page.add(Ui.plain(found.cuisine() + Ui.DOT + Food.priceLevel(found.priceLevel()) + Ui.DOT + found.address(), "SvMutedSmall")); - page.add(Ui.plain(Food.meta(found.rating(), found.ratingCount(), found.etaMinutes(), + page.add(Ui.text(Food.meta(found.rating(), found.ratingCount(), found.etaMinutes(), found.deliveryFeeCents(), null), "SvMetaLine")); if (found.description() != null && found.description().length() > 0) { page.add(Ui.text(found.description(), "SvMuted")); diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/TrackForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/TrackForm.java index b7be1d669ba..a0c24f5d77f 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/TrackForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/customer/TrackForm.java @@ -108,7 +108,9 @@ private void fill(OrderDto found) { markers(); sheet.reset(); Container peek = sheet.peek(); - peek.add(Ui.plain(Food.state(found.state()), "SvSheetTitle")); + Label title = Ui.plain(Food.state(found.state()), "SvSheetTitle"); + title.setName("orderState"); + peek.add(title); if (Food.active(found.state())) { peek.add(Ui.plain(eta(found), "SvMuted")); } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java index e421da3cea3..9c1ae7dae32 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/MerchantForm.java @@ -63,6 +63,7 @@ public class MerchantForm extends LiveChannel.Adapter implements Nav.Home { private final Menu menu = new Menu(form, Nav.MERCHANT); private final Container body = new Container(new BorderLayout()); private final Label title = Ui.label("Your restaurant", "SvHeading"); + private final Label status = Ui.label("Taking orders", "SvMutedSmall"); private final Switch open = new Switch(); private RestaurantDto restaurant; private Container queue; @@ -80,12 +81,14 @@ public MerchantForm() { Container bar = new Container(new BorderLayout()); bar.setUIID("SvHomeBar"); bar.add(BorderLayout.WEST, FlowLayout.encloseCenterMiddle(menu.flatButton())); - bar.add(BorderLayout.CENTER, FlowLayout.encloseLeftMiddle(title)); + // The name over whether it is taking orders, so that a long name and + // the switch share a phone's width without running into each other. + Container named = new Container(BoxLayout.y()); + named.add(title).add(status); + bar.add(BorderLayout.CENTER, BorderLayout.centerAbsolute(named)); open.setName("accepting"); open.addActionListener(e -> pause(!open.isValue())); - Container accepting = new Container(BoxLayout.x()); - accepting.add(Ui.plain(Lang.tr("Taking orders"), "SvMutedSmall")).add(open); - bar.add(BorderLayout.EAST, FlowLayout.encloseRightMiddle(accepting)); + bar.add(BorderLayout.EAST, FlowLayout.encloseRightMiddle(open)); Container screen = new Container(new BorderLayout()); screen.setUIID("SvForm"); screen.setSafeArea(true); @@ -126,7 +129,8 @@ private void shown(RestaurantDto found) { restaurant = found; title.setText(found.name()); open.setValue(!found.paused()); - title.getParent().revalidateLater(); + status.setText(Lang.tr(found.paused() ? "Paused" : "Taking orders")); + form.revalidateLater(); } private void pause(final boolean paused) { @@ -209,7 +213,7 @@ private void fill(Container into, List found) { Ui.say("New order"); } waiting = placed; - into.revalidateLater(); + Ui.relayout(into); if (detail != null) { if (again != null) { open(again); @@ -218,7 +222,7 @@ private void fill(Container into, List found) { detail.removeAll(); detail.add(BorderLayout.CENTER, Ui.empty(FontImage.MATERIAL_RECEIPT_LONG, Lang.tr("Pick an order"), null)); - detail.revalidateLater(); + Ui.relayout(detail); } } } @@ -227,10 +231,10 @@ private Component row(final OrderDto order) { Container row = new Container(new BorderLayout()); row.setUIID(order.id().equals(picked) ? "SvQueueRowPicked" : "SvQueueRow"); Container words = new Container(BoxLayout.y()); - words.add(Ui.plain(order.customerName() + Ui.DOT + "#" + Orders.shortId(order.id()), - "SvRowTitle")); + words.add(Ui.plain(order.customerName(), "SvRowTitle")); words.add(Ui.plain(Orders.summary(order), "SvMutedSmall")); - words.add(Ui.plain(Ui.when(order.placedAt()), "SvMutedSmall")); + words.add(Ui.plain("#" + Orders.shortId(order.id()) + Ui.DOT + Ui.when(order.placedAt()), + "SvMutedSmall")); row.add(BorderLayout.CENTER, words); row.add(BorderLayout.EAST, Ui.badge(Orders.state(order.state()), Orders.badge(order.state()))); @@ -249,7 +253,7 @@ private Component row(final OrderDto order) { private void open(OrderDto order) { detail.removeAll(); detail.add(BorderLayout.CENTER, OrderPane.page(order, changed -> loadQueue())); - detail.revalidateLater(); + Ui.relayout(detail); } @Override diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/SalesPage.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/SalesPage.java index 1416fd2197f..b5972db495e 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/SalesPage.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/merchant/SalesPage.java @@ -50,7 +50,7 @@ static Container page() { Lang.tr("Last 7 days"), "sales-week"); stats.stat(String.valueOf(summary.waiting()), Lang.tr("Waiting now"), "waiting"); numbers.add(stats); - page.revalidateLater(); + Ui.relayout(page); }, Ui::fail)); Api.merchant().history(Net.to(found -> { Ui.section(past, Lang.tr("Finished orders")); @@ -64,7 +64,7 @@ static Container page() { Ui.when(order.placedAt()) + Ui.DOT + Orders.summary(order), Ui.badge(Orders.state(order.state()), Orders.badge(order.state())))); } - page.revalidateLater(); + Ui.relayout(page); }, Ui::fail)); return page; } diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java index ac7edce5a21..f2a2e503178 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/SettingsForm.java @@ -64,7 +64,7 @@ public static void show(final Form previous) { Ui.section(page, "Account"); Container account = group(); account.add(Ui.tap(Ui.row(FontImage.MATERIAL_PERSON, "Profile", - "Name, emergency contact, home and work", chevron()), "profile", + "Your name and saved addresses", chevron()), "profile", e -> ProfileForm.show(form, () -> show(previous)))); account.add(Ui.row(FontImage.MATERIAL_PHONE, user.phone(), user.phoneVerified() ? null : "Needed before your first delivery", @@ -167,9 +167,9 @@ public static void show(final Form previous) { + "sent and no money changes hands.", null))); about.add(Ui.tap(Ui.row(FontImage.MATERIAL_POLICY, "Privacy policy", null, chevron()), "privacy", e -> legal(form, "Privacy policy", - "Your position is sent to the server you signed in to while you " - + "delivery or drive, and to nobody else. Your settings are kept with " - + "your account on that server.", + "Your position is sent only to the server you signed in to: to show " + + "what delivers to you and, while you deliver, to offer you orders. " + + "Your settings are kept with your account on that server.", "With usage statistics turned on, the app also counts which screens " + "are opened and which features are used, and reports that to " + "Codename One's analytics service under a made-up id for this " diff --git a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java index 53927ebb654..48f6fb22609 100644 --- a/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java +++ b/scripts/savor/common/src/main/java/com/codenameone/examples/savor/ui/Ui.java @@ -382,6 +382,18 @@ public static void shrink(Label label, float smallest) { label.setMinAutoSize(Math.min(smallest, styled)); } + /// Lays out the screen `changed` is on, after its content changed. Laying + /// out `changed` alone keeps the height it had before: a list that grew + /// from a placeholder to a dozen cards stays clipped to the placeholder. + public static void relayout(Container changed) { + Form form = changed.getComponentForm(); + if (form != null) { + form.revalidateLater(); + } else { + changed.revalidateLater(); + } + } + /// Lays `form` out again after its content changed while it was showing. /// /// Twice, the second time after the first has been drawn: text that wraps @@ -581,6 +593,10 @@ public static Container tap(Container row, String name, ActionListener send(String method, String path, String json) { NetworkManager.getInstance().addToQueueAndWait(request); status = request.getResponseCode(); byte[] body = request.getResponseData(); - if (body == null || body.length == 0 || body[0] != '{') { + // A list comes back as the parser gives it: under "root". + if (body == null || body.length == 0 || body[0] != '{' && body[0] != '[') { return new HashMap(); } try { @@ -130,6 +131,13 @@ private Map send(String method, String path, String json) { } } + /// What `path` answers when it answers a list. + java.util.List list(String path) { + Object root = get(path).get("root"); + return root instanceof java.util.List ? (java.util.List) root + : new java.util.ArrayList(); + } + /// A courier's position report. Map online(boolean online, double lat, double lng) { return post("/api/courier/status", "{\"online\":" + online + ",\"lat\":" + lat diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java index 4bc71269086..e59ab57fc23 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminDesktopTest.java @@ -158,7 +158,7 @@ protected boolean run() throws Exception { until(() -> findByName("Payments") != null, 20000, "the payments"); shot("admin-desktop-payments"); click("nav-pricing"); - until(() -> findByName("Pricing") != null && text("baseFare").length() > 0, 20000, + until(() -> findByName("Pricing") != null && text("baseCents").length() > 0, 20000, "the prices"); shot("admin-desktop-pricing"); click("nav-home"); diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java index 0bf516dd148..791b4569035 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AdminTest.java @@ -45,9 +45,9 @@ protected boolean run() throws Exception { click("fleet"); waitForForm("Fleet"); - untilText("fleetCount", "1 car online"); + untilText("fleetCount", "1 courier online"); courier.online(false, 0, 0); - untilText("fleetCount", "No cars online"); + untilText("fleetCount", "No couriers online"); back(); waitForForm("Admin"); @@ -131,8 +131,8 @@ protected boolean run() throws Exception { waitForForm("Admin"); click("pricing"); waitForForm("Pricing"); - until(() -> text("baseFare").length() > 0, 20000, "the prices"); - final String base = text("baseFare"); + until(() -> text("baseCents").length() > 0, 20000, "the prices"); + final String base = text("baseCents"); type("commissionPercent", "95"); click("submit"); until(() -> text("error").length() > 0, 20000, "a commission of 95% to be refused"); @@ -140,7 +140,7 @@ protected boolean run() throws Exception { waitForForm("Admin"); click("pricing"); waitForForm("Pricing"); - untilText("baseFare", base); + untilText("baseCents", base); return true; } diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java index c15ff64e5ff..eae0866a179 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/AnalyticsTest.java @@ -50,10 +50,13 @@ protected boolean run() throws Exception { waitForForm("Customer"); assertTrue(Telemetry.asked() && !Telemetry.allowed(), "the answer was no"); - // A no is a no: screens are shown, a fare is worked out, and nothing + // A no is a no: screens are shown, an order is priced, and nothing // is reported. - quote(); - click("back"); + cart(); + back(); + waitForForm("restaurant"); + back(); + waitForForm("Customer"); click("menu-open"); click("menu-settings"); waitForForm("Settings"); @@ -78,25 +81,24 @@ protected boolean run() throws Exception { back(); waitForForm("Customer"); until(() -> recorder.has("screen:Customer recorder.event("delivery_quoted") != null, 5000, "the quote to be reported"); - assertTrue(recorder.event("delivery_quoted").startsWith("delivery_quoted[delivery] {products="), - "the quote: " + recorder.event("delivery_quoted")); - - // Asking for the delivery, with what an operator would chart about it. - click("request"); - until(() -> recorder.event("delivery_requested") != null, 20000, - "the request to be reported"); - assertEqual("delivery_requested[delivery] {product=standard, payment=cash}", - recorder.event("delivery_requested")); - // Nobody is onboarding, so the server may give the delivery up before the - // customer does; either way the screen ends where it began. - until(() -> { - clickIfThere("dismiss"); - clickIfThere("cancel"); - return findByName("whereTo") != null; - }, 30000, "the delivery to be cancelled"); + Actor customer = Actor.signIn(CUSTOMER); + cart(); + assertTrue(recorder.has("screen:restaurant recorder.event("order_quoted") != null, 5000, "the quote to be reported"); + assertEqual("order_quoted[delivery] {lines=1}", recorder.event("order_quoted")); + + // Placing the order, with what an operator would chart about it. + click("place"); + until(() -> recorder.event("order_placed") != null, 20000, "the order to be reported"); + assertTrue(recorder.event("order_placed").startsWith("order_placed[delivery] {payment="), + "the order: " + recorder.event("order_placed")); + waitForForm("track"); + // Called off from the server's side, and the screen left by its button. + customer.post("/api/orders/" + placed(customer) + "/cancel", null); + assertEqual(200, customer.status, "cancelling the order"); + untilText("orderState", "You cancelled it"); + click("back"); + waitForForm("Customer"); // Signing out, and in again: both are reported now. signOut(); diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java index b89d92c7389..9fc8ff42aeb 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierModeTest.java @@ -22,8 +22,8 @@ import java.util.Map; -/// A whole delivery from the courier's side of the app, with the customer as the -/// other actor. +/// A whole delivery from the courier's side of the app, with the customer and +/// the kitchen as the other actors. public class CourierModeTest extends E2e { @Override protected boolean run() throws Exception { @@ -34,39 +34,45 @@ protected boolean run() throws Exception { click("goOnline"); untilText("courierState", "Looking for deliveries"); + // An order from the demo kitchen to the customer, which the kitchen + // takes: a courier is looked for once it is cooking. Actor customer = Actor.signIn(CUSTOMER); - Map delivery = customer.post("/api/deliveries", "{\"pickupLat\":37.8085," - + "\"pickupLng\":-122.4125,\"pickupAddress\":\"Pier 39\"," - + "\"dropoffLat\":37.7955,\"dropoffLng\":-122.3937," - + "\"dropoffAddress\":\"Ferry Building\",\"distanceMeters\":2400," - + "\"durationSeconds\":540}"); - assertEqual(200, customer.status, "the customer's request"); - final String id = Actor.text(delivery, "id"); + Actor kitchen = Actor.signIn(MERCHANT); + Map order = customer.post("/api/orders", "{\"restaurantId\":\"" + KITCHEN + + "\",\"lines\":[{\"dishId\":\"" + dish(customer) + "\",\"quantity\":1," + + "\"optionIds\":[]}],\"dropoffLat\":" + HERE.getLatitude() + ",\"dropoffLng\":" + + HERE.getLongitude() + ",\"dropoffAddress\":\"Pier 39\",\"note\":\"\"," + + "\"tipCents\":0,\"paymentMethodId\":\"cash\"}"); + assertEqual(200, customer.status, "the customer's order"); + final String id = Actor.text(order, "id"); + kitchen.post("/api/merchant/orders/" + id + "/accept", "{\"prepMinutes\":10}"); + assertEqual(200, kitchen.status, "the kitchen accepting the order"); untilText("courierState", "Delivery request"); assertTrue(text("fare").startsWith("$"), "the offer shows the fare"); shot("courier-offer"); click("accept"); - untilText("courierState", "Pick up Riley Customer"); - assertEqual("ACCEPTED", Actor.text(customer.get("/api/deliveries/" + id), "state")); + untilText("courierState", "Head to the restaurant"); click("arrived"); - untilText("courierState", "Waiting for Riley Customer"); + untilText("courierState", "Collect the order for Riley Customer"); + kitchen.post("/api/merchant/orders/" + id + "/ready", null); + assertEqual(200, kitchen.status, "the kitchen marking it ready"); click("start"); - untilText("courierState", "On the trip"); - shot("courier-on-trip"); + untilText("courierState", "Deliver to Riley Customer"); + shot("courier-on-the-way"); assertFalse(findByName("cancel") instanceof Button, - "a delivery under way cannot be cancelled by the courier"); + "an order on its way cannot be dropped by the courier"); click("complete"); untilText("courierState", "Looking for deliveries"); - assertEqual("COMPLETED", Actor.text(customer.get("/api/deliveries/" + id), "state")); + assertEqual("DELIVERED", Actor.text(customer.get("/api/orders/" + id), "state")); // What the courier earned shows up on their own screen. - until(() -> !"0".equals(text("trips")) && text("trips").length() > 0, 20000, - "the trip to be counted"); + until(() -> !"0".equals(text("deliveries")) && text("deliveries").length() > 0, 20000, + "the delivery to be counted"); click("goOffline"); untilText("courierState", "You are offline"); - // And on the screen that says what onboarding has paid. + // And on the screen that says what delivering has paid. click("menu-open"); click("menu-earnings"); waitForForm("Earnings"); diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java index eb6a7b404b6..91d33d36d25 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/CourierOnboardingTest.java @@ -62,7 +62,6 @@ protected boolean run() throws Exception { untilText("step", "Step 1 of 5"); type("legalName", "Dana Morgan Courier"); date("dateOfBirth", 1990, 5, 17); - click("gender-female"); click("next"); untilText("step", "Step 2 of 5"); type("licenceNumber", "D7781204"); diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java index d264e5ffe3e..47d4ca1b3ca 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/DeliveryFlowTest.java @@ -20,52 +20,66 @@ import java.util.Map; -/// A whole delivery, from the customer's side of the app: the courier is the other -/// actor, and everything the customer sees change is the server telling the app. +/// A whole order, from the customer's side of the app: the kitchen and the +/// courier are the other actors, and everything the customer sees change is +/// the server telling the app. public class DeliveryFlowTest extends E2e { @Override protected boolean run() throws Exception { - // A car near the customer, online. + // A courier by the kitchen, online, and the kitchen's own account. Actor courier = Actor.signIn(COURIER); - courier.online(true, HERE.getLatitude() + 0.004, HERE.getLongitude() + 0.003); + courier.online(true, KITCHEN_AT.getLatitude() + 0.002, KITCHEN_AT.getLongitude() + 0.001); assertEqual(200, courier.status, "the courier going online"); + Actor kitchen = Actor.signIn(MERCHANT); + Actor customer = Actor.signIn(CUSTOMER); signIn(CUSTOMER, "Customer"); - quote(); - assertTrue(text("price-standard").startsWith("$"), "each kind of delivery has its price"); - shot("delivery-quote"); - click("request"); - untilText("deliveryState", "Finding a courier"); + shot("customer-home"); + cart(); + shot("checkout"); + click("place"); + waitForForm("track"); + untilText("orderState", "Waiting for the restaurant"); + final String id = placed(customer); + assertTrue(id != null, "the order is on its way"); + shot("order-placed"); - // The offer reaches the courier, who takes it. - String id = offered(courier); + // The kitchen takes it; the courier is asked once it is cooking. + kitchen.post("/api/merchant/orders/" + id + "/accept", "{\"prepMinutes\":10}"); + assertEqual(200, kitchen.status, "the kitchen accepting the order"); + untilText("orderState", "Being prepared"); + assertEqual(id, offered(courier), "the courier is offered this order"); courier.post("/api/courier/offers/" + id + "/accept", null); assertEqual(200, courier.status, "accepting the offer"); - untilText("deliveryState", "Courier on the way"); - courier.online(true, HERE.getLatitude() + 0.002, HERE.getLongitude() + 0.001); - shot("delivery-courier-on-the-way"); - courier.post("/api/courier/deliveries/" + id + "/arrived", null); - untilText("deliveryState", "Courier at pickup"); + assertEqual(200, courier.status, "reaching the restaurant"); + + // Not before the kitchen says it is ready. + courier.post("/api/courier/deliveries/" + id + "/start", null); + assertEqual(409, courier.status, "collecting food the kitchen has not finished"); + kitchen.post("/api/merchant/orders/" + id + "/ready", null); + assertEqual(200, kitchen.status, "the kitchen marking it ready"); + untilText("orderState", "Ready, waiting for the courier"); courier.post("/api/courier/deliveries/" + id + "/start", null); - untilText("deliveryState", "On the trip"); + assertEqual(200, courier.status, "collecting the order"); + untilText("orderState", "On its way"); + courier.online(true, HERE.getLatitude() + 0.001, HERE.getLongitude()); + shot("order-on-its-way"); + courier.post("/api/courier/deliveries/" + id + "/complete", null); - assertEqual(200, courier.status, "completing the delivery"); - untilText("deliveryState", "You have arrived"); - assertTrue(text("fare").startsWith("$"), "the receipt shows what was charged"); - shot("delivery-receipt"); + assertEqual(200, courier.status, "delivering the order"); + untilText("orderState", "Delivered"); + shot("order-delivered"); - // The stars are sent as they are tapped, and the sheet stays for the - // rest of what a customer may want to say about the delivery. + // Rated once: the server holds the order as the customer left it. click("star-5"); - click("done"); - until(() -> findByName("whereTo") != null, 20000, "the customer's screen to reset"); - - // Rated once: the server holds the delivery as the customer left it. - Actor customer = Actor.signIn(CUSTOMER); - Map delivery = customer.get("/api/deliveries/" + id); - assertEqual("COMPLETED", Actor.text(delivery, "state")); - assertEqual(5, ((Number) delivery.get("rating")).intValue()); + until(() -> findByName("stars") == null, 20000, "the rating to be taken"); + Map order = customer.get("/api/orders/" + id); + assertEqual("DELIVERED", Actor.text(order, "state")); + assertEqual(5, ((Number) order.get("stars")).intValue()); + Map receipt = customer.get("/api/payments/receipts/" + id); + // The demo customer pays in cash; PaymentsTest follows a card's hold. + assertEqual("cash", Actor.text(receipt, "status"), "the cash paid on delivery"); courier.online(false, 0, 0); return true; } diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java index 4048defb992..8ad789ca8a7 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/E2e.java @@ -53,7 +53,9 @@ import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import java.util.Calendar; +import java.util.List; import java.util.Map; /// What the end-to-end tests share. Each of them drives the real app, through @@ -69,6 +71,7 @@ public abstract class E2e extends AbstractTest { public static final String CUSTOMER = "customer@savor.example"; public static final String COURIER = "courier@savor.example"; public static final String ADMIN = "admin@savor.example"; + public static final String MERCHANT = "merchant@savor.example"; /// Where the device stands: the middle of the bundled tiles. public static final LatLng HERE = new LatLng(37.8080, -122.4120); @@ -310,15 +313,57 @@ protected void date(final String name, final int year, final int month, final in }); } - /// Books the delivery to the Ferry Building as far as the sheet that prices - /// it, which is where a test chooses what to do next. - protected void quote() { - click("whereTo"); - waitForForm("PlaceSearch"); - type("query", "Ferry"); - click("place-0"); - waitForForm("Customer"); - until(() -> text("fare").startsWith("$"), 20000, "the fare"); + /// The restaurant the demo merchant runs, and where its kitchen is. + protected static final String KITCHEN = "r-ember"; + protected static final LatLng KITCHEN_AT = new LatLng(37.7952, -122.3934); + + /// The first dish of the demo kitchen's menu that is on sale and has + /// nothing to choose, read from the server: dish ids are made at seeding. + protected static String dish(Actor customer) { + Map restaurant = customer.get("/api/restaurants/" + KITCHEN + "?lat=" + + HERE.getLatitude() + "&lng=" + HERE.getLongitude()); + Object menu = restaurant.get("menu"); + for (Object section : menu instanceof List ? (List) menu : new ArrayList()) { + Object dishes = section instanceof Map ? ((Map) section).get("dishes") : null; + for (Object dish : dishes instanceof List ? (List) dishes : new ArrayList()) { + if (!(dish instanceof Map)) { + continue; + } + Map found = (Map) dish; + Object options = found.get("options"); + // The parser reads a JSON boolean as the string "true". + if ("true".equals(String.valueOf(found.get("available"))) + && (!(options instanceof List) || ((List) options).isEmpty())) { + return String.valueOf(found.get("id")); + } + } + } + throw new IllegalStateException("the demo kitchen has no plain dish on sale"); + } + + /// Puts a dish from the demo kitchen in the cart and opens the checkout, + /// as far as the server's price for it, which is where a test chooses + /// what to do next. + protected void cart() { + String dish = dish(Actor.signIn(CUSTOMER)); + reveal("restaurant-" + KITCHEN); + click("restaurant-" + KITCHEN); + waitForForm("restaurant"); + reveal("dish-" + dish); + click("dish-" + dish); + waitForForm("dish"); + click("add-to-order"); + waitForForm("restaurant"); + click("restaurant-cart"); + waitForForm("checkout"); + until(() -> text("place").indexOf("$") >= 0, 20000, "the order to be priced"); + } + + /// The id of the order the customer has on its way. + protected static String placed(Actor customer) { + List active = customer.list("/api/orders/active"); + return active.isEmpty() || !(active.get(0) instanceof Map) ? null + : Actor.text((Map) active.get(0), "id"); } /// Waits for a delivery to be offered to `courier` and returns its id. diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/MerchantModeTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/MerchantModeTest.java new file mode 100644 index 00000000000..49139eb634d --- /dev/null +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/MerchantModeTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.codenameone.examples.savor; + +import java.util.Map; + +/// An order from the restaurant's side of the app: it arrives in the queue, the +/// kitchen accepts it with a preparation time and marks it ready. +public class MerchantModeTest extends E2e { + @Override + protected boolean run() throws Exception { + onEdt(() -> AppConfig.setMode("merchant")); + signIn(MERCHANT, "Restaurant"); + + Actor customer = Actor.signIn(CUSTOMER); + Map order = customer.post("/api/orders", "{\"restaurantId\":\"" + KITCHEN + + "\",\"lines\":[{\"dishId\":\"" + dish(customer) + "\",\"quantity\":2," + + "\"optionIds\":[]}],\"dropoffLat\":" + HERE.getLatitude() + ",\"dropoffLng\":" + + HERE.getLongitude() + ",\"dropoffAddress\":\"Pier 39\",\"note\":\"No onions\"," + + "\"tipCents\":0,\"paymentMethodId\":\"cash\"}"); + assertEqual(200, customer.status, "the customer's order"); + final String id = Actor.text(order, "id"); + + // It arrives without anyone reloading the queue: the live channel says so. + until(() -> has("queue-" + id), 20000, "the order to arrive in the queue"); + shot("merchant-queue"); + click("queue-" + id); + waitForForm("merchant-order"); + until(() -> has("accept"), 20000, "the order's page"); + shot("merchant-order"); + click("prep-20"); + click("accept"); + waitForForm("Restaurant"); + until(() -> "ACCEPTED".equals(Actor.text(customer.get("/api/orders/" + id), "state")), + 20000, "the order to be accepted"); + assertEqual(20, ((Number) customer.get("/api/orders/" + id).get("prepMinutes")).intValue(), + "the preparation time chosen"); + + click("queue-" + id); + waitForForm("merchant-order"); + until(() -> has("ready"), 20000, "the button that marks it ready"); + click("ready"); + waitForForm("Restaurant"); + until(() -> "READY".equals(Actor.text(customer.get("/api/orders/" + id), "state")), + 20000, "the order to be ready"); + + // No courier is online in this test, so an admin cancels it rather + // than leave it searching for the next test to find. + Actor.signIn(ADMIN).post("/api/admin/orders/" + id + "/cancel", "{\"reason\":\"Test over\"}"); + return true; + } +} diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java index e78cf8345c6..8afdd6b061b 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/PaymentsTest.java @@ -20,13 +20,13 @@ import java.util.Map; -/// Paying: a card that is refused, a card that is added, a delivery charged to it, -/// a tip on top, and the receipt that says all of it. +/// Paying: a card that is refused, a card that is added, an order held on it +/// and charged when it arrives, a tip on top, and the receipt that says all of it. public class PaymentsTest extends E2e { @Override protected boolean run() throws Exception { Actor courier = Actor.signIn(COURIER); - courier.online(true, HERE.getLatitude() + 0.004, HERE.getLongitude() + 0.003); + courier.online(true, KITCHEN_AT.getLatitude() + 0.002, KITCHEN_AT.getLongitude() + 0.001); signIn(CUSTOMER, "Customer"); click("menu-open"); @@ -64,43 +64,55 @@ protected boolean run() throws Exception { back(); waitForForm("Customer"); - quote(); + cart(); until(() -> text("payWith").indexOf("4242") >= 0, 20000, - "the delivery to be paid by the new card (it says \"" + text("payWith") + "\")"); - click("request"); - untilText("deliveryState", "Finding a courier"); - final String id = offered(courier); + "the order to be paid by the new card (it says \"" + text("payWith") + "\")"); + click("place"); + waitForForm("track"); + final String id = placed(customer); + assertTrue(id != null, "the order is on its way"); + // Held, not taken, until the food arrives. + assertEqual("authorized", Actor.text(customer.get("/api/payments/receipts/" + id), + "status"), "the card held at placing"); + Actor kitchen = Actor.signIn(MERCHANT); + kitchen.post("/api/merchant/orders/" + id + "/accept", "{\"prepMinutes\":10}"); + assertEqual(id, offered(courier), "the courier is offered this order"); courier.post("/api/courier/offers/" + id + "/accept", null); courier.post("/api/courier/deliveries/" + id + "/arrived", null); + kitchen.post("/api/merchant/orders/" + id + "/ready", null); courier.post("/api/courier/deliveries/" + id + "/start", null); courier.post("/api/courier/deliveries/" + id + "/complete", null); - assertEqual(200, courier.status, "completing the delivery"); - untilText("deliveryState", "You have arrived"); - until(() -> text("paidWith").indexOf("4242") >= 0, 20000, "what the delivery was paid with"); + assertEqual(200, courier.status, "delivering the order"); + untilText("orderState", "Delivered"); Map receipt = customer.get("/api/payments/receipts/" + id); assertEqual("paid", Actor.text(receipt, "status"), "the charge"); assertEqual(0, ((Number) receipt.get("tip")).intValue(), "no tip before one is given"); - click("star-5"); - click("tip-15"); - click("done"); - until(() -> findByName("whereTo") != null, 20000, "the customer's screen to reset"); + // A tip afterwards, from the receipt, charged on its own. + click("receipt"); + waitForForm("Receipt"); + type("tipCustom", "3.00"); + click("sendTip"); until(() -> ((Number) customer.get("/api/payments/receipts/" + id).get("tip")).intValue() > 0, 20000, "the tip to be charged"); + // The app answers with the receipt again, tipped. Going back before it + // arrives would leave that receipt to open over the order afterwards. + until(() -> !has("sendTip") && findByName("tipPaid") != null, 20000, + "the tipped receipt"); + back(); + waitForForm("track"); + back(); + waitForForm("Customer"); - // The delivery is first in the history, and opens as its receipt. - click("menu-open"); - click("menu-history"); - waitForForm("Deliveries"); - click("delivery-0"); + // The order is first in the history, and opens as its receipt. + click("tab-orders"); + click("order-" + id); waitForForm("Receipt"); untilText("paymentStatus", "Paid"); assertTrue(text("tipPaid").startsWith("$"), "the receipt shows the tip"); assertFalse(has("sendTip"), "a delivery can be tipped once"); shot("receipt"); back(); - waitForForm("Deliveries"); - back(); waitForForm("Customer"); courier.online(false, 0, 0); return true; diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java index 9d9d8a30196..ebd864a728e 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SettingsTest.java @@ -18,11 +18,11 @@ */ package com.codenameone.examples.savor; -import com.codenameone.examples.savor.net.Account; +import com.codename1.ui.RadioButton; import java.util.Map; /// The settings: a language and a theme that change the app on the spot, and -/// a delivery preference that reaches the server and the next delivery asked for. +/// a default tip that reaches the server and the next checkout. public class SettingsTest extends E2e { @Override protected boolean run() throws Exception { @@ -57,39 +57,38 @@ protected boolean run() throws Exception { waitForForm("Settings"); click("theme-system"); - // A woman at the wheel: kept with the account, and sent with the delivery. - click("courierGender-women"); - until(() -> Account.lastSent() != null - && "women".equals(Account.lastSent().courierGender()), 20000, - "the preference to be sent"); + // A tip chosen here is the one the next checkout starts from, and is + // kept with the account. + click("tip-15"); + until(() -> { + Object kept = customer.get("/api/account/preferences").get("defaultTipPercent"); + return kept instanceof Number && ((Number) kept).intValue() == 15; + }, 20000, "the tip to be kept with the account"); until(() -> { Map kept = customer.get("/api/account/preferences"); - return "women".equals(Actor.text(kept, "courierGender")) - && "system".equals(Actor.text(kept, "theme")) + return "system".equals(Actor.text(kept, "theme")) && "en".equals(Actor.text(kept, "language")); }, 20000, "the account to hold what was chosen"); back(); waitForForm("Customer"); - quote(); - click("request"); - untilText("deliveryState", "Finding a courier"); - Map delivery = customer.get("/api/deliveries/active"); - assertEqual("women", Actor.text(delivery, "courierGender"), "the delivery asks for a woman"); - // With no woman onboarding nearby the server may give the delivery up before - // the customer does; either way the screen ends where it began. - until(() -> { - clickIfThere("dismiss"); - clickIfThere("cancel"); - return findByName("whereTo") != null; - }, 30000, "the delivery to be cancelled"); + cart(); + assertTrue(findByName("tip-15") instanceof RadioButton + && ((RadioButton) findByName("tip-15")).isSelected(), + "the checkout starts from the chosen tip"); + back(); + waitForForm("restaurant"); + back(); + waitForForm("Customer"); // Back as it was, for whoever uses the account next. click("menu-open"); click("menu-settings"); waitForForm("Settings"); - click("courierGender-any"); - until(() -> "any".equals(Actor.text(customer.get("/api/account/preferences"), - "courierGender")), 20000, "the preference to be put back"); + click("tip-0"); + until(() -> { + Object kept = customer.get("/api/account/preferences").get("defaultTipPercent"); + return kept instanceof Number && ((Number) kept).intValue() == 0; + }, 20000, "the tip to be put back"); click("signOut"); waitForForm("Welcome"); return true; diff --git a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java index 7ea88f785de..d891e3b7de0 100644 --- a/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java +++ b/scripts/savor/common/src/test/java/com/codenameone/examples/savor/SignInTest.java @@ -70,8 +70,8 @@ protected boolean run() throws Exception { // The menu offers no mode to switch to -- only the way to apply for // one -- and leads to the settings. - assertTrue(has("menu-drive"), "a customer is shown how to become a courier"); - assertFalse(findByName("switch-courier") instanceof Button, "a customer is offered onboarding"); + assertTrue(has("menu-deliver"), "a customer is shown how to become a courier"); + assertFalse(findByName("switch-courier") instanceof Button, "a customer is offered courier mode"); assertFalse(findByName("switch-admin") instanceof Button, "a customer is offered admin"); click("menu-open"); click("menu-settings"); diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/AcceptDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/AcceptDto.java index 5305610eca8..e505a5b7993 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/AcceptDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/AcceptDto.java @@ -18,7 +18,10 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// A restaurant taking an order, and how long it will take to make. +@Mapped public record AcceptDto( int prepMinutes) { } diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartDto.java index 16c491ccbb9..9ceb3418179 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartDto.java @@ -18,6 +18,8 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// What the customer is about to order: from one restaurant, to one place. @@ -26,6 +28,7 @@ /// @param note For the courier: a gate code, which door. /// @param tipCents For the courier, charged with the order. /// @param paymentMethodId Empty for the account's default. +@Mapped public record CartDto( String restaurantId, List lines, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartLineDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartLineDto.java index 7a704ca3ecb..c0874d15362 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartLineDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/CartLineDto.java @@ -18,9 +18,12 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// A dish in the cart, with the extras chosen for it. +@Mapped public record CartLineDto( String dishId, int quantity, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishDto.java index cea30b27331..e672ab8d0a2 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishDto.java @@ -18,11 +18,14 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// Something on a menu. /// /// @param available False while sold out: listed, and not orderable. +@Mapped public record DishDto( String id, String sectionId, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishOptionDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishOptionDto.java index f008b432786..09db83c2a64 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishOptionDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/DishOptionDto.java @@ -18,7 +18,10 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// An extra a dish can be ordered with. +@Mapped public record DishOptionDto( String id, String name, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MenuSectionDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MenuSectionDto.java index b1e3a4578f7..ec6ca1d5dda 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MenuSectionDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MenuSectionDto.java @@ -18,9 +18,12 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// A heading on a menu and the dishes under it. +@Mapped public record MenuSectionDto( String id, String name, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MerchantSummaryDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MerchantSummaryDto.java index 3e3759550b6..4d71bddbd9c 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MerchantSummaryDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/MerchantSummaryDto.java @@ -18,10 +18,13 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// How a restaurant's day is going. /// /// @param salesTodayCents The food, less the service's commission. /// @param waiting Orders not answered yet. +@Mapped public record MerchantSummaryDto( long ordersToday, long salesTodayCents, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderDto.java index 426133ad8f6..b72fefd5fbe 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderDto.java @@ -18,6 +18,8 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// An order, for its customer, its restaurant, its courier or an admin. @@ -25,6 +27,7 @@ /// @param etaMinutes Until it is at the door, as the server estimates it now; 0 once it has ended. /// @param reason Why it was rejected or cancelled. /// @param delivery Its courier's leg, or one whose state is NONE before the restaurant accepts. +@Mapped public record OrderDto( String id, OrderState state, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderLineDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderLineDto.java index 90f8f2e3d4a..ad980806f1a 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderLineDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/OrderLineDto.java @@ -18,9 +18,12 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// One line of an order, priced. /// /// @param options The extras chosen, as one line of text. +@Mapped public record OrderLineDto( String dishId, String name, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PhotoDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PhotoDto.java index 64a6bb62c79..eea6ef2a673 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PhotoDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PhotoDto.java @@ -18,7 +18,10 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// A picture, as base64 text. Sent without `id` to upload one. +@Mapped public record PhotoDto( String id, String contentType, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PricingDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PricingDto.java index 97dd4f4934f..c39327d4ddd 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PricingDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PricingDto.java @@ -18,6 +18,8 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// What an order costs on top of its food, and what the service keeps of it. /// /// @param baseCents The delivery fee's base. @@ -26,6 +28,7 @@ /// @param smallOrderCents Food worth less than this pays the difference ... /// @param smallOrderFeeCents ... up to this much. /// @param commissionPercent Kept of the food; the restaurant is paid the rest. +@Mapped public record PricingDto( String currency, long baseCents, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PromoDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PromoDto.java index 993a4b4d786..ba326b9eb21 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PromoDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/PromoDto.java @@ -18,7 +18,10 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// A promotion code, as the admin sees and sets it. +@Mapped public record PromoDto( String code, int percentOff, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/QuoteDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/QuoteDto.java index 20f7ebe27e4..25aacbab7ed 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/QuoteDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/QuoteDto.java @@ -18,11 +18,14 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// What a cart would cost, line by line and in total. /// /// @param promoMessage Why a promo code did not apply; empty when it did, or none was given. +@Mapped public record QuoteDto( String currency, List lines, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantDto.java index 16df2a95cc8..191ec3d93e6 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantDto.java @@ -18,6 +18,8 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + import java.util.List; /// A restaurant with its menu, as its page shows it. @@ -25,6 +27,7 @@ /// @param opensAt Minutes after local midnight. /// @param status `pending`, `approved`, `rejected` or `suspended`. /// @param reason Why an admin rejected or suspended it. +@Mapped public record RestaurantDto( String id, String name, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantEditDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantEditDto.java index 739d106e759..18ecf5c82ae 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantEditDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantEditDto.java @@ -18,7 +18,10 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// A restaurant's details as its applicant or its staff send them. +@Mapped public record RestaurantEditDto( String name, String cuisine, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantSummaryDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantSummaryDto.java index 522604ef352..487646c5d30 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantSummaryDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/RestaurantSummaryDto.java @@ -18,6 +18,8 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// A restaurant as a list shows it: enough to choose one. /// /// @param photoId Empty for none; fetched with `CatalogApi.photo`. @@ -25,6 +27,7 @@ /// @param priceLevel One to four. /// @param etaMinutes From ordering to the door, as the server estimates it now. /// @param open Taking orders now: within its hours and not paused. +@Mapped public record RestaurantSummaryDto( String id, String name, diff --git a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/SavedAddressDto.java b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/SavedAddressDto.java index d6318628b12..a3a1d0f09d9 100644 --- a/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/SavedAddressDto.java +++ b/scripts/savor/shared/src/main/java/com/codenameone/examples/savor/api/SavedAddressDto.java @@ -18,7 +18,10 @@ */ package com.codenameone.examples.savor.api; +import com.codename1.annotations.Mapped; + /// A place the customer keeps to be delivered to. +@Mapped public record SavedAddressDto( String id, String label, diff --git a/scripts/savor/web-e2e.mjs b/scripts/savor/web-e2e.mjs index 427e45f6166..3685cb603f4 100644 --- a/scripts/savor/web-e2e.mjs +++ b/scripts/savor/web-e2e.mjs @@ -63,7 +63,10 @@ const browser = await chromium.launch({ args: ['--disable-background-timer-throttling', '--disable-renderer-backgrounding', '--disable-backgrounding-occluded-windows', '--disable-features=IntensiveWakeUpThrottling'] }); -const page = await browser.newPage({ viewport: { width: 420, height: 860 } }); +// A browser reports where it is, and the restaurants listed are the ones that +// deliver there: this one is by the seeded restaurants, near Pier 39. +const page = await browser.newPage({ viewport: { width: 420, height: 860 }, + geolocation: { latitude: 37.8080, longitude: -122.4120 }, permissions: ['geolocation'] }); page.on('console', message => { const text = message.text(); if (text.startsWith('LF-SHIM')) { @@ -85,7 +88,9 @@ page.on('response', response => { } const line = `${response.status()} ${response.request().method()} ${at.pathname}`; requests.push(line); - note('NET ' + line); + // The query says where a list was asked for; it holds no secrets (a token + // travels in a header). + note('NET ' + line + (at.pathname.startsWith('/api/') ? at.search : '')); }); page.on('websocket', socket => { const seen = { url: socket.url(), frames: [] }; @@ -232,7 +237,15 @@ try { // 3. The home screen. await waitFor('the home screen', stepSeconds, async () => - (await screenText()).includes('Where to?')); + (await screenText()).includes('Deliver to')); + // The restaurants near the address, read from the server: the seeded ones. + const seeded = ['Ember & Crust', 'Harbor Burger Co.', 'Kumo Omakase', 'The Gilded Hen', 'Saffron Grill', 'Jade Bowl', 'Prime & Pepper', 'Luz Taqueria', 'Verde Table', 'Glaze Donut Atelier', 'Velvet Spoon', 'Morning Ritual']; + await waitFor('a restaurant near the address', stepSeconds, async () => { + const text = await screenText(); + return seeded.some(name => text.includes(name)); + }); + // The cards are laid out on the next frame; let the photos and words land. + await page.waitForTimeout(1500); await page.screenshot({ path: path.join(out, '3-home.png') }); say('signed in: the home screen is up'); @@ -296,7 +309,7 @@ try { // wide one. The figures are the same. const text = await screenText(); // A caption goes onto two lines where its tile is narrow. - return /Operations|Dashboard/.test(text) && /Fares\s+collected/.test(text); + return /Operations|Dashboard/.test(text) && /Orders\s+paid/.test(text); }); await waitFor('the console to ask the server for its figures', stepSeconds, async () => requests.some(r => /^200 GET \/api\/admin\//.test(r))); diff --git a/scripts/tests/GenerateInitializr.java b/scripts/tests/GenerateInitializr.java index bf08455aea2..f252eaffcea 100644 --- a/scripts/tests/GenerateInitializr.java +++ b/scripts/tests/GenerateInitializr.java @@ -64,16 +64,18 @@ public static void main(String[] args) throws Exception { model.writeProjectZip(out); } } - // The full-stack template, in both of its layouts and in a colour scheme of + // The full-stack templates, each in both of its layouts and in a colour scheme of // its own with an icon: an app, its server and the module they share. - for (boolean full : new boolean[] {false, true}) { - ProjectOptions options = ProjectOptions.defaults().forFullStack().withPlatformModules(full) - .withScheme(0x0f766e, false).withIcon(new byte[] {(byte)0x89, 'P', 'N', 'G'}); - GeneratorModel model=GeneratorModel.createForPluginVersion(ide,Template.WAYLINE,"LauncherProbe", - "com.example.probe",options,GeneratorModel.FULL_STACK_SINCE); - try(OutputStream out=Files.newOutputStream(Paths.get(args[0], - "WAYLINE-"+(full ? "FULL" : "APP_WITH_BACKEND")+"-"+ide.name()+".zip"))) { - model.writeProjectZip(out); + for (Template template : new Template[] {Template.WAYLINE, Template.SAVOR}) { + for (boolean full : new boolean[] {false, true}) { + ProjectOptions options = ProjectOptions.defaults().forFullStack().withPlatformModules(full) + .withScheme(0x0f766e, false).withIcon(new byte[] {(byte)0x89, 'P', 'N', 'G'}); + GeneratorModel model=GeneratorModel.createForPluginVersion(ide,template,"LauncherProbe", + "com.example.probe",options,GeneratorModel.FULL_STACK_SINCE); + try(OutputStream out=Files.newOutputStream(Paths.get(args[0], + template.name()+"-"+(full ? "FULL" : "APP_WITH_BACKEND")+"-"+ide.name()+".zip"))) { + model.writeProjectZip(out); + } } } } diff --git a/scripts/tests/generate-initializr-fixtures.py b/scripts/tests/generate-initializr-fixtures.py index 205a550cbb5..7a81892216e 100644 --- a/scripts/tests/generate-initializr-fixtures.py +++ b/scripts/tests/generate-initializr-fixtures.py @@ -81,10 +81,19 @@ def check_maven_layout_fixture(archive, z): return True -def check_wayline_fixture(archive, z): - """The full-stack template: WAYLINE--.zip. An app, its server and +# What each full-stack template's fixture must hold: its contract, and one screen. +FULL_STACK = { + 'WAYLINE': ('api/RiderApi.java', 'rider/RiderForm.java'), + 'SAVOR': ('api/OrderApi.java', 'customer/CustomerForm.java'), +} + + +def check_full_stack_fixture(archive, z, template): + """A full-stack template:

        Km-ho6Yk`NK1gM5z3u+8G~K_wG%?>y=J|j}c)Z^#YMkzKa#`bXV1Xjxc literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/savor-verify-phone.png b/docs/developer-guide/img/savor-verify-phone.png new file mode 100644 index 0000000000000000000000000000000000000000..97b09d70c4fd38418d8e9c3e17498a224a35d4db GIT binary patch literal 78514 zcmeFZRa6{NyDb`m2Z!M95ZobHaCdiihv4pM+?~eV-MxcLaCdhNF1PdVy&vuv=lQ&x z{m`Ro)mr7XKACfV4N#PqK>C9B<y84U*m-ZJ9&bqBnFa8Z^J z{!~3la13sUnrTXz%gKHE2408z1QBla3F_Yv@DC6C1Miv(@d*;Vg!s2D_w)bsLKNmg z{?9sO^uK{2E@+*fKK=M4B`T!q0db}WZTNNQ!H<+wG9Wk5P|C4o=i)-~sijfzTarO? zig4Z+yLe@6%RC0Pa!F`+TJ^Q{bwYI0k4-M4fsK7eaX9SfVkGyL4wKFC$yv``3sL}; z*bg*CsIL%WTb$%^2b_yF^K_ph0?2aVXgHPfkCE{%)oGZ+?;F!9h{n3Hi#S{X&62&viMR97$)h*gIQp zNT8C#8%dyyG@Sy9e1@DIOx3&mkU(v9JI~1C^PFL(*J?0N4)~-O_D(KB?7-zp1wv37 z#6uFlm2VZ zw9~m+CmfAX1a^Lsry=+vZ&n?8WbO%;_d^eJxDv#4ISjhI)ViNd!DiHrQz?;uYw7#5(Ls_6H3A*8`an)YlNd$FE6=FkPQy{I zk$NzZIkcR1zUn^wLhM8H{{B8wp;5z~BNvlNA-OvkoqE37A`={jWQ_;+Rp~g0^tlU07qvL$1Feb?ZMe_y=1A`Y9YON<15fB|P34;$l2xj7 zGK-(u^Y$S52HA?i`~FNAv+%#{?2gC}%ECuf>AV;Avu68sZS#o?284r&RNt2enZD1^ zd<%ZZGnDDDRamyA^EJ?Q6F;Tz3BOWvO;%&O5liV&jkXD7L&~Hz{kU?*11m z95G!MS!=iTgtojGr>?1vOGJ}|`iH&>OC5_2ALeb9Ox;h`f(|W|q$a}_kB3=Y_XN}p z$!TsYe@`uL$aA4+pIGFa0nzvz39nDLV;AckW`j|)*ASyqdn!X{%Z< zlw+Rao*cyn{IK2Vguc(5O(6H?q*clf*&9!vEtOtlBl!Lkr~NfiIJSh(^QIT_B*}KY zUF`l+kVLIQoyBfPxX$H}5ztOR`E;|NNWWM_v;uK|wj6Bn5ZPmZ*Y=ZEy4iM_41&ZI z;vE8=h=|Jj;erFkb|HsOgYn^H!l4|>{d~m?tM2N`u?B%2z;e1vmLcdbFp@-@ICNu~ z$?b8K+h))!wA6MqjSz>^FroXS;c%EVl-291`$=?(9u<+ksTw!8JJKQdk2zlKk^ZG-JbeHSvMqPRwi6-elaR@%5q9Imn zW9zL>1zAD9jO+^bv=ME+*Deba&c-Xq5=pf|(Cd+$lhi-Oje6E!#Nl6uajGTQ5L37lXtvI+dC%JoUH| z;kL{5-hU@4sKWnTROvL2w%s05U>~T1r#Yogvn^42jql4nE{fu$p!7Kb+m3{IxqSBb zgY~Ng@h)HxoFSRj4Jj1Rw>mYMN2Yc@PXrbr(xEaXkXaKFw%f`Kan$th>7wAmTZ6@v zmVxizCbbSXLIeaLCKi@asKnQLJPs>sIUwKf{vZN6h0Rp``Y&G*8}$A5)euDcPTq$L z)Z_DiSL%|IE)e^_8UcrlO*rglK)N~Xp3btkj)S$M$u*Xk4C-s6nM^&HSVV!Fn3eTr zV_L2!3k^STsw5PNR z{%kW_({b5GeUNcG7Hcy2>6A=;NIsi^8FEr*K|%1tSSlNN1ntnsuT5U>mr@8&-0EoL z;y(6{9=S59nhze^HikG5h!<^#0tg{}`?&X+yKa}h9~RjHt}A%gPlxJFC-Y7W{i6oC zxwwZ)1>#zURP?Csr=c5NRWn6nX>Fhgc{(@7P*jTZwEb#njQeoY60u#(U&U2q9m)X1!1dTv1DKL35{vbi@ zJ-&5yJ(QzKB3i6K(D3I^=YrtJE5@m3m!mhTBEIq$^p6nL`EETO6Bn&+cl?>6l zVfvop!}&V-+j9>+*k{H^V1ARFEuQFgGUu8y@e0ZmfM@C`Ko6}y|4KkW?|M2H6oLk! zHlfxUVv(fXWZh4sE>W&lWlcfroSW2MET2iXr@D?&^p$?i85LcoDx(R}{ro-ZEfZZA zYg3x-7r#P_#9UVE|+CNAo4 z*&w24vzSa+-0&FS-cK6pJYH*Sh;QaJe(!!-_`%k4cF!$1D?Gsss{{3Xe~NN^(l}S8 z13M$FM}Ewp*Qp8gu1>8Cqv}nkk(y1n=)(N*+)eG8|M zFrXdA68UWF)>Eo2n!IL5+JjBAF+_JMd#~wyF_wqnr+dG;Sj;4z$oYoD4`V4>1^&0j zWPSdmH%qM;Q*qGc0roW?4tMsa$lq4}%1iB0P6xywSOmbxU6Pn?w&hW2J! zf|yNLAm?yNxqc$z*=C{KPAS2)7)9Xm&zI38T+K#T{g@~Dvhdx7PB9tsW6)Y}>h=-W zP!zi6vh#kWg1{Sl@wwumuy(mo=-#B=#eAjK>htcDpvl1mpQLM&uJBEWw#&h!E0&Ft za6RP-Yh2-r3EvOVakiu_i^(i2wNlNIJHSknN;#0#?Ayt>Nnl>>LX`or3S-fUJ&HS* zuP>5vsdxUKTv<|r;7hyXwlLinS0yi(luMw3e+m8PZ=~z;_PrwOjetM=GPXsh?SOd)e+` z(yN?yTb3C74wm=`@bK}Hl9ofHq+Zel6|h9D1{+?dRcTHo*_EUR4kaXz+1L6I53yysl?D#sYqckG@YPa&DGbuni6DrD&@>za|g0)H9M}B{Dd#hE0Rf- zT7)h7aVRp+Subb4Gwghmm$pzWKK$Wg^YP9kbi#Y zw>c+Qx|AXv|GcJNTeHHsoP&>y%Q5JjH=j&eE+u_?eTAM$U{PF6U&x%d=+r6uA{9haZ(O=6Pw}Z4mq|vHI!5ebZNp5k?&c zHQ3Oz%ppfJhCr*{loJ$~wD*D|qn z(K&RPMNT0wk=K65e(?8X9>NKp8QbXvGxq6u~R#4HOTNSc=q;Q1k zPS4w@77P&ta74<{H{a6VfAbsLV7U2Q2U^_93i2Y$H;vgZDE`xBcSplDjA@{5DN3Jw z|5rRbYXSZ@5n)U?jpqi7NxTk}bG4E9RvMSQyMfQyb6@!dj!h_%3R4n~eB#DC9dLW) z42e>)^?d%uOL|E2IR7DM&#pVn?zaCR6B%^WszfPQc)EDGG&$gY!jb+>5u}mNh)+A( zrE4sSUqZDh_5(fvB?if$yxDFA?;eBTGjV(a?>lWP6Z%gYlId-kp+Edj zxBKqrxcZudDIH_xw1RRux|nTFAlBwAX9j zYtVMbAJB+hS|R_Id8J^(cq}9T`1a zDzRi(7}ahC7mGpLwzIUJMSY*pzPR{SncrO_^jonR11jT&WQm zw7%WVEmIFps^RYVmeGKf&v}hrNAuS}y8~ubo+!v){3^vC?DngJt|>zB#iG~fkFt=M zo5(dwl{!6WG1y9qr~4^uZ4RZU`IIDr7z-oW15|+~hPt5EUo~wl`vNbr5h90>52Bgl zGrwghChY7wEY>*fR-g{EWy}Yx|K%&+xtg#Tb)j}0ana<=aYt6V6rOoq!71NJ_)v}A zDrYUOX#&_0oA^&#a#Nbdsb9g|7Lx5WA)oW&1alFR*1|7%;PFJSsd_q2AqqN3)UOWf zy|UH0n~kUCgdl$*+=e(RQ@C!5&vF5~^>LG>E!q!yU7FDT0a8+N1DAsm4P(yIZ(P*| zu$i;MslTIeoD?SJ$`Yfs8;pX(dauJqsc|Nu-XW}H*(1UD2Hqp)Q9es__($d@7N7 zbw{BRR*NZdmb})m^Y+iZcT166zn75PEXGGDYW<^VwzD2u^3Syos)T;P&s6nsXh|2X zfbGkS_{7i0DhZ@I%5TSMQK76I3Gl1NQ!@ ztgT;p46IW#V~BkQ-(7S$4GL-`R%_QTdHffv^<-#hvxCMsxpq?+G&rePTW{M!{0={3 zBB~(|Y$|G7!Oc^N;I~nKSNj=IvMDBM0l7&)bBj&S4EzZS1O9@9K}>40S(Hj=HADSz zFO+)r(_s=T3v=81ex#JCCn32)J)JG**)Vt?Mev=p;XxEO-F7($?VDAs&lBbfGSkp9I2T6fGDtFTT9euzRr^uMU46k*;J<3--EOma ztICuU+kI&!9}svWQY5m8OI08q9hF|vm`c_jA`6>WIQqP8uNSR1RsC@>nO*3#8@43r zymDmDeKzmE^bU`K4YGrH1-$p>AO;gVyVb^f+ZreHh+S_j)VMGEV`D+k zA2^Fn4sOsZ@z3Nzzn^ZgO;;MNYU}wG z85mhIdm+KKOfk*VN+d~1DP_gx>=D_unFm3sghOL*YYZ3Lczhnr6bp-}PA1iNzGXd5 zyCuE=Yise8RzzMBN(-HO2q92F80r}i&Ricn{Y?jm->sO%I1 z7Xa5wE zLCgXn%q9Oz`$KboV+d5)FPQs@u9Zkg5h0JduK?qi^Gyu3>fmc zYgKb4m0b%M_30eCty)5{$p0$bA^$z?TDw z={N^vH4{nu$?r@^9wl9{S=QCbWQ#S~>;D&8Qv_28#PXsAJTizSRWB*V6K zKmG=!Z`tF7kEe#|6_}x@OQtd-z|aX7$YF(R9o?c;DOtE777NH?F-YOJ&Fbk_O8s`HewPbN#Xo^8eUPFxYODSq+^zhx zIDWR`^d_avrV}iB=6Ihg1I_^B2eq3ugBI zf8F4;Vt$|`TH`;o;N(9)md%#3g7F{XPnqMhbe!_vE2~}1|30{4(9grDqI=Q*985JD z9Db?t`>c5UXY>UD__?g}n3VA={f7fl`-cSozk6zdBGg)Xna2{^Cp1OdMB>>wZQ~!- zcCF1iXp*#0lR$ntiCCiKpT{#&-9AssBh>wpK*t|+_FqZ;@mixc4U*ND6Exx;7WF5l-)SZiU)a;#`S$IZQ+FVN3 zK6>9~npOu$WMLBuBpJvt$sNbwXEHb{_Und-QxCoPnTBGCAK>Q4)7kdpC5Vzg-Y)&? zy&vpjr@*MZTD#!QFQ2P-SbadsP?Xp7Au0*vOpSq{=i~N8{m*0N9Ge{PVEJJ9l}^uw zv;MW88mqo9>+WE@GWh2AaQwxx{mH}8^k#;$L=1te%KoF5IV;6#v)w;dXNc^+d&AAn z;O5`uD1-5Rv-$G*8!#?2veIP3e{VP#mCkB&Pms>(n^CD**T2iwa5x@6l2{U9fG2qU z@bK_B?!Qfj4FbcwBV{TjA<8D!|FX?^%(2w5Apduknb4XCQn?Iv@#L?ls5%2(NC?L} zzavdg7wkq>q6vAM#Zo&M+}glY8y5Nq+X#I!r41~)W*YHNS5UJ5t6zl*qMOC!+DUoi zD+LMa3zCd5sgoJ4Vs1bZt%^FBvZ9N`VoC`^!f)r3#~fa9J*A*g5pQ$GMZo-}aX6W+ z2h1yX=nD!>pp-GUQtas->-tzhVHD2se{VZm?NIP~uPI6Tro}ohgqp^w=*4#(5}zhh z+<(st;V@m9zBigKGn&kB+UYa9sncjl+9vEQ)8@QyoGey+o@(GP*2R-2?lVn)P)9U_ zK!m2*U>+UybWA;x!jXA*e47si1LNf*&$K}VWzQqD6R@dPmQN}Kv-BfNK zxL$7-c^1B1|Kt6Yg<+}IslZ8<21VWx;Y^g*?IHn85lQ(Cu~qyiygy%M%Kq+}l*Qu= z+S|wOJ_0j4oZoWSJKT-x3zLu->`k>_HuRYwUkRMZSeYwhkE%*Do3EJGWbgp2qf%F zu>j^iogFfUHrdl=fh|b;(*wt#5sV`ZPFn54+9XCj@u1)ak8$k9-{8~YyljF$^L^&Q z*hno&Nk&@j&Q?`=gAGbL7Ao~|5qMhni?ss*?DA-QF5E**W92+yPc{c#)?L`>%}g$b z_%oh*gv02P&{=*3pPsg@_-imL3vDabK~a@1QB>&jbh_`YL_K|9hi#I=#XfiUyNf8r zJ89pTbgjuI^|9m{c96cMQ@7JY37oT1fC=}Mj*IrOB4U>P{@Y4Z0D7_JicU%(&iO`{ zL{Lyr2(rlS0YhBC>9`~%vmZD=a&`z>+{}V~c_99DfPdYlkpD-$nUeT~UZa|i@5^ob zUeS3hj#5D#!sW5q_-33gX|W(08X8qiNgc^_L4+q5m8wU3>+;$!Pp?obS9LpXkTF~I zcdTY+TKr^;3BQAiM#PvL&&93)gu;N0fvj6?G70aC={aP29O^W%GKAUk>g!$wg#5Ce zc0Hoov??EI*y^H*d6KOXb#!#Bd{~*R$l(h7gcYa*1e-vc>@3^08x<6F~t zQ=Uw2!Dw@LG_BtD1(Ttawi1`kqMh&dW{gQkD4jLaB8%%PNE_@v>U;CFp}&M_ZHIo@ zWm|jYfA>JK`FMYu9KyIGWGZoeSbxxj9_X*S`XyXE9%O{3Zn@!uWvf>=+6!uWe|s>f z@O_C<*YpS(892PfqOo*6`ZK*!Ya~+1NAg`AkWf_V^Ms%oiN#P~Je6k8VLJ!cbDQZU z?0i^PzF2BC0p!PTJkXYX*JtW9+@DCD8jY<_=lXn3r`q~!gw5kBF70sVEwz8>D*_jcReF1+84^aW9uK; zjh;X4sjw`f5DVC0|QT_tbxw1-ONQ>TCaahm~FJ??*^y<<%J56WYJ1 zgLxH2jWo!PBR9t^p)2Eh$2BY_E#r0HsV;M(hILz}I?@eCW0t9osfo1DcQOfRl4$U> z?Ci1sIQ;vwbvl_8`gkmc41Dwm2&7W_GN6bYyPUl}j_n{LiCekfeY*&KsujNgL*(q?T`|X(fJD=?rMj6C2f;c1K}*tnnX7lEm*pxO zE-kpT;@!8=OlsAOj!OYH|5E!xssZ_*-OmOJ9?o{RXuRLBYP#7QWc2rFdREe5O2o@a`m>^4KtVMEGB+&uThAH&K_WUjkG8Yd zo;2~r`>!UGA$gbB4_Po>K8gUSBqpY`9hV;$TrNQ6{B_tDQt8XR*9P-3qxmWgLG`vSFMfwn-~7XmGkTUB z@}Ae3QS>ZKoTiAjlr*NExBE=Y1m2cu@y?}|8nNRcel%=eqwA*8lp4eR;rLlEu)8@M zW67aYuWlUO7AP8Kn&z~428V3w%q!A>oPBPLxi#_neDMHubk%#7mM6xpo0E!z^_`b( zP&T>`-FPgRVUDZqjvh&jV{)A3vJp3_q`tUKc+x@{Vo*t`G#7Bpb}BYWizNK+#(KMd zJwzN!*~gnRe+iJ-3Ue27R&3!OEk))Z!@AA3&}ZUwh^cV%X?2uJ{o4lD#=m=uQboEk#K}x} z;HCB?)AP5^Ta4adj-sdZ5A_RNI!=e&gJd!|Y+!zlY3&0$whceoFW5t#YbZwI!W&(5 zdQL;PzTrdrdT>bkUiLgxgR%CZH3q%%B--20g#)`o3LxD>>=XxTX>owunFwvtk5 zqI22r2&`cKQC7DgP?8LnJ)V~pYytbHumQ>rUVBC^{#Q6KGYR3)$!G-q1rPqFVS6B2 z&+5-sUz|G1n2Lgc)z9|}EdbJK%w_CMc(z-AHJi=7E%~}@XaTex+ob5K*J}oanHTA+>wiyT!0@DrDsdxj6tN1 zCj1^(pHx5F)P0hGmW_ zl9UyXB%n9itl?m#sK;)LrdHlaG*`kMjE(f%rQKbKTF* zv}jA*$)9JBECZePo87aea(4L#K2)@u8bbUY2Y0xrn~tb2$NvtCznRvMtJ-+6g7 zeub{nq6IUUi8q>}SpcBx&%VOcCAKMn!%nkq7t$%Hi<6n6$hr&9jvtd1#CPj=)mnAN zZJn{Ds3wE2rma1UC~kq++Gx8w)`umcKQ2DV&M0cS@2#U4*Se>~fKY5-LK4rNs4e~c ztoWQu+}?Hm!n=RVZWv5qp~zwnncoEH&3n?&%G-&ayJ-Cmo~1|YOpLQ{b|%w2Njrp zO*y$r9;cq>y#MpBqw4`U<_@1Wj2j`HCKt+=11Jmd8`vpGTL4?TiThvV!V*Fp|7;0) z%+U&>X(M=Jvl(3)H>!uMr0z*l1T|uskAB^A6}ZYSwo892kf<%*`}ac7v3IT2D!?L1 z6}dea^aO@XPMLUPF%v2}uI>bc0<^?1yK=bu{@SH{F`3rX4d(AF>(m-qOa`TEE>rw< zzaTca8m61S>e^x?S{4#;+#o#RhOGb+sc`L`_e$egF|4ymgJEz@z8jcRNn`jJl^u{S z0{I z313uhKWKT}J?I$y7n6I;k)wM!^;ugv#W!`_5f6@TY*wy$;Dq37^+3 zYkFV#ukd7x{CP$lm0oveo;hnA93KG3!^uwg@0#nS#a()5NH z3g3msa0)}kNEWQO!XvArvVh~q8@#f=M-7MBMO5&^<`A)ekS__^4ZHvtV?a~6PGQhN zx@W;ge+oU3f1f75_Ab2|jK-(i^nK`3S`?TrkjR%V_~tZMs>F&0j7aLG>57-ag<+}& z`xi8s`ohMwHkA_5`Mz*Ab41oot}Y&0+(b6{7^o5msbI0cXUlAZ@4tQNCPAS<-zb}D z7lF&0Y&wpazbL6X*Kb>El;H~a6ETFm(S>Ll_@!SOx@5|b|E91`S5-j11ztCcw z3tGkH$oAHqDyNjZPIozVB~rV?;qdw2zPleS5uG&5RHLlr8!X8i4({<$@UO(Vw4QsL zHkl^0GK~<+#?A6xVsRV`s@dMug}?_y&!DKetKz<0?LKG@S7}zSCD(4jLYpyXn$}ss zC-P=GiMTO!8y86@;8xH1lpVeM(xFqYiqwX8LO^;C=0=&5AIp~#oU4|b(mI}#n)juD zceRdil-WakyCMdV7~Fv<7=seG>!ryy>&@t}jX};H*?_5a*>cUgAVfk|&6C9nhZU*R z8xY049nqN(vF}Zp(p%@phWAX31F?xARbYXVK%MV{x*!)ZY(QGJfbSOwD zj98oLm}GeD(I2cO{7IaH&`SYUCSw*-zbtL?UYO^#0CMbCDqF2E773sj%xpT#DKl+k z_!KwbqjFpQepFIBuKnv;;R$dlESaibI*A7ujEpb4W{3}!4?=1=%?)#RpkrA^NG^kB z2=dO?qcZ4z4Un0$+mN~u=XMSr{AVo}naH^4vBKSId zwqenxJI+1L9cWcqBF~!#n7a9ez$iREF_r8)l0sF9RlJw6S5UOKU8}-4u{FWF zd{kdHCro#)uBF{;x*+vjz-#uBSgQ337nmTovBWP3!WM!8EL7>pJY1{?o^s$mPx3Cg z_N*D6mBj+u1~r|)I$jacm|OE$jB~3s-rs^m@qy7D;wuGE7godxE$!S>$Lx+s2%C-| zqAWIx7?%6XOFN-_3G(HR;w%3-|xvpY9}H zGa07Amo*h=qmJI?Hu`+8*S8#a33-T3C~y?YZM($#$5*0dZ3Jd|wjM0YNaKaLVMs$2 zx8Cw;+GOx^j%t;sphDx7<{XM4WKqrs8yJ-2-ZNvHpx66VreYt9?4*4E_hW1me3Lya z1sfIPm4c~uvaJu8gwrCOhsd>&jjk87$u*v^U3%q4xR|UXt1OH&?KYO5gO%yR?z-|J z>Pt!Dsmc!^=zPPzph5jQq7D1CadCb&evpSbIzOP51=BlH_7VHx73ep6S~C^td)2QS zBtwgRLemg<31+_0&(>K;!hdRMqtkw8FiT}De>pOnmQ$f& z1j$vnjj1@xD!Nor)BP=7=qzms%bv!(&<~y5t7t~b2(}iaPxFNO&E)%+={ivVS)P0G zEI-i53+G!TX0H4B3ute3$g%%E1?AmIKZd8J=IGbSbUNmo9C%25o93m6u% zS67R$YO?;X7~z6V7eyaAc4`S5cd~@G{}*!>@&F9!WBGES##ek3m%|Yo7Ir1!S>+Hi zTGhf*+6wL)4Pi~put%fPq=ltFk)cB-N?>=$fzRF67E9{e>Xc~oQ#y$rfK>TZH+}J; zd$YaIer}qH2C=?RCx#IdBNf_c%gMuc1NA;eavU?u??ac|EZ6baddiw$WxIrDkC$0 zwOwOeTo6^5Tb72myNNAjlvDYrCzsFDi#pV{@?>Ow4`wxE$qZO$WBZ9Z_Lf8bGwux$ zhvSsNRy^hxjV-5SD#QT?+LtfZKVITJ1eYzs#J+_gt&rk4OzKU#h9&HDp3auk$l%7w zSGLr)Wt)Iw)I4Lc5W;fX19>6aivt*QW;;gDN67K;&^MDMIQ`M2vHBSWGMqyr ztu=zlD}L}Trx9Ol_SR&o;kd99AK1V*ay_p{*uxI+3rX)5z)Q#dLajf}K8n-9u`t%? z@`~1U4MqdwCqU*iTw@tLADsF!Mqx>o%H-lbiBBRtkZEZ6he3tS>PM;SdR91qd{d-YK6gbPBv&3l`mK24*k8%KSh`UGvYj z7eX|-vrDmYNHE$k1GbTh!a&3h%n5bWF|cMfKO!i1x93zM6degr z?tMZyluMV(iz&h;5(WNW%XuS%xz#Hvdk!Syn4=be!Lb4ua=RED{U9N!de`yKscsm;iMx=ix5%L|gjm zmITRhP4YO)4trtfQmvYkMU=)6E zjFS}6=j7E)iX?stLXmxwqaj`m?4KM{kft8*!I?SOCJ*1HPY`$B8#kIWQ>*o3UMhPb za5-@Qn7n>Gz3j=;)3R(t{WTP>D5L=L>hT=JR7;v`c060^nbB0?=AwPGmiB{E&LW}o z$JUjh@Rb|i`#Ev`Ojl+`$&q7o{(S028mGHE7Oyj)0M*_? zS~^GN+tfMecQr4qZ`19GML(LPQp;rs7u~S}6*h}8LV~us@-1c8Vg?%znq>8LK`>&` zbP#u8LNok#GUigoB7EuIy~j4Aq=db5d8uO;>HW6#rP}wxyLs%D?3ESAA+*9d&QB{F zyx0&+&wG@j@440)(-&cGH@b1-qoQKxzBdEtCG`2wOkFxmhu zq%V2hd4gpml3j3xUM&t`f<|)&7nfVaB0j1=Q04taIqp-Ao6Y~-Dt z-o_;2K+0Xa0)kuC*KWe=rSzQeq3bCfeStCZBHSPIDm%u0pYRzxr75}eSbHhSzcmOr z80lo~GqX?egiwA*$XY@UGyN7Imw8Qzj!s1Gd-O*L<5vP{n5Kh32w%R9i!Q*Ve*h;v zmHO0tP%4wJ_7A@Jb+RjzQND*R6j!E~bLxT5k1XyQ(Zt;Wr@udgXkVmaxy8ReB&DTV zyfITG{i_0%1b3Dty-6+lt;vrkMkS^~X^I-$D ziV0DM``RPD-)+neJ%5X3)_6M*rE%62d7d*J%%1A~3#JyYZxc~L8mDu4!GNYiyS%+X z<5j1HDI&w)!y}oT4nn4v95;7~3K=xBZiGMqsWV*Oiw@%MRJmWs6P6QvN@4b6VD(=P zyY<{K`L?+h#j`)@29ZfM;b8n6T%k$YC#YEv8r*|qB2_fT*|V&1UjfNGkb5M{p7-aG zB8K-=*94z{_n%x+Zuy`gbP)3Fu=B}WHN({QBkY4{{}UCm(H>h==@yWiNt^WMm7E(! zBoX|bQ3!r6OCgEJIH*HKsMXZ}olcMKWC}tf7z%(})u$xJ?*>=XjhI4>jYlv)^Yoqv zUAs!9qFU!9#FaGSA1=!R`9>tMm+Q@9lexYGicATAxlTfzSRxorp=OWeK^X1YuvyG6 z;#ctW1c`5U`>5l$iREc5f{908c0r!A(r$4K%nUX%qfJW?Xr zrEi6 zfF98d@jsoMDU?qTiIdS@K1R;-(6S6;3SW1p-+KE2YAqqJ`?lmo?5^tk^3$6W`}cWs zZhc{(=fSb2GCDSe_}{-t9)%4#IXxs$UGOi?=NT3Jp~}bwDBjXh=o4PJ+wCqx^0g(c z2b6&~U&Yc89y$g#*rH3%}!+gF3+$t7QHT( z%S>xV649zytVZi$uSTf$76(#&w;V?0fy3OUtLY?LC-RaEVui+q4~idrxj$us+jp4! zZro^NVVtx#CE-WD&l(Whp>IU6%i zFq^-JH1p7U=1St?8|2K6V=n=#>7UiQxg1c?HxUJZ=WKp-p5h|1P|s}Z_JHTMl@DKX z(IQwZM{*7K2qk0;GGVOdvrC9n7e>c^(9pkdm=hbn#gKQ}3yS{(dbX6X+!A8IHo&Ez zXf&FuF<|PJFj=?2N5KA;RN8rq{YtJ@O)$aaymna?Ch`{BQrrq>^2o}|kVE@)<&3!f{-zdnUJGs_&8};q-<$}bt&ooQT(cCZXXT9(sS9c-r) zAOhJ(6bI!nh5E0ER9S-i;=NP`Jjw$iS&+s#Av&D@MwATgbcQvw?{pF`$~mt{$?@6H zy1_oFLol5wy;R(ppd&vBYUdtahwdJ z3@#gVk*KqNTDeB+`Oh(!xZ_rJe4p3llPWpw577QU3KUzsATvPESr9JJ@$EHz-wK*NkU;di`KcVb- zg`~Y^9XLyfv6?H)r^%FLRfCG@Mm&|!2q9ZFowp;Il!P_woSS&QR{K1CAtElpqxIkL zIXJra-@b|?0O-4BI{nZZl*R25FF2Uq<#S#U0IpQ068RiX0l6melYa3P_1Dsw1*C3~ za`W1*Hm}c;_Xp#Zl^1XsZVE8r)pS>oD8oxH!Yl8<%QWI8f|##8G3Ho(CBaX+lVl6y z|D)?IgW`y`aP0tt1%d{5m*6D0ySoMn8X&mK;O_434#C~s2@>4h-Sz97bM9Alf83g1 zUDOQC&@;XFTJQ5LT5d+m)_^3};H)74%98o~vqi>aG85{ojdKk0c$}{rCMNhn)3e6> zUN?mojFexeXD>y?yTX&)F92VMWwG7kCeFbA8>ajTHdrbeQ(DstgbC%rP} zyyB1}SlPv;srl?BD3;!m7bws8X+3!E(_d#)-ku|Z=hKaz?v|}_xY4U6BuFQ)Y@W_7 zICL-K*MamBZaSx!*(S>wz34S;;vydt6jl6BA23ITu3dK2n7Q=!+SRcma4&4)1iAYY z`*3Y|$v;b3Rf03^pP!4qIVBTlh&ebq4nvuBGDmhJpf!P+flrG%vOUm&2tVu!FD`tiE0%kI zc5{O7!cUg^Updo^nJqMHyKp+MMn;v;?{MW~l`gmXcNI^BFYl3=Xj!Dt(yALx0KAsP zWWu60TD1@IghsW?_iiC?bsQB`B$w%naD$}T>6Ag2%>QGryZxzN{Re8|xlBxqqR_L( zQ{7?2c)#o91w}oK|o7Ir~j3|Hh53wk=q^Q;CsunkQc+6Y& zkGuO>1wSKj(U53DEO+o1S{%b(US=j?ddm)$|9)Y&_(iT359}GAgi2K^DBCl^lmp5W zoP-nSPVFz`-Az~^x+A-I5<1xG7?A+V z?<`JxH3ngmclPjuo6K5ttiZ7j*4kM>EAw0Bhk1ot*Yr7*Dp-b`u))vY ztLxLb1X|S@GCv(qcSr!vWmx+4sav=*LorK{m)&sB0zR-b4sHF?Fy`W|mE_SfU^TRI z2Li(9s^@xA(y4ew0-r+M}4o0m17yJB#NE z2hLZQ=+6vF8o#zbtKat>pfk45vhT5+Is+b+!27Lv7M$&CO6|X4pXq;*7bFMWg#BBD z;X$&iy&HX*wincH0}2n&c!QW&j@a`p%2aX&)%D@yUBi!dQ-!PkQy8i0m&Tu`TxM&){z2A_x&c{`ZR;6%R13V*CVU(V7_w{1#LcZ`447UHiJ za!h~`2?WwM+Lvwc36VHSq$IaOLLVp4PLv#g_Icawp1|YI%;Xs5f;|5-qLL&RO7zV% z6O_b65|n*~@_kd%vDs z9ZL zlRB-m%};dci8Sz6cqmJ8&uE#eeZ~zsuHp5?B*bVw?3~TdaDG(I_EIR}4=8ZA#y-B) z#|7jgvel#N<=)>Nk`$+GXD)QA_Q!%!V`FU_$`}3n!b0%!)hl!nU`T>2{PJ%`Mt@_A z^`H4eV{AeD3+B#pm=5VR5WX6xgN7%hTh%hS>Z`L3f9M}jOX&!5Nb!(_oR$T` zeZrfw_8#1(-6kpd)#^`NG)WElpQbKa2#Vu#=JY~!ry+Bw&_NoNHe76xiS_O`DV~(@ zDclu~@U0L@2ZbRx*bidndEZ%>Lw-@6?dN=X#F2oQ5IL``s)Fk~S%z8;GeK@ak7l~> zRTDbQvq-6#IFWoKL`7w27C;0v5?{C*&M&Y%e9zWYG=Gj`()FXnbSvi`#r7@n(nELe z4=e&X?*SBU7D6B}1wCU2sf3c?PICqmcKXM0K`ery*B!c8ch88$dgDIgv*A0dz+N21!d}GwESHW?w7Is1EXW2 zHKLgT2v{F^cbd%V0g#_i>e4+Vb}c-GHN2(LcvOu!&5HAGlJB{EU#6+W>eUU0-KtF= z_vt<=a(eStzY#Cp#6fqHXY}J=I$K%OTu_TM<5kZpKQCD9;uu#E0fbjz2kUA$Xm)%#IEO=UDoffA9(JRMkz$dJq?cfyYI0nN&a zrT%x^A5L_vRXGA~+#+8ZI1%Z?@LeX`&DC0y-!c!|^1}Gs&PN+t!QCbrgji9d z5g~%4Rdw|Ayl1`lvsyonawflkgnhN7#P#C~ zBnWdCaV zc~|Kq4qW(`g=JS{P6NETtcUA$U@F%^;y8!-*R>he3fEdp%IIRSZbK6Dv#Aj>{IK!W zbY%A*&L5tS382E!500v^G^(ZXU^T$4#Ie=s80WkWo)=+oslNYaXc`3)%!Hfl1GEnM zw!i6gs!ayt99*!cnz0O+3vt6wIXs^76`d`+NO_?}lEb(OxNrsP_jcYw1gTK)80v&= zR)hf;6*)IKNGdj5u+eJO&1|Wu+BU$+`UcbDO$Q15Det%aw(W-#8S*DUQV}!B|GpAF zeBJJr;@Px+dxz)nb*;Gu2*7E!T2(AcW5TIY_kNx70M91<5E5E~r;|?gkj=E&n!;b< zR?U|{B@>LfT!3H3S_N{noW8*r82LbItvb~ipLU>e%@fZ=hG0;)fs1law>;>^7r1}* zg?Y=Pkh`%K$nNv{))wj7RAWP%z%hjS5Ysx~O@8`aKe{l(f(>I$iH3{&cNHEDel80z ze~_}6G7t_*NlwMD><@FxzF-iKhlI`caUlqrQeAQlF#&`R$MyKZa`qfFl<#7$PI zTp^lki>uhM@Aoh>Ma)hrgwO5kM8xAQQ0cfq`5HS2hr!)woy5M$M}&q(y13bd;*TN% zH2K6G!lAl&5&KSUwIr5Vjw|D)|{Z==;S87}vxIqqorPUPP60S=1m) zlYOG4H?I@J2LxR9@Mu_x;0+IFX#c+9jKO&*zU3BGbFzqJUe-T?w(H@GKc&@hL(=&t zw`}FVRW+sihlZ~iI+#tQum-76|5(E>9VEjw<-bhCj`7)5lC{T}RykX7&Ht59T1XI% za%%RDI%>dABWpC^Dr&MxBR0YN4}?Il>i=7+#{3-DhRU)>HRXvjH7=b*Lw`+DaJi$h zR7%}nC;HjKtYw ziB{E_J2eB?OQuMDf(S-qFp|cr#vo2i+J;f&=85a8?#HR?F zlZ_=Yz-YNB3 zrTj|j4<5qI7~+Nfrr@GlCp;4=1i>w%a5rzY+5 z10X|}57W#4dp(+e$MM~o=Kp~V1yFG_3ytwk>{Th5tg{U z;=ivK&GOI0f|Fj4`M(o(0si36$O3L!|Iar71!eN}bGxOcTIgI+0?ogZy7(d9H90wH zLfljV3IPqhwF3S}?QLrzzw`eCd0LS`noYw=D&nH#e|?FRe?G^Y!!?;|3?Pnyi}|3_ zVJBL-5#=A;DVQN+s45p~KXz02pVMWJ5D3?H$RLhkf(C&kzP~Jjjtaj>8bNpxBhj?H z#a)Q_z=?{A4%e5dM~Hlek|D+W9reHWg&Y}@93*CNb)VGLB>>+hmmFJkEXs49Ii4{e$LiklDDO^2G#s2fwDIr2s{?)E+b}CYlLiH6<&G`3JrT;y3 zN3e@hYT%I0H8i|7{xgY063LTJ0F>W<&58iv8}MP?s&bPiMk$cEy9^2GjAn}H|Gxw| zQM;DL>A7fP0JJSv@Pt;yNh(swfTJGiKfg8>3!-vWyH~lVc{356JQR${l_&YXf1M*~ z*Eb%DF^0x;Vw}>{8nCtUez1Aje=e*GRZvynIw~3(cT8>$vAd%{z0Q9w$b2PGQ{Xx? zh$bP@hwXotzy=qjGe~fHO69*yt^x?nw~M}!gd!jHDg3_|6>wpLB=`So-VODZ|L44< z3KYc4`R^t6fB(!zOD?>9m3M!_@EPCLQM%J3uN$2fM=3PzOlfrP17Hxb?l2v9=7TJW zB<`n!lMIe9S;4Gk47E*(B;xm(swW4Ols$gMXf4F4PquG7=~odyJcX_)jA=QKx0uhJ zRfN2dh`1@$QR|pF%+-dInGHiv?3-{9ISJLL&J4w)Qgx0O|C|BTz=M0Mb>U>HI;w;u zNC`u5%hv`vCwL-*+XPO?0J0$#8_*8Pu^3O#4I&dI-mih>v6WsIO|lY#gW;NEy*GTA zZ?m|dC-^>%jF3YR%QgC{R51`nJ8rJHULs#4$)vErQ-o?Ms!Pdn%=LHZ3~)p~8RVZV zdkGM_6^G51C{y*5exz#d?0TOLKw#Obo!kME7FP#`QBu3Dv;`U)!{m)xthA6NAfH*$ zXq&lbNt?gxp3n415iQ}+OBlxdD6qOLzc|=KdL1zWAXx(gD-GPz$NiBJ5x;_l7npMqLhjeU61#|#=m1b{V=Vlx z^UOJnOK3{P_cb^@hs#3@no!n zizbUNwJVv}M2$vv(N$Z$-~8e{m70QsqDTw8p`wAv)28H^A4(6nN0Gnxag#-e1-5x&Q|HY^0|)ioXg(^U(FYM2AUuWZz42W2reo`rPA}Z;{0jgo zNrBhSRgpAXZivwKRrwruLCWm@q{?`Nf%XjzpDzyPh|CbLXY0^CCWW1jP6h@U$0D@g zW0e0y%EOl8t_`$tilLVx%;@!gSojXzr1r%XvZ`>xSqs4FG9t_IL_Fjn-mRxt98OrV zpI>t7%A4{!xfZS#2RTM#*Zj~XrfyHZTB)kK?!PzNH^vJI>c+IJYscRnIggzu#M;`( zp`SW%8=UW$d8ts#zDEZM?M1AE3~p%=Z}z#NorkXS`lJZRB}H?UE0u_iN2%}?Ot9= zIrldwugH7I3l@&J*BSyI2Mwq5Mf#$RDip+AEKEWY*Q=dKPsQIR8@2n4x_$QGF(KKl z_z99`n-~f~ZczQ2kH_=wPDz}GKRqBMt=$<(kp76lfZzUy6K`Xe`-R8wz%u74Z6XZ*7^(CIG)70WhYb$;3B#|GBpeYwS!1L z(dAti%c?F%6WrpLbGFlynjGNc(F9kh$A4n=e0Lg$U;~%E^p%H4$hX#*V)t(P`2ada zgGgYl9eh6m?LHaHiEtGtX=+-WPr>SaR~6~uvIipH#*fy$-W|s?&oQJ|JXl967Lc?d zB_$neN*RfH|NPVrYv^{dA;{{?p|yq}yVst(R21&~m2WM<|wTVX&B?-eKcPlgd z0oD=557+Gn$fMitW9X^s!0tGj*o`H(f=WIk5h(YM=#MFLJ?%_p5nWTrOvCgn-#l>x zLCoD+9@aqc{V)*ppg_R4_-Jsp9{?h9@xV`?JQrBsl+X%yDYYiXL<2Vb7rWeO z9}#<|lEN<(>IS1DrJkAR<$InJ#=6XQ7OV9As=pKhvbH68;Qf3wX_VwwMuw~`vp+I( zuBnS>gvAQm{h~JHY!k0h>sFu6?}aKd`lD2(jQ0I_Q5lf?{M7xWCNW_J=#8!n&evi| z45Qs|rh=3{gnfh0*8G0S!U({oIM=;VHD=d8<0d7biId$S_+NJn^Y*rLv3~%3I?&r? zf%`Qi6!38vFk%{=sO=dlFZXsD^qJvMI_`|Fy0I;>-ex> z7u?-si*7WUIBvH$23YQX3mCLP@_LHpL7pz%3f5w`NSm#V>sU)4P60!A>FnbhUk8M#%d+%~AR#~-Qu#2a~{(O6Z2J{ef3RLWD zpb<(b{Nn_`W{_r$%+hyt$_|S=7y__=B;sAvz*1wy1$Y3fDeEtyqhL~46?2Fo-M#=(HMnCj4 zyX{J`>H?Am2nd9P`!xbeO@Uc-F3c(3KVHgsknfhh{-OpHly6xYH<4K!A^nxqm}3H( zK=3R4*&|)i1V~u-(NVf@N^~*!+OO6CeZ1a6b!_+#=&H0kroghmaxwH-7MDF1jY7(o zS_dM@tIOs=C7>G7nc-^-Xx{5FHVZmQ0UkJJFuld}Fw~w`tcP4bVDZ}Q5 z2BZGvVmlb?P8C{=QDZ0xWlLOj7$_wgpRFwZR4mh|Ce?kT+o-u80m^Ph4;PagvdKDx z9y_Sp#y=VPD(O2e!;&>j--*25NQ8qr44HNfum}mQFuO6nsNouTLTcA#r5SpUlfM9Q z{;S2JEF6Y5R10_@@nO9L>S10+*^uXZ(~wG}HNK!ztjWjcvr{P4BRN~DPx~B%K7b#yXJmieE(unu1+}e)r)w$&5<$5wYU2ZtK zTbSIQa^d5)KB)#WEo7kmZUy_g@5jd`6+d+ooVu9U!G8kpZvOWn0hF{}air1hDBhoU zor`FY6w|={+&km~D(>=2@WXj5-de)$mv|^Y8#Ok+?tg`_A+SgwB7s4^Aj8DTGCWG+ zcv_sM!A>#;WkoEL_QWk*c zHB_KvEgE)-$-a6GwO+G@`&wWHdaDJ6Fi!j(1%TqIbqT&=jLhTPbQ=;m1gqP$sl0K% zg2(GUJVy-<-Ou|7;`LdIuQ`a^Q-Pvhjxhyc}#cIZ}aXbT9CX&cYRL<=hq%CHH2e=U5miYcoc{9=0`(ogF(OAHs6%b_?% zE_hx{LK)z(5y|~%rdu4%&HVV~kA6@!Xfu^?xxc?63o_lOw5tNhcX|x%ur_cv(0%is zWr0)rgLR+-;EmKD`^QZxyn>Hz;G23US#%QsBRXTht3OA&d$N4*qJ80;3-Al{B*3d~HqtWcxJBIj(s7KBINp{-4v?QbuJJ5F~jZjQ<@VV=GU0D~`^fc+s53?Wbx~~I zk{Vy{y)P8pT(d`^29O2^w&)D2&k7;Hsf9OQc3 zIj-UwEz^)gMo`ia$nTEdx3&pEr2!D+DatYyH9+3EDV z`h4G6qLQMD09QdPpw)bTO=s^Mv?jEU)pyv?CD7JiU#TYHbg6!@;z#Y=2|F8Q1<9|Lgc{j;^O-r40+?e3rSmM0 zyT!`v;0cLtOpUDr6b1=5?IW=Y=Vi;r7%DV;XF$LG2OBwcOoRx_j{M!A@JtmrpKd7=+7EoLElJv<| z5P+Ea2X$+N1WI+vO)Va{=^_%;)Zbn;CbpFF3WAsjy z1gN%3MRs;}O(lK#t{4ei$*&E{ zU|Q{D{_WfgLN9O$HDZq8yFI4CFg^_3DHz2dfi@xUh~sCM?T79I!0T`$JiSiy;#+G) zdGBF6RMmn&M8 zbn@jc1{`ofSvEdQ0?};ni$b3R5g!#BDwy3EqjPusbDM?BZTJKd`h_kDXtMy5RjYN! z;cqu#nNKDYh*z_?6^_Mf^{L0S57~46RTc4W5uE$Oaq6X2^Q5#3dX8T&PPG~(95pqL z$R)Fc_WH`Dt4$GIAbgW)FutgMwQe?=M{*(ls8gZ@z3i-2(6dT>h)2V$?OjWk*8Ze+puCGaNelFPtGV&0bq^I5w8>OS_)Fuo)`t^iRm5lKIzEXq1 zl}DQ$D|zDbqL&X7Co~nS#UMn83;PzXw)o-EsV_Z$k}4?x1$WB@@K92cMo@= zjV3Ay&E^w)jhh{;`?FK7;SW6DpKh&9ihws7=*PIv;g!pmy?>^$xhLnzDSu7XWcrdi3e&=#|vHL;Kuz%ZYm zM+3PvDf(Y{+JcNKjGmC^AnF}ANdt_4PgyX$&Mu~>VapuJOa=@_)QukK^kJP74RUz* z1fMg2epoYtQ{*RrL5wzDoOugG{+cX^VwpWNN{KHh)e6BpE8`7m5Sxd13CwnsL3=2;MkSewBcQBRjL4IbaZqbO-XMp|DD08EYs7epMS*v0FHs0 zP?}dVUw>e*#L_oQ&oE2|JFNmaL=|Nf!%URQMf<&_R9nu^RG-41H+!;9a+tac8IF0J z*ROY#P;jZpZY<;-s8C|@E4j8MCSiElDeq%%|98vg!Q>d z;d}d;6~GGPqsP5Sn`DlHjQ{Ohm8 z=V>nLBLCGxrTQF}?~PXN^-$_E5Vm=wTMaLEt2xtOeu6h+NskIu!j3kt!Vox}!ztyG zsj2)*3a23$BuKtE#JzizV2%4Jo{B2gWcn?DZ;v)Zhej{eOHjQCpW;eB9HN8d&x=EB z+CG^1l>0$KoRTc#>N>Abkkax@!=dFbo~uL&@#dmC9Ly?SuP|EIOs>+QGRy_u#pDzB zzsNekw;SCmJ;Rdvn&<*zk&}*DkfiBs53@g!caw&5U*M;*?JxkMhM?8Wk1d(PL+m&87W4r?0LJu4FW`y7K$ zxCFX-j-wYlVgw|Sh5zi-m(An$8idDeY>Zw)_!vqV!=ilyi*un!+^o7Br_V;Vqwl*a zu!k33Z*<8CISLtcKpXR$a**c1Tw&*n`sT22Dbi|BE zju6$tu1p(jbD8K+(#4A5e$PXbuwRQ^|H!Tq3Ewe8dwHWPbY;w@S65fJ5;VPf7LX?| zXX**kNOwCbxgx}8Ab;>XE95c8Qo#!?Mt}Ml6}%;;V~|gOXrLHlHXTOo*i|dSm-Lp3bKnLr)kNw6t6Et{Ph*7U&`g65%3_ zN28He5Qd@9NZPRWoMAtdb-Ot@d%7no9)f4Y6|AKFYi8w2V-+nmH$VLez$2#Cx%c#Q7mjpUq+upEbG!|-$3ak z!w8ww3qN%-=kf$29W1r`90o^y{1AT?w0Y1)u|WDAc4N4yX>3{1zo`cD1MIQhX~Fal z2Oj2DxEkd~E1hx9PV!}(Gg!&Th@I@-;^4CntPZCBwv=0)BbO)~KjF~QaPlnzf1n1< zB2kqEKKVd_u6Qis%(wR^<$w9V4A2Lur`%tUUI#x>ejz8pXj-6<#vHER@VJ+Khq%h5 zHE%#*fUKMve~E0dS!liQM}^i<%CKn-@|fec32HjYmHK!cY%@MnD9a9?E0r)(NH|92 zcfZ1UeWG-NQzDp%pUP|AP*%<@vXI#%T&q;MDf!%Ryi2i%sLIyea{HgOw<$?nSy#dT^G8s6^BT6|Uq;?91j7Wjs zt{Fqsqzx?{k9=u^ZbPCcqptI9mvL{gt6-Z7FoA22R(GDRmX(j<&1oVEo?%ua`w8)v z+z#rHW$wo%={7CsTCA_tTP%J*dsF_*cFf!=EL!*ceAcdR&m(r_6F_y!Pvs|&*+Dzp zF}%eu@Kh`-up?ekY{_w~YWJ;@_I`~mRsjvIpk>-Q2!jL_g)+EGts)gOtc;^Hj+_)C zX`~(5*ANXdOQw)mLb;+iOGgRAQt8hukgILFYiWWC8T#F2bEw%)=*LxVrEuR6qadn`^H*F394vkGa|`avHDNJTz# zR(x-$`%q*vKEH^`n54>)mvqRV^>##&VKK71){L6Y&f6j5nO#oGT7wvk zK;)SOJL*)lLZ`jOF_v5)gRHyKcLIfW;^56>tHX#J!~t%A5wA8W>8KBjH0ZYZM9NtT zqA`1!Dmln55PkD?R>oDoNEgPl8ud;9)hx8&Yamf1eGIV){|u1?-?P7ZgO;bqsdS8x zfG&|pz25Y*%ym&RRGg6jMuNihOT~I!P%ktsEeZ&Wpc-2L8se}518tKN4y%M72UQ}+ zHX0qzB@TtM4moqR%M*&$6w64rw zQO)AKMiq!F1G-g|uC_wq!H&0)Ja4L|K@%j%kD7%xgre&O1vn{=qPCtjEL$}OqXJxZ zvyL0Zcj#w0ms-MbiUx*Trjy8P^C|K?MTDMd+UaUnhQlFL`w)r zdbE~Bw^mY2Wiv5bG??d%XIkLsy>Kpxgt8vlP7GQM;<8^@qvRgV zRf-oyo}$E$S^^S+^ydp^q{J&uPFRX_en9U#^zaPKH0%(8OTe-${D7eJT+$PzJ0jCJ zp*%Kkv(oEXZnJuVo9^@M3N%t||9<^+V$~;jJrqy=iUvPh@%@SseAA8O%yzx&I5iR; z!VS!DRvt{gq6YaBP6&8cX~SUk=~Q_pD{Oe}gPHF^2QErSQG)q4fi8VK`e(lFLmf!i zh?&oJCqf&^q!Kg)O--XoJ^CRXX-n6$nI#ZN^7zL3b%%%^5*3WRP*cXnKLTa85t(>{ zzsh}k9@!vC##d8H(>mCd>1NHa3~G_Ys;;`9S#%1$Msmh=S4$gcld!~&N9^XQXBHJn z%wFvEDpF&=aSEIxsbHJABsiKp+S&~=NNO<4RqZNg)lK#i}`>)tdqMSQpvk{hI zol!d(2khiFJuq;j4DT9p{Sv)ykR<$GS7Cfj-K!3#4sUi`fvz*Vo1@du2?v3u z!7R&N;3h+W>@}5}&JpGyr*s@%n!}Fu_6%Usie}fNl zf>!Jb%BpyDXu@eQ{+utM5uh8&ow^>R{GEEP!|O_agslobC41m@Aq+^kLpqk> z?!fv4ijS*h{irctBaNPb^+swhn)XKcl))9Nq%Du5wY)YKFWDkR8NOj1V5 z60Rq!V3I>YdyT0LaWRHY5c9q_B|=Ja|7x@P9PeH!5)xafR1lg7o!BA>X8TcJD{~&p z@w@zqv;bmTBj+-DFHbRVAXV7MG-C*k<&KfbAf1>CZPRkKr5{CVic~ZquQ(iSE`ht; zV^1(c*7q2<`&XP2!v$K*kPkd+-8IceLF|T-h|uOU1ieBLo1J*lk*_vLa}f#;_aXdlW+BK)Qu4!%gID9G7UE7ll&y;oMCDp;=*O}z(<05r@Sl#S|Dx)qKZ zReC<%?a8?*afyEIqMIIbexB+T+PhA)K`7@c)0yP2Fq3)5Qp>)0+oNghQFr>H(qC}` zG0v^$`9KZ%KZ!rG9TKb2oTC_$t`29mgDrkcG~Vok>5PRX$C+2;nE3sY23lmmc2P>J zwUyzFE=l`cRv2Kl=k~fg&JxO=Ns@~%q95SBziV#mYqzy38S z7Kk&Q>(MD{m~~cQwKaS(<|!4=-6cDna{gnjF!;^~_yEY@(O|{OGt!D~hO<1cO42y= zeV41vi$u)>h08T^a=|B;gC`fTM+jwk!Xl(~lL_FTTlq9jL-zYz)V?a+|LU_NHEyN5 zFK|c#ganjpUR%RO;n6Ez>+M!MzdKtdJ0C{qb?X!kOTpAQTK4x#=D)Rm?LVJ)o->h3 zIF0Q}CpLNAdHmT`6`KemJUW&`F)+|RP6!2WWcyeJPy36*P4iK_aCg_MK{iSQln(_Z zIlfXS9F5OSma5piC<0bijxh%Wu)n|ewsIzhQ=jPJCrN$iRN>%MXsHji_ z;e@9h4=)Jp)^F~B-+ek$g5z%J^!Qi^rtrZCtZjvg0pd4xNyj z9zP3o_EITqzi4C&JVl4?wFnQLOnLFrVplkOJuk0^n?3AoN3H%Q6K|3A#cTGFQ&U?q zuWXSTMmzo}7nF<7RG2{uXIL_J;qJSSa8q!SCFT}I% zdThz=^ukeK*V&`vG8HgzaFNa=R+dC>?@@0uyzkH*fYmhm%?dkb#bMt{KLqb}Ail+W6G;(N#@Q*{L5Q2x=l$E)L3H^nh$F+l*IX48Qr=#40V zi5IVJHCC+1Ktj^(aXrp{cIv$LEniJh1RDNBrSn{QidA0NOifmy0mmskIe&kH?#5W%kNeBSmNiHfS^*^2|r7anha97waOZ(0{8^yI+VmvZx^ zk%HzkACQ?ZmHl!Lq_J8SYfV~dYm7hBDKO=FqUH`;_ z2J@FfI!Q`s5kCgfyX*;~8bgWfz@Fmgz8wQ{K(q7mwR2y{t66P*ubMz+d&$0Ahln_U2Cwa~La)3$Ul*1Eoq32mGQ05CTwBTWPOW@@ zGNR(om5lF`BMi*UQ^`5B$%}@0$wH~dLYb9Ui?F>4*ulCdLwBtl!b||_}>1Mi7S$^^@H^| zp5WZ4Th4R8znu+(+PP}NuQ25+q3Mouy>_NW-s5v92WsX#Dx-0-toop9V2eW|o@Ub79FW=Ud0x^GA3bBKb^ z>0pp7QU-?9lYik@dl9-riI(Sk>~lPuRrKFSlh%OkrmeI{B+mHZ`PTmlLw%)KG4G(e zVBJzlA91>y!uOhUKNBye%RiXo#9E@IM6JFQ{ID6Mt&FP5(Q7tS*xI@3(I|!}4m^48Vi#(hQPF4MTpJkG;`Q8530^Un0(rI^d z)P|CzqM=Bw6^nbz@dQS`6!_BZ@Ff_5YM^m|w5B9nN7Xansb1Zd=bRx&YX5; zud#)JdnqZ(QoUQQAJD%)oEcR4g`zmC&DePp3qF5E78%N{QMvh>u*D-$iA^Yc;o3;< z7uL7hs78BU_Pbr~s3qa-sO+~LU?8wJ|6BR15k^Y-U5LTkXqBH4>vwP_ZRms$gCCX?fz(IHtC zfpHIHzFk}MsJ-K2iHu%+?9OM}%8EVy%(^Tj@1&F<$kldn`HHw%$EQjL)KSr$D;$Ft%*CZ%gb;EG3gpL_uLP98KJ80+|HNT$e8Ln$Vrf7<$Nd zj-z00Au)cW$0^ItTSzQ6)92P(R)BW=;Md>M-6iQ&$SHpg&xg&yEl^|YHF*p$E}=UZ z72gv`pXne=Dzz3Y69K669bdXxlU^L;<0@l*!1p|*-I2;rPX&4(2UabG-Es^A-(1qy zn8MVKD)smnqon9)y*@Tz;phA(yW46nToa}<&ALN##X}VjYQ@X7`yepQ>MO=#PXGdW zio2OM_{81!4(Yi9>wY+}PPyt~kk`vu{r0g|T0(}2d#{Mu| z10`0g%_WArT2Ppz1jY0P3SF$_>Qa3`_#}J%>7N|_MbBxy3angk z*1zJ;89mbT9KgoY@9isxJ-rsF6anJ-iaQB$=KyS)yWmd(oxn(ipH5@h1j%G`@U5%x zOYg1*LS{6*@mNI(maP82tw^(rdu`o4SNo&0z&6K1$Jrz6cfH_p=r) z3SL_c=fjIGj0toTUM;tCRdY2w_E0`>A`)Q`i}u~|8NApkpSq6mQ4+6P6mu87T``wL zX-|m3UO5gM5>g@o*EioI(=Zhsn-NORgI1t_ExpdS8iR&Q6V!`@^=mtZtNH>yKo;$L z_N8DOq(6Fi2xq42on|GlsjqDBfzOu)bqtcR+%x-;{tmYvZ-o6XE`hziRR} zvE?9MS&li9=m3lD+oAp|-4EUnH6mQ;HYvMPvSO$%Khs;?wAk ze~r~D4B3q!Sc0|PDC}t_ivde$!tiJX7?1t`F?UvBZFGIV#@*c=TD-Ux2<}dCcMtCF zZf$XGaCe8|4u#_G?(Xb7@Ap36KHo?CnzLM4nYAV>lbKof|Naf%`J5 z0?Kj}I6|Tg78yU9fGd@>-q}_DcjDdBFLDy1L}#GrQo9oU$c^}^cNV@bN#hSgdoC+w z!*NrgCG&1zt_*aoax(W0L<`}F)Y9!JEd!|+|P z%C`BuZikbz(>*W`un1mpZ|rN3w4t%%;79K<-SJKxjYPH#|CE|TW9ZcB8yqP`PiSeX zAwW_S8hTu%G4}KP@05@SSnJYxLK6jF&>(6cEmo+>3Jx!)Bqj>$>o0eRKHp&H%nEBpkwzQmu^(VrqMp`f9bi#|nL(t(TH^22@;R|PkdHXeUm&reCe2N`Jm}BE;X^#>3g;l`>TihAC|=-E zI!kdNrE0d&(`-uk=EdWf;jZ9cs`O)S{SetR+Pfhwokj=qexuj^sKFY@2ux~!uEmBF z1^5?ZO3dk~ogre5pj|F`n;E~C`!w%Njy@fBd<29XuSG6=CM_$JZGX&9f*;NYkaIM` znu#+puHnTB*_0Z0>JNYV5_nJjFhs$BGP?OP;PiUdA$S;P8o>5t??}Ffk7=DS=%S^nB$P-hQ8(ggGzeIN_i;U0iKL1k(6&EgibxsUsx+g7C{%H(O)j;T04n6W(s(@=P4`otH zDU0R%!lE`8`BDh{1iy`~yIFRF2>7KK=0i6MkVKA$Is(Yt4(w^n`hJJ16CPmvO_b2B zw~XxbaL!H7v+hIqtaxMUmvq=rp*puWmmv5U3JOtWGE}+A3q67eKb-&uQQ-Zssp(3q zq!jc20w2L{KH5sQEF2-mt;gY#tKDr;CrmXknaQ!hoW%24-1qiSg@uiBdN$SyHSvCb zlpY!m85LTUEgplpKXf`gPQeAh{JyX281-8oJBZa}y=q|38%+-g6O|1;PKxyug8rWC z^AUnBUzaVo$0L)zL)*jc0@kEvpZw{{K5N`f99{MGs}u%jkOGKdVj%d$V8c7?)Zv|a@_<5_n~1!{*3N+B0hjH2LUaA zHKJKgWJEbMq1{`xD!6Ex)#%0Zg)Ryp;W1c&@9LGEobrJ;s!*+&<~&yO6-$(jswJk! zCOaloBP~aNz^v9~<`!l{r+3IFMa%co4*+sV$*;Dh9DgQ75+SMp4 zC?1d-94)!{aJhw`;+V=A7>xjxWY~XXg}bjUZOWib;XGyg(o`8i33#^1R@o?69unVT=B%dRfi&9=x>CE_zm?sl8v0A@l-pkvdKkQSW+4ad4knXuqGNE9urWZc z27b5>`JR& z@luVf70SS@#}_pJmBm|9$y*fs9@rU(>(S3pzq+B5F#zYExEyvJI?uj6)adP<41eyn zm|%FwKJSC@S!Sk!iA~KD%7}*3u<>m04F@u(@oe~OR_Upf&6*IBsd*nvrF}Vd86n(` zbVZ_+=cbDzZGH`F8w;b*(>U(OruhMaS8V7*zcDB6lZV2W7#k+jDvYMFtGv4<{JV;{ zEO%PE=w@!O;uwxBk39)HCHT;x-{19IR=zgp_bl!9bD{IyTbK=2%$_ykK=f^xfa)Q7 zh9d%)D-UBC_fvI+vNb+M_w914kGAK63tjLYDxRI$b>Tda2AttL*kd>z9|#>8UJ2a1??`jwA*%lEV@% z$l~-*RmdPqv!;Gde`ya!mLyIrtOk1Xp=n`DLpZD*c&Rx>9A!xELfA0;E*7JanQEKh z9Y72ZgFfh1H_ML-(&=e6O`$?cBSI3V(J&uUB&B`B{j;|^;sDm3(LMDX-9RTx?D5EA zQC~FuNHsQYwIh$Jq-%Wo%hJ^4z6?##!e%-IZfng+fZg5to6mfdMrIMVU%a7ytX9%o z)mCpx`D<_7F+*(ST$dJg& z94Gan8P`IdebNp&LHm)Jnl|BwSo z6P+wn)xVL&(#IW%lObDFn&&c5;jTPh={b;nE?_85p!+11ZQk-txx*7NrQnaK2-EVg zacDi(_dU7Pl`PpGOod#ZNm^ryueLC1-Op@#WXGe7j9;=k^P?;9Ur_0hA1T;OGW$t8 zo%a|9<+WxL@~}T%(jr76-A4Fa!l_sVKG^sa$J+w zdLuAUl4AHgJiYJvxuRke$|}R1k@*b0wH;FmF7JJgOD#d(9lfG8fkweT1fkhSG@%Kd zlsW~bQNk2@wW+MSbUa4^1jYsLDN2II_L3{oThP+mC)M3eE4O#VoL>?|?cC%F7G?;%&x2^7;?CtzBAnB#uc{eE_iKB#N7a0$6E#qh$ zUfPpwd5>+AcqgPw7AZVzPS$$^z6n0|pWVZYKFT`{$4J&R@Yh~9EZ;NP5G^Y{kC^A5 zRXmki9$*oriCuEBI6+%w-9B8=GDDs17yw~~AMw7N)}O^sYGM8=cAYv3C$93~vYGvE zQ6Fo``weGndbl!k#UHDN{bI@H@Mv0cZH+`pGT-nmmZm9jZ+6@`-j4>@ zfNJ+rZ>7bf9?S%KX@w}+rqy#6iE62t=d}Kia3+e4O`8~@{C>|B#`)*EC-`OH$Nkg6 z2eqi$Up=K5+Z&dj{0`xXf1*Bdu|H^Y`)yt$JGXC-GM?pN9CzRw`l>t#bk#q)g5hjp zo;1^bi8iU}BDWKS91}&~RWDmImO2}4q( z4PuY-K5N>={umi#Z~IRCihhB^1xJ%BE-GOoo{Iz)U&W!oeCWZTqgc8+fiCz&7BD4v zQz2o#*`*Rk6jj9CkLr_z0Hwm;LZT0VdofBNnU{J*ZcKPSs^FMR%r?sag<+lxEqw)N zuwH-zbq^=M9+2oBjJ65fG`F8SD}9!Q8T4Zbo5y34OJ%S(c5YE`H}G(LzVqqJazI1c z?|7Jr`KTALZ(eh6U$3@aw|7M^=-!DJSo<3A(Y|wiCveea$+C$-ioxZPH_5x>+Mf2B zo#mtD$WplNJM+tOswF?9_iyl@?ctdcC(~xD5fc|YQPKRjH!1>*B`nnt?s*rZQ~?hI zeY6X+D*`9aJR>m){WzL99I4??=e%djontIg9*2L{)t9haI4d}Op3GB6s=UiE$KIp; zuh)y1n}h&a;=RgjRUA|NV~t4`&{DXD@#qMG`K+K_%FhqDo`E>hO?#$Q!3Njih6CX^eI%->q$vYkSiI< zktcJN#YJe#Bm~%yrp9`0(hP0dF4i*@*3XC_IE8i$0TL{Pt9lid^^*n*)MFlCoRWi` zEu>(LH8&=d6vK8rHYOu5LCWMatpIkJoIetR&==g6`D*?4!R&Hrvi`UjS+Zm7{w#_| zsxZJf;9Bo74vrL;>DbC~yEiU|duJ)n$%(b1>y+!9O z%40SJtVXSGc;|kBhdc<7&K?LX&02^OxbtUT+pdNEcfqcZ3;=4xxn6y88VoeV$OYa$ z2rt|5MU_pM>6;Ld8IG9j3|6B;eKp5IC=85oEH{D~u)BuaZO{bOhw2S#6$8C}RkB zdQ4oFG_rwBM_yt-vvYP5^^$D01t1`M;V!t znH_&qwM^bx(*On0>1BqwGVD1H<4mu~0D~x!MWuAfW$T>~J(9=*u@n{*ja2#F2&yik z-Sl-e%2fEvfoIs)8Vzye2Vf*a>HwvBmfE~iElX{mCjb}-8RlZQ)kVYQpjIKY zKL~47P|VSNi!tkIdbMHB45KAH``ZdJ-eRN{W+bqn4^ndyJ*U!7>EdJ<%9f z`1&RN{;u(-M5}(X9#Mo!(KS)_UrKW*=RjA*9$!r|w=Xykh`{c4UdyYrV5eYdLULO> z=@hnpygSlUrF&(Fp+&!>dBIBUxrFmR&l`md%M8AU|DP^H$OS(ipG|wG-rZ#DP;UDXmFl2BuFu0Ujala)-bMRAOvo*O!m|8yhG%H!g1J-} zN>|GQG_-?m*q+(lO&+~ffe@X0ld?j%DOrZ!#v>|{_<#85dD zr1l8pVfP%-2q6z_^4_3yE@UWq#KbnWRGlEkREa2RPtGl3eW|-_GW^qu^wP*GqUcr3 znbTWg@wE3=M*M1+o!AGa`}Wy;vptU@WRX~n$ z2VoY2ZytCXPbYpDd9w{q>U2{Jq^44+9qFs4w_+p55moEb#8@=^QBgAg%`$ja#b`i&Nf2A8+= zGy%`fm~Nj{%Sc-zYGa|$Rx|bDvz!@5*hI#Tcp*C>k9&n~RiX$yu6RpmFZrCBe{BabC8=<}+U#N?;lKimVu z)K^ph8*D5rJ)c6}PuskJ*pOarQxdSz){WZ#O#@(COsqm>e77nV77h5$(&J#q8Ir?79P9GEaU%w6*ub$XJF%?oJ85#TFLR|Ds8gkU%Js zUn&t)g2s94NNs;>(q-0{vU5e5oYR4Y@jqDeSOs4)mp*+)@=jOf*L;dAu~*q#>U_D^ zf_YWU77#C}wXM*rew+$0P`-Uga_@+W8qu-;#*hW}!T0#RFIoQd+Kgtbat)JM6jYJ* zYbN7>8R5+WwH7@@!nw`7_!E4$p4&6B|DItQjsRs6ASXe{^8?V5B=1W{@wwGPQFFU# z!N5nK?WD`C_*mIP1IBiy5#EI@l&be`(6&0tRIi{zkf-firH=OQo#z_d&6f5b$&QEk zK@Y%2`Wa72?s~1|?VHggpmRL-M*4V0cm@XGFybNZz&g*<=tN>20Fz5wJd6DCR=p?I zwlzr1ROZDJ7Lr>XdGq$%S$ZI>qYMTQyy(gXp|-&3a*Bv-xD$Gif#|Fze>m^Kxs*D$ zmnNfZxUoMr#*fBWyVX$!KmEY_m9>{bMT&w56D8RtKQpXFYrv#iWXHZ6-!IGFZ7C5; z@W3YvFPhxP?(H~ISNQYGO-h1r6USqM&xI4LxE0hk1QR0E+cUAFRx-Gdf;ckrbwt$x z!<@9#I>|XW>1Uzzbg!g*Iz9>_!)#n84?UZekBm~b9(Z}H^Z}vl<&MWt_c;aM;IX>k zLz8`<%~#U8FLf7Vcp4@Ob-XAKCNrk{tmYVENTBBGxF#K1tdtAyIpV=f5t3ecvncrC zCMZp*v=<1yG_;4!@mXI$a-iv$6UkOQ_ z&{gue5&E3^M``lBk|NZ{VL@>xj^YZyy)e4_q0r@b%DgS|MXbmlah%mCb-!B#N)UY1 zUq|ZuPD@Nfbb^R_a@eh}X-aSC+9zbERb?^TmVA8ragyxs!H7*fRt8YGBugg7`Q^+b z17=2T9Opzb2tjcn-Ro&j^H$%%W9Y28EoPSWC4>uVKW@%FL9I>7j_Wu^)c#a1PAD4`rg;=x7GCo|4M%gu6OAd9HN1zR#tz+4_15Ec&ZKpyv`Xsn4xj?H66P44vTL z>4Mwo;&d91k$g8Oh)J8I*%7(6EKAADHk z@*vopITyoiF^VB98g9z0dG4r((1(pC`}Y1*KfK=VlHQ9HR}?O7&m;SlarN>qlfKX?{t<-tSkm$<%Ff24;W^A?}pW;VUR+juOfeY=q}75Fps!W-OGWb&l@tN z>U=4$?kMW}KBVt+H#dr)LmRDZmF~nY9NV4ADSW%`v-hZ+X9TIKO6Vy!stDnchC)^* zHVfQ@BpF3O4Es7}g&iu6Mh}rtMH<3li{VGNs4^;z+6RDF1c7Tz=V$WKs#7}&4#=s| zGCtW(Evj5WV5`%HAu|q$m14>ED04>F9A!e@Jxuh$xVmZ3FW7fW!C}a&ZlfvUldNN@MX9($ z<9<(bnm?7wrKZTvnIuY^cYVvmwhS{vmPd-;pj?b3fLlxfR|A^dlN{f#eu_td{^p&| zW^iF(o-J2qPWCgNk4<$0v&DhIjUkHxkJCfp_$;lvSPP0XlZvYLG>zo9$ZUI`mK|sG zPH30RNBnSc?x}f8%**lWEaQMNc+6~bZ)fN2YwU#b-KsywMpB9+eO6gazDarmNy*HcVMc)Bye%dCMe;dzk?xroTi{?- z#{$8&d)w;HNiN5Eno(VWSmRzZ# zqP)M;R=Fq$k)~2v^(tD0eoZ}@89tU%!l6tF>Xy=YDbc6c`0oW$als94DhWS{tFGIP zB|o3KOp)@R(nAa&grdEGMGmWA4#1j}4iVCkeX0sdbRJl0neEoZ`;1%=iDrhVsVKPu zHa@hv*)F6L-NDfQk&~BpU2F5;aHETrPy0NL4@3NjM!-AFoU{ywmbUA8CeXm68}IeA zpODe5S=OB2cM@E)S2 zp=)VB?_x||Hn8pUWpuNlANN>bzzDkeqxiPwJ2o+|iG?KG5%Pzp1DYBz z(+6HrZlP2SYP4FKNhYQWs#`Z5ic7&?R;2B)vqt%5Bvxm^`E!vVIu^5+%X!x7Azu)i zc;tMxcq*4VH1>_QpiU*W7oi6kv?0UIK0o7ZaO6c%D)RkESP-2ZdFK)^Z{7gIoI!t8 za4+2=qNihn_D>xdJHOJbzoBVs8$~km{-m24odUan#sN#Vg3Z3)Y4;|2no4G~P{d2) zy%%dyb)KP-35-0W__Z=8V;2Z5h(sn4O8hGKqbm>kF|J&tzNoyAoO>)B^<*up)rF>0 zT#q#*q<1io;O%82p$(k#+TJ!=l%AMo<$a3TCkdAO47Nv*dH(^&+4t?uNER@2Z8biGj5?!>=Pg?bPumxPwG^3vu+@4*Yo_^E|oEAPLJ zKth7X0c(x6Lzs?=%altixi7a#m|hu_hY3RO!*BY727(1bPq)~Z`(@`DwpCmUe}Q%a zXmEt`GLRdF{jIUQM(o?ki}t^GpSJp>TJnX_xVSdv7W7O@8v&ja&@^F=@Thej9}K~L zL1;)*JdWRvk(U~;WoFt8(oM7(H+vjiUqiY{nXKz-DC@v0s~e#B?POjw5}bz$AyvlP zQ1$WWugtnVx7L5bwXVdC=4$jhpc*fWE#pR!(!bF0*{v*il>Kx)Szv0LqPky4Qh7nv z`N@A6gW4_@H>%z!S)G952@)Evtr+CL%y|AFZvG*mLqW|5)2(>MM}CM4)!1hw$d zIxF_!cSP&*QEAxSB}43D?i%p^gfC8!+WvC>7SY2d57T!RDqN30;=K}J6yV{!Lq~P! zK|~2yWd*eh88rOiKjd6)5fNuS2_2EON%Ik1+BkiuzU%G(*0@6JSdE zeN##IF@{_ByZ9=n28I+M#&K1(}PcRWWg*;aL)>u74L;>{q!7S#D3u{Wy& zdxC-*9%xwfnCKvwp_6$s!lH2*v}S~<X3H*ok&>dDj)D?qDKQvzOGW0v%d^$rWvt!Xo6J4U$ee7>v#k#d(Vq<8=N zXWICHz-4CK)wdOHND5^5h6mEYq01&2tALk)zZ?DAQec5gt{>j z$#>R-0F06hgQONTDE^C8msL`aRbyi3c6C3;?wfcS&JXZI%$@Jhx+nq=jfO!`qax+d z5535&L>>XnG=<#(^Np)%z!MT z?MVoo!s$YGUfrn87^J{VZ&sUh*~xrmxjHF(-l#x{e)~ODh9d(tIHMay?PNdvD z&vkU&??C^d7C~!`-)&8w%A`_~$M4dWpegi9lq2p4&1goQs0DcL!QZa?r- zZ|2R#k~)`$8O^-qk_U^xrr%?4LS50OTGz320Hvq)BK!n47a&+dMILa9=!LGR#(&0t z{>|yV1vSMU*V=+B)NmMG3TvK}x4X>kY@@sT6hdKwv7&YrIHAQ%Gix|}56i|q_4OmG zoGuhrtXHqra`XKqYqLgS=0SmHW0$4t5sPwQ`{%mf_#s=e?8XlrD@~Z`2v5t|G--~h z=*Ldn#s;u3!ZhPRKr%TKq63&Ntx8qb|9F1r zMroa4Dcw!lg{J4$V{M!&8C6XBH7-bb(oCbVDZ(L_Y}c&T)>_^5IyxI3(EV^xl~0rX zr5?Ll!5y-$M^kkO^}{?(KzD``h?WZ(d0e+Q7OTyu*HXgfXgJbpoCg}yVEh&ZJY#Ri zEnQ-5*@VJe!?yQ}qbNTsOb&dw5I&5?1sAPT{%kdVrvoX_H!7wI=TYxLh}SQ?Kc z5B&U^)+W%96JmbxCXoSMM+e(q5I5$YZ7BK*V5>^1` zJ2)KapLhZV$-2gp*lDCOqe5v`K&*g3Gsjn{X<^n{ymSzbP~o z-SdbYJEnVTgTIezd}fu|L`tQ|99MVZ=teXK{_c}lYh}J}pm!AoJX8mK-DXL8P|Wte z>)+!^&96`8o$YW~iZ(9_fkUjL#C8?_72Lwd%d`xxSp{;LQ1s~*6f z8h0>n>H>~_aJ`T_rqus>Og(>jGu4e#r*oV!oaeXZw$OUL%m_z)mhUnpu<-VF5x~)| z;mKSIU+}gWk}%F4{uR0)nO;52=IQFwFDx`=?BKmiH zNWbnc3sk;F&Qah^D@^z6G9$$nxE9wI{lMjydAaXnPsGI4rk>wMg_nz(Qt=$`mrUA` znvG1yHuf1NJwFZdm1biqM6uvwT#rk^w!RSEConR*rI=W3V7c~Yz0ef5cW5k;B~WlVV{aDcaPv#{Z*U$ty# zRmo^o*_Ou9XW=#7$ux3)HAL1tdskYzt|gcA>-eL(B*LT51PmWAklcwl^_x^J31nNUo04_6g<-T|t^B9f~NcV{CHxE1PwngYc@2eHX*IBBmD* z6#TU2s8*aFm)Op!3o;=2>is=Ymt_I4$~K(+aFq3~vp(1g1u+L zM66+g+AMA(K5Jisy;QwOeKV?UG@0eaKIj-zh9A;uam(XV6>c;FJDD5szU0SZ)+PUj zxdq|oq*D{OKrZc8I_u}z0p_>+v#AK&&gR#CB=#XNO~O;HU&z!45^-}K{j^*RT0dTx zVm4?Q)(5^nB|9kc%a@5}xAZU1`9qaNfKRDl-eZMR;#Z6&BjDE<%CqMxcyqzuM6A z^=UVymob6{o|~b~otNWwtd@z97*QtnGraK~OeP6e^bbz~PJzu*HXwqxp>xu#yI}^8 zq3s&a@oZtt3My0_vv)vGa2QqmH567)uLybhf)GVPR1c{P+Eg8F+t6&A##jjwXVVDE zTje`4qKalp&nkv5KVt|*GtyJ#_p&h6dJEPs;T#n2uO_9$c2+4D#Ko1%w{!PQ!Ou59LGp8cA z`e1g3{db<)+)f5KcRA*!Stz%t>H-FAg`2v+zgwV{godMk%w-9ZeaHD*XO6s}&(Qo$ zhbPN7apvy|`~jrN{Y(sNTf&|z6lUu|j!%WptV7AE#fZhtEXUMnpHqALQ4w)m%}~=6 zkw~6X){{=W$fCCsxv=ZCCWK`zBc?NF`#y)Zry%~wHvU49wm2r%N%JSKLb6znleKUJmJHq=rHtWI%p0$To3 zv%$Wg29J7kS^L(V09fC#HLTW)jQ((e*w)#|~-s>+y_GyO&j?Q3v z19ygtbA9QC;`q<21K!FDXuq?;L4>~j=|gWvBVEZ9k0y#oz$%e4MXu?q{J2|q(LT(r zryUUczAbCF>`E*9opX`8vB`RF`Myx*oU_8M*CM)DPo@ zz`bml>$0<#O|fgD?5&n|w*;2W0>B8u4sL^2$Gea({vL)ju;hxpUBRZ|T$L7OZlK9n z^+5i|@}A0(H=lZ{zIQ2M$<_=KKE1_5pt8N-+xTB@Ic}Afylxh%Mk&1!s#g=d05lAV zQ&098h22eYolRijES`s2tbls4&;2sK_?=f>+^pPoy`WV_t0$XG zMGcwIwR+YHd7l;NiF(a~FVk1E$I<^pEF+0c7wa)ci<#>;sXa*3pGj8J8w4kSZb=+< z4V09r`N7Ehl2n2^8Nw}$2NT(r2(-Lb_s)2)(APaL zNp2y^z!QH`(*>#Q~vD&Hl*_VJs9xV7t(H;=Nu$$XtG)g$mkb%n{4$z24Fa zd<#UZ83Djx6oGxmZF<|sOKIZTMlL;0jVZQ3+9ga848t)AqWGyun8a4YCEbt>FMB=o#Y#Nret=wf)Nw96=yxS`)lmAg9h(MUKfV&fv5 zV1<8grsn#`lNgk0MdtnPf!$inGK<+UnBV#Qx*-tbyT6c=eJASFM7uS8#U z0V}h|D}8fXZUE5mgsQb{U(ncyVy8ROZ-s`VKd<%&hLp%B>vCSA?vFU$_b55TEHWY@ z;u77{9RGT#{`R$JnVic8=7k(9=*Il|eg&%Ky3Bp>$({0E2p79IjI;Vb%NeZa&6;kSKVaDC&Vk)gQ<#q??4g?tR1jLa$x--FBs@(W^U9kP>+K zpaKuh1)hr3a&-xmDhTL*0Y0ilQjqx#fEac!(Ng&_`w_PK=z@6Bg9lzN2Jj8ptuG{D zYdXZk^-#*v8Iyv@gfhe`oghvksRb3tY%mgAbB1T0H!`vf&r^0hQ*gm4q;-3y`H~; zfxAP|k6Q)0vc|bU+i+_0SX2AxAwWdU{|4n@~mo7)^|TJ_q=+j>n*gY+i~WfmoL z;`1WgO-}zwtObw^hs7@aZ95?ZxH$_&QMrvFMil}};SFV}3vPf7g zL_nD?xHAth@g@H>M<{uY?@At}6&#ud#a%MNE!P;zd}Fp&-m{+S5ZND+dw=1v;m0bj zJvjHLOG+Wi-ux+g&mqaK{Aq7atJvYJFH+KVd<#9+u*Q0&s)2t(K)`Xb;m2=hn%-Yv$doDN{LTgy1WfBI`NrC1jSeXV^vH#lv5|Hx%+ zIm5n?P^3#=tG|M!~?T?-sk||m# zEE-<2UQoyD%!gfwh8j*-@(l76{(_+Dpca~lqrD=U@@5z;RNMWr?RX#J+cVh2y`RBp zZ?dIwef+z?T|}1erh;SEg1Xr-A|7giN+F2S^o43&xC28?ni-4FbBm$*@9Yg>|}z~8HXx7P_n zqm>mOHhik>D3}{~kaTtrRqE=06J)W9OEZK*SO?d%2!nIz2?mfz?7b31Y6xaEnNpWE zrQxWcIcjaE9Nzp8jxh$o*RXB64xMwI1#+#>xJxEQ1;l}_k(kmbT@ucP`>OYuZwAdJ zn6X?F1#ehOHX~a@LeI}9)wq?$T^&kF$=)>u2$qnPi%t$nvq|qs>DX&cZ3CwlPrwyLhVfJPieY7*;K>Ua|d7~K< zXOh4pxk`}|C9JJBop@|k`Y9YmS-pdjU-iA6`NnMY%mGFaNKhaZ+$O^=!5MBTKb z5h5hO#-_rMcwSV;u8~UzokX2@=xfgqNwq3~_3e_RwDRX_3gSK@R)&CM(Csm!8N|7f zhgKlG%+zq7=*HQKM-7k(_=Aukm^ssT)KSg#i{ck0n$6!N+qmnrn{?9H+ln8dA&^1f z#FxQSPJDgW{Pz2d@(BlM4eM%pv&NJLuLZ*olQu5|0b}jJRKhb@ zA0vs59_?sFgHl=A_Ro>ogX-FVoukJIt+Ejo^==l`Q=t^PS`UEURSne|#HD@3Q=h$w zrFa-zL_9&A>I^E%Z#%*ro>4drx#4s6FqF3IQAY6&Gme=ToOz5!#lCP<`AYcSl_I_! zfK)|yrbF8Z%2NuR5}U?f`mf$;x6%PP`)&oZ0;j}+xV3Y&36ZT$lH%mSi|QztL4p%% z<2Hq_=qC-LE_zjgoo**{Z2bhYJAxFrCI?#iZ!j|kHNc?diBA!^RieE-Ck*11Vyb}A zwMFcG1Lc>NC=gGTEJmqG)BVt{ObRXAyF~`f7XXug+K+l>#lNuyvhs8lS(b;&*ZetPMz(Ic`M=Z_MuIlpU;KI3@;R z1*TiS&Z@R_Ntz|nEG*RXCXQ!`Ficzi8>b;CU-H_w|6V+&b|m9K+*)`4SpGYM+A_ha zZPl^q17=7o+#}?xiVQdXB+mIke*?+~Mhf}_)IkvP*s0uXqCi-mv}hy}Mx}fODBUhL zAW#^19N9Qwepdr>#&eA~2?7ST)Fo4}>503prm}T8FK+$0Z0plP#Lr0f&+(2mk2^M2 z3XU|!-x5M|hmO>!*}5%g4J=@^y!kOJU5Q~7jfun!+sxUY{uxDUj9DuMcvTU=9lXqt6o`!zLuEJ22g>>EMtuVTF*#VE zk((q>^DVPxq1kq{YEZY{*+ct3Wf+yszsu?>zde)bjCL(ts3Qgvw2;;wqvSD z489l`)-*BS$a*}AOe?pEbg>TolNeOnH}5qkwH`j`Nzx(sl*qqYpUOc^i;M*8P9v=O zodnBK^Jvx-yy(QcHaSxFoX&7t%0Z(sPZkX#S`;Q>DQUS{naXygw^utXJVSsLp*_|Z2!b@S-@bdAKZ$L%-Pg) z8&R;U{kQ?rCHDc@S6zxC$PswNrjmOEdAYk#7-ECEn>_!-XQKwyK1Wyl9&%?}O;X0^ zIGjl@h(QeT$uaWAKxF9=vXi9Dxt)nx7(@{m;?#*T_7H+?vP52G)0^7bQh0VtwM!QQ z)tNzKjwONGEZj4?uLq_!5usg+v9WBvpM#;r{c=d>h0$lUV;7ItP#RM>{RgFtbH=Hc zdg?!od>F4f9pHpVY^I|i7Tz;?-5kBho%PhhMr3)xpQKf6vn$tMS>b1{r~QZ z8Uw;Iq`kh}j2IByo2>GE*J|JGV9sNyaj=X0zpSsANDCneY87`@HtP9$&t|;$;9}md z%?E=*)%8wGCC(JusN4!^&f9_ie&nV{pa>Qj_Ymxs2@C=a^{m^Hc3n0F8=oqpSemqy zopm3iq)6JWV3fG|ClX)7{~kD8nB*u3Dp8DItI5%qB^>QZLY5v+`Ut9DWFn`#-d+@R zL{+yP87a4MI!*$7{v(+KL&B6W$qlL)l#!UKDh6+t4;LE#n!z{%N6>7^s;4d=|IR28 zJW+_!&rx5@jmy&=j?pC&*}oziPX9apA+gPelnH0S!&ex=|88|yV7}O#meWu7Up_fK zJn)lrI$U=3;eV#bCS~Feu;GrLG5=`g`oGg+m=iolM>!9DCH`~Nz}~!yP;et{M1_r? z|IXSX%1uPoBESKo_rD{c{il(v)pOIo`({T4L&fZ<{QtLujQ=$HCi=H|@85k}BX|aX zxBl}K{crcQ5y6e>urj)s|J}FzKR+S)kV(zkf4e99Pop1yZtcDP-8a!cqa`}Zxz^^lmxuN@ zcr;!WXmF9x`JvJ%SKa$am45r`H7;YtMVUrfONZCTm(7vqk!QUB8Q{AS#-l#ld6(bI z>OQmXYLorSDmWBittz{H)gKcS42#?b1&h>D5Qco>;jh_XrJ`A)oc{~0O{4roB0<6bC zoGz`-d(INkIPT!*3m#|74Nemo+bBLLY|2=apf$KZMD8OF3 zxi>+(w3oeuEr*5kwJOm8q$)tCcU22bGFX}~XR>_G?XbE#AH_SKDZHUkrA4ockZhGV zW_sK!xYPvEMg4#K2mXbgt+g+p5?_@Q3qJa!@&p(xw7D@Zj#cj5QP6g!C*{jn6?mYb=s0#$wpZluC<RY-6qG4bD*eJn{WC`i}5dn zRmlIt-e1N=^?h-mup$VE2%><3z(_ZOgaSiKmvjs%-3`*+-2xKQF*HLBAzjii)KF3q zL&MPALI1yd?~8k%*U#tkJn!brIcM*+_g;JLwZ3bewba3q>Tqd$I(p)^qWoC^NaZ)t!98n^?m%AG4=-J;H-E>N}k)nIRP z9=c*Pe`dW`EyK#4a+s}?mp&qJH_oC{hjKu{Cb_KPGlVQ^D&xbv$a!1kg+6ve5dx*7 z!K%rNwbc=LvNcF&Q=aV8XUo@budKH@kvn~gdl40FE<8FuEv|%kzKH(`Xe0dl$>O$# z{{hgW@`2f}qY?rwf2OJ!`3{`-y1Kz*^=ZG}8($=Dd_l?`S*KhtZ#|vu4Mfh7-oVeX zU+B0bjmN{l-c$a3s7~g(Jt|Zs&mW$1OWRmQ84Nu6Mf@MnM~VVKqL_$+x zIH8K4DS89Dwz1o@`n?U=v}(=biqA__%eLzr;?#-VFWTFGc+MWlKc=bIAL}qWy{7n2 zTyKR4Z4`-I&MTam+%{hf^`KSF)X3;syk^!`(`j_ie-3e2AB1{d9Ccg&2-ogA4n44n z{q*>4$Fa@L<*ChJa@n(r25CYuq2GxC4w0eiFtF~93lsRW_wzD#r5(9cg0qsr_2e>* zy?0-%HQL5{h(@rfbfLF=|BYJ|zfa9Su;nqfaZFw~WM`V3M2uHZIQDav;uFFrFM`%^ zUrxT)ZbKqwN$p2qt1O_p{td^U>x9plVug7SWDeTCDsSZp`ri`kzWxy}GfV(kGe_kS z_!w~zOJ0Bc+4=6jgH`Ch8CfMOi%d!lq0b{)9BgK4&1)9a!>#p~T5rTPzO=C^*F_GpL6&TE@>mp`zm@7h z)pnU~ZU37&sz4!!g^B0f$!g9jnk;ENu0dkd-2HF$`$m7n4We?vQa?dOsXZZ3B76R5 z(k3qq0MoIW&Bc0BvpLKSk{3DMR@@A_7hO5qu5EMaK#yo3-ZahJdE!#RxYYP`2)vbP zKvz|hSkIM-dO>Ar0eBb+_zBGhz zDB!jrTp7CYK(Rf*x|p>>{`&{1F3XxuXq3_?28N9hy=Ap>?Jb9$n`+~MZxd*z0Bw|E zZcim1>4c#`zc%~9pph53?9xXW<&9NVTbR2hN_u9OnH62E-dZkreaN5x#Ss3`P4Bg?6w zR8bfqU(mUG7yzdivz(#qFK}2NQ0iTGu$(S)ohTf6mvS^*62M9LGE}1X%zA-C$4;zi zTTnl{l#wz;;#5s0h~9mJ1Xf}C{LPtES%}s-%3E6ZxwHK@uP{|q{P;n25q!*8&JAob_ ziM_CC07wt~G#p$$n7DGQ{RoRSF;rKQ zg#NI-l6nq(9{+_i-o?=Li%Ub=%K1vHRdv6K#}eu_(yyR*s`UHT+t;>~IGRD~LJx~P zlfo`_oK%e$J*S(OJiPAQhJi={ETIQzZdAU2bzBxg(FbVwzCR)OLb2sRM7<*SS3-+e z07*AdnQnaF2j~Dh7IgeP%OBlUf3ISB(6E#iN0r#o0S_KxY2!RZ-_kO(Xa0M&7WKeC zk*8MW>BIXDrbPf}L#nyFDQ8i#r{@Q?f}a#BPARZa?LR<2lKjb2H%6X~?L$7TKH z#~H(-3)v=oL5)2k>pp-?PCsm$U=@2$EXL3`p~^S>@E;{N=p0|x+H@l0Hx#Dj6VH>a zFF{RTe%Dg^b0S1If$wdUx1^SeJ$N<5=7^)w{}OTd0!u+Nwu}?>B|nUVNlH~C3`ZHx zaMrS0VA5;lX~L-?5&9XPl79AgFsZ&>Qff+KB?Hn4N$LV$Qw5_`V`+6FdkR7VRBL~2 z3OoO$qB0Uaey^jHxwxwOl^8=EJ>L*<)DS>?@N3&=Lew<)H9sG93h%LsTCmUmhR$HU zIzdJL6MreJ8za``z85$cozui}H=60>Bav|0SwfoZ71e;(c;r8NyF~!cdu*as?DI6x zr5q)+Oh5Ufmf(TkZ_4Z{km1)i4Ez3hs$$CJC*Br+t$_UB z(u61Z_Si+OU&y-==V{iV4Jm-8ae|E+|?{gsfuCUzpTr8SdAs`l3u_}A9s zan!fF%mXZ?WY!bYAed{uq+aLn%j3BgShWY6DW6ep0Q;cdRv7zD!eKj+$Ot zIy+^o_{V2eV~6kRIr-ij5+)I0zxOsUkayt#VcSdfCioj2eRX39! zUmP?>O!rl$oF0w;C@GOHEiZo3@pYRK zS}pi!&RY6LgPBG<)C4F})#fup+tb4mC#@;xoTRFzs(q+UTpHtfKW~Fl*+m#qIq&%R zEG3?0R0d#1YwQPk<6&o2F;W=uSnHZXZRQP}Pa!Mcf<{$b+}-B}$?HY4C~8_;{q$9* zCe>y)%1EuI_s9&gW2$RaTpSz*o_Tw7CQ|&NINF|21F2hr>WX){nOqek2}Hv>&SwI> zZnq5hT-FkCaZ9W^NJuQKccO<}2FpNYU@)f=Kt-+NVzw}3_k2v5)hw6X`dejBkNdpK z4$*v-VMJ159_1h-qrIJepTN~xYzDvgoVa)o^Sb}!)|(g^?|SS!^0)}%0Ygit8A}FqqJIs4=S591xvR{eh~_B| zk5pfQcHg2W0`(bJcfB_HRSP)K5+I+=ZkpRZr_Jnu>~c zwpYg$kqf%a$u|gX@y}1!Z8WgH=PFuQS$Xbp(OAD*Hob(aZ8Rh&D?2yVNr#y19|ruk zoC7(euW{XPvyW)`{ZE*r#3S<^CE(`~IgnV+zRZta!yx&h^imOA6*Pk6fn9Z4>N`3$%G-3&hRM%$e5nJeV@<9^($H2#+06kDVx2>lWl`7@L6EkZwqqENn$) z(S&{nVRJBIY#e{Tox9|+=`o~~j~u32ZZ@u9ZkJ%-!6@)-<^|sQwSp$TFNDlmz$dY~ z?RpOxK#$6T0@NUz^>eXs7+>@})!8}Xl2d4k1s?q&XR_x>4KzT^)qO@TdGAY{^)OxS zs=1nSLAcRfGtrGFH%k`naE!BUM_Vt4=n%J<`_IbRbQ&OJmR93s(LUGEtU1z(JqTI7 zRUJvzU_7a;vGG*%-bLE}Wl)8nfyZ7O@YloQ-Ly0A`Y+;$LP7U-T_0mxJ5n{3hV^yh zb(| zs;_Eh`GZV=qGoI}&WHtZckcW2TP&wg7D@%BYIkp%$2?Oa&Q1LcqoDos=(LRB9dU}| zX49^t>kZ99Q~|ZY2j`rL=*IcV;e}w=1;=!}^L~9rqwc*kK3fP3wxNG$<}Hn2*xFaj z+Tlece|EIYUGSb}9g+K$qzZIhsF~mP68KXD-7{i0cFi4K8WH6CvN`Mm)vuki$s%g%cR7aYA{Zi9?FTYELQ>AY4Upg4z*;c;J88%mGJO+V`@ znq+x<&U6>^d{_R%+4gtrk|>s7SdTL%c&y2AO8yjZ*!RSLMoMLXN!mIW?`O3;_0t3! zLWX{&k1lI@xQ$%naJW&K&D*uTs6s&S_O>)=)0oSSg7@pM0#lBW_!LyaTMf>AE)BKWUCz$IUc&9D$WO8xi)78}FWfwqgOQ?dxUID) zHfFo%(y6-;J9GR+Ig5`UVpob?49Y*mBh+BRDdR#W+bVp82 zPX|5ZHQLZdk7^`Hj~`d5j1La!@$df8t|9!?Jj=t}PMD$X578jQHrorEbxt>5;qj&s z9V|tAg52U_2DW)Z-t5M-pfNjgCo+*CP%{{fdN>o|V376hVsHHYX%5gs--rpnurN;DNpjA9XPqxyPsDLdlhS(1 zq!%-vgd>wf8DY|eY)hB|JSROkFg<=h;^{q>QYhlcV7S$%^y<)A#GoaHLXe9KaV#YI zBXfIu8^7FfS z#tp6X6s}K??OVZx%(s)F1|R14xOobM)e!*p!tmXEz}Th3&^=-2Fr(#~Qf(8L%d4c<+|BV)=4@&`jS=r+OuijwCK zzMmO(I7M-cuc!+(6P25F+Iwjt{+j)ugP^G|PgL}d zBddgqth6oFVzP2kf|@O*j7NsLa_pk?(^+R|&I;A3ly&jjmgbJbl?UF!_j;KLS~r8F z&5uM~?Ok5P(rvD|SyaVDYlEu6BkzWhs`EX}z3mBjG&6P-R4b%LBVIKpKuO?TN@8-L zA8(wb+id*uGZjt9^P~b+Sn0>_VPP2T{gd zkGPx_&nXJB(mtUu{o?xKI%ArFFE}MN*%KC`&s~3OY&`eS(n}&Nzz8`a`e)m`%m+wO zZ+oSa(+pL7!pp?Ys(F3vH-l;H8fR{bMuU4aM=q(>IrGIhW1whCVf4v=+%TYXCjXWhPc^L74yRC(1rWBu=q%R~YelFAH*^9lBNNE%To~s0@86#mOxaYG5 zY!r$81LhM%A8%dF>}Kc?sD#Un2b_iD6B86*w4h)aNlqThQsbeyO?KV;tl_?CBY9o3JDih zO}eoy1OiW(pb-^TeJmT`E%3xsW5W9aCfeA0OmaWPtynL!02j-wgT|``&h6o`?rCy_c98H10kQZd=eH;g)FMg2ZI4GpM>n~lRkNLsI5scO6NHgFH%S&n_drg#bH{l zPGGW(ux-}*y-)b89E&^b3%B{ASaQ!tybLv;I;>787=4rZHGt0EB=o3OiXx)Kkt*KXe;K- z)G8FNbUn0O6VoSyi8>iA6tA|;*{fWDbob00n*Q@zk$(?XQ3GT_pQ6nnSw1}vQ9x2S zMbNz$%zIcjxu9z5xh!DcJi(GXP^l`fR%t|I4R08R*=msz!7NN4iKP~6)2B05DeLal z+SV&ie>Q~LES1LrPgHQs>aCVufJaDe01#MM6A~El_Bsa=&AA(+0?*Qsz+qo%RMgtcR5z<#O4ff{1fnx^ZP#W7a0teC_Ts z3I@Hla2?H(n*7a2Z8lGaL1i|!izDbmWo3CXLrh-Dw+A~DN{R1M-0P+aiM}$V7VqrP z2}&siyD<5zG){L=(bO)%he7p2H7d1qi?J1!yxYbQnR=qe+>?0rMV9f!I??eAp_h*S?-l{*p>50 z!o3H$Y4bO;!ZDXWE94}yZu(`~-QzrDSgTe;6fRn==XVe3D6st5Z9M?>A8eqGhyUc%Qp&$REm~Y zE`7-+87_ljA}%reOs;pLu9!^Jd<`tBf2N6PPWhDLt|P!e_@I}H@dKyHB_{Tp+{I;)gqPzS$fZ$i)9dYD(T$DPDl5S) zdOLm|DM;u%M>n4<(pg@mDHhRkeyr}9Y1jfy;STb1|7z4FFas5|5rFR5Ja2ApC)MjsUT(uT z(zCYR#0~ttpZBY3HkLJc>Wa-{OEuJH#O6#^E4oEgsn6Qu>5qPoq`XPAXa-t0X>Glo zS0|ZKRdeLtyg4Lx-!ARSDc3ay`STrG7@z)eY$T%jNgx5N{JaU;SV5>Wl^*Y&Rl7$9 zZ?x5BWt|}sb`GSCiC2XLP>9I{bc4?28F{1Cmkp`5q{rSSsn zCJ2OLZRTVK!|r*Ko)rxRMMb3Jza%Df|JU9zRu_A6FF>+&D9VML=KdjHAw`1GS9|Fh z>n;0kw!$N1l>h#R zx_?`+vqrBrtm%?8ba}I~I7?1@{d}=1xJ__s;b`yT|_7arTQ zqLS0K894xq{V^^bpI_xd>mocpJ5*q5#S1ikUz4y>u_!}4u@OV}_t5hdvO=$YB-%ms z#|S%#S{m2|WxkG@;26l<4aZWdvj@g|!pf+zp$qI z#d(PO4S)2vsn7T**xJA0snb0=`2ZcCdhahQ;?rZH0Bm9H$GrE@=>s$@0_6RwqL4#$ z6bAxWA7$Jx(u0TSVIKDcJV1YfLOE6NQ~c!rQt`mQ3y69c^&3O@fiPp}pb*_(Djo>` zCja}QpFfIZ!Y zYMFn^$pBL6tHzB|Rr5;9a`JlgQYy|H%fA7nzBctLXd!2$cE|(`1C)W>yud&HoF)@B z1bHXnoj?AvnvFafHj|WeEMr+o-te|`dRcK!rxp{BmSuMjKm}!yF;PmIO2o{pw0pT& z9jpQ*JC$3wlgM2M=@Ei8!w=cD!qWY?-#+l0`UEiO|1|DHpq5rtnKq6p$qd@wt3)^> zXXi?+t2!m~fZbEuR_;B|aCd83bQI>Re#9(Ll3!BOz5JxqtifCv=Wr zp$;YD-qo>G;j6qmuAQvi>RJ!53pc+Jc74uo$ttng3{pOe5m)bN`54bVr9=*%m#ZHH z{|coCewKjjDpY;?9*d}166mUQRgr(rpGAHoo{Pn~qxy=Hpys`8gW6||52MCNO1V%z zkpIcR;b`)@8?Oot%GWZE_IA0r_q&S+`0&0NuHfFdqjG$6JkZP%G7VP&cex;9HT<8L zG^PX~Gwua7$skIUvq~zzf8pY^+_IeX3)%l!@NZwtY!8R%&7V$dWbP-r3X`NY>9@Qv zWlp8eK{-JZ{yIUJc^*bBkaTh-Z}P3{#9q>lT+|8WdXu}^GA@CODDF9fkvjINVMEUm z%1=_-HENVKH1jgf`Jy#`@*b%M;Gw^?FZ}0US;UYcEf^lQ-~;?`pi~e4Puk{KLtD0v zyj~aIHz_25ZD#ov9Ru{g6SMB z%$o%bW+`B=J8uWXo<;2~sh~t<`nE&)aKhMmnG;L;s-8lsEYmcS&+HeaSdBsmIYnk& zf7NT5k-V}ze&cm62AQPgE)8_jS)R8j{~v90QR!CVculK6TtVZJH)U#4YAL?mdYV1X zCx*sJ4)x+AX^O%Ymi+>dATdk!_kgL@RP&k%inICRn;UL8-)r>ZS`OoRa*Mh$B+o1u z=Os2;sUK?OB;`kP?$IR6z3`F`*F6|ZZ0y#cJRTOmy!1?^nQ5J7M+uff|P--W$V)V5qr55xxbwgaRi$h;BH)yZT@xvA{ zfAKfp!T7Gi{mG(?%gPa@d~8rn4QmA~jyL&uqQwAI0dTs0<=aqQLQ7nc|iE36KS(D~+E zih!}z&Jz3hq$9q9r^Ggtte;Hr7pp2!+bLc6x;C zsZwsUztF=MKSdaqBSfdm<%J$y&yjH%cyFBa-ng7(EWT5ysm$v#bg-I>-rK=_bY6#< z?w`c@-nk!b>}R=Tef#k$H#-N}hJ%IHKpD(OMPPb+{3?vyNBq~%JQ~m8!n#IyH?{R6 z(;n+u*AB*=L9n@~F}(^2a~**#+g3YqDlmcOLx9c;ZabG>CM|db6d0T*9v8D)nxz)Z zH4HHg%9E&YjPa5M0&Qko-EWntQf#ZeiK^nDkccE23bde4sKY+5R9LS()%Wq)QIpdA zrBH&iH?J9;;DID$54+TyA>A~J{_Aqbj}?7kE+>__rg{DbK(AID;JYBl?lEwoeLU?= z26l()jvod=1q+hP1M7@N@yRI}X)Ru+fT6QBq03!8Z2k~=sg3VpwR|0DeSFJ*!k6CB z4BHgwb-v8?o_Dg%UATZeOBLki%B=IcvqOD1kIeTekD$0&11G5$TFrH@w=cnsL>E>K z_N2ZbM{TWZt4k(56;A|WaAju~+kk`v{3jW>-m>E86|bo=0H0AxQ65?YmeWcPmSiIL z(!A0BP4>j)!tW0dpHUZ|;vuTXtiWQ8c**`B=L5>=dIu z1Ko8M7`sr!1>S5b))Phu^t~naKoyiNs>@FC)Z`@paUu|U1 zop1Tk-B>-_%y4g7XE|m(M&mS!M&NbRh_qMxRx)PV!lix7k;A2Ic@@%U#+>f@ctN48 z3fk5PE7=}gpy4sjx^5m^yty^^SYkn?S8V&WVCq=T#|9P(lOj%1(EDI(=?a^Hg=m4R z3#c`CQI1u~j(qO5XOns~nx7bm%P?CZtXM0@cZZUn^PsJ>26Eoy1bM-bq~rtN_PVku z*hPOVnZ5ejW4zZRv{PK~xePIv{v1lS)FKYtY8hQ$3^zby(EYy9pmfQdq}-(xtOk+9 zL>kYx<_TnOrM)_?qOjHSN;QgK69O7ceJi`|t(8PY-DRm~Ad3ySb2Y~>`J2j4+cOPS z<`|5L>(_#mv3wI6;VbhjjT|Jq}s#5R8Y&IM-&##PNKlf1`69~B!opan& z0fl3G6SG?#x^(ilPl{4#6V+7%bDjcLuq);`xeNHDnk%uz&}2XI3LeDse52;27v0-k z0sb$Ao{Qk&&4#+BJ`-@Ytk-q05Ie!aei${@OTmYlR6Dk|mBG@Y9@kQV^}A_SZN!x7_=aII=I>P&CQyrcsx&B_QyoOTwp+)&$(KS@d$vZe`7^ z^6SQ!LqJ|m-Q?b7H$-h(EAIJ=dZp^q5k8l8QtSxtYKG>NR~IqA-UUHy8>d+)hzA53 zDO<7#glKM(r#N??2aw);CxKUMNvHpK*9Q2Ujb0i1Xo}T^_-VrL%hDG8A8!mN2=1OBX6jC+ZdanT8?Nilug`g$Ab=)O7i*@?ll#VynCZJ z$_$hh>sV}SQ4c4sgq0uCL*Jl6WxGz>;*;_ufn{=+aAFv_Tj{imz#_?6J?UP?lb!Mb zMy0x&m1KRnb@8XVKBb9)KL?)w6w&&4R@?j?9bObWFH>H6l%`bgAddK6A*J-LV=D$T z;7_gfNMMNRg3&mUCgFq)MscM-X@S?RxT4K$7K2Yq&w^QaS^`GmnSMZ@Q~zk#^?4JA z&aP5B;Vi*M>snI+lala@La!xg=4vZh9F%4nQJ&!_tgFuV(qG6AA0?b)afEqnzWbsr z!x~UM6+hWyDU*ti*qd3*tfcCMU8RfuIcb*Ux|1%5k~M9y%NV)izl5f95RIexDFQ_D zCHnihUYQOwQ0{=oSZG+TfqOeeTJ@^)Kc>Cz1Y8l;NVl4BE&tcQn`vv41W{Vc?TnHm z934Iv7a_0cR0w>d_2RbIOJ_WF$yW<+~=u)2O`(>@0LV`2d>%$D%OMd!XSZ52SXPIiFwS8R1rr|G3+oCsU z7c{PPhU9O^Tm2ijnjluWt2X_AvuZ<=`6=YB%Xbt0{p{cdD=MLo&sJC{??%HDgX$%-U> z;dCAzlwP#a+FH7|TU)8(G3jiUptM9KpB#urRCnPRB<;1683YUTC!FKIc2C*op*tM2 z!OjwxHg3ttZN=%RWy{NWCPFj#DJFK5Ul}e%Ik#cc#AWkZ?i?o0O9%XE+uN-l4ESd6 z8h!?d#YAh^J$isD#vAKP%Kij>=2kyKkgm9whc)BWlv;J>l5aaVciE{K6B~XS1wqBu z$5{&OpPcDsod^YM)u(xyV%!xjkX`Cz+uUu>#b!knOi=Ye@VTzMt7?XW9WgB}+l8we zYb#Z8oQVmE>W5#-(LLln9TUQ8h@Fqt55{=s20FRdoZ14v{}e%cg8pYlXA{a*JNIWa zZ$JC<=G&FjAq8UcQY66>Am#5{TicU&I~G}vBnLMKJ=Oeo7Is8R{g95idu%3iA$>?@M+c!9`(6&Lx|?|W^2%y_&|zuMbkHlx0H{(N5+kAZ z8>9A*s+wNDAk4E?aJ8d!bol8<5VrBoIvaX;63TQ3t4DjYNeejwlWyrlFg-T|&FfMbEYyK-#Y$0@5*kqgPh(9#uVMl_5SJ%6%nct91Nqyfpq$d4nxgxzr(} z3MYXv=U5!S4qB8TKRf;5IxQvi9vIbHS`Q;OU)CL1W>J(*Dge*cAB-uek*K8ThVZhQ zY&!>4xR!vAdqo;ftBdA`1xrt()OQH9I%~5jbW`9#Ea||35#=JPd9%L$i*;kdZ6Nmt z^LpSEx&Lb-;)?GM&4EJCpQD1Y{(KpmvZRWA9@Z^>F1C$=PVQ>DEgEF?H;B_c=zdJJ z^{veslxFwgy-#((JadVjjpJd{Fc*{hS`9vHG&Bun5?am8p%zNiQPkz;Z7IQO2rC#; zmGI0^f$o!xwGTfn-9D-)Naz;Pl2cE2WRXUDk~g|KguFiDUCRGK+wjDGns7ZoZ$ka8 zFF*$apF&hWADf;-0S_=~gX=JL_$@4{m9jRA4J86v`;^$wljD4eI!xjfcv(2eAdy3g zgy?F4C>wxs#{4DPwB*k+Y65f{7c_J(_^$f5l^Vr%l#QRAUfdgLL-GJrU|h>j z$-nl{XE^%rSA)l_lEs0x^Enc3e}hMavgxtS{xbzjWTzUU5~J`3kX!$miYPyhF8YSPQ25YpQDmnRVkvjSJI9&R4&imZ~nR>t+C5jFP;ie*HFA z;C<)$>6^$5GT;|fET*0VjVQ}&7bl_YFVHhi6dDP3KqJVGl;hZ`a;i~FqpyLe&hNIu zD(1_3By=LR3~63M0b9F0rs81R-`~k7E=QxEc^hr^M$h)Cv_(ubpb*Z+;tx9#-6jp) zPrmoug9%`Mzd)K7{gulQPDO*MeB8PMhY0I3=mhulqH)wZLM^T&x+NWjzs%MozKfE_ zo0<7li7R*3K#x1qwOb@NB5RC?Ds_C{(3+o+RLSH(f-0-{sTv6OrtWbGW3k@zw*#4r zC;vH@gFuygtN;J~-jqrF_JarPyHcNoRsKIc z_x~IBvW-46{)q0NdiS2sIHBf6vUH+`c{Q9M?9+-fM~=ijn?M%?ApSX!4O|c$F6$AUTa){yE}Q{(FsryB_3yZiv+v_Zg*P zj#5cAkg{8dHF(w-w6k9oc*it3$>Tk|_yLIr(^#~@gWOfSz7S@dw1m95;blJwYjN4z z->vKEeV(`eKx@qzASvek&=I=y(c!1J%XSQ6?ckvd?F+R8i^|!P$f)6`vxy)Xki~xO zkhAM!)aR+%Z(A}UuSwI%s!C=RWeO*PCb=%-G0~K_ut|^E@ zpib=rjDuv#8kK*&)%vmV1tpCM2a}ql1r=gEZ9*O+HN{GLTI&vx)}p7ZOj5~i#kF$k z`?c2292;zrOrf6 z0(U~@S#|Np%!KzZxZk-gB7VItRnEG>`8hSdY!u^&!1XF(1HBl2UmuC~d2%|>jc?e| zfB@8}-Ob0?M|FI$);+?;)>hdHM=Etxu1OW{i3-C)O&YbppHGuHlA=wyK4BX)_cf>i~l>&YHwf4 z^{!>|VzZs*d@%hMl5yPkAxRw&^!RXl+&wGLyJOGx#vQ-D(VtjPb~w_Fdg1+9ql5;o ztPWNqZho1I`d~^~7v}dJ?55Aqt1Df|Y5)ZYd5%q?AEF~`qW#XIEZ02*IC(Y*(`vLg zP%l5f4anTw4Zsxar%i?H#14X5C#Q)xJGZUs@b0U+)5t6cD&Xb?vf3y}Z`$Q5xIUK+yAf4a z9eQTJAK_wi(hzmUpY_I?D_^c%thsQVX(h8CC~(!}Q$4u&@h;cSC+gSJz^hWomjK{s zNp=zw<_xpDZQXfGBNoyMdd(t$(kBjeYWm1+i)O9mj4jK2wwp1zuT8VqNU9dd8xxOg ztJI~5d*tWw6t3ekUR})0G1WCWLk8Q1kTXASUA6U}h1eqaNXfsc2em5tEFzO3(^kh1 z5o8{%$57a&nrIoGJVEerK(FU0?-$j4h>Ph2@n%v+T59d)u;=ud^9{9U=c0-uRJGCU zL^c!qbTS3vmOeZ0uqBL3W%C^9x~;&1SOq2E6PYzOdx3WV1ZRuI*DdV_?JW>Cja;@X_p(CNJ021H6gPg#7?c3AUAjHhh=>sN=uRG`(*&E| zG%3i&r1r0Ruy5gXMVQ`o1k<|fEZ5JAw<#3BtCaym{VNJLn=go~?gRpDO$Yx}FsB#B z@P)LusBI=6KaM$(JqONmxxh08E+kfNne8L^8FiX?kRi7^F1DA%l^M8r;)FC6lGsLf zBW&Y)6A0#qxE%`MtLU5EHHs^MJu2S$A`1}+;ztoq{y28mzR{=9OUSJs^K}`<7|#yf z^iAD}u>HxXm`US$D#ffvt^)cvhl_@`EVNfVHu9yxlTK!G`mEx2-REj?y&B zF7;WRJ#+JLvPmLst0&7J_zyB~md`2Xgo#IwEfvn;b7hTK<>y*9jkOX?;hUPoSO()Yaeu8y-qrqxr7;5H7p(M_PT@@DgB)Dn$o|7_i1on^fe>9q~=5M!;W z%`&~~!noU22+&V5gsMX5O`W?Ivr4RJ_0JDey5LTDNRj{8af$5aH3u zvQv1IF>K=mZ2NsG!x1r5;pf^diIRprr9@+<7sp6WpKjB)N@v@% zZIJTAgfn^9Hi>DkZMA^}13=kPqaS?oqx(_dFu~?;&KE*SK$n#zMm@y!gZukaJqK+HUTZFJ`YcLpvYZo`~oGHS_K%VQuu9+`um*A((%pC0VTbXIWYYp6w|N4!B~e|E_Bp6$>tAy8^Tf=blrGOOy0*j+z0c zdEBvOxlbEG@?*a1{nGQZ=gZouyPG8yQcKyLm$@>JjLwdkZj!LJK;GrAw!X3}0)9r; z#F8FsC4k9>fL_I`$m{u=CjFt}XV@YwAZh6Yn}zj))lLd1&arl-XWBv(FE5a@SJiXd z6F=oc*4gQo+1mWQz0v9SHc_}us)=$AVac=uyDN2iNTImLe0&Zwf+N46{lWaptYX$3 zRi!IGehPqWs@hC@pJCdM5r;$5+TH}dRgYAW&oVylY2FXfF*>Qt@?u4d!wbfOWOqkb8IoF=aFEl zY*SX-;O$vo8?qVh_jV;=p>Sb~i)`Z|Q5}t|M!rO)^Vd&$XY)>|guElpMvrUZm%lja zvh^QX<4h{mv3i{Qw@uT2!g&I!012l}f6o3H3i|S%yrtkHyI8Ew$P?zF70#xgbt+EG zRs$i3NSZo3A%ugah!jqI+rCZ9g(tz@2H&5oVRxb43%l>$kjDJkZNb!!sUz2yWvAZA zS@x_)pw+!{1g;0T#j+Ii))t*Or+hyYiW~FH>(5@(vwDKPZ>2lZY^>*8Q*Z^%9QdYV z`Ce$8E`wkl*(=Bcyv z{#*$8$Fr`v{XmzKA%VCv4Th{YnFmG17Yx?cN$KDpIXBg|-LtEleWtB)D184~9gTel zrVs`qHl~TdAYOaMuU^iEocPr|9Rqx@eDSU=7mMBNk$PwqxG^w?to%JqOPgkdfi5A3 z9OZbRkK^+8g4YT4kCV5P0VYiYaK zqpRQMTWFMWlIN1Z)jLkN*6&g)Qz%s=6;nfH?giN$wZ>CkpRkUtxPbO2k;yUnCttQU zi{_|V9ZvhpPD(+yy%)IJYmD`I@URpmO7xG-sn?kGW`fwPEu!QmWe?pX7H(E5Sy~YZ zpH{m1=-#Fs`eItpHyEJ^V>^xTyDX@9{^TlI=G3CNi-m!OP7+!vn@>j!)IQC>LEFLc zy#yx$r(Wa5$K|Jz!X1ip{*)>;F(Wtf)xN5}L9xT{26>)I8EbG;oke=h<%JELcp>^+4V%Z9gP;;3*bd?=Ois=2WjR5JlKz)mz%Dn-^v{ zjD6hL`!J;EJZXg&MEGHRx4aX_un<*xuygatsrsL5rG#y!!;i)0>6}CA>yJx6O(|~v zxYp9<7Z5P2hB0^GC!8}Lh}!f8 zfTO{9S`w1&npwj(btIWqUaM_l6Z*+4TW5dAk25A&uDaL6i(HC5znT@A*y*EI21M-Am`#Zb6{%S=D>x$@9G?Q7R{m56e4 zTCS>G0e4A9uwQv5%zlVxZo{1Sruput9p>KbPq|v#9TQ(32IE>6pJmX~ds%nxn`y_S zsA#e`a}t!LmH6G>a;wR@tc5RotYh6gYjWvz&Q9`eTNV07+?j1qUrU0GF>uE+r#4h` zkMBnE6GTAr3-RTey6BH>trO3$e{}HX?|SjS5eS^80 z%he#w+dNM4*Kt!0a>s%vr6|F*JrjBa1M3(b+V-m4jL}1YnH<_ zWp8GorPeRXYdhR6eRZ5m={e=%nYRk^&7Wd#Alt^GD`zzKH5tOf8`qgV)dG5pyV$K^ zSqo{-=u4;%@W-c;N#asvOf~R?6&b&6${9A73^GqZA`F?EYU+0WIVBU$1pZ?ReX87Q zxM+o%_CRv^x{+AadBnXf^74l5(>@rse-NSu;x-6PrxGf+ps-IzZS(kav5?TL<(g=o zvOazzOulQVuqN|73Q-$UT|YoO4g z8&FO%)ZKVrq!V{BZnc5ir(&H6(R&M(0*LpqPb)h;0>uVECXQ8AGY>vkF>aQ8|7NnA z7!=yJ8P6MZB?09-pvT-**vvELy5@#Lj%=0@w^~q2LsoifkX*sBh$%$cq7f@j63<%e zqwlwlu3ns>68jy%$E5g{rHO%;$<{sWJON@hUBsZ~EOSHBC#O1iFq94-wkBBmC2?QX z(yp^`Jm@OaC_J00$*?EYn;z{`Fz<>RkMc|Ej}z|n6U%TgiQjXs*{(vX_;c=0haz`v zKkTn2z1c7fpG6`u(4K*}hw%?tpCM(+YfrNGO@D%sUvVGmB3AWxEu?sFS&9`1Lz;E) zR`7!`;wd-&rsQv_es9wEI3j`fzr+=RZFnf&`%H5<_m{hz3HGA=IQA8b$QYKTPi&=g z-zY2lSd;XB zd`^H?Bdkc)u=tA^gy^eSXnU82-@M!x!$(V$#K(}VCeiBd^doHLn`HrCZ$6KFPgvkW z<7VE4Pc=o3w_F%>C;lvp<$pitTMFrX`vO{3oBy_Lj@J*T#0t5rAzj|7@$BWe*f$3u zzdo}4L?PZ#zz`q!{DsuwmlWTc?|q`cjd<916SDf_ZugC>`Q0sdju85jg|#R+)eUZ; zNWINak9-5m-(!~w&-?~Lp3L>T#h!L(rd8K02&hnUX}Q(wVxBT+2~F?s%7|> z$Sny4#NWczKL7C@yM#Xm&{oagJZjm&>TJkB^*})~F8c2%>O5C~jci_%zWX1e;SM+7 z?`bkC9YE^2A|j;$8MSll-T9p%tW|dH@nGITwn*%-efog&#y{=X_Me?03f}-rSSU`% z{UD(8zk9a>DhFPi+`lsf#Tf7d$laOepX``f=D>v`KS>1a0Q6s`0qO%wgSDpaI0t~xKvV`CDABxdkb57X5R`p2J%GOJ9e|>b$=LY_(?DB_kQ*|I zK|Ve>RR3}!(=1R4g6{-EPVps|GU;^WaLEI{>tXW7o^S`sL3Tg(6?p>fMfIF7WiVIo z`0w2jTz(89yVQc}WZbU~@IaiKMQ7?ZEnPy`pEo`=nD&)s9fCV$gMZK)v&U8-C) zqQQel>Q9)TTIE_?|I9g1s03$K>!z@Ua3@NnfhSM2)-)$p$oz4JeNcSyRwa}DFw||O zg#3Wj3Wrlz3I~S;X29xeS!BOC9Ajl6!s*KEwXnA5tzZ-6DM!+wx%i^>!HO=Z7%0jW~rZ}xUgF?{T#NIoa%Z{iv zbO(=bT@>Q`PkWL011KkY|C-9l)`=t3rnrzu%D^@Cn*+f6G z@`KeI1|d6=5Ih&6oSyC8@}@q~X=NX$-2*(Hi%o@O&qAH{MG%$BJUNr8>5?`*U&277 zHdjS;iWKD+#=g|jm`m;%w2XI1nXGU(-IfJU50~d`Zyz`PNvASh*m)B?)M4pWKscMo zXdQ|OW~AQcG%DpPlYp(VWZR;7$&n}8BH`%yCjA~>lqrJ>-HyXzX-5>Zwe)Z~kLLFp zpM@ld7@$iypkpc1EOlX8{OZKB7mDqz8?+VTr`qEI%UX5E_=*I96dm=YWA-)Qn$MKM z@$RmO55co!0ljiUE2mBAA=1mA0fNX+a;v)FGT6xYE~+|sGD0C^@4t}D#Dtp%Z40(VR3JR zAoLG?RD-9ael!ll0kiQ+oN}m{9F^vSLG`l}p$<|+H4V@>trNH1hybfi7uWw*pKo}V zadhLWWxVDYqn;N;XbJXi4!oGC+F}0CQSfO5m;Qlsmp(j$$-eVm7}J$q%7rRnMQ5OpWtk4IcvRwC*G`tBq5owZ$!$hG@Nt z^CzDepz<&0zm_eGB$=NO3B_GZjj@a)^~&RV#_vh-5?p>OeWK&RZ!!Df)*n3U?M;x( zAn%i?c0Z!!tu4*Dt@ev_IxEin>Xuavg)_bsNSk-uKb%Lz@4~p>>a+L%2(@4aAx58` z|2Znhc}zO?#QxQs=X#j{!!CH2r2k7TT)aV|(`qP)I=yOR<>8>7RwHO*0W8ij!ygw5 zg=|KONe$iMvV32&YJ=h4s~r$Uzwph+^Mh^2_P84|i+{L)!*mNOa~owoTr=b{7}ZyP z+MVY59-H4cdxSRP+;WB>N!({uZtqFV_aAjC$@4U}^Ma36_Q#Lk@y;WwrFWOioBIaM z-v8lU*gx1c5be_(S&YEQE!NZ2+U+OunynGn_o9$SwS0g{XmbA=(k6H z+zY$8uKk;x)t`qO(o)}y2weMYgQO%6&Lwfl$Cw*Y&x00;*zqN1W$AcjLW2tMyCNnz zVE!z3;4F;TzBBtbfxQdho-w*cYPnS~>`aq2b;FqNB*o@CWnRKllkg|1?S82vzsqAB zq3fr7_scOj#&@)$LO}jz81$=>3QUljzR&mwU?N9yVU`?}Ke&B2JbSIR$M#T9l?vbL zRm{o6*ri-_@%8#7gq*`sFerP;^QZVYz7}{;PXQ0=>8TD@;=TaO`33U@`ofUrJ>O{^@3;jCWC{>}d1$6s%mn(=usQU4ODk~`&9x$i_V}_iLidJZB{+L%= z+UrcEk6@4i8AZ(|bJHjF-zD(<;?ximLa#a=FL`(ge=SL;cac=hSJ8X&yK+yi%3X&l z8+?9QeWlv$qTqxtHF)Ubn=4};Bc0>{J^rk=85m+wO_R`q&t8`&R*Lx17|B=DJy1+6 z?G4ncQzLnRx^;M-vw6T}$4KNf%q6DzK>8&+Lfn!R>ll)DnKxx-AJN1Zzj}n|lp&hn ziIj^n&HyzLM3NsnvC`VdL&&YDs%(jRz9+&!`ioYs!6)&Rv4TD7pB`$LjdwG8JJ8}G zUeES|wcEErJ6ilf7WuHGMb>={TOsjI{8<)YRFj$tdnew9Hx$@E^y`qpjCMwaALI$4)m5xu2gPkNxqJD%qnV|+J>oS@kLT>{Jn9>}y1P$1)QqGy zsqLRsS5w%pDsgmvDdaTuuQxMrEH;U~+54Zb+By|YvTi><|^6$o> zt9u6mr8dGk#OYE#+v{Ga$Ay=ql!dR8eAen0H#34{Z8Y^>#Tq&Pj!Et~mzr_Za(d~) zl)q8T&5pZP%|af|)3<`|B%FRXvtQZ_L7BmnL5S)a#up$r;Z~+hyXcQ#)*P0PK)Sa&xu%n`wAW-Eb z&hb|uY^*L}A|nGt^|cHM1RQD(1opSc*M#*oeeIe93eh|~@Q#1AAPETHTLeAeNvr81Jn_E8=9GmmH|x~;bPQmW0?`qI<2HDQu7!JRx| zT$=3m!{i|5x#QB7Y1x!1jf7c(?D)>2>R45?tzp$$7f~1ySVSaFg+CVe&ATfYVzOfm z2BUdPB>N`tal=t|zVtgjE1?j45>~e9D`fv1iB{;CL zI42@1un0erJeU{ASHwF5_XFxbA?n|4;Qzbrf4Tf`pZI%u{J*;%!aAe7&sUoYJ3Do- zu&`FX7j}4B<1N61cY86=)OT-gzPFAhm<}^ME$VRK3aH4-6C58- zN(g^`FnK;$K|w;AjX3NM!b!-;B*jk4rv-ZU^z_g^-bH*}{PjdzK4eY2`J1S~*+Myu zjjBw{%oDoK&JANP^V?|^6*MEm!?EdXwyjFv78VvbwIrmZBN#ef5}mi1UCHTaXz-7Bq5r93dKb2|rbeMc#p2ZRKAmWq2aLk` z?V0UUFw5(NlEdvPXRKF|Ac_0+4dw-lLnMw?ejS&K;b<3?>?bMvK9Sw*GAYcL@q zO3!Lu&#y8SYltt63_wE4DD!m#+rsk4GZvpKF3#eiUdEtn_G!~m0c-u)J)J9LywwdH zpWr9~kI%7Fn^>SE;qW|5N;ym)DP7bK6km`bk-SZ8Aiu%?w@ot$2+yz2VfY_f zr1|hpj#KluxZ34@1e3T*v)aE&y1CjIowZB&jf)7O%mdKCdDZqOrPGja)oA>mPp1@ z=v~L8Gj5-HFF!xdFZVb~Aa$R2%RA+9e+Lq6p@De>eN{wCPClmlKHpSWUm?wEvqV8c z@V6?WYP}C`D1=M$v}zRXHkXOqqtQ`G2|2k0pLao03JP&L-4>F?n;V?0T&(k>8AB$s zX#$OAJ^wx)sR_|ll)_l1NW)|aG$%O{Ci#WX%UsU*%fiJh$DLQ(^|JWHJyZ^lv(&iw z5{o?+*T(OMOm|I0Tjh{$nNsTGN1B@-ji_iCV+bGjuQ#YpPk)bw7!d=5d}d}Q9zxXd z#i_P`aImp`2U*!Ug$?-fFsGTm{7`*2}-hqJV z7>mQHcPPj_W_Ep8Qqoa{l*y(k(BEx)ze2CX75S%4OI=_V1P|f+R(y;26FWUNHntxZ zil6NL`T($PH;R<`Xq>ItCEo)t$PR|V5U0?p{e8E5m_7E``S>`fNk>B?{UnwyBQXN! z{(jH4$LD*EUu(6@bb4*$t;i%9QhzJ=c^6{`$44Cqj=&0Ah#Aqsw&{kF<-Qr$^%P;- z*c`6Quj%^~h##Kam0hYPDH%SpD6dQYr${D24BuTCpxERg%)BWB);<{9Ji5F0#7Z{Z zVCjDIe)$$Xrj>xdM6huN7P64JXSrW$!! zP7DA8#1e{K&{#WDE@Y6gur>-`A$>7HczY?T+zGE{O7#p>^JTHdOy; z#8kejiCq1l$9)2^W?H#kd;W$w=Ht!cFp%VJ;)w=3xaf2NAe6V=1Fmu$bG!Dm* zK1Vnz{BQO>>7GX$mqHC;~`*(~Y!P<$Wpk7NAL zT#;7y6<=Mcy4;7_N_U}&H8=d&s%c%SQxkV@+_5_O_g<33x{aH z*}~nHu*Z8N3De8EgU~F>H@CNYnN6F?2i|v;tJ;Ao6yya=B#Tc+DpQE5tkz|)_;`ls z{KW~f*df9Yp%DZJ)X7zTNW0AvjL`k~n7Kk&wKpy0ywZ+n=9 z#K3%p1_mUU$nz87qpaz=noLeRFo45o+-!bHnAiqORcvs>&7pe42jZ-Y*!Xzp;$Y1| z6C<=K&Y6LXUr;jP-5mjcp?{qWlHh9W_Y<|QMEyR4z0Z%$PvZQVGN&fW#?oWfpTdie z-fPsjIp;@sAj-u9L*(61gxD?x`8Q{EQ<&+}8X7I*Y0N{YyaxS{yH|EQ{ZP5M=F78% zf=QTi%Id97VPnLB7M7>oU{tvm)c#6BVRLh;V`C-JJAX;rC!W)`5&!4CZdWqj+sEV0F8(LxxVd`v z%BDTN>qEy9>q_9kL?*RnW49>SA&?}d@_>Ch@HxMhn zIzPBk4wdc>&&w>Hn~m2?se@c@jWaHtfVF0klr^m%viTeoL1PTfY@!(C@w!W`i4?}JDJR~HT z(MUdSrYPu+zT`@|K2Aw*?{C44#D7n5w4WLPxhIb>H6A(H;|9O$Np6?-H6Q<7Tr>*H zy~?okyiE#HI*nUHRDd=mutQ99TV_bEl^@|7N{ym{AdwXVlAQ%bViQ|e;6!oZ&5>aY z*V*-U-CE;S#OGTfSZK&_S0AP6#L2uW(qQzTn?4v_v$pV`m_UzrSDBZqCSV+cM^p!sAqJbAikz*U3{n4Yf4TktY%ldt$* zK!#O{ytu78ZsC*iIrX!bbn=@ zKRhw;MuezVOc8aXz@<_EU%$0-)TQHMRe-}`p;9VsTZ>hFXCh?^FA~pWE@R)bR>BcC66bBq%#UV zik{2aQsUyVUGM2#ai2kkplpjbv0L&>2?vvz4RmM{nJU4%`-=#lZ*}9Un7>YK# z^Ak_n2!#Ekg2ZsN>oVuvBN4}@(UFjltS@Gyu57P(@c?(=(WEapL+Ix#4PzM~0>y1W zW>{pA*?PR^_gcW<{ZXx#osU`V*9V#hF*X+w=(G;qV4TXAy2Oqcd>DBO#J3*D#KdzcM5(vt2+W^g(y+UVa>At-6ryTt_shomVr zGBiF6+1s6h>RB1_!|G24${w@#B;cP>SzH)PN=dOrS|5z3*~C2NDkld`bbJkn zLO8D)g?q+q-=6HRz%$I|JSHQf&)L167}`H41(+=3kHh}h7tG|bN>2eG-hV?rQGtxc zdNr8V>-;BcUo(ZgOI7Og4#{U#`%hXTl9LWeA#_6K_e=Y~aW~gjd;=f-BmBRaKR+9i ze8`*(5KG&CV?S3!`H($aoCehYCi?z1L^-gf=|C1P|0MpG1UaxHTOgClA5(!1NXatVf0cyxKtCpu%dL{a|HuwGkWMIHTEaS* zeBD37gB!$GywRf|rSO$iebsqH_SM2xs2mfu|HfDFUvYb2esbP_g^Ot!utY*<6#j|I z|HfOmVA7C|a9F3f4FA)HN@7HHH8t`m8JU3QA#HjQN2-5Cj&)&zX+8q}GzB(+|7pRx zSU18+>*_kasMO!g?mz8r4fW$>aYEpm`29~Uq+hkVLy6Oh{)5@`FJ?TSUz-15mimhs zIrc~7KbRlCu+H(keQ){)bIM=Lqy!C^|6sl#{KD+{WNz~hX8NyqM2g>n@n6hBUzi`S z{;d5I>j-{fPVt*F_`e6pRB*9f-10BxpfAkyz@-EKVn+CjxlhjVU(8Tnu?~o0#J`yF z{$dWzK2ZG!GxA@|Uvg0;y?Ls=T4-p0#99c~2hZ^%Q8*xP} zDJe+=4TTTm!CkjMH<%=q z;B%GD%gZym?)}!<-BO;vebLoE1el#jXWQ2j1^=<)LTXK0X^R4Jt4mGr)!mr%-`K+d ztQGfpK5eIvi76>#$+VgSd<^m?lc}Y!r7%1W4`&+7t=^$DS`43+Oe9Qsb;rjJrwg=c z-|h2+5pY+67o6y&4uY-RF9Z1`dh5+=A>B&)*Lk>KJudJ}#lHmy-@K%!GfuAv&M%46 zIjxEdOg^5Tp6mx7W-Eovp*Oa91cMPHT98$iWWHA9hsOc2>gNiSKKhVxf4C|;`#-g( zht1A>jb%yHDs>X7yr1`3VM|-OY|?!$%8CNsttIp-SFpAYZRe7K)wjNeS{`1guz1|+ ztk!Elz$0t*evukd)WgCU}6T%O7O{r&8a5*ajxLyabUBmLDX}U4wI~nwO)T&3!Shnc+fygY>y1&P&cYai9)C0CiQPHthjKyHf*uI1o1)a>fxdp7YAM$pw zU_v73A&rVMCZxzuCfrzlV!x|ptzdYP1Qj3-B}PhrIG01Se^09|2`B|dS5cYoE>j5U zE|C`dMvY|ah+n?8D%0Jnn@ZXA>~re4aC`2JjNf>O{*vF=2!CIh@+BzLF%_?bTi21x ziZ+(QKR1sa1bT(wndcrcA2u%Ew!U1fx$F$zXrx#~h9N#+k2Vb=ZBE%M!dYQqVUM#D z7gciau){C&MhAjNLL^HFPF;6%XC^@}RTWK`{dCOMv^MOj{_u*n-hMi0JKOE^XzY;N zG5cUZe|y(_O>~TO+*h5aE#37JcwN$!#$l^0pgerEyQt|ZFSq?twtT?lqHW1s&v!Zw zIDpQ|ExRcgePgu*dfB8BMam3~lVa?4nzG)9zU> zAtK*w{yg0 zWD>^DXhuSzx6r}y!bI7@bp*BAX|!v_iHL}BEQD9*A*N#zesaIgB`CrlhI_xl4;PN9 z@pD@g>VDeoMKNa?fI`4K4X@6`WMts%mc)S~5X#PB!LG$_Ap)Qp25%tnVwaX`GRc17 zynSW6@hIwX2ZF_N2I7KP&;|fTJwNW3DA43l+me8=x6T&_9@b zbgFp#xCVLoxW5!&dT;1R64TMx_P1padH}iEUX7kHMy94x{PNXahxDI$!PBuNMa9_( z#6!^%`ua#GGZb-gfyNta9M#7nFmoT7l4&J}Uqf=CcO?Tm&NL@v>j}n_6-L9MJnQry zqlT-{5#f7F*Sqma&H_|a)UfdH0;z+2BgP-?&rA;eb}7QSg^0!U(bk(_&iaWs+)nC> z+gkip0+JAQrc;S%2sF$=haNheXPaHRwT^Utm~6xRB^Aju8dl0Z|Hje?dXGA;srhue08o83>^tWRC?dadoU3t^Gg*ZrlG9Cnx2 z>FH-dVR7!eN{GZn28R%ydTW7k)-qT2EgvE^&kHHWJy!= z#E!GhyAC-9667PbO)sI|zTT*JLag#Uw5M)9L4J zlN+DbTP4aCh45%xg8S5;q?k__jL%iMx4ItN?kR!&Dq;?T-cK{=eUN#25CAX}^e`2} zjeBkmx4l%Yb|;a}_moXxp3vseiyJKRD`3Rom~D3z4UJ$ZG|E1GB^V&W{NLv{Nn->q z#WfszwVi5?-@Yg@yr1QU-$D zWOij*?RGN^Z66=NA(&ShyslT%0?#Pi$ITu^xb)oZU5SRhGeCGS;2#sBNW+1T5IjeLm3&b8Bl_*s@8pGI->W_7g+HcvH>pQPO!uA- zv%XztoJ?;%FlBVDuJT$Q&@6Y@;*Ux&dJ98DS-LOs1JT#1=n*LGKUQ^;k?Lig2}y?D zfwe`}uoC3?!pt;o)LeE8EH{1n!S<>D#Q82xG=qnHnaFI)$Xd5Q)pgC1#%UaW-@Gap zU4DuwWZQY)sNux(GTg_6y1SMYt~cd=f6q2SWUx1k6vD5nM?;cN-%|ZFIz~S>SBl#; zMwI|dKuisKdxVvGZgt9}dfcEWuMT9QW%*K$Ad~Ou6E-f zQxMA2JH&!7{RP7{{cTG@`yLG4bc`F1oF)Ti(RIUHPL?z?#0Y~hUo`6bc<5pRzg+xv zll`u!Mu$U7V83)2C+U3!m>wOiU(=RasUS=#3>~ugxrI4wO&>*}d`msoFcyNSn$hw^Jl5;wg$L5qH01^t@|XCB>;aWh9$G zE?3_*=r$A_1U|O$4iP1}4MWdusXFiV^8~mkBKCtFN}RxqO}$<(SKmsxtH7YwyVj9> zPCW`aDC`ff2Gc;=XHg(gIcl7Le@e;_m@T`%*$Hf7XW4FD-!p9N!OEH<;x6 zka%$4Me}<~_d)aSpUm3NuP*mC*>*!K?xdCK_6r2G^a2L_cF7d-LP?I7kQo_;pP;6D z=_U?`%SG@3cL@Pji3Cx2e@yp<=mf`-r2b3a>=UOOszGilfIRIW_U8kTSm&%Ih=gUp z9wSt>QyorbhbFrB5{L9Bv10(7K-42@WFybAtP|%Nt=8!d8B~+?lVn0fR$=Fbxu(gv zc%PLU&!76m<1&}^urLuJRUC8r+Q?)v>3v@pHyOt-zm&mRqZwp(yIqevwg>9m&CEh! z7?{%pOt0fYj0tYH_oyZkWS9!R*O$fWBN)qMm%hH=ViU!_e%XnM$-`W+TM+v8_h*0P z^kWvp1K&?=JB<$(2*rb4LWGN&eK&nL?4Dw!)7T|Cfnu~K0ZeTuKf)1cCf% zM|fDd^wA{m#qZT5%lpOrw#_{W*5*>cKvzT{O7Ni1%Sy>;k6*~^427AJ6aae-a>6W} z#s8^3{HB>Sz9CH`EUXH-b#6UFrLg&X9Dp0N!WlAfiaN+&M-Dj6vr@CIH8@Vg*gi=Ne4s@IEKk5-Zn+;=u#Zax2X$FU zuX&G4gG~-a?PIQC6Z|$K7!59ll z42DDf_?zAUzHQh1jdxBIL4#cKkJ?mvr&a=1X_wwvufpfgySeXu(=GA@hR(7^M)rTY zarnDuYnFeV-?MM8%K(|Yx3Vy~$LH^f=S_~zxbI1n_+)P4f@_kZ*t?`GkzsvOw$#xF z=yL(%vcSN(p6`Qx_p)7osL7C)^moD`pW1q_gxxf+R*Fl54}=SiV0uWp7n866st3gh zU98-1DK{vtQX(>_efeGYZE1BKkI}lf){fyj=2Z8>*tN0T0h>LT>;qHDN{52bD!ehb+xsE;QH6!jc<%nrW~T<^=B4TwDt_Pp#p9Y zt&Eha)$5C|u1tz?e!-NKH7b(MB`nKlLImPP;W96j;wDw+illii!rZJ+nwM92Zr90` zoI!qvAW=hFyseUAROPKlQpsV)K3O4`H2QHx=&-DYGrX3nrmKlCiRYe)YTv zQ;?IxKT2&YY3LmYDERQtDz8;9jmIV}vR00Z>e#K{s;!m9ka%NHy{$Uz<5e;OXHxMg zROblbZIR8P?+-k8Nh-l=4{ih#m}pHx%0Uv?l&KPWqNrYhN>q-W#j(sv z7jVCH2%kHs5aHKvDzQ_+JxegU=V=MaKO8L9U$1|ckz4>k>J69z3g+5f2P$vz6 zCW`Xb0Qh-aZQqeZakf>4#Ez!)9Dp zOKRDR)=8N-8q;C$Iknh}f!*jydCHPs3_ub^L26egRxHJE3q<(DN1F)UQ~XM}rZ{YR zaxAjSgyc}H$7p`aLSCE*Oh5y)l~}avYgqb3@>sk7C?ib3A0xCb(2%i1;3AXV{zPIl zi92eBw0AqndfvW;4TIHN9pdT+;_ahyAkm`A4vKTD4KmnCGE92Q&;A%Mk!+nMcB1Zp zYXf=2j7{~7uifgn4OP=&q-s^6R>L|3S%GDj-}D1^p(jb6Btb@m_gQhFxOR^pPTdRu z&XA5BywqSWYLagE{d}g6e3@|%@KCh5)j?%)f287*8$BLn?x&~7Sq2xnq;n06EI(#_ z2)=seV#Riydbo3L)%(1TsY=UtJ($WQrdH(!O1VQCbl)SuF&U53u%Qwd$q3u*3n^V&as)Z;J|3YVbTWHS3bM5;Y45 zrx*NyhNq*$AQ)24N5fy@i@+i?00;l!rEGgf=TA8{nhXTJlM zBI3Zic{+=Pec^7~5>$FfuNYZm3eA?x^E&>A$$B&~ImN~G2F*X`dpBv)hC)`EkJ%l4 zivk(7fS22jpO8cDtbOz44up2Zi2CUEf;S6BBMCcv^DjtM)fMuGvt`pWfL!lz7Z#p| zDpUC@-CWP;RFXHroS?J3{)XR+ELnXfjut^Za^HfjDgf74P-ov^iE`$F#ci5@=?@tA zStGLFJv*)_#l)=1&M{H%=;`^?*Kla$+Y(bzug=vDh7P~?4^l2yYAa9N=GJT;%cASy zys=Bd=8wnF#M`YvgkRp`52m4pN#iQtSLN8A?8FCnGAnZT^4hpP8d##&%_+F4`X_|e zP*7o2Q~`5OXl&HmRO{_xQxm&*+IZlHpq$9-G*#cp{OvHXU~F)6Rf#d9@f7TS9^2&A zK{lc63Ej&TLtkXJW}}2O+ZM;?8SECVBUsdI_x*tTAhF=C@h3-!ZEqoRNbHg*XgOWe zK_zyvgp} zNIi|M&RFej)x19xon$%-%TZ=?sNW?i2>hm9yKJ*FyZjyV#ePK{%if_+;|!_>`})~u z#j069Ns%-x>hQO4)D^c{If+gECkOdPHmfz&nGmo^mnZ-eX4@a! z%jSuU*r~8`uh|}t%UWkS48{q{w5&G_PMb2N5dqn)tPmD2)6G>ZW)?K+P}e~khtp$e zNlk0N<{+xvb8?Z)K>B`YgV;k!XS`*uXrezu6W=W!+B}YJ(vaj&dbl-7?9g6+n34(M z5%lR~eP4^V_wl24Qml9e=MNtHIzqOln;>8tyi7LJWJ2+^Q+tpX*WHe9AR`k|YdyBQ z^Llic^v!(wmq6a1V$qmkJQxBd0R?)#1Iu;h$Z*8Z(9Mn>@#69=8gbyK1%F25vm*z> z8@n{NZz5SQ-u0iA_oprCL8!B%mBCRL7k_I9z8p&Ms&?7x8SaJLTSm1}sP!5-Ib{&! zV`5-iM*d!l^!z4$e6>w1+L}L^uFpG}A;HoZ;m6NsH8(NT0BvaLsQ$aDPvmGtM(xFV zwaIYz4B+Mjm+DZT*nl*`6L8 z+cgYM4pAQyA}hMbu#`@x9Sq~#$Zh2tHreDnJ?S=jLwv-|(X)6_yEnum$~nVHiOQH- zJx!vxpPE6&MbNp!YU8>5HLE_#nUSGh(;x>A>qxz544$#2)2nX!VRpnI;VNXaV%=sf zoe50GirHDQW_ulZMjIPLh6o>`mJJmT6d9-I1C0{iYx=tlsoGNG8nf=oLQG>W-05Q4 zA0&8uPK(rMtrEV88Xk3qo4xp(8|b>`yztG0@6hLkW9%%5UzQ$b=Yla-3<<3a`)GU) zm$?$C#R`r2LG1W*J>6y*x|dAvPLBcT7Fe%i>(_&WrU^W;EcqU5%HCqfU9p+NhF zNWv!i<5jjFVguS{5TsFqq9KfSn-Sb)CWDb+eL%dM?;Khrb#QU?{$q!B3_1NzU|$xu zL>zWINSh9s1IQ#*GCv+v`EPON-cQ@Ax7_4H?(S*!4SRd*)h@r6FSjIaIXDi#M0}1E zdVSqt(l4*n1Tu@$PknGNi1lI1=v}a|aaEriET;r13=I(Rk}XBaDT2YR!&!^n6MeW%o_ zG`55UMs_||@|FmfA`$2TPgh`(($7YVvK;VasHn(b<2IwQ0zyW#XC~=237Jp>&Qudw zjD0j9H`Q)m50s_}D#!j|1v?>Kn^D5t{4pzBbVSNhQ-UaTfJ9=|b{N)L3ol4ScqoC) z^lBw@>3XB#+YHPo{_c!%LXH@uo&V~W?dh(^yhIav|Ga6F1+2ZO416O7ZK;oGf<4tv z|7yF@y4Gui$!a#kqD*=|-O>FF1y5Vp{Nr}&aoDuYCA~ve3Z0e}o(#vcT=n<&tRyWO zt7kXt4W8MvsU03<@0~hyF(Bs>Pbr$Khx(E7%`c(WbH#ml;|l#yB6`X*sA)@mPv5;N&>d)^f67%j`1xI}M2udZ&E z${`wl?jT!WI#=Fm1GVz=6*wbM7NNf%HbJbq%5HUNmGP1e^Bz|vo|`ft%X#3g|C`b; z2AegWsaTK?zzASz<4NY44Sy(MI*N&qck(wE9RyT|akC=9%pq`YhH#yUEL)H{tZMYY zfXD$XW2J!`4ZO7!PD!YJB0|`U)ZArguwKj{i#Yc4AXi92ge=tB*$C9RdHA5;AyuR& z)X{T4FtfuGh;R}S zTZf|Lj(X8{M)Q^YiUlIS0W;J|NND%m6HXcm+hI~q1T~%o!^$wl%^#V}!`f?QVAqQ& z?&0`y`!cxZ&Cus&(9E_+{FV9{2FNlrj%!mAS=+j!zO%i#dc;(G%{1!m`eRY){ zr9`AZRlK)DU2U(pi4{VOV%QK~!TsriU$tBv#_G1~P~*^U=0v*W??~s2yDKf?1Szlt zdtSRovQ)+?C8LfK&N^i6evM7eW}w@%8mf%tK_#f>TsO2-8VpeD{XE=& z-R#%jOSY;8=`DanDn4ZTGI4jG+Nh^71T`59uOuzFGKyLnNUc%>5AxoNTyMYBmP)a>gPKG1}5p zR)Q4~j+hptdH0%r#_k#>=-Z^F)kCVTn20Er;-*-01#dAn3%abFh)_uc7}c$w=NENnZoAyQhBHFiZE;pEfOeWf&OkiJcZX71sJYT37aIsMZY@4`d^;Jqo1LPQX@ zo?(H%sGzbYr6Isx83i)#jE}f0t4me4at@H#`QDqd)BW!ed_Q^W9z&$V%V;|-8MrgF z+?gBHAYOSY#gF&JLWY>nEBt%mTA%eiaH^FrofobHf<`UK0l*M+fN*NBBRBrRfgU+I zK1(J?19StT8gJD1w3Kz}_Lj#1l=fvlWuDpntVTFw9a;49&7a?(3NrbZuFWsGPRDt7 z!{fOgwcQbFI!WlflBH8It8LqEI-^%6S>q&bcu0iNqgAn&$Vu4yXOc#Pf+9SdQ%MJD39)q4x*g=q%O(#nttXkDfC>s%?p^?F`VaC#$79O`%F5D*!VV*-7gAuTR=7E$ zpXt<&(J=)zG;Z-2AN;jxFHYPKEln;!@GnaBaXEZ{yE+Ha-a4$k7$U7~sDc?tayRqE zhAunIumj<_O-A;$*qpyN4pL5C8D_`Nw4L}S&&;$F44zRJJsiLdNdrjgp-WbmKXI7X z4g^Kr`3l+wV?D66xP)FdeIK>OsXFkpT8dXo_Wxs0u%joOZrNAHx8&eC;@rJn82VK_ zukzQKu$3jaRmsV6M-qP*2^9nBS{iUX@@TRAk1Il3Xe-ND+$pLkAJ}>R%MIiTCpl60 zkAVRf>_P)u(UpIu{hz`eG2J}YLMPtr_fh}uF7jovP>cKH^{>g|D~?y&p6mVBWD)Ks z$H8Lf2w-cp4C-4yjp|HeW0Gy)f}P&C@PPW8Lv%jUsOyrqPU>`HMd z<`(+pEyn`c)6#hN_9g088jsn+EO}?IswgOU?)Q5|fwFEOva|h9@u~fl+H*rk-j#|p z=JDa$1;DtG;NS~zk>ROPyv^L5Wa(h(JaNho7PKi zX1m5go(y7ba+sSAPy6eYDV@51Z7lw;Nd7trv2KYU>FI1^3P6Wk;`P~WHLzH|-w zB>BcN$CfG&&#kz@vG6k~1{on{)btIVcM8%$Rao(m`^zS^*-d;3@GL^@0!3sBaNa?B zRxJT4w{p#fHSfv41f0c)%_Uqjjk6n)&R|#IN5>r6zgG5=$y*p5fqXE|Qp~4S*e+$} z-=4c;hWk%dN+wA9738*!Z}~3?{D_A zw*@cp_h;pD{X{c-QD#IKgAjGi$t z+Qu3Ogjm@{|M{bk{uAnutuGM~g-RJT3`#J2NvL0aU)?ZEC*f^_`yfm|6$(RtD4)x4 z!z0`0ffmdom@tz&iNq8!q7w;8+Zk^1e-WROHBDUqH9VbF=`FWTN2>czlFqCos z=-C;J0}M)Rg0Y8S9!)A=*F^tz0eix)kgJ^(#0!iXHAJ?sVsA%-ewlO&LB|BdAIDp9 z#PRODV+^$IlDw~0P5KlSD4=$v{X?2Dt?x#-k7pZ}MBH>@^;2IKkG-So1P9*Rc0w)K zU#!pq>*z$ir|e+Mbp~WL{09@mdCBoGKDGXQ{@{Jf`6Oy;`= zc%5d0L$FlJlUM)7a_c)vzjavR5T5%|q6k_4^q9m*@kBrldJrW8!7xLR3N;=>PwyJ* z^f5T#BU;Xtkkh=yO8Du>zNQ9VE|%c@<;)Sq&j#*f*v~-K0NdsKMOw0Zdf~SYEi7DS zhL@<~pIK=d>cOb5VMqpzcB`;K2wYvQ``t*bqhr;t;hvO>61@>Su_%ye7>r*zt}cQ@ zf*Oloz99!H$`P+?2J{PPu!GKP1J z`v6y`jPC^%q@y>s*})oKF9Z+?EM*?#|8R1GH6GpwXOOy-@dkmEoWYs}oXEhRO0UyS zqOpv@2qmIgX-6`ek%RAZM~JBnrfLE@ev(i=BLz9+eX-__5^PT_NUs@mY_cpPTpje? z|D{^?4KH%OChSc^WxYtUlFlw5b3#1j>wB3r@iGYlRLZB_vIYF{Bk+PMDM)9Z?cQ(h z*UyJWUp8{u0lOQU)y6(VSEksH5;h|dln_pspc^X4#cc0$YLkY^c_$^Me5h|SMa;Gr zI2B}?Y1A;r+R=gT_ySKiD&=awl6b3%O^t@JZEaY`(~u|ujLFDG`U-JSV8f=|GFmf5 z1x5V(KVSno_y;+R_fB}uty=d2Vka{gwaHd55{8QiDm-{oFAF44NT0_691drxccDQz z$XGNY!2^)g*@nu+Up}H>*4dQbC9Jvthxbg^O_6j-#Chk$FmI8O`{9{g2g<`*uj@j3 zf{I78P_M7Alt5CN2r?HW&zc#ToVw+~j>}n)1S7)3akKUjY$V(sJNRtw(T!D)=L zuQ=1jtsTgZmjI#h(yFS-BSSxHzTQBs9B7~I@_0V#Wk5gza8a&_Z$FOmf0Jo(2x%dR z;9@C+2J{j!BxX8Xyt%vY)C?J~HP^r!o0yoS2d~=+v+5971CVRjUEYGA4wj&NjL`8e z+h2Jx?~DWK0|jcca2}0NVc!i*XDA0#k!^Few5CpUfN6{4N6$l;m>=2}WL@vPe7*XvH~-zW!mXsqCplwM{orVVGGI$ZzYLX$O;O z#9x+g#IJ=srhG6^+p#=bD$) z)drecM4kVLX*OYoroqLourHK2k0H1<`0S ziQ@iwE$)nwKd2amz12I$%)_HSJNpN5!=t_ZtuRIUyB-s02)QYwz0`nJ(&a_MtgfIS z$l#T}`0o+2vkoC;eb|74J=78=YGaPUI#OCDt2YSW7kL{!eDuD2K0lXuU^|^yLZ4d8 z6cWswE)_ay*RN8|#u(Sg)L-Amxjg1{pGnP< zd_9*ShZ$hZ!|X9fA#UP)vjKrG$dY2L9E+Z7jCmc3=T!|yn4XnN(3fwxw-&`z;RREX zC*Ne2#;%_i7a*I-OY4|(QjZc_e+N6#r9+@pBm)j_D9tPReNYq}WDJev{%%|dmImjC z&a|d3zkhgy)ZUC3UVNc?w5D)8@@D6|UUTePc=b+`-ATz0Bh*9YT397=N2t)3aC3^@ z(W;Qv2#cQY+(Hr|U>RYCaoASxg#P`o5sZy!JA#8t2q%harI5}hPmcm|fsxih<@O)D zo==Bgc~TaSXOaGB;zm{cbF?*XQ&JPv{@t-r-EbP4t;^UGyVG6C-XX>?Nr?cPew;~D zSTr^Ub&QpVyn_Rs0Cp^)`Qao0t)bZYv>R4*f6ap8|bPhp7UM*TwO`~&kgsgm>@cc zs3@AAkN#=Dahohz=6Cml$&B^93uwPP)-(8;HQ%G08kDtWg%$9&PTrswqLoX#BkribUQ#5|2LtGmMN zeH`|l6eJ*kzszOk%)Awc&m1(IE8>aE-5(Tln$O?94FU!}B8xZ4{fDD58F3Vag9|cb zffb4~rZXts<0Ya1%nM8UZb@a8w}3R0@qW3?lkUh1BO!O<$OV7HWv*W<|Na*MA?X7!*dZG8A6mNd?e&&}>s%|Mi8-IK(F4w(-qmeCpRc(*aDz;9Ho|grU#F*#hI` z#kxm_Ex|N3vZ86u*4`e0YqE{}9w`FVuQ_H@W0C?q zvX!Y?CVKJj%F$tAzb7%3=cgqI@Hw9%s;AzN=q~L46ph>+LovDBVeW>RB0(Oy-p|=$ zb&yBKSQAPwc$W0u-F8!Udt_y68GSkMq!2T{-;~b291&Y+Y0g?Zd1+gB^wc9$$xzo! zNF~z*5%;6Dc(5R7B5g38tOEX^Of=_{>_jCD=Q_LsNYl5g={SM zG$&qj48$vrM@X1fYq{Gz!M?xc*ixh$REgR@EB6n27UGHO!{Xet1F-69Pm-@;^Ev0T zkWqHv?1@-Ywvj|)%9sg=NIJ{aDg&5BZ^4=lk561pi7qN}u-ZD}j+Ce67tc1Pc*zwU zWr??*m!=WFttRDra;fyl@jkPkIoM?Y8;S>D-O;298ECcJLjydA`evwNz@5vCTrZa` z&%7}7PKP^VX$^-4p*zqhp}5+5G~KnmCxbGLE;m3Gfkb~LCDAhLQ@6uc&EDF%VR9QW zyqJ1Ov92_lRps%JVZNW!UB7F&D#%EOZ5oEm4K)1ta*w#bgbf)#Zg|JW*G3MRsk*Q# z$95adLrVamG%=G(%GGn>4z>ho`Ef+Lk3-ZgIbmVp+(JRwtFvoa0&WRY7nRr5R8>av z24MQ#$J_eEs$O|Pc*kNnL2<)o$nCI}T(@%UueG}jIOe=j2-5DDMNhKd-}tJ3*{A?q ziq0;0rF!{`#0B-2+0;$2xQkf3-lOfgiV3sIWmd?K7_QPkpF0#7Tk=qxCV1?QV$*V* z`0yAVg6vBV@#0$?P1`Eycs2(6&ND8#)01r=hkArUC-5@pyWT{aUe&bF7bZpMh}IYn zTxy-4(V^(pE3vUpNa4#WAlNEhHP8#mJeoMjB_j-TvGPYRXsQVncsy=`61#VAPIccU z)4jjX$oulb6$v+JpDceL@e}ym8Gso)5GggqI@vPck@W5Dw^x@Y>En$D$-}ngwM|y* zPSX#w|5ZH-?a%p#{YMVry&mfY@*^qIr(TJdw#3rKjlHL^niA~Vc((hA1ar}(-?9;Z zgLsiJy)37i^snzVg=S@Hn;x&)#86Fu+b&DI+bv1!*^D&i;N2wYu;LE+^^pNBH+A;L zsB!b#cAYPMp?)6bUS6}=gVhkuu&ZuXPE4gW%T+rXk0&H5HTPz2H>L;FHkYyD0Rm~q zqO4C?P_k@3bMH$HWB0&O>B*-dAP{GF|FRlyMtGAs`4$%g;5xM89MNR&vy6|}5V+pO z9*wbj%5zV|WqF8AmHy0*MtDLcs6bluw7fo4+k)^Qk+j0Bq>Nj>Z)0$t2dT~ z3rgS}W$XVatIdRLfx{lcuD{8663ufstWLS%&ss*58y|KN#5Zwq&cx|r?G9@wr%4%?eF`4_q?3z zT-ThLJ+o)8z1Dqy7EBm9S}B)Cn5;dlDo}Zy4y@&>042>-u5^iA;kSWhgX?<6TtCga zAN(LB)tw9X1f}!#q@%{0tE!g^#_i|AG6UqEG&JWddFn9bSG7?x;Jb8!MG>5$Bk9;0 z*Uc>V_n-3JTicn;`A#q5-hu9Sbhnc*%Dt4LEZ^_+kdd_g0}E#R+sT_yVbK%hGMMzo zYsrLcrKV#=f-D9R&)16Yk^SnBa*v2$?E}%ATklLTxY_R6*UM=oSS?4E<#@-ui~#Tx zd6Kwhi0Tbw1w|!uHKnbL1am$ztdpA7RH}EyNrm~}eAn-XLQ6DTTwfs=_}$l;uA5_L zaI3t)g-zLe{GO&FMfrHdN-3<#oaI&h#)5M=pdWH-mu^LngkwLO$!ryum(bdK(j&%9 zT-Sb$zDw3=#0a{kO1eHth{$nSs#eJ_*^2KTM*AMbg%DL-6! zO$Q2R9R{*bUw~F-oXQa0?SRx1Tw7$a0(y6&hkGs{fKo%AD<7!S5ZfFE_-8BbRg z53FodKt8JRTq&u5+jp3Go3X*><>fs9%xi{CE(cZmB|L?C3Zk+1oc$n^zA5hQHw#oD}_(NruiifVf1_6ZYwDXx4I z`k8UHD&-oU+uIY&44g&VI<&DJigOK;mD?srdGRaKksbWbFT%(k=~VEfG7Iya4P^mL z{bKp;i{~6ArGX_mFyR9UzGv?xsv(_&@f4eM=;Vx72gti9#tBRd#T)NvzoCY{eY zAvW@;Kh}@qha_;#>kbc7-&{Z7+wVVc(EfTL4Er{(X@c=f6$@O$hoWM^Z3k@LtWHUD zQg1MLuI4whaZ-3NW+tD~pFuuzoh$pc8Ua_GiTYDFaR@fvftaC{+m_b}lR-@Yi?WDDdumvpIOY<=g27n}fIwaR^N| zM<4J<=BcmFmwxY>`|Q}-aB37Lu`GtI%i&?3TU&-dDKIBrQz0_$n;fbmFRc$T+j}LU z1t65Lg4@w}JAiN(MB9a_0+9@MQeve<2A}K#aMZeGpyc@_qbG)+EIB3s5*KnHZqQ8? zKFpn>E%ZSI>^r<#+Iu-G>H_CkJE!Yas2>ezu&YM!I*w$-YNK6}_m9KSZBmE)DM68J zDIK9QQ-1cr6uVdEo6tx7ruF)~xGlpoTWfr~x-LiMxfr&0xa2cjE69qEX*5%hk#)Pp=hg)RO zD~A~LKRm$vML=!6$GS(dK&g*IyZ8%}Qp@@tEA$)XoiEm?vwHMW<9@=>Z2iFNoTp04 zcHT$fF&5fumI%wtcwpsY+Hk%6>Wkg>aFAvuAMK`=^Qm6>J?x{+NT*>n*NUOw{4qa& z&ukF!?6bD*L{`_aawPv6txBuj>6ZOE6~atLYQk{L*awsR&jM4HO}_Ub)1+6x0~6t- z81{?iYS=68cu39in_`byG<~n8f=P7D{HFpuZdvvqz~|$6A@j zc(vdfmb6h{Ujhbs77gjkqA|xS!q2XQI%<`m!=ULn0MGiUA!9}9ANIIR_%KUMW~5Ry z*9ZI*a*n(iulWp*KkfWFeuRCIuc(r&r;8O#aWs^aP?W4XiKk0_L+<0rF;qx4D@!%h zvm*+<v% zjI|@S?|p{ZntU3&l}%01fn1Y}?H`&Y%6#>Z7X$IJ+;Q;VwlL?_>dgodu#c`#n0jV8 zj+zJa+=Z(w4@LxV$~0+}{1kBFCQJmkG|feFQ*q}Y?@V`9pA-cmQ&X{A!Dnrz;_#{ulW z!G{$cNdQ(&eIf^<Z;OXA~4$;>9nl{cwj~KCToG?g_~{n@HYoXnEpp$8Zc-9MdmZz`hx_-I0<2( zDd=j+vVDg%>JKI(yw>wtL6YK6C#QB>l7+)Je?ITW*&qM7R7LX2RKcIfpsFVJ*50<% zw(Ldc+({)+IgqHBzq@5fU@5^C)cUqhoUa?}j(cq>tSvz~zb!*1%0Nz#dJomDkq4u1 zS4$#9D~kB8`-XxZ^x1jnmOpAWMGMY08xTQr%vW5X4q`T9Vrj#hs4&y>Z|ZriC79}( z3(UgRL3+C>4iPU)IN^G%1eDoOd(QVbDqaj{SC? zIDqgvK~kAvgl9W_Am!pQfscw>N*or}+ET^9^{E^6oHh^_$u5*+Opxi!t8O)d4tTa} zA-qNWI!EXQ>!+u(tW_M%`Lh6A+dHjK$XY{=OQ5SwXSrF1?Dy_Ke@mO(A?Qc5v}fhM z{nkkbn8A9zhn98*y{9v~_?OukL!Iex3yWC%3dGVN~KyR#Dil zl?f$`vxI3DR~qawAChI~wM{zq+P*2@QNr@FC{L=BXDnYOj5etFX2P83nsRMnPu$y? z%%iclZ(o1&1rp1__VAB^HaI8(g^fRxPyAr*2d0OwiogB@>nwAy(35Tcl+HD=L@Tx9MKYJ}XY7m>4BKt<%>Lon9> z*cBPr?ths1-$+VN6neuGZ1;a5pG3AShoqILeF1A15xX=jJ{(IioeR9XJB^r|Qe^1e z>Z-<|)KbwcD!C--%8SCF*ZYCTyHcxrzmDNvbWnFa<1r6-=HfnR4=A-uoH><8=ztN~ zEcvPySm*PX^@0ihYiyH&4?&xmyY&%3_=r@oqWqtOUY;bIJzpNIFzP>t`yD*+j@%(N z=+*wS=*K~?vB)FvW&t)h@0^XHq5g8h{(=s@;QTzqg-1Hq$KhVZ-!k_=@F=Ki9)z9OrAAfhWJY`ml0y0$a;I7a9KEryM*R*Y` z>}uy5`A`3b*0}gpdO^E?RD-Qu=(VrGeY+oJWo3a(npo&`&^dkBSd-88QnY7+hDv^6 z=6$S~uK0JpE^xt!~LSr@YXAGkpfiFm3EJ}i*= z^2N!pZ)S~F0|o3QF>GZR83fQW?CY+iB=S8OGNpH7(wTH)N(`-{IsY!$)Bi^bJhjaI zIyZQ~@fw+jQ57@fT3#c5ZwiYdK^7sGXCSbGIa&Os!$e*RK3P~G#hzQYQC=P`_z8mM zYRm3l7$K%QFNkdAVZ?(-!Y@O%(^)>y>>t`1+m&-+4+tphw1$@i9DMXN1&1vl< zillYCC@K5Y!+DmkpKpwx1JFb+>FdLld`uwvgIY?Mpg>)%px*e zNJzdicQtCtZlr><|FYOfAp-8${;7dv7Ri}2U5{Ah_vRiy;^{A!z%p49uml}CgA=hN zgrW>}pdYVICl^Y0`kR;u{u!8z3x#f9lqHZZ2BfAse-rtpHv+UCcr2Kn;gvC!= zH2xj6KIo*&P6e=J%5cnY@lXVsM)F*RtoB?S8o*Mo4$!m69{3E*6mVEFl$Le)UjB9M zzCYozn3eOs@9hQ?E7|lHS3jU*ZG|8oHpe1F)lmfsWi@)Y!z>Jteh93z$T2fB@6+iN z0ZrL6v41)`gq+Ve=p~}?IhZWiiPCQrk?*BNsop&&^_E-h1oCTiZz3uqoI`!3rRcD7 zC|zG3CNf$9`E`h~GGw{ch=T1{yA-GZzjJz8S|@0x%#;)h-u)hAwR+^7wP9bX7)@#x zmeFEFLRSL|nNSCTx+LH@=>|4uM?{SMI*FIMIb%`CJ69^y<3J{^YiO7dHLjNl0(3}2 zjwMT5saM3B%3#9vredO_k1W`%l40*py9I`S@!&7>{@-v3DiBQpab!m^yNU?9;>E!Z zH%oa)T%sUyfT$$zszt?hCvGk+AUKPX#heTWlJOh6`piV36dE*d7IxIzMQo?O@7}&{ zNFNj2rEA}k&J*3ddVH5{e5RP@l@;RhEfFyupZnaBCA-ZEm9cmf`=f=#a+7yPx}P-d z7x=)FKbplCXE3}y#gF2olfdH6Igt|M(@NEmp<>Ry^hCiZ&>RfUiLWdP%*0T%oF%N$ zI_^8#!inUPxv|sZvlkmkB_D7bmA50lWhVKogTvq<)|2I3f0swP$Sqf{PeL4yItN+{ zUodc0e2V2XD)mfPJYIhNR@5D`-vI&&{|aIdz`x&1@;x=ItbFH6%QRD8g{dlOB8dlv z2P4JRqy>eA^2)Qy_?EMx`1q(swcI)`AJd^v%%tM*NrQx5bwSu#lZ>n{=Y^|`FB;lO zW0Y#DU%xg6BBG(ZlLnqJoK8+6ZFdr1&vhwnov2BO4~)SV$qt5uumk;R0N1EU1T+c& zM=6W6sCtXv4a33Skmyq60a1etpQ>dFW=;93a0$ihj#wQjBx;ViVQEMgJ}>f&in?=R zbwD-z%Bc?dN}4i}92+~^Q1LYBwyVU?Y`<}LN*|G$4Eo$i0$91U*yJ+ zOqyHM`qLyt3F^izZVO8hbdu|)7Z|0UT8^5daVm4p+4km!H0ad&q%gbHocWC~@EoGH zU0MPpbe$i>7`vpy(wO_fAK(yR5@awLMC#S8(qU^1plCvV+ikZs2bV05{(SVp=+U3$ zBF3EA#I@_7zrLO|5jV;O5U{MA>DKH@PIi(MD+GWAkbS!Q8K+He1h+sk){6HQAF1X_ z{gWtd2xn_xDbt5CMV!`2Od%}m!UpAmrHIhyla)4{1YO1d^ovvCRUCg~tbVaZ&D{;qF z<8?OkU2^Nhe$>_S2wj^OKen#`AOCPd*-*$wUxP)RVhQmOsVwFeTLtjqGYwToAt0e1 zU|C2qc|mhfJnY5&v7?Jsn2yqm4|1*C&1VIh0;34nzRUM|i%Gz(#1Gwxi>R@qE2|+t zz(hpglWJ_0dSN?Lv_$K7X*n@nt>B*Kv`m!F?n$8;3gFnPI^R+4c|>OM+Mdbidp(CM zf<`h%L3U;E5m8N@fu^xp6$1G)n>0PwXf~1lI4b`Od4OBpN=CAaufLBLH?8sLzfn6b zv!lK%H|ft#u((bcPzDE@@}Op2JL6!mz>B?zM5u%E%pgb2n`F)fpbh}(hJd_*emE{> z>mRIZU3NpcVsHp7M%XG8^31O(H9xBsVP{AmzyHaso+$?7a|?OLwzbq{^xg z*BdJf3$7(eX69D%55Z1H`o(nnE@Q{Divd5OrMxdr`f~KDK2`K?EOJ5glq4iNH#zDu z10Uor6Ck=?hbDCeglDoq2u=c!e}R3>;Jq@JxHHgQ8!>?%A00^`V7u0`i?;Lq!s90B zwf-!OQXrZsA`4^;*G`7XT3kK1G&0SJI%9g-AJ?7F^sWwKCMeT~P0`6x`R^*;l?HwH zJ5cnX2D&tdbxC5v+;n6(-vqh8gwu<-4_SmjzZUl)Ds~5V;N0Ea$X^6E1Rfh!!_0?z zU|?K^XpJe_Qj-FvGQ)w(CO9DGPM*a`&K(SNf;wX3$nKLE^Y^pr!yf;nczG zvgo}w`~%jsO&eY$KBjY8bukLV$in%m>1+V5DRlmf0p51sjq41=>R7MN`5+>B zQT|ZaZV{E@28zit$Ycq!8VxDw}z-DX4Y!WSjdk z&0IF};M4VBP24(rpBhdp1#>TAJdND;L{+5k-O!GjmNrgiNuVA?&km>yu^YEYSoFh* zk^TAhbWOEe`m}BvJ#7D2DASbi^Z~P^-A@{hQzhjxlP_fxDN>UO(fEUOJlcX_$w%?G z^lwwJjo!Q@urH~)UCo0QKyi#sn85mm(RR z?vE1lOm|dSbK%ibxLY`KHa^{ALER2(i%};aT=p+}iv#&@49C-tW8HGL$bd)5Oa_B+ zS+aCB(ib2y435#;5PL+1f9-uYm2)#SfKcOc$$vIW9mn^C$LbooJZQ{BR&gNFi~G%S zO3=(A&Nbta8iupz2PYQq>)k9T%@NU@rdDjGgI@kI-kByS1s-=n;Lwl|;&RVR&jK_-_c2IUL~cB{Yi?u4^%WVUEx~>Xv`O-Vhq~KBQ9aPDmX2#b?0Y`Ec1WT6t6wpG z-!p5n#-^Yvf-FZ6$P9V<togt2K@t3d0o=WR^y0%`RlDrp<0hH< z`WKW2!8ZOMIBm=6FDVXRnO^a~Z7A(uQXH&hs>J_}yH5X-;<)p_XaD<$-+~%{NpWB2 zSfl=T?7IjA^zAuG)(if(&#eFg_)2JtEeQU13|dJFB*mRZTl)QS!Ia-pXxXf#L1$VI zq5M6Gft=0FKvGV5R*@RPAG)~R2l`p}9NMe--wm|~fr#*e`x3{%#1M;tBP9wElakT= z$`=Jkh`AD6H1u?D@pjp+yIfUC_@31CrUD#>MHG6|R-c zz+KNxnd6@7^q^yHhv)!OGP&_9r7tcL$d?-y)Y$J)?k%JBp8v)+6(t)#tS;&EB-JuM z`=lnFfd>&yI!KnVMw$X4-FVKeJxHUHIsdIRyoTqJO;S{e+2sfl1__6%j(9x?%db7C zvg6+~_T%>hNkN@=k&8}e9+e+i#(gzWpW~LotI+{_cb8B;VZ&86{URK7*iIZE5t z7?p}>>DyHNFTMe03NAYY4*+C68&*Zh=eCQcUrtC|YD%#hHhC~Et zK#2+eU-F~_0HWp!>3KIjAX=$Q2C-2U44SljM2v{=ugM_)@7!KrK&7P&{ZN>RT#NVC zM^3J#fhR64re)*tf9>wqe#dfe6S#-?_nN!ngSty3&3|=m*Et09$RwV}4Cs9r#Fip< zZ7e-Y=MnVzmlWDsgIpRplmBW;8+EIv>yb^y z3^9wrN&#|CIqM+5Q2)~6UQ$+>^wPp@VQCqgy=4Fc2bYlDMT91|#HBCMiHnmKRSMe$ z8dGs)q#wOJzpxO#EB$w?*c z^Z@B)mQ7^xQFci~F`OEz>LbIuQbeJk%y!#4Fi3E5Yi(As&J?;ph?>bo&vLM)vzp_T z-tG!R7Ly9_i`iShBZ!ARrUjM^lSqMU(z+a7isZPd*g z6~oT}I_e?Xc;KQn%@KjhNB12Sb(f6ap%fml7o(wr1I$CB`(=s)A0HAa9!TNM@q_No z0pVthTlnEfrS~gK`AaVVeCuRB9x_&*V$`Mc9tkyxQSD$Xi(W{m+i^!T_HEbYa~T=P z;`;al9z4K7Z0%A{q?BOa2}iWv4yFut4k{I*7!+kJP;W>u%aF;O1-xfqjEFq z^BDnD@nKQZkg=+s5RoS&CCC^UWS1{jDSx~%bpCG3Ti-F;7Nn4rI=$E67(MlIl>rM7 z*-PN?riYn@!loW_k9@E3T9tRZO=v8*6UWZ4CL)cg&fMSM555+-9_zii(a?|Y4uGm} z4XqlPQvklIK&ba$E~tJ;Gxa{5UW1~ZhiPp(xQZ0dPk9Ww)Wg`S*iaRdYZQA9ig^`jg`U9my(Uvts$t;byvdw)vZ(-87SzF;__{k$rWsgmm5%vP+u}#SQm+KFgWC&-WxBMnG>y zpV3IL_^(Iv*S&fHYjNy(gM;xC0gdr7&LnbTO+4Pm8E(c;YE&*N<&my*#4{c;LhmZM z4>+ew%msyyx5^F@?+Ak`0_h(W8wHDh(`?3nS8RY%V`g!{`;U#bWoCNY(sXX`@6KK= ztBhnCK`#NJ`lGGiLJ;v1ijRnE%+{sB`z>P&gWa{N3?kp&k_2Lb{T0Z-8@`IFWb6Cxvh9?aQS$24x7NjhVO0qywD;)IbySWChLs1U(7^`ysq^0 z-aiN*S<^|Xj|57>zq#C3B_Qi;)k_f;?$hU? z;|pWtZrh8rJz(uzc3rv?d<;^R%W7E=IjdyWHwt^JW_eNh(R!XOBvBe5N1GO#QF!2Toe++AJh8d`TWNWtPsV zi8^sG8_)rU_hTow$8&neNw2bf<1AOO*n^G+Z=SNwlHupznDMSn0V2h=YC%DWCUodi z+rbHn`lmt}1AchcmR%TnhEHT;{YACwO&9vhZutKh{M9utM%m!tHQ!<>ciO6!`C% zV4~nTzOAc!@m4}QSLuJ8xj5b|Q9rLGWyaS(mM6XxUTmAq8@I1WRw;Xbp4~d`ELo)d zPOgsXo}I?cw~NYg`cU*?L)(`lu~fGx-Xw&IRrwj-Nc@R$X?DiEXq{i^-C2!mH+8$* z6A^(c+qe^$ksJ?V$6UN)aJkgxKnu9~Iw0g%&1tTX6{kX}AhU9$niAqlF5}qp6|#=7 zzqdm4D}kCbZlg7mQ0Bc8!VElz)dv@bw6+kj&;UYfb%)Z4l5LK`hxD^esj0jTGf_PP|8(v8FuX*LV7;Wa$5Cf&AiWhgYeZinepl6JrB=Y zofYY9l0*Ad?eE)-3`2yUSGfRS-emD}o0b`+yINWtBlf(Kj8rycX6h^;CgG# zABBiX3CG6z`pZK`M&8T)!MjegwB$Ryy~q93r;(8kRQ4)2MyN_@>{bo`13O$-xGg<#vS3pWW zHMLehF1*mHvV$QLGwGh>x4FRign0^61t}?*i4dF73(r$-^UCz0l=H1)!VlTf(Xzd3 zJ6;k)p`6$N0302^UWa{hF%-5SLmG5!f@k{0yzxM(-My`j-WrQtcH<)IZ{HHnwOtI2 z5d2)$VUaZ+v*@Iik2S|tARb%w->37b-EZp~O-DUe1!cqcdhLHC^Z)!!431_6&RiP6 zvDuOFx<9_SQQxfGo8ym)@pEumWRgPnS0zG+cL#;|5wbEqg2YJ1m#U~~rhvyChQK|S zk246EK!o#i4vysA+JgyS%(W2i=n64$ANLFLXKFBI6$y&r#g2PZtq!xWn47cVP!Vyd zQ-#TP>oc{y!Tj!4+qky-0Z5iqYp(W-o1eE^ecLwqnOj?eyUR_^HWCR0!^I#Kmja*H zcG-@eVh41|iar5R?WB2Qx5r{#@m+&^*m~*U)rdMn;k$b z%ECl#>)41gNRzu0n4-2!>R z@ge@ZgSh-SZA2F2T_w|YDvydV?on@vjp}dSAe#H~idbKtf{Mzbr>d&z*U+}NmU1ip zJ+;xk;+nZm-UN4K>*fO(XX5NVz?c2wtG75}aMrSF6O-Hj8zj_V#->p*EVoR9-IWe)S+> zeCxuWcsWUdJ7DkxNayvI-f+9+i^Gfx1X_V;J3WAj^Z=7WpVx$1P_njOy{1Y()~ zg&1h?D{-4NEc9!yUEI4f6V+7=KQO#qNj@LiKVxg7WBGyEU6bveXH*n?TUATmMRvJ5 zlFBtRH^}F+Tx8L=KRz!WapOJUgDV<8J_+uIgf&&loe-OiUA^;?)f~o5+5)BdH~5k%#zNu%sM#58P!VxH)mbgp zElq9x4>SDJhR?BA7U;IZ4*8So^4Q3?HZO84Y{4cX2bU6?3Jy4GpQQ*d;*3YNocy_^ z6WWgfsgsFl8~r;|12k0oNd~%T*d@IeqyF6pC^ARQrasZZJL=(OBy{80Vxv?xyA~yu z_x6U1_V?#TPbB!G)bo%nCF!$>XguMV<*jQ?~Wa9M({ zGZA%*^Iqq#{P7#OYWV8gbuP}0_5hq}kW*Df2({+&uyB};!=8R`PafUba0XYX*s1BW zGRG%w<`#{_yhL^EU|BpDg=G4;-8f>4R)z6?e%oeyVMp3g6CukMSp}@+qQ@dAJr-6X z9X-T3W&@b8md|plWl7)X@^r4B-HGPqubl}KyFnrSqk}Sq3R$p^+X~o&V6Qf)-1<|k zc2A@Q?Pllh+$3kwq*s7Em(@10pC&!vuD{mTdW%-;^CmrFUcbQxCc-oDqegJ3>yuZ> zeBHw&@g5#rG9Js6CMxh1^nnSX)CE=EnTlk?N2^!Jj*20BxZwIxaUIYr2-+}OTv6J8 zAoF9G-o9X@YQ4IxeLvaU8V!Mr1tU`<>XWXLO&)8k_R-u#xQN>hE1z`0%pnay0{R6G7nzsl1jcimOU>S`a*luUpc(gY%>| z6bocl^CQEL3O=4<6{MW;s0v1oK&RAg(iaaTT!jvcrGOvnFRf#uG8Pov?#Xlka88K% zHAx?DE$Cm9hU`s9n%v1}_iT*2tU=o@leg~Y7dIKXo|@i;Z*9uSmd#4xp0m7&ndbcO z5W6o#S21A1V^O{39*R;P5fr7o&j~wB=3>^J*cl(4a=rJBCoWhcvYj^6l z;f2ahsIrrd(+{s|ZjW=d1L4&lc%Ymp_Zo(>%5PV$$o-aRQRp~0G%`sHmfGDs8%Y9F zp;z`zq)`bm79|5-Ywq^XCX>l*Us099&epxUmc*W#UIXG216=umIpmA7zTwOuq!&to zTMls&ZZMLi#?x#EaY@_+SnbinXm|7YInVP~@p%btG_V{SnOR>oM@GNEp3)=Fe(k`XE*?ba;6_chTd;P6rI50du zcDd5B)3H6p5Z7|~pg0N&b!qdb45Z~AmpY5Sv*tlsRMpnVI@+?ES zdcUn`$nxq08}xYTd5ERfc`PD54X62HkqOo*ARaM(eRx+Ud>sw)$3=yqKD$01x?@VG zX)#N|5kQ?GFuJofj+tL|zUDTMLd`lo`z_q8r*4$rf47v+?s#(Co=Qyq`~ojqzYan{ zwrbgmO;e#%Ww=)FPESEsob0aE)*eu$EC8>Z?Vs~0^V2HM^KF)piR(NWF%%n*ZpOaN zgKw?>(CBcf$mcgrx4>zxV@?t-03tY3`W3^ihKLuzfpFB0*gnpIVs zoJ3c*1hvpwa8iLMFH`zUWd`b$&PY`qJ1*hTz1=$@5yU!JD!!CyBMO?OsMOsIf50>$&;b-571! zhW*(8t)64vi8rYaBfxjn=Q-+fhc7{C0xoTmoxF%5)jd6n?WwaM&BZQ!oPUnyQ8twx zNiH+$I)LVDLcOr#AIoBv!0VeZzMA752e;=Z0#b5M^KoO;Gu*9T!VM?2^-uu;pXZpk z>OAk+rQb^afIqWInbLmKLioH z%!R4k^kikRAnDsd`MzH1aJqn&l0FIw-)sLmV~hsDqq>sScK&L9eh7{WY94;HR4jjZ zBe5NYZz@q(kW7b^2;NNyJG_z!C~v`Ll)m300r#GvPd&WZZk)h}!UVRCy!9w8as6DS z&Lb`n3;^q~xlqMaqy449fhLT}(e1f?=dO{YC~VXKA+I^6ZYW@ZJyuoU_axP@TX9m= z;nKG8;w{-`baXsrzuG@%oTj6Nn>8Eii>$?psvXrwo_w;&?jknY7Y#)*8DuhF zL9+im?z$jjS!(d5lsN~*&weNj1~yL7#H`UrnEDxAT}{$f47;Km3=JyW9zP?o2_&aa zw4NR=E9iTEYN>yp`Yf~pFYhHNQAm}qK{_Vv@Ot{f1JWcz#UH=?`Ou&KWJ7+OZk4DF zQ4?Z6G^!X@%{E&aN>?_n8a4DZNVQBMVpa6$`scs?@M(TNpN5pJH&ak^H5Dy? z^4`r{ZX^WVS^)O9qp!#H%WTyC5p^8`Y?K+qj{o6QG$j*R#%CTRfT}T*P<#7~FEv0y zH2709>6V8pbGd!8(NbH(WUZZtlMC*rpvFDU*w$Hgffe|X+9GA3i3_fkJB1c2=^PPk z{>?Ik%P1(;Yh}{cVcep}(UohKs-8omVR1c#F~WTEEuCR(`bMQz~L2V zW2D)YO;WT+4FN-X_n{XJWr9sm(Bj^UdY z5{DBq?s7$1kn)UR`Wlo`8V>@Dm`SaRToLI@5U|*_h6q--;^c#Kww^%+6c9xuxef1< z;W}r{5`};@iBtBVMK%z?{#dO{Pb9SU(xo}I0?_}~bE`w)tA$$@hyA(NYL&^@fLT~i zsg*I>NdXZZo4l4u=9qI8T#Z}9qUx8Y9W3bG>JyF!LfjuDE{WbC3lqR9T(g|yrWa|V zA44ecmQPGMx*=LQHJ&6tGMqlj%;N+o$6~?Gu%*N+eQwa@r-ovl9DdrCs$ut{0P9+f z;vo?wF2o6R!|9Wx4kH`WKqdbvO%XVo6FLf(M$voizL>R6XuRJ-zb?Q`1s%rB<-^)Y znzK^TnSs-S)ROsE7>bXQ4Qt~l{JRbExEK}fCSpWciU5OFk8w($s!j@C)zr&$`HLfi zQRoq~H;U+8Wtx6$<0PIx+&$LwQ@R?(McBpqr`zSA$>BW94Wa#WAIEIF-GYdS{1I*GNi4<$ zIiZ#FvKcj)L2y?w#QeOw<=B_##gHBpJ$*|jFjAdX~R^9Ko#S9pUrxs7RHI@|d zzv7}=kb#%p&}`R?1u`^ml{8u;@T^oK@;!1RQ7{X{?S-;48Z%m4{BJ+XO zOTQijy~JTGJ*`R7acCl>BVUjOI4k-@aEuya0I{BUWzi^x4NoNUJBg{|vM2UtRi6_J zJb@CvZOL-}rJO77t`US6Uu8Ye?z|G5>WKm}XM`rJNU5j!$Jnd7?tAMBXjfGxFBt77 z2(AJ}m7KBs`38%kM5I$UTD?NL&Ut8PatCv-yRa>7N=T;Qz<&ffh~(6?p_g)_f(0b> z1{`TYM%AA8Uk>RWNlW^1Y}Z z9y^E`P!UgYsTh3T^XGA5E~9-%Wg zFlM9J_l}7x8+teARY3^7#oRTCVk1^^v?xN64n&G7DJ_x#!F{soMG73+v7EiE#Qqbo z=O+7Sc&9uq+F2tLv6t=blvte+fL2tSMQa6_Y-HeTUifIaXtQg@ zvrQ; zs0->3tW1VW@mHq5?W4Y? zMFr%cebsHK?A6WiYmEf0=j)!3^u;G^&eXA;xX1C;!dbfVQ;@{sv`v@-KMWSnCqK5i z?p0p}?3f*I;FKk~oa_kg67$%IGT2RVB*pCV70`Q4lUDS|b#JZIH1*nV^p_ikb{qHt zFj?Kxh7pI=x2wfm^cEJ^hbKGUZHzr@xfYnp_~m=xzZcrXpWljS7cF-;V(yuoemSkV zm}%8eR=RxGuz55QM-KOVLRjsC>BP`3((!$LsC{Mc*2)pWD3Xh3+cisuhf7cH4e&R) zE{1W0?5qBIEf6KEOO&@wF@2;K9+C`ZVq-7Co8fMon}s~B=lr2q6;?P`@Z>!nneb5Sit&GB zyGJE|_!AO@-3eucJr%eI$ADYdJlW?hpxt2I2+?-$h>n#pE_Yg7W~NutL6?q|{3pB0 z7gDS2pn+on^Pn3J4k_U(G*U`(Ro=cA_XB!#l%@bSW%5e^*fwKN{QH;IE2#BQ>+Sm` z^A=U;Q7aP@wlvQaNDflxXaHMT53O38GfPNXT@JhZ`hVv@oI z+cM_S%Uc2YW6ygUF6T}xy(;?O{}ntxOxj=G7~jD4U70HqNhAugna_16u9o+{ww zqJh+w)JY70E;a?Avk0PhG3_M8y;iLcSnNhCDQa!Ij(!vP*_LeMPxcGu;^5F=R(inP zY$0E+j7QXJ9i+rwuCsjd%W=J(xgN)(r}c}b?8bJ@eY;JK@#wd;b{7Fo?OGw6Clpbm zTpS+fIydB(Ql2p^T`ev8%J#PksSV3m21ltq#pDySjUMb8lV#qx!IAAMZGPoO@0%s9 z<;GXW*xJZgD8km(MYI%coV9+rC(^^c%!Cwru_fg^qDIM^n{X66bt;$jcGXyAw5oo| z!vr!+@W>zA0Ib`wApw{Fx@HI_=b98>d`Wy8QxNjLs5K! z2FaIKk%sXvb}Kwjo@c90lJXgCz8*p&gJTj2%4jnuz5!&^TtXQ183ANHCAxPF@5rkP zjvkNakJF1rDZHlX7Z2P=R@HXmoJQJqb_%ldDuy(x^?R1wHWm1vL|6}hRqbpsyXyMpSM;*Ik+upI0 zj?Ip3+eXK>ZJQn2>DXp}`}8^Q_{RNvf8U>bWT$F9>#4P>cFncs{7`My=J!X~eFG+;R*CSM(`xc*V?lcKQv}01eo@lQY6A;(kwF?Qc5F#nANWe_HFiv?? zmGTs~^{u6I5K?9O=v%wj%s{cXk>Q@~YoYJ@YyG6GoP?E9t(X29$;z*G0oAwHR??o3 z$ji;&%bQn+)?1}D3}*S$&~@IXHp2u<;%t3Vl$Fg=ylQQyqr8vAsvcdGPEYum5@fYYN`}y}r6e^zIhrDU zR6W|k!M>mVBPBTUNFQh6VPGVhQ2K7-@V+dvltD2-IDmVL@LX9}jU4s6HaYOMVrUdP zJl>OYdGGPU6bth|o=C`=x=2}8!>P|DLAhRV`dM9KMYS`*u1%rg?xkDc>~N`|!UeFx0)roc%WS*$@MGQ@ZiBZ^Q!n$>Z=J zu0fF1J{q-Mg~@+TXzc^eG||Gw4xSR%swpcc*{=q>ozmY=(r{bPy|}I}2R5+3M~0nV zd(lF3Z0-HHd?O*N<)@yQN~_H*T`Ze(tO9o{;-GGm-rG+||L2HdX0h<-6i+v#q;;Ul z`sfxrq5(SdG2sS%SpQEEql}MYGy27+vnIpUz+PdN_0jsYokxuGs0WZt;blv5bLoX% z&mOVGMLvh|Ysb`Z#s%A!n@OPd#3?r}w0Z><5y>TLGCTKbDSZ{aT0rTxn{@#mkAt|& z73a6a;wTP?ISfe!L`OPO3ClcsyjVJ+wBBX@I%{~eBYI6HtT(Y>DQryY+UB&ped82f zW=UP2Pr9+O>Ew#khFZii#sY4qC9^=>SFc;(^>pW-!XWV=DGU{VV4G6QvmdMH-bU{i zK9w$};Mj{*Yg_Q`Uyd=S45pO1js52c)1zS*;;DiVboJO8y(NZo)8FXh-piE=Y!HzV z;-6;$;qbAHHPFXt^+kTBm1FNV?wBAon5_c$NkTFvGn(W$k+P z>lmNM6QH9kh{y37KBlK&pF^Dmc%AuSJd%uwCm`Mu&@kXLbIXV7F2B&@9oMrcO!$$L zfyvFm_{$0Kl1#DNZ(n}X$KxV?V#DELk8Dm?km23X>`)fMeZIbV`7t+kB6Z4<{@l1KzGV9jOKHpM{#a z7&}$YD_6ZFx2{$kXJ&idNoPxVjxPkM@M2jMV?eQ#qYL?E&Q|qbS;)gh)?JWYSmm(L z^R6U@-$*iBh{6#*A`JpmH*^3omfaFOl0Rvi&pHuyWub()bw`3qo6T~pPrpr3h_><% zv(IypBJv!^gpdtMBD%5}TiJL;`g-MQ;*ZXuc5vjvhU4c-Bs9V89{DRg@8HN&H*Tut zRezig(NfXTCC<*GNak>UrTIajVbxQNVU_Iu(ta?&o)Cw}VDWwNPC`;XcO%oQG!;lK zwJv)8rmU?(txj(y{F8PVVx5}FP7RxjTTGm6(3vsiPE*(Q!=t(`xiFS+b4iq-!{4L9 zj+Z2XXC)>+;GLw+i&z;BE@JYiY`%@BYHGa@dtjQD&Z?vdmNCt_6T=ne- zr+-LDP+B9Kvop*ZgnPY@dhkKr`k%ku8v`m^6=P}FI4T{?UqJ-7EA0p2!+NNtO~ za;Wr+*E&oWOBqk5r4Rh{==LXXzeVCC_wvXP)}Ks_$Eu36Or3!%?L{+R{-VC@S2l!qgpjK{V4|)XmC`wA5XcdA#0)Nu#~?j z0Pv)ONI#YsLL+Qc)$nA4uhNLO1+B`qKsLElC5`4J#`&IV2A7UAGpz;O2g~nw)~Gr4K?jSzC)T z--ATC>YeM9;Ydd~r;;uM%}xfZ{>Ex=W&3Gcj3HNyLv)nij|Ql`5syB$x-n7PPaAx^ zT|7I+4-k<#~BR5pYs# zhW3?@o8jtm@6Np+2RDu(+9Uew?vE2nhnzpqC4b)*A&7(-4Mz_GYpDBfdCOJGwhF(@ zhP@paoj>)z#b`~|bb8j|keVqGj6H6wPSl&d36V-xgSj+{wYMfWm_8o!2-ueYG$^LU+b{A(8F?G7#&SwqX@LSFw zl?TCW*`lfy&5T##wg)J^g<4!IaP8s<+j zLbpAB5hC4IENi+<2>81E?S}ohw&gX?l(D1}beT;n&mqG3{_%f4U9Z za_9PLQ(U%D^A-MMHQ6_88ay7Gg{k(djo27t(G9r(nHj&yvid?tDVRc04=-*Sm1TP& zJIHeNVS4g@t(oNUkOUQ7HGZ=3{oA6;l5 zM=;euRyq@w5Z7}U#6pC69p7#0l{E1*$5Q$|IIr6zCvo z%~W2rI$m?iGxG4cn;nt`@u9H5@S(vph;?9At+G!qP8!C}{sxh(BwPp=|F--+7zsHk z>DK0;AogCWf+Yr#J-?&Dc#u6)z*CI9r(=F4Hib5aGO;AaRSHeb_xOu2EC)l6f_Su) zKe$|+^aFv>JV5tFVE_{&1+3;Pr^EBtv09L2ERt-PSh-N)1Tt|gjtlWw=WtprCc@@n z*8QTfqo@(tK6rsA69a1>n6#n-@WVp(EL*)T(KCk#kRlR2!z*~UO(GIf7d3LS5|J$d zy`MRBqxzUm??xjrgQxXb5auP4f?f$9u2zs2d`5Pc5y|i<$E_IDH3behb4oaBvlHqJ zarSl=zq#b#(@4J{fs&C=BxfGQbAs&<2|2G7-KppGSNOrzZmn2lF702N(5&$x3?@K4 z5L-(;22W#i)Zgvk!%!h#$>Z@D!NeuRSo;K7!dbSOmeSy8Io?T=YRHO$9B~b9YPThj z*K|J9pDUv8xMUduxKJL+${#m$ILqeuV=va8r_~Bfb?QvxVO5G6rD=6A2*avyj-^Qh zbVi~G!^Z}o`IV>D8^|8wiK>u&&kaa77Zw)!ACo90ka7|bZEXW(8I>=6&8vvr_m4pW z@*D>ZN*!at_hIcA)49yx&aEGcWv9BQ%sDz)`|#+l)vWyBP!R1F7j%8E;4>0h7^43q zrC3G6C#QKJnx$eTgaHKkx4u8`Cg(tFDGEE>g(bKp>_kkCE&l?x5XUsY#%xdhvTrQ}cCQV2ztfnF z0uTb}h8$|`I&FQuObMx8;#D|4ojVlssV6d0RhXuaQ6Y8cq+Ofn2Bi{J9&D1FjsY9r4##Fr_ zbju(+-OpN}bXofgzB^Vs$`k5Q31)6F-5U&7j{RQi{tQJ+;81bnC*nmz=US5*Ui%EX z=fimf9*rxyT{JmFA<&qOgSIF2$w=Tm$;sehIl3L@Ogj^4)-y>JEU6gUPY9M z{GUluN>FQb0cW#XV|f^f1b@7CevK@8>0zu7F2KEwFs9GQKxHlXf8P}G$VV4#*d&t$ zOKUNwU?v@$C__IYyMxnS%e(cs%DPC1{KEgl-})5?T^ONC6uQ-#@|eF%;b{iCrl@i0GvxhLfgrAu$`>*mnI!EK`HXNcTf1rf z1i)KAAwH$o+cWMwbY@D#IYbl>2@XD=lQ2@R(bYEDU!gWSs zw}CqSVEaI`jE+DySSlJ8=*I?2^o?pk`BdvsysF4ifU=b}hEs zc3;SGC1c#{d7HEYQg-CzW3B!!ty#&#gli^%t9wh~D4pZO0jtW1kz|&mVOC$XSl}&a zEck{t(>N33M?Xu2lbhYex;KOzN~$p!Z`G1OD;&-4#OO@(n9PFxb^p)Uph{Lv9x18H ztW$2ee3?9vvY;cz-X^FRD2^T8UKg?w3d41Cxioq!#p$mndpyknpe>-a&WDGX<0i)6 z0A{r41TB7Njz;Q=aK~_|l**+sMbaw6xJRX+_RxJxn$x zG908cTGZ@(XJd41`ZG<=l57QMe1Zmr)_trn-XIelwiJ{qd^(dKneBVGX#ajRHL(bo zw;v?sER&Hn*A9LER@2=F_-G`S4ewbeF<*QnATFvUZ^8k_iZFBGg>ccKPa!&(=;d3W zoz_>aS{-l-MeRX0j@XCBIMJUrU!N=}_s!;^IE@w-+0)3{{k{@N0|E!ZzgbOWXNAfb zF4>jBj1>uHZ(v&LkFJo6g!jTsS1{Nsk%U|Z`PWF5G1c$24+W+;p@gyq#f-; zoeN^^Y)Uxn^%FxpH4LZMRG%wteX^H8Zw11mmWMR~+mmDb&=NYt!)WxRV82c7Opbbc zI%D*dBt)M0V-nA@!q9KNz<6vn8sMr8(A670kQ5B(>-6G^HaIPUwZdRcXp|bW_YdE4 zW~(h>Zh}Gyj{aw+5Au4uqcfC!Hi8Sn4dF*dvr;T^fl^4hF|_~e}}=w-n<&Tnn2FBCT8 z$V$kRUUCU( zic^TDkc;rQQiCCSzkmCEuuoXj^xar14NeP-!|UKXDhEAG{xNLWS(?eZ#;*HDPXrQF zT-TaB=x}O$d${-4NLI=7>Iy@oT|#bx*{008@0}cPV;A(J5u|l--&>th3j0EqHb1qD zFklUl;gBGsMo(4p$k(i{(pTFf>p&|t%RubQlL`}Hxn!J@p#IpJEOlyv$7N0 zH@P7__UTpvgcI-HH=Spc#zcfynFmc5`x0X;+3Dr>3`p_#OvwO_X~Z*f%N@`CQAV%d zjOSs5c=B`Cl#{dIOYeUm3j+()5d-_eBmJ}Hv7z@od>Wb{?u=Q$b-Z`;T<*N&)*g+G z+0wU}(-G8&39=LDdO|K|0{!$J$;T^`KaF^1XBu@=MK;T^_L0B3x zx`D0Z!%eu%Z@32 z#5ux-_<(BPMbtez=(-N0XM11ff&Ud1xdU-OJ8DJt7Zx=D#*i5NwyS#m=@{R=wpeZr zs)F$84^f?k5y@aQnJ4L|=@v?aw{}~(yXW!8>Fmnmyv4=);jwKDP$%cf&(9^#1Yzg_ zp5K~5yPBaqoi5y161J`;wx>aBHIzk1MvJ;U`)$neiLnsONN7^(t2k>lmXxAC5@-~- zUrF-4t5Sk4+*3bBJ?;7R(ep)WlTqfBE;G&mL*W;x1HK*pR=tr&q$16wB;docE{UK)JFQVHP$mbhr^->1{ z0dR+dn)%-R5QI*g^)1aXgtxHbp51)oRy=%^j`2(uiWuyYn9P`Hc25_AlWGmkx&5iz zg9F&qiX8=GhjjJFeFKwjCH8rBLF~f~yy}0v2zsb?$oRw%`FfuI62BfJ9*}j-h+Y#bWFgHrU53Og{%E#9Ye6 z2-eZWQt_X!08JHyX%tjd*9P{+gC)LoEq=%WK>3Bx@RMj)7mtJhm-eS;t_$~2f1BVD zKq1n`y7Th@ry1qs#BMS@Pp#Xq;e6PHOzO0|Yvhq@dlX1h_B%+t3dMW||5zIl)?!$uMFwv#4&Y z@xH%;HZ((rcrP50{y76}-$2tOn|!_j_7z+{axWBzz{$9e}DbI^TrMjYe!Yr zKk+Run%mYS`Bn3D5cdz{GlZuhI<$7iPc2;QG>;c4Ki4>k7CqRpf=~(EWh|I$=+(8g zwK{4BhMUJ*CUm_K@YfSL5(Vv4(j)07$x`1!nMhqa{xs1JEltgg;Go|ZhtX&6?VhW- z0t_`2BGpNR@pgZ{r(-rqbzbL`q+G@}mplrt7r|_R`A0=0Ds8HSG(3wmq9(RHOV!$z zLc9b^x9UX&y^3w|`b3W3+x3vd>3HjIO?qge1&oXqmKN>p&wo&rm(puFSkg8#skq}2{BZbIApn;mSDF)fCr*3^Z) zNKAwmywV98G((_^HuEX?yxw3?-Y@~h{PY!jPOBNJ@4@xpw=PR8%-miSpT)SaD~GZn zEB;%Cr#5LxaU0RCTQe6htWB%{9mnWF$l~#|uvNo1ZSvrzW%8QGh|U~G)Eb=Il~P+_ z%LqHqw~%o}Tusa1(}Lm@abP5yVZuzmZFOLH)!L>FLKXpx1vBp~L+@L<1{OBssEFHe z?YnZ;QFkmmFdl;sX2Tui$IYfbEPi||@1W;#_@o_)q)T9unbND+v{Ezy> zqKZHCx>fwQWv~KYeE)Q_)s{%m#|5FF=@2a_oeJ`z+4`jn!lPQqV&LE&7rA3R4GBy{>Y%Q`-9mS{Sn4q6Z2%a4z`UboKYtuW}Q`QjP7R(ut znW6&3$eNa(I(M$SZ-r#?8^>PgF-JuhA2n9AP?)N2ApzvnMBU&J53^t@Y{^naj$J2O zx$-dCzp!JQah#7qZtUKL1!%xTU_s@?yYENA)PMcztJ5D(G6X17pYkCW*@&0 zW>U=WLW-A7{6eSgI1RuX^;OOXM>i4M+a8VnIpI+M?A@swU;G;?99Al2wa#^5lov5k zIa-@qfVM>1MN;wuap4d=@RKtg%e4jpnc#2*Z(mGJAp=&vvCEP5_NPkcCKZl-IoG!h=#fhjLQnC2lBUypO@IN#3Qlc2~ zDqTvwa&CpDwYToDMLE*JWM}6Pe;194%p^?=rDGAb_1zcdb>(sV696fdRw?(K zrbt6bw+7L4WFm=}LGo1OQ+IKH8zRi5)vQjRjL%wl7HP(cc{*|Ot3psgyRme<8j;q! z0LG0PtZh+!@?a-YGKg&dI~)^gkK@IWL(?87#i?<_X;q))hutoem#Z)V{)4k5Y}Upu z^I9>|mrn9AgG7BA9Aw-@LL$&)Y}jBWSxg{AzexVFv*J4#w(qJvZ2m~%f zS#2r5I=!9%38b?|8cL7fRX_LQ(K&p!-gnn2JQA*hC7q##3;421NeT(NGdtK4IR;yX zHZ^y(t)3ga(d(b-tz|A#P_TNz%PY`~y8C(g4Gee_;E=Yxa-1Wy5a%U3!frcV*)IZV zlPpZr1&o-AA^K&_MJg-vN|52MN6`Wky8*66sB>~M3isrE)%#Bg+7DO>%$Bk zr}>@9FX*uMi0p$RupQk+UyWP<)5#Z!uf^Pev)J^bUQ1~)?|8gXPw9Lx34qeI+iLog z8LvcG-D!^skhSzot4dKMu&5rr>h7I2D{4}yv52Em4I+E_pL*4@)yXu~7u#pZN z8}yqsW10P2u8JVrYWF)wRJkR>Mo^*%rPDDkc~BLXZ&Cw!hl$GuU%k~}C=mt%$3Z=K z;y`!jvWe$(&Vk5&H#8MZ?uBOged*&~M7Hslb9DL%371%lQXAqasfbnpc`QyyE6XEq~SPkV-v$VSaKJ6#07NL1+cDa;|9) z*P|}l5jc+h%$$kP5g$Dg{f$`UuXIK`&Mt5XeHtjr5hq4(Dza=#;rZ6)KM)JERTU7$ zGG4!l#XVaJ<&w2ZDoPI`lf{9bTwPS=jH1d6S{TfmAdz)lCM|~N#K|;f6bgHTi(GRd8e-m(Q4 zN*>LOQ4GfLgN^Toi#&s@U{L*fw98jRX zSKWz4KIytLWyaVoJ2WOXt{BCYGlUH|a3vS8=tL;%F7&t;9ecyxe)2E#r@4e&4jfuTau*tddJJu9DL6}YU%yP!ij zfXqLJhXezca!6FtpwOUG%?W4QWrc&vyY{J6&{Fc5L*3=6EWi-|IWT3vRuzM3om8LT z1wliQJVZ*6hyhDzlhH{6X2>MeWy1?1{nde7b`AE#X-uw7(|NAcaEU^|go5a-ILT{? zoR4a`VmiZ7F7+}_>?Srn`G%}CaJt`O9}i3}HGfk@S+fYNPzG2r!VJt>YuGmkOUf%F51|p<$wTFsn`4z^MAddJc_HM$ z8l?}GwJ$^gPP#i0j|3>8V&Y3pNonQ_amd+P6}X|?9Ai^r$I6i+8Zt>Vk>wNrwy_w z4zxF#s8~M81D_D{s2N`v6wQs8ur}B$C1+Vk~ju;<$fXUeF zKzL+em^i@uDygh+K0W&;7%jmpDzG^}2X%)`A^?Dt5k_d%kZxCYbVPRkZS%Vd20Ecw#%MU8@3IxR|CCl5xcAt%l+>4=K^=^`CO6Z`@d zbvPbWX>I+V56&ga*qmf;1)mA+yduT-D+L~wPAjylT6g7{RBKe|B$#R85xRPmM289& zY;>k38Ke-=52|sO-^3JxN3rSFHM3goz|vE3LqR<}7YlSY$qVJ8oo^t4JSmv$4~8R& z0I)79C&4ytdu;Z?id)TL-?5cZ_v=#aZ<2AmBzYkh;Zl*!jAT^({lt?iFdgdz<-{jm z3Q_izR@4DM8wm#0f+$NNRqZC%a~LY*`1L+(Ifk~9rmL>1)al_$eTuy zxisb@r&oj6P!q(_5!XQ3YZ|8!A%b^xO{-75W*Y~_mgRaZEvq=5_JrEMbWqD=p9D*0 z9BEY#S@mx0J_&!cF1i^W8Y;;l;K0|^h}4eYF(ILlQB~A#C-IvPJjF76lnK}90yICNMwNMyKT<)z#g5$~I@?V60zIa+u;IRAVAd`eyB z4!~^Ab|_b?W+ivM)ZpOa-9$q5LG*#gCJ@jR$mdN!Lyl-0C?a}dz;~Ut`e7f7!CK_T z<~H}N!_2xEpp_f;XR}(tZnVlz2(5rU9&}Ta1`MZZ>#3P!Mu8h(nS;gKLv~TTBrXw$ zst;lqK(22G%b{h0Xkbh1mtTLEmS&k2x1O%yuBmBdL}_2)<=J8t;IWr9sllZwAK zHe2(O5YuM$*GB4_gAHdj+^XeA^1O%WZfdYVOn;YTm_R5jWM7Dce$K=?1JBExoocPw zyrU$NZ#WpsM`>4Oucz-=W>)<8^yi_-WTb0hrR=nRXZ01r7)UeWNZ9_cGvfNZsaKT! z$}w~E!E+zO-anBD?!RUp>wD%}vV5%`t~JDY!&bk~dc*$_z=m&BtKAg2trY=~igq0@ z7DCIFeh+AcHbLxs+6z#Q4jdE%$Zpcfyr073ezlI5(e|iFh`d)hGKekQAAsXb2A)uV zU~)O-*U_2M8r!HUAMMBc*oy~L*;M+?%Z(f@ySP*kZ}bYUCWkIDJiEftLAtv)#UdQsiae(U4()B>j(Pr3_&<}Y*ASjVW) z+!0)765eN&w`3JC@j$V^vFk>6;ke}3=VxRppF1ekk*~T!KisC6HTkskvUGFVNf8vo z=DftF-rwYF7J8H_v|NH(FF2CyUymPJe%{V<>M>0u);m#piCrqiu_Y2_E$OGiYO>-m zzwK7)_8ZYx@R+$n;7V5X(G!>F%ce(Laa9O)h=1~p-C?n(t8XgosGME76A~UXk_hB| zfIUugQLIJqDdlowPb2*@Ssb}L7iN1+Y($!kVKTPh8T?fh6%$QVOWH@9%8+cZWjNYG z(QRN@(Q6k|H_HG-7FpU{NDOk3U8epyTF;qWxZf{6usO`G{um5f_D3oy8z&&Kp}8-H zl+hdf7V9*YQ%6Y!$70K*p&|%$FvvEFB<#uOv;8|3f-ejX)-6h@ms+#wA4Q;YW#9rj zw2}prRu&_D6t)AXAXO5!J^YvQFe>7@1ChBdkg@PO?R7zlo( zjN@PZPcqLSEUYU5VGR9ZpRH~KOe5(7K^8%w9|b`N2H^QqCxj5Cm5$9n&93+QpCy9h z?`6*F_lI+2Pw_mq6$Ru2UC}pc4A*%22RN{*%A`RDdbSEm3?i%Qg`$9P55EW z_?*cJt<2B-kN&w5^`3cVvVp=2(_=)ru&MY;!X1xHB`k$j;MjIZTE1y3{k$^bFW%|$ z<9nT2d5FD~rM~zBu@%|oUUWvR2WTS9GyBqC6t3Lc4_Ns276LP`TOLjLeF~ClklLTb z(Tq^x7uVtxVeda_6X#=$CZEeMG11tqU?vpFc@l;*^Q&%YX(4`Gm@PxY#t2A~z(w774H@GZy=hR%JC}UBn{5kL z5n@h>wr)dm*ocWj+}Ms(B6Qs{!9+;#5$95k#J4}uUeEe7e3YYj>BZ6r86o(GxYtic zuC`#njMMq%cLW!9`4^9MCLD5WHr|}zAPi(^zi(bhq6@$*FYcjXkg`9{kAZ&DI=cGZ zxrLIxXf3w9>LdLy^D4nvt@t;g| zGTmc?wUgL!P`HvEL&ZiHGIRsaG*Zrq1*w5?+R{*+Sa)k;jE~S2R*xA3ZrZ=*r_!|C z8LiRw&D)i+Gi9vO1H!0TmX4yoQ||RXHlXlirv@}iZzxQo@<^Fg5n)TQ7Lb}Y%Wib0 zpfj$EA3>mHc5Mc+6ULBJT9((B5=6pEC8WcgXF($KBzViit;<_X00i7@zMDHXtiGbv z3GG-Vjs+O^I-aPGJTnWFUX*f^6_Yis+PeM*U0iDusS~})GK^tHVH904 z9n+lItChNtRXg?PvA|&|P0sZR9<|v&cA`|ww z0*wVxZnT^GZJ98QFb${vb|125nIzyfQi=GUeFM-APk=eTl$l{IS3g%CthJGAROz!z zmS$kBF1>({y}|RC<|^-SBOHF2aGwXQ=pc8hr(vcYaW zyG~G<;jH=9SarBrF~Wqf?9r%~^{tQT$fgC)l{=16fcQ=9_HYka_<_r%*FinGCOCqJ zhU18Urp`ZM-P-#(I(eF{ofOCG?$l`gFU&#=(O$ek3=!n=pR&}>H1Go5$?RsijMosm z5%sUN-@v2*uL%D`RUm+$2$&GoVE(g>oXi{QzaeBz<%)#szbL)fV~_P?ewx}mmm zEqfSsKRiATH+mi&ZSsf%$nuJdDJ&>ZcVo#GBGdy6dvQ8r{U2riG0C4N0wm?bbnB4! z9FjSosMmcN+PAyA+che)bC#%is;UT8bYW%1oQ$Ntfe#iH&!X@)29=>!QVfw(!L)S+s=IxEYR^EH0bXi?w3Bm^mn#meXIqah3$dThrdxY>-!Hz>&uO{ zZ@A^#r9Wk(zxa9KxAZTIILS^-Tjw@88NT>`>sTz8=O4@%nw;-HTK(MZe-Lulu!n!4 z(QjazLV~bmJl{^Z{&V;Tu=+=-N2HSDzpyz*0<&31F5`*D89!p|>Y!{L0kXZ0md%&Q zuDztQn6+CICgt%yuDJpytOWvOPNCUB_4UkSh|`w5b|xMr5-dbZ?%Zs)}YMl%PJ9QflG_q!>MLs#GDz)UW0@x9QIiKMbba$;g3 zu(~EsV7JDXF9#D3=T@sdfBaN{l$|949~QgakC)V2FvgE}4~}Pin~NM>|A-N|v4dS^ zobcj8w=~+UH(TEd{COj7b2yBs)UFfGex5~VcAp~)53!sc8Bza=iyKtliC22VTx|*i zJBfZSWdkbl7mO7l_{T0ju$MS5pVx+*iJ?d=li!sZw@o$PvcD(OH%~6fN`4hcOzeq8 z7M7Pwb?T56lWQz4FXKv5gmv7w6f7(*;v^Id4*l>DcIi|%GAd|`@hmX^3zq-?MG-BI zBlKKO*Kz5y0ylNB+~lNh(vqUPyStBiuzWkmPI2+^#KFO0N*WqeLVbM-FVS&a4Q4qu_Qs00Y{6fvouLRSA=NQ`?iaf;Keum0KP2koR1@n9z5l9gUMH)!vQSka`{iW8B zB*JzK&z46c43uDw@6RW3Kgx1FsPXld7?+YRL{ds`Y#hJGnLpIpeov9f;w~EKx)1!D z$XkGQMROOq%TqtfJUZ+yJKg++C}c8;nBtf{lwFUQ;o-ysBRY$<(pMyN#5`Dbd!2^Y+r6oZf8Z1CC$xC7Z#VsRRJ1fWB6ke?Q!Lu z!@tE}KCXNiv|4NuB@-#e3m=}am+zOi-E`P(+~k!K5^p|w(yOAY&hG#2n*Qs+csy~L zJ^S5+fq|iWuB@c&Y1~oQGt_mqJVA;qKuanS!>a4fJB(c%iN!GHIKxNj`FMUJ z2xxpeU#ZjiNsP*Odrf-Vhs?j_a&>xY`Bu);`Sf@j0-;KOW$Ud+;CasEzVrD;4hyk> zkosWSTKebd&u8rUR+ogO-BoREEzU|l4-^zOpI)2OHoy+Z&nU|yEG(@3n2V8*x9IV- z>Dj{z8&1Pal7FGiZ8%HU+1MxpTZV`ML_L67lfaEsNiWwelKU8M=WrlSJzgAu)1noj zzdklK#U_XJPB@@^k5L8e6d}^*A7_4q0%!eWu&&0UYUX$} zmF)fYl);$~44ScelZmw$*Q2RwosNUl8U7!JiQRAV+n~gBaX|hg zk^wZ&(1gAl2g5w}w}qWI!?^VZ`<}OVdhbdpv>GJnlrnbiRRn({yH^JW!5qlC6j0qc z)DE7Xlapf~d^z3L;yB&ze0Ctu@U8(=5IW%K`N)dGVG*Z=23LPzMy;q55W;Hl9+4Y_ z#27D6rZM5`yr3O{TyKg37}|FQL(Z7((Ec$O;C4Ar(-vvA|Gl6rT%T9+2dL!Pn72_10-<u(?$B(3UVGw*dH#QySag1AayulAhugNaNTmGpkJ0C~dJ_l{?^+dJd^ zzC?hU{)NZm zKBA$a)iF<4KAJVaP(bj3fFdKdSK4T_Xqf#--_oCkD}qkcg4Q0oqgzbfZ<~sqs)rT= z@>!&RNC7&p%u6ReFDvxo3kyX7k~{}Hd4Ue+&rIPku_%88eJzB}<>?Gle#Q+tt<;)p zG*~)kSlS$ps{(ZsV@Fe}v<@NTG-RwQA)epHzLkDv_hVu(5Esp=LDYY4|M~dN|I{|q zYw`v=f5Z%NFlQ>tAi%A}I=~WJ-ZK=5IWZ#esc8HIa0wF5fQQ#gMMO?*hDNHI`I~d2 zpQjv=N2P%9?;-S<2@KfOT^=7Z5*LMj5@wwAGm|8-M1~#ReyQol3c^*Pr1$>ersB`N zqt{}OvZ^Yk9mOH%<7DUP8wrl^=8A)(T{r+%`{0|1lZW_rVZtUWgmHMp*JW_=A*=IhI!pI5}c zAX|qXO}E*-|0$~r#06ylAg73R>Un4e-!ltcGn2aCtLBY|!Ai+z@zZJ7tz#wpYPH`# zaLoCrgatu_LrOR!DqPm})Y0{Mz&M{?o3l8SXIqL4`=wE5*v1QevJd^|6y+Lg=&j+M0FlBejvLyT3sIg$$M{V#(wi^vI}PR04wlfE`n%%{Odx{X0@RpUYd91yo`WPG>8s&T1 zm=_mULqECf{KCYM#Mn-)t6=c*{HpI6+MRgZ-kw9C{BQMOEiq#$Ot`M2Op(agfCg;P z#~>#+Cr{}%)xSk{8;TH67{u;xZqlHse6J4^D#lng7not_1!5(!EnyHw zHPV6x{buLGL{7(l#MpBb6Je%^4#^&JW8h_Q8sszXDF3@B0u!ru%dfoRd*g z9|4szXiH}o3tS7;CdKd`Ow0}7j@K`OvgIcKUwiKr&{Wg4fhwYcG^HvX5orPJR9FXDRS0VmvV$%<+6z*9+@pFeLwGho;diziF%v3OCOhh zkAl#(+tf%E?jtfnXTV*n!#{$5v;HTJrH&)PBa0kW!``~z=2hoIH@g$LLotzZ2T(qB zO_;BJVjJT)^@(05WPj-zKAp`8kZlx}Oa67`cut$;HY@!Y|I)0UtWm7(N(F{^lMaW% z{!o}@7Sm^nZ!!<=;MzPmMi51ICoCMe^=2WHA9YZbiLz#jp6H~L)08mi#nsnp6Wv2$ zXd1+MTj63U#{bkGAglVVj7z>PCA+5nND2hJwgPrJU`%ot?7gWjfa?udER&|zZ8SU`fa1K?qk(3J2GYOslrNu9pR+^Y=K zaGc7jK(WC3?GaB={B$4+Lp1P!i92|*x`2D}u}39_<#&fh^thiKWDyrey@}@T7omP% z9;vIO=PL*7Io|&x27z92T37aCVg#I+%uv%46cQ>Akydw4+ktH^} zlf)pcR;l$$J<(c&F}<@rqaRRP^H$Yf-CdmARjFUc-X1bDrZ`%zsHmn_2_U@NVO#5= zsrR#Vz910ZYNgAJ2PhwzN$Z2kdvFXLE`<&vw!Eb|K|vBHCFY8*Zi$a2)j!%0F3Y9 z*)d_fcc$93?IN4=0S;Edz5{wYwW=Xy|0aBQ<3d@l;Pq!SFpkV?BtL&wlTrL) zB&ML@N9Wy8mUQN-9#Hmlw}_}{&VX!Uad-((ESsb>Rcdp?3X3d#9H#v63Gi_^E$@vk zs*|0HoHg%r|2lo3;Y)6u12paX^_N9AOup5to3f%qUSDOSIPJJt-`F_6#0|>M^rGYr z(idKQ-fY&$4o!TP9q(Vi9%a>)vOA^uWU#;Yowd#DFFpOnJ%r01z|pJp5kiVj@)De1oqAb7fH%u3d~-d;v3<@I zERFY<>$%QkdRry_uA8pP*GQXyvlO8$yMly3te98Z_4((QLFaj`$?{elL2EZ3q3I8T zu;1L1Cf;j~98d(F98zC9!7D89);g?X;xmW4F3wNkU9;(pELT&Dl+ih6n75Zzj=!8b z4n56hL7+L5w{AjY`MKX#Ausu9NLbvQepy zl0*IxrNM-Kt@UKNnW(d@kHpjBvu{bu9TA4?GXVeL`4c%m*^2UcXvCe4$ShYu1ymnp z6KjXoo2M}}hf;Tgh0hw<#cU4AyJl^>9P8Pqwn%d*z(=w261)o+@qwkMr{Hy|(}V3F zxx#6EwY&2$p9IYDUE2iqF)T_lfR)31HDdMp%ElXUgB2egDvgAo!`;#P6=XY7#<1kv zOy50{rRW_Y!gYa6SC1opVjpS}qfA$!mX2ex#brpJ43(2#tVnL&K}H%Ffj2Aa1sYJb zQiS(T3HqOYr&BORSofur)cYC{=4=)8bbh1?nKMGPaYw9&T+#|TrKWO@~?)-0=LT&sFx3UIT`=-?0LTq~LR`F`sco|`7d)6Y*yivvf3 zMwK_CKb{^pOiIw7pjY6_KUrKpX`OiOhh}l^sQ;=TK?(I~9B^-3fidJMcoTpH&V+(!%}-)BI?cm50i7asGG-A-O z62=FTG9dr;_u#bAbL(`O!RbenqW7Dx0Nz=nEJU9OOg|y5v1&!^ zcfVc8o8|)ByvEPSTy0er$1Ws$z+=;#17NxFJsWvh*&vLJTB&1Lc$FemSe2Ekq@Y^M z&(>=i@3L4JFEuAi#ap3{@ZLQF-DpSmosJ{1_5y43+dp2&lav4}qBwAx=yTRxzlL8% zO};N3dlE)&*1?Rk${kaRNZvFN0IxkUl-!3F-iVH*a&|+)Ga)mDKXX=xOvy)Tk8CPh zMa|9Y#w|w1fs(V)5{wFah~!c|UzS53m+5gcAx{b`AL}s#0Sd;J{Ah2`jvrO3Q!&>n z)2I>$+9?1Ra#OPbqe;FiR&5g=&RkM&C!N!=vYfIi*IWk#Kr8I-cp@MGf%);rG?^aZb5h3)D~WiTq?1 z4Swt_TT4=S&ewycvBsDFE#!-VVR4)1c&FeN8(Vl7XL6AG_9585Z`i!aV0%HfsEuLa zZoRO9ACwT|-X|m(8MKWvF_nwO78!>yp<>o1iXvm=zg{s)JmYC_U-?Mjxp<+Z>6|(5 zdL9*Zk43x&dvV%lDFPR|7>Ta^f=ViH5ir!nh?w7Dre>p*1O*2=0>I|a>Pxlwpsivz z%nf6v%NC0CDx!j>cxhSzCF(1?^@~(Ki+;AO%>099g%jJTie~mlpse%$^EQUO-I+=Y ztEqf-3^5fou=p2j8R`(Dmv%mh2Op0E$E@D`u(hhCs27Yuy+3x#5j37H{PEdL{bhVG zNAnkOay7C)jwZJ&|D`VcnLi zYyr_dpZE3OtERErbb4`UjcIAdiXFGdh(v0!Oz1a#Wnr~3wExswg@-oHgK>p7YaZd) zyAn8<;DaO^D>>!Jv2;@&oo16jag-d1M<@{8uIY2+~Ykh6~ zdQ7k9kFsi#mZxxLZo}UAqmR$P>n_8`JJ?3tYmNyTiHaqz50Ya^ z-||H!tP-?nP!i7a@AeX1QDybpeFZnki{B(ZEa6>s3rxWK3^gSPR_f3S0Sh6(sgs^M zEB2%D`^z!O>R`1p5t@6x-4!*D^c_Zpb~OwXlk2C{69j|m6C12I)ra9$$mX&AgJz-l ze0WzwfvDPnF@D}BAwocSJa;`t-wz+v!MOYGl=>)Q&hk-oaPaPtUsbKlQ5wISeA~se z3$Pz(>Sn~+ihrC2zBW#eQ0&iY<&5sz6t&XF@}+ACIhs!c!O%6>y z$OMP1Si|PiGjeZ)C=Vp#Cd#t(n_{oyf3i4ht8CR;RQlU2=;VyB8XWhO4NFh2<62C++uL0#&IwWLN{C%1U*RiYg~%k!ktXk~<_c z8Zs@;iO2j12=S2T;Ua9hq>~}W4enu5owgyS~iZvN*hfFCl! zv~wii3T_RR)t)oQ&lF5Rcpo+v--bU*4OeZTAwVPO_OUH#D z>83t5q)Jvl2<)PkKZ&`=K$WwyxNntqGMj}jc?e?IK!C-9S|-ZOnTXc$9U32+iA80^ z>+iv}i}0UlFZt%6Z5P7vniEM&!{BBPW2vAt@Gd*EJB9rx0XfW=HfY#LO%8;XrYwl$ z(Cr(Bmz6v%F4fr1CY*d5&aDlO28xyQ%*f&#WIVFq?&`%ki9b7X<@wr z%d3;}7wM4^xb3c|Py>@9sfY2YOnx$`UWW1M;uogFjr(QdKY%?cD-yxfP3y^#JOIQ| z=V8I&1Xe2y-UGPnqlbHs{?SBenHSeGC~6l`s5`@ysZ;VXdf>N>{LIp-hc)X7ukhMw zyP2&9N(Np#e$!Vl>sEkwD{O<4zwq4s^8j69Av9sWJY*}eh}O)setZJ zB`%TkLh==iN504fyb;o#pR`<}$L(7Ph&6hi5v0t=P5u3gwp2X_x|JPgU+!rc%sIp$ z#z&gxemNI+O0za^seY?@k?1+Cmy+^WHb~TVG>|zG-t^YHkXd})daV1-uSq~o(&#~8 zsWQs5x8*#pXyjs#;a*OGN9M+$XwuIHRvpS{zkUn#QDG6yLpQB}8fJ54dntiL_MFYB z7Avg#>&v6Z*^#$_6~=6l{^$BvF{)BpQGAhyjp>DcbC!d}KO<^#VF=(`v)Qu9Rk*!in7v}1EzOl{IgBhc z-4tY>3}uq7aiI4{C`z|w>)H$S{kNL?7oRMW5lwUg$J!^iWab znd=3^n?Tl~Pgv&*&)!UbBeE^=abec#HVxW~a6!5g;fa{8BD9ijM-kch(zy-NGJXM( zQ~^?5eGx*mID`kOr1N@`6TuqE{`%oX?8W=J&w-2-O#CsgJq!omuqc3fDAQP-?cKte zO-hSN2W_yLyC4U5Z6)`?yN7%0M8oy@-UD}!iJLrskgA< z>L2!O7qhGimNApI=&^NQy>^PPS_OmyDWX?K3J=>WQ*J|z$Q>?x>vxsAw&z(MC=Uta z=D=Wpqx`vT^NYbn0p)UN^0Y=w# z+?BUEn5xrUkFBeKwTI{VpPi1I7ZhYJCzNG1O@p?gP;oLohAKaH@cwt}XUxtjeB^yk zzJC{O%hmtgTM_s$SWEwYtk}TB`#Y7B6Z+d_Qz`0}eh=|p9o<`F>}b>M9>199I&zc9 z1z0sNz+5lp$@@b-YYXZ)m{@-x7A}=#yd|HXmHsWHVEx*Ky!5GaPRjMKEtrFWo*pG- z`^oax0Ra)q4n#P^0^6@U>zeuJ;`Xxb@v7*MAi#WFM6PduKYg5S;WRagN)gxB*FT zY%KR;%G~32oz+D;g_S^QSDVm7fWP?9?vu2F!@e@>%dhmnF-beRS(+L;_$?miSCgG* zU->6HQ%cYAPt`!3+wf}1dDlRLmsIRC=}&hb8jPS;agVj$hm>Ybu9XT4@;*r&G=vRqly`zxe78u7!3og^+ z>g+nb*Cpb=!n;gBjh}7glhrTE2%T*VcdfR+?V9t5)1>h6POq<@sqxd>9BwXPmP_r@ zkiji%wVjUm2|=8^M`B0k=lR^~@|`dRn2gA2Q^!`ixQz40!}E2~@i`6lM|}B8=+|&w z(2Gl!frH1Pbl$vZO6d8%>33mc2HBxJhevBsC%xZGfYK*c`HuE=*m3tL@jLEFeb z&9H4O^GzBK%4@atGjWojitseV`^ku8?6NP-92i5MG^rWYytA*-@n_n@lHx*OKu{vN zRzC+y2IUtRoqZYJ9~=8#A9WV6sw~flO(N3C9lz&Q}ar zdR+$MJJow(vZbSQrdmQZp4*j7_h6yvrM)}xvd*$mIX>jR#6XH##Ny569^R~!GBeMF zuwqJBBn$)PuKT1g1cn~HgeSkV$~Rjc_>#vhLA*UkjjM&Kcr8O^CzEQyufT`(mnR&l z?p^SrCpkxOVpSPGdK7Ru%CEeRURDz^-@C7m(Z?)wDN5Y`!&f%b$CM(7{kg&x0G@9c zIggo)4i=n7;?JgM^X_~E)~~$iGyq{%Jcufq3o= z@jFwATm2~|e9*#!GI|;v@a3+kfzjuybzN~jxWZ8I^up&36K12sokf7pykCFKeffp7 zVqLdYr%=X;!Pt)UxH{S85cek`-iwCP$LrPjXxJXe&#We8a3R@bGI`GO=ynJ%dfxKr z;nx9%;H?a{^;Nk!?a~GO{TKDot561sfwSx4i&a*C{=fs_5`isxX-sx@?PX9zATFes z5x|H{y+BQ5@48mNJD4s=64(LiWu1R^glH&^gEaX$N^0d6=BjH5qAX}7KZ#4JG7~pt z@1ob^;{6m%}YinN#E6nScC)FE`kkG7MH_I2mX^eUgMOYXA zjg4(ur$mtj$e-scJ^mG#{m!O}Rd^3bZu?Q!{z~`{2u1Y=|5Zagd(+iIrVQvfI!BSB z)CzR*h`WN{15t-I=p$-Rq{o|ykUEx82*PJ)ly%LVCAm3<4yz47{nTL zaRBMf5ezGEtFFxhsV3h><;%3pb1b7SB48c+`CUoudfx5rb)9gfE5( zGjEIY;^zih`PiQxRDoKSB9!%v0@!bP*!;wd>H)m)-5Ek#;=TxtlK_0^KuZmwdPfRe zI4Y`_IKH}d_t+0y(B~DuC>R}FZ_b`p`<*>`&+sMuM4n^PG~le|+#~M~>RR*XeI)zY zs|@@;xGcZ(M57Ap)0&bW=S&aIsD<`8U(bz!HV9?H3X=iIZRnpJ#*#}fI7o>#FMqO3Zg?Tj*m6(S5YlkcHiHynr{afG&KbxgsFsPG->!5nss90&)s=U1Ug$> zJTMI25<$*5)z9+d$Ujegd7WHbXLG{5D^j$53+n^AT!7iQQTgLETipPmV0i$3CA!&8 zzw;YMoRwdHI;_>`dkhPMd_~&G6x|#{pu6dA(yHMg?9li^&KKS1CM-p$o*0w9c(pSe zI#KujicCwm(|BiBmz?N%&t5~2bHl~S-l_SiI(?lkSod;%(G7q9oOH>Uk+dkq6R}(i zaM_>7oO7b>&Nwa!CRM3!)C0>Cx4Tn5u*5B%U(kdiJuW-yFVtR&#~m1#k*hV6@>2)b zJ$yXn|DMG8vmcWUBHY)`q9#I_@Aijh=i~%fgZ2Sf^Vhwn?_B2|7GD_FKxFT$mGK%H zm~3<1!H_Iyt*$5Wng=@i=4?~Aa*}eo8j=xUn~yYTinMfbHlkm_hQdMYppRUzgU5yJ zVZmCcgfr{AB~h#UQS%2w)~2D)zL3fRkw|-KE6-Tkklrkvlpn(kvBi^#Y@zyB) zklg<8+l}Oi_Cdr>ycklF>>2TiR3zzp5w*0jyuW`wJQL*Aj)XRCguat@|4DwG_~Z(y zG4IvM<)cElgRq)z9vy^sMy4ZV{O^l>p4(Z6rjhZ=1J`>_BiV)p%x{r$Ub#wEqe)WU zVU{`x9WzZl+ayt_{X%l}udK4952(HOklz767sgE!o2GN{1(xhvuaw}&e{sN34e-edB%3)@a zlX+vxi89+&1W4AYHABZdlS!y(`8b0_m1GWy@9l69SNZQpLQb;m!LCV zbtts|yQ%LgW~4om`$;#>S8x7g%Px5L;9njuXdd} zbeq<&Bj!h>%vycwY}?{rEjV2>p7sCKBMHl*UGh@ZJiv{%pp3SK)w~B2XzE56qT9_B zDRle)j4lpt6aw)>b=rR`Y)x(HQ~kA(vqY4I?Ff-SOiOKrRxd5+H1|p0c_3?#1gQAE zcy?w*7IEjgdhUbdVo3`0O=695(vTE82&pJ%vdG>SKwBtcp;P z0j}u#C<=V+)`|g?W)aBDqJ~88Jx_dp$-hSZw=A!gHj^6XO@5Iz){lX-VvqE!5aEx< zkFEwX<`HAh!mpi3TZ;Z7>=nAV-2Iz;gfm-48khD!EmOvmjO|blv7kHeuZF(b2&}pM z9YSc>=X>!0iLSB`Hoa14`E`M**Jzy8+Iai3=^01h7d8)TRh(f)K^ zv80ss$NzP0u56iEuX@y;|5qs47rJ}x!xAb-uF>FsZ+{!z&xXoQEZc6m519Sj2&ET8 zm6jjIr^Q^u{2e#cl0Iysi5CW-L>+-1iZZ#!d0MEkc@43&H`o6*sTZ9q8yea)kx%@P zsbyk@qMYVmQ%G>X)E}4GqxtvC!8wOUZJ^bMCKTy0T@2q`^RHEU3C^pviIZY)j z=I`_F@5C{JxXER_De|iaC6?`<>czYtI1Lcd-2cO81QDOi?dv0db>`Ft5Mk$U$V&b{ zc@K#Aye*viKagw_VK-jAqV_+0auE|oxHgLa!>7I$5q1y;)&KPXD)O=2s49uW5Dlc- zGZy(er`aip%*u7J(3^l=)hG4XMj2j6I(O^n-aY4nt8se9FNom2BTOw%N1{XO8Pee3 zNkIo!>ls_jP-)Y~uup5@pQsS@4rQ zn_oOP^Cd8~u5BTZ4%vt*bFvX>>SxsTE(MTofAVgvpcBNhs=|qmFEL0tOnKci2KiUX)|Tz1%EjV3_@d>!9fn(Jzu0J&$tLrb#Hp&5hRL1EeaN8_E(BiE%=$ zWLgN{htyv8SCLBZM)yOwU^OdlAmh>UK-k{dj`T7|@L|GCw&+LrB#|@+`Iv2v6^+`v z*kjly3#hUpBD9W{Dj9VTuvDs^kii0dt=~#zs?71f%lzN5PCq9pSbXKQ9495GMFNqW zX|%HSwb>A}K%fE3zuT@^w4GgFZuVh{q<-&}rK3YbO#sDsTnx%IR^s>rdpeHH3BeVgh zK85nc1~}H_ti74Fu0ywdWon(&^sp6WRnMIw$`*YX7kUj!+Eawm4RY$JuHB#{OO4|Q?)6A-Dm|0- z-P4{bMNPAtUsi5p`yX!Ji=bqqy|I1bc3a!K?uW0M2L{q?u{%?1vHB;@qT+6?!>q6; zY$X=1fyr(VeH+ zdETE#=_YMmivbhXBAI9f&;DA!bqG!mNiLm|LzLe7fnAsBj_T4}6>~!*%;fqvy54EVP zRQ1;3LRSQVrIcc}gOn6Z!nar?o%9(km1-&iBUMBRxW%*AoAU))JNJxV1f8SIBiH*< zBg;(cBF=B(wg}ug8k52n?F_(=B7np7$Npo5R}L`-rdJ*@ifRU%)1w;>;wzm{b&MV*wAUgP zIcc9I5;(_VpP3lgi1hn$5yQ71ntBXKEEB!(`q%wAV|BwD3C;L+v2E#jVpMR|{YMc+ zj8n~U|q-=27>ZtBX zI4+cYPl{~Yhb)vj)_RhjrwW^OK#x%EL?S$N-I*c;9Y21PFQ~e#kHLT!PlI+u>+B~V znu=|uP0hNGz9m>lunwMQs|oUq5#1FQ9g*Q6;Dc{N zf`h#8ax0X7e?nxAh2x5p1}T^_fd8GU9*-I+#bGc(Tr4wHTf>AlN&%=4f0X9P7#-9ttwoe%G-<=O_E5#^;0G{LdDtK z#)WtOe4`$6FI*&u%QR@(+M+U|O76#odT{%WV2Al7qLo^tbzl8&M9H`NSU_PoY=Cik zYq=%satFEm^2ks|gR|u*a`6^^!N+CO!*j}scnyhTb>#R;K>WrRe+PCn%i@_z!*+2A z(QCIrtjj44md9`35(XE8dF4)bs+Ua`Pw-VYjr#5VE((;osE2=cu?Ne&g4($IKBKy7 zUXmAXB#OO>q&13w#gDY-yGt!oiA6+-g#BX z_q_ia%|}^u12?sKc3Kbu)*^Tb3hxwnnW)P!Y2gH0GBl7}cx7 z5oi8*G@DB7UG~VmM*&p(eruoZR7FlVT6Nil(Tn~>EC!wKy2Yi!SLsCWO1UmoS->_E z=MFU~7}FI_M-`~9@f6BWK1W)@=E3f(%RUO1vdcfyU7|y`PH|s2p}y6vJVgzl9=dG*1&^Uep-RulKW4;zjj1 z-At2pU)8cCLe>xR4!Z&`CdfvzxHKp#Iqt4B#R_TxCoYDl*~RxB2w@XblJ3g2u1J@# zl(&kyduwq)N^Z?hGK*{X+*B1@d4FyB{cN3IVP7l9*xB%e(`-i#tqk(=s1%+ZulAn0XQGP8sBLB=S+yMO8(lM3z3Kz{w)@epQ}77}rH&g*6gn z!R;(CL~_lK$`x7+C;eI(gNq?V&KFYKIjP2vrDCAdQz<+cA#w+KQ##~y`8cyEZyuow zk9A|?`Jj2L>a7l^wjjx2;(O(%t0?FSsKDgki= z8JfJ!L+#u@QrN;Zy+*b=;?z96kV4g$A`)}(iv47^#Ut`#2TZdwUhCL?|8!Jp%}&%? ztE8p@2=yPhNjn{ugIt2R@$G&3SX}ewcgc4qK|tgp*N`sJHqeN2JL4zsd>4@ly2TM# z^VFsWpaAyv)l+)s>;&_*8&J}4N{-%*)3lWnQ!+C3`NRGdtRSo3=pWS05Kv%gCjjw_ zzASo*dz@v^_1!b`K|q|39Nf>K$%%{L+YzzXXMXXdiO^0VllS`7x6S;o7|k@3Um{s` zSSUn5ctgoEJ8+W!S2c0LgQDjJJ3AG99P*H+SO^gFM9m0M8bbYOtP$V!(zVJ|Kw@Jx zRLYd<36(t?Hm8~Mcj=fkEi|@{&!|JHm9Hqum^^6Ie6W<03s6r8+OWs?Ox%8GDz9iz z|D^fBlYDs5NNOxh>XF$=%SfU*MM*N{W5Z@rJgeN{hDAk(Ds@SHHD+y@*Q`6&u&72s zCV14fkZWhWGA)U<_f+?FrpcB+R>E8f5E03vqHvDY>L?t}YnN<29&kNgPZjJdEAY;e zx(m{I9)`4d=WPIwls0PX!pyfy?kv>8aVBrM1Zj;rVGScjl%6tGcodc zxP~Ul284_(^bAuNKTdIoOGK_i`HEH&i1gO-392HY!wxNdTy-0_>lw{*^*p{DHB)N^ z+#&bBy5E);`cAZ_+i4McUW0Ly@pjV;A{CGnJ6QeDWgGTE;Au>I?02!1v-ugqi)@B8uJO#2prsu+ln8?8D_P~np9l7!<>Hfq zs}6$)h{*Ey@I^2N;C9V`#@;|+z9wP=4Z}>V@`0#gQXf%Mj8{W3G8ch+>kY@g7Q(~q+*eK+O)-FBs-{Z%$GkpZ{&Np%R-yvbN zSM%K+G$2B~%$`p{RZM32f+nn|4s1h8=6-4(P8{LN>Is)^G#5U;OVwGQrzoR4GH1t< zJpL>VjQ$KK?$(8a`mZyqdsxD`)ni(b5Ln(7A@e%9e%;VdV0D|lg%uvr=2^t!;-3vS zrEx1&Zg+&O3_}oJKLV! z7G{~Ugb2b_@6DAWEuYSEoV7`^RFAJ&>A|1-WHGioPc?AKH3V!u@_LtvOF3j51l+e=2}en3tpEfTvb68C%uGST6$Io5K1+CJHW z8y|w(Z$lHp1>|LNLq&rYjV4Tn5_zBk$D|%F=SWh3D zT0oQaHeLXQHZI-n`V79ac_sgWd0Cu1K-Dv6Vl8%PC=;nMQ)0V@(5@GrZ-2M@WG~Jo zfLnNME+?AsOGAWWwC0nTu%Q8b`+6lrmHB6x7nILwuDMQ$DzY}a4%~o_Ky^sAUc?QV zB_&|b|&t+Y={YYzf1Q@@Umm_o-??{))aj7vFT;$4;1-& zbo)oE7Fhh%R2~@bI$D!%-JvAv6#$!1IljYDH~REVwo?i6n{e~ah-=cB#Hc&0e;~%X zIa51T)65bxjLOca5#3F(H?BGL5_*I#w`dDnE{+0*qiPI`8rk3K$shQmc$St;Xm}=n z*hp4XhRi(eIbEN2p^=6*Fm1E<$&`(|i)`Mn zx+~E7sGD)uKoHndUzVeVk+=BhUGuo(+J11%pqsl22Nj)+V(u3+M(IVH)t>`yZ53kZ z3|;vb_s8{GoKq3A5?M&k6M5Rc_Mh&WXMq=YDqs2DUFaCAeMF`!v}NP#1e__KT5-N+ zQ0MiCoskjv1+9~8n1Ut7AYkBl1MKNciz?THbtL^%o=}0*B!G}YKf9&g!q?)%1!atB zrxB4CI?9u3DGJW}w0*{RONI|37?dmp`Te5HSH71aqQZhDfa=oevqw>cf!yBarRIwF zSl1@Zccw1IU9efcwO*cROQB-!G5)0)r9{ZbG!oTLUQT_WT7 z+wUdOU=itv*89sb8gmfWfk$x4n7{O6MkgUz=d>cfQ|nj^Eo6#-azeR214l`$WD=Cp3Qy9OmNs0pN#xRaBq_}pyOqyvuHx! zrJif^0+6{f80lO8*wimvOHkMZ&4yq`nI8?&-4WJ%MtSc+(eJ_5zxj^vF8#k);>g{z z23!A^M`Zl?zig|kH(A4=_qQ3-U+5Z9tik!^dB+7o~MJo3SSqi02JN3ylOYp?BKRN2&PIp?GWs~vbV;#~` zQc|LS$Py;Sz_o1tm$g0O(eB5Xe9F+7eCeS=B+@ZFvR+I-NDe-8tF~K^k60X~FdxFg zdn>k5S8iW~|5MzURtnm}ckA=UN#^TGP^f-|be{X<;CD`jh&P|2IVirY6Ay;|UtN2X zK6=jn{THEg@w`50&{`T9>M5LwT=-YHiPRxb>*@T}Cn^7HglJ-`)8~c0{`>L?kz}^M z>uLIzWKO()m6RV|_?GUkKZKIVH{ZL#mio7Kf2dtiR2LPPA|>+DoJvIGMJRUtbCmEC zCpz8OTTlP8>fMP~LcdxJx&3v8Jo`_?e--r~qW*-`%) wod224|E(|nTP6LsocwRy{r@|AuylE?QSq+EXX?uJE5zSRWz84m&)?l!o426uP8L-PHP_wla# z@~?GfP0yU}uI@h7yLMIeOt6xIBr+mC;+r>bkfo(QE5CUImG|ZiOgQ{I$Tx$wSnH5C zCcc|CzvY`Lx zE>vC?%>R^O!e1-;JD|6`c_Z>h`tv7M7pTKl)41=0t{ES;TDh%C7E_anPo9uW-o5z* zZ{60J4-YtO6-FDk{OO~@cr+0VgVao!A`Fi;XVA6~_ z+$*fY%5}V6WKi!ZxMBa@@+CF!QAL~iBbW3O0di@QHX{EkVZlPZ&+{#MXWI3z74)Fh z-^OxTr0|-g*q=FhFsTfnAs3F6HO#-#+fNF-w1?C5e1O`z=EE%fgoG-^duEmf!s=A^QG&tNcmTkKOQ929<%&Cf8qSR7z;D z3{i+V_-P1#CjgmB{eQFk->LlXQ~6&7``^9te?_ppcR-&zf~dgQzo4kd4*|FXH9zs< zU)233UBGAFf52C!&;e}@5F&WM{-~;owY{_5J>TKTb7!G{``pft?KT=76r-HGiYK>b z1x@vP20qTZ7FvJod{kj1Ap3aG_B!3i_OW->c5m0fY*mF|@kTv}7l9V;AK1o2@U3z}^A+H_IOSXV14`nUZ*_|#1P`F3*X zhx(JTwTVy|id?{Z5)@g2eFR++lz>6-p3+xstPt6d?v^|?c?Z||7hBn+)LJJXo!pDr zD|V6@_y9XieipFX+L5Js$?8V1+HTz94IG@FWS=faLLMVUA3?%@w zK{VSYJ6)qoDPj+u+UANG{6m`??eY*yLy8M|JpM8{n^FWR8AtPh<*YF*-)zgkLQqDb@|1~+k`Ft3eqf82S zy8dhp+HT926S*}~J7&hj`V!*GY%%0Y;ES9O6cRYv7vteSGRuo1ccX^5}%RZ0-qW&+SLd49z1{hCy~mB zkuqw1G5fiA{OVbWV_5grj7DzIovDIo(ZAf&)@0t(*~n~F&B|dVMShI?Rb4ymp}E^d zCeubQr#k1EEuZ)SX7g;+5n+NTLdTR74B?ltuI9n0mRpnw~;y1Ol{tM3gfu?sS}p)luT zV6FLl{KdMYPzlm3j-pz&n1~Z&eP>asT&_xX0%y(e%_K2=z^5u!*)1j06|0t~=VPzy z5f$1YPaai3;pjFN78qN3)i19A#~f}M6mZv2xvXs&Wbd;u&Y7>$UQT!DJ^nCj^()ne z>y@@}KxbO`^ekpibkY)CWYvoC9fKd6VGO^0hDcvQdr@Ax&Wpe1^X&av|B_vxG}qS# zSWO1=89D(WLb-W;6w^h%m5&h;r0Gq*D@?)V%f8Fbm|`22M*BLlm~C0Ee_dW$M2lQ6 z(Ax$;c^#$!w1#Zf>09M$C#e`Sdq=9Tozh@zURpChmF*DtIiS<7#UP_VP^okOQFykM zclb?tvqXZ|GOV0|B_@rF4;DAi%;@5dFdkyR_c#F)Arayfh8$!c?rN8in7)(qs~<|v zR!uE;TWhk+-ZO=0cpaaQFSgVVlSKy%>HxiRqJ2~tU8A$CW{^YBxt7lbA5)!Lf9%P5 z8{n?&T_FT(Y-7=Aqw*R zseYf+I%p7WZ3_%=32-uEr_fJ!qQ*#?QJk%9fA$`!vOQhRct{bu7^QokXIZcZ|H{-v z9L@vap~7>u`?)=yNr(_ppB+u-y^6S+)NyI+#e)qdP1{5Ta#~6#_}H=Xt`nG96y{#_ zufj*fFy&B{6qsWLXOY9wbRg;MAG(Cu8~8SPEnZiQxPA;z&sg*;Qstpkw$`jVu6n zTe6pgmx!4xH&m}H_-|H4@R|g&WA#h{EX3<%*cG9+F`q5wzYtxot{t*R;_y6lzs`uu zucJ_uG#@waj`F$6!FeY={EUR3E6v-R#k$08Z{8MHRvPp9nwR`6!l)$S`~4aDv(A+uFH)@M{tr z-$!#u!t;Jc0w@kZecWP*em<0FtFh7D(Us2zpi0K+kXiYspK!h^LM{9<7PNU)l$rc{ zUtgjIOvzuLj*y;LydB8WTnqL2ef84|`SG=?K>m^$t2iS8Tyu=dH@L5eJC6meF~@Hs zGdaf{6Gz_jHBHuHRhKA2V5zO4R2H$2_QakpTY#0kVuY1Tbs|1lIapK{tL z4(^_3rzM*S^iBJNQ6!g*TJyQnQGR^Ko?aO~)oqpMzlP^^Q)5yY(Rf3J^FPgV-Z)7)Qic z$c{pruZ9lB)VE9->VAGm?fQ{|!%=vWmaDp?4OTIXRbN+pRj~#r*t#+iU&b!e_E`!w z^~4@s!X*CnU*~?2gERc{QY$~*Y%yMY`@Mzm!^(#OYPzkG>aeApbrd&VmgT55hEHUxk!OGLNH7b*t*iQ&U&WzPGKtYT@6x zLOQgE&p;uPU3JwE+Z^c9l5_sg+HfayB;ZQ@>FextpIMm?+?jrDLK@qVkRdkY5B^Jn zI$+NwJD_R357VpvQ7U{0DdNC;w)o2zqJ$v?>(1yT?v*cfz)nK^^8C(Ijeoo95&gHw z5Q%%?kAGDqAl)Dbw*Lc&%}9uI{XEZa{I4npq#JXV$C)6B#^N^Ryhy-t7;DEX42rc^xwFj zA!FN$Y*l%+>rj|duid=s*8ex|{~uFClq1Cs$jKp__><}PcZ414WJ=ob0rCjhXxrP{ zUCU)X(LLW;eQ6O3(YAMX-hWIT{jVN?BFfpvpCu(F3AuX((-3GwC#MJsm^@!sf>2;O z3t2IMk!w*ARVt3mTonyc0zW6VR+)cS+P z86`LKO6=y$g`q6r(_e_4MA(!k#m2>jI3q}b!QkZI8?*PebEqfqy~g|7lpeR`b+rQ1 zHkQ$`-5v=dISzAb=9>1tNy(@VF=&+b{xr1o(PI8yvNb0o!(V(mu4cN{J*6^wbyR%K zteu!ZG(@nJXK`^j0pBPaVhm)~V@xF+NB=bL6)z-JR@+Oj_1M|-os;#9dfsSPv& zC}~?c{lX&&KNBH7Uy9;$xbHK2X@r0V9!UzK*h;M7>E!1mytHT>PI95}_7Xkb1)?`{n$ zv9Y@|7Dqc#6x^EZmTC$Pn)K}zwQTk6-t}*=Dj0>JsHvLlgWPNueqNF~l8SIUz375D z2R8=9i6voXMzn0;t(=?nX{l zwS*B%El!h|u~PlT_*L6JLLPyG&W>)l;Z8XK+|@4zj$h-)mDs%Y+}yh^@`)>+D%j8; zPyD84C@*HYC&Y_ykviO+-LEaC+YH3g6@IuDxxIZ@0oPWhzbA6$@{b_DnrNxp$>=*> z$?&7)ot}_jHv6tSn(>rfR8VD6?#-k1y3)^lrDcp9{_vU$dHZU`* z4C|@@ex>TD-h!U68vloG;#r%6mB%lIu0GijsQ?K;d3?4$Rn-@BEZs?|CdC3^bV*aS zlDaOT{5R=mG^rF8#F1u;yt5yu?i3=6*E65lSFUyTyNc%?y2kSd$8*;BDdV;&E;aid zw@04GoxM~|>Qa7--LNu?i|{vjcyP2_!@BX^m}vuT7osK;Hf>0VF|S@F%Da zU7N7DUY)}_;lH!D!NN0>kmk8oj@*ti$$95b{x)0VLeO;WIFi`g+0#pM+SVlRjN)ay z`uMTyT1i`taI|HH#EtThWiB61&b|5A3_kKXd?m$_F4R@}Wo7N3iAca4*p*0paUp*e zrD45$0aX$rp?bR}0weF0$fjly5JFBK4=}-baz?b3wUi&(DqSf!(6`O+oBB3cSTLAK z2xcFV6);k~W6)%w6_6f?EfNY;@tKH-CR;IxtKY2;axd}|DGWcVYw6>om_sqqX6@OI zP4E6FN?@^E(I@|u2RjAri?D+LYzUs%8)t+HtgFQv|9QESO|32Js%DNA7}|ap`p?cO zK)a@j$&8I0UbfUB#)x&H+C-X^-?RZ(J|q`2Ma(McNU$-jgXd z{N=A#{T24S;v;2pj*%n_gmMN_dl~9CoXDn67`rKn?#1r7=WKW3XMr<{PuEY<;=4P~ zY@atiC3g>Pv5aF`dl$%CCer2pl){Bdx*MX@L=l)KyQ21wo%}@=Xxg0yEHV-nc|Zhb zrIFLo*c^i`eL;{#rH`SgBHoEM%j0M;u&QJ)iCSG@nRjPIB<@W`}+ zI!UKVk%kf58z;KK2YznA040kr7VsyY7s|JPQWIzpKiNxlgbl~)M>mUWId)Q}xEw@bg*PXyV|EQ&F^{gF2hpGuk~mL}+u zOM*QQq9$Tm)eg*S7)tclvX2%1r)ONBMOMs-{l3Eyc@GVe%J)ajc79dh-DXSfw>1z0 z$%T)6t|8bT08{Jax<&%TdAR;aWH50|$_C&}H{{YC#yAs-ZuZbWf#-);Qv6a-+)r#! z09M#vRMtN@d73Tg63GAJ;lE6udqnfv3gwGBFKBP3oY#aCC)QF7U=R3@U(8?*q>6~b zUkw1<{;X>Xczq)-u0qqabljSj+C52u7e6ntoFkj&At7gc+v!(~)X7F01Wib6%eO1Y zj}!Pnookz#;2>sU$mshSs*}37?j?G=VG25Bx_@mC0UJO$@ip=IV`7R!$?SbmAd4Uu zu8`Mp2u`g+Az82}?N+bzrTfYk*~Mb3$P~&bm(k9@K=@}LVRPhr&`+0#v8Hu}ckahl zVd;E&R`8LAL}F*7bIgHN&&9uWLlscXk;e`X%VsI#B`|d>VL$Nm8_<;|I6ClX*({H&%ElT`)+U3jt`0DxyAuz+LL%imRZd7(djHpoTMaK1G zlC@&-DkHlLgv9eFZNlMMQ8RIBz2neTZ)CQcm51Jl-P?{wjWiYI&meJQ>o0LVCx7`q zi_bFbIdxPV$=Cdad~nRRBafHw({9LgYNWYM;|IWL!%yK$_GZyoe|XVc&BoRTNhh<9 zN1w59n4hncZwa+0N0)#Q&R0JS1_L=t&rYAbo!JELh97`-mJkO~8ugRPN*<>)1_O@- zsQ`-LEC%XJcR0Twh_2l8hC490@(|&~>>w+$%~Kq@h}mr>I~k0znj>D9HU)fW3)~Hh z7Kp@zbx^2@H7@S09z=cS_{)ej<3(0sF-#iyfhUB`)JPWY#0sIkK0i4{8QnJv7)=A7 z7Ii8UvIcR75twJVkG@EAr`j-%&8k6R}Rzs+H+KBn(sOhD5yRgB&=75`u*&m@q zHkt3Lp-+;?gGur&2fhDrSO)#%4I7bb2BC_NNH+{hENTDeJbC%va~6HooHh8Gl$*gv z!RM&!%fa_v?+_#^KVbP%s6JTLA?FAEmgBQbKhuy@;9LMWU@5n*<`H4+2*ZSh= z8JR%+|GXOmY-m?(De1NxHLE8!GFOHb{CbygW^?r>3R zjSC@L=l{|I9*Ync#&gjEboVz#k{SmIkIQRUF(teL|Npz2SUn2Jcm6oe>9P^>i*ZMI zJhm)qIn&8dw#LEQReBz-zQ?6Dmu5ez6!Lq4PL^1kq zwDPUWICp8f?T}mJgKi3eoJi&RC6KFg`e&R_R?+;?{=2!3Z}xMGFw7FbWc}S~bgVfY z;;Ng;NUUv_+u)=-EDw0R_v>~?qo$Dy3&e%*7v zeQ<*Zg&vf>VJ>XWp!SSJD3g1`Zu__sSj(12a4P!=5WM?74VK^0m5q4Od2e2-;Wisc zflzWl%=JYp_=|B*P!=66Ga}~*cD96MaiOYk{WJ1hX#-mcfW=3iQY6*Bc~Lb_15>M$ zhtpsZ|0olMTK{MY%DouVs6WWGYl7s`-xHOS5tvYV(t*jGQtA_Tun_JxK4N}c)cXXz?ali5jKR&`r{uB1eY>qD*-0rOOsd9x z`!iz1T2aBg9oojsvGBN$PmR5|zl`F!lAh(xJb_=diBd(v-NY7q{SywB9QK#H`! zYy{QCMkBwt`XOM2iO|g!SM98kAbIXchz(~fjst7hMo%1Dh0*TE6#ch#)rv7qDxuMs z2Z0f!nQ?bEGMO%us9qYUi#w}+cQlJJsIrZ#HM59tu&0mc|K`BgQ#2X8gQ zZ1hCya%d7lc|tkKD=SnZP#p~IHHn4xJ?Jb{i9V#$j}@M?0kkBJ2|B$3lq?CyCAo(w8=WAFFT;kuzhR=WVDjQ05g%o?1_ya8ibt-ktDx%k4Wy zexF z)r{#vhVps0g*x)0g@B=eNJVz_+S*|TB@w$U~}b1xgCsHzoGvxzmD z3U#GXxd){xgp6Qap|90UrU4Q|w6DGZdmPm7!j3cqhD+6KS%=i3r8A^Wl}{+LmA6WY z7{gDTD%}1g4|qB9UBiIDU8Nk!qbFKD=TOU ztrN_ZiPF40m(o&zzKv1t6nXq5+mp{}u#$h|i~JbMX%NcYExI7AZDSayoCcigq~ADO zcu#^HU6iRfm2{bhZ6^Ef{`{#(pg_KKv+&s5<^ z%oT*blP=sM2y|3uerUY``KRGA`PX^Yf+NOEH^s-s4L1PJ`&UA)5QAYI;%KAWaK&Dp zU7Sm&i$iZ|rhH-eXVTy03KGsimCRP9gIaB>=Ogqeqb#T?Ls|!%?b`BGI4v~P@GU>} z65+FiONX8yYeYo$+0WRNj@MXaUqnD$G49K+>4q58N9HnJN9n@tcztlDUC%+O-K!5p z!LwV%pu!pNooW2+ugQ$WKRLwL!t)q>Gd8Cv6kF8Y0)EbxH)qkd zqJTMf6M<*l6;0v;tB1}eV^KG)k5B!`luBsO!4sx8!K~&|V%beq42SMfxTWe;723K4 z@clC%*0M*d6Vp(?f@(e26fl!mMSx8tdPE>(^j5bX-g1>snUL5#JU3B7~Mx>0K^MG;GFmswCSI zhzSk>~Y^`T#mZkATYbT<#ABSa%ppHhvM!HX)w{Y%C6RRX=I){Ff&DPjv_ z69_wWRYL_R!Jm35w<^V z+wXmGq;lh(E%hslk>FV_MA6fW)P;m9O}($OU^|qFWmHX9s3plNSN3-ouhoJyRFB&# zs^XlAKVg-Pl{!3j2@4TAEo-+!!lkiX4ttmgskqJEMU$%l4I``pX`y0xiSv_}t=`jT zZf52<-SbZ{QHIE{de2Wa;M-8a{RUYhYLGU!|5>v0JC3XaWYnjtl`3{-b!xdZAb%N3 zY~2J)!q~@*W|Mf?8K}F*+lR!N0*P-A%?#P+ zvzd;)j>CBq-+WDfv$&WG2t3We46o|Lg5xpC!Ebq)bkR37NotK#gw-EBb&fRra!fD1 zIN5$_nYF{?0K1$ye!A~4Gtp%lpXsK=_PHG9>($n_o%XCPl0fn~YRR(0jqk{GOEz|{ zDv)THfwgp6y!TrofU}M4-xqcF(;`yF_5;j5zGzY$))MKto+{v<9B=%;H1h4kL~`^` zxZMPUi=ZIqCs;wli&f7oB&#ajPk)vY#hUf{B%k|^X++G*T21@c?{nEPxRMnxIXpP( zxSxyY2|e5R3Ej##1#WAaMbrH;a%;G!rQ2SPWX~vHqP;z2X_$08zo%^`y2wsTC+>Q| zzF`)Gz8&8v2V>;cCqCpP#0YiecW2e)sqBr1PGv{)5J7GCC%#-P%!AiHj%=@H&KILw zyH3;SKMMl19k0EezN8;MQ{3Ht7}4Jc^=t3j$|I zhdaS_tq3<6eG!TAo_yNuUDb?$Bw{5Ual&gv!tr}%~18ROv) zQU|LR#`C{;X@$=Zdf0<=JCks@4TjQUXtzO2e%TTpXdYFH+PmM;%5?XC+b$HA6T0Pa zZa;MeiGgQ?d2khE3^HD_812*3b1vcr)z4b5A9lL!%>6bAkZTKy$xvAICaxme*N9f9 z4Zsc<-}5rfT9M#cq7@=W14tgPwGaXuPfy2=5UubT!cpY|5;vLTg6=!(EB%Qi_t|L} zUSjVhV0g|Q-Fc{?CMg+_!%O7>EW)u(xSb2R=#RYSUda1AsKN_4-Uj6$0&fo?57O{F zwHx8fnsmdR=s+`j!uZ+C34L9JlgF)q(=MB|$v?#&$+me&N$*&@w-N7oXP7wW=gm4E z!+GOBE&cXKu)54zbO;BQPie282Eu}?cHFy;GFNc;tZjZl^VgA^U;H*=dCe94S~RWo z?-|ENhBlw8ytYJhU=_HlzhN|%t{nN&d$s5-NR8Z1CGgCBGa&PYATdPAyYexy2Hzk( z7`osSKBiyl=Ui`ay<(6a@~-vE@^zBBysUVVGV!Pd%UL$Co3_PO+B`d)k#gULJLYEz zPrJ`Mb|Dtbh~Z=(S7bh1iRhY|nlEoFEomdur|bZ}hK4^|G6<)lQ3pO>`{=3i{dV`Z zH|1fNf`ZP{*0WBM$#{Z;>yFUDk&rZxCd*vxqt!2yOA#xdY@|5K-`Uhm`8h2h%VB)|`2x)Z3FH~Zv^S2n z4yyfVRzG+s@;tBuBfL=D_{N-ik|nUhEIU(^;eGGL1gQE*Bpr_sQ8L2<5?ww_<)ahp zzTP+M>*(_m%^ls%s6a^_^!u=_`W5b>tjYm54QL57#p8c^l+t`icHCthFCG1|{2ad!O7aCW zmEnfqQy!*ZymMU>O2l3cg$1x`HRT-Zg=*yi)IkK4-J&ZWIF?jn_9B@dA`<2xc~LW^ z)^J((evgKz5f>7G`}$T!g9v7M*@v2VuRRT@vbQ42&k!N8s~MVYbe+(63FtmzYXtkDV`SbZL_RE-$nUaWv9 z;U72H_~z;}iINUmKlz-1ImVwHaB8jM!G;Kn`+bu%hni8KM};cB6~9UN;nEP!NLH*J zl3j{D^F4T%Mpjpek0qVZ3>t`zZ7q)gD`U1LxuG7Ol58Gd#;p!EPA**Q*xBjjFGLq> z)Cr;O?WMf@01THYsVIT__wMU?9QgQGgZ?h@`)@yz%vCZ*<#n49+beHp zNF*b(s!_EB_P#Ilp+i>fgDa$4NRdM#CIq}2J9Ae}D-l;nSH2k6P4%)?9TM7&tiGAB)MbQ)_&# zOeq1!VbH0y{TKwZKd@kDP`>=#_@so1MY!Ajemv3H{JEHCCPS5a%T1Ava<{m0H%(NW zrsE<^u5@aM+aX>YpM{NC`hh4e(7!;@HSp@@=OXe^Bo`>F;N;QFQPj}OyvUtSh&_j| z$2`|A2uRjDB0}{pUvjAXQ2rm^^$Y)2&4D43*cKgk~9RmHh)N3oQ1IM14`d+|j4akj`n0`p{Xuy&IUplg5F zOd!*`#o^a4Z|+)oGShJ8s5y!@;Vbv)TyVbMpt$!k4>GGZWi|BVAmr!r5`I$G=ZHU2 z-bvLGcjPnk7}jJ&%*o4mRGoy+KR z+qeyrgjmo}%bF8HZm(-ufIheyoCS*_u!t`&4ua8}CM383P=q18w|Xz%a#0tkSqV?S zNNyDcg$13$Cx?nvDBgv`XzgLf<#dX~XZ<-Vga5?rSBAB;XB0oIbb(SHgNjA!Q%aE0 zv!&972J;Kkk#n&CaPY(P;DI#+HGESRr29?iwk5BUn#Q#|670M6mOutcxlpJH$%VP~!TDx6r%OGF5Q2TYfyqvo8fv!=S}BYx^IAalvyZqgvgXGS0-8f>eK`<`0MB4;KLchr=X zmGF%sM9tR1@-+%N5BnInyY40q`XK0n&@m^`PJf9>8NCDd<{^QoK@1JBk>*VBC!NN| zDjzL$#$#l1r#?%p#SfKK%SpyedL`J8)%H~RM^m|}ZQ6G>9r_v}3+MiJC^MJxhjegFuIE>Is|)Ag5DD9k%)?AR4=N9{1N(&)uCed12eoUzoj`H)2j)7f z8Sdj9Bqs#y8E(7*^tE&1-EK<-rNMD0HlDos&Jh5dow-EBxdY-M zX9oBaY{h>V)ApWXu!H?+=cw;x$>Ids5M%!$CYmcu>Jx|Lh_@msi6gT)KN;ztPI~0vXS&WMvXf7_O#Mp-NygB2oOnG5=XReO(#S*d_Pz>L1RE|aC3g>S=o(Wr-%@L< z=FY;N5u%`%Wp=9J7rqAxz^C#djHs9AcK&ewl7epC?vi^n;jv!p6i@o;K?0MUiO@{* z1dG33e!W(69@*p5@59DO-thB=+3;%eGW4E$;Zj%5<*VKZlzJ%`v@}*cK|^0-bovNk zX&ovpf2M+x!6Pm^HgnQd#D;DQ3eTdW3_jf^6tX=JssbQ!ssN%%v=HE%h{3%t z#dq+)8R*;$b=TA1nlXXDOnTqTH$57C0&i^?(H8mx6S?||>rE2n%v`*$%!&8oWrXQV#~l_iv8dvEl1TO|o>Ok6=+>VS(B#UjgSc0alARSw!NDUTc28tNZU zILyNt&^CKPAd)UYhl#C_4^l6mo}h_VKQiL=yF+;=}4g2wYGDU{?Ojy znr&LPkfz43X5r?lthdH<{mKtpPnL-qG=5^+Ny4)^h6$t$?o>xB6u7|FeLh$4AiDnv zE^r&3U8pPdygR~&4n-xY!-m4Imey*7|C}Pq<-;~mvz9vI`joi);#VSKjm&-fho_Mx z=i4(J*RFy1RPmqM{35y9j4aUM$F-9bRGYfJqtHn(9}L$G0}{tV+a2h_cHO@qm@~YU z2q$7YI-G;n)$^Zgb7HCp?zD2X7hTGs5fZ3&+Zqa?Mhkz?h9SaQN;*_aiMx5S(N!0! zwZ)jz=Vi}}40Lr}D2E*7y_&#`>Mh6pt{$3_bsot5!9lh`C{mg~Z1K-KcBw|z;t8lf zy*;v_@-JEYhPm4B9g`&K;^Ag_0YVIMhjAkEa`cWk{FGtz94Uf~g(xlMPNlh{wJ<&J zLeXu4=B$xfXiyh>Ew$xBe=E9t$&60qbHh>AN3KNPku_ETerw@k9tsP_RCw@OK2{k; zl1|$sao$b0=j<;zy`E$45nr0J2sImLNE14fu~{DRvkHrl>h4-Xu1GpPWfLdwLEkBR zhs)y>Hc)yLC-9i@B7Y>n{4s!iptgRUqQBI8vJS;8s&^Tjbbekv8_}CC@%#I@S3Jo+ zOr@MZ>Q(2150~rGjiqApr3Zi%Rwlj64jdY!>5ol-Vx)s|O zW^LaK&VMODKM-ma_`%q%XBkE@p^V2!o)?Q2km%n{uD_a#>%xS%X!BBPWCt91gbBPb zG>_KE6BNvP8y7@u6M7+kb{Fv2D?o+5eM^Dda(X>fc3F2W<{W?!d6#j;!=5LZjK9LB zs7mZikmaz<2o>)V@}<4uWgVM~h8wW`-u5QKLgby)ZS)N#{vPe#gXrC)O%?-oZOK@B z8~t%>AASua^Jj-p^bRmfJH-%tkk$^UoE;@}t@B)IK^rZdah5nvcnZF*znBed0HEUw z7#kV#z{K_l48v@ae7YDPI}8zh%YKu`MSL9fCnwif5*-%959eGVRhmQg`rb08E?9$% zU&i3R?|VqiC^jRH&2bUJL)&=TxuXvHe)SLL2lCloi?MPg5>EV|Lih24;Kmmh8UFhN zq$+O!8H%=<1CSYXk@Sn?A)xhKr0myG=C%DC>JgIIImpQLofB>6bg*me4Yp5ebwgax zn7JpB=bHqWS(1#|pZmUPT6)jSrocQH#R=u3G`0_O8!OifN8sF(uL*se(H8c@GP}ao z6h1BR6dWOX<M4(?1)mQLA@gypa79HAe6@mtq zF)amHgjt0%f;`i_j7STD24UEx0&wPf zb1A0L(SB=f6!!|bGI&gPShJ#W$Nv67s6%fkN|>CyBmP58Z``Y5pwo{Nv%z4>(DdE% zmh-ZD=~1ZiofuE`8WC^sor+{YJt5PgbR(IhZtjIm)WfMZ6gw(a(@pIQF}amfSw~C4 zs#XOyT>F%H6+JXQ;=^V%mxF4sf$Y9>B(T$Aav@pYOeJ+d&AQIZVuc^u8Cq(nHZI5& zfpbK%*keXJ@^g!HmeAP`BYyn4bVFd+9I30UODCd$-{Xv6rS3i~O|%w|kYxp+{AcN4YzoUYGYLnEffsw4I{v zkrzhqjU&wm$S)0#I7CAUluQ+ZReo0cF(9jy0m}X$rKhw1;O1B8; zSNQa9v^OY*8kTOvz}6Xln_%~w!YDs92-iw8(o4eH_)x~I_&mAV&YyqpT}jEy}E8Q#J)6!**)?DesYNx@3}>3US*T|C?bTq!&alfMf-moI617V)acqZY()f6V z@>Qz`TFi3&B6XNI_#X%t7I<3 zfhzCFC+Jxjne)VSt8!uev^ZpO=BNtcFP+0Jl^EbsujyKyX&p@IuwQN@Fa|t09R z&ms;anBe~Bew}sSjaocO1>g=)igm7h+ADU+CE|t@VR#j4E8w?wvs{MkOV8tIXsxnm z*%iN?Ro+uUVK}u_!D*O7xkXOneIV#Ob|;UB>^D4pJh`B*ce@wt`M2s^9(Wi!Z*s6j zM(`PZa=ae!sz-W5ZzL~CFK}jE=cxXwFWO80+_UC9v8SJ!ncqR_M{~>}DC&Yy(c3Ki z#4_Hyd@VRb0ryj*vgW}JUO&~0Y1jeXqFszL9?6^s+9)eOkl@|+!B>-^&F4)A!L%6n zDPh-$fc|8##RhqXYETA|X-uDXyTh8qq78Yx@zaWdX{zD#}m8MxHAOM?F56{a)pqMhbokQ=PDN zbgw6%bg*v=vzL>UOgQU(6W7WYr60yIwIh2*(Mpp(2qSON6$w~OdWvS)r|}1L_k1F% zttLJy${P9uO6s7(99SZpq)P0buwwHuljv+#>_R}L3L2q~N;!!dl)m6DgYj+jL-=6~ zJsVYLe^jaL1f)PArfA6h`gWT0vB%$|x??G<5-N|R)f{pNYK23s@$Ri1b>p2JyAj8R z9NDuwJ?3wb#p>Ejdp||MuK*)@nOQ5!nRW3Hgy8Z=tt2Y|{oKG6UGv+?D>9uwXVv*g zOdZ8nTGeA)ycCw91ppeI?QE8Ua-SNC;*SYBm-dKLzntTRzUr^RD0^=F4b|1ihUsaj_mH znFv{(h?vN187ENm+uBrDpdC>(kc2OHodkPVT3~+VLe^almDk4LPj`SeyWg(1+)=T! z=%kR8w?%S_jW8-Ip1W4nn2Ih>PdaIcfSt$K-8cbQ`mMd!_8rIJ14=XW$CYuj%o|dl zW~h)?rB^Y3?2l?-EKB?o-Z}=o*LTj8G(C^zpzWSw<(NA?!t_RG_&3~|Y`e3S>W5&wU-;}jf#oP2ROJ5rbPlwi&Wjot_bGQ z74z@E7n$(x=%C!CC)G9h&H%L@6F5A|n>sHyXD)7sxG#k|I8J5yN3<^q%&)&EBFj8+ zWHmIu{N?T}1ha@Q8GaS zT~kQ|O(IP|pk(EgH;T1Z+Omyf`v*r=xSp|2IJCOBVQK+J!b~wQR?hec)75N?vIsP- z^G-vG1(776q`pTbOySo{VbDy*P8&r;xInDeiA39uOz4irkWowKIg7n63%7A!^jCda zlH{98$F;tSN>vTW!I}sLN)Dc;)OEFkUm$Z#0;jp;cd&{_b11ZjOiNMFm@(LjttH-$=~ z*i+ku9XV&a5-}%wk{R1+8;yWo2ko_05tbnI#>eG0xNM1GCiWMq_|;Qzm+7EsKb!Oe1sJepwfZtj=S^iP zYjL%iTa0k?AB*9-K^gtt_kB>~%4(P8hhfXu)~`HX15(|tUe2Nm-M)7HW zVVHcNFxA%(N!W+H@sq3Sn_j6YCv%7lp?pxp?LT>ECnIZfs(itB;)HZk+hL6HjrO?W zKE)&8z4T;ps|se{w?=+(TX%KxplfBD{o)@OC2qze&eS;;=F=Sh*`^&u(=Qx+OGPq! zC$Anq@fT@O3&5u#^zgrR0sATm9$2;{CH@h%ju+gJb9rq6-pA6}T;Qn)?k!0da;U%p zxau+HUEQeLs~@ZyB-<;MJ1brlM_Oc3OtmQBVk!w9Iai&}m)GvEd_)Jnpf@@~d)WE2 zE}u#U=cgNTnlZuH_F#y27GxH9ovln%F}0xT>Npf=v>s!WF$e{H#mplOBlSwuAsot6 zmaT&!K_K1MLg&s<+;~{A_y@ZrEYkb9a=6TsQP%VIBFVv(hGiVk@+--6bPGlDIpoT8 z1HDq^2$vAh-4t4kRFGg^o9iN52DxP5MY&v)v4x%!Cqp2?iBNr(2!yxy!joW1Q&eCv z?V3YCkQ|F(Q~cLvF`P1m$Xz?LlYIcf*}j|oG-C#Ix}XPPFFot7puI+T;9+Rub#Gqh z0ANQX!XydPTT$`H>DtW=mYYFe{^;&rbA?0*ZtXV@CS<=IAd@nE!BqQ$%hG3Je*Rj0 zOrm-z%%Kk)ER!bRwoD*~N}u&eQzk90GvzZdhu~slGp(i;#8Qs9XNaBW$uq5>nsd|g zu_2eW<9}q3>0p_uN0<-mLtn(`G5Q2U;p_O9A8(IJdLXo({bGIJjb~X;$?ExC%_I>7 z$bS^M_fjXh-lem>&tRFA_6}tBp$FA;m3Vq?2SxGg8AY<-;@Vk*Q7G(m8!9NK_9iw> zs}{w>({-M+*SZ9WZpC27+}~xqVj>|*eKeYuA7P|L$+o&oa&B#hUP?vP2Wyj)tLRQ5 z*j?6QzMviDy^?@k8jRTb{B@Y>$7A6K6pu*8$In;!BV?7g;u@Ay^pcGFzY6+}Pg-7X zYEeyK?|l?~JjQQFG(3ADs{E;dW0J!WeqZ*EC3BwvD=j}qWA0^*PNzGoV(A%g*~D7+ zJuI)kR}BqNHQ_`7OVaoW%U?-l0Ji`S7w2O)VvO%AxCB$&xTh5g*d}&&Vz)U(y(;OK z3nSNKI%@z^K&M?fqoI~6Q;B%_EVYQ@>LLdjZ)Su4kg>eP92AXr4qYzHinoOaf;vd3FXf~mQVp%=gwPt z!!r-WS-UY?G_Lgo#Mf;m82zDGYk$P)WPrBKb&gFRtGf=MFOOqN`V%cgZ^}N@VDg_H zpT-^G%DoR@PmOI_dW-4};+lzBd`)`l9LN=zqxOwSIeVoDFKU86(JE3NmMIbD;p52! z%z~soIyTcEMKmgQ2vnbyJU|nr$I#@2e4uQs-?6?7`s|-jgocp4sZVwQ zY}gpqIIQowV!Ea0bb2?4W&VMtR2^e2t%vmytKE(9pl<3=?QIF%DmeH^>>VE8)sV0K zl44QQAf7A6X}bpadoc<>r4?;PI<&92zZQQg|*LHpWt+6b(H1)S~4BfI0cJLHpP0u@$wSL=DvFzu z(P|}|h~%^uDwE4FL}C&Y*p7sBH5qTVgJY$O#cOlkra*JAj)A=Smfj?cSH}0K)usf` zh|y0mbn|GzScJ&f78z^FV=f4#6R000Hy@@FlT7^uhbn!bUPm^&iLx}DoH}EL-a9xF zo!T9i2svqQmixhh(mT`rEv+tf>h~GG$N0mtK2_3Xw0>Y3o&<+arfbDrP~7`9p)pbRx6Q;wk`0sx(8Ro z;3Yy3bi2Uuz5p~{d2{l#H##5usc)~u_5Ob6n}Z>0)k9Ycf3w%Ag%Ise(mY+VihWb7 zbV@77t7U)ww+E75^8qc~6|Bw$Dn%-z#G1gXK10E`%}mQr8ss?|sRRh{$PrmSPd`N6 zHk-d_GEokhem>PLg^()+X%u^am&$!+K2Ysx?rbAzRN{6CV_M+mFDEBsiGWCyKR`{g zJv%`$)(tgZ#FI%3eulU>VC@rKmo}3MdA%_`u{xFX(`7yTrE>Cvek^yb{bGl(CuIF} zkun|2_=Y|t|EkK4@uLyRQvzSW>lvenN$~v!nu%oI1GIA*hXOj7xr-r{XE}{CdlX`w zS13i_=i4?jVZvvOUT^NEZCL9IrZI6pQE~l~lrE#k4ENrYbT0)Hfkc;!DOY;K?jlUk zNifpTgYP>mLBjY@vnIT$^c&d>4j6pfMBgpDUyoH5r*Fq7O8bF+Qhi5$AV7Q+?Ht6GVwy}1ze(v0R zzQ?zICvHw{3&PsX*O8f+Z`+$-|HfYWbl1sG5`{OGviph<#}Z0m8`h`xNj=YiNyaip zz*Mzx7IsU#*L1x|GYg+*Xe2*HZBk&mAu%LTWJax-Go(-MVmyz;Rki9zk{Z92re*c% zFjxpXinvH)TUQW5Law3Q_tP<=P$^^Sced$@gp*qqMGWjlsbdi`!q&R9g|yMJZvF19cG0;)C_Rfw6*7DX>T(-5~lz4J)e9>cVH5h9-^o{_h3m5~)n&N{XX?af#V z=$s`Ma_}_KD>B3wuzQ-eX+IN177xcKHXVG#LU^M*=xdyaaSi~(ZuE$NO1NjVd&{)! zOq)Ox4)-UrZ@CL=Y2+%4DF9#2fhRCKgMU05P9B5@7q^`1qaFn~wx67!)S5v5d3$sk zXUB(mm&GMp^y4{>Zy58Q6x@uOdSI25?3@Vew*r{71H-~Ejq%d)!TwH z`0FK5`GWUelv>CWch!5lBEWL)kKZUmN|@0kEL?w_D@|9948v4X-KjwAH`Wq~q$?>HZ#6m5llaD-vl^*ysktQQz*dtVT@ zb2jxzigd*J%S>>)3I-d%A!l$#u&V@TD`C}iSqS&NQ76M>N6Ind+#h`{{IVut@aJrj@2!Xo#kDbqzbAJHA!-3ohdl z!L&VYjlEK@Ij(~JOF{dps0#q2Z0G{N%G}kOWBSP=jfASMacN5?TF!^=05A3J;MRPR zh{1Z7qI4d|RtfCiRD~LGTuz*n7js{_HI?6ezij+v1w!kp&k#g6twrVz4!aXuqs?Hpc4wS8dTs@BL10R=o6b+?TNlfquCa zlYb7TV|3(;3z8E0h|0$kb`ex5n7i1`s`h67rDpfTq zKyHrI$((lRF;hqIoB>dye<9ODB2cz$-Tw15rSsF5wsJEzfOeDhWrWq25h?-~^~7Xa zy74{gWGckRz8oIj=vyzLiG}q{ zMU+27Ff=!WmmO7q9AGIPOh!k+WSu4>pV3IS$Gnvzr0Xyj8B(nh5OG{=6-Va=5Gtd* z@#Pv4X{ z%OY$<)#e*to87B%CY1w|Gme~6ux>WH-RvzI{-2eMC$5+6>^ z3b=PPf4oi9zO311=fX2{l-K|D!Bg$RIJ5joFG$=7374FRP09Lm619%Z7@5D6Z&Ft0 zW6}qjP&0jt5BV1+zOTrM=|c3g?6Ck}hKvWwyNc~ZoIl}rs6qFp$bPvzJ$@&96}-{N z-#po9`4~EIn&kgvkiMbUaPiUNb~ni)VU6E=hH@gfW3tZXihwvMcO!k4;qeM~*Mk1n zL6cT+PioWW&q*z0!ebp}EqvVy^5*YbI>csBv%P#&oThY7lBR}4TV`WLQ%CsxADR_) zT-Oi6?YWM-r-O+TBCOctxxP%2@5Ft`LLczTXPstr_!4rOj-VIy_9!#=-3oTD0lai+iSjY?DDN9~ev{6zqDB`Ifzw|m{`mpLnv2(Y+K0|= zO2oyodV@2gW*mbhP$13*P4BW;iPwfi!E7V*C)>zlNGkW;T1CI#01;AQ8lC4vY$ThT z2sCB*p2Oqn(bzB&lc1dM?%v-+F2ib&Tdn*`@MrDRRKJfOjelI4I_wZ_UASOq*9Fre zm|rV-(B|DYqvXpZTM?^YPu)vBYlbO|#YJQH=Nlzd34M&~1nQXg$3tFU#W}zK$noRg9N|iu;`CF`7mu^NF8*=i1UlD~Bof#?`~9D~ zD1E>AhfwMAIjHy@0NNB8Zf}{A>Z!aUSvV7Tq0``nDuK@Ukk4O!G{)zJamalh8JCCv zjSUg69SR{hsXxtsNL87He*ZRPJ&Il?IS@n99Xd*!Kwk+rbx-fYya)f?J(3Y0PuN_D zfG|(KlEAYN$lGvAesL4OUU2kg{w2C(jC9)DkEZ#=Pt+_xQt)FP+Jp@*eM0zTVla=& z@2~h>ahoWZ$<$^h5!)9>O|!wJi_Q-nz-*4OA0D@hYzc2?-hIxf99oZwnX5Rp2;2=$ zN>DJbA!e;P%n6!;XT!@FpA_>-9vFJ*A`vl6X$PI=uQZvKknJ|l5ZVHL_+Y{D((~Oy+AvDEM zjQB(!7Ksy%1IbiwZ#M0NKAOF#`U40p2e~?W={OU*Ug}>jDb8V<%$l|@3~kz zPvy@P1c-l+y5vY{@EEzQBAHJxVA8odzY~04md{m$^>yX5MS+nwUaWyf<^U-yuQrv< za_ww_u=0IT>WT-?Z_J;f_yO-xmae)!@#hqvCTBrFL#+jx$}}1cfMj0jLT3+Ui_!yj z=@&SYDV%p8Ka_I-2tO(q)HT z-_(!zC&rz7U0CMW%htZsDisP9Vmli5b;0d~_Cz)PhA3{HEm z+|Jv>S)VQ_cve!r9lOMh?Pb2raOz@Lj74K_1`dDbS3)mwylTbRI6CtZ)P)de>!u^M zNh~*+pu@6N*3*cF)5`17mo{#@+ya>vCQMoFdIC`Z?&B`J#fr7`6rR{HaKqpb=qV9tq$uz@o!hg|daOAab zw8)0wrzk)J7&Ajt6l-W#YnK_Lgpi448?w{3@PPF1l9ebGD~g1#YDGT{{z=GaVHoU# zid!i95i1@6D_FNT+8qpj7`dJq&iK3|z;u`WWWwztm+Z_DRFQykzCxW7dCgPkH3oTH zS2qyrg@*8k4MB%G0ou33AKGow#tS)h$CaHz1sSl!+|dFyq^;U3pw?1-nN;PPR# zlDO%PFcJ@<3^nBOz3lQjBH$LbFKMRJ6i1Upp@s(elP%7c9O)j*S2$kM4&6oU6tjp0$pw!*QSf+tVEFf;t58klAX@p%H=#v(@vDhuE37`w~{9 zu*@JO8M~h^#O{ndY@0nEPb0?_Ln>uj)v0_3`y~e}u|n42=Ls+Q+Vo(^TxwC(rIVp$ zDx>&^3S9&Y&G>k`(WbYd46>^p+)AQx;VPVKq#t+u1mpY1Mhg9R^+69<)254Gr(mCK#xV~0g z)$6=l`Usv5Pr(PA6klt1y8w7dp_KVrpt z8~KFN(tv1>=S^61!pF)XV()d!BI}iGVv~EQU5hhVy~lciHa0XBeS0GQZ};D4Seq(F z(}Ub%N{0QKS9^|b!TiM6mhHp{ap9>^xvVCFv`xGHmwR?j*h%ctU&&&V<~fuq$K-9w zqp!zE3L@OyHox(3VVq}^&c3nu{=&Q*x5Df5L5_L^kex-W*iDq*@;9nK>VSy#+*{6- ziNuXc25V!K=^AiOhVm|O?iEnS4q&}OAa_{ntO*~{`sLV85`o)2z+%SVuQwd#DVJlLD1I0>J(Q(e zswANc0COBvzoNtfN-dtSm`Q%TF@%gW>R7E4agpT_&W5uOwmE7ZXBoy#ymwWl5&2G2 zK@{*llRqy6jh}9un9eHpSRr7*VL7j+b1Dp$?T*`YY$8FcUqOYftJ#kh+ES4b6v>2E zcE>?8-pujz?!yN&AQd%VVGuBxN;fU`wCnv`B@P9RQgiJMc4N7I&W6`IA%tmU;N}q1 zZS}Cc`rR=ZQcmT(Uuc8L;pj*>K)#Uan&Jy(N8jVNQHpE5-v`k5WY>>2y_0m=#F zoa{sFOd{D{F_#nx4euhn7bdPh^+%Ge?7!SSJ)X?|DnKk4to9K3XXuUAHp#^FD3BxD zJI=jAIaM(GXFUf0+L;V=u`II5?qnt-87;h6$p$eBue;s&P_^%Unp^_O2#N!jCwr0e zSd_=_D~W*Q|)_wa^fB*t>deG@1 z;A8mDg*kS{jlq&azZ}2$*)Xql`4K`a`DPB|P~CJtSV(@6aEU!J=*W3JTPX&dACW$5 z6n}}7%-FOCDR(cw%Opt&AALIvttkv`l}l3m9rCM}`nv?_36Eq5lyj&He5nh}vn71r zZWs#?^+ox%PvIq!+r<_7rz^-^0o(<3R@+`sxS|)jWUXWH5RnA1`0*{BNOxWjT9kl* z$VUBP)DfYZHNW>St$5gH$A06l;*$NmC)jz_Z}pl#HBAjkqvYrBvd!Y^dCyBB@;4cT zm}vJ#E6LYFYP%GIQimta{-!W^bPp4g<1?QI%H(UNrO@a?3UX$e! zQ5FlsG16bTy|;1sdDCT-7WN!Z6LZ&U)fjB5JWW341?Y2ubPV+0QQoR`U~^i=UJbH^ zn@{-{(PT{{S9*40$gSe8TWZiaf9zE@FqZ-bqzWeWkvoi@QBLO9JJLJUacZ#dwHeZU zG^#(7V4?GApigzfzOh8Cx#-8yaYp5JYC4sa_;lKy;W25 zyE=T_QAGKa+l(u{5`m;Q7=ejT%F9E^gCxHU5GZ_>+%!E1G#XNrF{lVO=6Z&2@7JEr zGwY{gPFwsv*&~6B7cx@AAxwmkPQKae*&`euOL?ue zL-4%J@m57K=7|`_S&6^b{33_rndZWYiunWy&cBbxaoPQ@n65-p@A%uxTtuCfK+T16 z>}+(RDZfubc+*5~TfyDU#3Gn zVk~!ym^@#Ye}zRHcdclG*M1nG2&#Pg&}5#^e)%jOgMZko($D2Lintz=ybLbg-)m`a z<d6P95I;<0S@dB9CJk@7155Wx~Qo<5jYR{B|k?OO5d(1?|VwTGs-#GS?L-US5FAqp&iW!5k;T+ZCX>x z3mL%(AMIaqro#EoJ?Jo<4b-4jfF<7Cs$4{jJnLh?oa&9ru!X1rp?UDeMr_j^(OBJHA<{wghl8%eFf%nIi^f zkzYEvQgjx|xyhnXmo4Fu@~BsK10A?UWGu-Li42=(h}qYjU@%rVs-#~)E@|6>$tPd@ zekjS1E3Qr{!k(KzXz26P2c8qikbd3PuR?=c1`3@_EK*f$Fiz+XcLgRFlKr28JA{4+ z)jkJq^j~F-2c_tERGe4vTw&<&8L+n{3@v^|f)FbS1Ng;jwO!j!1wq2t)f!S1*+TNq zIJE2mwy*@B1x26Ue*l+Q&q%*7R^YEh_S{q^8Xd(g*9VK*YWT1gJ={OZC+oGY5yVd)xw5j#&w4Ok8ap7#0L>v>HodV z5Um7jCM~S$A28Qu0F1W(hWOtsKFb)@e}ekoTKs=a-2^;!wqLyXdC)w1B~gVq4=5s$ z##hihiM7LDs%7yIQeFpL#PWN&r}K}g8{pE{#Lb5Rq73lL=1UvB9r9^o-}Hi*<-StM zrwMXeC9w1O}>jk=)xyy#u*RM%Q_21J=l@?Gyrxq%uX-aSxKxqTFuSjBU7llB z#nV7p-xx`7aOj5L`8-8mf2rd1U;KP^5nfTB-7SM3Rky#6%3Eq#zO{pou7%T=pz=9z zdR*Pc2Ex{;gfV2NGDRRX-a4iylvZ9*H$$sV_IL85Md5L?x`A()2bmiNb0>H^T!TkQ z+)^`C8~UZKZSP)*N{)JE_>ryMM`u}|*w&c=C>wVmk05s*`})F0WSb*Px2WQt*5%xv z+7|swvcb=7)`onADy%*EFb}fTPIjyDme@9C@EltU`7Up1M*=6_f^YhT6{*P=L*w4afN;ST)VDdHy-+f}UX& z#9ZYVh{PcnjD?i+?7qtdekDT|z^q-UT}ct-d?OdKc)esQ^w@{laNNf=s5z#N`P`z8 zxOgsY8o6!L(y1aDF;0HNRk+k!{M-HHdvRWaM&T70X(bJH3xYzEV#JB|s+(A_rTr-% zHyR^}MFkS$E-`qj?X|c4KONErD9)c>qTw3Ta=Z}(a+APg_p`v8RJC#QF&+iRZ{Nj< z{GF=JmvD%TvVyVZ&l1`14*ycU5khqp0#gjpG>F*wWP5U{aIkKumQbnW7C_&opD7ZL z#1@Zs&Zn_Le&(+&{Z}e80G7J{Gam&czp^n5#zc}?jf@+$-y*d`WDhM*xLE)Pms!(! z7RF$|(E=kr15fQC$p6Ly0PU3Mt|$%tihSxRSeQnD52iY!?q`DMob_UTAF{7kVGqdT zp|q?E%=KYrWUKyB)2pp*_!_S?QHp#Pv}l6V>!q!!&JJ8UII_8DZRBoWf8TX-NW5=aR_T}eqRBuoplSDoxUOD#deKBb1HRQl^M2g&HTE_GtCKeeT7Jl zeq|CoarOgyD1x$twrb^hP3#Ml^i|DFGOc#Y_`V3ZH>2=7*#{Zk^wpIwhfwm6g?Vs) z@f@&BhV2i*UMOnqtnf%kw~aO{aAiyG48!;tP9wCWTouD1r>&z&5xixoAJ?TkGZIgF zJSM0S#7@?0c&kipu}dT2gfUT#IgP2hID?JPvx9}#+&V=m+0Y~cx?~Ie;azt12@Q3IUnR|bzc~8o6VjWi>?zFFB%Qjo zYdfl{$=5bto0p{pYV0tAS$a-$j`n!~n znJ|*@WW|IutD-KbD$9BUEHJf5gLC16OMoQzuH7bN<~BO(AXN5jOFsvo>oP`pU?02i zBgB1am`i5qHQ`r^ii?9p(8eumWt}viVqJHv58!-Tsd3~R{bK6UHxCUXEz2S!o`F%n zsd^(V!AZ7rKlgI`>X6RP(LYLTO>++;K7{?1*}* zHf%>*OurEg3@w~X`eK)Sqxj-bHfKBEl@M#%uSunf#XT7Ui}kTx+{t)X{BRNfMD)B~oOV-*!{Z@PmYzQkd}h;g9v!7##z()n5{ZvLu}R|5pa5)85+cna>=+f5T%1o17AoOGa zc)2-!%zArr+yb_B5_R(%QDm;9V#pN>&c{%JNATGUu`nx{>8Hy!X$zxZ@#sa_UPzf! z;vhEndNcN9pSJQe($IK4u=t{w@+dT5>U3_D2WrE7nzf>M#OuqOhZrI2Y?VyqrERyn@p5`<@Fob z{TVrD;D*E(W*8kE#JWQzCw&_i{9zil)q;~JRI9p2)=`dDuWgQ^UK`wNa#PM$8*v%sF$#yH{dr+Y5Putk3F|KLMIiS_ zb%P`ZxRKl4wy4H7M>$WJKKF&;aqvS?kA7vd9g>O^{Ou?sw387JuKM0_e zERiib0cK|qZ@#ba{M0maJEyZNlo$^|C5)<*c1X4!I$oIJQP~2;b(f5UAmmBuUon8E zYja(^XM)QXJsUd7Y1OL^prX@rCpIdK%&js;Io&{%#RlNqZI4!j{SH2SPb!SG^0tsc zX~e~q)2!V$YRAUiO=BMS_NPZa^$Vr%&FUR8xIlcI8EZ?|yIO57vN*2JvC6(zv}uB! zi-k9D<}{@$ZR+LA-dp85e8`XZaJA+8wxil@`uk_F7VtObERjenMR`~teX!Z z#(aygY0L3X2!5?(NEp)o}3#ZW&krp>+J=@Y{ynuS^RCMzPEm23eNG^AlMta83!2y%w< z*&B70A+*KjX5Pbok%5ql(mzSTUM1TIM~!oQb}OwwkjPt)KoGkKdcxIJCGsF2(}v|v z-obhm?yRS^&S1=_n=!}f2!fe{AYw2QY?vQi5fWfNT5uh=v8HCMg^h2l1qXj!It_3irA&oyb(U|V!rw{qmYOsgR^_B-`x}0dqoyKd)z3VpIZ{wF(%e#_ zc7L-%4tqWq*2Sds|L5zqdgkI2yuLkVefLh$!y;Df+1tvYqgrD5pUQ#Ug`TkyOk^82 zg6bXFbSo8a{`z3pM7Z#^E46F2TYI9Dta8p5g<6@mk8Y-W_Go`y&9A?(@uH6sF~-8E zg_MFb1h+pA9mHRA*z*0(!ms`I?D@&SKPJ%Iw|&DH6L-{G6rqg~w7R^ltftq~-=XR( zco*S00COi{#3IZ7AFo*kv7{D;u#|B=ip@Kp!;&cQ%q#IvAMv6qw2PZy-m@oNi zfiYg!zen|tJ^|YCxAAK&uajHD!=D}R{3!9FkSwd@%G~TlR%d|kT0UQjtw&*`F!-}r zUJNpd-hu7P0xAa82Wz0zY~ zCY!n10L6glso$$D;o|2P<9cH#=S3O1e3cm; zjgyaaj+&kF+!z)6ivaW>Uo2I@fxn+G0_@>m;!-00l}aLlXSFbap$Y$Yaj((9JU858+(7@9I=$@rXeZ z&#@RQMD;`aO%D0=S7>Mz`T4XP6dE!}@=`8JN{{$ktTGump)uER+3nB63l<0f+N&|K zuwY1erIgzLK}O%g7~rzDv%|QMPO+xi`a2|;ei~2jux+q)uiA?@9okN=8cn9M>L~8@ z=YDU2K1A*!>xA&kpr1I=5$^gdX)H?_B{VdF)5mb=BcW=Ack#~890NMTujJCgR)1y~ zjcH*1CuHF~MOQ|%=N^dOz_TitSSeS&jGcYmaP#pCQ7P#gnJ)-`*-QK;J4a~b!6Li4 z9w2AZRW_aAm51(1Fa5XTXL$QIo-g#z%IG05p9=ZG@h`>V?zoPyehLJw2=l91RNdX- z8iJqa5RRTIN%CJPO1|`mfBdmM?O&aYMf2`MGqoJ$&sNarK53 zBzkfr>~Ew@oQe-7Of3^p_0K{kJ`2;wwH;_yf5l@NCN^6#Vw10)Cr(!dW?bH}=EFTR z+4&pHJtlpSV}54C7ZF&*>Yyze`I*pu3|O?q{*tERS;C^PU;$!9`Gfr+Z9=veg-WyY(_eafdlOGGV>|3WW~#M>t4f;%J?v%O zhxy*)WmRu)Z?7$=SGfcLyw8sXIWH^Xd+-n&yn7Mv>*A|$;Qj3!YyiCrF%sLbC|rt# z-|}09pVpA$hBW)ufn#XT!|W``dMDGP!_ev0jMr|Fd+N4zQE#c*B#*`4s)fbZDaFo> z&3c}~of-W)mjE>vodUD*te#CPkUKR3E z@cF))gF;zf<+GJ#H#p6B9Z}`K!sL77{cG$JTRWmWI1;8)T;ENl#yzecx1ET#JzOtU zROGFIpvRB}6tSB}_GSE6${fMHZ{M1{th)Qj)uvoa01PE0E? zK2gs|fqB!(ivMUe0AcNplM?yel@0g1U2Vp#!OEs{=O6c(m|{i3`uBocRRZsPk(~G0 zfBe)Bn=^w{t~6R-SqlLs7SO8IjfkezOxdPWa4p zAfN8;)l5=kN#(kl-u|+J!Z(I%J|Ci*ILlHNX%H`mt*|HirjE7|#kqhsO>v`@vZ`!Z zQ`NvhUJm^h;hn-eWd2tVqDZy76jwQs>+84{Fk8zoS5zIal~MY@r0+|cBY%_^o#L-O z9kMUhpy6BQH?@;pp^_DR@Q|e%yi;;NZR{IWjCqH6B?5MHFxmCa2(PreY%LxAoI56G zLrGU)R1_SHtlw^(Ps|mIn)70L)ruNyd7a_^tt6E43Ht^voAOF>rJ?p>D2ktk~DFB?{Tzj6`u62 z#v;pW*8L6|`~a;eZ5>qRUqb{O=!326g!?6tRV0gAs?r=f7BcnE(-V@C7Cx|5RChi> zJDX-3-ODOT%qVYup?GIHy0yRGxL)$N!4kPtWhnaHyD@!Rw73L*Y4qnx2z-}l2dbT^Y&+K5 zaSRfow%bj+f7(4ynp z>M=$E?V)`9G$ztC54B7Rhe3{4Rike7EnPWyelv7ElNLE5@CfTR^n2*R68o&*WQWo7 z+4RrP8ZJWC`Z*(r-pQ11`+I5l+n-HR7ik)WiZxh%yWb=XzIA)LxcQyAj4d2>K2#{B zvv+|Y9grXt>I@w~;yW+Ur9c+HQ=sn+4P{NRFZ6JB$^J?>3pJ~&By&d}bVnexv+UBg zzju^CH*nK@{@KcUzF)oy$ZIP8Dc)F%X+Y#!&Yv( z8CV*391wvlZy?tU%0yLS{;}vCOt}7qfVCKx3FdpeIN+!p=)#Uq5KmFp34c1pr=WNC` zw?(W(=?q>_?)=+@PH4Kw$$ClX?}n0TieM) zy9nfv%RR7esm3(qVM^z*_xhx&iF3zoyuGoH!uL0C>$vY)xbd#pca2usYi*UY&ih_A zps%reRo?KQW`@!TfN$k#jVxsY^u+(y<6hR|L}l~&pi!`K=5EDf^=a1Am2solP~xd0 z=K^d>m{X;EZq{Uvw5q%t(54h^&QNf|CY+=CH3jzly|PIbnb?>;5`_+9WGOhpPR)`G zNzj{wFjoP&c(N-R7Fz&a^!eMGf#}bHnvg5f*(H^GF0VhtMN3*b zhWMfyDk4|$0JC!geJSu>-Q`TxL)lnXCgJ|#ey;b4h(`ztf|I~1#lwn`MGI7v$ZpQa zr6I#}J#3aDPo>+D24;#jQS23olH^9~(9jgP$$I}7gUl)OmG^mD?&fAwi4j36^qb~| zz_*xf_qEL33ZeFXYRNa%G%Z~68Uto<8OadVR!6{pVmG1HrpE7)cIfsy{dYmM?di0p2}+6 zJMNvCJ85$8yXZXRC9HAqc_6($c{-zPM|7rQ#U-3^YgO?!_Uheo9~Id7-m0KV>3hfl z!R_4qb8i9|1{?c7T(#%oZES=_2vF5(JdeV)or~4iEGigF32ZhY< zz$w}GNaMRc|9TM~z+pX7(IWlR;n-okhB9W$vB#KZF10d=QGD~EE8N5Vj)QBBFR0Y; zD{N6bb@%RYPO+Dx-h2TJlGslV^@2ZZ0*Zz0EK0996aF!QzR2T?qa)G7|JB}m2D176asRCjRn)3l zMO9l$jhZzQRke4`B2-n)(Aq?(s;H{f7OlN%6DwjxN?NP-iWQ-1k5~~x;-7wh&;L*F z2lt)l*U6Q0o$KUt-tX7@oRYmTu-{5;(SS>|+RM!{IFgUzTkE!W?f9<<&?~kjTkaMK z?m#K62nkM>J&MYN9!%zz?+*KfKR9&Z%5C0A-}>d=jfu-fcZ-RMslu`U`Y zyeUJuA?S{5BSHXN7Y6>WrWCmH&cSw7Xq7jhaWTaf=NfSSz6w^;2xj1{``}iqI7$^K zjybZ*CV$B+%jHFDVbrs?Umb{K1q};??cawSEv8aR$RG3h1O{w%$n(7}>TWMC!{+VyqFOtOq;IWSNu7X8Im+Sey4BJX%kqRTwa)U;S4?g3a`= zNgY7En4yPU*<^i4B-R+3-(;BaUMNYR~dEWT^mwrSuoV~vObZK` zPUfR3-4ntdfl#yV>*uqviF0G*M@|fZ`26qR`v5-}H_f~X7B7^x%4`gwEskvdxMiZB;>V0{JwU(_wOm=8hozE3 z2@`8YBjIvt0ceOn7^oJwdv_1CGJCb>L*;5*UG&?|yO;IPm67_!Or9<%KG!5A+4pj^&HNI0Zp&A0EVZWS-@f#88X08l6N zJrrT9-7w(LtmY^b7Y>s;t6DHWtq71G5lsD9n9lq(lJ(82f}(*eKKhV_VAlrSSQkXG zg7PiNW=e^z%EPW3s%yU7Y1u2=^PK#=0-3p%>w1X1<4@i^f_=bbf6ut{**jGIzVH*%W*;Uu0*c z)FC|ZN=C}^qT3?}Eoh|gNJf9I27P4LMN_uaK`+8N@7l_tmb7 zpld92)AgI%BbfnfC41l&(zmyqPo9!y);(Vzpjx^Q4V&z z4Qwf${{^01b^_%@|1k;I+wNB*%p_(mrTV>(5D02N2IBI?Z)$qoQd zjr`x}wWcS;)op~1g~eD~8IR86ki~U$aizFb`F0Z(>DF#%YLn)jSX5)!J}yxXC5#Z< z(Jfzo+$7S$y;J4B-w~jo9=8siyBW5-AUK^FIJ3Iw&Dh4AraheaMchPGyhr!D=)^-U zTZ}~-$%F5qs=u*Sw!rpLxg?7|2BrQtkLkEYEc`VdiEhLDlX(?Mw@~p=Y&7j86w|YJ z(|!M&AV(hP(>x-|IUcI*;e`@3!N z5=ge1pEGCHL!9+`x2^Hd@FkALGR6b-i~kS#_Q^Xi3QM>9TvlzcqmwA`4PU zmpoF=JH8R#6}xGypJgSNA^$@{z3SE@W#!OIH3_op!xk?f%?jIKmh99O@~d6ZQ`gMF zocJ(ceq>WZYz@u3`f>qOByzd~Oe)ufXifw6dS!tT&BNC+>T^V90)D*g@?qL#u{B05 zbS5y)kAGMhX&HO!BH;($FgcwRcQ|#xJVI{|E{bG_4omL3R{dM7x1W6Q8a8qIuI3y) z|Ao3?f^;Sv`&GQ?Be?oFM2(WGMmCgu+oki@CJFaVLj0<069=i=+G+;~3-OIO?-t;@ z&*Hr{(LaqbUs15IJjlp)DMCLXs4j!yX{)x`;7(NK7E8vu_F1^ zLJ~~umwSd<*T(=B+o9Kd3Z9pl?1spvc0t+Kn&L= z6O`M8MYmMne0fElGgGy_sfnEB@nitCOTq~j+5;EG2a>PLXg_m(DI{x!XM6gI+FPgJ zmW~hAklvQ*zub)@OJW9FSTE{P`(z}oM5rx$$1(_B+i3aDMN z!>>MN)i1WW1{rq{=p25QXnvcwIn^c=0@+BMS)tjq)AO#)9sool19pCK9W`a>IW*Vw z3y@Y6na#eBYY;Ho@oRZn*XI*ca|Y*AReAOOn?^BN`9IDlOPpnbkuC)@E#4giH6uKN z%skTjcXOC&OHR`tnxX?rk3How0*1RqCNpRg&RvOz6?iK_n2^RJlG@rS>x(&(W%G^9 z<)Ikml*30aG&}CP-XsnpnzWgmul5tW6r%FsPiv=^qXS?A4g*Ac?bpL0H^#JMjf#Rt zo%VBBHJ|p>rf_ksZDBnW-*bh=VWq;3Vs8|KBsRN=oaiCxnx5h1Y79y-euGw?JL_hz zCLH@D1PpI~`uOJBD=H381zp0V2o3dLAV*?9``2wf-eXrgo*a1I;huAc-svxF6-Nj| z@~tsAo?S%K_KuAv8nYwW2OQvO@2sRFVCgs22ir9#OBTI0?cq zW-VcLG7c9(jyu}1yPtk^4{|l;j_v$ml!Ec!c>C&@K*9y0QFHBL1Px!SrUTbt*vEL1 zpm)PNby#rh0Kcx^SO7vdmL`lWrab%W{7gRE+@PwWr8AT)jx3lCr5bG)KW zy0|r~wp+k?ZHp_4J}$6@0&ZI`sld5Rv6wnM&70Q;Nu3?S#04ywxYRYg7j56ARoIu$ zUQ;8_v$PN903GH#2_t!H_~R>V&xt9>mPy(Pu;z+T*ww_GmPJrWSjgAm#%O42oN+i= z*74lvoGw3+?fLdI&chr{EU)sY#;7~jlRie+W>v;n?EqHMhM@#MOAD`-ynsBB7Qd9_ z-@(7PeLkZOi7vLDX9gVM`=~#c?O#{IU;M!KzvOY&IU4rrJSr;q3Z;X zs!{`Z1UcSHd}t@#cMCiN*3QTAm?Ir1yQv3}f+vH>_dMSI$7|)YaV_sWKC#fdE$H(= zVuo>~li4^O!}kKy%k$%9IP3l_S&=#`jLnB7n|i-*62#1_SKr;(8E!Y0Xd3J1z#n#m z!*n5ivP7`)n#&_5XA`6sj<+5;xyBYyW(lw6Y=x~BsU5kwV*R|2y|}c%kXsl36bS(U zl-Nu6)Jrz=hZ%EGv;B*?j4LS_;mC&^RNxwZ(!~Lk5G#kLP1qly_Z|?kH3D&3m z%hp4-T1A3>T)t+KxyV*Vs2huib-V~QR$cwc%62yr9)@jq(o_IaMn_t$567!Czra+C zNsb`E4+9?-t@X;RkTqQwq|l{fW&C}|)nP7-5$)Kztw8AOjXU_Uxism5qRoUmMj0=R z52ed@?H3s%JV5)1jy1(r#6VyxC9?`JN)u}X95zmp<}7qS?jNxsqg6fa@`@#LQ;r8t z$u2v`_~TXPF+s~ zm)k#mn)9M-QQjb6Ag?3)JklF?tv&L#fef9@gv^JjP;>De{7z+H6PJwA$^D*U+$sd# z9og7(VfCZzz($(!imkDfOKWsg_N%;)vz1k>J4*QQ4Vu2;yYLs16Z{@d$6Gg$_7U;; zA)`3wT-7+&mr79cc=XFGqz2XeRC^2TMh|0NSG<@z90r#8vfRM!e%h#Ok2DpCLAsCq zcC6mYw}*YKQB~&=*5vLHelVc%W_o%`V_HtNSRyiOzrN?8+u`;H@ybgo*4K3t^Ip0e znxR;56}|}%86|FevL~}ZS1^uc>0GH2{-$<(*6xhypN)5usk0r;p(1BuFr2NJNElX5 z@VhYkEpU1P;6$?Z@AKxlw~Qg@#l?_>vX96m_ZOiE*1*pMLJ|ENiD@KEA{0mp4Cgxp z(*APQ6Xj=u`o^rHJ>{;3#7o^u(Y|Z%fvse0le{c8`S*C6Arlq`C$*n&d#3#6y1DX6O^c(v^&C3o!Ku@XtD!!A&-wv1e zKGLMnW15OVDUOXKl=AaH!pB0t9?aX0;O2+_ly zpL#J*=vOP{2O&~8kwJa;S>eiOxduJ{bqijvM8^+Q*92l%CBg8aaqksb?&h>KfR{PW z_SHh3NC-oG)>{N=#xCL2h}T7SreGoE(J`6FywoA3s81}TlqL=1KQ?^A7!(HE;X1VJ zs_56_aNNC074;rlO(f`xI8Od`?n>;c9SZ$4>rECT8=r*qgp*f`+NH52MUt1+-@gd; zccmKTRU*v)6geVg@S+yZ`m8@6YXP6<)l5%l{`Jt#0Jukb+@1t%sV|+6Y@(&dmNWz?P3;+iK5yxKN*M$rG%o8rdg>|-y6$TW?!{7+Xd@PsoOJt5NS?D z4xaOURP{mf&jU$1a(Qg7fWUC@4V~43^uiV*JzOLFXPYQFTJz#V!fn>Hg(3M8>?#?f zeYnT6Y{uL@zXu}tp+DN2Yx&v3gjJsv_ip_xzBeYU-0qJX7CsVSGQI@3j1mMgFST3X= z65>uBg7EZ?3hP;wX1&Z8*25A);Cs0eI8sqoLgkPL{v^3DpFjkaP)_+k(`GM8&XKy< z?@dxo^~dAed4Bxv+TqL1{T-)ch;w6_Rz>wn^7s`f9+JLxxiR&3YHlH0*LKONM1)}}=zu$D5z2g0mx-=?Xj z_*NzNzC5*80NsNYwH{C;O0wr=aiy9KP+KLug&4E7PVmo>NstHhKyr@r_OwF)!zgX# zBGrU|R`15uR-l-7j?YCsK?B=(Kf+K?T>qYOD8u!z7yPaM1uy@H7tqlRB;lOTZa4o~ z;GQ%S*jYo$RQsO^Ec}LM;?XI8yYnyY(RGn#4k+xi`@f@Ez0&`G7d<3jC>fwbu>JV* zzY76$JXMQsT(>H|j<302`PVROml0iw%8P>93;+D>P2G!MZgb?(C%J{r7vZ(e{>!b`7LwC&BLQsBQ9=y3m=>v zqg!JTQ9$EfXoSSWhZw&L0)IgEmMPV_P48_xF6dqeY~Yqh*4vbw`qd#xK?ax1M!Yx->kjmSpPo0aJSUlWTiL$x|ZhN<^MPZNga&iJ2BbnQ0>*N zGwl)o0}+07-A=3)ZBB~)n(~irdGq5LZL~z@{*?c3>?HmRtW2?){p(R0YYC$b7j8Yf z|6vbqv|P$}{`(jH>yH0>FMBxuO!5yejfbF|zfw2))T~oTT)epML*AN`*sJj00<-{F z{cgLdrKPpM0m{FeA&(8fhR&bGW7)uwm3XXb|GQ~#YH#Si%$HmtEhnci)F#jCwrwgYRg{d zgVh>3#<^4pRw%{y=CCrw=dtS>y|U3^z1mr%+%00W(v3vKKUQS3$;xsEOm8*Kq2g6t zA8!fS>`j=MPeu!f&>d)+%?>w~QJ0uf)M+qSRJ5^Ewfb|up< z`t_{gW}KsIacwOoI@m6PeElf+LSc49JPufn3_E4CBn*LW(@+kl(GmsZn!$j$w)8!qUci6??Dlr4*#E)x{ICpqy?ITxxMOekU}57GsgguvTcs-<(#qX=&lAz)V4d zc0StEO^pWJFjygY+7XsjJVU$vRC3AJ%ftlue(MX&!(d!c*P351Lnf028mfsu#BA}~ zZ&e~U|E2E^uiNNuw6-$v_g7Q~N3(z{iA{tnXN5SU^1*sEf~{!H4;}i+$Ac{yXQ>E&S6`W#t0^48qa0DZz)_ zWTh{X(sxh@T#VTBuB&H2vg04jhPs@O6iS~k1C4L5&Kfe=oJJu zFeW0wv%WFHYJ!-@@Zl3Ayqv!j%c0_p=nSC3S)#Y9A2GD9>*Ve=J^RCl^6H~{*bQ2E z$8=(*!{^mSk&r!2S9MLep5O{+%gjv)M^;mf+iclINps)+4!BR}br|Qz5tT4@imECcX~eZIiAj3!nWP zY$%zHT@Rz|wr3sN$sJ7GHk)<-bpiC&YCS0e>3QNZ9qfvd;0>?PzF^PJw897p_x|lJ zSc7lermS{5Tru7-g{295>>z-xk#eO~@2DL5$n*-NLAV1|zcCMsg3zz1^x`s3N z>Tk=V1K}>&v1X^?pJ;*CzAZeJgQWF)v!{em|5S2oSI!En1~%Rz$H_OPS`QAO*xM=( z6efoOEK@yD7XzDyU|Iq9iUax7i*|*9C(jD>Qj3|;Qry(=}vJ! ze!BO=QJYGc7{7O%1hIZ`PK?h%L4uQupnW5PszwoJ{i)+o1GAWwULV*PXLj&++O9#F z9Mh9zEGLvWDiOXSE&V;Qe`3XNpJDMnetIb+i1N{RO4q4>PlR|g9YhO57H7j-hq7?c z=*-OykcQ)s(17My4teVNN}+p+Hk}1KY)xtPN#cP#;lS7U zh;=5&_Q8SPK)pxM+R!GNmYNo~`&e0x?I&Ar56fLq@))Hn15dfFv$U*`0IS5tJEeN0 zvRk>BlyRUdSMv{$+-jfWgw9dQ{>~P8hC8f}M7CEu9zX#XW{Oh}f>PKIH{%2{tUw-9*~=4`3J^N#!=%% zo9DT@`Ib(;C&O~cU+N^Wi?Wc|jl&ZQ{_JAeaLM*}D5ynS2MKIqID^Fp&?m6ukeE0T^XPj@1cx}X0< zgru=}pw{1=x-1(u0GS3hyr$+bR%+GR6_jwGNbQ3TCubao02KL)hLXQ1`iQ#0)Te9y z!rGVz8WHDjte#gpvJ4{&>>6C&+uNMt5e&#jif-J=If`mmCF#wuW-2W0N?X&!2dDXN zH6k^cX#Dl(_G87Ah2JhPGxn0jV*y``-3G=8&f}5+0ip!0)TIE|E@CWz9qvLKtgtJ&T11}S8QjB7 zVSwVLZJkeilpwo`#QWR?j5Iu(@>ryBt8jr@$@GIjZ}MNK9d986YgN+`8ozXeg?Pb^Ut z#G)-u>R+^|UEZ^c%<`N;paJgFwe80h9GCUga^74PY%Oz}`MYOI-%ccJSD(GiooI?1 zshecZF?i;k+r8c!r~z@4eg{w#z@(2h3K;f!=sej|WmixBuGvx2S$OU@;AiWvXnGeL zi|%)$X7ecW%>m{*rf|HI6Gmoz+vKF%d2ePgVGW{OXJ&63PSWC9J_;Rtdq3450yK5U zdVuyMNutO`=`YrivY@4A&BxK!Ti_`Hf$M%C4{bA+S_bN*sWj~E4LPa6Lejvug-W05 zEI!=qy2V@H8nuS5vJiI?xE_tT6GSUP`!D5D9L=WfB?c0p^+_v<70eQDB%*n!l! z3HVSp8_jKtF@xZJ^1Dx3S0j%=?al4WR+bH^h2Yd%I?wNFMFE*6Qw>oNKfSV7gbMfr z0ZF)`@%Px}FcyKKTqUHHkKdShRwSLW+(3~iIy&H_S4ziulCvNu#I>P%a$3S$E4n2I z+jr7H30P3{+-u_uBS>v%lIoV4SL_}f!C0BtBn^(VeO->B@vBi&$DXhIQ1w&MZZ43~ zzek@dNx?{8v&z4lI=2SV{@gLQw1U#*J@W>q^$T0aBAMkVh|7UJ~AsKX?FBiuvx((oifHl=IVB z=&eML7BOkrY{U0Ps$rP6agK(cy_SWB-Xbt{3_qAuIYR05?GkRnh#aM20nhU<*ZJcF z&YWTF(tWIM-Wes4A~$pA9HoI?j`n)ixj-h0#l_&iFu51*tKEi5JO8frwAjz&4Sn#t zZAN*;qv?Fjh!SUT)vxK0zEgkan-jplYwCjEalS^TOC#b&)q&ZE!2~2dcy;zTv>_l8 zPvoP^`N&0+Ifb)&s$*fu>(}u@H0&}R8Krrt9nz$02^)czg7S20)z&0vCJnj zdH>{z(&mI&Yt7f0TM_35#%4B+;REEEA0bFh;NQi)JIO%g;L!ZdM5}8{;EO$mLfxId zy+nU;o7lSF(Q|T4el^B^;yoT;Ae==B%`H|bmxsoslUuSscVu{TsQQ$nH97)rsIL*o zZ3TeUp?9?Aokqlrs*t@$2Jf$oY(Hz2wP{**Ol_RS_&>MWIMp@2f)Et2*|4=_%>>3= z2!kJ78x>-4&Jp&%^g74mq8@x3q5DD7b8o)kn*gzxs6pX>J{g#$?b%6gnfPVqRTTw9 zBTA`vA+M(UXlF51;ElN^-MlGM6?2a7GQJ> zfOIQ1VhiVGx1;&nBVxu(SxG$%5oZJ%hVZ5!<-5Kjos~3nz=qSSAPo(gg-73k z2g+426umUh*nm~)CmHcWzQHxVZLJ#wOh($CPGgaB#K7a%EK#~gMkJZK2ER;*PKb}? znW(Im;}9is$j6aRJD#_^c*fOW|sE9+`o`@izi0j>pT#AA`enY5o?sc@u?{juv>#Vl7dbbcM{ zX*F^O9XOXMAA%cQ=IUd(>t|0KPb^vTzvwF$?3eXZnwThU)kG45o7@ivwOs>QiT-u? z_=H7s*W$>eI`6i8U5TS%e3Jg$VTYxbBs|0Ihy%EBOE^rAc;uMmTqob_m7oWi31lyJ zn_0s=lZCo&z50UVPPoz>(m}tOLOgZX58ZM53=wgOhagy<8nW2cHCWE}s4BJ=H5K`) zcGm5F_AHG~7SA(&tJKC-k!9^~e@~pD?Yr6RyarI5GWGpn-QQuCXa;AA;NgrK+X9b9 z9k{hqZSa7bW6~WmDzNiBGu=RQi6hx^w4F-7~)-l5VfZ1W;4_k zgu;XrmKq6HQjzyeEdakwqvXDP-Iuw=;LR}2;btpJ>wTJ3k_(0Zde29aB2`l@96|bS zVOD?5ouaj$9GSsY^J$gl=&zTSEJ86qR=q;6{-Ml`r{lPRy!B_+Z0t(-S<`T`t_P?w zqm@Q&Z$A{QNe=H@r{Q63BnbURd(j`vD28wRqWlE<{_=@?;K0Je||8rrs){rU4 zX83scuK>A91Oa`5b$p;_>f+p*fT!JMyBBybsw>*^;!rAfWZvLq_ zx1gW&d00V;TF*Jz%4~|R!_O0MAi925V0Jz$KQ@C-;Fdc)dvB)-0;|_t+s^sa+WBnC z*3<;-y?>#a(^fkgBv~i^c-6?Ir17@PgwQb9ih zZ?%%K#RZjjcw#PTy-epUEq~<>ZZ=ytMi^4T)x2pVW?T2N#fN)>X}kSw69IAwrq~(` zDwpeo>QDVLy^?3irts%WM1}?8M-Ro;C0`@mwQ>@D>%2(De3Bn;ztV7SqAwVeFZ!VG zeitA2O5%iDje|h)IN7z?{&;4-UU$^Dw_H!6<;JM|^y7`Bh8`{x@15W-=)A9N8&gE(@a(UTwR-0Xn2AYe#-sP(%e$9+;P2}n4d-7#UBCaNl|qanE0CEqhrTv><%Eqd8|`_BX%OS;lf^c? zwDAgi$6Yty+=wfu79#!$)G&z%;e<@4X?L^x|Fa$BX?k@fQFQa)&TEO^X|?LT%?G8- z|Ke&`TA9%}o?l1*&r?XDbxlp(Lb1X>r@)Q|$t>C=3=8~g;_1JT*>HUR)W7C^;%I=D z2S5+v9~olBT_9cO%BX&jZv*KaTBC)4WKQM8(Dqb+a9|OdT~n-uZW`yQ|3#HM{!s zg)&DmZ?n%aZ?g|DZ?IoxB2fSes7sTSW>>VuRu>9LB=U|#;_c6W`mcZfLjL^Y7k~SY zkKg_KpT7Kp{{IJjxw$AVi@ouB{oS`;W#49J7e)5rqMR4mye!{mc&c@}u+^*KybX8tv7saZ0h8`D1w#YBhA~(+o zI%-{hSnOr5^6>@rC$DA~Ikj6C;}XzZ<@0TkP0Lj_$ye{Q%W_gY$hPZZl8vqzq|v+_ zzt8dw|D(VgGH>c{I-3_X_wT;C|Ds&1H~9i1e*7xKpV?wO-%g5O^Udt4cwUzC<7~6q z7SH(Mq?qR0`DTcDjEk3VfBI>$InM6w-;<}`eVd*B`YJp77vOLB;^dcC571k7Ih)UC zm&Im9lO*_;7c}e1D*v!1&@PX&!@YxSoG&-Ca`Ax20UU>WkIYQ-)%64X^YNo>R8IKg zQy^rqS(Wn#*%6vu6!`?}@*sP-_b}VcHgh22aWd$ykaYmuRSp8b8B zuQq5f&sXR8MU9I+LN)<1DXwPYVqjnyOfiwcdYsP-Ao5`E@KJ@!;doUP3($?w;orBj z@%#Dpu_eO~InfCbK)%3#vr8;l38FCrZC3eWJ)^IN%c7XbQXTF)i**JjSbvyp#uu0d zpqFVfxeWR1Y`wXj7k{Ba78~2KymEH2y&Me*@PA>*6>#%(m5<*$PoJ1+g-`z%-aXno z@Sjr8KP}e9=FJ1{*>2@=pEsEb~lg)*+^t&MtP8V&hXfW=Aq2;M4iUxxb za5y>6?my1QPahr$m78w|OAs`;4NsqrCe!c7mIq!EJI;#@NPwh8DFWi07V{WlXtpWn zyV-^*3-q$dM)|6kUuTEM2NuaX3KM$12AkbK*grhj&lQOEb~Vk%pql$r{u6vZ$mjG< zF`e#D_rqrlIz2rs9u^Pv>8ALDhZ!A=4<_;@IQoaxb_5Cl_#k$YG{r&*<^Izr^pkBD zo7udY5GKd7bml@`4=%SGM%VYF?~BJ4CR##(G64=JQw4>-9IV0Ni*r01KO8?DKhTX3m6|Fb10$$-nzWF?~{eAHZiHKAb)*3_2~4`fxg) z?pL!GNULYXs2Dx_ilv5<11$hXd@`MUU(H;AG4wbvO=2Xg#H4Ma6lEViIe2;yk{gkg zHaL8E_~_7?Hq)c9g*G|Lk48s!g_sHqcp$v*X?7$M2ZM?}^v%)!=xFprpEEs}H~l^x zO3_zA($hzlz6`n8hmXgPpW0a&n$d^T{rvEu1E(Yt1d|6B$5RG5v^Es3fsbEqaA1?BZDmG9#EEOn?3G5wa0=xk(fC9_b7e%r8 z%i3Xq6_oB4Y>|pN^J>uR0>+cvTo6gG|gOt#i-0fw)gnhyN=Mz{< zU_?BbZ65HKliB4ZiO^92yNftdAuX2$42xp1tpFUCt3?6p${2SHuLCVGfd0Wc2m1$4 z4h>-es^54(nQaE8cag10eHVk-`T|x4_qmrnpF@|(Sk$K6Zh)ZCd`tL)atFW)84Lij z!Dv#LF8)Q6BV-Ezc#WkEgv<)E2BHZ9NK7GG4J<%0pYCaACyVP0HYZ_(><|JKFT(O% z&c>T98B}JrEU*BIs2aR+Nv4Q)GcGTeF!*TwMW$_Mb6QmZIL;TaYE9n+D=bqOX^Yax zK+JPKuc2{3;~i(K^HKiw{)5c?w|DUH8>NKxcr}AzcYmM%=(kEAL`%onLnRz6!x^vy zT7aT~-OC8Ux`W#2`+_8}Vp4Rz0RO%ymTXYY7ECYIe7CC^kfyW?j6>&=9hZ-QZYI7Q zsD;+5?6ScC;}2*ofbFYPeV|-;;7LZ{g2>9zngha*HdaCRZ(n_dc@A#)Co^h)O;+Al z!>iZj>N5Mt6CZ&@MU@T6T>wy5`RePzz`S9WW|>dORzA**fV_l_I)hz;e*(hc)!Q7% z-l@Z*%S%|}-{WsAtbCGn_lGcxEglV1^HQn!Loo-bf>qe?WvUxq{aCEZ7Jyh7hx0oi zDBuv_?1WndR3fe5H{MUIXoMfOxC3Ed+9km1_T_V)<3`cjZqtgk0q^h*e*#m1}_A?FOX)y*M)SDbI*uhfA*+Pn!yg^)$`n|0wh8!nEeY6LI0Pea7 z0c#wn5a0(^*3djUA)M?^GhPGI>ktupkzhJxM#CpTyg~zqoA?i=3ja26y_{Xa1X{H~ zMwVwdU_hkOdDZnTC;-wQ8JWvQ0VD+%wK3rJ6jv-X0|MP=!45*c44A*+y0w{6(?V5&uAxcm>x@93FTIG?wuSvky>&YiVJ0>1&uNw~LoY zB%?#I)QXpRG3|_&;nl0v3SQVpMx!U5#20T{&|aLwqV$FW_1ALKPJeaD>^TO#RBKX| z^qRV{4p=Xv*Ez%jmE zW*@@?C{_V4&<+VOO8Bfk`uuTVL}Qy%G!NsN=Ry``D5H85hgqq2qEzk@=EC;xW?q6Zlw5NRjARAs8 zEV^G2TK4pd7=FmUF#9{o;QWp+f;va6w1G@G=g-5KfK*gN73OGumovv7rF zV}}Bt6n-?e%bR@poa5#((4(^EI2$hx2HgKZctHejii7-^9KAa#|DY&6V$_2*CYd9= zx?edqK?{mJQ+yr4HW?>)e!(wCA6?O-$I(X~x&;=4XX{rP@11B?i9=jx(df=+LoybX zJ-JN8v}k^c0h6g}6gX|3BKS^G*XcaRPQj=c=ZLx6eQ|wmHXF>MKF?R5bWxjdX1r<9 z#{4&9&fv^KdIOx@`;+&`CBH%-%zP_sVURT} z^40hPfOO`7Xw3O`Ov|{4L)L&&zboM?d*4|AL@*JMCZm!KD)}rtpk!=JqADV5B%Z-T z&!u27Wo6F|WBwHJ;Wbm!^))Tve}>DQ?LX~|3+5xgh8XU{kOTs z<|U=*tn+JlnWegzgoZu4Y5y_d{Ki!RVxjzcla^w-;;aQ(O7qT-&?_nI-#Egu8{XK6 zs3}9w&JbC8b5evbnXM5Ako~83*t2a$yszqvp*SkBEsg!bgXJGs&-sk9*=Vnd?1H+^ zp*^L2urB|O(ai7)aP(wRt6})%)SiS#0)%BNRrZ;bX$uF|Qw^wY3&KO+K?GO)KLM%& z_$ENUEH*iEd_LAABQ*>qy`~0fVNqB?OJO;zy4@?Wf?B3gx2!^HXXO4fCrKv2Hk6hG zIIehHTU#bD;ox|m9VR=9Q<ni3fC#8-u4F>jGWFz;jCI@-TC ziuYrYX3`oQ8DdRR$7+RaR^B|*AIj3IN|@Q$)xsIk{VgkQg$Zp7s&@;3?m%1|-Rk~1VeJ;SN*YA5-3_srL;~&&Me)tWn51=+*T>{z zj6`IJ&X(-Fk0saLB&&^bO1{0u_O~QgEqJ$I%tjz^yRJN&G=pJ9h)7McIsWelGtyR}7oXaXCWv1XKBU#`k!Ii1G3 z?W85*y&{Pw)*~|FdIHIqh;`K*Top$mgbi{t3Rau;_2}EP|4qb(zN@r}cq*1b8QC;+ z;G5J~!rEP;YdPY@Com*Ac0-30bbnSUiDH%j4@Z3HQ^idp=H&c_$AMl}W7Xa_Z2ZV9 z%`Pb~8k9)PdUJ78x7~y?8S2obH`wX~zS0I^UOH=X@O5*<*%Ycd(Vk-}%i~Id8-6C{ zKmF%d@3ONWUx`GawI9zBe}m{y?Y3s6#o7-NL*)x?-Ot7s8oZ{iR$H7G;v8y8peC(^ zZG6rWqeu$pF_!Z+PFa1+@9_q{o)HtToYQ70Ej&9f;Wa=i6*z^a0*MTe?RtnvwQld% zDJjh~^-LUQ5sDI9{_NAA6L5}tin3loFw`V_bNoM+KS!#oJvizl3)a}VMbbq>BC#Yw z(@zlH@47!=kR7~zYkLEn1a2Tq%80lN-92_omZwB+?We%*p-mE6d-<)lJjbtTi2})6 zCobV{UlIt}yry%SCf%i!4-c^UfP5baTuS<jpd`p>}L7nbqLm!BX*5jOjjxHG~{} zB>ayl>lV_6^;wDIATXiW9zrG#%oAfWm6QA$jd7Y*4hA6nIJ^>Jv^0L?oB2UReE zWs&sR#SyI2Qcdvn<_qh6xrmNNmSVV7jP!`s-DDCC2Ec)}0Ct=RRDu^6ZR8 zZg}+rnSK#MtDEQb+1I3ygK#mz4-&^tMDLw3WH{7|WCUcE)&bq%I6n?JPt7U43IdVg z^ze_u}7mAQG9cmOpJMMt5gb&!1!q#842ZLroBK7!A%i*I{Z>2NH>(iwqryxR2G8>>;fEQ-~A_D6(E$TMN(ORy=9 zC4rqcXoalCRBS}+$kd6wCWb8yhhYH~X>@W2T|=C`z%k?pIPtw+<9sR>>;j?vW5_p& zxFXVX0ke%xA=ne-My8!GP`xEu4b}$pp=~J|%w&txRvI@d8`DvG6U)1W!M+egCjJozqIo zkE+B~D@)`nCXye@fHYEsDvML41Pcl57LlcL3=kjC39y+^afyyPQHm2}&k1Kd!DK>^ z0)?}vGL$)@j$>CPidb<$FRoI6(hT(b0f$WGATC*2O9-vMa5)B2vu74}#cW?RgbD8fL2UJ0>WAeHNKi%2ic8>wz^hm#=H z#G{4hfYLHy2%F!&DZ=7hy6(P-T}Mc5^rRMS5WSYmTyl-rk)duPNh4FZ>ljgA;GpX*JvgY!dbb=ZT6QZ_~|JlLRLDt5{;gfg@r`7 zRIpJNBdaVQVN(=JKS!P**Hoi$A#JWA9%`udIqH}+oY<~WjHOqK9+Q(`{*ThDbe>8P zjH}e0Ol8$&r1dO>N09_Wtg2+(8;+_VY{+*a4ACSaTDsddvX)Y^Nl27~q`fvIK53K4 zz-{}lqUmedPwv3!45)^2cQP<^R4*{;mRccEqNaY$XsOX)Z(iwRyCV!ptj`g)xCoP_ zI|ImS(S=j7yV7whtowxeO0tAMLUR&-#C(cys}XAXC2G1+ktU<)NQ|Jsk~#(Q^kY1m z_0Cohn+^i8t8$`qHLg{1_1G$*Vn19vhm8=aoJ9iEl*3fJmRreGJE&4Cc0!_IYBIhD z%j**{lc>rVj^p6S(tC;*;#>*)(Cm`2jHZ+%1Irl-E+uUW&-u_bXmFUlCea!!riGo7 zBj-up2h<~u75{C{$Uh^u&K zB0S(*;6FL2b6mFrE89L1gpMY0k;S0cZx(a5tCK&#fC+y41Y$-P+v3z7_le@v2oV!8 zLcRbmDGVbdSCD0dZD6e)#E;ySziX>7Zb&8Eua5RCixZBipxhg3Zi1m9+{dUiNp_*X zOTo=FyF@FFaXWs+pyEwI1#lw^zAF>xf*EEAc-L_05J7J-AE`Dj?G;c_J(>e&9>dSE z+swKfGs)K%qmmK@#O*mjal|P1?qsPjx1~dQhKYI_0?sy>t4JbiZ6n{Q7`WNRAU5TR zcXYfpTf&KVgM@Z-yq&keXJxsjWiy^P`K;JATT_8$_#eEqWc5@1!}siVgA=DF4T@AB zgbY2R`ipsnOaZQWjRXU(DvbbPj-r2f8&#oYwNJU`w4u|wcD#F)Bvw2rCi^KMZBa|Q zQz71`fZcPk^uW{bN?xa=(yCH}=hR690VIVL3BDLtprUis0}ak}DaR9$NyjNqs-O8> z$#V6DQnH*Xrd^|2D3!Zh;pD3%&`I!LMK>P6m2I<5bS~9=I~g8iuG0VP%2jnQZSV_^ z0*8)vjh<8S>7)!z@6seKir#W2x&;#rGXz8{WJ;6xRd!uI)=h08pyXcDZO!6{@_Pil z7HRp=u$uL(X_a-2jH69c8eU>E;*+dPgWqKB*NP1yYe0s~hf+@uyPLqG+Sf~>`BAa? zfa2t&C@)Eb;`eN&aq+kLZ1oHF`jbMk<_1;}AqvR;T9h%V*63{Yj_OwJ)Hl3a=|4Vi zFufGXB@K~hlXAQz5r8lfEgv0!XLN9)D{uZ8Nx*gOW2?*5qo;vc0Mu+>PhWJ~2lsaK zEaq&G-;$rZpJ~ifu8CkK`G6zSq&msk@d~Iy&Qq=6B#sTxRIoR6m_i@Wm{~s}QcSlc zY%btio=XDqdPB8%8P%}4aJ2W{5Z`2n^wpz#2DkK8GVtrk)*5PSRMuOVS)8_r0ivsP z+yuDiLe>;dz%>zEjhRqBEm20uPI!N%Gw+T z!WIr-G_F9^gtANN$_y^BJHDo}8ORHuO~YmW_br#Z8&Mf>Q(u+8rtAS|gjgmuxX)f7}`dUB{ohi5jwxLcn)9Acw*yO=_hiiS@S z>20R>E7g3FDSCUd{mjIka>A)5+9oH`kbnOCTZ`DXNvML3s%VlCzw;Rr$TQ+~Nc;lM z^b(>T&KM4dsFm;rw-538=h%Y9C40~GSTPqJf1~HZI(0x@oJ7N~5^OMbAX|m}*U~5e zBT){TjQ!-Vl)FAOogE!#v%=50c1cq9))uj^0&kCW>CP=32Vs-rqNq71-!g-M5oV*^5 zy|<|KZZ1pBps4E9*`+kDw2juXd^EtM)}ik{fT2M>ILBwEgz-%Xheh2meoNk$#6PB5c8Zaj$vMh8U}5Dz==2Z+%uC8CB`)8wzB3% z7y#w7@QM`EQ;$sOBan#Q&8<*206>UIv(WuYK!U{2031?1G);;3HF=Iy$U(B!XSi}F z$E_PQ4}W9A-Gdvx$5mdq_$Wqd8oX{g^jj0+)-yLdvq^L59&-K>siw;zr?{Z!;-1+n z$|Z?R3Ysm^rjQ*6YJOI}lg2S{Z`E#S(6-%KfSEcVdL>QvjcILQBTBh89H?66Yl`YS zyJqSwGc{JXy_}WEI&W7+F3YrTuGp0mdkxb}h1yL6E;DA%oZ75Jh3t)FAjcLeaNBPX z!Dv_|n*w5|74;8yQKHOJQlSSySyW}C+VFH0nB9zlgS(;f&U6~iN)-lAt172Xe~6wcE=DE)H`J&Qd2-R?e|eJ|I4i;1+FBaDJuDl^haGA32`f zA}}NBjG~R*;wVB5qQIQbF}jDq15BUEU5y-f^xPMr_B7a^-&eJgr=n@YqJnq+J3WEV z>~YmOQY(#;N8&6+dIq=SVg=@qXlA{5h+^Kx%19bV4Pu8AVaQf@?W2e*Cg;|bUHH^U zBzsX~4)WXhn#4jqoHLVyB4!p|MU*?Wo2fm^0(iDf$2};)r5nabBGGsP4A&cIv)!V- zyvNo}PDSNE?X4_6g;OURJRC^i)l7yG2%gWLH)#co@k_9O#0O$ytZB?q*r4KLlX`;6 zf^P+N$Hg$o@TPkx%}$b4Q18kQ4b+IIGF&(1#TGwRYoP)$FJBFRK0AH;mtl7L`>)wA zr>8&fA+H|}^!Rl6Ap0Hp4s;xWc8!0TEw+D%HsurT=DKO>Xhddpfl^eYpy@Om?blH7 z+uRyy55BWwQDk3JJGtZTjGFup`HX7ZxJ8mW#NC{mZ)7ZL>X{J6FQM@MFZnfW`}N+0 z?oIyso9rBg7}s)8!p8NrEDfs4u zsV`$hUvpvJRz7H=6(87QY?|th}Wn;LT%zgcLoC238V6y zxe(mo+#q&RjFG!utkolDUeewhkORQ(S&^hW=qBE?ikF7ntMsJ>>`4tsQ<%&Oo|F>p3 z;(g(QY7;yn&x>yqU1ba0ERHV8-GRk;1q1I}F6H~pv8*iBWyhFE;3v$LJUGEukqeH( z;A{cy$N;cQx@??o0A*09vsHnW#eL790a{HtxlGOll{(rZuuYjiK`#=1Kx1SfIv3kx z8;86|>JDQ_d)jv@{#fF`Ge)-AdZ{y|PZ^+)=uKVN=ztrxslygr)HkJm}iNV^;F(~q`X7udqfH|5IqG|p5=H>g z?`ysx*4YZxIf+pdkn=ass)Hx{&z#U~M(JC^4|GB7F8z_~t=oTOHOG3FgK){-JsgJl&V8#r9lDJ7$KT`UNh_jkSUovno%=AQWMsWQuGzaWXZvH5wpL&g zhWh&V1b|kQ07%N1SCEQ()n%& z7f%cz+tvV-j#|dhPC#n-Ap&L~>~BCN*0Xg9<#6wiY1TRgdc}ZXw+Uzya=25N>y(Wh zY#(5WWfGQ&0h{qGF@^}ltg_p2tI`k`xNZ@<26hAM`#c|R8yvT|k zu=FBEA=rsg;Md?RlQ#x^<;oZF+pYg@=WMRV@!mbd4jX?T6+-+(u5_urMPEC_VfRw~ zhWTjy0u^&`{X6btRQ+A;O0poSgbc^S?83=*5-%SOgrLJ~RHh)G7p@3=CpnaZ&v=7_ zNlV00%wrIthUqy+2Y%Kf#|5Szcz2B46~`$irwYM)(}zy~t6}lH9L*NX0`r z1C+cZCzre}1mGo89wniuH>wql+yYdIy~v8iL{oprD6~7fA!guw0{aNW^Ch*q{uUcj z_f4`bozKBS0W0;BJ;swIm!92Uvp=XvVULeoPjd8tzW^QQs{)&dG)ud0Y7Y3I(mecR z!uaw6ox>lUpbW0rE~UAe6GFZSu&0BPLdQSDoFJ2yu%fTNn6O;*azIvB7zZ&R6j(*b z?+U3xbo_Tk-NE^v|9_@uL@eTNQM1f-s&rNA*Av}0E~JlLR=dD1eplFKV^H)3#;kVM zlavwotByLt`&Jv(E##CkOB?VyiMTSiTuFq}VQO-W$0rmf$KHmU9a&8<%Biz5(nqKX zB1eYpXvrjbEMc?I@sVoTlW=`bLzaEs3W;)ToD&lhNHm93?*NH0v$byZ$r++FR@Il% z#H~*RC~b3j3M-JPyrUvFpGVWoNR??gXF&D+?i7Mqv%8CT;{}Z*Ua{4&X%))?32$6*{z!=3viQ<67iA z#?_y~CrzN$2Ud1}t8B&?u&-dI;&`}^S?e;?Rh_4#`{L4fn%fo~yD2j|2kKd}KTr{k z=j9*Ie27`wDf9FO%ZE#{94xY!k(D;p{i0YJP97#evve(K&{cGR)q~QvH-GI|ihV^a z6c6!s06o@cen)wmy8MhZ`N+p^Ty5LpRUs*Q3wzt-Sc`$QX+tq430PBytAPr41-352 zO-zqbVf?plfS#D@I-dl}@5P^Xp{#kC9xo{$I96p<#41V$y@kEy6N+Lr>10!aNiOaB zBnUu=d7MZ+RISXEnG*wAFnFJue~kTqHq)9DJ_bvc_SFE4n-! zb3)nUa?U&Oi)&44puoktf zZBDQxoh&ZO^jUFeLZo$e5Ou-;N?E4Q!Oh@R&%hN?SqGLXuu|g2%aeCMXKzklzT%t@ zCv%39lo605z7r87xg!)KrF*M*Zd(+7J3+<>j!dA4Q^8qFC@KlI=hFp}Fhj?SiCIlf z3*{Wmsa`7O)JQ@I;*gYu_2ReT+3A~C?~sNyeDUtpt6ztdRP@7YJ3_E=c?3Aa(rc8KyLr5b z35wV;VJ{|h_M29)A8bJgqytQHRK53ppO3F<;%V zktXu5U2)ZDjp`OBP1yPDOkKCNB(DGk{se7ZLdnALQt!_MlFi#goQN7n;u&EIbW)g@ z^ygji6_XB3^tX(xcLnkZwSSLP%#+#Q_ z7jH>-R1|%GdQ?1~9JQv%>@W#2_^Od{RSc3jKUVTux8&flvM^%L)lnC92J_+tK5A0w zC{0WjBbpnklxn)kSZzOuLd_!LEEcNkfG%EqQ%J$rE|&dlrc}LvLXvmw*)(TuNi#cZ zNsIMjj`a3Pboi==Y8E{JASUmJ)M^|}@X^DjmI5^u9hp5TlF-g1I<3>o+4>^2E$&%L;N0Ym_#E$D_q^_nYRbY$OvFLLk*l7lE0X9`;x62a^)_~Hn zV>VO*u&brm@+?%@V{>1sZtfe+X}Wd(-nrV00|AwPDX>=UvBekU7q>`#$6&p<@nBX zTymC=?t<9tmVju=ie0N=W2t2%#0@eVM92vq%4##}7 zb1*RLY*5)8SgOR!uG5Q2&>PNeNcTP8lKUUpCxAl{WV5qEzde#{ zSr1xW0_`PbSl@1!&CF+9iNKQJc#7j-n15TMC0Chbt8r=pI7TtB*Js+O+WQZ6mdIo4 z8}Yyl+$r{5F4)<)*Am(5fiAJPd!?P`gbcZ4%vc@)+;`W8?1UpUz(V`07{e z+x_*oSHHcYT|2t6h&Sgb(^JmgbIl_u;Vb)gCAko_Di43%7TbanvViRrfjL1l?qpCq z;TQLweHlWWDA~o`hBNybl{w(#cYf9q|nx?c;ICeVCnadT2&C@A3(dNmB zRM_upDl-yjt{4Uya)-k%t6bjDP0^M+EHEYntE1+y#i=*dzqAe(%=FwrD$47|w$`~Q zS86wt*_x{L#WUEwwMs#$5{3r~he1TUTwn$@Ot_2I)^mh`Y`-1w&5b_FKv*ldRyWkNPJ&R z24ajq(*UaI%s%TJP`Vw_nOXD5)l)w_rxXw&Bsc({cv^PEyPTE;V8n;=sG-k#M9^jX zjRGIXdQ>nZV?)YGJFKO5O55cbp0p>-lutX^w-zL^&zsCcZR+&j15*)%1HhOW)HtKg zb^M<&3P5rYERGl~b5cGC6BRk@@x5JBJF=&iYH_;+EqW7MTF4J&JlXUT9siwZPe6rc@nwP$yB}aWz}s0bFP3c1Tx@1dHvO8A=_* z$lLWD03jXs5-8hsU&Z$(U$$~@id-5NmQt#Kyz0$1J>_DydaJW?|$4^GZ!#7cXMENblm_htT*M&WA_dNI~Vl znF7nSidld^_|e=?K@2~X&TJ}>V?$R@ePJVuk%&;q<%N!9^xxGr3Ir9s#WxgoeX+L< zC)X;b^7)jnboB|I-m0d$C(oHhn_}oh+0rORK?G>es4cycEEyPCm#&rf%=wfE_tA*Y z&DCm}ZJNyS374Y1)@ndgJCH_025$(CDThY8`YHZUcp{i^im7#rbeI>MYo^kEMH@U( zrKSTIb`FK3E2?+uzoqK0;wqrK<_Z%r@A0a+;6tSUl&rW``Dc0)i+rg>zu zl({ELtqp}Vg5FJ0ffNn{1`^?yu+f5^`Ivaf@SZ+Wb;vE>7J1Fym(~eWfZu`(Qro49 zc?r#D)}uJ&S33KTZwkJ!#TTE^*)~;yRN0VPjwX^0%P^+;&?Sw^>r=iYvV-sgqxPZCxC&uJ!$}`K5;!bI4WNh1KX5ts98}|Ea%m$aNEsy=Vh`p;9vjpH`9LY0 zM+t%6dyeC#H3F8_p;M;7(QChMFGrjy5YY%t??q3aHxCz_fN(bOJg!O2ykRXVM=)w! zMzQs}EQQ0`M)D|g(Af|~yr>pNRCWs%oQgR&EOund#%OFv;tENS#^~x!P}{!l1iceQ zep;!}D_t#nZQFs{W8#`#w@luF##1yArKHyT9CQJFO6swAlmygkt6>MHg2rf#J*v#W zo}NW#3RgPoh}5kTD>O6Y0g}v2^uqn>7fLzMFGAhV3mp&S@FuP6g`|L#1&vvB(nV_Q z2C4?%J5=fsTkIZeK~t|Mb+kCwJxsfYvT!q%ECw$3Y6qQI<6K{ib?P zPw}d{Q`a|-k`xzQZR@|4S`@u#wLvoTtDZ2|m+bAV{#Xj3_l{ISYDvrtZbu2ld;j}z zVm-8YzuuiUvG-dgVYG*oGP`f?L9$7nG|Be(O8s6F1vM!Zqa(Eo-Nc&oA0{n#nR^v} zmA-@r(r68o*@}dk_KD(vMoC%?jj%P?4{J@=6DvJ6yRwP^yteY{ zF-qEDdQCa&N#M>>p|a!~Z@QN2j!o-Ea<4~9I?-4|T6%JF&B5*tl-V5mZX7oq1W&%! z5V}mhQw5Vlp~@CthdH*zWLt(#l3a(ODN$t?7Q|~eBE6RQW^%^Oq12WM_bEPEXSRm& zl40Yf`j25-X8&ktjLFA~bE#fNyf;}WG3>vg)}S^`DIIk{B2M{^wf%HNok*`DfBJF? z+?Z1u)VOOff_1Wiw^h|$qtIs;t0uiyT=lhMSs6uJ)lt&kPEI4O2!mHzYPz+pMgpJh zwc6S`X0`}nZ7rZ?UTCJBn00l7@@2YQ!>U$UR7JGFrkISdq0@5@(u{7tP$Glrt_et; z)61?!xa@MKJP2_>Vo@J6Mvs|AP-HskRCeozuy$oe0;%))LTPsWfS&Jo3+hbS~2xu-s)0KB{pEvtqP_B0o_!-BcNcYts@};x`n%ZV;7?oP%;AcdZt7tGl5{Q4-Z&Y?QUTE!azy{o7Y>fBy2n z{P|CR{zCrz;}?JXkB{H|`=7r2g8u&p^DX`Q-(S+-xMSh6*c-3cZ-4%8|DC@05JmhDXSh$oJko_t*J-&OLYg>p%I^e{{qDym#Zr|MJiN!T7akunf6D+m2U5K`*b`y?Tx4X-sES+-pgLWv)*8IdeR$C3*H^|2fbUx z;mgsaH!RNk)0f5ivpX-gcecLTe6sOC0B-*5=Ff_?VbLG1ob*mcN6{PG!UIa zljD8ys@FSp7!0{M=uOXi%yxV>oZPe775q6I-f3$_lhN7uu-85t4<58vnaOc)sx@tQ z5A4(J@u+)tsMRe_)f>Z~X{hmQHq2{~R>_v2*Xb53D@BJ9pN=Ne<8g0tZ*{d8jf<0u z$zKih^Yfi2_DPuaq;t|M4o6*}G2Eha+|g1N{b~E=Y{w7o`3~K?Ne5d)Ux~2EHlnaY zodG*$KTmGiK8?<%MTeCFgUS_IJ?RWP$Gz2;Ok?`;RuO+c=}pJ|Lt4fM^uV*1y|e7u z_oIUegV3MVlf%_>ro{i_PPf-Kwh)H~8|e1NKKgO*=xox{LS4GX?P6EME%{Eyb8KTZ z3(*3A*p}C9)_Bssd2&IMA?V$noJ`cjUOWf`$zGp!F0>!nH)*}{uh@ZEDBBgD`Op99 zzxdbwn?Jd6L;wG54$qwSCev&J&NTJ#YHn#wi%mnd^OrCINOq-nmkGX%Op&F~_Bf*i z4~6$y9+u+$Nx|B$Oh+sHQwSDxAxtq)UwrBOeNQ7XR*-|D?dTk8Q6tdXdG?e3t6s4W z!R?FqpstdGm#kZzS2*h}pyb#+)kGYtA~tJK;5yTQSPisjO1LGeMMU)O6{myFp=bk? z8;sy9-Qw(&0f51SJ~J2{u?$m)PyL7GfFj0-DHA8qWSD5tnY^0N9VB*uU@MM$BeBDA zU>O(I>6OfIk8XT*CjH|fJtC-J&PkjwvLm>dO#`t4UuGPdQ|D+(UmeM@-WX>2vVZuJ zp+h^pH=V=j;KH}ZrUB#?03p|ybpIsq949-7a5_4zU_GDrhi!!O%1M8CHtjvQbNlvf zv6pq`Hv!!mc}GjD|B}C0QZYu+w|ckr>P&1i1z;J@B^D!ei|pl|*3lQX*ExJ?ysOxK zx|_&SlBiPzg40v>a55TKhW+-$%22kO`Q)qK=`_)T?-SF4XLN5ROv?u$c3TqrxOX@j zcfq}zRdW9q|HuFHzy8nv?u{Gz|37FZ_Zdo`^rllFAuFUs1kBVxz~v^bWYD<)KTL7b zE(}KJ;4Kq9Wl+6tZ-^>UtQC`s$+UN(1(hy#Ay#e}BjGL<2mplpNZJ$3-04Vo12Mkr z4FRXGd&AfL@n~q8+w0D_FI~xAyy{)R$xj*c$c0!{j}fwJ28Q9ll7jbWZEtON>-o<5 z#tRgVAUdQK7!~O4mutJ>o#DKI7$X8b5LNK46IN#`^b!xd7@dJ|8f-2IR_-%nDPa$@ zbq=4|`s$rCE4b#Q5A%Y4G}jZfEM_+z!De8Sr)OjIB@sjcMSBUlgV2YA&SY}h zL3L&RgAo&zegz4eCTBA;p0Yj~9`(^GPJ2?MN^$i;HaNIVAUHGV%g-!DWr{d z(e>3?e>r0MB28aUN4QK=cOfo@bdS$QN5 zE4;kZj+^};OhxKYoR(V#aGV*7qG<6@l4LTy#g-g)2Ft~;*Mo?nLC_makP^92Vns@^ z)BzjB9*@!8h1}Bba`LOns4A-jP*5V+_Q?9283sfb=86U@g3s3!qhU0j4*SP1!xsZ^ zKw^o7MZB3Qaw3ur3Qf0nIvAOLV&rhEFm?|`fXKqas1Lvv*n>|LDT;w0bXUhTyP_0tZjxs>;nQU6@ z7n$=F1ExK1LB#=l@nJirofo2oXyTYzYTFM)(*-eHMrX&EikhD^j6n1v1nH-7XG%(# zp7#g+QCzih^4eH`*M^6h%O(>To(5L>keG z(EZuKt(XIi5_7+J7JOAlII3XxLASQ0R5xlS{lq63oIp-a+tEVJC~20#h~*}6goK#j zMmc3~@av@2*Nn?=F53ZbE#)ZNf!);Bbbsiq`1tO}ck6#?0}c<-xqGbQ$5mVyUB&>XgnwUZgs~(-44ti5`P*hddWdzZAfd=Z3Bmw1h zL5p)#4R`=7w4gz8gcJhG>lUnpW7Fyn4}Yx0mTwe@eKl(2KoqHlJ*6z1Z9OdgGZxs{XAd!rU1_ z&a_YXBwP|ZKvUIPAB;!ms7Z+#tg@S{@P7P=qPz@`98*kp2VNIV+-wd<5p#>nTEReh zHN+RE$59<~ec!QEOwUa)RQ;C#5w-yjH=H|~7f}0xlfsyHMK=|rz)$!Y@JfN_x}j79 zBiS*|IZd5mMAR>)=cA(AKRSZN+hWby@VN$2G0lxz3`S>gkt9~e>}aspHI$tDm2Z4? zgI6OMJsAu)5>4#nA_{QfTNoT0e5kH#EYhFFEjE3{wqqTBW2X2PMwBK2FloqmO5=(K zE@9jF6-J;3Ki04$`Q-6g=e*ZXR2nb`f^_Rx+Jc4@I0s7_MV0TjFv1p=9k%E~pxw%_ z!Wx#el<%W!rH18(UglBMM2fy7pZ?T77gy0Ac(OYr^&q)2tm)(sbNMsY!#J87=K+{C zt^buX6Y#yH$?fO6k6(Pf@jJ{xxg|A1-##a3{0M;kCNNLlp4ca0T$5=Z)*0eTM!6cV zWK?9^pV{YiEkJ8-4#=QSIEbiCn83-n6f&;VZH)wAa6A-0Aly==8m%@P^;#wvQy7V3 zv_AgDvM^rW_7Q%=AtHe|0f8RLy%SVusSzFV$+J^zI-@kC%^9x2linCP`9uzfwFuYN zWRI0X2Z_$qgf&v$By;-%@XBqbU``n8iVR-*PrLZaHDA^>@GZ#`Rwj5`qJ5cL7jAxN zI@&8+dkx=2wu!pRg4oxAT)yh{wtK|Ta1Lv)N~E)okfXLuqhb8$d$a)LrH592{zLA3 z`q5%0fe#X5EE^MprhPTm~MHH&(|VE`J@WSpVa8MuC0MZ zoP-59CggdDE*W~4PXs4ivAwmk2jxZ|qJ|Zj76UZD9lA@{N~}5-=HRT40_>8WeY&vq z@!>etwbkE)U)}zd`=k+25n)4=R(KH|=;qX8QI2u$}J=`Y7`gn4=eO00Y>O zOY#6O0$$p@_#`p?mDcvLMCkXNv91P$1~b}Fdd)uY98U? z!_EKtpZwz+H}wC%nR$dyMGYJ)?Ma7dBAx-Mo9)r~cy)X0>FUSr+k#?W-g`6Yr?1b? z&)er8*>`tu-@dc@mrtMU+V}|}Ky5gVX*KEJ^GQ9yhbFO*n$Pd7m^Yj^ldg|(#Rk>p z_oat7#li1O*&h7X=l#&$-e0Xe^UqG<8N;c!v3AGE&(qEBLx{R9`$z&*AW6|@2|%142-qJ;n_cegVH!Li2JMA)AG}>9}n4%mEC7++q;jq_U^C7r#>s8Lb?985rZ{Ay332U(j zr9_FSh>xCMGi}me!Lw4)b=d zG3gDUF<42YHlJYvCXE@JW+{k%4;X|&hQ;XiEOc%hx}4QpySsf+{BH}v!mOed@SER0 zwLJ*BKpN%ScD51%011$~H8v^d37#Km3L%1kRpER*eSEE6Wwxi5>L$z$+~LjSGg&H% z3E*`O+YPOsoD#a}?&$+#v@&fht%SxEVBp`0@US-MxE(?2k97!PoXyB@xk@O(p;P}-ef6A)+5L<5)?XBSEhYR(4-5Bky^wH0Fs+}VtAHHE{+$czY(Tp?RO#~TKL zR)Ehc>Yb*W`3QKSW$H{3uo$tz@s*Vz`h3Na^2B&D$(9~pln>Qzo@{B5arSv8RPr62 zD^}DbGpa9zjJxd8unwS$n;4IqX$RR)x zLG<(erWLjqQPcSO4?#+`!xMHYzN^f&SZ6cu+ExI+wG>kwmzHy%>e4eDD`$NZ2#mufwG?$Di4$Ra74GqwbgAy>@XWK!Ox{ER zYeo{dL<&+^DJVhAKg|Nh3CunTZN63E??-4aqyfpfr(6$~D`;-+l-3+1#cHXvC6Mc( zAJ#_u4WYTtU;(tl1SNfU<@@fz;Q|=3<@Xuz3{IIWw%htVU_xWGOMIF zcJ-hKQ#A4Tg{DHQFM0A=6{h!VOinmN&n%+rlRYCfd3fFyAz;n9LAC%^G)Siv4RHY! zxl%-Bh~gj@!;&kxuD0d-Gd7LCPr>OXOVj$!q_BOzlD^O0RJJE^++VamZGUt*@-H~2 zj22ASu>S6wPG9O6_fH3BM0PV(#3tZR%g+qM`RkhMNPRK#O*fCiigz@B(+xs2eSPD- z$z>`^8tsfy65qhK?~@=hTvsmFLs$Axk_0W5mT6>b*1mA=`pWp6ygn2g~u7)R_ zv-rkWNm@gYcrQDHBY7V@7MN7f89^LpnExB})MPWsSg^h1)JOhZsM-m<|;+~xEkR1o!6biXkeM{l5C*pV#$==c14G58sU8{YPlEXkX$*1YwP{oaryf> zn{t0OVOukV5*wS}YryvdvY~X!n#~t&AvBBa0u8=9Ec5toM45W}{^V+SU+l4L>y?}+17&C} zmD2>6DVU4$m542~8}fGrWzbU1jcAG{sJwvS(q!Hp8`(NKA_8a*ysI3Xw$tWgzj`yx zU8Rv?;^8hczbM|U;Y0=uNvcCROQRE4-(3wdxO&CpFcUIYrh?76eK~pAKgF_X(mxVo z5@$CY2-bN}^#53Bk%^+3uwo}o-K|QXpaWMQYVb+hmEs=)Z5{}7Fye+GA~mz36~~K$ z4=a;14!DuVaXp>)BL(zUp8jHvNnpc7$0>6gXQyG`NLRupjY<-migk(@3sft2uly}~ zBn%16Nhet?;yg1y0kuEIJu!8I_9cudlLpG9upy_s-oFGLB^i`@DwIbrv+ z_g2p)<5lQnl_V50wv@p`nE6Y&aO9;l!f{{muA-7*fjpuMMy2S7l0U~;>s_mmp4swFgX0-7m3ao0o8O=-fL7D3|Z2yrAOc6-*6<}W5x z>?>M%I6!gQzZV8etbs$viFqzpWR#eajE#uLf=DpvA=x7OFCXMbM`l)?JXUN_sD0wpe}}@tRC~rVDyKFgR^dLS2>RlAQd}g zxbg^MBeeM_p85-m8HNx`MED}D5Yx3qx|!Cg z<&R4@(NCE3U;i}+N67KRJJ@@$1T$K?sR8LrB}Cp^k%Z#wsDVAWeUonW_e&Lq`L_5u zD$?<^xGhfFl|%6m-zYrkjzWeuZqvw?mB8arS64DQ01eU_oa50;(Do(-i8ffVCo3s@G67M}hFl)CHidy~!~gSXZ< zbrOL!^m&D)46`%5T()T%nk@ZX(_AVZ6iab7LBco+zwoC)fLLou023hE<8N6xrX&EY zsCLJ{;hA+}6Vb%&w}yA@t-qV6Q3=~g`zj*UBjFXZv|;%);+APH zck;RT5@cUiC#)ry+cvI4Fdsyv0g$j2tku?&a7so~kc&0SnBk|^94d*cKeubPDSh-7 zBTih!fGF_6Xj{{9vC=Kx|NZ#)!}p8(%|i~Zyw#^0duuD3&%QF(dzjXW0HHM*P*Wg4 z08`n1Zm|^tbKcE|gsI#R>(6T*axaSm^$qsXw)M&S)jES5a(0evQU@F+}~q zQPIW~X)e%D@$>jY%dq%XaLIt$4#24~!ZLcSR;Kvx|KCrS{`Y_V#tr@dMQ+WG4@uF* zgw?dLs89GmW!2))_pI7g%c@;nHwn7vI^Jd8U%D*dRIbrrwVpF|c zGGxvs8*g+%kYLqkm$xD{&rhPSaS|QxdnkakXsciAhT7+2b_*Lh=~#&=N!)Soug>IQIp|3^8Cqah z@U4Bn#}_Dz7KX$iA_>9rnB>IsY_z7xWYAM(TUZvGEI5wC8bFoG8p+_omg6z@l?EZD z!Ve|M7K}!eRCQAr^hqi46Av_idKZ_)dX!NWvi#!Q0?>4}tTV=lVFK-&p*R3WAoXyJ zUJ6Gn)9VY_(68}YhKE66D`W$R>u5!_LVo_A)O95i>LiPdq|C3NG4@38gQU%b5rz{4 z&bI|_Z8$ks1*(s4-*H=QI64@0y-tQ#8R!o^=a(Y@3jAOS(yu2)j!z&85QJ3AAU)t1E2l&dqwn!MH65}xy)Facja~rlnUCy&H*t$h1N1*Sm4A+@ zeO}BV@D@vbt617)b*0!^I_^?7q#tcf1S}kVtaR%`&+y@!mnsJ54vv7cFQHD#kPwmY zQ42*2>A8pUV@Pk_-`RSy@nT=9(oz3S(;gg9Cr(*4Z1WC3wRB>WJrTrwzS$Znr_UDh zkFh0T1=O9m7Wo`($ei^Li)K#La-YjZ4O2NcJ}B+W7GtUS&}LV*h>Yz1=jaGHH(1d+ z^L(f@q6ZNzDx+&FKSq2oM>bc4TcADb`V6S8<$N`@<%W+ov^IPaN3gz@-=;)l>Ts7DXc*C53ww){uAM~KDV$DCz92d zCkEc4q@|UK4mV<3%R_Gjxc8%#gCRb3Lmfe7WlBsC=A`>;_2p-C!4#V`ma7w{es)K zpCI0aR#99DNMhC*bJ}BOp*%nkK-!6YUy^-NNmcuyKfEUz!bapJZp-QtHL)ifl*4V= z&kiuBh=APwqF4=6nBkrppdX0HwkeA9#<9*F56d>5E;gK%fE2}_T{f=Clu3{yu9Z^( zF7t}->+gLb^8R$R&uLbi5fndAF8N8nYn7BNcADgIx%~E#<^uCka8T&lXvz_TVV^_8RNLgl0;d{__4t)xPXbo9(M;uf6#Gz^Bj#Pl zo;ru)5n0i3;b0NQNfIP?`Ac8Hv1&=9ksXPb2)CvDF>OM7)~Kq<2sOX`jscSa?1-Vw zQ;9BUYC6i%ReYau-+y4coWBv71!R^|VK8rhS914~pNH-w`Sz;!R)@{_&VW=Mln#fcltpU)#RhQpsa#nGq#nZ57z%)E%>}~h`-aiJt%>!6fJ!UrIf?^F#CD6uujXD3=UzLVW};TZ46B5LD4c` zHgN0f!{Y9fWi8t;K57_H9EyF)So9TRiQr;mV1obk_9qy}FM$xhzR%E}6rZQxKP&)t zFa5ewd|HQ~X#t>|-G^c4&?%x^p{QFYy$N zb*QzaewX7f@2|X{eI@Q0I@he~JSPM4{_VV7N%z?&2I!KG*e(2TZ`V!9UgAW6woI{W zUxH{XWLzhjcf{Y{5cG#crOl#=D#1}9`ke8gswxvsM*+3jQjAUaO#;oNHZlx75w981 zy9TKhZNL6x6J{0omU_>Z)#R0gU$DX~I>7Hi0H6!LhcEYbdMs6aZQA@iNE|(zqAfP z1Mo>xIV>I)?|=IKWj0xD27+IrU-^?HYaq1SC0Ct4%a2{w&Q3C;R(J$k0fElU7Y;d( zA~X<3BiK~lkg6-*suAob@ohPHK@!o_;QT6n*$C&;__l{jP9!QK#lf+Vl z#>Q%@FF~b=`77KK#QUmp1&~6S7z!v&$tc8+i17)<*UkIaDL%S`1Vrurs&_CxQ`Oj5 zzs&=UZfG5pDeMxq*!r=28w`sIj`kbsDp@@8s9}W}-2#?!glIlVWCj%X#eS#|2-xMn z!AtMl)vua#e5Idx;i{IgMJgwX-~;+5?om9B8a++KI2T00a^H8@#9{VVEWikf23z|+H|NBftl zcs-yKP&93dB=|X5uSuqyJ3mn0^>T(@Eg@9!3x{w0W(n8I#p9?a*CernjCMDnIf_W% z%l0;Gh(u06eU_)?NYVCX`BEF&y7S3$m4{UA+0c&~3jx?i!-JgWB}{o<>UQ?P(Z#t2 z+&(um=S#M{(L5nXKV}pfP%sL-`qsua3AWJCxF;jo613&_cAH~m0kuTXh{C|Qvr*qH<<@k% zF-D>bAy32SVWI0z7p;Ql_jQ{2j|KOK5Z`{!TZyi3_AshR8;gbes00GvH84X@1 z3=6d1rf`BP?qBQ@dnIJFc*8AiPiPgJm019fIS=r(bD98_Z=SFb4Ug?(Ba9tF$08)( zCj^=BC05jhggjg=ViAi}(JB#Gykf>`6;7OD_rvQkNg7s~B1Dr~i2KcytWU?Y~-ToL(3g-zI1{7}Q{^EsMU00_KJ2-&zR_A~TzZHdLuwKXyC z-y9B%yQ*V(qb3S8|5$ME`c8QH$y~<>I!q`_$&qlC?*j*(c^&G{i&o2jTn=s9g8Exk zv4m9RlZvt`@sqW^KA#s$OAN!?w{03*M=kp| zNzvJnNo1LTiIPbcrQwAYcQVKtu29QXU1*_&S30{C9%1qts3*dWO}$hY2|1F3fwcOe z3Y5^yKt~*FT7;4fIAG%lbC*LF;mo#`Ao5Mth{Hy=@-M8p@1H&?HL|cBMhx!RsVu2J zF5#e}DvtFad*mAd-j~3&oXw#Q8dpLcv+fs7m9JcpY-?0R@Iy#nzg^}kO6|&_ROedG zIxLZPzk<|Nv0mZwjx^WXqY8j|+f@7Lr|eV(RNAb{)Bk_%mx$9iv8;q2gdjm!{m4Nt zaa(Sja!Xbcz>pl>Uq73@+C-X&)cZ~pw+acLhyLlLB@ui-in)iRy_`?-EuC#wT600J zSK%&3GU|rgMg9OC%(Ld=m^374(^_C@m=881OVI*DYI97)9Fr|RPw(bI&Zis0-1dz zFMv(U-c^Y-7k!$@6@ssnCInyZ`Nz4SixaA=q`&|7{{BDzA9iot(EpF$)*j2!?l^k; zlaoIdfLE$#zlc4x2UB653-p^}d=`vP}aeKt^*@ZK|RSmVRG~;fMYORAw`!`gwd(Q8V z{P(AJZSlejJp(QSwdi~f&7Ix;&H1Z^xIkKm&~7_ig0?YKy8q%M_g-tQzp}E(WTY-S z_)&)}*1MRbENe&^_1ol(diJRG`Zkcfj;r96X9AvD>iadJeSbd1DPV=1e{(*4MB3st zmMF|kFlfAX)}=h`VGqvLC-kK=0gLgGDwy>_r+@MjVQktJeLoRMYJT0lmUoNfe{DRm zHs9^)-n)?cnhXD~xkihE-VGP6z6k-N4-596qJA#Q0(dchwU;c^e?9 zvC~6j8s9lp_Fv1dkmN(uq-%Tg7^aifD;E+R^&f!T{eb zw~Kwvgj0m(ZnkVKu)+EX&zK7B{4)z2kwz+`QJ1z-`(MLV^g^y)dp^97KQG+(YDvri z3s5^$s=`|M449vtR6aD10PX|bN6ukMZpQAPYSZa>G&~sb*!+lD6%`wy532mahHtxJ z389R`*O#Gn^J@BX?b<$OP~r;U1g9G-jE}oQ&A=5+&QF8kc-Qo-x^u{}W(9NuF>&rM zDwb6|Ip=5QqU!CR1oyRwV~+(S4sYA8Gk!)=&{BumOKb6dNFT#eKb+0Z-XqqQ20`r_`0V%q)pIgzl2s6;qGZ?zcJ^#vXM`=wzj`qTmSmS&c>t7osD&i zD~2jRckcdDbwLb)+biUDnP)r|6E#cz1MQ zMc=>8OTfG1A193hdgu;t28u04MQy&*0c z9M?|XOB8nx#vVp^3AJwS?mpkx(an1Tpvo=D_^e3iptk$X;efS;{X(86`ziELGc}V? z4TfuqJ8M^hC$rWhS*LBd8#);t^xiQ}fZ=iN!|20Jxr5G1gXlzzfK=L$aIj667L-$~ z-UP;>BWBTAutI~$-_jD8WSFPN?X~B7kGFO< z|D(0N&8=rI*0&zL8?ZYYUvWY6W4|nS&OkHC(wY$*bJ|#y|JIW3y6AV=kMKK+2q*V@ z^VZl(YH*s^Nk_|G1{Z@qCBc%*lqkOK0Il!3+ZhtocD+%M*UH8YYJdO(&iS5es_|kI zUCgLtDV=0gCWVRzhGgof4=5ob;Syl-TdTtSj5|5_P@Evw9F!<$K%*H{0SAeu;}jgO zLZQIefFq3K<%HpAt%mjzdXNg!g9761Mw(pUzoOAPwUm~6g=4GL7hCAVx&=7RPEP~- ze?3BzZKS`zuX6sVcKTDHTmL27TYV8&ynfW5ONUUOikMu^M*_F=em1*J0}2aM(--D3 zrUVIzV?MigKX^D)WYTXxV1V?yL>%V~(wCOCu1KV4pg>SEMonqOcVeXzbZ+O{pUo&q zq|!?8nhtOGT0>gBGOswqfCIgu%`4{!onOW*hDk;5Hxy$C@n7trsPCTZ!e1jiWe!PW z zWZ>g|cVJNFAi}s)x5oB$S|rWw+n?F!yK7TTa?S0f?>udlO?1_qyL|ZB)}7AEMCOE} z30&Cgz|7TU!c%$MytoXS=)h|V;j2ZOhzN}4?Lx;&Udfuq`nC4cqmf^ zJ5e=f-Uo%RoT5$67C!Pi{aY%5<(KQ_V$A#AbisNHd$>HS?1VmIL?cL&a@o_1rnZKQ z>5H<foU5B>6uLKt_YEMnT z1&FT|JwhiGP_nI#hB*aMcU|0%N-t#;;S=IFvy!Y-8uc6(^eL2*Dc}XjW2zZGf2DzBx?Y%Y&Mn%=mzg=1T*45?caV$LrWR2Wy)L| z-emr_;cNW+r5~4s zakZ0UGfIJ=v|om;Tqly+TOrP)3+9#>1~)>PC7hGBG{oLrB$Bb759V>$y{}V zI3sOgbTAw;obe|mMn9ZI=&<4bWuq1vG6`dD8|5Mb=Phe4#&>Np_r?g5oQFg$mU?QC zXxTVm_UDe^_tG2T#f~#2uXGnl`7sdktv1!ypq3=8`w`0NHW6?{@RTMm94pyMc-@NQ zr2M1tWStk`w1%QIlY6VHI<;ZH=x0^GQO7faI92R$tPf#S%rz5(5*7OabM_q}&7d+L zIuc1BCH4}+s3@=OP`vkwomwy_$G&4-W-ccIM~q<>#A!TEB`kDc;jTT2pmXH)lj@n=*t&n1d@t~CN96Akko3x{Fwd2vK#11yzbShx-5uxiCko67lg`Ww*%+G$RCX&CF?I`1GuCS;z`4uCI+o7SUEctDfqELw&MIH#Q-dV zdCUeRcz%Hj?{!F9s_2ojgV68=!&LCfa4y}dl;j#aQc_eairP+AtAXQ*t$ApyT%+YVROzRbQIsz|(Vpcl#4tmq` zUWiu51R=CyTM+`irz-d-cmiOKB7Y}=${Q-GkdY?F9UWj&@j3LUP`w8r_P4io_li}j zh(kUyIG_jKDM?Gwd`C2Aa;Dsuy1u6<7%<~m;(wuJp@%wc=55tsG|+@~N9sA)4B26i zFl#uuE@CB?fK4%u*WOK3gi})}N%9Lj2++FYj*j5^uUZ7_R&rU0wmEF&$?|s9PE`$i zn=s`(2r2l@xgFs_o4ayWye8tkJ6!d!3|>GPHm=EbZ~WA5NV)8Xq-{o5vSpwvk8Bm^N<4Z7-;La;s?73;e@Upb07>k@WyZdrH-K5La$&<-&R zPlSx5)Icp1Uo^}l&!jSZAWn#)_d-gW7aX$C6Jq|zs)9zps#h=NvFut<9u43dX_RGz zOX~qZ!+r@6S*8`@8!iS?45>l!!;i#48voKB-9GPo*W+J}?zQA>Y`M)hyI>qnL91ldM^bC1H8R=KrR zE?{(~>#(4tgnx!7m7Llcs@^rW$B}xZfuXd?7vYPxOLEIn(4-Q`dh}5fxMH0uBL}qu zPJ|@aPJwR)ilp$VD-+0x{)s8i9Xr7f^FiMv3a9iokwqOIeHz|EXcnVmGi(fAE8vWb z9g(><3{M)?dPyqu6im%XE$9GR3JR0zA1$u`ROz$!abS&+fv+_kID>^w;|EJxy2!EV zWvRwPYv8M=xLV0GarKF>*4^756LzVLo%LT6k^3&rSugh#Mu#$+1Db0K%9^SgqZlcR zD()!Gk~mv%PjW*c4-=5E1d+YhOLuV=ZQa_~JpnE- zrTRom9)+?I(i9XT_2lA8j8v#$+LM&sz@qEmV(pb0DT*&cKosva4h_oa-%8^+$M&Rh zBCi#ld~{k0Rbt@gwoDuCM`n@>>hA5^RV^h#kN-Bfr_2>4#?$L=4)I2*D*oags0erw zPgE7#QcK-v$gQ`JAykecW)pXIP4)rnHgCqdC8BXXBlrboY&9qUNK>xP8ASdy*Se5B zeGJh|CM5L;vs`3!rc+f{k&}Y>usf;2VOauWPR@aRu2fd03q(_oOy0Oze=Dld`s9QCFyg=MAPwUQ!B$MWU5??cJRvO@ zux2>Si+=6FlXbj==m2Z1E5`9HD%h0NA=C5BQzlbTiQRsr#Pwp|s2Qv5uFvBVtBkbZ z29vC4G~27Y`O_`6=0sS}q`krldsIK-iGm`NTLqqH-dVb5nvtOyh#>Dl+ON`w9N-9D zI0Kr_Ot$i0CMg}+4w`<0@{~TDv}Vvx_}+A$v_zZg$_mXIoZxu)0;J9AanPs}&U75{ z^<}37-Y38D+wKEAl9dx-P>b`t3P=h7

    Dm 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