From 31af7fab389e89d218fbca4d2e878987c50e0974 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Tue, 3 Oct 2023 13:26:53 +0530 Subject: [PATCH 1/9] Make Freekassa the default payment in iran --- lib/plans/checkout.dart | 278 +++++++++++++++++++++------------------- pubspec.lock | 46 +++---- 2 files changed, 165 insertions(+), 159 deletions(-) diff --git a/lib/plans/checkout.dart b/lib/plans/checkout.dart index 30b581df2..562a6f3c0 100644 --- a/lib/plans/checkout.dart +++ b/lib/plans/checkout.dart @@ -154,7 +154,7 @@ class _CheckoutState extends State case 'freekassa': widgets.add( PaymentProvider( - logoPaths: [ + logoPaths: const [ ImagePaths.mnp, ImagePaths.qiwi, ImagePaths.visa, @@ -238,150 +238,164 @@ class _CheckoutState extends State @override Widget build(BuildContext context) { return BaseScreen( - resizeToAvoidBottomInset: false, - title: 'lantern_pro_checkout'.i18n, - body: sessionModel.paymentMethods( - builder: ( - context, - Iterable> paymentMethods, - Widget? child, - ) { - return sessionModel.emailAddress(( - BuildContext context, - String emailAddress, - Widget? child, - ) { - return Container( - padding: const EdgeInsetsDirectional.only( - start: 16, - end: 16, - top: 24, - bottom: 32, - ), - child: Column( - children: [ - // * Step 2 - PlanStep( - stepNum: '2', - description: 'enter_email'.i18n, + resizeToAvoidBottomInset: false, + title: 'lantern_pro_checkout'.i18n, + body: sessionModel.countryCode((context, countryCode, child) { + changePaymentCountryWise(countryCode); + return sessionModel.paymentMethods( + builder: ( + context, + Iterable> paymentMethods, + Widget? child, + ) { + return sessionModel.emailAddress(( + BuildContext context, + String emailAddress, + Widget? child, + ) { + return Container( + padding: const EdgeInsetsDirectional.only( + start: 16, + end: 16, + top: 24, + bottom: 32, ), - // * Email field - Container( - padding: const EdgeInsetsDirectional.only( - top: 8, - bottom: 8, - ), - child: Form( - key: emailFieldKey, - child: CTextField( - initialValue: widget.isPro ? emailAddress : '', - controller: emailController, - autovalidateMode: widget.isPro - ? AutovalidateMode.always - : AutovalidateMode.disabled, - label: 'email'.i18n, - keyboardType: TextInputType.emailAddress, - prefixIcon: const CAssetImage(path: ImagePaths.email), + child: Column( + children: [ + // * Step 2 + PlanStep( + stepNum: '2', + description: 'enter_email'.i18n, ), - ), - ), - // * Referral Code field - initially hidden - Visibility( - visible: isRefCodeFieldShowing, - child: Container( - padding: const EdgeInsetsDirectional.only( - top: 8, - bottom: 16, + // * Email field + Container( + padding: const EdgeInsetsDirectional.only( + top: 8, + bottom: 8, + ), + child: Form( + key: emailFieldKey, + child: CTextField( + initialValue: widget.isPro ? emailAddress : '', + controller: emailController, + autovalidateMode: widget.isPro + ? AutovalidateMode.always + : AutovalidateMode.disabled, + label: 'email'.i18n, + keyboardType: TextInputType.emailAddress, + prefixIcon: + const CAssetImage(path: ImagePaths.email), + ), + ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Flexible( - flex: 2, - child: Form( - key: refCodeFieldKey, - child: CTextField( - controller: refCodeController, - autovalidateMode: AutovalidateMode.disabled, - textCapitalization: - TextCapitalization.characters, - label: 'referral_code'.i18n, - keyboardType: TextInputType.text, - prefixIcon: - const CAssetImage(path: ImagePaths.star), + // * Referral Code field - initially hidden + Visibility( + visible: isRefCodeFieldShowing, + child: Container( + padding: const EdgeInsetsDirectional.only( + top: 8, + bottom: 16, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible( + flex: 2, + child: Form( + key: refCodeFieldKey, + child: CTextField( + controller: refCodeController, + autovalidateMode: AutovalidateMode.disabled, + textCapitalization: + TextCapitalization.characters, + label: 'referral_code'.i18n, + keyboardType: TextInputType.text, + prefixIcon: const CAssetImage( + path: ImagePaths.star), + ), + ), ), + ], + ), + ), + ), + // * Add Referral code + Visibility( + visible: !isRefCodeFieldShowing, + child: GestureDetector( + onTap: () async => + setState(() => isRefCodeFieldShowing = true), + child: Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsetsDirectional.only( + top: 8, + bottom: 16, + ), + child: Row( + children: [ + const CAssetImage(path: ImagePaths.add), + Padding( + padding: const EdgeInsetsDirectional.only( + start: 8.0, + ), + child: CText( + 'add_referral_code'.i18n, + style: tsBody1, + ), + ), + ], ), ), - ], + ), ), - ), - ), - // * Add Referral code - Visibility( - visible: !isRefCodeFieldShowing, - child: GestureDetector( - onTap: () async => - setState(() => isRefCodeFieldShowing = true), - child: Container( - width: MediaQuery.of(context).size.width, + // * Step 3 + PlanStep( + stepNum: '3', + description: 'choose_payment_method'.i18n, + ), + //* Payment options + Container( padding: const EdgeInsetsDirectional.only( - top: 8, - bottom: 16, - ), - child: Row( - children: [ - const CAssetImage(path: ImagePaths.add), - Padding( - padding: const EdgeInsetsDirectional.only( - start: 8.0, - ), - child: CText( - 'add_referral_code'.i18n, - style: tsBody1, - ), - ), - ], + top: 16, bottom: 16), + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: paymentOptions(paymentMethods), ), ), - ), - ), - // * Step 3 - PlanStep( - stepNum: '3', - description: 'choose_payment_method'.i18n, - ), - //* Payment options - Container( - padding: - const EdgeInsetsDirectional.only(top: 16, bottom: 16), - width: MediaQuery.of(context).size.width, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: paymentOptions(paymentMethods), - ), - ), - // * Price summary, unused pro time disclaimer, Continue button + // * Price summary, unused pro time disclaimer, Continue button - Center( - child: Tooltip( - message: AppKeys.continueCheckout, - child: Button( - text: 'continue'.i18n, - disabled: emailController.value.text.isEmpty || - emailFieldKey.currentState?.validate() == false || - refCodeFieldKey.currentState?.validate() == false, - onPressed: onContinueTapped, + Center( + child: Tooltip( + message: AppKeys.continueCheckout, + child: Button( + text: 'continue'.i18n, + disabled: emailController.value.text.isEmpty || + emailFieldKey.currentState?.validate() == + false || + refCodeFieldKey.currentState?.validate() == + false, + onPressed: onContinueTapped, + ), + ), ), - ), + ], ), - ], - ), - ); - }); - }, - ), - ); + ); + }); + }, + ); + })); + } + + void changePaymentCountryWise(String countryCode){ + if(countryCode == ""){ + return; + } + if(countryCode.toLowerCase()=='ir'){ + selectedPaymentProvider = Providers.freekassa; + } } void onContinueTapped() { diff --git a/pubspec.lock b/pubspec.lock index 3cf74f15a..fb56a78dc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -261,10 +261,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.17.1" convert: dependency: transitive description: @@ -784,10 +784,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.18.0" io: dependency: transitive description: @@ -856,18 +856,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -1325,10 +1325,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" sprintf: dependency: transitive description: @@ -1429,26 +1429,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.1" timezone: dependency: transitive description: @@ -1637,10 +1637,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.3.0" wakelock: dependency: "direct main" description: @@ -1689,14 +1689,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1802,5 +1794,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.0.3 <4.0.0" flutter: ">=3.10.0" From 7421c0e7621864d96db60d238f3a79f1d87c3a4f Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 4 Oct 2023 11:27:52 +0530 Subject: [PATCH 2/9] Replace icons at checkout screen. --- assets/images/bitcoin.svg | 3 +++ assets/images/webmoney.svg | 3 +++ lib/common/ui/image_paths.dart | 2 ++ lib/plans/checkout.dart | 4 ++-- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 assets/images/bitcoin.svg create mode 100644 assets/images/webmoney.svg diff --git a/assets/images/bitcoin.svg b/assets/images/bitcoin.svg new file mode 100644 index 000000000..2331b627e --- /dev/null +++ b/assets/images/bitcoin.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/webmoney.svg b/assets/images/webmoney.svg new file mode 100644 index 000000000..8fc9e9774 --- /dev/null +++ b/assets/images/webmoney.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/common/ui/image_paths.dart b/lib/common/ui/image_paths.dart index 4adc97d73..df539d739 100644 --- a/lib/common/ui/image_paths.dart +++ b/lib/common/ui/image_paths.dart @@ -35,6 +35,8 @@ class ImagePaths { static const lock = 'assets/images/lock.svg'; static const split_tunneling = 'assets/images/split_tunneling.svg'; static const forum = 'assets/images/forum.svg'; + static const webMoney = 'assets/images/webmoney.svg'; + static const bitcoin = 'assets/images/bitcoin.svg'; // Messaging static const more_vert = 'assets/images/more_vert.svg'; diff --git a/lib/plans/checkout.dart b/lib/plans/checkout.dart index 562a6f3c0..750c6bf7d 100644 --- a/lib/plans/checkout.dart +++ b/lib/plans/checkout.dart @@ -157,8 +157,8 @@ class _CheckoutState extends State logoPaths: const [ ImagePaths.mnp, ImagePaths.qiwi, - ImagePaths.visa, - ImagePaths.mastercard + ImagePaths.webMoney, + ImagePaths.bitcoin ], onChanged: () => selectPaymentProvider(Providers.freekassa), selectedPaymentProvider: selectedPaymentProvider, From a6ac3a510755cd35609ba61a897f6e0e97d8f708 Mon Sep 17 00:00:00 2001 From: Ox Cart Date: Wed, 4 Oct 2023 07:11:27 -0500 Subject: [PATCH 3/9] Check ads availability everytime MainActivity resumes --- .../app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt b/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt index b241614ac..c61b116b1 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt @@ -183,6 +183,8 @@ class MainActivity : Logger.d(TAG, "LanternVpnService is running, updating VPN preference") vpnModel.setVpnOn(true) } + + sessionModel.checkAdsAvailability() Logger.debug(TAG, "onResume() finished at ${System.currentTimeMillis() - start}") } From 32bc1346c60e78fe57662f3037cf67c5c9398266 Mon Sep 17 00:00:00 2001 From: Ox Cart Date: Wed, 4 Oct 2023 07:14:21 -0500 Subject: [PATCH 4/9] Upgraded to flashlight 7.6.14 --- android/app/libs/liblantern-all.aar | 3 +++ go.mod | 4 ++-- go.sum | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 android/app/libs/liblantern-all.aar diff --git a/android/app/libs/liblantern-all.aar b/android/app/libs/liblantern-all.aar new file mode 100644 index 000000000..ad352be94 --- /dev/null +++ b/android/app/libs/liblantern-all.aar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c50fcd8ea9c441c0a81ee74117613ad6cbe32d90a1ed6dfba06e6207b8b71fe2 +size 78316381 diff --git a/go.mod b/go.mod index 907d6068a..2c019145e 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/getlantern/dnsgrab v0.0.0-20230822102054-7ff232ec3148 github.com/getlantern/errors v1.0.3 github.com/getlantern/eventual/v2 v2.0.2 - github.com/getlantern/flashlight/v7 v7.6.9 + github.com/getlantern/flashlight/v7 v7.6.14 github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 github.com/getlantern/idletiming v0.0.0-20201229174729-33d04d220c4e github.com/getlantern/ipproxy v0.0.0-20230511223023-ee52513fd782 @@ -108,7 +108,7 @@ require ( github.com/felixge/httpsnoop v1.0.3 // indirect github.com/gaukas/godicttls v0.0.3 // indirect github.com/getlantern/borda v0.0.0-20230421223744-4e208135f082 // indirect - github.com/getlantern/broflake v0.0.0-20230926124502-c8269f54a586 // indirect + github.com/getlantern/broflake v0.0.0-20230928170657-97fc977bdc6f // indirect github.com/getlantern/bufconn v0.0.0-20210901195825-fd7c0267b493 // indirect github.com/getlantern/byteexec v0.0.0-20220903142956-e6ed20032cfd // indirect github.com/getlantern/cmux v0.0.0-20230301223233-dac79088a4c0 // indirect diff --git a/go.sum b/go.sum index 8983fef8c..cc6276cb0 100644 --- a/go.sum +++ b/go.sum @@ -312,8 +312,8 @@ github.com/getlantern/autoupdate v0.0.0-20211217175350-d0b211f39ba7 h1:/efTOJpxX github.com/getlantern/autoupdate v0.0.0-20211217175350-d0b211f39ba7/go.mod h1:+X8pAviVhThDBjPEqLUB0iO7EPxhpWk7Q9VYxvz6rCY= github.com/getlantern/borda v0.0.0-20230421223744-4e208135f082 h1:Ka9rIAgef8zYhBr/VgLrt5+Qs7zE33g0OButzpIGcSs= github.com/getlantern/borda v0.0.0-20230421223744-4e208135f082/go.mod h1:oCpQojhSaK0F/6rWMrDvN8/QFHQhTC9Gb3uf7GcqPQQ= -github.com/getlantern/broflake v0.0.0-20230926124502-c8269f54a586 h1:d8z7UmXW6aKEqhM4lwmYE5IBHjNoii6xP4B5ikWEq+s= -github.com/getlantern/broflake v0.0.0-20230926124502-c8269f54a586/go.mod h1:Ehdl8IASN5rJi9brldVuCjTDcSU25nvaGRlzNprgeQo= +github.com/getlantern/broflake v0.0.0-20230928170657-97fc977bdc6f h1:x7vxkMJlrse2xlfw8AeGRz2YTsPzERXImMvW712vi2w= +github.com/getlantern/broflake v0.0.0-20230928170657-97fc977bdc6f/go.mod h1:Ehdl8IASN5rJi9brldVuCjTDcSU25nvaGRlzNprgeQo= github.com/getlantern/bufconn v0.0.0-20190625204133-a08544339f8d/go.mod h1:d6O4RY+V87kIt4o9wru4SaNo7C2NAkD3YnmJFXEpODo= github.com/getlantern/bufconn v0.0.0-20210901195825-fd7c0267b493 h1:8WjDNmpDLFVsAfcnHxqF4pfVKkdAQxyJ9iCHB4LxSfc= github.com/getlantern/bufconn v0.0.0-20210901195825-fd7c0267b493/go.mod h1:d6O4RY+V87kIt4o9wru4SaNo7C2NAkD3YnmJFXEpODo= @@ -373,8 +373,8 @@ github.com/getlantern/fdcount v0.0.0-20210503151800-5decd65b3731/go.mod h1:XZwE+ github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM= github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= -github.com/getlantern/flashlight/v7 v7.6.9 h1:GqGY05jwvCy7sVQ7vOjLN51tT90yC0ZYMIp5MNQ0NSY= -github.com/getlantern/flashlight/v7 v7.6.9/go.mod h1:E81GrKxK8GU5mRzskGF3+4wZKMrEobE+Ff13S8ryPvs= +github.com/getlantern/flashlight/v7 v7.6.14 h1:Ic/B+JUWufpvtf1LSwGntQR9U/SHTg8U/Ypje0ZSw1M= +github.com/getlantern/flashlight/v7 v7.6.14/go.mod h1:tMY6W9caWMCsFSqKAr0VPoUtmxIFM5um1yidEiNnN9M= github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede h1:yrU6Px3ZkvCsDLPryPGi6FN+2iqFPq+JeCb7EFoDBhw= github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede/go.mod h1:nhnoiS6DE6zfe+BaCMU4YI01UpsuiXnDqM5S8jxHuuI= github.com/getlantern/fronted v0.0.0-20230601004823-7fec719639d8 h1:r/Z/SPPIfLXDI3QA7/tE6nOfPncrqeUPDjiFjnNGP50= From ccf9c77e155e2a6736edd1a2cb1dc7e5c467dc4d Mon Sep 17 00:00:00 2001 From: Ox Cart Date: Wed, 4 Oct 2023 08:03:03 -0500 Subject: [PATCH 5/9] Only default payment provider if not already selected --- lib/plans/checkout.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/plans/checkout.dart b/lib/plans/checkout.dart index 750c6bf7d..a62109a64 100644 --- a/lib/plans/checkout.dart +++ b/lib/plans/checkout.dart @@ -46,7 +46,7 @@ class _CheckoutState extends State ); var isRefCodeFieldShowing = false; - var selectedPaymentProvider = Providers.stripe; + Providers? selectedPaymentProvider; var loadingPercentage = 0; late AnimationController animationController; late Animation pulseAnimation; @@ -146,7 +146,7 @@ class _CheckoutState extends State ImagePaths.unionpay ], onChanged: () => selectPaymentProvider(Providers.stripe), - selectedPaymentProvider: selectedPaymentProvider, + selectedPaymentProvider: selectedPaymentProvider!, paymentType: Providers.stripe, ), ); @@ -161,7 +161,7 @@ class _CheckoutState extends State ImagePaths.bitcoin ], onChanged: () => selectPaymentProvider(Providers.freekassa), - selectedPaymentProvider: selectedPaymentProvider, + selectedPaymentProvider: selectedPaymentProvider!, paymentType: Providers.freekassa, ), ); @@ -171,7 +171,7 @@ class _CheckoutState extends State PaymentProvider( logoPaths: [ImagePaths.btc], onChanged: () => selectPaymentProvider(Providers.btcpay), - selectedPaymentProvider: selectedPaymentProvider, + selectedPaymentProvider: selectedPaymentProvider!, paymentType: Providers.btcpay, ), ); @@ -183,7 +183,7 @@ class _CheckoutState extends State } Future resolvePaymentRoute() async { - switch (selectedPaymentProvider) { + switch (selectedPaymentProvider!) { case Providers.stripe: // * Stripe selected await context.pushRoute( @@ -241,7 +241,7 @@ class _CheckoutState extends State resizeToAvoidBottomInset: false, title: 'lantern_pro_checkout'.i18n, body: sessionModel.countryCode((context, countryCode, child) { - changePaymentCountryWise(countryCode); + defaultProviderIfNecessary(countryCode); return sessionModel.paymentMethods( builder: ( context, @@ -389,12 +389,14 @@ class _CheckoutState extends State })); } - void changePaymentCountryWise(String countryCode){ - if(countryCode == ""){ + void defaultProviderIfNecessary(String countryCode) { + if (selectedPaymentProvider != null) { return; } - if(countryCode.toLowerCase()=='ir'){ + if (countryCode.toLowerCase() == 'ir') { selectedPaymentProvider = Providers.freekassa; + } else { + selectedPaymentProvider = Providers.stripe; } } From 3fd71836c551f20fb98d263ce6c83b929a4d8382 Mon Sep 17 00:00:00 2001 From: atavism Date: Mon, 9 Oct 2023 11:55:01 -0700 Subject: [PATCH 6/9] Disable notification sounds (#922) * Disable notification sound * disable vibration * Update channel name --- .../org/getlantern/lantern/notification/NotificationHelper.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/notification/NotificationHelper.kt b/android/app/src/main/kotlin/org/getlantern/lantern/notification/NotificationHelper.kt index 6177a75d5..8367b89da 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/notification/NotificationHelper.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/notification/NotificationHelper.kt @@ -54,6 +54,7 @@ class NotificationHelper( notificationChannel.enableLights(true) notificationChannel.lightColor = Color.GREEN notificationChannel.enableVibration(false) + notificationChannel.setSound(null, null) manager.createNotificationChannel(notificationChannel) } @@ -92,7 +93,7 @@ class NotificationHelper( private const val LANTERN_NOTIFICATION = "lantern.notification" private const val DATA_USAGE = 36 const val VPN_CONNECTED = 37 - private const val CHANNEL_VPN = "vpn" + private const val CHANNEL_VPN = "lantern_vpn" private const val CHANNEL_DATA_USAGE = "data_usage" private const val VPN_DESC = "VPN" private const val DATA_USAGE_DESC = "Data Usage" From 5ff6078f4d36d76d9940857e9529dd4c96f6ca7f Mon Sep 17 00:00:00 2001 From: atavism Date: Tue, 10 Oct 2023 07:10:33 -0700 Subject: [PATCH 7/9] Remove use of Datadog RUM (#925) * Remove datadog * Update CI * update Makefile * update Makefile * remove datadog directory * Use Sentry for crash reporting (#927) * update Makefile * Add back Sentry for crash reporting * remove datadog from gradle config --- .github/workflows/browerstack.yml | 8 + .github/workflows/release.yml | 17 +- Makefile | 16 +- android/app/build.gradle | 49 +++--- android/app/libs/liblantern-all.aar | 4 +- .../mobilesdk/model/SessionManager.kt | 2 - .../kotlin/io/lantern/model/SessionModel.kt | 5 - .../org/getlantern/lantern/LanternApp.kt | 6 +- .../org/getlantern/lantern/MainActivity.kt | 9 +- .../org/getlantern/lantern/datadog/Datadog.kt | 151 ------------------ .../FlutterExcludingComponentPredicate.kt | 22 --- .../lantern/service/LanternService.kt | 3 - .../getlantern/lantern/util/PaymentsUtil.kt | 27 +--- .../org/getlantern/lantern/util/SentryUtil.kt | 56 +++++++ .../lantern/vpn/LanternVpnService.kt | 3 - lib/ad_helper.dart | 19 --- lib/catcher_setup.dart | 44 +++++ lib/common/common.dart | 3 - lib/common/datadog.dart | 29 ---- lib/home.dart | 6 - lib/main.dart | 3 +- pubspec.lock | 95 +++++++---- pubspec.yaml | 8 +- 23 files changed, 230 insertions(+), 355 deletions(-) delete mode 100644 android/app/src/main/kotlin/org/getlantern/lantern/datadog/Datadog.kt delete mode 100644 android/app/src/main/kotlin/org/getlantern/lantern/datadog/FlutterExcludingComponentPredicate.kt create mode 100644 android/app/src/main/kotlin/org/getlantern/lantern/util/SentryUtil.kt create mode 100644 lib/catcher_setup.dart delete mode 100644 lib/common/datadog.dart diff --git a/.github/workflows/browerstack.yml b/.github/workflows/browerstack.yml index cd85f3afa..a34ddb1f1 100644 --- a/.github/workflows/browerstack.yml +++ b/.github/workflows/browerstack.yml @@ -36,6 +36,14 @@ jobs: run: | git config --global url."https://${{ secrets.GH_TOKEN }}:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Setup Sentry CLI + uses: mathieu-bour/setup-sentry-cli@v1 + with: + version: latest + token: ${{ SECRETS.SENTRY_TOKEN }} # from GitHub secrets + organization: getlantern + project: android + - name: Setup JDK 17 uses: actions/setup-java@v3 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1e0f394c..dbc54fd5e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,8 +8,6 @@ permissions: env: GOPRIVATE: github.com/getlantern S3_BUCKET: lantern - DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} - DATADOG_SITE: datadoghq.eu jobs: set-version: runs-on: @@ -84,6 +82,14 @@ jobs: run: | git config --global url."https://${{ secrets.GH_TOKEN }}:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Setup Sentry CLI + uses: mathieu-bour/setup-sentry-cli@v1 + with: + version: latest + token: ${{ SECRETS.SENTRY_TOKEN }} # from GitHub secrets + organization: getlantern + project: android + - name: Setup JDK 11 uses: actions/setup-java@v3 with: @@ -118,16 +124,9 @@ jobs: fileDir: './android/app' encodedString: ${{ secrets.KEYSTORE }} - - name: Install Datadog CI - run: npm install -g @datadog/datadog-ci - - name: Build Android installers run: make package-android env: - DD_APPLICATION_ID: ${{ secrets.DD_APPLICATION_ID }} - DD_CLIENT_TOKEN: ${{ secrets.DD_CLIENT_TOKEN }} - DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} - DATADOG_SITE: datadoghq.eu INTERSTITIAL_AD_UNIT: "${{ secrets.INTERSTITIAL_AD_UNIT_ID }}" VERSION: "${{ env.version }}" diff --git a/Makefile b/Makefile index 3ca47d731..f5d3497a6 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,6 @@ ADB := $(call get-command,adb) OPENSSL := $(call get-command,openssl) GMSAAS := $(call get-command,gmsaas) SENTRY := $(call get-command,sentry-cli) -DATADOGCI := $(call get-command,datadog-ci) BASE64 := $(call get-command,base64) GIT_REVISION_SHORTCODE := $(shell git rev-parse --short HEAD) @@ -90,7 +89,6 @@ PROD_BASE_NAME ?= $(INSTALLER_NAME) ## secrets Keys INTERSTITIAL_AD_UNIT=ca-app-pub-2685698271254859/9922829329 ## vault secrets -VAULT_DD_SECRETS_PATH ?= secret/apps/datadog/android VAULT_ADS_SECRETS_PATH ?= secret/googleAds ## vault keys @@ -245,10 +243,6 @@ require-magick: require-sentry: @if [[ -z "$(SENTRY)" ]]; then echo 'Missing "sentry-cli" command. See sentry.io for installation instructions.'; exit 1; fi -.PHONY: require-datadog-ci -require-datadog-ci: - @if [[ -z "$(DATADOGCI)" ]]; then echo 'Missing "datadog-ci" command. See https://www.npmjs.com/package/@datadog/datadog-ci for installation instructions.'; exit 1; fi - release-autoupdate: require-version @TAG_COMMIT=$$(git rev-list --abbrev-commit -1 $(TAG)) && \ if [[ -z "$$TAG_COMMIT" ]]; then \ @@ -299,7 +293,7 @@ do-android-debug: $(MOBILE_SOURCES) $(MOBILE_ANDROID_LIB) echo "Value of DART_DEFINES is: $$DART_DEFINES" && \ CI="$$CI" && \ echo "Value of CI is: $$CI" && \ - $(GRADLE) -Pdart-defines="$$DART_DEFINES" -PlanternVersion=$(DEBUG_VERSION) -PddClientToken=$$DD_CLIENT_TOKEN -PddApplicationID=$$DD_APPLICATION_ID \ + $(GRADLE) -Pdart-defines="$$DART_DEFINES" -PlanternVersion=$(DEBUG_VERSION) \ -PproServerUrl=$(PRO_SERVER_URL) -PpaymentProvider=$(PAYMENT_PROVIDER) -Pcountry=$(COUNTRY) \ -PplayVersion=$(FORCE_PLAY_VERSION) -PuseStaging=$(STAGING) -PstickyConfig=$(STICKY_CONFIG) \ -PlanternRevisionDate=$(REVISION_DATE) -PandroidArch=$(ANDROID_ARCH) \ @@ -314,7 +308,7 @@ $(MOBILE_DEBUG_APK): $(MOBILE_SOURCES) $(GO_SOURCES) make do-android-debug && \ cp $(MOBILE_ANDROID_DEBUG) $(MOBILE_DEBUG_APK) -$(MOBILE_RELEASE_APK): $(MOBILE_SOURCES) $(GO_SOURCES) $(MOBILE_ANDROID_LIB) require-datadog-ci +$(MOBILE_RELEASE_APK): $(MOBILE_SOURCES) $(GO_SOURCES) $(MOBILE_ANDROID_LIB) require-sentry echo $(MOBILE_ANDROID_LIB) && \ mkdir -p ~/.gradle && \ ln -fs $(MOBILE_DIR)/gradle.properties . && \ @@ -328,13 +322,12 @@ $(MOBILE_RELEASE_APK): $(MOBILE_SOURCES) $(GO_SOURCES) $(MOBILE_ANDROID_LIB) req -PandroidArchJava="$(ANDROID_ARCH_JAVA)" -PproServerUrl=$(PRO_SERVER_URL) -PpaymentProvider=$(PAYMENT_PROVIDER) \ -Pcountry=$(COUNTRY) -PplayVersion=$(FORCE_PLAY_VERSION) -PuseStaging=$(STAGING) -PstickyConfig=$(STICKY_CONFIG) \ -PversionCode=$(VERSION_CODE) -PdevelopmentMode=$(DEVELOPMENT_MODE) -b $(MOBILE_DIR)/app/build.gradle assembleProdSideload && \ - DATADOG_API_KEY=4901456bb88bbf1dc7799eab7d4f71ae DATADOG_SITE=datadoghq.eu datadog-ci flutter-symbols upload --service-name lantern-android --dart-symbols-location build/app/intermediates/merged_native_libs/prodSideload/out/lib \ - --android-mapping-location build/app/outputs/mapping/prodSideload/mapping.txt --android-mapping --ios-dsyms && \ + sentry-cli upload-dif --wait -o getlantern -p android build/app/intermediates/merged_native_libs/prodSideload/out/lib && \ cp $(MOBILE_ANDROID_RELEASE) $(MOBILE_RELEASE_APK) && \ cat $(MOBILE_RELEASE_APK) | bzip2 > lantern_update_android_arm.bz2 -$(MOBILE_BUNDLE): $(MOBILE_SOURCES) $(GO_SOURCES) $(MOBILE_ANDROID_LIB) require-datadog-ci +$(MOBILE_BUNDLE): $(MOBILE_SOURCES) $(GO_SOURCES) $(MOBILE_ANDROID_LIB) require-sentry @mkdir -p ~/.gradle && \ ln -fs $(MOBILE_DIR)/gradle.properties . && \ COUNTRY="$$COUNTRY" && \ @@ -344,6 +337,7 @@ $(MOBILE_BUNDLE): $(MOBILE_SOURCES) $(GO_SOURCES) $(MOBILE_ANDROID_LIB) require- $(GRADLE) -PlanternVersion=$$VERSION -PlanternRevisionDate=$(REVISION_DATE) -PandroidArch=$(ANDROID_ARCH) -PandroidArchJava="$(ANDROID_ARCH_JAVA)" \ -PddClientToken=$(DD_CLIENT_TOKEN) -PddApplicationID=$(DD_APPLICATION_ID) -PproServerUrl=$(PRO_SERVER_URL) -PpaymentProvider=$(PAYMENT_PROVIDER) \ -Pcountry=$(COUNTRY) -PplayVersion=true -PuseStaging=$(STAGING) -PstickyConfig=$(STICKY_CONFIG) -b $(MOBILE_DIR)/app/build.gradle bundlePlay && \ + sentry-cli upload-dif --wait -o getlantern -p android build/app/intermediates/merged_native_libs/prodPlay/out/lib && \ cp $(MOBILE_ANDROID_BUNDLE) $(MOBILE_BUNDLE) android-debug: $(MOBILE_DEBUG_APK) diff --git a/android/app/build.gradle b/android/app/build.gradle index 418f9fde8..45a8c0cd6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -6,7 +6,7 @@ plugins { id 'kotlin-parcelize' id 'kotlin-kapt' id 'com.google.protobuf' - id("com.datadoghq.dd-sdk-android-gradle-plugin") version "1.10.0" + id "io.sentry.android.gradle" version "3.13.0" } def localProperties = new Properties() @@ -180,8 +180,6 @@ android { buildConfigField "boolean", "STICKY_CONFIG", getBoolean("stickyConfig") buildConfigField "boolean", "STAGING", getBoolean("useStaging") buildConfigField "boolean", "PLAY_VERSION", getBoolean("playVersion") - buildConfigField "String", "DD_CLIENT_TOKEN", ddClientToken() - buildConfigField "String", "DD_APPLICATION_ID", ddApplicationID() buildConfigField "String", "COUNTRY", userCountry() buildConfigField "String", "PAYMENT_PROVIDER", paymentProvider() buildConfigField "String", "PRO_SERVER_URL", proServerUrl() @@ -323,22 +321,6 @@ def getInt(name) { return value.toInteger() } -def ddApplicationID() { - def value = project.getProperties().get("ddApplicationID") - if (value == null || !value?.trim()) { - return "\"\"" - } - return String.format("\"%s\"", value) -} - -def ddClientToken() { - def value = project.getProperties().get("ddClientToken") - if (value == null || !value?.trim()) { - return "\"\"" - } - return String.format("\"%s\"", value) -} - def userCountry() { def value = project.getProperties().get("country") if (value == null || !value?.trim()) { @@ -473,3 +455,32 @@ dependencies { } apply plugin: 'com.google.gms.google-services' + +sentry { + + // Disables or enables the handling of Proguard mapping for Sentry. + // If enabled the plugin will generate a UUID and will take care of + // uploading the mapping to Sentry. If disabled, all the logic + // related to proguard mapping will be excluded. + // Default is enabled. + includeProguardMapping = true + + + // Whether the plugin should attempt to auto-upload the mapping file to Sentry or not. + // If disabled the plugin will run a dry-run and just generate a UUID. + // The mapping file has to be uploaded manually via sentry-cli in this case. + // Default is enabled. + autoUploadProguardMapping = true + + // Disables or enables the automatic configuration of Native Symbols + // for Sentry. This executes sentry-cli automatically so + // you don't need to do it manually. + // Default is disabled. + uploadNativeSymbols = true + + // Does or doesn't include the source code of native code for Sentry. + // This executes sentry-cli with the --include-sources param. automatically so + // you don't need to do it manually. + // Default is disabled. + includeNativeSources = false +} diff --git a/android/app/libs/liblantern-all.aar b/android/app/libs/liblantern-all.aar index ad352be94..fd4be06b0 100644 --- a/android/app/libs/liblantern-all.aar +++ b/android/app/libs/liblantern-all.aar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c50fcd8ea9c441c0a81ee74117613ad6cbe32d90a1ed6dfba06e6207b8b71fe2 -size 78316381 +oid sha256:091cd2629b6eff2c058dcdf883fb75fa200f00a7cf768e6eae0b2d6b064f926f +size 77696936 diff --git a/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt b/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt index 58841b846..1c9292d3a 100644 --- a/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt +++ b/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt @@ -24,7 +24,6 @@ import io.lantern.model.BaseModel import io.lantern.model.Vpn import org.getlantern.lantern.BuildConfig import org.getlantern.lantern.LanternApp -import org.getlantern.lantern.datadog.Datadog import org.getlantern.lantern.model.Bandwidth import org.getlantern.lantern.model.Stats import org.getlantern.lantern.model.Utils @@ -386,7 +385,6 @@ abstract class SessionManager(application: Application) : Session { override fun setCountry(country: String) { prefs.edit().putString(GEO_COUNTRY_CODE, country).apply() - Datadog.setCountry(country) } private val hasUpdatedStats = AtomicBoolean() diff --git a/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt b/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt index 1ad9ca633..afdfa7c53 100644 --- a/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt +++ b/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt @@ -23,7 +23,6 @@ import org.getlantern.lantern.MainActivity import org.getlantern.lantern.R import org.getlantern.lantern.activity.FreeKassaActivity_ import org.getlantern.lantern.activity.WebViewActivity_ -import org.getlantern.lantern.datadog.Datadog import org.getlantern.lantern.model.LanternHttpClient import org.getlantern.lantern.model.LanternHttpClient.ProCallback import org.getlantern.lantern.model.LanternHttpClient.ProUserCallback @@ -182,10 +181,6 @@ class SessionModel( } } - "trackUserAction" -> { - Datadog.trackUserClick(call.argument("message")!!) - } - "acceptTerms" -> { LanternApp.getSession().acceptTerms() } diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/LanternApp.kt b/android/app/src/main/kotlin/org/getlantern/lantern/LanternApp.kt index 730fea353..65864e666 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/LanternApp.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/LanternApp.kt @@ -3,7 +3,6 @@ package org.getlantern.lantern import android.app.Application import android.content.Context import android.os.StrictMode -import android.util.Log import androidx.appcompat.app.AppCompatDelegate import androidx.multidex.MultiDex import org.getlantern.lantern.model.InAppBilling @@ -11,7 +10,7 @@ import org.getlantern.lantern.model.LanternHttpClient import org.getlantern.lantern.model.LanternSessionManager import org.getlantern.lantern.util.debugOnly import org.getlantern.lantern.util.LanternProxySelector -import org.getlantern.mobilesdk.Logger +import org.getlantern.lantern.util.SentryUtil import org.getlantern.mobilesdk.util.HttpClient open class LanternApp : Application() { @@ -40,13 +39,14 @@ open class LanternApp : Application() { override fun onCreate() { super.onCreate() + SentryUtil.enableGoPanicEnrichment(this) // Necessary to locate a back arrow resource we use from the // support library. See http://stackoverflow.com/questions/37615470/support-library-vectordrawable-resourcesnotfoundexception AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) appContext = applicationContext session = LanternSessionManager(this) - + LanternProxySelector(session) if (session.isPlayVersion) inAppBilling = InAppBilling(this) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt b/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt index c61b116b1..5791393db 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt @@ -25,7 +25,6 @@ import io.lantern.model.VpnModel import kotlinx.coroutines.* import okhttp3.Response import org.getlantern.lantern.activity.WebViewActivity_ -import org.getlantern.lantern.datadog.Datadog import org.getlantern.lantern.event.EventManager import org.getlantern.lantern.model.AccountInitializationStatus import org.getlantern.lantern.model.Bandwidth @@ -79,7 +78,6 @@ class MainActivity : override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { val start = System.currentTimeMillis() super.configureFlutterEngine(flutterEngine) - FlutterEngineCache.getInstance().put("datadoghq_engine", flutterEngine) messagingModel = MessagingModel(this, flutterEngine) vpnModel = VpnModel(this, flutterEngine, ::switchLantern) sessionModel = SessionModel(this, flutterEngine) @@ -89,7 +87,6 @@ class MainActivity : eventManager = object : EventManager("lantern_event_channel", flutterEngine) { override fun onListen(event: Event) { if (LanternApp.getSession().lanternDidStart()) { - flutterNavigation.invokeMethod("initDatadog", null) fetchLoConf() Logger.debug( TAG, @@ -305,7 +302,7 @@ class MainActivity : private fun updateUserData() { lanternClient.userData(object : ProUserCallback { override fun onFailure(throwable: Throwable?, error: ProError?) { - Datadog.addError("Unable to fetch user data: $error", throwable) + Logger.error(TAG, "Unable to fetch user data: $error", throwable) } override fun onSuccess(response: Response, user: ProUser?) { @@ -330,7 +327,7 @@ class MainActivity : private fun updatePlans() { lanternClient.getPlans(object : PlansCallback { override fun onFailure(throwable: Throwable?, error: ProError?) { - Datadog.addError("Unable to fetch user plans: $error", throwable) + Logger.error(TAG, "Unable to fetch user plans: $error", throwable) } override fun onSuccess(proPlans: Map) { @@ -347,7 +344,7 @@ class MainActivity : private fun updatePaymentMethods() { lanternClient.plansV3(object : PlansV3Callback { override fun onFailure(throwable: Throwable?, error: ProError?) { - Datadog.addError("Unable to fetch payment methods: $error", throwable) + Logger.error(TAG, "Unable to fetch payment methods: $error", throwable) } override fun onSuccess( diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/datadog/Datadog.kt b/android/app/src/main/kotlin/org/getlantern/lantern/datadog/Datadog.kt deleted file mode 100644 index c98085d2f..000000000 --- a/android/app/src/main/kotlin/org/getlantern/lantern/datadog/Datadog.kt +++ /dev/null @@ -1,151 +0,0 @@ -package org.getlantern.lantern.datadog - -import android.util.Log -import com.datadog.android.DatadogSite -import com.datadog.android.core.configuration.BatchSize -import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.configuration.Credentials -import com.datadog.android.core.configuration.UploadFrequency -import com.datadog.android.privacy.TrackingConsent -import com.datadog.android.rum.GlobalRum -import com.datadog.android.rum.RumActionType -import com.datadog.android.rum.RumErrorSource -import com.datadog.android.rum.RumMonitor -import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy -import org.getlantern.lantern.LanternApp -import org.getlantern.mobilesdk.Logger -import java.net.InetSocketAddress -import java.net.Proxy -import java.net.URI -import java.util.concurrent.atomic.AtomicBoolean -import com.datadog.android.Datadog as DatadogMain - -object Datadog { - private val tracedHosts = - listOf( - "datadoghq.eu", - "127.0.0.1", - "iantem.io", - "getlantern.org", - "getiantem.org", - "lantern.io", - ) - private val initialized = AtomicBoolean() - private lateinit var datadogConfig: Configuration - - fun initialize() { - if (initialized.get()) return - - DatadogMain.setVerbosity(Log.VERBOSE) - datadogConfig = createDatadogConfiguration() - - val datadogCredentials = - Credentials( - clientToken = "puba617ab01333a95a25a9d3709f04e1654", - envName = "prod", - rumApplicationId = "f8eabf3c-5db3-4f7e-8e6a-5a72433b46d2", - variant = "release", - serviceName = "lantern-android", - ) - - DatadogMain.initialize( - LanternApp.getAppContext(), - credentials = datadogCredentials, - configuration = datadogConfig, - TrackingConsent.GRANTED, - ) - - DatadogMain.setUserInfo( - id = LanternApp.getSession().userId().toString(), - ) - - GlobalRum.registerIfAbsent { - RumMonitor.Builder().build() - } - val session = LanternApp.getSession() - setCountry(session.countryCode) - initialized.set(true) - - // For some reason, sessions don't show up in DataDog RUM until we register a user action - // of some sort. So, here we fire the custom action "started" to get data to start flowing. - GlobalRum.get().addUserAction(RumActionType.CUSTOM, "started", emptyMap()) - } - - fun setCountry(country: String) { - GlobalRum.addAttribute("lantern.country_code", country) - } - - fun addError( - message: String, - throwable: Throwable? = null, - attributes: Map = emptyMap(), - ) { - Logger.e(TAG, message, throwable) - GlobalRum.get().addError(message, RumErrorSource.SOURCE, throwable, attributes) - } - - // trackUserAction is used to track specific user actions (such as taps, clicks, and scrolls) - // with RumMonitor - private fun trackUserAction( - actionType: RumActionType, - name: String, - actionAttributes: Map = emptyMap(), - ) { - GlobalRum.get().addUserAction(actionType, name, actionAttributes) - } - - // trackUserClick is used to track user clicks with RumMonitor - fun trackUserClick( - name: String, - actionAttributes: Map = emptyMap(), - ) { - trackUserAction(RumActionType.CLICK, name, actionAttributes) - } - - // trackUserTap is used to track user taps with RumMonitor - fun trackUserTap( - name: String, - actionAttributes: Map = emptyMap(), - ) { - trackUserAction(RumActionType.TAP, name, actionAttributes) - } - - private fun createDatadogConfiguration(): Configuration { - val session = LanternApp.getSession() - val hTTPAddr = session.hTTPAddr - val uri = URI("http://" + hTTPAddr) - return Configuration.Builder( - logsEnabled = true, - tracesEnabled = true, - crashReportsEnabled = true, - rumEnabled = true, - ) - .setBatchSize(BatchSize.SMALL) - .setProxy( - Proxy( - Proxy.Type.HTTP, - InetSocketAddress( - "127.0.0.1", - uri.getPort(), - ), - ), - null, - ) - .sampleRumSessions(100f) - .setUploadFrequency(UploadFrequency.FREQUENT) - .useSite(DatadogSite.EU1) - .trackBackgroundRumEvents(true) - .trackInteractions() - .trackLongTasks() - .setFirstPartyHosts(tracedHosts) - .useViewTrackingStrategy( - ActivityViewTrackingStrategy( - trackExtras = false, - componentPredicate = FlutterExcludingComponentPredicate(), - ), - ) - .build() - } - - private val TAG = Datadog::class.java.name -} diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/datadog/FlutterExcludingComponentPredicate.kt b/android/app/src/main/kotlin/org/getlantern/lantern/datadog/FlutterExcludingComponentPredicate.kt deleted file mode 100644 index 49d227731..000000000 --- a/android/app/src/main/kotlin/org/getlantern/lantern/datadog/FlutterExcludingComponentPredicate.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.getlantern.lantern.datadog - -import android.app.Activity -import com.datadog.android.rum.tracking.AcceptAllActivities -import com.datadog.android.rum.tracking.ComponentPredicate -import io.flutter.embedding.android.FlutterActivity - -class FlutterExcludingComponentPredicate : ComponentPredicate { - val innerPredicate = AcceptAllActivities() - - override fun accept(component: Activity): Boolean { - if (component is FlutterActivity) { - return false - } - - return innerPredicate.accept(component) - } - - override fun getViewName(component: Activity): String? { - return innerPredicate.getViewName(component) - } -} diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/service/LanternService.kt b/android/app/src/main/kotlin/org/getlantern/lantern/service/LanternService.kt index a49cd12ba..ac39595ae 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/service/LanternService.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/service/LanternService.kt @@ -13,7 +13,6 @@ import org.androidannotations.annotations.EService import org.getlantern.lantern.BuildConfig import org.getlantern.lantern.LanternApp import org.getlantern.lantern.R -import org.getlantern.lantern.datadog.Datadog import org.getlantern.lantern.model.AccountInitializationStatus import org.getlantern.lantern.model.LanternHttpClient import org.getlantern.lantern.model.LanternStatus @@ -110,8 +109,6 @@ open class LanternService : Service(), Runnable { createUser(0) } - Datadog.initialize() - if (!BuildConfig.PLAY_VERSION && !BuildConfig.DEVELOPMENT_MODE) { // check if an update is available autoUpdater.checkForUpdates() diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/util/PaymentsUtil.kt b/android/app/src/main/kotlin/org/getlantern/lantern/util/PaymentsUtil.kt index d116a0dac..02d20db40 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/util/PaymentsUtil.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/util/PaymentsUtil.kt @@ -17,7 +17,6 @@ import okhttp3.FormBody import okhttp3.Response import org.getlantern.lantern.LanternApp import org.getlantern.lantern.R -import org.getlantern.lantern.datadog.Datadog import org.getlantern.lantern.model.LanternHttpClient import org.getlantern.lantern.model.LanternHttpClient.ProCallback import org.getlantern.lantern.model.LanternSessionManager @@ -41,10 +40,6 @@ class PaymentsUtil(private val activity: Activity) { methodCallResult: MethodChannel.Result, ) { try { - Datadog.trackUserClick("submitStripePayment", mapOf( - "email" to email, - "planID" to planID, - )) val date = expirationDate.split("/").toTypedArray() val card: CardParams = CardParams( cardNumber.replace("[\\s]", ""), @@ -69,7 +64,7 @@ class PaymentsUtil(private val activity: Activity) { override fun onError(@NonNull error: Exception) { dialog?.dismiss() - Datadog.addError("Error submitting to Stripe: $error") + Logger.error(TAG, "Error submitting to Stripe: $error") methodCallResult.error( "errorSubmittingToStripe", error.getLocalizedMessage(), @@ -80,7 +75,7 @@ class PaymentsUtil(private val activity: Activity) { ) } catch (t: Throwable) { dialog?.dismiss() - Datadog.addError("Error submitting to Stripe", t) + Logger.error(TAG, "Error submitting to Stripe", t) methodCallResult.error( "errorSubmittingToStripe", activity.getString(R.string.error_making_purchase), @@ -96,10 +91,6 @@ class PaymentsUtil(private val activity: Activity) { methodCallResult: MethodChannel.Result, ) { try { - Datadog.trackUserClick("submitBitcoinPayment", mapOf( - "email" to email, - "planID" to planID, - )) val provider = PaymentProvider.BTCPay.toString().lowercase() val params = mutableMapOf( "email" to email, @@ -111,7 +102,7 @@ class PaymentsUtil(private val activity: Activity) { LanternHttpClient.createProUrl("/payment-redirect", params), object : ProCallback { override fun onFailure(throwable: Throwable?, error: ProError?) { - Datadog.addError("BTCPay is unavailable", throwable) + Logger.error(TAG, "BTCPay is unavailable", throwable) methodCallResult.error( "unknownError", "BTCPay is unavailable", // This error message is localized Flutter-side @@ -130,7 +121,6 @@ class PaymentsUtil(private val activity: Activity) { }, ) } catch (t: Throwable) { - Datadog.addError("BTCPay is unavailable", t) methodCallResult.error( "unknownError", "BTCPay is unavailable", // This error message is localized Flutter-side @@ -142,9 +132,6 @@ class PaymentsUtil(private val activity: Activity) { // Handles Google Play transactions fun submitGooglePlayPayment(planID: String, methodCallResult: MethodChannel.Result) { val inAppBilling = LanternApp.getInAppBilling() - Datadog.trackUserClick("googlePlayPayment", mapOf( - "planID" to planID, - )) if (inAppBilling == null) { Logger.error(TAG, "Missing inAppBilling") @@ -169,7 +156,6 @@ class PaymentsUtil(private val activity: Activity) { activity.resources.getString(R.string.error_making_purchase), null, ) - Datadog.addError("Google Play: error making purchase") return } @@ -213,7 +199,6 @@ class PaymentsUtil(private val activity: Activity) { formBody, object : ProCallback { override fun onFailure(throwable: Throwable?, error: ProError?) { - Datadog.addError("Error retrieving referral code: $error", throwable) if (error != null && error.message != null) { methodCallResult.error( "unknownError", @@ -235,7 +220,6 @@ class PaymentsUtil(private val activity: Activity) { }, ) } catch (t: Throwable) { - Datadog.addError("Unable to apply referral code", t) methodCallResult.error( "unknownError", "Something went wrong while applying your referral code", @@ -328,11 +312,6 @@ class PaymentsUtil(private val activity: Activity) { override fun onFailure(t: Throwable?, error: ProError?) { Logger.e(TAG, "Error with purchase request: $error") - Datadog.addError("Error with purchase request: $error", t, mapOf( - "provider" to provider.toString().lowercase(), - "plan" to planID, - "deviceName" to session.deviceName(), - )) dialog?.dismiss() methodCallResult.error( "errorMakingPurchase", diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/util/SentryUtil.kt b/android/app/src/main/kotlin/org/getlantern/lantern/util/SentryUtil.kt new file mode 100644 index 000000000..6bb485387 --- /dev/null +++ b/android/app/src/main/kotlin/org/getlantern/lantern/util/SentryUtil.kt @@ -0,0 +1,56 @@ +package org.getlantern.lantern.util + +import android.content.Context +import android.os.Process +import io.sentry.SentryOptions +import io.sentry.android.core.SentryAndroid +import org.getlantern.mobilesdk.Logger +import java.io.BufferedReader +import java.io.InputStreamReader + +object SentryUtil { + private val TAG = SentryUtil::class.java.name + + /** + * Enables enrichment of sentry crash reports with the most recent Go panic from logcat. + * Keep in mind that Sentry only finds panics the next time that it runs after the process + * actually panicked. So, we can safely exclude logs from our current run. + * + * Keep in mind also that there's no guarantee that the panic log in question belongs to our + * specific panic, we're just picking up the most recent panic log information. + */ + @JvmStatic + fun enableGoPanicEnrichment(ctx: Context) { + SentryAndroid.init(ctx) { options -> + options.beforeSend = SentryOptions.BeforeSendCallback { event, _ -> + // enable enrichment only for exceptions related to OS signals like SIGABRT + if (event.exceptions?.firstOrNull()?.type?.startsWith("SIG") == true) { + val myPid = Process.myPid().toString() + val goErrorLog = StringBuilder() + val process = Runtime.getRuntime().exec( + "logcat -d -v brief" + ) + BufferedReader(InputStreamReader(process.inputStream)).use { reader -> + reader.forEachLine { line -> + if (!line.contains(myPid) && line.startsWith("E/Go ")) { + if (line.contains("panic: ")) { + // this is the first line of the most recent panic, remove old rows + // from what must be prior panics + goErrorLog.clear() + } + goErrorLog.appendLine(line) + } + } + } + + if (goErrorLog.isNotEmpty()) { + Logger.debug(TAG, "Attaching latestgopanic to event") + event.setExtra("latestgopanic", goErrorLog.toString()) + } + } + + event + } + } + } +} diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/vpn/LanternVpnService.kt b/android/app/src/main/kotlin/org/getlantern/lantern/vpn/LanternVpnService.kt index 9b0e29f72..70f551a93 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/vpn/LanternVpnService.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/vpn/LanternVpnService.kt @@ -7,7 +7,6 @@ import android.content.ServiceConnection import android.net.VpnService import android.os.IBinder import org.getlantern.lantern.LanternApp -import org.getlantern.lantern.datadog.Datadog import org.getlantern.lantern.service.LanternService_ import org.getlantern.mobilesdk.Logger @@ -59,7 +58,6 @@ class LanternVpnService : VpnService(), Runnable { return START_STICKY } return if (intent.action == ACTION_DISCONNECT) { - Datadog.trackUserTap("switchVPN", mapOf("status" to "disconnect")) stop() START_NOT_STICKY } else { @@ -71,7 +69,6 @@ class LanternVpnService : VpnService(), Runnable { private fun connect() { Logger.d(TAG, "connect") - Datadog.trackUserTap("switchVPN", mapOf("status" to "connect")) Thread(this, "VpnService").start() } diff --git a/lib/ad_helper.dart b/lib/ad_helper.dart index 9802104ec..c9b6f12e4 100644 --- a/lib/ad_helper.dart +++ b/lib/ad_helper.dart @@ -11,7 +11,6 @@ import 'package:clever_ads_solutions/public/MediationManager.dart'; import 'package:flutter/foundation.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:logger/logger.dart'; -import 'package:lantern/common/datadog.dart'; import 'package:lantern/replica/common.dart'; enum AdType { Google, CAS } @@ -107,20 +106,13 @@ class AdHelper { ad.fullScreenContentCallback = FullScreenContentCallback( onAdClicked: (ad) { logger.i('[Ads Manager] onAdClicked callback'); - Datadog.trackUserTap( - 'User tapped on interstitial ad', googleAttributes); }, onAdShowedFullScreenContent: (ad) { logger.i('[Ads Manager] Showing Ads'); - Datadog.trackUserCustom( - 'User shown interstitial ad', googleAttributes); }, onAdFailedToShowFullScreenContent: (ad, error) { logger.i( '[Ads Manager] onAdFailedToShowFullScreenContent callback'); - Datadog.addError( - 'Ad failed to show full screen content: $error', - attributes: googleAttributes); //if ads fail to load let user turn on VPN _postShowingAds(); }, @@ -131,13 +123,10 @@ class AdHelper { ); _interstitialAd = ad; logger.i('[Ads Manager] to loaded $ad'); - Datadog.trackUserCustom('Interstitial ad loaded', googleAttributes); }, onAdFailedToLoad: (err) { _failedLoadAttempts++; // increment the count on failure logger.i('[Ads Manager] failed to load $err'); - Datadog.addError('failed to load interstitial ad: $err', - attributes: googleAttributes); _postShowingAds(); }, ), @@ -207,7 +196,6 @@ class AdHelper { if (casMediationManager != null) { await casMediationManager!.loadInterstitial(); logger.i('[Ads Manager] Request: Initiating CAS Interstitial loading.'); - Datadog.trackUserCustom('Interstitial ad loaded', casAttributes); } } @@ -220,16 +208,12 @@ class AdHelper { void _onCASAdShowFailed() { logger.e('[Ads Manager] Error: CAS Interstitial failed to display.'); - Datadog.addError('Failed to display interstitial ad', - attributes: casAttributes); _failedCASLoadAttempts++; _postShowingAds(); // Reload or decide the next action } void _onCASAdClosedOrComplete() { logger.i('[Ads Manager] Completion: CAS Interstitial closed or completed.'); - Datadog.trackUserCustom( - 'Interstitial ad closed or completed', casAttributes); // Reset the counter when the ad successfully shows and closes/completes _failedCASLoadAttempts = 0; _postShowingAds(); @@ -289,14 +273,11 @@ class InterstitialListenerWrapper extends AdCallback { onFailed.call(); logger.i( '[CASIntegrationHelper] - InterstitialListenerWrapper onShowFailed-:$message'); - Datadog.addError('Interstitial ad onShowFailed: $message', - attributes: casAttributes); } @override void onShown() { // Called when ad is shown. logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onShown'); - Datadog.trackUserCustom('User shown interstitial ad', casAttributes); } } diff --git a/lib/catcher_setup.dart b/lib/catcher_setup.dart new file mode 100644 index 000000000..ee141d83e --- /dev/null +++ b/lib/catcher_setup.dart @@ -0,0 +1,44 @@ +import 'package:catcher_2/catcher_2.dart'; +import 'package:flutter/material.dart'; +import 'package:sentry/sentry.dart'; + +final debugOption = Catcher2Options( + SilentReportMode(), + [ + ConsoleHandler( + enableApplicationParameters: true, + enableDeviceParameters: true, + enableCustomParameters: true, + enableStackTrace: true, + ), + ], +); + +final releaseOption = Catcher2Options( + SilentReportMode(), + [ + ConsoleHandler( + enableApplicationParameters: true, + enableDeviceParameters: true, + enableCustomParameters: true, + enableStackTrace: true, + ), + SentryHandler( + SentryClient( + SentryOptions( + dsn: + 'https://4753d78f885f4b79a497435907ce4210@o75725.ingest.sentry.io/5850353', + ), + ), + printLogs: true, + ), + ], +); + +Catcher2 setupCatcherAndRun(StatelessWidget root) { + return Catcher2( + rootWidget: root, + debugConfig: debugOption, + releaseConfig: releaseOption, + ); +} diff --git a/lib/common/common.dart b/lib/common/common.dart index db237aa49..64a1b3506 100644 --- a/lib/common/common.dart +++ b/lib/common/common.dart @@ -28,11 +28,8 @@ export 'package:provider/provider.dart'; export 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; export 'package:stop_watch_timer/stop_watch_timer.dart'; export 'package:wakelock/wakelock.dart'; -export 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; -export 'package:datadog_tracking_http_client/datadog_tracking_http_client.dart'; export 'add_nonbreaking_spaces.dart'; -export 'datadog.dart'; export 'disable_back_button.dart'; export 'iterable_extension.dart'; export 'list_subscriber.dart'; diff --git a/lib/common/datadog.dart b/lib/common/datadog.dart deleted file mode 100644 index 6fbee1efa..000000000 --- a/lib/common/datadog.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; -import 'package:datadog_tracking_http_client/datadog_tracking_http_client.dart'; - -class Datadog { - static final DatadogSdk _instance = DatadogSdk.instance; - - static trackUserTap(String message, [Map attributes = const {}]) { - _instance.rum?.addUserAction(RumUserActionType.tap, message, attributes); - } - - static trackUserCustom(String message, [Map attributes = const {}]) { - _instance.rum?.addUserAction(RumUserActionType.custom, message, attributes); - } - - // Notifies Datadog that an Exception or Error [error] occurred in the currently - // presented View - static addError( - Object error, { - StackTrace? st, - Map attributes = const {}, - }) { - _instance.rum?.addErrorInfo( - error.toString(), - RumErrorSource.source, - stackTrace: st, - attributes: attributes, - ); - } -} diff --git a/lib/home.dart b/lib/home.dart index 9a407c52a..eb8babf8e 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -97,12 +97,6 @@ class _HomePageState extends State { Future _handleNativeNavigationRequest(MethodCall methodCall) async { switch (methodCall.method) { - case 'initDatadog': - final config = DdSdkExistingConfiguration( - loggingConfiguration: LoggingConfiguration(), - detectLongTasks: true, - )..enableHttpTracking(); - await DatadogSdk.instance.attachToExisting(config); case 'openConversation': final contact = Contact.fromBuffer(methodCall.arguments as Uint8List); await _context!.router.push(Conversation(contactId: contact.contactId)); diff --git a/lib/main.dart b/lib/main.dart index e96ba72f6..f4f137df9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter_driver/driver_extension.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:lantern/app.dart'; +import 'package:lantern/catcher_setup.dart'; import 'package:lantern/common/common.dart'; Future main() async { @@ -14,7 +15,7 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); await _initGoogleMobileAds(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - runApp(LanternApp()); + setupCatcherAndRun(LanternApp()); } Future _initGoogleMobileAds() async { diff --git a/pubspec.lock b/pubspec.lock index fb56a78dc..16831dac9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -217,6 +217,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + catcher_2: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: "7be5bb3ea4867f0ab549443511478303e54db4b7" + url: "https://github.com/ThexXTURBOXx/catcher.git" + source: git + version: "1.0.0-alpha.2" characters: dependency: transitive description: @@ -261,10 +270,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -337,22 +346,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.10.1" - datadog_flutter_plugin: - dependency: "direct main" - description: - name: datadog_flutter_plugin - sha256: "2db71372f12f250a549f249046912594f15bf59113dc64a7e2fd5ae85ad90729" - url: "https://pub.dev" - source: hosted - version: "1.4.0" - datadog_tracking_http_client: - dependency: "direct main" - description: - name: datadog_tracking_http_client - sha256: "7274fee927d8ec012a6c4fd3e3333616035236875129f83525fa0bbbe2a66bbb" - url: "https://pub.dev" - source: hosted - version: "1.4.0" dbus: dependency: transitive description: @@ -784,10 +777,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -844,6 +837,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + mailer: + dependency: transitive + description: + name: mailer + sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382" + url: "https://pub.dev" + source: hosted + version: "6.0.1" markdown: dependency: transitive description: @@ -856,18 +857,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -1180,6 +1181,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.8" + sentry: + dependency: "direct main" + description: + name: sentry + sha256: "830667eadc0398fea3a3424ed1b74568e2db603a42758d0922e2f2974ce55a60" + url: "https://pub.dev" + source: hosted + version: "7.10.1" share_plus: dependency: "direct main" description: @@ -1325,10 +1334,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -1429,26 +1438,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.3" timezone: dependency: transitive description: @@ -1473,6 +1482,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" url_launcher: dependency: "direct main" description: @@ -1637,10 +1654,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "11.7.1" wakelock: dependency: "direct main" description: @@ -1689,6 +1706,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1794,5 +1819,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.3 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 1ca26afbe..2192591fa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,12 @@ dependencies: google_fonts: flutter_switch: ^0.3.2 flag: ^7.0.0 + # from sdk. See https://github.com/jhomlala/catcher/pull/234 for more details + catcher_2: + git: + url: https://github.com/ThexXTURBOXx/catcher.git + + sentry: ^7.10.1 badges: ^3.1.1 dotted_border: ^2.0.0+3 styled_text: ^8.1.0 @@ -95,8 +101,6 @@ dependencies: device_info_plus: ^9.0.3 flutter_mailer: ^2.0.0 fluttertoast: ^8.2.2 - datadog_flutter_plugin: ^1.4.0 - datadog_tracking_http_client: ^1.4.0 # Package information package_info_plus: ^4.1.0 From bd226a08d2660ef2c48394c5d1373b07d2676e9f Mon Sep 17 00:00:00 2001 From: jigar-f <132374182+jigar-f@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:35:56 +0530 Subject: [PATCH 8/9] Appium Flavor (#928) * Added Appium flavor. * Update browserstack CI * Update browerstack.yml --- .github/workflows/browerstack.yml | 4 ++-- .run/Appium Test.run.xml | 8 ++++++++ Makefile | 5 ++++- android/app/build.gradle | 6 ++++-- .../org/getlantern/mobilesdk/model/SessionManager.kt | 2 +- lib/main.dart | 9 +++++---- 6 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 .run/Appium Test.run.xml diff --git a/.github/workflows/browerstack.yml b/.github/workflows/browerstack.yml index a34ddb1f1..06dd5a072 100644 --- a/.github/workflows/browerstack.yml +++ b/.github/workflows/browerstack.yml @@ -70,7 +70,7 @@ jobs: CI: "true" run: | flutter pub get - make do-android-debug ANDROID_ARCH=all + make appium-test-build - name: List files in current directory run: ls -la @@ -82,7 +82,7 @@ jobs: env: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - BROWSERSTACK_APP_PATH: "build/app/outputs/flutter-apk/app-prod-debug.apk" + BROWSERSTACK_APP_PATH: "build/app/outputs/flutter-apk/app-appiumtest-debug.apk" run: | response=$(curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ diff --git a/.run/Appium Test.run.xml b/.run/Appium Test.run.xml new file mode 100644 index 000000000..4933e78fa --- /dev/null +++ b/.run/Appium Test.run.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/Makefile b/Makefile index f5d3497a6..408018a10 100644 --- a/Makefile +++ b/Makefile @@ -274,9 +274,12 @@ $(MOBILE_ANDROID_LIB): $(ANDROID_LIB) mkdir -p $(MOBILE_LIBS) && \ cp $(ANDROID_LIB) $(MOBILE_ANDROID_LIB) -.PHONY: android-lib +.PHONY: android-lib appium-test-build android-lib: $(MOBILE_ANDROID_LIB) +appium-test-build: + flutter build apk --flavor=appiumTest --dart-define=app.flavor=appiumTest --debug + $(MOBILE_TEST_APK) $(MOBILE_TESTS_APK): $(MOBILE_SOURCES) $(MOBILE_ANDROID_LIB) @$(GRADLE) -PandroidArch=$(ANDROID_ARCH) \ -PandroidArchJava="$(ANDROID_ARCH_JAVA)" \ diff --git a/android/app/build.gradle b/android/app/build.gradle index 45a8c0cd6..fe5f86a0f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -170,8 +170,7 @@ android { } buildTypes { - - debug { + debug { minifyEnabled false shrinkResources false debuggable true @@ -254,6 +253,9 @@ android { autoTest { versionCode 1 // so we can always test auto-update. } + appiumTest { + buildConfigField "boolean", "CI", "true" + } } splits { diff --git a/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt b/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt index 1c9292d3a..4d0e071ba 100644 --- a/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt +++ b/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt @@ -328,7 +328,7 @@ abstract class SessionManager(application: Application) : Session { val isPaymentTestMode: Boolean get() { val paymentTestMode = prefs.getBoolean(PAYMENT_TEST_MODE, false) - val ciValue = BuildConfig.CI + val ciValue = BuildConfig.FLAVOR == "appiumTest" return ciValue || paymentTestMode } diff --git a/lib/main.dart b/lib/main.dart index f4f137df9..34cd5fb94 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,12 +6,13 @@ import 'package:lantern/common/common.dart'; Future main() async { // CI will be true only when running appium test - var CI = const String.fromEnvironment('CI', defaultValue: 'false'); - var boolCI = bool.fromEnvironment("CI", defaultValue: false); - print('Appium CI is running $CI and bool $boolCI'); - if (CI == 'true') { + const String flavor = String.fromEnvironment('app.flavor'); + print("Running Flavor $flavor"); + if (flavor == 'appiumTest') { + print("Flutter extension enabled $flavor"); enableFlutterDriverExtension(); } + WidgetsFlutterBinding.ensureInitialized(); await _initGoogleMobileAds(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); From 12a67041567bf658c6a9f70785aacf8e5c70cb15 Mon Sep 17 00:00:00 2001 From: atavism Date: Tue, 10 Oct 2023 12:32:13 -0700 Subject: [PATCH 9/9] Add Appium test to install app from Play store with Lantern running (#906) * Add initial GooglePlayTest * Add initial GooglePlayTest * Updates to google play tests * Updates to google play tests * Updates to google play tests * switch VPN on prior to running Play tests * open play app using existing driver * remove semicolon * update README * update README * move tests to AppTest * remove unused * remove datadog from gradle config --------- Co-authored-by: atavism --- README.md | 36 ++++++++ .../test/java/appium_kotlin/tests/AppTest.kt | 92 +++++++++++++++++++ .../test/java/appium_kotlin/tests/BaseTest.kt | 82 +++++++++-------- 3 files changed, 173 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 6953eaa04..f73c3ea4a 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,42 @@ You'd most probably wanna run this against Lantern's staging servers **and** tur - You can debug pro-server-neu's staging instance (i.e., `api-staging.getiantem.org`) using a combination of log, telemetry and checking the staging Redis instance (see [here](https://github.com/getlantern/pro-server-neu/blob/c79c1b8da9e418bc4b075392fde9b051c699141d/README.md?plain=1#L125) for more info) +## Running Appium tests locally + +To run the Appium tests locally with a connected device, you need to follow a few steps: + +1. Install appium with npm: + +```bash +npm install -g appium +``` + +2. Install the necessary drivers: + +```bash +appium driver install uiautomator2 +appium driver install --source=npm appium-flutter-driver +appium driver install espresso +``` + +3. Generate a debug build with `CI=true make android-debug ANDROID_ARCH=all` ... CI needs to be set to true to enable the +Flutter driver extension. + +4. Modify [local_config.json](appium_kotlin/app/src/test/resources/local/local_config.json) to specify the path of a debug build APK on your system, and change `appium:udid` to specify your device ID (you can get this from `adb devices`) + +5. Make sure your device is connected to your computer and then run + +```bash +cd appium_kotlin +./gradlew test +``` + +To run a specific test, you can do + +```bash +./gradlew test --tests '*GooglePlay*' +``` + ## Source Dump Lantern Android source code is made available via source dump tarballs. To create one, run: diff --git a/appium_kotlin/app/src/test/java/appium_kotlin/tests/AppTest.kt b/appium_kotlin/app/src/test/java/appium_kotlin/tests/AppTest.kt index 5140b56b4..b1a19282c 100644 --- a/appium_kotlin/app/src/test/java/appium_kotlin/tests/AppTest.kt +++ b/appium_kotlin/app/src/test/java/appium_kotlin/tests/AppTest.kt @@ -22,26 +22,36 @@ import appium_kotlin.REPORT_DESCRIPTION import appium_kotlin.REPORT_ISSUE_SUCCESS import appium_kotlin.SEND_REPORT import appium_kotlin.SUPPORT +import io.appium.java_client.MobileBy import io.appium.java_client.TouchAction import io.appium.java_client.android.Activity import io.appium.java_client.android.AndroidDriver +import io.appium.java_client.android.nativekey.AndroidKey +import io.appium.java_client.android.nativekey.KeyEvent +import io.appium.java_client.remote.AndroidMobileCapabilityType import io.appium.java_client.touch.WaitOptions import io.appium.java_client.touch.offset.PointOption import org.junit.jupiter.api.Assertions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.openqa.selenium.By +import org.openqa.selenium.remote.DesiredCapabilities import org.openqa.selenium.logging.LogEntries import pro.truongsinh.appium_flutter.FlutterFinder import java.io.IOException +import java.net.URL import java.time.Duration import java.time.LocalDate import java.time.format.DateTimeFormatter +import java.util.concurrent.TimeUnit import java.util.regex.Pattern class AppTest() : BaseTest() { private val isLocalRun = (System.getenv("RUN_ENV") ?: "local") == "local" + private val testAppName = "chromecast" + private val testAppPackage = "com.google.android.apps.chromecast.app" + private val testAppActivity = ".DiscoveryActivity" @ParameterizedTest @MethodSource("devices") @@ -63,6 +73,8 @@ class AppTest() : BaseTest() { // Report and issue flow reportAnIssueFlow(androidDriver, taskId, flutterFinder) + googlePlayFlow(androidDriver, taskId, flutterFinder) + if (!isLocalRun) { testPassed(androidDriver) } @@ -404,4 +416,84 @@ class AppTest() : BaseTest() { return "" } + @Throws(IOException::class, InterruptedException::class) + private fun googlePlayFlow( + driver: AndroidDriver, + taskId: Int, + flutterFinder: FlutterFinder + ) { + turnVPNon(driver, taskId, flutterFinder) + driver.startActivity(Activity("com.android.vending", ".AssetBrowserActivity")) + testEstablishPlaySession(driver) + testGooglePlayFeatures(driver) + installAppFromPlayStore(taskId, driver) + } + + fun turnVPNon( + driver: AndroidDriver, + taskId: Int, + flutterFinder: FlutterFinder, + ) { + Thread.sleep(5000) + + switchToContext(ContextType.NATIVE_APP, driver) + Thread.sleep(5000) + driver.activateApp(LANTERN_PACKAGE_ID) + Thread.sleep(5000) + + + switchToContext(ContextType.FLUTTER, driver) + val vpnSwitchFinder = flutterFinder.byType("FlutterSwitch") + vpnSwitchFinder.click() + Thread.sleep(2000) + // Approve VPN Permissions dialog + switchToContext(ContextType.NATIVE_APP, driver) + Thread.sleep(1000) + } + + fun testEstablishPlaySession(driver: AndroidDriver) { + Assertions.assertEquals(driver.currentPackage, "com.android.vending") + Assertions.assertEquals(driver.currentActivity(), ".AssetBrowserActivity") + } + + fun testGooglePlayFeatures(driver: AndroidDriver) { + driver.findElement(By.xpath("//android.widget.FrameLayout[@content-desc = 'Show navigation drawer']"))?.click() + val elements = driver.findElements(By.xpath("//android.widget.TextView")) + if (elements == null) return + for (element in elements) { + if (element.text.equals("Settings")) { + element.click() + break + } + } + } + + fun openSearchForm(driver: AndroidDriver) { + val elements = driver?.findElements(By.xpath("//android.widget.TextView")) + if (elements == null) return + for (element in elements) { + if (element.text.equals("Search for apps & games")) { + element.click() + break + } + } + } + + @Throws(Exception::class) + fun installAppFromPlayStore(taskId: Int, driver: AndroidDriver) { + openSearchForm(driver) + driver.findElement(MobileBy.className("android.widget.EditText"))?.sendKeys(testAppName) + + driver.findElement(By.xpath("//android.support.v7.widget.RecyclerView[1]/android.widget.LinearLayout[1]"))?.click() + + val button = driver.findElement(MobileBy.className("android.widget.Button")) + if (button?.text.equals("Install")) { + println("Installing application") + button?.click() + } + + driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS) + driver.pressKey(KeyEvent(AndroidKey.HOME)) + } + } \ No newline at end of file diff --git a/appium_kotlin/app/src/test/java/appium_kotlin/tests/BaseTest.kt b/appium_kotlin/app/src/test/java/appium_kotlin/tests/BaseTest.kt index 9f8c09e40..2fd4f6bf6 100644 --- a/appium_kotlin/app/src/test/java/appium_kotlin/tests/BaseTest.kt +++ b/appium_kotlin/app/src/test/java/appium_kotlin/tests/BaseTest.kt @@ -4,8 +4,8 @@ import appium_kotlin.ContextType import com.google.gson.JsonArray import com.google.gson.JsonObject import com.google.gson.JsonParser -import io.appium.java_client.AppiumDriver import io.appium.java_client.android.AndroidDriver +import io.appium.java_client.remote.MobileCapabilityType import io.appium.java_client.service.local.AppiumDriverLocalService import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException import io.appium.java_client.service.local.AppiumServiceBuilder @@ -16,10 +16,8 @@ import org.junit.jupiter.api.parallel.ExecutionMode import org.junit.jupiter.params.provider.MethodSource import org.openqa.selenium.JavascriptExecutor import org.openqa.selenium.remote.DesiredCapabilities -import pro.truongsinh.appium_flutter.FlutterFinder import java.io.FileReader import java.net.URL -import java.util.concurrent.TimeUnit import java.util.stream.Stream /** Here is the device list-:https://www.browserstack.com/list-of-browsers-and-platforms/app_automate */ @@ -31,7 +29,6 @@ open class BaseTest { lateinit var config: JsonObject lateinit var service: AppiumDriverLocalService - @JvmStatic @MethodSource("devices") fun devices(): Stream { @@ -54,33 +51,14 @@ open class BaseTest { } } - fun setupAndCreateConnection(taskId: Int): AndroidDriver { - println("Setup and creating connection for TaskId: $taskId") + // Initialize DesiredCapabilities + fun initialCapabilities(taskId: Int): DesiredCapabilities { // Initialize DesiredCapabilities val capabilities = DesiredCapabilities() // Get common capabilities from config val commonCapabilities = config["capabilities"] as JsonObject - // Get environment variables if available or get from config - val username = System.getenv("BROWSERSTACK_USERNAME") ?: config.get("username").asString - val accessKey = - System.getenv("BROWSERSTACK_ACCESS_KEY") ?: config.get("access_key").asString val app = System.getenv("BROWSERSTACK_APP_ID") ?: config.get("app").asString - val server = System.getenv("SERVER") ?: config.get("server").asString - // Check if it is a local run - val isLocalRun = (System.getenv("RUN_ENV") ?: "local") == "local" val envs = config["environments"] as JsonArray - // If local run, start Appium Server - if (isLocalRun) { - // Start Appium Server for local run - service = AppiumServiceBuilder() - .withArgument(GeneralServerFlag.ALLOW_INSECURE, "chromedriver_autodownload") - .build() - service.start() - - if (!service.isRunning) { - throw AppiumServerHasNotBeenStartedLocallyException("An appium server node is not started!") - } - } // Iterate over common capabilities val it = commonCapabilities.entrySet().iterator() @@ -103,16 +81,15 @@ open class BaseTest { if (capabilities.getCapability(pair.key.toString()) == null) { capabilities.setCapability( pair.key.toString(), - pair.value.toString().replace("\"", "") + pair.value.toString().replace("\"", ""), ) } } } capabilities.setCapability("app", app) -// capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Flutter") + capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Flutter") println("Setup for TaskId $taskId: $capabilities") - val envCapabilities = envs[taskId] as JsonObject println("TaskId: $taskId | Current Evn $envCapabilities") @@ -120,15 +97,50 @@ open class BaseTest { envCapabilities.entrySet().iterator().forEach { pair -> capabilities.setCapability(pair.key, pair.value.toString().replace("\"", "")) } - val url = if (isLocalRun) { + return capabilities + } + + // Check if it is a local run + fun checkLocalRun(): Boolean { + val isLocalRun = (System.getenv("RUN_ENV") ?: "local") == "local" + // If local run, start Appium Server + if (isLocalRun) { + // Start Appium Server for local run + service = AppiumServiceBuilder() + .withArgument(GeneralServerFlag.ALLOW_INSECURE, "chromedriver_autodownload") + .build() + service.start() + + if (!service.isRunning) { + throw AppiumServerHasNotBeenStartedLocallyException("An appium server node is not started!") + } + } + return isLocalRun + } + + fun serviceURL(isLocalRun: Boolean): String { + // Get environment variables if available or get from config + val username = System.getenv("BROWSERSTACK_USERNAME") ?: config.get("username").asString + val accessKey = + System.getenv("BROWSERSTACK_ACCESS_KEY") ?: config.get("access_key").asString + val server = System.getenv("SERVER") ?: config.get("server").asString + return if (isLocalRun) { service.url.toString() } else { - "https://${username}:${accessKey}@$server" + "https://$username:$accessKey@$server" } + } + fun setupAndCreateConnection(taskId: Int): AndroidDriver { + println("Setup and creating connection for TaskId: $taskId") + + val isLocalRun = checkLocalRun() + val capabilities = initialCapabilities(taskId) + + val url = serviceURL(isLocalRun) val driver = AndroidDriver( URL(url), - capabilities + capabilities, ) println("TaskId: $taskId | Driver created") @@ -136,7 +148,6 @@ open class BaseTest { return driver } - fun testPassed(driver: AndroidDriver) { val jse = (driver as JavascriptExecutor) jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"passed\", \"reason\": \"All test passed!\"}}") @@ -144,15 +155,13 @@ open class BaseTest { fun testFail(failureMessage: String, driver: AndroidDriver) { val jse = (driver as JavascriptExecutor) - jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\":\"failed\", \"reason\": \"$failureMessage\"}}"); + jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\":\"failed\", \"reason\": \"$failureMessage\"}}") } - protected fun switchToContext(contextType: ContextType, driver: AndroidDriver) { val context = getContextString(contextType) driver.context(context) print("Android", "Switched to context: $context") - } private fun getContextString(contextType: ContextType): String { @@ -166,5 +175,4 @@ open class BaseTest { protected fun print(tag: String, message: String) { println("[$tag] $message") } - -} \ No newline at end of file +}