import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; // import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:hive/hive.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:stripe_payment/stripe_payment.dart'; import '../constants.dart'; import '../events/eventbus.dart'; import '../events/events.dart'; import '../generated/l10n.dart'; import '../models/comment.dart'; import '../models/order.dart'; import '../models/payment_platform.dart'; import '../models/stripe_payment_method.dart'; import '../models/user.dart'; import '../routes.dart'; import '../store/actions.dart'; import '../store/store.dart'; import '../widgets/general/stripe_pay.dart'; import 'http_util.dart'; import 'utils.dart'; typedef void OnGotFile(int imageId, String filePath); class Util { static final Util _util = Util._internal(); factory Util() { return _util; } Util._internal(); // final FirebaseMessaging firebaseMessaging = FirebaseMessaging(); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin(); static bool notificationTapped = false; init() { // initLocalNotification(); // initFirebaseMessaging(); // ePmqRwRUTmKuFNfbHA-6zq:APA91bEFEQx1YgJqfx0V0VYo86uRDGjG6SP1SPCAZW4OvQ7gMOeKWDQ47bfuEM00YavtKq7ZGpsWfFL7B3ypm00ZA6crjJJLK_-j_H8bS_dnQbLFwyRcW67IXCs5sRkro71bRZv7L1WM // eventBus.on().listen((event) { // if (firebaseMessaging != null) { // firebaseMessaging.subscribeToTopic(event.topic); // } // }); // eventBus.on().listen((event) { // if (firebaseMessaging != null) { // firebaseMessaging.unsubscribeFromTopic(event.topic); // } // }); } initLocalNotification() async { var initializationSettingsAndroid = new AndroidInitializationSettings('ic_launcher'); var initializationSettingsIOS = new IOSInitializationSettings( onDidReceiveLocalNotification: onDidReceiveLocalNotification); var initializationSettings = new InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, ); await flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: onSelectNotification); } Future onSelectNotification(String payload, {bool replace=false}) async { print('payload: $payload'); if (payload != null && payload.isNotEmpty) { Routes.router.navigateTo(store.state.context, payload, replace: replace); } } // initFirebaseMessaging() { // if (Platform.isIOS) { // iosPermission(); // } // firebaseMessaging.getToken().then((token) { // print('FCM token: $token'); // store.dispatch(UpdateFcmToken(token)); // }); // firebaseMessaging.configure( // onMessage: (Map message) { // print('FCM onMessage: $message'); // // eventBus.fire(OnFcmReceived(message)); // showNotificationWithDefaultSound(message); // return; // }, // onBackgroundMessage: Platform.isIOS ? null : backgroundMessageHandler, // onResume: (Map message) { // print('FCM onResume: $message'); // if (Platform.isAndroid) { // if (message != null && message['data'] != null && message['data']['route'] != null) { // notificationTapped = true; // onSelectNotification(message['data']['route']); // } // } else { // if (message != null && message['route'] != null) { // notificationTapped = true; // onSelectNotification(message['route']); // } // } // return; // }, // onLaunch: (Map message) { // print('FCM onLaunch: $message'); // if (Platform.isAndroid) { // if (message != null && message['data'] != null && message['data']['route'] != null) { // notificationTapped = true; // onSelectNotification(message['data']['route'], replace: true); // } // } else { // if (message != null && message['route'] != null) { // notificationTapped = true; // onSelectNotification(message['route'], replace: true); // } // } // return; // }, // ); // } Future onDidReceiveLocalNotification(int id, String title, String body, String payload) async { showDialog( context: store.state.context, builder: (BuildContext context) => CupertinoAlertDialog( title: Text(title), content: Text(body), actions: [ CupertinoDialogAction( isDefaultAction: true, child: Text(S.of(context).ok), onPressed: () async { Navigator.of(context, rootNavigator: true).pop(); if (payload != null && payload.isNotEmpty) { Routes.router.navigateTo(context, payload); } }, ) ], ), ); } Future showNotificationWithDefaultSound(Map message) async { var androidPlatformChannelSpecifics = new AndroidNotificationDetails( 'channelId', 'channelName', 'channelDescription', importance: Importance.max, priority: Priority.high, ); var iOSPlatformChannelSpecifics = new IOSNotificationDetails(presentSound: true, badgeNumber: 1); var platformChannelSpecifics = new NotificationDetails( android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics, ); await flutterLocalNotificationsPlugin.show( 0, Platform.isAndroid ? message['notification']['title'] : message['title'], Platform.isAndroid ? message['notification']['body'] : message['body'], platformChannelSpecifics, payload: Platform.isAndroid ? message['data']['route'] : message['route'], ); } // iosPermission() { // firebaseMessaging.requestNotificationPermissions( // const IosNotificationSettings( // sound: true, // badge: true, // alert: true, // ) // ); // firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) { // print('IOS settings registered: $settings'); // }); // } static Future backgroundMessageHandler(Map message) { print('FCM background message handler: $message'); return Future.value(); } static Future getBox() async { final dir = await getApplicationDocumentsDirectory(); Hive.init(dir.path); Box box = await Hive.openBox('app_data'); return box; } static Widget showImage(String imageUrl, {double width, double height, BoxFit fit, Widget Function(BuildContext, String, dynamic) errorWidget}) { if (imageUrl != null && imageUrl.isNotEmpty && imageUrl.startsWith('https:')) { return CachedNetworkImage( imageUrl: imageUrl, width: width, height: width, fit: fit, placeholder: (context, url) => Utils.imageLoadingIndicator(width: width, height: height), errorWidget: errorWidget != null ? errorWidget : (context, url, error) { return Image.asset( 'assets/images/not_found.png', width: width, height: height, fit: fit, ); }, ); } else if (imageUrl != null && imageUrl.isNotEmpty) { return Image.file( File(imageUrl), width: width, height: height, fit: fit, errorBuilder: (BuildContext context, Object object, StackTrace stackTrace) { return Image.asset( 'assets/images/not_found.png', width: width, height: height, fit: fit, ); }, ); } else { return Image.asset( 'assets/images/not_found.png', width: width, height: height, fit: fit, ); } } static void openWebUrl(BuildContext context, String link) { Routes.router.navigateTo(context, '/webview/$link'); } AlertDialog getPicture(BuildContext context, User user, {int commentId = -1, int orderId = 0}) { return AlertDialog( title: Text( S.of(context).get_picture, ), content: Container( height: 130.0, child: Column( children: [ Container( margin: EdgeInsets.only(bottom: 12.0), child: Text( S.of(context).get_picture_from, style: TextStyle( fontSize: 15.0, color: Colors.black26, ), ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_library, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).gallery, ), ), ], ), onPressed: () { getPictureFromGallery(context, user, commentId: commentId, orderId: orderId); }, ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_camera, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).camera, ), ), ], ), onPressed: () { getPictureFromCamera(context, user, commentId: commentId, orderId: orderId); }, ), ) ], ), ), ); } void getPictureFromGallery(BuildContext context, User user, {int commentId = -1, int orderId = 0}) async { final picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); Navigator.of(context).pop(); uploadPicture(context, File(image.path), user, commentId: commentId, orderId: orderId); } void getPictureFromCamera(BuildContext context, User user, {int commentId = -1, int orderId = 0}) async { final picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.camera); Navigator.of(context).pop(); uploadPicture(context, File(image.path), user, commentId: commentId, orderId: orderId); } void uploadPicture(BuildContext context, File image, User user, {int commentId = -1, int orderId = 0}) async { if (await image.exists()) { print('image: ${image.path}'); String imageName = basename(image.path); MultipartFile imageFile = await MultipartFile.fromFile(image.path, filename: imageName); Map formMap = { 'id': user.id, 'file': imageFile, }; if (commentId >= 0) { formMap = { 'contact_id': user.id, 'comment_id': commentId, 'order_id': orderId, 'file': imageFile, }; HttpUtil.httpPost( 'v1/upload-comment-image', (response) { eventBus.fire(new OnCommentUpdatedEvent(Comment.fromJson(response.data))); }, isFormData: true, body: formMap, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnUploadCommentImageProgressEvent(false, 0.0)); } else { eventBus.fire(OnUploadCommentImageProgressEvent(true, sent / total)); } }, ).catchError((error) { Utils.showMessageDialog(context, error); }); } else { HttpUtil.httpPost( 'v1/upload-avatar', (response) { store.dispatch(new UpdateCurrentUser(User.fromJson(response.data))); eventBus.fire(new OnCurrentUserUpdated()); }, isFormData: true, body: formMap, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnProgressEvent(false, 0.0)); } else { eventBus.fire(OnProgressEvent(true, sent / total)); } }, ).catchError((error) { Utils.showMessageDialog(context, error); }); } } } AlertDialog getPicture2(BuildContext context, int imageId, OnGotFile onGotFile) { return AlertDialog( title: Text( S.of(context).get_picture, ), content: Container( height: 130.0, child: Column( children: [ Container( margin: EdgeInsets.only(bottom: 12.0), child: Text( S.of(context).get_picture_from, style: TextStyle( fontSize: 15.0, color: Colors.black26, ), ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_library, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).gallery, ), ), ], ), onPressed: () { getPictureFromGallery2(context, imageId, onGotFile); }, ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_camera, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).camera, ), ), ], ), onPressed: () { getPictureFromCamera2(context, imageId, onGotFile); }, ), ) ], ), ), ); } void getPictureFromGallery2(BuildContext context, int imageId, OnGotFile onGotFile) async { ImagePicker picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); Navigator.of(context).pop(); onGotFile(imageId, image.path); } void getPictureFromCamera2(BuildContext context, int imageId, OnGotFile onGotFile) async { ImagePicker picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.camera); Navigator.of(context).pop(); onGotFile(imageId, image.path); } Future createTicket(BuildContext context, String msg, List> images, OnSuccess onSuccess, OnError onError, {int id}) { var formData = FormData(); formData.fields.add(MapEntry("msg", msg)); formData.fields.add(MapEntry('id', id == null ? '0' : id.toString())); for (int i = 0; i < images.length; i++) { String imagePath = images[i]['path']; if (imagePath.isNotEmpty) { String imageName = basename(imagePath); formData.files.add( MapEntry( "files", MultipartFile.fromFileSync(imagePath, filename: imageName) ) ); } } HttpUtil.httpPost('create-new-ticket-app/', (response) { if (onSuccess != null) { onSuccess(response); } }, isFormData: true, // body: null, formData: formData, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnSubmitProgressEvent(false, 100.0)); } else { eventBus.fire(OnSubmitProgressEvent(true, sent / total * 100.0)); } }, businessId: Constants.BUSINESS_ID, ).catchError((error) { if (onError != null) { onError(error); } }); } Widget getNativePay(BuildContext context, Order order, PaymentPlatform paymentPlatform) { return GestureDetector( child: Container( padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( margin: EdgeInsets.only(right: 6.0), child: Text( Platform.isAndroid ? S.of(context).pay_with : S.of(context).pay_with, style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, ), ), ), Container( padding: EdgeInsets.only(right: 10.0), child: Platform.isAndroid ? Image.asset( 'assets/images/google_pay.png', height: 22.0, fit: BoxFit.fitHeight, ) : Image.asset( 'assets/images/apple_pay.png', height: 22.0, fit: BoxFit.fitHeight, ), ), ], ), ), decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 10, color: Colors.black26, ), ), ), ), onTap: () { print(paymentPlatform); PaymentPlatform newPaymentPlatform = PaymentPlatform.fromJson(json.decode(json.encode(paymentPlatform))); newPaymentPlatform.code = Constants.PAYMENT_METHOD_NATIVE_PAY; goPayment(context, order, newPaymentPlatform); }, ); } static void goPayment(BuildContext context, Order order, PaymentPlatform paymentPlatform, { bool googlePay=false, bool applePay=false, StripePaymentMethod stripePaymentMethod, }) { switch(paymentPlatform.code) { case Constants.PAYMENT_METHOD_CODE_SQUARE: break; case Constants.PAYMENT_METHOD_CODE_CHASE: break; case Constants.PAYMENT_METHOD_CODE_PAYPAL: break; case Constants.PAYMENT_METHOD_CODE_OTT_ALIPAY: break; case Constants.PAYMENT_METHOD_CODE_OTT_WECHATPAY: break; case Constants.PAYMENT_METHOD_CODE_POD: break; case Constants.PAYMENT_METHOD_NATIVE_PAY: StripePayment.setOptions( StripeOptions(publishableKey: paymentPlatform.publishableKey, merchantId: paymentPlatform.merchantId, androidPayMode: paymentPlatform.publishableKey.contains('_test_') ? 'test' : 'production' ) ); StripePayment.paymentRequestWithNativePay( androidPayOptions: AndroidPayPaymentRequest( totalPrice: order.totalPrice.toStringAsFixed(2), currencyCode: order.businessInfo.currency, ), applePayOptions: ApplePayPaymentOptions( currencyCode: order.businessInfo.currency, countryCode: order.businessInfo.address.country, items: [ ApplePayItem( label: order.businessInfo.name, amount: order.totalPrice.toStringAsFixed(2), ), ], ), ).then((token) { print('return native token: ${token.tokenId}'); processNativePay(context, token, order); }).catchError((error) { print('native pay error: $error'); }); break; case Constants.PAYMENT_METHOD_CODE_STRIPE: Navigator.pushReplacement(context, MaterialPageRoute( builder: (BuildContext context) { return StripePay(order, paymentPlatform, stripePaymentMethod: stripePaymentMethod,); } )); break; } } static void processNativePay(BuildContext context, Token token, Order order) async { PaymentMethod paymentMethod = await StripePayment.createPaymentMethod( PaymentMethodRequest( card: CreditCard( token: token.tokenId, ), ), ); if (paymentMethod != null) { Utils.stripePaymentIntent(order, null, paymentMethod.id, paymentMethod.type, (response) { if (response.data['status'] == Constants.STRIPE_STATUS_REQUIRES_CONFIRMATION) { StripePayment.confirmPaymentIntent( PaymentIntent( clientSecret: response.data[Constants.STRIPE_CLIENT_SECRET], paymentMethodId: response.data['payment_method'], ), ).then((paymentIntentResult) { if (paymentIntentResult.status == Constants.STRIPE_STATUS_SUCCEDED) { Utils.stripeChargedSuccess(order, paymentMethod.id, paymentIntentResult.paymentIntentId, (response) { StripePayment.completeNativePayRequest().then((_) { eventBus.fire(OnOrderUpdated()); Routes.router.navigateTo(context, '/orderdetail/${order.id}', replace: true); }).catchError((error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); }); }, (error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); } ); } else { Utils.showMessageDialog(context, Exception('Unknown error')); } }).catchError((error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); }); } }, (error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); }); } else { } } static Future getBytesFromAsset(String path, int width) async { ByteData data = await rootBundle.load(path); ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width); ui.FrameInfo fi = await codec.getNextFrame(); return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List(); } }