麦位bug
This commit is contained in:
parent
78c3a95160
commit
1b4d0c40c6
@ -502,7 +502,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_TEAM = S9X2AJ2US9;
|
DEVELOPMENT_TEAM = S9X2AJ2US9;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -511,7 +511,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.2;
|
MARKETING_VERSION = 1.2.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -693,7 +693,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_TEAM = F33K8VUZ62;
|
DEVELOPMENT_TEAM = F33K8VUZ62;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -702,7 +702,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.2;
|
MARKETING_VERSION = 1.2.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -722,7 +722,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_TEAM = F33K8VUZ62;
|
DEVELOPMENT_TEAM = F33K8VUZ62;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -731,7 +731,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.2;
|
MARKETING_VERSION = 1.2.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
const double roomGameDesignWidth = 750;
|
const double roomGameDesignWidth = 750;
|
||||||
@ -64,79 +62,115 @@ String resolveRoomGameScreenMode(
|
|||||||
return fullScreen ? 'full' : 'half';
|
return fullScreen ? 'full' : 'half';
|
||||||
}
|
}
|
||||||
|
|
||||||
String buildRoomGameNativeFitScript({
|
double resolveRoomGameCompatibilityBottomHeight(BuildContext context) {
|
||||||
required double visibleHeight,
|
final mediaQuery = MediaQuery.maybeOf(context);
|
||||||
required double expectedHeight,
|
if (mediaQuery == null) {
|
||||||
}) {
|
|
||||||
final options = jsonEncode(<String, dynamic>{
|
|
||||||
'visibleHeight': _roundMetric(visibleHeight),
|
|
||||||
'expectedHeight': _roundMetric(expectedHeight),
|
|
||||||
});
|
|
||||||
return '''
|
|
||||||
(function() {
|
|
||||||
const options = $options;
|
|
||||||
const fitKey = '__roomGameNativeViewportFit';
|
|
||||||
|
|
||||||
function applyFit() {
|
|
||||||
try {
|
|
||||||
const visibleHeight = Number(options.visibleHeight) || window.innerHeight || 0;
|
|
||||||
const expectedHeight = Number(options.expectedHeight) || 0;
|
|
||||||
if (!document.body || !visibleHeight || !expectedHeight) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawScale = visibleHeight / expectedHeight;
|
|
||||||
const scale = Math.min(1, rawScale);
|
|
||||||
window[fitKey] = {
|
|
||||||
scale: scale,
|
|
||||||
visibleHeight: visibleHeight,
|
|
||||||
expectedHeight: expectedHeight,
|
|
||||||
compact: scale < 0.999
|
|
||||||
};
|
|
||||||
|
|
||||||
if (scale >= 0.999) {
|
|
||||||
document.documentElement.style.overflow = '';
|
|
||||||
document.body.style.overflow = '';
|
|
||||||
document.body.style.transform = '';
|
|
||||||
document.body.style.transformOrigin = '';
|
|
||||||
document.body.style.width = '';
|
|
||||||
document.body.style.minHeight = '';
|
|
||||||
document.documentElement.removeAttribute('data-room-game-native-fit');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.setAttribute('data-room-game-native-fit', 'true');
|
|
||||||
document.documentElement.style.overflow = 'hidden';
|
|
||||||
document.body.style.overflow = 'hidden';
|
|
||||||
document.body.style.transform = 'scale(' + scale + ')';
|
|
||||||
document.body.style.transformOrigin = 'top left';
|
|
||||||
document.body.style.width = (100 / scale) + '%';
|
|
||||||
document.body.style.minHeight = expectedHeight + 'px';
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', applyFit, { once: true });
|
|
||||||
} else {
|
|
||||||
applyFit();
|
|
||||||
}
|
|
||||||
[50, 250, 800, 1600, 3000].forEach(function(delay) {
|
|
||||||
window.setTimeout(applyFit, delay);
|
|
||||||
});
|
|
||||||
if (!window.__roomGameNativeFitResizeReady) {
|
|
||||||
window.__roomGameNativeFitResizeReady = true;
|
|
||||||
window.addEventListener('resize', applyFit, true);
|
|
||||||
window.addEventListener('orientationchange', function() {
|
|
||||||
window.setTimeout(applyFit, 120);
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
|
|
||||||
double _roundMetric(double value) {
|
|
||||||
if (!value.isFinite) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return double.parse(value.toStringAsFixed(2));
|
final isClassicScreen = mediaQuery.padding.bottom <= 0.5;
|
||||||
|
if (!isClassicScreen) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (mediaQuery.size.width * 0.19).clamp(56.0, 88.0).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomGameCompatibilityBottomFrame extends StatelessWidget {
|
||||||
|
const RoomGameCompatibilityBottomFrame({super.key, required this.height});
|
||||||
|
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (height <= 0) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: height,
|
||||||
|
width: double.infinity,
|
||||||
|
child: CustomPaint(painter: _RoomGameBottomFramePainter()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RoomGameBottomFramePainter extends CustomPainter {
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final radius = Radius.circular(size.height * 0.18);
|
||||||
|
final frameRect = RRect.fromRectAndCorners(
|
||||||
|
Offset.zero & size,
|
||||||
|
topLeft: radius,
|
||||||
|
topRight: radius,
|
||||||
|
);
|
||||||
|
|
||||||
|
final backgroundPaint =
|
||||||
|
Paint()
|
||||||
|
..shader = const LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: <Color>[Color(0xFF0E392E), Color(0xFF082A23)],
|
||||||
|
).createShader(Offset.zero & size);
|
||||||
|
canvas.drawRRect(frameRect, backgroundPaint);
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
canvas.clipRRect(frameRect);
|
||||||
|
final patternPaint =
|
||||||
|
Paint()
|
||||||
|
..color = const Color(0xFF2C5B4A).withValues(alpha: 0.20)
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1.5;
|
||||||
|
final motifWidth = size.width / 8;
|
||||||
|
final motifHeight = size.height / 2.4;
|
||||||
|
for (double x = -motifWidth; x < size.width + motifWidth; x += motifWidth) {
|
||||||
|
for (
|
||||||
|
double y = -motifHeight * 0.2;
|
||||||
|
y < size.height + motifHeight;
|
||||||
|
y += motifHeight * 0.72
|
||||||
|
) {
|
||||||
|
final rect = Rect.fromCenter(
|
||||||
|
center: Offset(x + motifWidth * 0.52, y + motifHeight * 0.55),
|
||||||
|
width: motifWidth * 0.9,
|
||||||
|
height: motifHeight,
|
||||||
|
);
|
||||||
|
canvas.drawArc(rect, -0.35, 4.5, false, patternPaint);
|
||||||
|
canvas.drawArc(
|
||||||
|
rect.translate(motifWidth * 0.34, 0),
|
||||||
|
2.75,
|
||||||
|
3.6,
|
||||||
|
false,
|
||||||
|
patternPaint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
|
final borderPaint =
|
||||||
|
Paint()
|
||||||
|
..color = const Color(0xFFE9D58A)
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 3;
|
||||||
|
final innerBorderPaint =
|
||||||
|
Paint()
|
||||||
|
..color = const Color(0xFF8F7136).withValues(alpha: 0.75)
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1;
|
||||||
|
|
||||||
|
canvas.drawRRect(frameRect.deflate(1.5), borderPaint);
|
||||||
|
canvas.drawRRect(frameRect.deflate(5), innerBorderPaint);
|
||||||
|
|
||||||
|
final highlightPaint =
|
||||||
|
Paint()
|
||||||
|
..shader = const LinearGradient(
|
||||||
|
colors: <Color>[
|
||||||
|
Color(0x00F6E8A7),
|
||||||
|
Color(0x99F6E8A7),
|
||||||
|
Color(0x00F6E8A7),
|
||||||
|
],
|
||||||
|
).createShader(Rect.fromLTWH(0, 0, size.width, 3));
|
||||||
|
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, 3), highlightPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,6 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
|
|||||||
_didFinishPageLoad = true;
|
_didFinishPageLoad = true;
|
||||||
_log('page_finished url=${_clip(url, 240)}');
|
_log('page_finished url=${_clip(url, 240)}');
|
||||||
await _injectBridge(reason: 'page_finished');
|
await _injectBridge(reason: 'page_finished');
|
||||||
await _applyNativeViewportFit(reason: 'page_finished');
|
|
||||||
},
|
},
|
||||||
onWebResourceError: (WebResourceError error) {
|
onWebResourceError: (WebResourceError error) {
|
||||||
_log(
|
_log(
|
||||||
@ -274,7 +273,6 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
|
|||||||
jsCallbackPath: jsCallbackPath,
|
jsCallbackPath: jsCallbackPath,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await _applyNativeViewportFit(reason: 'send_config');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
_log('send_config_error error=${_clip(error.toString(), 300)}');
|
_log('send_config_error error=${_clip(error.toString(), 300)}');
|
||||||
}
|
}
|
||||||
@ -332,7 +330,6 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
|
|||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
});
|
});
|
||||||
await _applyNativeViewportFit(reason: 'game_loaded');
|
|
||||||
break;
|
break;
|
||||||
case BaishunBridgeActions.gameRecharge:
|
case BaishunBridgeActions.gameRecharge:
|
||||||
_log('game_recharge open_wallet');
|
_log('game_recharge open_wallet');
|
||||||
@ -636,35 +633,12 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
|
|||||||
return screenSize.width / ratio;
|
return screenSize.width / ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _applyNativeViewportFit({String reason = 'manual'}) async {
|
|
||||||
final topCrop = 28.w;
|
|
||||||
final visibleHeight = _calculateWebViewHeight(context) + topCrop;
|
|
||||||
final expectedHeight = _calculatePreferredWebViewHeight(context);
|
|
||||||
if (expectedHeight <= visibleHeight + 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_log(
|
|
||||||
'native_fit reason=$reason visible=${visibleHeight.toStringAsFixed(1)} '
|
|
||||||
'expected=${expectedHeight.toStringAsFixed(1)}',
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await _controller.runJavaScript(
|
|
||||||
buildRoomGameNativeFitScript(
|
|
||||||
visibleHeight: visibleHeight,
|
|
||||||
expectedHeight: expectedHeight,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
_log(
|
|
||||||
'native_fit_error reason=$reason error=${_clip(error.toString(), 300)}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final topCrop = 28.w;
|
final topCrop = 28.w;
|
||||||
final webViewHeight = _calculateWebViewHeight(context);
|
final webViewHeight = _calculateWebViewHeight(context);
|
||||||
|
final bottomFrameHeight = resolveRoomGameCompatibilityBottomHeight(context);
|
||||||
|
final dialogHeight = webViewHeight + bottomFrameHeight;
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvokedWithResult: (bool didPop, Object? result) async {
|
onPopInvokedWithResult: (bool didPop, Object? result) async {
|
||||||
@ -684,7 +658,7 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
|
|||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: ScreenUtil().screenWidth,
|
width: ScreenUtil().screenWidth,
|
||||||
height: webViewHeight,
|
height: dialogHeight,
|
||||||
color: const Color(0xFF081915),
|
color: const Color(0xFF081915),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -692,12 +666,22 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
|
|||||||
top: -topCrop,
|
top: -topCrop,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: bottomFrameHeight,
|
||||||
child: WebViewWidget(
|
child: WebViewWidget(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
gestureRecognizers: _webGestureRecognizers,
|
gestureRecognizers: _webGestureRecognizers,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (bottomFrameHeight > 0)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
height: bottomFrameHeight,
|
||||||
|
child: RoomGameCompatibilityBottomFrame(
|
||||||
|
height: bottomFrameHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (_errorMessage != null) _buildErrorState(),
|
if (_errorMessage != null) _buildErrorState(),
|
||||||
if (_isLoading && _errorMessage == null)
|
if (_isLoading && _errorMessage == null)
|
||||||
const IgnorePointer(
|
const IgnorePointer(
|
||||||
|
|||||||
@ -219,7 +219,6 @@ class _LeaderGamePageState extends State<LeaderGamePage> {
|
|||||||
await _controller.runJavaScript(
|
await _controller.runJavaScript(
|
||||||
LeaderJsBridge.bootstrapScript(launchPayload: _buildLaunchPayload()),
|
LeaderJsBridge.bootstrapScript(launchPayload: _buildLaunchPayload()),
|
||||||
);
|
);
|
||||||
await _applyNativeViewportFit(reason: reason);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
_log(
|
_log(
|
||||||
'inject_bridge_error reason=$reason error=${_clip(error.toString(), 300)}',
|
'inject_bridge_error reason=$reason error=${_clip(error.toString(), 300)}',
|
||||||
@ -387,35 +386,6 @@ class _LeaderGamePageState extends State<LeaderGamePage> {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _applyNativeViewportFit({String reason = 'manual'}) async {
|
|
||||||
final screenMode = _resolveScreenMode();
|
|
||||||
if (screenMode == 'full') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final headerHeight = 44.w;
|
|
||||||
final visibleHeight = _calculateBodyHeight(context, headerHeight);
|
|
||||||
final expectedHeight = _calculatePreferredBodyHeight(context);
|
|
||||||
if (expectedHeight <= visibleHeight + 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_log(
|
|
||||||
'native_fit reason=$reason visible=${visibleHeight.toStringAsFixed(1)} '
|
|
||||||
'expected=${expectedHeight.toStringAsFixed(1)}',
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await _controller.runJavaScript(
|
|
||||||
buildRoomGameNativeFitScript(
|
|
||||||
visibleHeight: visibleHeight,
|
|
||||||
expectedHeight: expectedHeight,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
_log(
|
|
||||||
'native_fit_error reason=$reason error=${_clip(error.toString(), 300)}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _log(String message) {
|
void _log(String message) {
|
||||||
debugPrint('$_logPrefix $message');
|
debugPrint('$_logPrefix $message');
|
||||||
}
|
}
|
||||||
@ -576,6 +546,7 @@ class _LeaderGamePageState extends State<LeaderGamePage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final headerHeight = 44.w;
|
final headerHeight = 44.w;
|
||||||
final bodyHeight = _calculateBodyHeight(context, headerHeight);
|
final bodyHeight = _calculateBodyHeight(context, headerHeight);
|
||||||
|
final bottomFrameHeight = resolveRoomGameCompatibilityBottomHeight(context);
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvokedWithResult: (bool didPop, Object? result) async {
|
onPopInvokedWithResult: (bool didPop, Object? result) async {
|
||||||
@ -595,7 +566,7 @@ class _LeaderGamePageState extends State<LeaderGamePage> {
|
|||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: ScreenUtil().screenWidth,
|
width: ScreenUtil().screenWidth,
|
||||||
height: bodyHeight + headerHeight,
|
height: bodyHeight + headerHeight + bottomFrameHeight,
|
||||||
color: const Color(0xFF081915),
|
color: const Color(0xFF081915),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -610,12 +581,22 @@ class _LeaderGamePageState extends State<LeaderGamePage> {
|
|||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
top: headerHeight,
|
top: headerHeight,
|
||||||
bottom: 0,
|
bottom: bottomFrameHeight,
|
||||||
child: WebViewWidget(
|
child: WebViewWidget(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
gestureRecognizers: _webGestureRecognizers,
|
gestureRecognizers: _webGestureRecognizers,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (bottomFrameHeight > 0)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
height: bottomFrameHeight,
|
||||||
|
child: RoomGameCompatibilityBottomFrame(
|
||||||
|
height: bottomFrameHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (_errorMessage != null) _buildErrorState(),
|
if (_errorMessage != null) _buildErrorState(),
|
||||||
if (_isLoading && _errorMessage == null)
|
if (_isLoading && _errorMessage == null)
|
||||||
const IgnorePointer(
|
const IgnorePointer(
|
||||||
|
|||||||
@ -57,16 +57,25 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
static const Duration _giftTriggeredMicRefreshMinInterval = Duration(
|
static const Duration _giftTriggeredMicRefreshMinInterval = Duration(
|
||||||
milliseconds: 900,
|
milliseconds: 900,
|
||||||
);
|
);
|
||||||
|
static const Duration _agoraJoinTimeout = Duration(seconds: 8);
|
||||||
|
static const Duration _agoraDisconnectedGracePeriod = Duration(seconds: 12);
|
||||||
|
|
||||||
bool needUpDataUserInfo = false;
|
bool needUpDataUserInfo = false;
|
||||||
bool _roomVisualEffectsEnabled = false;
|
bool _roomVisualEffectsEnabled = false;
|
||||||
bool _isExitingCurrentVoiceRoomSession = false;
|
bool _isExitingCurrentVoiceRoomSession = false;
|
||||||
|
bool _isHandlingAgoraRoomFailure = false;
|
||||||
|
bool _agoraJoined = false;
|
||||||
|
bool _agoraRoleIsBroadcaster = false;
|
||||||
RoomStartupStatus _roomStartupStatus = RoomStartupStatus.idle;
|
RoomStartupStatus _roomStartupStatus = RoomStartupStatus.idle;
|
||||||
RoomStartupFailureType _roomStartupFailureType = RoomStartupFailureType.none;
|
RoomStartupFailureType _roomStartupFailureType = RoomStartupFailureType.none;
|
||||||
int _roomStartupFailureToken = 0;
|
int _roomStartupFailureToken = 0;
|
||||||
bool _isHandlingRoomStartupFailure = false;
|
bool _isHandlingRoomStartupFailure = false;
|
||||||
Timer? _micListPollingTimer;
|
Timer? _micListPollingTimer;
|
||||||
Timer? _onlineUsersPollingTimer;
|
Timer? _onlineUsersPollingTimer;
|
||||||
|
Timer? _joinAgoraTimeoutTimer;
|
||||||
|
Timer? _agoraDisconnectedCleanupTimer;
|
||||||
|
Completer<void>? _joinAgoraCompleter;
|
||||||
|
String? _joiningChannelId;
|
||||||
bool _isRefreshingMicList = false;
|
bool _isRefreshingMicList = false;
|
||||||
bool _isRefreshingOnlineUsers = false;
|
bool _isRefreshingOnlineUsers = false;
|
||||||
bool _disableMicListRefreshForCurrentSession = false;
|
bool _disableMicListRefreshForCurrentSession = false;
|
||||||
@ -580,6 +589,96 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isCurrentAgoraChannel(String? channelId) {
|
||||||
|
final normalizedChannelId = (channelId ?? "").trim();
|
||||||
|
final currentRoomId =
|
||||||
|
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
|
if (normalizedChannelId.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return normalizedChannelId == currentRoomId ||
|
||||||
|
normalizedChannelId == (_joiningChannelId ?? "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelAgoraJoinWaiter([Object? error]) {
|
||||||
|
_joinAgoraTimeoutTimer?.cancel();
|
||||||
|
_joinAgoraTimeoutTimer = null;
|
||||||
|
final completer = _joinAgoraCompleter;
|
||||||
|
_joinAgoraCompleter = null;
|
||||||
|
if (error != null && completer != null && !completer.isCompleted) {
|
||||||
|
completer.completeError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelAgoraDisconnectedCleanup() {
|
||||||
|
_agoraDisconnectedCleanupTimer?.cancel();
|
||||||
|
_agoraDisconnectedCleanupTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetAgoraTracking({bool clearJoiningChannel = true}) {
|
||||||
|
_cancelAgoraJoinWaiter();
|
||||||
|
_cancelAgoraDisconnectedCleanup();
|
||||||
|
_agoraJoined = false;
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
if (clearJoiningChannel) {
|
||||||
|
_joiningChannelId = null;
|
||||||
|
}
|
||||||
|
_resetLocalAudioRuntimeTracking();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _canReportMicActive {
|
||||||
|
return _agoraJoined && _agoraRoleIsBroadcaster && isOnMai();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _joinAgoraCompleterIsPending {
|
||||||
|
final completer = _joinAgoraCompleter;
|
||||||
|
return completer != null && !completer.isCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resyncHeartbeatFromAgoraState() {
|
||||||
|
final roomId = currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
||||||
|
final canReportMicActive = _canReportMicActive;
|
||||||
|
_syncRoomHeartbeatState(
|
||||||
|
roomId: roomId,
|
||||||
|
isOnMic: canReportMicActive,
|
||||||
|
shouldRunAnchorHeartbeat: canReportMicActive,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isFatalAgoraError(ErrorCodeType err) {
|
||||||
|
return err == ErrorCodeType.errInvalidArgument ||
|
||||||
|
err == ErrorCodeType.errNotReady ||
|
||||||
|
err == ErrorCodeType.errNotSupported ||
|
||||||
|
err == ErrorCodeType.errRefused ||
|
||||||
|
err == ErrorCodeType.errInvalidState ||
|
||||||
|
err == ErrorCodeType.errNoPermission ||
|
||||||
|
err == ErrorCodeType.errJoinChannelRejected ||
|
||||||
|
err == ErrorCodeType.errInvalidAppId ||
|
||||||
|
err == ErrorCodeType.errInvalidChannelName ||
|
||||||
|
err == ErrorCodeType.errTokenExpired ||
|
||||||
|
err == ErrorCodeType.errInvalidToken ||
|
||||||
|
err == ErrorCodeType.errConnectionLost;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isFatalConnectionReason(ConnectionChangedReasonType reason) {
|
||||||
|
return reason ==
|
||||||
|
ConnectionChangedReasonType.connectionChangedBannedByServer ||
|
||||||
|
reason == ConnectionChangedReasonType.connectionChangedJoinFailed ||
|
||||||
|
reason == ConnectionChangedReasonType.connectionChangedInvalidAppId ||
|
||||||
|
reason ==
|
||||||
|
ConnectionChangedReasonType.connectionChangedInvalidChannelName ||
|
||||||
|
reason == ConnectionChangedReasonType.connectionChangedInvalidToken ||
|
||||||
|
reason == ConnectionChangedReasonType.connectionChangedTokenExpired ||
|
||||||
|
reason ==
|
||||||
|
ConnectionChangedReasonType.connectionChangedRejectedByServer ||
|
||||||
|
reason == ConnectionChangedReasonType.connectionChangedSameUidLogin ||
|
||||||
|
reason ==
|
||||||
|
ConnectionChangedReasonType.connectionChangedTooManyBroadcasters ||
|
||||||
|
reason ==
|
||||||
|
ConnectionChangedReasonType
|
||||||
|
.connectionChangedLicenseValidationFailure;
|
||||||
|
}
|
||||||
|
|
||||||
void _syncSelfMicRuntimeState() {
|
void _syncSelfMicRuntimeState() {
|
||||||
final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id;
|
final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id;
|
||||||
if ((currentUserId ?? "").isEmpty) {
|
if ((currentUserId ?? "").isEmpty) {
|
||||||
@ -597,6 +696,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
if (currentUserMic == null) {
|
if (currentUserMic == null) {
|
||||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
_syncRoomHeartbeatState(
|
_syncRoomHeartbeatState(
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
isOnMic: false,
|
isOnMic: false,
|
||||||
@ -610,23 +710,16 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_preferredSelfMicIndex = currentUserMic.micIndex;
|
_preferredSelfMicIndex = currentUserMic.micIndex;
|
||||||
|
final canReportMicActive = _canReportMicActive;
|
||||||
_syncRoomHeartbeatState(
|
_syncRoomHeartbeatState(
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
isOnMic: true,
|
isOnMic: canReportMicActive,
|
||||||
shouldRunAnchorHeartbeat: true,
|
shouldRunAnchorHeartbeat: canReportMicActive,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((currentUserMic.micMute ?? false) || isMic) {
|
|
||||||
_applyLocalAudioRuntimeState(
|
|
||||||
clientRole: ClientRoleType.clientRoleAudience,
|
|
||||||
muted: true,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_applyLocalAudioRuntimeState(
|
_applyLocalAudioRuntimeState(
|
||||||
clientRole: ClientRoleType.clientRoleBroadcaster,
|
clientRole: ClientRoleType.clientRoleBroadcaster,
|
||||||
muted: false,
|
muted: (currentUserMic.micMute ?? false) || isMic,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,6 +734,19 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_agoraJoined) {
|
||||||
|
if (_lastScheduledVoiceLiveRoomId != null ||
|
||||||
|
_lastScheduledAnchorRoomId != null) {
|
||||||
|
SCHeartbeatUtils.scheduleHeartbeat(
|
||||||
|
SCHeartbeatStatus.ONLINE.name,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
SCHeartbeatUtils.cancelAnchorTimer();
|
||||||
|
}
|
||||||
|
_resetHeartbeatTracking();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final voiceLiveChanged =
|
final voiceLiveChanged =
|
||||||
_lastScheduledVoiceLiveRoomId != normalizedRoomId ||
|
_lastScheduledVoiceLiveRoomId != normalizedRoomId ||
|
||||||
_lastScheduledVoiceLiveOnMic != isOnMic;
|
_lastScheduledVoiceLiveOnMic != isOnMic;
|
||||||
@ -692,6 +798,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
required ClientRoleType clientRole,
|
required ClientRoleType clientRole,
|
||||||
required bool muted,
|
required bool muted,
|
||||||
}) {
|
}) {
|
||||||
|
if (clientRole != ClientRoleType.clientRoleBroadcaster) {
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
}
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
_lastAppliedClientRole = null;
|
_lastAppliedClientRole = null;
|
||||||
_lastAppliedLocalAudioMuted = null;
|
_lastAppliedLocalAudioMuted = null;
|
||||||
@ -818,7 +927,83 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _cleanupAgoraRoomState({bool clearMicSeats = false}) async {
|
||||||
|
_resetAgoraTracking();
|
||||||
|
try {
|
||||||
|
await engine?.leaveChannel();
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Agora] leave channel during cleanup failed: $error');
|
||||||
|
}
|
||||||
|
if (clearMicSeats) {
|
||||||
|
roomWheatMap.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleAgoraJoinFailed(
|
||||||
|
Object error, {
|
||||||
|
required bool exitRoom,
|
||||||
|
}) async {
|
||||||
|
debugPrint('[Agora] join failed: $error');
|
||||||
|
await _cleanupAgoraRoomState(clearMicSeats: true);
|
||||||
|
if (!exitRoom || _isExitingCurrentVoiceRoomSession) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SCTts.show("Join room fail");
|
||||||
|
unawaited(exitCurrentVoiceRoomSession(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleAgoraDisconnected(Object reason) async {
|
||||||
|
if (_isHandlingAgoraRoomFailure || _isExitingCurrentVoiceRoomSession) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isHandlingAgoraRoomFailure = true;
|
||||||
|
debugPrint('[Agora] disconnected, leave room: $reason');
|
||||||
|
SCTts.show("Voice connection failed");
|
||||||
|
await _cleanupAgoraRoomState(clearMicSeats: true);
|
||||||
|
await exitCurrentVoiceRoomSession(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleAgoraDisconnectedCleanup(Object reason) {
|
||||||
|
if (_isExitingCurrentVoiceRoomSession) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_agoraDisconnectedCleanupTimer?.cancel();
|
||||||
|
_agoraDisconnectedCleanupTimer = Timer(_agoraDisconnectedGracePeriod, () {
|
||||||
|
_agoraDisconnectedCleanupTimer = null;
|
||||||
|
if (!_agoraJoined && !_isExitingCurrentVoiceRoomSession) {
|
||||||
|
unawaited(_handleAgoraDisconnected(reason));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> joinAgoraVoiceChannel({bool throwOnError = false}) async {
|
Future<void> joinAgoraVoiceChannel({bool throwOnError = false}) async {
|
||||||
|
final channelId = (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
|
if (channelId.isEmpty) {
|
||||||
|
final error = StateError('Agora channel id is empty');
|
||||||
|
await _handleAgoraJoinFailed(error, exitRoom: !throwOnError);
|
||||||
|
if (throwOnError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancelAgoraJoinWaiter();
|
||||||
|
_cancelAgoraDisconnectedCleanup();
|
||||||
|
_joiningChannelId = channelId;
|
||||||
|
_agoraJoined = false;
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
final joinCompleter = Completer<void>();
|
||||||
|
_joinAgoraCompleter = joinCompleter;
|
||||||
|
_joinAgoraTimeoutTimer = Timer(_agoraJoinTimeout, () {
|
||||||
|
if (!joinCompleter.isCompleted) {
|
||||||
|
joinCompleter.completeError(
|
||||||
|
TimeoutException('Agora join timeout', _agoraJoinTimeout),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final rtcEngine = await _initAgoraRtcEngine();
|
final rtcEngine = await _initAgoraRtcEngine();
|
||||||
rtcEngine.setAudioProfile(
|
rtcEngine.setAudioProfile(
|
||||||
@ -832,15 +1017,15 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
await rtcEngine.disableVideo();
|
await rtcEngine.disableVideo();
|
||||||
var rtcToken = await SCAccountRepository().getRtcToken(
|
var rtcToken = await SCAccountRepository().getRtcToken(
|
||||||
currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
channelId,
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id ?? "",
|
AccountStorage().getCurrentUser()?.userProfile?.id ?? "",
|
||||||
isPublisher: false,
|
isPublisher: false,
|
||||||
);
|
);
|
||||||
await rtcEngine.joinChannel(
|
await rtcEngine.joinChannel(
|
||||||
token: rtcToken.rtcToken ?? "",
|
token: rtcToken.rtcToken ?? "",
|
||||||
channelId: currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
channelId: channelId,
|
||||||
uid: _resolveAgoraUidForCurrentUser(),
|
uid: _resolveAgoraUidForCurrentUser(),
|
||||||
options: ChannelMediaOptions(
|
options: const ChannelMediaOptions(
|
||||||
// 自动订阅所有视频流
|
// 自动订阅所有视频流
|
||||||
autoSubscribeVideo: false,
|
autoSubscribeVideo: false,
|
||||||
// 自动订阅所有音频流
|
// 自动订阅所有音频流
|
||||||
@ -854,15 +1039,122 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
|
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
await joinCompleter.future;
|
||||||
_syncSelfMicRuntimeState();
|
_syncSelfMicRuntimeState();
|
||||||
rtcEngine.muteAllRemoteAudioStreams(roomIsMute);
|
rtcEngine.muteAllRemoteAudioStreams(roomIsMute);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await _handleAgoraJoinFailed(e, exitRoom: !throwOnError);
|
||||||
if (throwOnError) {
|
if (throwOnError) {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
SCTts.show("Join room fail");
|
|
||||||
exitCurrentVoiceRoomSession(false);
|
|
||||||
print('加入失败:${e.runtimeType},${e.toString()}');
|
print('加入失败:${e.runtimeType},${e.toString()}');
|
||||||
|
} finally {
|
||||||
|
if (identical(_joinAgoraCompleter, joinCompleter)) {
|
||||||
|
_cancelAgoraJoinWaiter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _switchAgoraToBroadcaster({
|
||||||
|
required String publisherToken,
|
||||||
|
required bool muted,
|
||||||
|
}) async {
|
||||||
|
if (!_agoraJoined || engine == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (publisherToken.trim().isNotEmpty) {
|
||||||
|
await engine?.renewToken(publisherToken);
|
||||||
|
}
|
||||||
|
await engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
|
||||||
|
if (!muted) {
|
||||||
|
adjustRecordingSignalVolume(100);
|
||||||
|
}
|
||||||
|
await engine?.muteLocalAudioStream(muted);
|
||||||
|
_lastAppliedClientRole = ClientRoleType.clientRoleBroadcaster;
|
||||||
|
_lastAppliedLocalAudioMuted = muted;
|
||||||
|
_agoraRoleIsBroadcaster = true;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Agora] switch to broadcaster failed: $error');
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _switchAgoraToAudience() async {
|
||||||
|
try {
|
||||||
|
await engine?.setClientRole(role: ClientRoleType.clientRoleAudience);
|
||||||
|
await engine?.muteLocalAudioStream(true);
|
||||||
|
_lastAppliedClientRole = ClientRoleType.clientRoleAudience;
|
||||||
|
_lastAppliedLocalAudioMuted = true;
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Agora] switch to audience failed: $error');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setSelfMicMutedFromBottom(bool muted) async {
|
||||||
|
isMic = muted;
|
||||||
|
final currentUserId =
|
||||||
|
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
|
||||||
|
final seatIndex = userOnMaiInIndex(currentUserId);
|
||||||
|
if (seatIndex < 0) {
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final seat = roomWheatMap[seatIndex];
|
||||||
|
final seatMuted = seat?.micMute ?? false;
|
||||||
|
if (seatMuted || muted) {
|
||||||
|
if (_agoraJoined && isOnMai()) {
|
||||||
|
await _switchAgoraToBroadcaster(
|
||||||
|
publisherToken: seat?.roomToken ?? "",
|
||||||
|
muted: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await _switchAgoraToBroadcaster(
|
||||||
|
publisherToken: seat?.roomToken ?? "",
|
||||||
|
muted: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleSelfMicRemovedByRemote({
|
||||||
|
bool refreshMicList = true,
|
||||||
|
}) async {
|
||||||
|
final currentUserId =
|
||||||
|
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
|
||||||
|
if (currentUserId.isEmpty) {
|
||||||
|
await _switchAgoraToAudience();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentSeatIndex = userOnMaiInIndex(currentUserId);
|
||||||
|
isMic = true;
|
||||||
|
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||||
|
if (currentSeatIndex > -1) {
|
||||||
|
_startSelfMicReleaseGuard(sourceIndex: currentSeatIndex);
|
||||||
|
} else {
|
||||||
|
_clearSelfMicReleaseGuard();
|
||||||
|
}
|
||||||
|
_clearUserFromSeats(currentUserId);
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
_syncSelfMicRuntimeState();
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
await _switchAgoraToAudience();
|
||||||
|
if (refreshMicList) {
|
||||||
|
requestMicrophoneListRefresh(
|
||||||
|
notifyIfUnchanged: false,
|
||||||
|
minInterval: const Duration(milliseconds: 350),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,6 +1170,119 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_rtcEngineEventHandler = RtcEngineEventHandler(
|
_rtcEngineEventHandler = RtcEngineEventHandler(
|
||||||
onError: (ErrorCodeType err, String msg) {
|
onError: (ErrorCodeType err, String msg) {
|
||||||
print('rtc错误$err');
|
print('rtc错误$err');
|
||||||
|
final error = 'Agora error: $err $msg';
|
||||||
|
if (!_agoraJoined) {
|
||||||
|
final completer = _joinAgoraCompleter;
|
||||||
|
if (completer != null && !completer.isCompleted) {
|
||||||
|
completer.completeError(StateError(error));
|
||||||
|
} else {
|
||||||
|
unawaited(_handleAgoraJoinFailed(error, exitRoom: true));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_isFatalAgoraError(err)) {
|
||||||
|
unawaited(_handleAgoraDisconnected(error));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
|
||||||
|
print('rtc 自己加入 ${connection.channelId} ${connection.localUid}');
|
||||||
|
final joinedChannelId = connection.channelId ?? "";
|
||||||
|
if (!_isCurrentAgoraChannel(joinedChannelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_agoraJoined = true;
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
_cancelAgoraDisconnectedCleanup();
|
||||||
|
_joinAgoraTimeoutTimer?.cancel();
|
||||||
|
_joinAgoraTimeoutTimer = null;
|
||||||
|
final completer = _joinAgoraCompleter;
|
||||||
|
if (completer != null && !completer.isCompleted) {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
},
|
||||||
|
onLeaveChannel: (RtcConnection connection, RtcStats stats) {
|
||||||
|
if (_isCurrentAgoraChannel(connection.channelId)) {
|
||||||
|
final completer = _joinAgoraCompleter;
|
||||||
|
if (completer != null && !completer.isCompleted) {
|
||||||
|
completer.completeError(
|
||||||
|
StateError('Agora left channel before join success'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_resetAgoraTracking();
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClientRoleChanged: (
|
||||||
|
RtcConnection connection,
|
||||||
|
ClientRoleType oldRole,
|
||||||
|
ClientRoleType newRole,
|
||||||
|
ClientRoleOptions newRoleOptions,
|
||||||
|
) {
|
||||||
|
if (!_isCurrentAgoraChannel(connection.channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_agoraRoleIsBroadcaster =
|
||||||
|
newRole == ClientRoleType.clientRoleBroadcaster;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
},
|
||||||
|
onClientRoleChangeFailed: (
|
||||||
|
RtcConnection connection,
|
||||||
|
ClientRoleChangeFailedReason reason,
|
||||||
|
ClientRoleType currentRole,
|
||||||
|
) {
|
||||||
|
if (!_isCurrentAgoraChannel(connection.channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_agoraRoleIsBroadcaster =
|
||||||
|
currentRole == ClientRoleType.clientRoleBroadcaster;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
},
|
||||||
|
onConnectionStateChanged: (
|
||||||
|
RtcConnection connection,
|
||||||
|
ConnectionStateType state,
|
||||||
|
ConnectionChangedReasonType reason,
|
||||||
|
) {
|
||||||
|
if (!_isCurrentAgoraChannel(connection.channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ConnectionStateType.connectionStateConnected &&
|
||||||
|
reason ==
|
||||||
|
ConnectionChangedReasonType.connectionChangedRejoinSuccess) {
|
||||||
|
_agoraJoined = true;
|
||||||
|
_cancelAgoraDisconnectedCleanup();
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (reason ==
|
||||||
|
ConnectionChangedReasonType.connectionChangedLeaveChannel) {
|
||||||
|
_resetAgoraTracking();
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ConnectionStateType.connectionStateFailed ||
|
||||||
|
_isFatalConnectionReason(reason)) {
|
||||||
|
_agoraJoined = false;
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
if (_joinAgoraCompleterIsPending) {
|
||||||
|
_joinAgoraCompleter?.completeError(
|
||||||
|
StateError('Agora connection failed before join: $reason'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unawaited(
|
||||||
|
_handleAgoraDisconnected('Agora connection failed: $reason'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ConnectionStateType.connectionStateDisconnected ||
|
||||||
|
state == ConnectionStateType.connectionStateReconnecting) {
|
||||||
|
_agoraJoined = false;
|
||||||
|
_agoraRoleIsBroadcaster = false;
|
||||||
|
_resyncHeartbeatFromAgoraState();
|
||||||
|
_scheduleAgoraDisconnectedCleanup(reason);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onAudioMixingStateChanged: (
|
onAudioMixingStateChanged: (
|
||||||
AudioMixingStateType state,
|
AudioMixingStateType state,
|
||||||
@ -895,9 +1300,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
|
|
||||||
print('rtc 自己加入 ${connection.channelId} ${connection.localUid}');
|
|
||||||
},
|
|
||||||
onUserJoined: (connection, remoteUid, elapsed) {
|
onUserJoined: (connection, remoteUid, elapsed) {
|
||||||
print('rtc用户 $remoteUid 加入了频道');
|
print('rtc用户 $remoteUid 加入了频道');
|
||||||
},
|
},
|
||||||
@ -912,7 +1314,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
var rtcToken = await SCAccountRepository().getRtcToken(
|
var rtcToken = await SCAccountRepository().getRtcToken(
|
||||||
currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id ?? "",
|
AccountStorage().getCurrentUser()?.userProfile?.id ?? "",
|
||||||
isPublisher: isOnMai(),
|
isPublisher: _agoraRoleIsBroadcaster,
|
||||||
);
|
);
|
||||||
engine?.renewToken(rtcToken.rtcToken ?? "");
|
engine?.renewToken(rtcToken.rtcToken ?? "");
|
||||||
},
|
},
|
||||||
@ -940,6 +1342,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('rtc销毁前离开频道出错: $e');
|
print('rtc销毁前离开频道出错: $e');
|
||||||
}
|
}
|
||||||
|
_resetAgoraTracking();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await rtcEngine.release();
|
await rtcEngine.release();
|
||||||
@ -1207,8 +1610,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await _bootstrapRoomHeartbeatState();
|
|
||||||
|
|
||||||
///获取麦位
|
///获取麦位
|
||||||
retrieveMicrophoneList();
|
retrieveMicrophoneList();
|
||||||
fetchOnlineUsersList();
|
fetchOnlineUsersList();
|
||||||
@ -1222,6 +1623,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
failureType = RoomStartupFailureType.rtc;
|
failureType = RoomStartupFailureType.rtc;
|
||||||
await joinAgoraVoiceChannel(throwOnError: true);
|
await joinAgoraVoiceChannel(throwOnError: true);
|
||||||
|
await _bootstrapRoomHeartbeatState();
|
||||||
_setRoomStartupReady();
|
_setRoomStartupReady();
|
||||||
|
|
||||||
Msg joinMsg = Msg(
|
Msg joinMsg = Msg(
|
||||||
@ -1421,7 +1823,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
SCHeartbeatUtils.scheduleHeartbeat(SCHeartbeatStatus.ONLINE.name, false);
|
SCHeartbeatUtils.scheduleHeartbeat(SCHeartbeatStatus.ONLINE.name, false);
|
||||||
SCHeartbeatUtils.cancelAnchorTimer();
|
SCHeartbeatUtils.cancelAnchorTimer();
|
||||||
rtmProvider?.cleanRoomData();
|
rtmProvider?.cleanRoomData();
|
||||||
await engine?.leaveChannel();
|
await _cleanupAgoraRoomState();
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
await rtmProvider?.quitGroup(
|
await rtmProvider?.quitGroup(
|
||||||
currenRoom!.roomProfile?.roomProfile?.roomAccount ?? "",
|
currenRoom!.roomProfile?.roomProfile?.roomAccount ?? "",
|
||||||
@ -1454,7 +1856,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
if (groupId.isNotEmpty) {
|
if (groupId.isNotEmpty) {
|
||||||
await rtmProvider?.quitGroup(groupId);
|
await rtmProvider?.quitGroup(groupId);
|
||||||
}
|
}
|
||||||
await engine?.leaveChannel();
|
await _cleanupAgoraRoomState();
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('rtc清理本地房间状态出错: $e');
|
print('rtc清理本地房间状态出错: $e');
|
||||||
@ -1487,7 +1889,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
if (groupId.isNotEmpty) {
|
if (groupId.isNotEmpty) {
|
||||||
await rtmProvider?.quitGroup(groupId);
|
await rtmProvider?.quitGroup(groupId);
|
||||||
}
|
}
|
||||||
await engine?.leaveChannel();
|
await _cleanupAgoraRoomState(clearMicSeats: true);
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
if (roomId.isNotEmpty) {
|
if (roomId.isNotEmpty) {
|
||||||
await SCAccountRepository().quitRoom(roomId);
|
await SCAccountRepository().quitRoom(roomId);
|
||||||
@ -1508,13 +1910,14 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
void _clearData() {
|
void _clearData() {
|
||||||
_stopRoomStatePolling();
|
_stopRoomStatePolling();
|
||||||
_resetHeartbeatTracking();
|
_resetHeartbeatTracking();
|
||||||
_resetLocalAudioRuntimeTracking();
|
_resetAgoraTracking();
|
||||||
_disableMicListRefreshForCurrentSession = false;
|
_disableMicListRefreshForCurrentSession = false;
|
||||||
_disableOnlineUsersRefreshForCurrentSession = false;
|
_disableOnlineUsersRefreshForCurrentSession = false;
|
||||||
_isSelfMicActionInFlight = false;
|
_isSelfMicActionInFlight = false;
|
||||||
_roomStartupStatus = RoomStartupStatus.idle;
|
_roomStartupStatus = RoomStartupStatus.idle;
|
||||||
_roomStartupFailureType = RoomStartupFailureType.none;
|
_roomStartupFailureType = RoomStartupFailureType.none;
|
||||||
_isHandlingRoomStartupFailure = false;
|
_isHandlingRoomStartupFailure = false;
|
||||||
|
_isHandlingAgoraRoomFailure = false;
|
||||||
_pendingMicListRefresh = false;
|
_pendingMicListRefresh = false;
|
||||||
_pendingMicListRefreshNotifyIfUnchanged = false;
|
_pendingMicListRefreshNotifyIfUnchanged = false;
|
||||||
_seatEmojiEventVersions.clear();
|
_seatEmojiEventVersions.clear();
|
||||||
@ -1719,22 +2122,32 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
inviterId: inviterId,
|
inviterId: inviterId,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// 设置成主播角色
|
|
||||||
engine?.renewToken(micGoUpRes.roomToken ?? "");
|
|
||||||
if (!micGoUpRes.micMute!) {
|
|
||||||
if (!isMic) {
|
|
||||||
engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
|
|
||||||
adjustRecordingSignalVolume(100);
|
|
||||||
await engine?.muteLocalAudioStream(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
engine?.setClientRole(role: ClientRoleType.clientRoleAudience);
|
|
||||||
await engine?.muteLocalAudioStream(true);
|
|
||||||
}
|
|
||||||
SCHeartbeatUtils.scheduleAnchorHeartbeat(
|
|
||||||
currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
|
||||||
);
|
|
||||||
final targetIndex = micGoUpRes.micIndex ?? index;
|
final targetIndex = micGoUpRes.micIndex ?? index;
|
||||||
|
final switched = await _switchAgoraToBroadcaster(
|
||||||
|
publisherToken: micGoUpRes.roomToken ?? "",
|
||||||
|
muted: (micGoUpRes.micMute ?? false) || isMic,
|
||||||
|
);
|
||||||
|
if (!switched) {
|
||||||
|
await _switchAgoraToAudience();
|
||||||
|
try {
|
||||||
|
await SCChatRoomRepository().micGoDown(
|
||||||
|
currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||||||
|
targetIndex,
|
||||||
|
);
|
||||||
|
} catch (downError) {
|
||||||
|
debugPrint(
|
||||||
|
'[Agora] release backend mic after broadcaster switch failed: $downError',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||||
|
_clearUserFromSeats(myUser?.id);
|
||||||
|
_syncSelfMicRuntimeState();
|
||||||
|
notifyListeners();
|
||||||
|
_refreshMicListSilently(notifyIfUnchanged: true);
|
||||||
|
SCTts.show('Failed to put on the microphone, Agora switch failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final previousSelfSeatIndex =
|
final previousSelfSeatIndex =
|
||||||
myUser != null ? userOnMaiInIndex(myUser.id ?? "") : -1;
|
myUser != null ? userOnMaiInIndex(myUser.id ?? "") : -1;
|
||||||
final isSeatSwitching =
|
final isSeatSwitching =
|
||||||
@ -1806,7 +2219,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
final currentUserSeatIndex = userOnMaiInIndex(currentUserId);
|
final currentUserSeatIndex = userOnMaiInIndex(currentUserId);
|
||||||
if (roomWheatMap[index]?.user?.id == currentUserId) {
|
if (roomWheatMap[index]?.user?.id == currentUserId) {
|
||||||
isMic = true;
|
isMic = true;
|
||||||
engine?.muteLocalAudioStream(true);
|
|
||||||
}
|
}
|
||||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||||
if (currentUserSeatIndex > -1) {
|
if (currentUserSeatIndex > -1) {
|
||||||
@ -1816,9 +2228,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
SCHeartbeatUtils.cancelAnchorTimer();
|
SCHeartbeatUtils.cancelAnchorTimer();
|
||||||
|
|
||||||
/// 设置成主持人角色
|
await _switchAgoraToAudience();
|
||||||
engine?.renewToken("");
|
|
||||||
engine?.setClientRole(role: ClientRoleType.clientRoleAudience);
|
|
||||||
_clearUserFromSeats(currentUserId);
|
_clearUserFromSeats(currentUserId);
|
||||||
_syncSelfMicRuntimeState();
|
_syncSelfMicRuntimeState();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -2054,14 +2464,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if (isOnMaiInIndex(index)) {
|
if (isOnMaiInIndex(index)) {
|
||||||
Provider.of<RtcProvider>(
|
await _switchAgoraToBroadcaster(
|
||||||
context!,
|
publisherToken: roomWheatMap[index]?.roomToken ?? "",
|
||||||
listen: false,
|
muted: true,
|
||||||
).engine?.setClientRole(role: ClientRoleType.clientRoleAudience);
|
);
|
||||||
Provider.of<RtcProvider>(
|
|
||||||
context!,
|
|
||||||
listen: false,
|
|
||||||
).engine?.muteLocalAudioStream(true);
|
|
||||||
}
|
}
|
||||||
var mic = roomWheatMap[index];
|
var mic = roomWheatMap[index];
|
||||||
if (mic != null) {
|
if (mic != null) {
|
||||||
@ -2079,17 +2485,12 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
if (isOnMaiInIndex(index)) {
|
if (isOnMaiInIndex(index)) {
|
||||||
if (!Provider.of<RtcProvider>(context!, listen: false).isMic) {
|
if (!isMic) {
|
||||||
if (roomWheatMap[index]?.user != null) {
|
if (roomWheatMap[index]?.user != null) {
|
||||||
adjustRecordingSignalVolume(100);
|
await _switchAgoraToBroadcaster(
|
||||||
Provider.of<RtcProvider>(
|
publisherToken: roomWheatMap[index]?.roomToken ?? "",
|
||||||
context!,
|
muted: false,
|
||||||
listen: false,
|
);
|
||||||
).engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
|
|
||||||
Provider.of<RtcProvider>(
|
|
||||||
context!,
|
|
||||||
listen: false,
|
|
||||||
).engine?.muteLocalAudioStream(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
|
||||||
import 'package:extended_image/extended_image.dart';
|
import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -304,16 +303,16 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
if (memberList.isNotEmpty) {
|
if (memberList.isNotEmpty) {
|
||||||
if (memberList.first.userID ==
|
if (memberList.first.userID ==
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id) {
|
AccountStorage().getCurrentUser()?.userProfile?.id) {
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).engine?.setClientRole(role: ClientRoleType.clientRoleAudience);
|
);
|
||||||
|
unawaited(
|
||||||
|
rtcProvider.handleSelfMicRemovedByRemote(refreshMicList: false),
|
||||||
|
);
|
||||||
|
|
||||||
///退出房间
|
///退出房间
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
rtcProvider.exitCurrentVoiceRoomSession(false).whenComplete(() {
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).exitCurrentVoiceRoomSession(false).whenComplete(() {
|
|
||||||
SCRoomUtils.closeAllDialogs();
|
SCRoomUtils.closeAllDialogs();
|
||||||
SmartDialog.show(
|
SmartDialog.show(
|
||||||
tag: "showConfirmDialog",
|
tag: "showConfirmDialog",
|
||||||
@ -1395,16 +1394,16 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
if (msg.type == SCRoomMsgType.killXiaMai) {
|
if (msg.type == SCRoomMsgType.killXiaMai) {
|
||||||
///踢下麦
|
///踢下麦
|
||||||
if (msg.msg == AccountStorage().getCurrentUser()?.userProfile?.id) {
|
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
|
||||||
context!,
|
|
||||||
listen: false,
|
|
||||||
).engine?.setClientRole(role: ClientRoleType.clientRoleAudience);
|
|
||||||
}
|
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
|
||||||
context!,
|
context!,
|
||||||
listen: false,
|
listen: false,
|
||||||
).retrieveMicrophoneList(notifyIfUnchanged: false);
|
);
|
||||||
|
if (msg.msg == AccountStorage().getCurrentUser()?.userProfile?.id) {
|
||||||
|
unawaited(
|
||||||
|
rtcProvider.handleSelfMicRemovedByRemote(refreshMicList: false),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rtcProvider.retrieveMicrophoneList(notifyIfUnchanged: false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -13,7 +14,6 @@ import 'package:yumi/ui_kit/widgets/room/room_menu_dialog.dart';
|
|||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_input.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_msg_input.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
|
|
||||||
import '../../../app/routes/sc_fluro_navigator.dart';
|
import '../../../app/routes/sc_fluro_navigator.dart';
|
||||||
@ -340,32 +340,8 @@ class _RoomBottomWidgetState extends State<RoomBottomWidget> {
|
|||||||
final provider = context.read<RtcProvider>();
|
final provider = context.read<RtcProvider>();
|
||||||
setState(() {
|
setState(() {
|
||||||
provider.isMic = !provider.isMic;
|
provider.isMic = !provider.isMic;
|
||||||
|
|
||||||
provider.roomWheatMap.forEach((k, v) {
|
|
||||||
final seat = provider.micAtIndexForDisplay(k);
|
|
||||||
if ((seat?.user?.id ?? "").trim() ==
|
|
||||||
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "")
|
|
||||||
.trim() &&
|
|
||||||
!(seat?.micMute ?? true)) {
|
|
||||||
if (!provider.isMic) {
|
|
||||||
provider.engine?.adjustRecordingSignalVolume(100);
|
|
||||||
provider.engine?.setClientRole(
|
|
||||||
role: ClientRoleType.clientRoleBroadcaster,
|
|
||||||
);
|
|
||||||
provider.engine?.muteLocalAudioStream(false);
|
|
||||||
} else {
|
|
||||||
if (provider.isMusicPlaying) {
|
|
||||||
provider.engine?.adjustRecordingSignalVolume(0);
|
|
||||||
} else {
|
|
||||||
provider.engine?.setClientRole(
|
|
||||||
role: ClientRoleType.clientRoleAudience,
|
|
||||||
);
|
|
||||||
provider.engine?.muteLocalAudioStream(provider.isMic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
unawaited(provider.setSelfMicMutedFromBottom(provider.isMic));
|
||||||
},
|
},
|
||||||
child: RoomBottomCircleAction(
|
child: RoomBottomCircleAction(
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
|
|||||||
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.2.2+6
|
version: 1.2.5+7
|
||||||
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user