import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:eatvio_app/core/app_config.dart'; import 'package:eatvio_app/core/app_initializer.dart'; import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:flutter/services.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/token_storage.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/services.dart' show MethodChannel, PlatformException; import 'package:eatvio_app/core/error_reporting/error_reporter.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; class FirebaseApi { final ErrorReporter _errorReporter; final _firebaseMessaging = FirebaseMessaging.instance; FirebaseApi({required ErrorReporter errorReporter}) : _errorReporter = errorReporter; Future getToken() async { String? token; if (await _isHuaweiDevice()) { token = await _getHuaweiPushTokenNative().timeout( const Duration(seconds: 5), onTimeout: () => null, ); } else { if ((sl().buildType == AppBuildType.prod)) { try { await _firebaseMessaging.requestPermission().timeout( const Duration(seconds: 5), ); } catch (e) { // Разрешение не критично, продолжаем log('Failed to request Firebase messaging permission: $e'); } } token = await _firebaseMessaging.getToken().timeout( const Duration(seconds: 5), onTimeout: () => null, ); } return token; } Future registerAnonymousPushToken() async { try { final deviceInfo = DeviceInfoPlugin(); await sl().post( '/api/anonymous_token', data: { 'platform': Platform.isIOS ? 'ios' : (await _isHuaweiDevice()) ? 'huawei' : 'android', 'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id, 'token': await getToken(), }, ).timeout( const Duration(seconds: 10), onTimeout: () { throw TimeoutException('registerAnonymousPushToken timeout'); }, ); } catch (e, st) { log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); // Не пробрасываем ошибку дальше, так как это не критичная операция } } Future registerPushToken() async { if (await TokenStorage.haveTokenInStorage()) { try { final deviceInfo = DeviceInfoPlugin(); final isHuawei = Platform.isAndroid ? await _isHuaweiDevice() : false; await sl().post( '/api/profile/notification/token/register', data: { 'platform': Platform.isIOS ? 'ios' : (isHuawei ? 'huawei' : 'android'), 'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id, 'token': await getToken(), }, ).timeout( const Duration(seconds: 10), onTimeout: () { throw TimeoutException('registerPushToken timeout'); }, ); if (Platform.isIOS) { await _registerIosVoipPushToken(); } } catch (e, st) { log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); // Не пробрасываем ошибку дальше, так как это не критичная операция } } } Future unregisterPushTokens() async { if (!await TokenStorage.haveTokenInStorage()) return; try { await unregisterPushToken(); } catch (_) {} if (Platform.isIOS) { try { await _unregisterIosVoipPushToken(); } catch (_) {} } } Future unregisterPushToken() async { if (!await TokenStorage.haveTokenInStorage()) return; try { final isHuawei = Platform.isAndroid ? await _isHuaweiDevice() : false; final platform = Platform.isIOS ? 'ios' : (isHuawei ? 'huawei' : 'android'); final token = (await getToken()) ?? ''; await sl().post( '/api/profile/notification/token/unregister', data: { 'platform': platform, 'token': token, }, ).timeout( const Duration(seconds: 10), onTimeout: () { throw TimeoutException('unregisterPushToken timeout'); }, ); } catch (e, st) { log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); } } Future _registerIosVoipPushToken() async { try { final token = (await FlutterCallkitIncoming.getDevicePushTokenVoIP())?.trim(); if (token == null || token.isEmpty) return; final deviceInfo = DeviceInfoPlugin(); await sl().post( '/api/profile/notification/token/voip/register', data: { 'platform': 'ios', 'hw_id': (await deviceInfo.iosInfo).identifierForVendor, 'token': token, }, ).timeout( const Duration(seconds: 10), onTimeout: () { throw TimeoutException('registerPushToken voip timeout'); }, ); } catch (e, st) { log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); } } Future _unregisterIosVoipPushToken() async { try { final token = (await FlutterCallkitIncoming.getDevicePushTokenVoIP())?.trim() ?? ''; await sl().post( '/api/profile/notification/token/voip/unregister', data: { 'token': token, }, ).timeout( const Duration(seconds: 10), onTimeout: () { throw TimeoutException('unregisterPushToken voip timeout'); }, ); } catch (e, st) { log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); } } Future _isHuaweiDevice() async { if (!Platform.isAndroid) return false; try { final info = await DeviceInfoPlugin().androidInfo; final brand = info.brand.toLowerCase(); final manufacturer = info.manufacturer.toLowerCase(); return brand.contains('huawei') || brand.contains('honor') || manufacturer.contains('huawei') || manufacturer.contains('honor'); } catch (_) { return false; } } Future _getHuaweiPushTokenNative() async { try { const channel = MethodChannel('com.eatvioapp/hms_push'); final String? token = await channel.invokeMethod('getHuaweiToken'); return token; } on PlatformException catch (e, st) { showErrorToast('$e $st'); log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); return null; } catch (e, st) { showErrorToast('$e $st'); log(e.toString(), stackTrace: st); await _errorReporter.report(e, st, origin: 'FirebaseApi'); return null; } } }