麦克风开关修复

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