import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; import '../../models/pb/jinzhi/v1/push.pb.dart' as pb; import '../network/api_exception.dart'; import 'notification_permission.dart'; import 'push_api_client.dart'; import 'umeng_push_service.dart'; class DevicePushRegistrar { DevicePushRegistrar(this._read); final T Function(ProviderListenable) _read; StreamSubscription? _subscription; String? _lastToken; bool? _lastPermission; Future _registrationQueue = Future.value(); void start() { _subscription ??= UmengPushService.onDeviceToken.listen(_enqueue); UmengPushService.emitCachedToken(); } Future stop() async => _subscription?.cancel(); Future refresh() => UmengPushService.refreshDeviceToken(); void forceRegisterCurrentToken() { // BLOCKED(server contract): guest registration is intentionally preserved. // Do not add a client-side unbind call until the backend confirms whether // guest registration reliably clears the previous user binding. _lastToken = null; _lastPermission = null; UmengPushService.emitCachedToken(); } void _enqueue(String token) { _registrationQueue = _registrationQueue .catchError((_) {}) .then((_) => _register(token)); } Future _register(String token) async { final permission = await NotificationPermission.isGranted(); if (token == _lastToken && permission == _lastPermission) return; final package = await PackageInfo.fromPlatform(); final request = pb.RegisterPushDeviceRequest( umengDeviceToken: token, platform: defaultTargetPlatform == TargetPlatform.iOS ? pb.Platform.PLATFORM_IOS : pb.Platform.PLATFORM_ANDROID, appVersion: package.version, permissionGranted: permission, ); for (var attempt = 0; attempt < 4; attempt++) { try { await _read(pushApiClientProvider).registerDevice(request); _lastToken = token; _lastPermission = permission; return; } catch (error) { if (attempt == 3 || !ApiException.from(error).isRetryable) return; await Future.delayed(Duration(seconds: 1 << attempt)); } } } }