麦克风开关修复

This commit is contained in:
roxy 2026-06-03 14:28:43 +08:00
parent 732e2bfcfb
commit 67baeb15f3
2 changed files with 161 additions and 18 deletions

View File

@ -436,6 +436,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
int? _pendingSelfMicSwitchGuardUntilMs;
num? _pendingSelfMicReleaseIndex;
int? _pendingSelfMicReleaseGuardUntilMs;
final Set<String> _confirmedSelfMicIdentityKeys = <String>{};
int? _seatResizeOccupantPreserveUntilMs;
_RoomRtcLocalAudioRole? _lastAppliedLocalAudioRole;
bool? _lastAppliedLocalAudioMuted;
@ -529,6 +530,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
currentUser?.id?.trim() ?? "",
currentUser?.account?.trim() ?? "",
currentUser?.getID().trim() ?? "",
..._confirmedSelfMicIdentityKeys,
}..removeWhere((item) => item.isEmpty);
}
@ -550,6 +552,16 @@ class RealTimeCommunicationManager extends ChangeNotifier {
return _userProfileIdentityKeys(user).any(identityKeys.contains);
}
void _rememberConfirmedSelfMicIdentity(SocialChatUserProfile? user) {
_confirmedSelfMicIdentityKeys
..clear()
..addAll(_userProfileIdentityKeys(user));
}
void _clearConfirmedSelfMicIdentity() {
_confirmedSelfMicIdentityKeys.clear();
}
///
List<SCRoomRedPacketListRes> redPacketList = [];
@ -2522,7 +2534,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
notifyListeners();
final currentUserId =
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
final seatIndex = userOnMaiInIndex(currentUserId);
final seatIndex =
currentUserId.isNotEmpty
? userOnMaiInIndex(currentUserId)
: (_currentUserMicSeat()?.micIndex ?? -1);
if (seatIndex < 0) {
notifyListeners();
return;
@ -2545,14 +2560,18 @@ class RealTimeCommunicationManager extends ChangeNotifier {
Future<void> handleSelfMicRemovedByRemote({
bool refreshMicList = true,
}) async {
final currentUserKeys = _currentUserIdentityKeys();
final currentUserId =
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
if (currentUserId.isEmpty) {
if (currentUserKeys.isEmpty) {
await _switchRoomRtcToAudience();
return;
}
final currentSeatIndex = userOnMaiInIndex(currentUserId);
final currentSeatIndex =
currentUserId.isNotEmpty
? userOnMaiInIndex(currentUserId)
: (_currentUserMicSeat()?.micIndex ?? -1);
isMic = true;
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
@ -2561,9 +2580,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
} else {
_clearSelfMicReleaseGuard();
}
_clearUserFromSeats(currentUserId);
_clearCurrentUserFromSeats();
_roomRtcRoleIsBroadcaster = false;
_syncSelfMicRuntimeState();
_clearConfirmedSelfMicIdentity();
notifyListeners();
await _switchRoomRtcToAudience();
@ -5049,6 +5069,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
_clearSelfMicReleaseGuard();
_clearConfirmedSelfMicIdentity();
_seatResizeOccupantPreserveUntilMs = null;
roomIsMute = false;
rtmProvider?.roomAllMsgList.clear();
@ -5207,16 +5228,21 @@ class RealTimeCommunicationManager extends ChangeNotifier {
).catchError((_) {});
}
void _clearUserFromSeats(String? userId, {num? exceptIndex}) {
final normalizedUserId = (userId ?? "").trim();
if (normalizedUserId.isEmpty) {
void _clearCurrentUserFromSeats({num? exceptIndex}) {
_clearUsersFromSeatsByIdentityKeys(
_currentUserIdentityKeys(),
exceptIndex: exceptIndex,
);
}
void _clearUsersFromSeatsByIdentityKeys(
Set<String> lookupKeys, {
num? exceptIndex,
}) {
lookupKeys.removeWhere((item) => item.trim().isEmpty);
if (lookupKeys.isEmpty) {
return;
}
final lookupKeys = <String>{normalizedUserId};
final currentUserKeys = _currentUserIdentityKeys();
if (currentUserKeys.contains(normalizedUserId)) {
lookupKeys.addAll(currentUserKeys);
}
roomWheatMap.forEach((seatIndex, seat) {
final seatUserKeys = _userProfileIdentityKeys(seat.user);
if (!seatUserKeys.any(lookupKeys.contains) || seatIndex == exceptIndex) {
@ -5232,6 +5258,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
required SCMicGoUpRes micGoUpRes,
required bool selfMicMuted,
}) {
_rememberConfirmedSelfMicIdentity(myUser);
final previousSelfSeatIndex = userOnMaiInIndex(myUser.id ?? "");
final isSeatSwitching =
previousSelfSeatIndex > -1 && previousSelfSeatIndex != targetIndex;
@ -5246,7 +5273,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_clearSelfMicSwitchGuard();
_startSelfMicGoUpGuard(targetIndex: targetIndex);
}
_clearUserFromSeats(myUser.id, exceptIndex: targetIndex);
_clearCurrentUserFromSeats(exceptIndex: targetIndex);
final currentSeat = roomWheatMap[targetIndex];
final currentUser = myUser.copyWith(roles: currenRoom?.entrants?.roles);
@ -5269,6 +5296,82 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_syncSelfMicRuntimeState();
}
SocialChatUserProfile? _resolveSelfMicGoUpUser({
required SocialChatUserProfile? localUser,
required SCMicGoUpRes micGoUpRes,
}) {
final responseUser = _profileFromMicGoUpUser(micGoUpRes.user);
if (localUser == null) {
return responseUser;
}
if (responseUser == null) {
return localUser.copyWith(roles: currenRoom?.entrants?.roles);
}
return localUser.copyWith(
id: _firstNonBlankText([localUser.id, responseUser.id]),
account: _firstNonBlankText([localUser.account, responseUser.account]),
userAvatar: _firstNonBlankText([
localUser.userAvatar,
responseUser.userAvatar,
]),
userNickname: _firstNonBlankText([
localUser.userNickname,
responseUser.userNickname,
]),
roles: _firstNonBlankText([
localUser.roles,
responseUser.roles,
currenRoom?.entrants?.roles,
]),
charmLevel: localUser.charmLevel ?? responseUser.charmLevel,
heartbeatVal: localUser.heartbeatVal ?? responseUser.heartbeatVal,
userSex: localUser.userSex ?? responseUser.userSex,
);
}
SocialChatUserProfile? _profileFromMicGoUpUser(User? user) {
if (user == null) {
return null;
}
final id = _firstNonBlankText([user.id]);
final account = _firstNonBlankText([user.account]);
if (id == null && account == null) {
return null;
}
return SocialChatUserProfile(
id: id,
account: account,
charmLevel: user.charmLevel,
heartbeatVal: user.heartbeatVal,
roles: _firstNonBlankText([user.roles, currenRoom?.entrants?.roles]),
userAvatar: _firstNonBlankText([user.userAvatar]),
userNickname: _firstNonBlankText([user.userNickname, user.account]),
userSex: user.userSex,
);
}
@visibleForTesting
void debugApplySelfMicGoUpResponse({
SocialChatUserProfile? localUser,
required num targetIndex,
required SCMicGoUpRes micGoUpRes,
}) {
final selfUser = _resolveSelfMicGoUpUser(
localUser: localUser,
micGoUpRes: micGoUpRes,
);
if (selfUser == null) {
return;
}
_applySelfMicGoUpState(
myUser: selfUser,
targetIndex: targetIndex,
micGoUpRes: micGoUpRes,
selfMicMuted: micGoUpRes.micMute ?? false,
);
notifyListeners();
}
void _openRoomUserInfoCard(String? userId) {
final normalizedUserId = (userId ?? '').trim();
if (normalizedUserId.isEmpty) {
@ -5332,9 +5435,13 @@ class RealTimeCommunicationManager extends ChangeNotifier {
final targetIndex = micGoUpRes.micIndex ?? index;
final selfMicMuted = micGoUpRes.micMute ?? false;
if (myUser != null) {
final selfUser = _resolveSelfMicGoUpUser(
localUser: myUser,
micGoUpRes: micGoUpRes,
);
if (selfUser != null) {
_applySelfMicGoUpState(
myUser: myUser,
myUser: selfUser,
targetIndex: targetIndex,
micGoUpRes: micGoUpRes,
selfMicMuted: selfMicMuted,
@ -5392,8 +5499,15 @@ class RealTimeCommunicationManager extends ChangeNotifier {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id ?? "";
final currentUserSeatIndex = userOnMaiInIndex(currentUserId);
if (roomWheatMap[index]?.user?.id == currentUserId) {
final currentUserKeys = _currentUserIdentityKeys();
final currentUserSeatIndex =
currentUserId.trim().isNotEmpty
? userOnMaiInIndex(currentUserId)
: (_currentUserMicSeat()?.micIndex ?? -1);
if (_userProfileMatchesAnyIdentity(
roomWheatMap[index]?.user,
currentUserKeys,
)) {
isMic = true;
}
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
@ -5406,8 +5520,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
SCHeartbeatUtils.cancelAnchorTimer();
await _switchRoomRtcToAudience();
_clearUserFromSeats(currentUserId);
_clearCurrentUserFromSeats();
_syncSelfMicRuntimeState();
_clearConfirmedSelfMicIdentity();
notifyListeners();
requestMicrophoneListRefresh(
notifyIfUnchanged: false,

View File

@ -3,6 +3,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
import 'package:yumi/shared/business_logic/models/res/mic_res.dart';
import 'package:yumi/shared/business_logic/models/res/sc_mic_go_up_res.dart'
as mic_go_up;
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
void main() {
@ -55,4 +57,30 @@ void main() {
expect(rtcProvider.isOnMaiInIndex(2), isTrue);
expect(rtcProvider.userOnMaiInIndex('user-1'), 2);
});
test('self go-up uses response user when local profile is unavailable', () {
SharedPreferences.setMockInitialValues({});
final rtcProvider =
RealTimeCommunicationManager()..roomWheatMap = {4: MicRes(micIndex: 4)};
rtcProvider.debugApplySelfMicGoUpResponse(
targetIndex: 4,
micGoUpRes: mic_go_up.SCMicGoUpRes(
micIndex: 4,
micMute: false,
micLock: false,
roomToken: 'token',
user: mic_go_up.User(
id: 'user-android',
account: 'android-account',
userNickname: 'Android User',
),
),
);
expect(rtcProvider.isOnMai(), isTrue);
expect(rtcProvider.isOnMaiInIndex(4), isTrue);
expect(rtcProvider.roomWheatMap[4]?.user?.id, 'user-android');
expect(rtcProvider.roomWheatMap[4]?.user?.account, 'android-account');
});
}