fix:第一轮跑
This commit is contained in:
1 parent
e834ec7586
commit
33235f450b
56 files changed
+4357
-210
No files matched your search
@@ -0,0 +1,95 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../network/dio_provider.dart';
|
||||
import 'notification_permission.dart';
|
||||
import 'umeng_push_service.dart';
|
||||
|
||||
class DevicePushRegistrar {
|
||||
DevicePushRegistrar(this._read);
|
||||
final T Function<T>(ProviderListenable<T>) _read;
|
||||
StreamSubscription<String>? _subscription;
|
||||
String? _lastToken;
|
||||
bool? _lastPermission;
|
||||
int _attempt = 0;
|
||||
|
||||
void start() {
|
||||
_subscription ??= UmengPushService.onDeviceToken.listen(_register);
|
||||
UmengPushService.emitCachedToken();
|
||||
}
|
||||
|
||||
Future<void> stop() async => _subscription?.cancel();
|
||||
Future<void> refresh() => UmengPushService.refreshDeviceToken();
|
||||
void forceRegisterCurrentToken() {
|
||||
_lastToken = null;
|
||||
_lastPermission = null;
|
||||
UmengPushService.emitCachedToken();
|
||||
}
|
||||
|
||||
Future<void> _register(String token) async {
|
||||
final permission = await NotificationPermission.isGranted();
|
||||
if (token == _lastToken && permission == _lastPermission) return;
|
||||
try {
|
||||
final package = await PackageInfo.fromPlatform();
|
||||
await _read(dioProvider).post<List<int>>(
|
||||
'/api/v1/push/devices',
|
||||
data: _PushRegisterRequest(
|
||||
token: token,
|
||||
platform: defaultTargetPlatform == TargetPlatform.iOS ? 1 : 2,
|
||||
version: package.version,
|
||||
permission: permission,
|
||||
).toBytes(),
|
||||
options: protobufOptions(),
|
||||
);
|
||||
_lastToken = token;
|
||||
_lastPermission = permission;
|
||||
_attempt = 0;
|
||||
} catch (_) {
|
||||
if (_attempt++ >= 3) return;
|
||||
await Future<void>.delayed(Duration(seconds: 1 << (_attempt - 1)));
|
||||
await _register(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _PushRegisterRequest {
|
||||
const _PushRegisterRequest({
|
||||
required this.token,
|
||||
required this.platform,
|
||||
required this.version,
|
||||
required this.permission,
|
||||
});
|
||||
final String token;
|
||||
final int platform;
|
||||
final String version;
|
||||
final bool permission;
|
||||
Uint8List toBytes() {
|
||||
final bytes = BytesBuilder(copy: false);
|
||||
void putInt(int value) {
|
||||
while (value > 0x7f) {
|
||||
bytes.addByte((value & 0x7f) | 0x80);
|
||||
value >>= 7;
|
||||
}
|
||||
bytes.addByte(value);
|
||||
}
|
||||
|
||||
void putString(int tag, String value) {
|
||||
final raw = value.codeUnits;
|
||||
putInt((tag << 3) | 2);
|
||||
putInt(raw.length);
|
||||
bytes.add(raw);
|
||||
}
|
||||
|
||||
putString(1, token);
|
||||
putInt(2 << 3);
|
||||
putInt(platform);
|
||||
putString(3, version);
|
||||
putInt(4 << 3);
|
||||
putInt(permission ? 1 : 0);
|
||||
return bytes.toBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import '../storage/storage_keys.dart';
|
||||
import '../storage/storage_service.dart';
|
||||
import '../widgets/notification_permission_tip.dart';
|
||||
|
||||
abstract final class NotificationPermission {
|
||||
static Future<bool> isGranted() async =>
|
||||
!kIsWeb && (await Permission.notification.status).isGranted;
|
||||
|
||||
/// 对齐研听:Android 13+ 由应用解释后主动申请;iOS 由 U-Push 注册 APNs
|
||||
/// 时触发系统授权,绝不展示 Android 的自定义顶部提示。
|
||||
static Future<bool> requestIfNeeded(BuildContext context) async {
|
||||
if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) {
|
||||
return isGranted();
|
||||
}
|
||||
final android = await DeviceInfoPlugin().androidInfo;
|
||||
if (android.version.sdkInt < 33 ||
|
||||
StorageService.to.getBool(storagePushPermissionPrompted)) {
|
||||
return isGranted();
|
||||
}
|
||||
if (await isGranted() || !context.mounted) return true;
|
||||
await showNotificationPermissionTip(context);
|
||||
await StorageService.to.setBool(storagePushPermissionPrompted, true);
|
||||
return (await Permission.notification.request()).isGranted;
|
||||
}
|
||||
|
||||
/// 设置页不使用顶部说明;iOS/Android 都直接请求系统授权。
|
||||
static Future<bool> requestFromSettings() async {
|
||||
if (kIsWeb) return false;
|
||||
final current = await Permission.notification.status;
|
||||
if (current.isGranted) return true;
|
||||
await StorageService.to.setBool(storagePushPermissionPrompted, true);
|
||||
return (await Permission.notification.request()).isGranted;
|
||||
}
|
||||
|
||||
static Future<bool> openSystemSettings() => openAppSettings();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:umeng_common_sdk/umeng_common_sdk.dart';
|
||||
import 'package:umeng_push_sdk/umeng_push_sdk.dart';
|
||||
|
||||
import '../config/umeng_config.dart';
|
||||
import '../privacy/privacy_gate.dart';
|
||||
import '../services/device_header_service.dart';
|
||||
import '../storage/storage_keys.dart';
|
||||
import '../storage/storage_service.dart';
|
||||
|
||||
abstract final class UmengPushService {
|
||||
static final _tokens = StreamController<String>.broadcast();
|
||||
static Future<void>? _starting;
|
||||
static bool _started = false;
|
||||
static void Function(String payload)? _onNotificationOpen;
|
||||
static String? _pendingNotificationOpen;
|
||||
|
||||
static Stream<String> get onDeviceToken => _tokens.stream;
|
||||
|
||||
static void configureNotificationOpen(
|
||||
void Function(String payload) callback,
|
||||
) {
|
||||
_onNotificationOpen = callback;
|
||||
final pending = _pendingNotificationOpen;
|
||||
if (pending != null) {
|
||||
_pendingNotificationOpen = null;
|
||||
callback(pending);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> startIfAllowed() {
|
||||
if (kIsWeb ||
|
||||
_started ||
|
||||
requiresAndroidPrivacyConsent(
|
||||
isWeb: kIsWeb,
|
||||
platform: defaultTargetPlatform,
|
||||
privacyAgreed: StorageService.to.getBool(storagePrivacyAgreed),
|
||||
)) {
|
||||
return Future.value();
|
||||
}
|
||||
return _starting ??= _start().whenComplete(() => _starting = null);
|
||||
}
|
||||
|
||||
static Future<void> _start() async {
|
||||
await DeviceHeaderService.to.ensureInitialized();
|
||||
await UmengCommonSdk.initCommon(
|
||||
UmengConfig.androidAppKey,
|
||||
UmengConfig.iosAppKey,
|
||||
DeviceHeaderService.to.commonHeaders()['User-Channel'] ?? 'test',
|
||||
UmengConfig.androidPushSecret,
|
||||
);
|
||||
UmengPushSdk.setTokenCallback(_emit);
|
||||
UmengPushSdk.setNotificationCallback((_) {}, (payload) {
|
||||
final callback = _onNotificationOpen;
|
||||
if (callback == null) {
|
||||
_pendingNotificationOpen = payload;
|
||||
} else {
|
||||
callback(payload);
|
||||
}
|
||||
});
|
||||
await UmengPushSdk.register(
|
||||
UmengConfig.iosAppKey,
|
||||
DeviceHeaderService.to.commonHeaders()['User-Channel'] ?? 'test',
|
||||
);
|
||||
if (Platform.isAndroid) await UmengPushSdk.setPushEnable(true);
|
||||
_started = true;
|
||||
await refreshDeviceToken();
|
||||
}
|
||||
|
||||
static Future<void> refreshDeviceToken() async {
|
||||
if (!_started || kIsWeb) return;
|
||||
_emit(await UmengPushSdk.getRegisteredId());
|
||||
}
|
||||
|
||||
static void emitCachedToken() =>
|
||||
_emit(StorageService.to.getString(storageDeviceToken));
|
||||
|
||||
static void _emit(String? token) {
|
||||
if (token == null || token.isEmpty) return;
|
||||
_tokens.add(token);
|
||||
unawaited(StorageService.to.setString(storageDeviceToken, token));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user