You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
5.8 KiB
209 lines
5.8 KiB
import 'package:agora_rtm/agora_rtm.dart';
|
|
import 'package:agora_token_generator/agora_token_generator.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
|
|
import '../model/rtc/rtc_channel_data.dart';
|
|
import '../network/network_service.dart';
|
|
import '../network/rtc_api.dart';
|
|
|
|
/// 声网 RTM 管理器,聚合常用的客户端与 StreamChannel 操作
|
|
class RTMManager {
|
|
RTMManager._internal();
|
|
|
|
static final RTMManager _instance = RTMManager._internal();
|
|
factory RTMManager() => _instance;
|
|
static RTMManager get instance => _instance;
|
|
|
|
RtmClient? _client;
|
|
bool _isInitialized = false;
|
|
bool _isLoggedIn = false;
|
|
String? _currentAppId;
|
|
String? _currentUserId;
|
|
late NetworkService _networkService;
|
|
|
|
final Map<String, StreamChannel> _streamChannels = {};
|
|
|
|
/// 监听 link state
|
|
void Function(LinkStateEvent event)? onLinkStateEvent;
|
|
|
|
/// 监听消息事件(频道 / 主题)
|
|
void Function(MessageEvent event)? onMessageEvent;
|
|
|
|
/// 监听 presence 事件
|
|
void Function(PresenceEvent event)? onPresenceEvent;
|
|
|
|
/// 监听 topic 事件
|
|
void Function(TopicEvent event)? onTopicEvent;
|
|
|
|
/// 监听 lock 事件
|
|
void Function(LockEvent event)? onLockEvent;
|
|
|
|
/// 监听 storage 事件
|
|
void Function(StorageEvent event)? onStorageEvent;
|
|
|
|
/// 监听 token 事件
|
|
void Function(TokenEvent event)? onTokenEvent;
|
|
|
|
/// 统一错误通知
|
|
void Function(RtmStatus status)? onOperationError;
|
|
|
|
/// 创建/初始化 RTM Client
|
|
Future<bool> initialize({
|
|
required String appId,
|
|
required String userId,
|
|
RtmConfig? config,
|
|
}) async {
|
|
if (_isInitialized &&
|
|
_client != null &&
|
|
_currentAppId == appId &&
|
|
_currentUserId == userId) {
|
|
return true;
|
|
}
|
|
_networkService = Get.find<NetworkService>();
|
|
await dispose();
|
|
|
|
final (status, client) = await RTM(appId, userId, config: config);
|
|
print('RTM初始化成功');
|
|
|
|
if (status.error) {
|
|
onOperationError?.call(status);
|
|
return false;
|
|
}
|
|
_client = client;
|
|
_currentAppId = appId;
|
|
_currentUserId = userId;
|
|
_isInitialized = true;
|
|
_registerClientListeners();
|
|
final response = await _networkService.rtcApi.getSwRtmToken();
|
|
// 处理响应
|
|
if (response.data.isSuccess) {
|
|
await login(response.data.data?.token ?? '');
|
|
} else {
|
|
SmartDialog.showToast(response.data.message);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// 登录 RTM
|
|
Future<bool> login(String token) async {
|
|
print('RTM TOKEN:$token');
|
|
_ensureInitialized();
|
|
GetStorage storage = GetStorage();
|
|
String userId = storage.read('userId') ?? '';
|
|
print('RTM userId:$userId');
|
|
String tokens = RtmTokenBuilder.buildToken(
|
|
appId: '4c2ea9dcb4c5440593a418df0fdd512d',
|
|
appCertificate: '16f34b45181a4fae8acdb1a28762fcfa',
|
|
userId: userId,
|
|
tokenExpireSeconds: 3600,
|
|
);
|
|
print('RTM TOKEN:$tokens');
|
|
final (status, _) = await _client!.login(tokens);
|
|
final ok = _handleStatus(status);
|
|
print(ok ? 'RTM 登录成功' : 'RTM 登录失败');
|
|
if (ok) {
|
|
_isLoggedIn = true;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/// 创建频道
|
|
Future<bool> subscribe(String channelName) async {
|
|
_ensureInitialized();
|
|
final (status, _) = await _client!.subscribe(channelName);
|
|
print(!status.error ? 'RTM 订阅成功' : 'RTM 订阅失败');
|
|
return _handleStatus(status);
|
|
}
|
|
|
|
/// 取消订阅频道
|
|
Future<bool> unsubscribe(String channelName) async {
|
|
_ensureInitialized();
|
|
final (status, _) = await _client!.unsubscribe(channelName);
|
|
return _handleStatus(status);
|
|
}
|
|
|
|
/// 登出 RTM
|
|
Future<void> logout() async {
|
|
if (!_isInitialized || _client == null || !_isLoggedIn) return;
|
|
|
|
final (status, _) = await _client!.logout();
|
|
_handleStatus(status);
|
|
_isLoggedIn = false;
|
|
}
|
|
|
|
/// 刷新 Token
|
|
Future<bool> renewToken(String token) async {
|
|
_ensureInitialized();
|
|
final (status, _) = await _client!.renewToken(token);
|
|
return _handleStatus(status);
|
|
}
|
|
|
|
/// 发布频道文本消息
|
|
Future<bool> publishChannelMessage({
|
|
required String channelName,
|
|
required String message,
|
|
RtmChannelType channelType = RtmChannelType.message,
|
|
String? customType,
|
|
bool storeInHistory = false,
|
|
}) async {
|
|
_ensureInitialized();
|
|
final (status, _) = await _client!.publish(
|
|
channelName,
|
|
message,
|
|
channelType: channelType,
|
|
customType: customType,
|
|
storeInHistory: storeInHistory,
|
|
);
|
|
return _handleStatus(status);
|
|
}
|
|
|
|
/// 释放 RTM Client
|
|
Future<void> dispose() async {
|
|
if (_client != null && _isLoggedIn) {
|
|
final (status, _) = await _client!.logout();
|
|
_handleStatus(status);
|
|
}
|
|
await _client?.release();
|
|
|
|
_client = null;
|
|
_isInitialized = false;
|
|
_isLoggedIn = false;
|
|
_currentAppId = null;
|
|
_currentUserId = null;
|
|
_streamChannels.clear();
|
|
}
|
|
|
|
bool get isInitialized => _isInitialized;
|
|
bool get isLoggedIn => _isLoggedIn;
|
|
String? get currentUserId => _currentUserId;
|
|
Iterable<String> get joinedStreamChannels => _streamChannels.keys;
|
|
|
|
void _registerClientListeners() {
|
|
if (_client == null) return;
|
|
_client!.addListener(
|
|
linkState: (event) => onLinkStateEvent?.call(event),
|
|
message: (event) => onMessageEvent?.call(event),
|
|
presence: (event) => onPresenceEvent?.call(event),
|
|
topic: (event) => onTopicEvent?.call(event),
|
|
lock: (event) => onLockEvent?.call(event),
|
|
storage: (event) => onStorageEvent?.call(event),
|
|
token: (event) => onTokenEvent?.call(event),
|
|
);
|
|
}
|
|
|
|
bool _handleStatus(RtmStatus status) {
|
|
if (status.error) {
|
|
onOperationError?.call(status);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void _ensureInitialized() {
|
|
if (!_isInitialized || _client == null) {
|
|
throw Exception('RTM Client 未初始化,请先调用 initialize');
|
|
}
|
|
}
|
|
}
|