🐛 Bug fixes on macos
This commit is contained in:
		| @@ -78,6 +78,14 @@ | ||||
|   "chatCall": "Call", | ||||
|   "chatCallOngoing": "A call is ongoing", | ||||
|   "chatCallJoin": "Join", | ||||
|   "chatCallMute": "Mute", | ||||
|   "chatCallUnMute": "Un-mute", | ||||
|   "chatCallVideoOff": "Turn Off Video", | ||||
|   "chatCallVideoOn": "Turn On Video", | ||||
|   "chatCallVideoFlip": "Flip Camera", | ||||
|   "chatCallScreenOn": "Start Screen Share", | ||||
|   "chatCallScreenOff": "Stop Screen Share", | ||||
|   "chatCallChangeSpeaker": "Change Speaker", | ||||
|   "chatMessagePlaceholder": "Write a message...", | ||||
|   "chatMessageEditNotify": "You are about editing a message.", | ||||
|   "chatMessageReplyNotify": "You are about replying a message.", | ||||
|   | ||||
| @@ -76,6 +76,14 @@ | ||||
|   "chatChannelLeaveConfirm": "你确定你要离开这个频道吗?你在这个频道里的消息将被存储下来,但是当你重新加入本频道后你将会失去对你之前消息的权限。", | ||||
|   "chatChannelDeleteConfirm": "你确定你要删除这个频道吗?这个频道里的所有消息都将消失,并且不可被反转!", | ||||
|   "chatCall": "通话", | ||||
|   "chatCallMute": "静音", | ||||
|   "chatCallUnMute": "取消静音", | ||||
|   "chatCallVideoOff": "关闭摄像头", | ||||
|   "chatCallVideoOn": "启动摄像头", | ||||
|   "chatCallVideoFlip": "翻转视频输出", | ||||
|   "chatCallScreenOn": "开启屏幕分享", | ||||
|   "chatCallScreenOff": "停止屏幕分享", | ||||
|   "chatCallChangeSpeaker": "切换扬声器", | ||||
|   "chatCallOngoing": "一则通话正在进行中", | ||||
|   "chatCallJoin": "加入", | ||||
|   "chatMessagePlaceholder": "发条消息……", | ||||
|   | ||||
| @@ -12,6 +12,8 @@ import 'package:solian/utils/video_player.dart'; | ||||
| import 'package:solian/widgets/notification_notifier.dart'; | ||||
|  | ||||
| void main() { | ||||
|   WidgetsFlutterBinding.ensureInitialized(); | ||||
|  | ||||
|   initVideo(); | ||||
|   initTimeAgo(); | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import 'package:solian/widgets/chat/call/participant.dart'; | ||||
| import 'package:solian/widgets/indent_wrapper.dart'; | ||||
| import 'package:permission_handler/permission_handler.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:wakelock_plus/wakelock_plus.dart'; | ||||
| import 'dart:math' as math; | ||||
|  | ||||
| import '../../widgets/chat/call/controls.dart'; | ||||
| @@ -50,9 +51,8 @@ class _ChatCallState extends State<ChatCall> { | ||||
|  | ||||
|   List<ParticipantTrack> _participantTracks = []; | ||||
|  | ||||
|   bool get _fastConnection => _callRoom.engine.fastConnectOptions != null; | ||||
|  | ||||
|   Future<void> checkPermissions() async { | ||||
|     if (lkPlatformIs(PlatformType.macOS) || lkPlatformIs(PlatformType.linux)) return; | ||||
|     await Permission.camera.request(); | ||||
|     await Permission.microphone.request(); | ||||
|     await Permission.bluetooth.request(); | ||||
| @@ -87,7 +87,7 @@ class _ChatCallState extends State<ChatCall> { | ||||
|   } | ||||
|  | ||||
|   void joinRoom(String url, String token) async { | ||||
|     if(_isMounted) { | ||||
|     if (_isMounted) { | ||||
|       return; | ||||
|     } else { | ||||
|       _isMounted = true; | ||||
| @@ -145,16 +145,14 @@ class _ChatCallState extends State<ChatCall> { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void askPublish() async { | ||||
|     final result = await context.showPublishDialog(); | ||||
|     if (result != true) return; | ||||
|   void autoPublish() async { | ||||
|     try { | ||||
|       await _callRoom.localParticipant?.setCameraEnabled(true); | ||||
|       if (_enableVideo) await _callRoom.localParticipant?.setCameraEnabled(true); | ||||
|     } catch (error) { | ||||
|       await context.showErrorDialog(error); | ||||
|     } | ||||
|     try { | ||||
|       await _callRoom.localParticipant?.setMicrophoneEnabled(true); | ||||
|       if (_enableAudio) await _callRoom.localParticipant?.setMicrophoneEnabled(true); | ||||
|     } catch (error) { | ||||
|       await context.showErrorDialog(error); | ||||
|     } | ||||
| @@ -164,11 +162,7 @@ class _ChatCallState extends State<ChatCall> { | ||||
|     _callRoom.addListener(onRoomDidUpdate); | ||||
|     setupRoomListeners(); | ||||
|     sortParticipants(); | ||||
|     WidgetsBindingCompatible.instance?.addPostFrameCallback((_) { | ||||
|       if (!_fastConnection) { | ||||
|         askPublish(); | ||||
|       } | ||||
|     }); | ||||
|     WidgetsBindingCompatible.instance?.addPostFrameCallback((_) => autoPublish()); | ||||
|  | ||||
|     if (lkPlatformIsMobile()) { | ||||
|       Hardware.instance.setSpeakerphoneOn(true); | ||||
| @@ -362,6 +356,7 @@ class _ChatCallState extends State<ChatCall> { | ||||
|     _callRoom = Room(); | ||||
|     _callListener = _callRoom.createListener(); | ||||
|     Hardware.instance.enumerateDevices().then(revertDevices); | ||||
|     WakelockPlus.enable(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
| @@ -425,6 +420,7 @@ class _ChatCallState extends State<ChatCall> { | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     WakelockPlus.disable(); | ||||
|     (() async { | ||||
|       _callRoom.removeListener(onRoomDidUpdate); | ||||
|       await _callRoom.disconnect(); | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_background/flutter_background.dart'; | ||||
| import 'package:flutter_webrtc/flutter_webrtc.dart'; | ||||
| import 'package:livekit_client/livekit_client.dart'; | ||||
| import 'package:solian/widgets/chat/call/exts.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
|  | ||||
| class ControlsWidget extends StatefulWidget { | ||||
|   final Room room; | ||||
| @@ -61,11 +61,6 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|  | ||||
|   void onChange() => setState(() {}); | ||||
|  | ||||
|   void unpublishAll() async { | ||||
|     final result = await context.showUnPublishDialog(); | ||||
|     if (result == true) await participant.unpublishAllTracks(); | ||||
|   } | ||||
|  | ||||
|   bool get isMuted => participant.isMuted; | ||||
|  | ||||
|   void disableAudio() async { | ||||
| @@ -106,7 +101,6 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|   } | ||||
|  | ||||
|   void toggleCamera() async { | ||||
|     // | ||||
|     final track = participant.videoTrackPublications.firstOrNull?.track; | ||||
|     if (track == null) return; | ||||
|  | ||||
| @@ -202,22 +196,6 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void onTapUpdateSubscribePermission() async { | ||||
|     final result = await context.showSubscribePermissionDialog(); | ||||
|     if (result != null) { | ||||
|       try { | ||||
|         widget.room.localParticipant?.setTrackSubscriptionPermissions( | ||||
|           allParticipantsAllowed: result, | ||||
|         ); | ||||
|       } catch (e) { | ||||
|         final message = e.toString(); | ||||
|         ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|           content: Text('Something went wrong... $message'), | ||||
|         )); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Padding( | ||||
| @@ -230,17 +208,12 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|         spacing: 5, | ||||
|         runSpacing: 5, | ||||
|         children: [ | ||||
|           IconButton( | ||||
|             onPressed: unpublishAll, | ||||
|             icon: const Icon(Icons.cancel), | ||||
|             tooltip: 'Unpublish all', | ||||
|           ), | ||||
|           if (participant.isMicrophoneEnabled()) | ||||
|             if (lkPlatformIs(PlatformType.android)) | ||||
|               IconButton( | ||||
|                 onPressed: disableAudio, | ||||
|                 icon: const Icon(Icons.mic), | ||||
|                 tooltip: 'mute audio', | ||||
|                 tooltip: AppLocalizations.of(context)!.chatCallMute, | ||||
|               ) | ||||
|             else | ||||
|               PopupMenuButton<MediaDevice>( | ||||
| @@ -250,9 +223,9 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|                     PopupMenuItem<MediaDevice>( | ||||
|                       value: null, | ||||
|                       onTap: isMuted ? enableAudio : disableAudio, | ||||
|                       child: const ListTile( | ||||
|                         leading: Icon(Icons.mic_off), | ||||
|                         title: Text('Mute Microphone'), | ||||
|                       child: ListTile( | ||||
|                         leading: const Icon(Icons.mic_off), | ||||
|                         title: Text(AppLocalizations.of(context)!.chatCallMute), | ||||
|                       ), | ||||
|                     ), | ||||
|                     if (_audioInputs != null) | ||||
| @@ -275,7 +248,7 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|             IconButton( | ||||
|               onPressed: enableAudio, | ||||
|               icon: const Icon(Icons.mic_off), | ||||
|               tooltip: 'un-mute audio', | ||||
|               tooltip: AppLocalizations.of(context)!.chatCallUnMute, | ||||
|             ), | ||||
|           if (!lkPlatformIs(PlatformType.iOS)) | ||||
|             PopupMenuButton<MediaDevice>( | ||||
| @@ -310,7 +283,7 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|               disabledColor: Colors.grey, | ||||
|               onPressed: Hardware.instance.canSwitchSpeakerphone ? setSpeakerphoneOn : null, | ||||
|               icon: Icon(_speakerphoneOn ? Icons.speaker_phone : Icons.phone_android), | ||||
|               tooltip: 'Switch SpeakerPhone', | ||||
|               tooltip: AppLocalizations.of(context)!.chatCallChangeSpeaker, | ||||
|             ), | ||||
|           if (participant.isCameraEnabled()) | ||||
|             PopupMenuButton<MediaDevice>( | ||||
| @@ -320,12 +293,9 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|                   PopupMenuItem<MediaDevice>( | ||||
|                     value: null, | ||||
|                     onTap: disableVideo, | ||||
|                     child: const ListTile( | ||||
|                       leading: Icon( | ||||
|                         Icons.videocam_off, | ||||
|                         color: Colors.white, | ||||
|                       ), | ||||
|                       title: Text('Disable Camera'), | ||||
|                     child: ListTile( | ||||
|                       leading: const Icon(Icons.videocam_off, color: Colors.white), | ||||
|                       title: Text(AppLocalizations.of(context)!.chatCallVideoOff), | ||||
|                     ), | ||||
|                   ), | ||||
|                   if (_videoInputs != null) | ||||
| @@ -348,30 +318,25 @@ class _ControlsWidgetState extends State<ControlsWidget> { | ||||
|             IconButton( | ||||
|               onPressed: enableVideo, | ||||
|               icon: const Icon(Icons.videocam_off), | ||||
|               tooltip: 'un-mute video', | ||||
|               tooltip: AppLocalizations.of(context)!.chatCallVideoOn, | ||||
|             ), | ||||
|           IconButton( | ||||
|             icon: Icon(position == CameraPosition.back ? Icons.video_camera_back : Icons.video_camera_front), | ||||
|             onPressed: () => toggleCamera(), | ||||
|             tooltip: 'toggle camera', | ||||
|             tooltip: AppLocalizations.of(context)!.chatCallVideoFlip, | ||||
|           ), | ||||
|           if (participant.isScreenShareEnabled()) | ||||
|             IconButton( | ||||
|               icon: const Icon(Icons.monitor_outlined), | ||||
|               onPressed: () => disableScreenShare(), | ||||
|               tooltip: 'unshare screen (experimental)', | ||||
|               tooltip: AppLocalizations.of(context)!.chatCallScreenOff, | ||||
|             ) | ||||
|           else | ||||
|             IconButton( | ||||
|               icon: const Icon(Icons.monitor), | ||||
|               onPressed: () => enableScreenShare(), | ||||
|               tooltip: 'share screen (experimental)', | ||||
|               tooltip: AppLocalizations.of(context)!.chatCallScreenOn, | ||||
|             ), | ||||
|           IconButton( | ||||
|             onPressed: onTapUpdateSubscribePermission, | ||||
|             icon: const Icon(Icons.settings), | ||||
|             tooltip: 'Subscribe permission', | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -156,7 +156,6 @@ abstract class _ParticipantWidgetState<T extends ParticipantWidget> extends Stat | ||||
|                         _firstAudioPublication?.muted == false && _firstAudioPublication?.subscribed == true, | ||||
|                     connectionQuality: widget.participant.connectionQuality, | ||||
|                     isScreenShare: widget.isScreenShare, | ||||
|                     enabledE2EE: widget.participant.isEncrypted, | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|   | ||||
| @@ -6,7 +6,6 @@ class ParticipantInfoWidget extends StatelessWidget { | ||||
|   final bool audioAvailable; | ||||
|   final ConnectionQuality connectionQuality; | ||||
|   final bool isScreenShare; | ||||
|   final bool enabledE2EE; | ||||
|  | ||||
|   const ParticipantInfoWidget({ | ||||
|     super.key, | ||||
| @@ -14,7 +13,6 @@ class ParticipantInfoWidget extends StatelessWidget { | ||||
|     this.audioAvailable = true, | ||||
|     this.connectionQuality = ConnectionQuality.unknown, | ||||
|     this.isScreenShare = false, | ||||
|     this.enabledE2EE = false, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
| @@ -65,14 +63,6 @@ class ParticipantInfoWidget extends StatelessWidget { | ||||
|                   size: 16, | ||||
|                 ), | ||||
|               ), | ||||
|             Padding( | ||||
|               padding: const EdgeInsets.only(left: 5), | ||||
|               child: Icon( | ||||
|                 enabledE2EE ? Icons.lock : Icons.lock_open, | ||||
|                 color: enabledE2EE ? Colors.green : Colors.red, | ||||
|                 size: 16, | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ); | ||||
|   | ||||
| @@ -27,6 +27,7 @@ class _ChatMessageEditorState extends State<ChatMessageEditor> { | ||||
|   final _textController = TextEditingController(); | ||||
|  | ||||
|   bool _isSubmitting = false; | ||||
|   int? _prevEditingId; | ||||
|  | ||||
|   List<Attachment> _attachments = List.empty(growable: true); | ||||
|  | ||||
| @@ -80,8 +81,9 @@ class _ChatMessageEditorState extends State<ChatMessageEditor> { | ||||
|   } | ||||
|  | ||||
|   void syncWidget() { | ||||
|     if (widget.editing != null) { | ||||
|     if (widget.editing != null && _prevEditingId != widget.editing!.id) { | ||||
|       setState(() { | ||||
|         _prevEditingId = widget.editing!.id; | ||||
|         _textController.text = widget.editing!.content; | ||||
|         _attachments = widget.editing!.attachments ?? List.empty(growable: true); | ||||
|       }); | ||||
|   | ||||
| @@ -6,11 +6,19 @@ | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.cs.allow-jit</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.network.server</key> | ||||
| 	<key>com.apple.security.device.audio-input</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.device.bluetooth</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.device.camera</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.files.user-selected.read-only</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.network.client</key> | ||||
|     <true/> | ||||
|     <key>com.apple.security.files.user-selected.read-only</key> | ||||
|     <true/> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.network.server</key> | ||||
| 	<true/> | ||||
| 	<key>keychain-access-groups</key> | ||||
| 	<array/> | ||||
| </dict> | ||||
| </plist> | ||||
|   | ||||
| @@ -4,9 +4,17 @@ | ||||
| <dict> | ||||
| 	<key>com.apple.security.app-sandbox</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.device.audio-input</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.device.bluetooth</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.device.camera</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.files.user-selected.read-only</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.network.client</key> | ||||
|     <true/> | ||||
|     <key>com.apple.security.files.user-selected.read-only</key> | ||||
|     <true/> | ||||
| 	<true/> | ||||
| 	<key>keychain-access-groups</key> | ||||
| 	<array/> | ||||
| </dict> | ||||
| </plist> | ||||
|   | ||||
| @@ -1163,7 +1163,7 @@ packages: | ||||
|     source: hosted | ||||
|     version: "2.0.7" | ||||
|   wakelock_plus: | ||||
|     dependency: transitive | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: wakelock_plus | ||||
|       sha256: c8b7cc80f045533b40a0e6c9109905494e3cf32c0fbd5c62616998e0de44003f | ||||
|   | ||||
| @@ -64,6 +64,7 @@ dependencies: | ||||
|   permission_handler: ^11.3.1 | ||||
|   flutter_webrtc: ^0.10.3 | ||||
|   flutter_background: ^1.2.0 | ||||
|   wakelock_plus: ^1.2.4 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user